opcrew 0.1.4 → 0.1.5
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/dist/cli.js +165 -33
- package/dist/opencode-plugin.js +152 -54
- package/dist/skills/skills/write-adr/SKILL.md +114 -0
- package/dist/skills/skills/write-adr/templates/adr-template.md +13 -0
- package/dist/skills/write-adr/SKILL.md +114 -0
- package/dist/skills/write-adr/templates/adr-template.md +13 -0
- package/docs/knowledge/decisions/20260329-002-strict-agent-boundaries.md +51 -0
- package/docs/knowledge/decisions/20260329-003-plugin-factory-pattern.md +83 -0
- package/package.json +3 -2
package/dist/cli.js
CHANGED
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
import process from "process";
|
|
6
6
|
|
|
7
7
|
// src/cli/install.ts
|
|
8
|
-
import { mkdir, writeFile } from "fs/promises";
|
|
9
|
-
import
|
|
8
|
+
import { mkdir, writeFile, cp, rm } from "fs/promises";
|
|
9
|
+
import path2 from "path";
|
|
10
10
|
// src/core/tools/translations.ts
|
|
11
11
|
var CLAUDE_TOOLS = {
|
|
12
12
|
read: "Read",
|
|
@@ -20,7 +20,7 @@ var CLAUDE_TOOLS = {
|
|
|
20
20
|
delegate: "Agent",
|
|
21
21
|
todo: "TodoWrite",
|
|
22
22
|
test: "Bash",
|
|
23
|
-
logbook: "
|
|
23
|
+
logbook: "",
|
|
24
24
|
skill: "Skill"
|
|
25
25
|
};
|
|
26
26
|
var OPENCODE_TOOLS = {
|
|
@@ -61,7 +61,7 @@ function translateTools(canonicalTools, platform) {
|
|
|
61
61
|
};
|
|
62
62
|
const translationMap = maps[platform];
|
|
63
63
|
const translated = canonicalTools.map((tool) => translationMap[tool]);
|
|
64
|
-
return [...new Set(translated)];
|
|
64
|
+
return [...new Set(translated)].filter((tool) => tool !== "");
|
|
65
65
|
}
|
|
66
66
|
// src/core/Agent.ts
|
|
67
67
|
class OpCrewAgent {
|
|
@@ -69,8 +69,24 @@ class OpCrewAgent {
|
|
|
69
69
|
constructor(config) {
|
|
70
70
|
this.config = config;
|
|
71
71
|
}
|
|
72
|
+
getAllowedTools() {
|
|
73
|
+
return this.config.allowedTools ?? this.config.tools ?? [];
|
|
74
|
+
}
|
|
75
|
+
hasTool(tool) {
|
|
76
|
+
return this.getAllowedTools().includes(tool);
|
|
77
|
+
}
|
|
78
|
+
validateToolUsage(tool) {
|
|
79
|
+
if (!this.hasTool(tool)) {
|
|
80
|
+
const allowedList = this.getAllowedTools().join(", ") || "(none)";
|
|
81
|
+
throw new Error(`Tool '${tool}' is not allowed for agent '${this.config.name}'. ` + `Allowed tools: [${allowedList}]`);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
getForbiddenTools(allAvailableTools) {
|
|
85
|
+
const allowedSet = new Set(this.getAllowedTools());
|
|
86
|
+
return allAvailableTools.filter((tool) => !allowedSet.has(tool));
|
|
87
|
+
}
|
|
72
88
|
compileToMarkdown() {
|
|
73
|
-
const translatedTools = translateTools(this.
|
|
89
|
+
const translatedTools = translateTools(this.getAllowedTools(), "claude");
|
|
74
90
|
const lowerName = this.config.name.toLowerCase();
|
|
75
91
|
const frontmatter = `---
|
|
76
92
|
name: ${lowerName}
|
|
@@ -85,7 +101,7 @@ ${this.buildMarkdownBody()}`;
|
|
|
85
101
|
Orchestrator: { edit: "deny", bash: "ask", webfetch: "deny" },
|
|
86
102
|
Planner: { edit: "deny", bash: "ask", webfetch: "allow" },
|
|
87
103
|
Executor: { edit: "allow", bash: "allow", webfetch: "ask" },
|
|
88
|
-
Reviewer: { edit: "deny", bash: "
|
|
104
|
+
Reviewer: { edit: "deny", bash: "allow", webfetch: "deny" },
|
|
89
105
|
Researcher: { edit: "deny", bash: "deny", webfetch: "allow" }
|
|
90
106
|
};
|
|
91
107
|
const permission = permissionByRole[this.config.role];
|
|
@@ -101,7 +117,7 @@ permission:
|
|
|
101
117
|
${this.buildMarkdownBody()}`;
|
|
102
118
|
}
|
|
103
119
|
compileToCodexMarkdown() {
|
|
104
|
-
const translatedTools = translateTools(this.
|
|
120
|
+
const translatedTools = translateTools(this.getAllowedTools(), "codex");
|
|
105
121
|
const frontmatter = `---
|
|
106
122
|
name: ${this.config.name}
|
|
107
123
|
role: ${this.config.role}
|
|
@@ -130,21 +146,22 @@ var Captain = new OpCrewAgent({
|
|
|
130
146
|
role: "Orchestrator",
|
|
131
147
|
mode: "primary",
|
|
132
148
|
instructions: [
|
|
149
|
+
"TOOL BOUNDARY: You are ONLY permitted to use these tools: read, logbook, delegate, skill. Using any other tool is a violation.",
|
|
133
150
|
"You are the orchestrator - you NEVER execute work directly. Your role is to coordinate, delegate, and verify.",
|
|
134
|
-
"
|
|
151
|
+
"Workstream Fan-out: divide missions into independent Workstreams (e.g., Logic, Testing, Documentation) to enable parallel specialists.",
|
|
135
152
|
"Assign roles in order: Navigator plans, Boatswain executes, Quartermaster reviews; resolve conflicts quickly.",
|
|
136
|
-
"
|
|
153
|
+
"Collision Prevention: ensure no two Boatswains are assigned the same file or symbol in parallel. Launch workstreams only if they are logically decoupled.",
|
|
137
154
|
"Escalate blockers immediately and re-sequence work to unblock the critical path.",
|
|
138
155
|
"Enforce conventions: small, reviewable diffs; no scope creep; verification required before declaring done.",
|
|
139
156
|
"Intent gate: restate user intent, classify task type (trivial/explicit/exploratory/open-ended/ambiguous), and choose the workflow before action.",
|
|
140
157
|
"Delegation mandate: ALWAYS delegate work to specialists using the task tool. Never perform file edits, code changes, or direct execution yourself.",
|
|
141
158
|
"Delegation protocol: require task/outcome/tools/must-do/must-not/context in every order; reject vague asks.",
|
|
142
|
-
"Parallel exploration: launch explore/librarian tasks concurrently for non-trivial discovery, and avoid duplicate searches.",
|
|
143
159
|
"Verification gate: delegate verification to Quartermaster; ensure diagnostics/tests run and evidence is logged before marking complete.",
|
|
160
|
+
"Conflict Resolution: if specialists disagree on implementation, make the final call based on project documentation.",
|
|
144
161
|
"Knowledge discipline: require crew to read/update docs/knowledge (project map, glossary, relevant ADRs) for any decision or scope change.",
|
|
145
162
|
"Ensure every material decision is captured as an ADR in docs/knowledge/decisions."
|
|
146
163
|
],
|
|
147
|
-
tools: ["read", "logbook", "delegate"]
|
|
164
|
+
tools: ["read", "logbook", "delegate", "skill"]
|
|
148
165
|
});
|
|
149
166
|
|
|
150
167
|
// src/crew/Navigator.ts
|
|
@@ -154,19 +171,26 @@ var Navigator = new OpCrewAgent({
|
|
|
154
171
|
role: "Planner",
|
|
155
172
|
mode: "subagent",
|
|
156
173
|
instructions: [
|
|
174
|
+
"TOOL BOUNDARY: You are ONLY permitted to use these tools: read, websearch, logbook, todo, skill. Using any other tool is a violation.",
|
|
157
175
|
"Chart the implementation path based on the Captain's intent and constraints.",
|
|
158
176
|
"Decompose work into atomic, ordered tasks with clear inputs, outputs, and ownership.",
|
|
177
|
+
"Workstream Decomposition: break the plan into atomic, parallel tracks. Define File Ownership (e.g., Boatswain-A owns src/, Boatswain-B owns tests/).",
|
|
178
|
+
"Survey the Stack: identify the project's native build system (BUILD_CMD), test runner (TEST_CMD), and style guide immediately.",
|
|
179
|
+
"Documentation Maintenance: plan for updates to docs/project-map.md and docs/glossary.md; assign these to a specific workstream.",
|
|
180
|
+
"Dependency Graphing: flag Blocking Tasks where one workstream depends on the output of another; sequence these to avoid idle agents.",
|
|
181
|
+
"Execution Manifest: for every task, specify Target File, Affected Symbols, Invariants, and the exact Verification Command.",
|
|
159
182
|
"Record the plan in the logbook with file targets, dependencies, and acceptance checks.",
|
|
160
183
|
"Maintain a current project map (key files, modules, and ownership boundaries).",
|
|
161
184
|
"Flag ambiguity early, propose alternatives, and surface risks before execution starts.",
|
|
162
185
|
"Apply the intent gate: classify the request and select the correct workflow (explore vs. implement vs. clarify).",
|
|
163
186
|
"Create and manage todo/task lists for any multi-step plan; enforce one in-progress item at a time.",
|
|
164
187
|
"Mandate parallel discovery for unknowns: launch multiple explore/librarian threads and consolidate findings.",
|
|
188
|
+
"Discovery Primitives: use language-agnostic tools (grep, find, tree) to map dependencies before proposing edits.",
|
|
165
189
|
"Delegate external research to Scout when you need information from outside the project: libraries, APIs, best practices, or documentation.",
|
|
166
190
|
"Define verification steps explicitly (diagnostics/tests/build) and include success evidence in the plan.",
|
|
167
191
|
"Consult docs/knowledge before planning; update project-map/glossary/ADRs when scope or definitions change."
|
|
168
192
|
],
|
|
169
|
-
tools: ["read", "websearch", "logbook", "todo"]
|
|
193
|
+
tools: ["read", "websearch", "logbook", "todo", "skill"]
|
|
170
194
|
});
|
|
171
195
|
|
|
172
196
|
// src/crew/Boatswain.ts
|
|
@@ -176,17 +200,20 @@ var Boatswain = new OpCrewAgent({
|
|
|
176
200
|
role: "Executor",
|
|
177
201
|
mode: "subagent",
|
|
178
202
|
instructions: [
|
|
179
|
-
"
|
|
203
|
+
"TOOL BOUNDARY: You are ONLY permitted to use these tools: read, edit, write, test, todo, glob, grep, skill. Using any other tool is a violation.",
|
|
204
|
+
"Scope Guarding: ONLY touch files assigned to your Workstream ID. If you must edit a file owned by another agent, escalate to Captain for a plan revision.",
|
|
180
205
|
"Follow existing patterns, naming, and formatting; avoid speculative changes.",
|
|
206
|
+
"Context Anchoring: before editing, read the surrounding code to match indentation, casing, and comment styles. Maintain the project's local laws.",
|
|
207
|
+
"Parallel Execution: implement the Navigator's Manifest exactly. Update code, tests, and documentation (docs/project-map.md or docs/glossary.md) as assigned.",
|
|
181
208
|
"Keep diffs lean and standards-compliant; update only what the plan calls for.",
|
|
182
|
-
"
|
|
183
|
-
"Log completion status, assumptions, and blockers immediately for the Captain.",
|
|
209
|
+
"Atomic Verification: run the assigned TEST_CMD for your scope. Record stdout/stderr logs in the logbook immediately.",
|
|
184
210
|
"Do not re-run delegated exploration; wait for Navigator findings before implementing dependent changes.",
|
|
185
|
-
"
|
|
211
|
+
"LSP/Compiler Check: use native diagnostics to ensure no syntax errors were introduced in touched files.",
|
|
186
212
|
"Update docs/knowledge when execution changes assumptions, definitions, or operational steps.",
|
|
187
|
-
"Update todo task status immediately upon completion of each task."
|
|
213
|
+
"Update todo task status immediately upon completion of each task.",
|
|
214
|
+
"Status Discipline: update your specific task status in logbook.json as you work; do not wait for other workstreams to finish."
|
|
188
215
|
],
|
|
189
|
-
tools: ["read", "edit", "test", "todo"]
|
|
216
|
+
tools: ["read", "edit", "write", "test", "todo", "glob", "grep", "skill"]
|
|
190
217
|
});
|
|
191
218
|
|
|
192
219
|
// src/crew/Quartermaster.ts
|
|
@@ -196,16 +223,20 @@ var Quartermaster = new OpCrewAgent({
|
|
|
196
223
|
role: "Reviewer",
|
|
197
224
|
mode: "subagent",
|
|
198
225
|
instructions: [
|
|
226
|
+
"TOOL BOUNDARY: You are ONLY permitted to use these tools: read, grep, execute, write, skill. Using any other tool is a violation.",
|
|
199
227
|
"Inspect each diff for regression risks, scope drift, and missing context.",
|
|
200
|
-
"
|
|
201
|
-
"
|
|
228
|
+
"Integration Audit: once all parallel Boatswains report Complete, verify the combined diff for logic conflicts or signature mismatches.",
|
|
229
|
+
"Documentation Gate: REJECT approval if code changed but docs/project-map.md or docs/glossary.md were not updated to reflect the new state.",
|
|
230
|
+
"Evidence Verification: ensure every workstream provided terminal logs proving their respective tests or build commands passed.",
|
|
231
|
+
"Traceability: confirm the final implementation matches the Navigator's original Invariants.",
|
|
202
232
|
"Escalate unresolved quality gaps to the Captain before approval.",
|
|
203
233
|
"Require rework when verification is missing or conventions are violated.",
|
|
204
234
|
"Reject approvals lacking evidence (diagnostics/tests/build logs) or missing todo/task tracking.",
|
|
235
|
+
"Regression Check: require a full project TEST_CMD run (if available) to ensure integrated workstreams didn't break the system.",
|
|
205
236
|
"Verify delegation protocol compliance when specialists were involved (task/outcome/tools/must-do/must-not/context).",
|
|
206
237
|
"Reject approval if docs/knowledge changes are missing for material decisions or scope shifts."
|
|
207
238
|
],
|
|
208
|
-
tools: ["read", "grep", "execute", "write"]
|
|
239
|
+
tools: ["read", "grep", "execute", "write", "skill"]
|
|
209
240
|
});
|
|
210
241
|
|
|
211
242
|
// src/crew/Scout.ts
|
|
@@ -215,18 +246,22 @@ var Scout = new OpCrewAgent({
|
|
|
215
246
|
role: "Researcher",
|
|
216
247
|
mode: "subagent",
|
|
217
248
|
instructions: [
|
|
249
|
+
"TOOL BOUNDARY: You are ONLY permitted to use these tools: websearch, webfetch, read, skill. Using any other tool is a violation.",
|
|
218
250
|
"You are the crew's eyes to the outside world - research and gather information when summoned by Captain or Navigator.",
|
|
219
251
|
"Focus exclusively on external research: web searches, documentation lookup, API references, and best practices.",
|
|
220
252
|
"Never modify project files - your job is to gather intelligence, not execute changes.",
|
|
221
253
|
"Provide concise, actionable findings with clear sources and citations.",
|
|
254
|
+
"Rosetta Stone Summaries: when researching a new language, explain concepts via analogy - 'This is the [Go] equivalent of [Python's] Decorators.'",
|
|
222
255
|
"When researching libraries or frameworks, include: version info, installation methods, key APIs, and gotchas.",
|
|
223
256
|
"Compare alternatives when relevant: pros/cons, trade-offs, and recommendations with rationale.",
|
|
224
257
|
"Flag deprecated or outdated information; always verify against official sources when possible.",
|
|
225
258
|
"Summarize findings in a structured format: problem, findings, recommendations, sources.",
|
|
259
|
+
"Glossary Contribution: propose new terms or external definitions for the Boatswain to add to docs/glossary.md.",
|
|
226
260
|
"If research reveals the task is more complex than expected, escalate to Navigator for replanning.",
|
|
227
|
-
"
|
|
261
|
+
"Environment Discovery: research specific error messages or build tool quirks if the Boatswain hits a blocker.",
|
|
262
|
+
"Knowledge Caching: store research summaries in docs/research/ for future crew reference."
|
|
228
263
|
],
|
|
229
|
-
tools: ["websearch", "webfetch", "read"]
|
|
264
|
+
tools: ["websearch", "webfetch", "read", "skill"]
|
|
230
265
|
});
|
|
231
266
|
|
|
232
267
|
// src/crew/index.ts
|
|
@@ -238,10 +273,66 @@ var crew = [
|
|
|
238
273
|
Scout
|
|
239
274
|
];
|
|
240
275
|
|
|
276
|
+
// src/skills.ts
|
|
277
|
+
import { readdir, readFile } from "fs/promises";
|
|
278
|
+
import path from "path";
|
|
279
|
+
import { fileURLToPath } from "url";
|
|
280
|
+
var __dirname2 = path.dirname(fileURLToPath(import.meta.url));
|
|
281
|
+
async function loadSkills() {
|
|
282
|
+
const skillsDir = path.join(__dirname2, "skills");
|
|
283
|
+
try {
|
|
284
|
+
const entries = await readdir(skillsDir, { withFileTypes: true });
|
|
285
|
+
const skills = [];
|
|
286
|
+
for (const entry of entries) {
|
|
287
|
+
if (!entry.isDirectory())
|
|
288
|
+
continue;
|
|
289
|
+
const skillPath = path.join(skillsDir, entry.name);
|
|
290
|
+
const skillMdPath = path.join(skillPath, "SKILL.md");
|
|
291
|
+
try {
|
|
292
|
+
const content = await readFile(skillMdPath, "utf-8");
|
|
293
|
+
const { name, description } = parseSkillFrontmatter(content);
|
|
294
|
+
skills.push({
|
|
295
|
+
name: name || entry.name,
|
|
296
|
+
description: description || "",
|
|
297
|
+
sourcePath: skillPath
|
|
298
|
+
});
|
|
299
|
+
} catch {
|
|
300
|
+
console.warn(`Warning: No SKILL.md found in ${skillPath}`);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
return skills;
|
|
304
|
+
} catch {
|
|
305
|
+
return [];
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
function parseSkillFrontmatter(content) {
|
|
309
|
+
const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---/);
|
|
310
|
+
if (!frontmatterMatch) {
|
|
311
|
+
return {};
|
|
312
|
+
}
|
|
313
|
+
const frontmatter = frontmatterMatch[1];
|
|
314
|
+
const result = {};
|
|
315
|
+
const nameMatch = frontmatter.match(/^name:\s*(.+)$/m);
|
|
316
|
+
if (nameMatch) {
|
|
317
|
+
result.name = nameMatch[1].trim();
|
|
318
|
+
}
|
|
319
|
+
const descMatch = frontmatter.match(/^description:\s*(.+)$/m);
|
|
320
|
+
if (descMatch) {
|
|
321
|
+
result.description = descMatch[1].trim();
|
|
322
|
+
}
|
|
323
|
+
return result;
|
|
324
|
+
}
|
|
325
|
+
async function getSkillContent(skillName) {
|
|
326
|
+
const skillMdPath = path.join(__dirname2, "skills", skillName, "SKILL.md");
|
|
327
|
+
return readFile(skillMdPath, "utf-8");
|
|
328
|
+
}
|
|
329
|
+
|
|
241
330
|
// src/cli/install.ts
|
|
242
|
-
var
|
|
243
|
-
var
|
|
244
|
-
var
|
|
331
|
+
var CLAUDE_AGENTS_DIR = path2.join(".claude", "agents");
|
|
332
|
+
var CLAUDE_SKILLS_DIR = path2.join(".claude", "skills");
|
|
333
|
+
var OPENCODE_AGENTS_DIR = path2.join(".opencode", "agents");
|
|
334
|
+
var OPENCODE_SKILLS_DIR = path2.join(".opencode", "skills");
|
|
335
|
+
var CODEX_FILE = path2.join(".codex", "instructions.md");
|
|
245
336
|
async function ensureDir(targetDir) {
|
|
246
337
|
await mkdir(targetDir, { recursive: true });
|
|
247
338
|
}
|
|
@@ -250,26 +341,67 @@ async function installToTool(tool) {
|
|
|
250
341
|
await installForCodex();
|
|
251
342
|
return;
|
|
252
343
|
}
|
|
253
|
-
const
|
|
254
|
-
|
|
344
|
+
const agentsDir = tool === "claude" ? CLAUDE_AGENTS_DIR : OPENCODE_AGENTS_DIR;
|
|
345
|
+
const skillsDir = tool === "claude" ? CLAUDE_SKILLS_DIR : OPENCODE_SKILLS_DIR;
|
|
346
|
+
await ensureDir(agentsDir);
|
|
255
347
|
for (const agent of crew) {
|
|
256
348
|
const filename = `${agent.config.name.toLowerCase()}.md`;
|
|
257
|
-
const filePath =
|
|
349
|
+
const filePath = path2.join(agentsDir, filename);
|
|
258
350
|
const content = tool === "opencode" ? agent.compileToOpenCodeMarkdown() : agent.compileToMarkdown();
|
|
259
351
|
await writeFile(filePath, content);
|
|
260
|
-
console.log(`Synced ${agent.config.name} to ${
|
|
352
|
+
console.log(`Synced ${agent.config.name} to ${agentsDir}`);
|
|
353
|
+
}
|
|
354
|
+
await installSkills(skillsDir, tool);
|
|
355
|
+
}
|
|
356
|
+
async function installSkills(targetDir, tool) {
|
|
357
|
+
const skills = await loadSkills();
|
|
358
|
+
if (skills.length === 0) {
|
|
359
|
+
console.log("No skills found to install.");
|
|
360
|
+
return;
|
|
361
|
+
}
|
|
362
|
+
await ensureDir(targetDir);
|
|
363
|
+
for (const skill of skills) {
|
|
364
|
+
const skillDestDir = path2.join(targetDir, skill.name);
|
|
365
|
+
await rm(skillDestDir, { recursive: true, force: true });
|
|
366
|
+
await cp(skill.sourcePath, skillDestDir, { recursive: true });
|
|
367
|
+
console.log(`Synced skill ${skill.name} to ${targetDir}`);
|
|
261
368
|
}
|
|
262
369
|
}
|
|
263
370
|
async function installForCodex() {
|
|
264
|
-
const codexDir =
|
|
371
|
+
const codexDir = path2.dirname(CODEX_FILE);
|
|
265
372
|
await ensureDir(codexDir);
|
|
266
|
-
const
|
|
373
|
+
const agentsContent = crew.map((agent) => agent.compileToCodexMarkdown()).join(`
|
|
374
|
+
|
|
375
|
+
---
|
|
376
|
+
|
|
377
|
+
`);
|
|
378
|
+
const skills = await loadSkills();
|
|
379
|
+
let skillsContent = "";
|
|
380
|
+
if (skills.length > 0) {
|
|
381
|
+
const skillContents = await Promise.all(skills.map(async (skill) => {
|
|
382
|
+
const content = await getSkillContent(skill.name);
|
|
383
|
+
return `## Skill: ${skill.name}
|
|
384
|
+
|
|
385
|
+
${content}`;
|
|
386
|
+
}));
|
|
387
|
+
skillsContent = `
|
|
388
|
+
|
|
389
|
+
---
|
|
390
|
+
|
|
391
|
+
# Skills
|
|
392
|
+
|
|
393
|
+
` + skillContents.join(`
|
|
267
394
|
|
|
268
395
|
---
|
|
269
396
|
|
|
270
397
|
`);
|
|
271
|
-
|
|
398
|
+
}
|
|
399
|
+
const fullContent = agentsContent + skillsContent;
|
|
400
|
+
await writeFile(CODEX_FILE, fullContent);
|
|
272
401
|
console.log(`Synced Codex instructions to ${CODEX_FILE}`);
|
|
402
|
+
if (skills.length > 0) {
|
|
403
|
+
console.log(`Synced ${skills.length} skill(s) to Codex instructions`);
|
|
404
|
+
}
|
|
273
405
|
}
|
|
274
406
|
|
|
275
407
|
// src/cli/index.ts
|
package/dist/opencode-plugin.js
CHANGED
|
@@ -26,7 +26,7 @@ var CLAUDE_TOOLS = {
|
|
|
26
26
|
delegate: "Agent",
|
|
27
27
|
todo: "TodoWrite",
|
|
28
28
|
test: "Bash",
|
|
29
|
-
logbook: "
|
|
29
|
+
logbook: "",
|
|
30
30
|
skill: "Skill"
|
|
31
31
|
};
|
|
32
32
|
var OPENCODE_TOOLS = {
|
|
@@ -67,7 +67,7 @@ function translateTools(canonicalTools, platform) {
|
|
|
67
67
|
};
|
|
68
68
|
const translationMap = maps[platform];
|
|
69
69
|
const translated = canonicalTools.map((tool) => translationMap[tool]);
|
|
70
|
-
return [...new Set(translated)];
|
|
70
|
+
return [...new Set(translated)].filter((tool) => tool !== "");
|
|
71
71
|
}
|
|
72
72
|
// src/core/Agent.ts
|
|
73
73
|
class OpCrewAgent {
|
|
@@ -75,8 +75,24 @@ class OpCrewAgent {
|
|
|
75
75
|
constructor(config) {
|
|
76
76
|
this.config = config;
|
|
77
77
|
}
|
|
78
|
+
getAllowedTools() {
|
|
79
|
+
return this.config.allowedTools ?? this.config.tools ?? [];
|
|
80
|
+
}
|
|
81
|
+
hasTool(tool) {
|
|
82
|
+
return this.getAllowedTools().includes(tool);
|
|
83
|
+
}
|
|
84
|
+
validateToolUsage(tool) {
|
|
85
|
+
if (!this.hasTool(tool)) {
|
|
86
|
+
const allowedList = this.getAllowedTools().join(", ") || "(none)";
|
|
87
|
+
throw new Error(`Tool '${tool}' is not allowed for agent '${this.config.name}'. ` + `Allowed tools: [${allowedList}]`);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
getForbiddenTools(allAvailableTools) {
|
|
91
|
+
const allowedSet = new Set(this.getAllowedTools());
|
|
92
|
+
return allAvailableTools.filter((tool) => !allowedSet.has(tool));
|
|
93
|
+
}
|
|
78
94
|
compileToMarkdown() {
|
|
79
|
-
const translatedTools = translateTools(this.
|
|
95
|
+
const translatedTools = translateTools(this.getAllowedTools(), "claude");
|
|
80
96
|
const lowerName = this.config.name.toLowerCase();
|
|
81
97
|
const frontmatter = `---
|
|
82
98
|
name: ${lowerName}
|
|
@@ -91,7 +107,7 @@ ${this.buildMarkdownBody()}`;
|
|
|
91
107
|
Orchestrator: { edit: "deny", bash: "ask", webfetch: "deny" },
|
|
92
108
|
Planner: { edit: "deny", bash: "ask", webfetch: "allow" },
|
|
93
109
|
Executor: { edit: "allow", bash: "allow", webfetch: "ask" },
|
|
94
|
-
Reviewer: { edit: "deny", bash: "
|
|
110
|
+
Reviewer: { edit: "deny", bash: "allow", webfetch: "deny" },
|
|
95
111
|
Researcher: { edit: "deny", bash: "deny", webfetch: "allow" }
|
|
96
112
|
};
|
|
97
113
|
const permission = permissionByRole[this.config.role];
|
|
@@ -107,7 +123,7 @@ permission:
|
|
|
107
123
|
${this.buildMarkdownBody()}`;
|
|
108
124
|
}
|
|
109
125
|
compileToCodexMarkdown() {
|
|
110
|
-
const translatedTools = translateTools(this.
|
|
126
|
+
const translatedTools = translateTools(this.getAllowedTools(), "codex");
|
|
111
127
|
const frontmatter = `---
|
|
112
128
|
name: ${this.config.name}
|
|
113
129
|
role: ${this.config.role}
|
|
@@ -136,21 +152,22 @@ var Captain = new OpCrewAgent({
|
|
|
136
152
|
role: "Orchestrator",
|
|
137
153
|
mode: "primary",
|
|
138
154
|
instructions: [
|
|
155
|
+
"TOOL BOUNDARY: You are ONLY permitted to use these tools: read, logbook, delegate, skill. Using any other tool is a violation.",
|
|
139
156
|
"You are the orchestrator - you NEVER execute work directly. Your role is to coordinate, delegate, and verify.",
|
|
140
|
-
"
|
|
157
|
+
"Workstream Fan-out: divide missions into independent Workstreams (e.g., Logic, Testing, Documentation) to enable parallel specialists.",
|
|
141
158
|
"Assign roles in order: Navigator plans, Boatswain executes, Quartermaster reviews; resolve conflicts quickly.",
|
|
142
|
-
"
|
|
159
|
+
"Collision Prevention: ensure no two Boatswains are assigned the same file or symbol in parallel. Launch workstreams only if they are logically decoupled.",
|
|
143
160
|
"Escalate blockers immediately and re-sequence work to unblock the critical path.",
|
|
144
161
|
"Enforce conventions: small, reviewable diffs; no scope creep; verification required before declaring done.",
|
|
145
162
|
"Intent gate: restate user intent, classify task type (trivial/explicit/exploratory/open-ended/ambiguous), and choose the workflow before action.",
|
|
146
163
|
"Delegation mandate: ALWAYS delegate work to specialists using the task tool. Never perform file edits, code changes, or direct execution yourself.",
|
|
147
164
|
"Delegation protocol: require task/outcome/tools/must-do/must-not/context in every order; reject vague asks.",
|
|
148
|
-
"Parallel exploration: launch explore/librarian tasks concurrently for non-trivial discovery, and avoid duplicate searches.",
|
|
149
165
|
"Verification gate: delegate verification to Quartermaster; ensure diagnostics/tests run and evidence is logged before marking complete.",
|
|
166
|
+
"Conflict Resolution: if specialists disagree on implementation, make the final call based on project documentation.",
|
|
150
167
|
"Knowledge discipline: require crew to read/update docs/knowledge (project map, glossary, relevant ADRs) for any decision or scope change.",
|
|
151
168
|
"Ensure every material decision is captured as an ADR in docs/knowledge/decisions."
|
|
152
169
|
],
|
|
153
|
-
tools: ["read", "logbook", "delegate"]
|
|
170
|
+
tools: ["read", "logbook", "delegate", "skill"]
|
|
154
171
|
});
|
|
155
172
|
|
|
156
173
|
// src/crew/Navigator.ts
|
|
@@ -160,19 +177,26 @@ var Navigator = new OpCrewAgent({
|
|
|
160
177
|
role: "Planner",
|
|
161
178
|
mode: "subagent",
|
|
162
179
|
instructions: [
|
|
180
|
+
"TOOL BOUNDARY: You are ONLY permitted to use these tools: read, websearch, logbook, todo, skill. Using any other tool is a violation.",
|
|
163
181
|
"Chart the implementation path based on the Captain's intent and constraints.",
|
|
164
182
|
"Decompose work into atomic, ordered tasks with clear inputs, outputs, and ownership.",
|
|
183
|
+
"Workstream Decomposition: break the plan into atomic, parallel tracks. Define File Ownership (e.g., Boatswain-A owns src/, Boatswain-B owns tests/).",
|
|
184
|
+
"Survey the Stack: identify the project's native build system (BUILD_CMD), test runner (TEST_CMD), and style guide immediately.",
|
|
185
|
+
"Documentation Maintenance: plan for updates to docs/project-map.md and docs/glossary.md; assign these to a specific workstream.",
|
|
186
|
+
"Dependency Graphing: flag Blocking Tasks where one workstream depends on the output of another; sequence these to avoid idle agents.",
|
|
187
|
+
"Execution Manifest: for every task, specify Target File, Affected Symbols, Invariants, and the exact Verification Command.",
|
|
165
188
|
"Record the plan in the logbook with file targets, dependencies, and acceptance checks.",
|
|
166
189
|
"Maintain a current project map (key files, modules, and ownership boundaries).",
|
|
167
190
|
"Flag ambiguity early, propose alternatives, and surface risks before execution starts.",
|
|
168
191
|
"Apply the intent gate: classify the request and select the correct workflow (explore vs. implement vs. clarify).",
|
|
169
192
|
"Create and manage todo/task lists for any multi-step plan; enforce one in-progress item at a time.",
|
|
170
193
|
"Mandate parallel discovery for unknowns: launch multiple explore/librarian threads and consolidate findings.",
|
|
194
|
+
"Discovery Primitives: use language-agnostic tools (grep, find, tree) to map dependencies before proposing edits.",
|
|
171
195
|
"Delegate external research to Scout when you need information from outside the project: libraries, APIs, best practices, or documentation.",
|
|
172
196
|
"Define verification steps explicitly (diagnostics/tests/build) and include success evidence in the plan.",
|
|
173
197
|
"Consult docs/knowledge before planning; update project-map/glossary/ADRs when scope or definitions change."
|
|
174
198
|
],
|
|
175
|
-
tools: ["read", "websearch", "logbook", "todo"]
|
|
199
|
+
tools: ["read", "websearch", "logbook", "todo", "skill"]
|
|
176
200
|
});
|
|
177
201
|
|
|
178
202
|
// src/crew/Boatswain.ts
|
|
@@ -182,17 +206,20 @@ var Boatswain = new OpCrewAgent({
|
|
|
182
206
|
role: "Executor",
|
|
183
207
|
mode: "subagent",
|
|
184
208
|
instructions: [
|
|
185
|
-
"
|
|
209
|
+
"TOOL BOUNDARY: You are ONLY permitted to use these tools: read, edit, write, test, todo, glob, grep, skill. Using any other tool is a violation.",
|
|
210
|
+
"Scope Guarding: ONLY touch files assigned to your Workstream ID. If you must edit a file owned by another agent, escalate to Captain for a plan revision.",
|
|
186
211
|
"Follow existing patterns, naming, and formatting; avoid speculative changes.",
|
|
212
|
+
"Context Anchoring: before editing, read the surrounding code to match indentation, casing, and comment styles. Maintain the project's local laws.",
|
|
213
|
+
"Parallel Execution: implement the Navigator's Manifest exactly. Update code, tests, and documentation (docs/project-map.md or docs/glossary.md) as assigned.",
|
|
187
214
|
"Keep diffs lean and standards-compliant; update only what the plan calls for.",
|
|
188
|
-
"
|
|
189
|
-
"Log completion status, assumptions, and blockers immediately for the Captain.",
|
|
215
|
+
"Atomic Verification: run the assigned TEST_CMD for your scope. Record stdout/stderr logs in the logbook immediately.",
|
|
190
216
|
"Do not re-run delegated exploration; wait for Navigator findings before implementing dependent changes.",
|
|
191
|
-
"
|
|
217
|
+
"LSP/Compiler Check: use native diagnostics to ensure no syntax errors were introduced in touched files.",
|
|
192
218
|
"Update docs/knowledge when execution changes assumptions, definitions, or operational steps.",
|
|
193
|
-
"Update todo task status immediately upon completion of each task."
|
|
219
|
+
"Update todo task status immediately upon completion of each task.",
|
|
220
|
+
"Status Discipline: update your specific task status in logbook.json as you work; do not wait for other workstreams to finish."
|
|
194
221
|
],
|
|
195
|
-
tools: ["read", "edit", "test", "todo"]
|
|
222
|
+
tools: ["read", "edit", "write", "test", "todo", "glob", "grep", "skill"]
|
|
196
223
|
});
|
|
197
224
|
|
|
198
225
|
// src/crew/Quartermaster.ts
|
|
@@ -202,16 +229,20 @@ var Quartermaster = new OpCrewAgent({
|
|
|
202
229
|
role: "Reviewer",
|
|
203
230
|
mode: "subagent",
|
|
204
231
|
instructions: [
|
|
232
|
+
"TOOL BOUNDARY: You are ONLY permitted to use these tools: read, grep, execute, write, skill. Using any other tool is a violation.",
|
|
205
233
|
"Inspect each diff for regression risks, scope drift, and missing context.",
|
|
206
|
-
"
|
|
207
|
-
"
|
|
234
|
+
"Integration Audit: once all parallel Boatswains report Complete, verify the combined diff for logic conflicts or signature mismatches.",
|
|
235
|
+
"Documentation Gate: REJECT approval if code changed but docs/project-map.md or docs/glossary.md were not updated to reflect the new state.",
|
|
236
|
+
"Evidence Verification: ensure every workstream provided terminal logs proving their respective tests or build commands passed.",
|
|
237
|
+
"Traceability: confirm the final implementation matches the Navigator's original Invariants.",
|
|
208
238
|
"Escalate unresolved quality gaps to the Captain before approval.",
|
|
209
239
|
"Require rework when verification is missing or conventions are violated.",
|
|
210
240
|
"Reject approvals lacking evidence (diagnostics/tests/build logs) or missing todo/task tracking.",
|
|
241
|
+
"Regression Check: require a full project TEST_CMD run (if available) to ensure integrated workstreams didn't break the system.",
|
|
211
242
|
"Verify delegation protocol compliance when specialists were involved (task/outcome/tools/must-do/must-not/context).",
|
|
212
243
|
"Reject approval if docs/knowledge changes are missing for material decisions or scope shifts."
|
|
213
244
|
],
|
|
214
|
-
tools: ["read", "grep", "execute", "write"]
|
|
245
|
+
tools: ["read", "grep", "execute", "write", "skill"]
|
|
215
246
|
});
|
|
216
247
|
|
|
217
248
|
// src/crew/Scout.ts
|
|
@@ -221,18 +252,22 @@ var Scout = new OpCrewAgent({
|
|
|
221
252
|
role: "Researcher",
|
|
222
253
|
mode: "subagent",
|
|
223
254
|
instructions: [
|
|
255
|
+
"TOOL BOUNDARY: You are ONLY permitted to use these tools: websearch, webfetch, read, skill. Using any other tool is a violation.",
|
|
224
256
|
"You are the crew's eyes to the outside world - research and gather information when summoned by Captain or Navigator.",
|
|
225
257
|
"Focus exclusively on external research: web searches, documentation lookup, API references, and best practices.",
|
|
226
258
|
"Never modify project files - your job is to gather intelligence, not execute changes.",
|
|
227
259
|
"Provide concise, actionable findings with clear sources and citations.",
|
|
260
|
+
"Rosetta Stone Summaries: when researching a new language, explain concepts via analogy - 'This is the [Go] equivalent of [Python's] Decorators.'",
|
|
228
261
|
"When researching libraries or frameworks, include: version info, installation methods, key APIs, and gotchas.",
|
|
229
262
|
"Compare alternatives when relevant: pros/cons, trade-offs, and recommendations with rationale.",
|
|
230
263
|
"Flag deprecated or outdated information; always verify against official sources when possible.",
|
|
231
264
|
"Summarize findings in a structured format: problem, findings, recommendations, sources.",
|
|
265
|
+
"Glossary Contribution: propose new terms or external definitions for the Boatswain to add to docs/glossary.md.",
|
|
232
266
|
"If research reveals the task is more complex than expected, escalate to Navigator for replanning.",
|
|
233
|
-
"
|
|
267
|
+
"Environment Discovery: research specific error messages or build tool quirks if the Boatswain hits a blocker.",
|
|
268
|
+
"Knowledge Caching: store research summaries in docs/research/ for future crew reference."
|
|
234
269
|
],
|
|
235
|
-
tools: ["websearch", "webfetch", "read"]
|
|
270
|
+
tools: ["websearch", "webfetch", "read", "skill"]
|
|
236
271
|
});
|
|
237
272
|
|
|
238
273
|
// src/crew/index.ts
|
|
@@ -12686,65 +12721,128 @@ The logbook tracks:
|
|
|
12686
12721
|
});
|
|
12687
12722
|
|
|
12688
12723
|
// src/opencode-plugin.ts
|
|
12724
|
+
var DEFAULT_CANONICAL_TOOLS = [
|
|
12725
|
+
"read",
|
|
12726
|
+
"write",
|
|
12727
|
+
"edit",
|
|
12728
|
+
"glob",
|
|
12729
|
+
"grep",
|
|
12730
|
+
"execute",
|
|
12731
|
+
"websearch",
|
|
12732
|
+
"webfetch",
|
|
12733
|
+
"delegate",
|
|
12734
|
+
"todo",
|
|
12735
|
+
"test",
|
|
12736
|
+
"logbook",
|
|
12737
|
+
"skill"
|
|
12738
|
+
];
|
|
12739
|
+
var ALL_AVAILABLE_TOOLS = DEFAULT_CANONICAL_TOOLS;
|
|
12689
12740
|
var permissionByRole = {
|
|
12690
12741
|
Orchestrator: { edit: "deny", bash: "ask", webfetch: "deny" },
|
|
12691
12742
|
Planner: { edit: "deny", bash: "ask", webfetch: "allow" },
|
|
12692
12743
|
Executor: { edit: "allow", bash: "allow", webfetch: "ask" },
|
|
12693
|
-
Reviewer: { edit: "deny", bash: "
|
|
12744
|
+
Reviewer: { edit: "deny", bash: "allow", webfetch: "deny" },
|
|
12694
12745
|
Researcher: { edit: "deny", bash: "deny", webfetch: "allow" }
|
|
12695
12746
|
};
|
|
12696
12747
|
function toAgentId(agent) {
|
|
12697
12748
|
return agent.config.name.toLowerCase().replace(/\s+/g, "-");
|
|
12698
12749
|
}
|
|
12699
|
-
function buildPrompt(agent) {
|
|
12750
|
+
function buildPrompt(agent, availableTools) {
|
|
12700
12751
|
const instructionsList = agent.config.instructions.map((instruction) => `- ${instruction}`).join(`
|
|
12701
12752
|
`);
|
|
12753
|
+
const allowedTools = agent.getAllowedTools();
|
|
12754
|
+
const forbiddenTools = agent.getForbiddenTools(availableTools);
|
|
12755
|
+
const toolBoundariesSection = `## TOOL BOUNDARIES
|
|
12756
|
+
- **Allowed:** ${allowedTools.length > 0 ? allowedTools.join(", ") : "(none)"}
|
|
12757
|
+
- **Forbidden:** ${forbiddenTools.length > 0 ? forbiddenTools.join(", ") : "(none)"}`;
|
|
12702
12758
|
return `# ${agent.config.name}
|
|
12759
|
+
|
|
12760
|
+
${toolBoundariesSection}
|
|
12761
|
+
|
|
12703
12762
|
${instructionsList}
|
|
12704
12763
|
|
|
12705
12764
|
## Shared Context
|
|
12706
12765
|
Refer to \`.opcrew/logbook.json\` for the current voyage status.
|
|
12707
12766
|
`;
|
|
12708
12767
|
}
|
|
12709
|
-
function toOpenCodeAgentConfig(agent) {
|
|
12768
|
+
function toOpenCodeAgentConfig(agent, availableTools) {
|
|
12769
|
+
const allowedTools = agent.getAllowedTools();
|
|
12770
|
+
const validTools = allowedTools.every((tool3) => availableTools.includes(tool3));
|
|
12771
|
+
if (!validTools) {
|
|
12772
|
+
const invalidTools = allowedTools.filter((tool3) => !availableTools.includes(tool3));
|
|
12773
|
+
throw new Error(`Agent '${agent.config.name}' has invalid tools in allowlist: [${invalidTools.join(", ")}]. ` + `Valid tools: [${availableTools.join(", ")}]`);
|
|
12774
|
+
}
|
|
12710
12775
|
return {
|
|
12711
12776
|
description: agent.config.name,
|
|
12712
12777
|
mode: agent.config.mode,
|
|
12713
|
-
prompt: buildPrompt(agent),
|
|
12778
|
+
prompt: buildPrompt(agent, availableTools),
|
|
12714
12779
|
permission: permissionByRole[agent.config.role]
|
|
12715
12780
|
};
|
|
12716
12781
|
}
|
|
12717
|
-
|
|
12718
|
-
|
|
12719
|
-
|
|
12720
|
-
|
|
12721
|
-
|
|
12722
|
-
|
|
12723
|
-
|
|
12724
|
-
}
|
|
12725
|
-
|
|
12726
|
-
|
|
12727
|
-
|
|
12728
|
-
|
|
12729
|
-
|
|
12730
|
-
|
|
12731
|
-
|
|
12732
|
-
|
|
12733
|
-
|
|
12734
|
-
|
|
12735
|
-
|
|
12736
|
-
|
|
12737
|
-
|
|
12738
|
-
|
|
12739
|
-
|
|
12740
|
-
|
|
12741
|
-
|
|
12742
|
-
|
|
12743
|
-
|
|
12782
|
+
function createOpCrewPlugin(options = {}) {
|
|
12783
|
+
const {
|
|
12784
|
+
tools: customTools = {},
|
|
12785
|
+
skills,
|
|
12786
|
+
availableTools = DEFAULT_CANONICAL_TOOLS,
|
|
12787
|
+
includeDefaultTools = true,
|
|
12788
|
+
crew: customCrew = crew
|
|
12789
|
+
} = options;
|
|
12790
|
+
const defaultTools = includeDefaultTools ? { edit_logbook: editLogbookTool } : {};
|
|
12791
|
+
const mergedTools = {
|
|
12792
|
+
...defaultTools,
|
|
12793
|
+
...customTools
|
|
12794
|
+
};
|
|
12795
|
+
const _skills = skills;
|
|
12796
|
+
return async ({ client }) => {
|
|
12797
|
+
await client.app.log({
|
|
12798
|
+
body: {
|
|
12799
|
+
service: "opcrew",
|
|
12800
|
+
level: "info",
|
|
12801
|
+
message: "OpenCode plugin initialized"
|
|
12802
|
+
}
|
|
12803
|
+
});
|
|
12804
|
+
return {
|
|
12805
|
+
config: async (config2) => {
|
|
12806
|
+
const agents = config2.agent ?? {};
|
|
12807
|
+
agents["build"] = { ...agents["build"], disable: true };
|
|
12808
|
+
for (const agent of customCrew) {
|
|
12809
|
+
const agentId = toAgentId(agent);
|
|
12810
|
+
const allowedTools = agent.getAllowedTools();
|
|
12811
|
+
const forbiddenTools = agent.getForbiddenTools(availableTools);
|
|
12812
|
+
await client.app.log({
|
|
12813
|
+
body: {
|
|
12814
|
+
service: "opcrew",
|
|
12815
|
+
level: "info",
|
|
12816
|
+
message: `Registering agent '${agent.config.name}' with tool boundaries`,
|
|
12817
|
+
extra: {
|
|
12818
|
+
agentId,
|
|
12819
|
+
allowedTools,
|
|
12820
|
+
forbiddenTools
|
|
12821
|
+
}
|
|
12822
|
+
}
|
|
12823
|
+
});
|
|
12824
|
+
agents[agentId] = toOpenCodeAgentConfig(agent, availableTools);
|
|
12825
|
+
}
|
|
12826
|
+
config2.agent = agents;
|
|
12827
|
+
await client.app.log({
|
|
12828
|
+
body: {
|
|
12829
|
+
service: "opcrew",
|
|
12830
|
+
level: "info",
|
|
12831
|
+
message: "Crew agents registered",
|
|
12832
|
+
extra: { agents: Object.keys(agents) }
|
|
12833
|
+
}
|
|
12834
|
+
});
|
|
12835
|
+
},
|
|
12836
|
+
tool: mergedTools
|
|
12837
|
+
};
|
|
12744
12838
|
};
|
|
12745
|
-
}
|
|
12839
|
+
}
|
|
12840
|
+
var OpCrewPlugin = createOpCrewPlugin();
|
|
12746
12841
|
var opencode_plugin_default = OpCrewPlugin;
|
|
12747
12842
|
export {
|
|
12748
12843
|
opencode_plugin_default as default,
|
|
12749
|
-
|
|
12844
|
+
createOpCrewPlugin,
|
|
12845
|
+
OpCrewPlugin,
|
|
12846
|
+
DEFAULT_CANONICAL_TOOLS,
|
|
12847
|
+
ALL_AVAILABLE_TOOLS
|
|
12750
12848
|
};
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: write-adr
|
|
3
|
+
description: Guides the crew to write Architecture Decision Records (ADRs). Use when making material decisions that should be documented, when asked to create an ADR, or when a decision with long-term impact is made.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Write ADR Skill
|
|
7
|
+
|
|
8
|
+
This skill guides the OpCrew to write well-structured Architecture Decision Records (ADRs) following the project's conventions.
|
|
9
|
+
|
|
10
|
+
## When to Use This Skill
|
|
11
|
+
|
|
12
|
+
Use this skill when:
|
|
13
|
+
- Making a material decision that affects the project's architecture, conventions, or workflows
|
|
14
|
+
- A decision has long-term impact and should be preserved for future reference
|
|
15
|
+
- Explicitly asked to create or write an ADR
|
|
16
|
+
- Changing a previous architectural decision
|
|
17
|
+
- Introducing new patterns, tools, or approaches that others should follow
|
|
18
|
+
|
|
19
|
+
## What is an ADR?
|
|
20
|
+
|
|
21
|
+
An Architecture Decision Record (ADR) captures a significant architectural decision along with its context and consequences. ADRs:
|
|
22
|
+
|
|
23
|
+
- Create a decision log that's reviewable in Git
|
|
24
|
+
- Help future team members understand *why* decisions were made
|
|
25
|
+
- Prevent "why did we do this?" questions months later
|
|
26
|
+
- Make decisions explicit and traceable
|
|
27
|
+
|
|
28
|
+
## ADR Structure
|
|
29
|
+
|
|
30
|
+
OpCrew ADRs use four sections:
|
|
31
|
+
|
|
32
|
+
### 1. Status
|
|
33
|
+
The current state of the decision:
|
|
34
|
+
- **Proposed** - Under discussion, not yet finalized
|
|
35
|
+
- **Accepted** - Approved and in effect
|
|
36
|
+
- **Deprecated** - Still in effect but should be avoided for new work
|
|
37
|
+
- **Superseded** - Replaced by a newer ADR (link to it)
|
|
38
|
+
|
|
39
|
+
### 2. Context
|
|
40
|
+
What is the issue or situation motivating this decision?
|
|
41
|
+
- Describe the problem or opportunity
|
|
42
|
+
- Include relevant constraints and requirements
|
|
43
|
+
- Explain why a decision is needed now
|
|
44
|
+
|
|
45
|
+
### 3. Decision
|
|
46
|
+
What is the change being made or proposed?
|
|
47
|
+
- State the decision clearly and concisely
|
|
48
|
+
- Be specific about what is being done
|
|
49
|
+
- Include any alternatives considered (briefly)
|
|
50
|
+
|
|
51
|
+
### 4. Consequences
|
|
52
|
+
What becomes easier or more difficult because of this change?
|
|
53
|
+
- List positive outcomes
|
|
54
|
+
- Acknowledge trade-offs and limitations
|
|
55
|
+
- Note any follow-up actions required
|
|
56
|
+
|
|
57
|
+
## Filename Convention
|
|
58
|
+
|
|
59
|
+
ADRs are stored in `docs/knowledge/decisions/` with this format:
|
|
60
|
+
|
|
61
|
+
```
|
|
62
|
+
YYYYMMDD-NNN-slug.md
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
- `YYYYMMDD` - Date the ADR is created
|
|
66
|
+
- `NNN` - Sequential number (001, 002, 003...)
|
|
67
|
+
- `slug` - Short kebab-case description
|
|
68
|
+
|
|
69
|
+
**Examples:**
|
|
70
|
+
- `20260328-001-knowledge-base-source-of-truth.md`
|
|
71
|
+
- `20260329-002-agent-configuration-standard.md`
|
|
72
|
+
|
|
73
|
+
## Step-by-Step Workflow
|
|
74
|
+
|
|
75
|
+
1. **Identify the decision** - Is this a material decision worth documenting?
|
|
76
|
+
2. **Check existing ADRs** - Look in `docs/knowledge/decisions/` for related decisions
|
|
77
|
+
3. **Determine the next sequence number** - Look at existing files and increment
|
|
78
|
+
4. **Create the file** - Use the template and filename convention
|
|
79
|
+
5. **Write the ADR** - Fill in all four sections with clear, concise content
|
|
80
|
+
6. **Review** - Ensure the ADR captures the full context and rationale
|
|
81
|
+
7. **Commit** - Include the ADR in your commit with related changes
|
|
82
|
+
|
|
83
|
+
## Example ADR
|
|
84
|
+
|
|
85
|
+
```markdown
|
|
86
|
+
# Knowledge Base Source of Truth
|
|
87
|
+
|
|
88
|
+
## Status
|
|
89
|
+
Accepted
|
|
90
|
+
|
|
91
|
+
## Context
|
|
92
|
+
We need a maintainable way to preserve project decisions, conventions, and operational guidance for agents and humans.
|
|
93
|
+
|
|
94
|
+
## Decision
|
|
95
|
+
Use the filesystem as the canonical knowledge base in `docs/knowledge/`. Keep decisions as ADRs, project map and glossary as living docs, and runbooks for operational procedures.
|
|
96
|
+
|
|
97
|
+
## Consequences
|
|
98
|
+
- Decisions become reviewable and diffable in Git.
|
|
99
|
+
- Agents must read/update these docs as part of workflow conventions.
|
|
100
|
+
- A database can be added later only as a derived index, not as the source of truth.
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## Tips for Good ADRs
|
|
104
|
+
|
|
105
|
+
- **Be concise** - ADRs should be readable in 2-3 minutes
|
|
106
|
+
- **Focus on why** - The context and rationale matter more than the details
|
|
107
|
+
- **Write for the future** - Assume the reader doesn't have your current context
|
|
108
|
+
- **Include trade-offs** - Every decision has downsides; acknowledge them
|
|
109
|
+
- **Link related ADRs** - If this supersedes or relates to another ADR, mention it
|
|
110
|
+
- **Keep it current** - Update status if the decision changes
|
|
111
|
+
|
|
112
|
+
## Template Location
|
|
113
|
+
|
|
114
|
+
Use the template at `templates/adr-template.md` as a starting point.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# [Title]
|
|
2
|
+
|
|
3
|
+
## Status
|
|
4
|
+
[Proposed | Accepted | Deprecated | Superseded]
|
|
5
|
+
|
|
6
|
+
## Context
|
|
7
|
+
[What is the issue that we're seeing that is motivating this decision or change?]
|
|
8
|
+
|
|
9
|
+
## Decision
|
|
10
|
+
[What is the change that we're proposing and/or doing?]
|
|
11
|
+
|
|
12
|
+
## Consequences
|
|
13
|
+
[What becomes easier or more difficult to do because of this change?]
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: write-adr
|
|
3
|
+
description: Guides the crew to write Architecture Decision Records (ADRs). Use when making material decisions that should be documented, when asked to create an ADR, or when a decision with long-term impact is made.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Write ADR Skill
|
|
7
|
+
|
|
8
|
+
This skill guides the OpCrew to write well-structured Architecture Decision Records (ADRs) following the project's conventions.
|
|
9
|
+
|
|
10
|
+
## When to Use This Skill
|
|
11
|
+
|
|
12
|
+
Use this skill when:
|
|
13
|
+
- Making a material decision that affects the project's architecture, conventions, or workflows
|
|
14
|
+
- A decision has long-term impact and should be preserved for future reference
|
|
15
|
+
- Explicitly asked to create or write an ADR
|
|
16
|
+
- Changing a previous architectural decision
|
|
17
|
+
- Introducing new patterns, tools, or approaches that others should follow
|
|
18
|
+
|
|
19
|
+
## What is an ADR?
|
|
20
|
+
|
|
21
|
+
An Architecture Decision Record (ADR) captures a significant architectural decision along with its context and consequences. ADRs:
|
|
22
|
+
|
|
23
|
+
- Create a decision log that's reviewable in Git
|
|
24
|
+
- Help future team members understand *why* decisions were made
|
|
25
|
+
- Prevent "why did we do this?" questions months later
|
|
26
|
+
- Make decisions explicit and traceable
|
|
27
|
+
|
|
28
|
+
## ADR Structure
|
|
29
|
+
|
|
30
|
+
OpCrew ADRs use four sections:
|
|
31
|
+
|
|
32
|
+
### 1. Status
|
|
33
|
+
The current state of the decision:
|
|
34
|
+
- **Proposed** - Under discussion, not yet finalized
|
|
35
|
+
- **Accepted** - Approved and in effect
|
|
36
|
+
- **Deprecated** - Still in effect but should be avoided for new work
|
|
37
|
+
- **Superseded** - Replaced by a newer ADR (link to it)
|
|
38
|
+
|
|
39
|
+
### 2. Context
|
|
40
|
+
What is the issue or situation motivating this decision?
|
|
41
|
+
- Describe the problem or opportunity
|
|
42
|
+
- Include relevant constraints and requirements
|
|
43
|
+
- Explain why a decision is needed now
|
|
44
|
+
|
|
45
|
+
### 3. Decision
|
|
46
|
+
What is the change being made or proposed?
|
|
47
|
+
- State the decision clearly and concisely
|
|
48
|
+
- Be specific about what is being done
|
|
49
|
+
- Include any alternatives considered (briefly)
|
|
50
|
+
|
|
51
|
+
### 4. Consequences
|
|
52
|
+
What becomes easier or more difficult because of this change?
|
|
53
|
+
- List positive outcomes
|
|
54
|
+
- Acknowledge trade-offs and limitations
|
|
55
|
+
- Note any follow-up actions required
|
|
56
|
+
|
|
57
|
+
## Filename Convention
|
|
58
|
+
|
|
59
|
+
ADRs are stored in `docs/knowledge/decisions/` with this format:
|
|
60
|
+
|
|
61
|
+
```
|
|
62
|
+
YYYYMMDD-NNN-slug.md
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
- `YYYYMMDD` - Date the ADR is created
|
|
66
|
+
- `NNN` - Sequential number (001, 002, 003...)
|
|
67
|
+
- `slug` - Short kebab-case description
|
|
68
|
+
|
|
69
|
+
**Examples:**
|
|
70
|
+
- `20260328-001-knowledge-base-source-of-truth.md`
|
|
71
|
+
- `20260329-002-agent-configuration-standard.md`
|
|
72
|
+
|
|
73
|
+
## Step-by-Step Workflow
|
|
74
|
+
|
|
75
|
+
1. **Identify the decision** - Is this a material decision worth documenting?
|
|
76
|
+
2. **Check existing ADRs** - Look in `docs/knowledge/decisions/` for related decisions
|
|
77
|
+
3. **Determine the next sequence number** - Look at existing files and increment
|
|
78
|
+
4. **Create the file** - Use the template and filename convention
|
|
79
|
+
5. **Write the ADR** - Fill in all four sections with clear, concise content
|
|
80
|
+
6. **Review** - Ensure the ADR captures the full context and rationale
|
|
81
|
+
7. **Commit** - Include the ADR in your commit with related changes
|
|
82
|
+
|
|
83
|
+
## Example ADR
|
|
84
|
+
|
|
85
|
+
```markdown
|
|
86
|
+
# Knowledge Base Source of Truth
|
|
87
|
+
|
|
88
|
+
## Status
|
|
89
|
+
Accepted
|
|
90
|
+
|
|
91
|
+
## Context
|
|
92
|
+
We need a maintainable way to preserve project decisions, conventions, and operational guidance for agents and humans.
|
|
93
|
+
|
|
94
|
+
## Decision
|
|
95
|
+
Use the filesystem as the canonical knowledge base in `docs/knowledge/`. Keep decisions as ADRs, project map and glossary as living docs, and runbooks for operational procedures.
|
|
96
|
+
|
|
97
|
+
## Consequences
|
|
98
|
+
- Decisions become reviewable and diffable in Git.
|
|
99
|
+
- Agents must read/update these docs as part of workflow conventions.
|
|
100
|
+
- A database can be added later only as a derived index, not as the source of truth.
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## Tips for Good ADRs
|
|
104
|
+
|
|
105
|
+
- **Be concise** - ADRs should be readable in 2-3 minutes
|
|
106
|
+
- **Focus on why** - The context and rationale matter more than the details
|
|
107
|
+
- **Write for the future** - Assume the reader doesn't have your current context
|
|
108
|
+
- **Include trade-offs** - Every decision has downsides; acknowledge them
|
|
109
|
+
- **Link related ADRs** - If this supersedes or relates to another ADR, mention it
|
|
110
|
+
- **Keep it current** - Update status if the decision changes
|
|
111
|
+
|
|
112
|
+
## Template Location
|
|
113
|
+
|
|
114
|
+
Use the template at `templates/adr-template.md` as a starting point.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# [Title]
|
|
2
|
+
|
|
3
|
+
## Status
|
|
4
|
+
[Proposed | Accepted | Deprecated | Superseded]
|
|
5
|
+
|
|
6
|
+
## Context
|
|
7
|
+
[What is the issue that we're seeing that is motivating this decision or change?]
|
|
8
|
+
|
|
9
|
+
## Decision
|
|
10
|
+
[What is the change that we're proposing and/or doing?]
|
|
11
|
+
|
|
12
|
+
## Consequences
|
|
13
|
+
[What becomes easier or more difficult to do because of this change?]
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# Strict Agent Tool Boundaries
|
|
2
|
+
|
|
3
|
+
## Status
|
|
4
|
+
Accepted
|
|
5
|
+
|
|
6
|
+
## Context
|
|
7
|
+
OpenCrew agents currently have tool assignments defined declaratively in their configuration, but these assignments are NOT technically enforced. The existing permission system only covers three operations (edit, bash, webfetch) at the platform level, while tool assignments like `read`, `write`, `delegate`, `logbook`, etc. are purely instructional.
|
|
8
|
+
|
|
9
|
+
This creates several problems:
|
|
10
|
+
1. **Role confusion**: Captain could theoretically use tools meant for Boatswain (like `edit`)
|
|
11
|
+
2. **Accountability gaps**: No verification that agents stay within their designated scope
|
|
12
|
+
3. **Debugging difficulty**: When something goes wrong, it's unclear which agent overstepped
|
|
13
|
+
4. **Security risk**: An agent could access tools it shouldn't have access to
|
|
14
|
+
|
|
15
|
+
The principle "Captain never writes code - that's Boatswain's job" is currently enforced only through instructions, not technical barriers.
|
|
16
|
+
|
|
17
|
+
## Decision
|
|
18
|
+
Implement strict technical enforcement of agent tool boundaries through:
|
|
19
|
+
|
|
20
|
+
1. **Tool Allowlist per Agent**: Each agent definition will include an explicit `allowedTools` array that is validated at runtime
|
|
21
|
+
2. **Plugin-Level Enforcement**: The OpenCode plugin will filter available tools based on the agent's allowlist before registration
|
|
22
|
+
3. **Instruction Injection**: Agent instructions will be auto-generated with explicit "ALLOWED TOOLS" and "FORBIDDEN TOOLS" sections
|
|
23
|
+
4. **Runtime Validation**: A guard function will validate tool usage before each invocation
|
|
24
|
+
|
|
25
|
+
Agent tool boundaries:
|
|
26
|
+
- **Captain (Orchestrator)**: read, logbook, delegate, skill
|
|
27
|
+
- **Navigator (Planner)**: read, websearch, logbook, todo, skill
|
|
28
|
+
- **Boatswain (Executor)**: read, edit, write, test, todo, glob, grep, skill
|
|
29
|
+
- **Quartermaster (Reviewer)**: read, grep, execute, write, skill
|
|
30
|
+
- **Scout (Researcher)**: websearch, webfetch, read, skill
|
|
31
|
+
|
|
32
|
+
## Consequences
|
|
33
|
+
|
|
34
|
+
**Benefits:**
|
|
35
|
+
- Clear separation of concerns - each agent has a defined operational scope
|
|
36
|
+
- Easier debugging - tool usage violations are caught immediately
|
|
37
|
+
- Better security - no accidental or intentional tool misuse
|
|
38
|
+
- Self-documenting - agent capabilities are explicit in code
|
|
39
|
+
- Improved trust - users can rely on agents staying in their lane
|
|
40
|
+
|
|
41
|
+
**Trade-offs:**
|
|
42
|
+
- More rigid system - changing tool access requires code changes
|
|
43
|
+
- Potential friction - if an agent legitimately needs a new tool, must update config
|
|
44
|
+
- Implementation overhead - validation logic adds complexity
|
|
45
|
+
|
|
46
|
+
**Follow-up Actions:**
|
|
47
|
+
- Update Agent.ts with tool enforcement methods
|
|
48
|
+
- Add allowedTools array to each agent definition
|
|
49
|
+
- Create guard functions in src/core/guards.ts
|
|
50
|
+
- Update opencode-plugin.ts to filter tools at registration
|
|
51
|
+
- Write tests for boundary enforcement
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# Plugin Factory Pattern for Configuration
|
|
2
|
+
|
|
3
|
+
## Status
|
|
4
|
+
Accepted
|
|
5
|
+
|
|
6
|
+
## Context
|
|
7
|
+
The OpenCrew plugin (`src/opencode-plugin.ts`) previously hardcoded all configuration:
|
|
8
|
+
- Tools (`edit_logbook`) were imported and registered directly
|
|
9
|
+
- `ALL_AVAILABLE_TOOLS` was defined inline
|
|
10
|
+
- The crew of agents was imported from a fixed location
|
|
11
|
+
- Skills were not configurable through the plugin
|
|
12
|
+
|
|
13
|
+
This made it difficult to:
|
|
14
|
+
1. Add custom tools without modifying the plugin source
|
|
15
|
+
2. Extend the available tools list for custom tool types
|
|
16
|
+
3. Use a custom crew configuration
|
|
17
|
+
4. Configure the plugin for different deployment scenarios
|
|
18
|
+
|
|
19
|
+
The `Plugin` type from `@opencode-ai/plugin` is a function signature `(input: PluginInput) => Promise<Hooks>`, which doesn't accept custom configuration parameters directly.
|
|
20
|
+
|
|
21
|
+
## Decision
|
|
22
|
+
Implement a **Factory Pattern** for the OpCrew plugin:
|
|
23
|
+
|
|
24
|
+
1. **`OpCrewPluginOptions` interface** - Configuration object with:
|
|
25
|
+
- `tools?: Record<string, ToolDefinition>` - Custom tools to register
|
|
26
|
+
- `skills?: SkillConfig[]` - Skills to make available
|
|
27
|
+
- `availableTools?: CanonicalTool[]` - Canonical tools for boundary enforcement
|
|
28
|
+
- `includeDefaultTools?: boolean` - Whether to include default tools (default: true)
|
|
29
|
+
- `crew?: OpCrewAgent[]` - Custom crew of agents
|
|
30
|
+
|
|
31
|
+
2. **`createOpCrewPlugin(options)` factory function** - Creates a configured Plugin instance
|
|
32
|
+
|
|
33
|
+
3. **`DEFAULT_CANONICAL_TOOLS` export** - The default list for extension
|
|
34
|
+
|
|
35
|
+
4. **Backwards compatibility** - Default export `OpCrewPlugin` uses default options
|
|
36
|
+
|
|
37
|
+
### Usage Examples
|
|
38
|
+
|
|
39
|
+
**Default (backwards compatible):**
|
|
40
|
+
```typescript
|
|
41
|
+
import { OpCrewPlugin } from 'opcrew';
|
|
42
|
+
export default OpCrewPlugin;
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
**With custom tools:**
|
|
46
|
+
```typescript
|
|
47
|
+
import { createOpCrewPlugin } from 'opcrew';
|
|
48
|
+
import { myTool } from './my-tool';
|
|
49
|
+
|
|
50
|
+
export default createOpCrewPlugin({
|
|
51
|
+
tools: { my_tool: myTool },
|
|
52
|
+
});
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
**Full customization:**
|
|
56
|
+
```typescript
|
|
57
|
+
import { createOpCrewPlugin, DEFAULT_CANONICAL_TOOLS } from 'opcrew';
|
|
58
|
+
|
|
59
|
+
export default createOpCrewPlugin({
|
|
60
|
+
tools: { custom_tool: myTool },
|
|
61
|
+
availableTools: [...DEFAULT_CANONICAL_TOOLS, 'custom_tool'],
|
|
62
|
+
includeDefaultTools: false,
|
|
63
|
+
crew: [customAgent],
|
|
64
|
+
});
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Consequences
|
|
68
|
+
|
|
69
|
+
**Benefits:**
|
|
70
|
+
- Extensibility: Users can add custom tools without forking
|
|
71
|
+
- Flexibility: Different configurations for different environments
|
|
72
|
+
- Testability: Easier to test with mock configurations
|
|
73
|
+
- Backwards compatible: Existing usage continues to work
|
|
74
|
+
|
|
75
|
+
**Trade-offs:**
|
|
76
|
+
- Slightly more complex API surface
|
|
77
|
+
- Need to document configuration options
|
|
78
|
+
- Type definitions must be exported for consumers
|
|
79
|
+
|
|
80
|
+
**Follow-up:**
|
|
81
|
+
- Document the configuration API in README
|
|
82
|
+
- Consider adding validation for configuration options
|
|
83
|
+
- Update package.json exports if needed
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opcrew",
|
|
3
3
|
"description": "OpenCrew agents and OpenCode plugin",
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.5",
|
|
5
5
|
"main": "./dist/opencode-plugin.js",
|
|
6
6
|
"module": "./dist/opencode-plugin.js",
|
|
7
7
|
"type": "module",
|
|
@@ -29,9 +29,10 @@
|
|
|
29
29
|
"opcrew": "./dist/cli.js"
|
|
30
30
|
},
|
|
31
31
|
"scripts": {
|
|
32
|
-
"build": "bun run build:plugin && bun run build:cli",
|
|
32
|
+
"build": "bun run build:plugin && bun run build:cli && bun run build:skills",
|
|
33
33
|
"build:plugin": "bun build src/opencode-plugin.ts --outdir dist --target bun",
|
|
34
34
|
"build:cli": "bun build src/cli/index.ts --outfile dist/cli.js --target bun",
|
|
35
|
+
"build:skills": "cp -r src/skills dist/skills",
|
|
35
36
|
"prepack": "bun run build",
|
|
36
37
|
"test": "bun test",
|
|
37
38
|
"build:types": "bunx tsc -p tsconfig.build.json"
|