opcrew 0.1.5 → 0.1.7
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 +75 -10
- package/dist/opencode-plugin.js +148 -105
- package/dist/skills/find-skills/SKILL.md +531 -0
- package/dist/skills/skills/find-skills/SKILL.md +531 -0
- package/dist/templates/knowledge/README.md +67 -0
- package/dist/templates/knowledge/glossary.md +10 -0
- package/dist/templates/knowledge/project-map.md +13 -0
- package/dist/templates/logbook.schema.json +82 -0
- package/dist/templates/logbook.template.json +8 -0
- package/dist/templates/templates/knowledge/README.md +67 -0
- package/dist/templates/templates/knowledge/glossary.md +10 -0
- package/dist/templates/templates/knowledge/project-map.md +13 -0
- package/dist/templates/templates/logbook.schema.json +82 -0
- package/dist/templates/templates/logbook.template.json +8 -0
- package/package.json +3 -2
package/dist/cli.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
import process from "process";
|
|
6
6
|
|
|
7
7
|
// src/cli/install.ts
|
|
8
|
-
import { mkdir, writeFile, cp, rm } from "fs/promises";
|
|
8
|
+
import { mkdir, writeFile, cp, rm, stat } from "fs/promises";
|
|
9
9
|
import path2 from "path";
|
|
10
10
|
// src/core/tools/translations.ts
|
|
11
11
|
var CLAUDE_TOOLS = {
|
|
@@ -94,7 +94,7 @@ description: ${this.config.description}
|
|
|
94
94
|
tools: [${translatedTools.join(", ")}]
|
|
95
95
|
---`;
|
|
96
96
|
return `${frontmatter}
|
|
97
|
-
${this.buildMarkdownBody()}`;
|
|
97
|
+
${this.buildMarkdownBody("claude")}`;
|
|
98
98
|
}
|
|
99
99
|
compileToOpenCodeMarkdown() {
|
|
100
100
|
const permissionByRole = {
|
|
@@ -114,7 +114,7 @@ permission:
|
|
|
114
114
|
webfetch: ${permission.webfetch}
|
|
115
115
|
---`;
|
|
116
116
|
return `${frontmatter}
|
|
117
|
-
${this.buildMarkdownBody()}`;
|
|
117
|
+
${this.buildMarkdownBody("opencode")}`;
|
|
118
118
|
}
|
|
119
119
|
compileToCodexMarkdown() {
|
|
120
120
|
const translatedTools = translateTools(this.getAllowedTools(), "codex");
|
|
@@ -124,13 +124,23 @@ role: ${this.config.role}
|
|
|
124
124
|
tools: [${translatedTools.join(", ")}]
|
|
125
125
|
---`;
|
|
126
126
|
return `${frontmatter}
|
|
127
|
-
${this.buildMarkdownBody()}`;
|
|
127
|
+
${this.buildMarkdownBody("codex")}`;
|
|
128
128
|
}
|
|
129
|
-
|
|
129
|
+
buildToolBoundary(platform) {
|
|
130
|
+
const translatedTools = translateTools(this.getAllowedTools(), platform);
|
|
131
|
+
if (translatedTools.length === 0) {
|
|
132
|
+
return "TOOL BOUNDARY: No tools are available for this agent on this platform.";
|
|
133
|
+
}
|
|
134
|
+
const toolList = translatedTools.join(", ");
|
|
135
|
+
return `TOOL BOUNDARY: You are ONLY permitted to use these tools: ${toolList}. Using any other tool is a violation.`;
|
|
136
|
+
}
|
|
137
|
+
buildMarkdownBody(platform) {
|
|
138
|
+
const toolBoundary = this.buildToolBoundary(platform);
|
|
130
139
|
const instructionsList = this.config.instructions.map((instruction) => `- ${instruction}`).join(`
|
|
131
140
|
`);
|
|
132
141
|
return `
|
|
133
142
|
# ${this.config.name}
|
|
143
|
+
- ${toolBoundary}
|
|
134
144
|
${instructionsList}
|
|
135
145
|
|
|
136
146
|
## Shared Context
|
|
@@ -146,7 +156,6 @@ var Captain = new OpCrewAgent({
|
|
|
146
156
|
role: "Orchestrator",
|
|
147
157
|
mode: "primary",
|
|
148
158
|
instructions: [
|
|
149
|
-
"TOOL BOUNDARY: You are ONLY permitted to use these tools: read, logbook, delegate, skill. Using any other tool is a violation.",
|
|
150
159
|
"You are the orchestrator - you NEVER execute work directly. Your role is to coordinate, delegate, and verify.",
|
|
151
160
|
"Workstream Fan-out: divide missions into independent Workstreams (e.g., Logic, Testing, Documentation) to enable parallel specialists.",
|
|
152
161
|
"Assign roles in order: Navigator plans, Boatswain executes, Quartermaster reviews; resolve conflicts quickly.",
|
|
@@ -171,7 +180,6 @@ var Navigator = new OpCrewAgent({
|
|
|
171
180
|
role: "Planner",
|
|
172
181
|
mode: "subagent",
|
|
173
182
|
instructions: [
|
|
174
|
-
"TOOL BOUNDARY: You are ONLY permitted to use these tools: read, websearch, logbook, todo, skill. Using any other tool is a violation.",
|
|
175
183
|
"Chart the implementation path based on the Captain's intent and constraints.",
|
|
176
184
|
"Decompose work into atomic, ordered tasks with clear inputs, outputs, and ownership.",
|
|
177
185
|
"Workstream Decomposition: break the plan into atomic, parallel tracks. Define File Ownership (e.g., Boatswain-A owns src/, Boatswain-B owns tests/).",
|
|
@@ -200,7 +208,6 @@ var Boatswain = new OpCrewAgent({
|
|
|
200
208
|
role: "Executor",
|
|
201
209
|
mode: "subagent",
|
|
202
210
|
instructions: [
|
|
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
211
|
"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.",
|
|
205
212
|
"Follow existing patterns, naming, and formatting; avoid speculative changes.",
|
|
206
213
|
"Context Anchoring: before editing, read the surrounding code to match indentation, casing, and comment styles. Maintain the project's local laws.",
|
|
@@ -223,7 +230,6 @@ var Quartermaster = new OpCrewAgent({
|
|
|
223
230
|
role: "Reviewer",
|
|
224
231
|
mode: "subagent",
|
|
225
232
|
instructions: [
|
|
226
|
-
"TOOL BOUNDARY: You are ONLY permitted to use these tools: read, grep, execute, write, skill. Using any other tool is a violation.",
|
|
227
233
|
"Inspect each diff for regression risks, scope drift, and missing context.",
|
|
228
234
|
"Integration Audit: once all parallel Boatswains report Complete, verify the combined diff for logic conflicts or signature mismatches.",
|
|
229
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.",
|
|
@@ -246,7 +252,6 @@ var Scout = new OpCrewAgent({
|
|
|
246
252
|
role: "Researcher",
|
|
247
253
|
mode: "subagent",
|
|
248
254
|
instructions: [
|
|
249
|
-
"TOOL BOUNDARY: You are ONLY permitted to use these tools: websearch, webfetch, read, skill. Using any other tool is a violation.",
|
|
250
255
|
"You are the crew's eyes to the outside world - research and gather information when summoned by Captain or Navigator.",
|
|
251
256
|
"Focus exclusively on external research: web searches, documentation lookup, API references, and best practices.",
|
|
252
257
|
"Never modify project files - your job is to gather intelligence, not execute changes.",
|
|
@@ -333,10 +338,70 @@ var CLAUDE_SKILLS_DIR = path2.join(".claude", "skills");
|
|
|
333
338
|
var OPENCODE_AGENTS_DIR = path2.join(".opencode", "agents");
|
|
334
339
|
var OPENCODE_SKILLS_DIR = path2.join(".opencode", "skills");
|
|
335
340
|
var CODEX_FILE = path2.join(".codex", "instructions.md");
|
|
341
|
+
var OPCREW_DIR = ".opcrew";
|
|
342
|
+
var KNOWLEDGE_DIR = path2.join("docs", "knowledge");
|
|
343
|
+
var TEMPLATE_DIR = path2.join(import.meta.dir, "..", "templates");
|
|
336
344
|
async function ensureDir(targetDir) {
|
|
337
345
|
await mkdir(targetDir, { recursive: true });
|
|
338
346
|
}
|
|
347
|
+
async function fileExists(filePath) {
|
|
348
|
+
try {
|
|
349
|
+
await stat(filePath);
|
|
350
|
+
return true;
|
|
351
|
+
} catch {
|
|
352
|
+
return false;
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
async function bootstrapLogbook() {
|
|
356
|
+
const logbookPath = path2.join(OPCREW_DIR, "logbook.json");
|
|
357
|
+
const schemaPath = path2.join(OPCREW_DIR, "logbook.schema.json");
|
|
358
|
+
await ensureDir(OPCREW_DIR);
|
|
359
|
+
if (await fileExists(logbookPath)) {
|
|
360
|
+
console.log(`Skipped ${logbookPath} (already exists)`);
|
|
361
|
+
} else {
|
|
362
|
+
const templatePath = path2.join(TEMPLATE_DIR, "logbook.template.json");
|
|
363
|
+
await cp(templatePath, logbookPath);
|
|
364
|
+
console.log(`Created ${logbookPath}`);
|
|
365
|
+
}
|
|
366
|
+
if (await fileExists(schemaPath)) {
|
|
367
|
+
console.log(`Skipped ${schemaPath} (already exists)`);
|
|
368
|
+
} else {
|
|
369
|
+
const schemaTemplatePath = path2.join(TEMPLATE_DIR, "logbook.schema.json");
|
|
370
|
+
await cp(schemaTemplatePath, schemaPath);
|
|
371
|
+
console.log(`Created ${schemaPath}`);
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
async function bootstrapKnowledgeBase() {
|
|
375
|
+
const decisionsDir = path2.join(KNOWLEDGE_DIR, "decisions");
|
|
376
|
+
const readmePath = path2.join(KNOWLEDGE_DIR, "README.md");
|
|
377
|
+
const glossaryPath = path2.join(KNOWLEDGE_DIR, "glossary.md");
|
|
378
|
+
const projectMapPath = path2.join(KNOWLEDGE_DIR, "project-map.md");
|
|
379
|
+
await ensureDir(decisionsDir);
|
|
380
|
+
if (await fileExists(readmePath)) {
|
|
381
|
+
console.log(`Skipped ${readmePath} (already exists)`);
|
|
382
|
+
} else {
|
|
383
|
+
const templatePath = path2.join(TEMPLATE_DIR, "knowledge", "README.md");
|
|
384
|
+
await cp(templatePath, readmePath);
|
|
385
|
+
console.log(`Created ${readmePath}`);
|
|
386
|
+
}
|
|
387
|
+
if (await fileExists(glossaryPath)) {
|
|
388
|
+
console.log(`Skipped ${glossaryPath} (already exists)`);
|
|
389
|
+
} else {
|
|
390
|
+
const templatePath = path2.join(TEMPLATE_DIR, "knowledge", "glossary.md");
|
|
391
|
+
await cp(templatePath, glossaryPath);
|
|
392
|
+
console.log(`Created ${glossaryPath}`);
|
|
393
|
+
}
|
|
394
|
+
if (await fileExists(projectMapPath)) {
|
|
395
|
+
console.log(`Skipped ${projectMapPath} (already exists)`);
|
|
396
|
+
} else {
|
|
397
|
+
const templatePath = path2.join(TEMPLATE_DIR, "knowledge", "project-map.md");
|
|
398
|
+
await cp(templatePath, projectMapPath);
|
|
399
|
+
console.log(`Created ${projectMapPath}`);
|
|
400
|
+
}
|
|
401
|
+
}
|
|
339
402
|
async function installToTool(tool) {
|
|
403
|
+
await bootstrapLogbook();
|
|
404
|
+
await bootstrapKnowledgeBase();
|
|
340
405
|
if (tool === "codex") {
|
|
341
406
|
await installForCodex();
|
|
342
407
|
return;
|
package/dist/opencode-plugin.js
CHANGED
|
@@ -100,7 +100,7 @@ description: ${this.config.description}
|
|
|
100
100
|
tools: [${translatedTools.join(", ")}]
|
|
101
101
|
---`;
|
|
102
102
|
return `${frontmatter}
|
|
103
|
-
${this.buildMarkdownBody()}`;
|
|
103
|
+
${this.buildMarkdownBody("claude")}`;
|
|
104
104
|
}
|
|
105
105
|
compileToOpenCodeMarkdown() {
|
|
106
106
|
const permissionByRole = {
|
|
@@ -120,7 +120,7 @@ permission:
|
|
|
120
120
|
webfetch: ${permission.webfetch}
|
|
121
121
|
---`;
|
|
122
122
|
return `${frontmatter}
|
|
123
|
-
${this.buildMarkdownBody()}`;
|
|
123
|
+
${this.buildMarkdownBody("opencode")}`;
|
|
124
124
|
}
|
|
125
125
|
compileToCodexMarkdown() {
|
|
126
126
|
const translatedTools = translateTools(this.getAllowedTools(), "codex");
|
|
@@ -130,13 +130,23 @@ role: ${this.config.role}
|
|
|
130
130
|
tools: [${translatedTools.join(", ")}]
|
|
131
131
|
---`;
|
|
132
132
|
return `${frontmatter}
|
|
133
|
-
${this.buildMarkdownBody()}`;
|
|
133
|
+
${this.buildMarkdownBody("codex")}`;
|
|
134
134
|
}
|
|
135
|
-
|
|
135
|
+
buildToolBoundary(platform) {
|
|
136
|
+
const translatedTools = translateTools(this.getAllowedTools(), platform);
|
|
137
|
+
if (translatedTools.length === 0) {
|
|
138
|
+
return "TOOL BOUNDARY: No tools are available for this agent on this platform.";
|
|
139
|
+
}
|
|
140
|
+
const toolList = translatedTools.join(", ");
|
|
141
|
+
return `TOOL BOUNDARY: You are ONLY permitted to use these tools: ${toolList}. Using any other tool is a violation.`;
|
|
142
|
+
}
|
|
143
|
+
buildMarkdownBody(platform) {
|
|
144
|
+
const toolBoundary = this.buildToolBoundary(platform);
|
|
136
145
|
const instructionsList = this.config.instructions.map((instruction) => `- ${instruction}`).join(`
|
|
137
146
|
`);
|
|
138
147
|
return `
|
|
139
148
|
# ${this.config.name}
|
|
149
|
+
- ${toolBoundary}
|
|
140
150
|
${instructionsList}
|
|
141
151
|
|
|
142
152
|
## Shared Context
|
|
@@ -152,7 +162,6 @@ var Captain = new OpCrewAgent({
|
|
|
152
162
|
role: "Orchestrator",
|
|
153
163
|
mode: "primary",
|
|
154
164
|
instructions: [
|
|
155
|
-
"TOOL BOUNDARY: You are ONLY permitted to use these tools: read, logbook, delegate, skill. Using any other tool is a violation.",
|
|
156
165
|
"You are the orchestrator - you NEVER execute work directly. Your role is to coordinate, delegate, and verify.",
|
|
157
166
|
"Workstream Fan-out: divide missions into independent Workstreams (e.g., Logic, Testing, Documentation) to enable parallel specialists.",
|
|
158
167
|
"Assign roles in order: Navigator plans, Boatswain executes, Quartermaster reviews; resolve conflicts quickly.",
|
|
@@ -177,7 +186,6 @@ var Navigator = new OpCrewAgent({
|
|
|
177
186
|
role: "Planner",
|
|
178
187
|
mode: "subagent",
|
|
179
188
|
instructions: [
|
|
180
|
-
"TOOL BOUNDARY: You are ONLY permitted to use these tools: read, websearch, logbook, todo, skill. Using any other tool is a violation.",
|
|
181
189
|
"Chart the implementation path based on the Captain's intent and constraints.",
|
|
182
190
|
"Decompose work into atomic, ordered tasks with clear inputs, outputs, and ownership.",
|
|
183
191
|
"Workstream Decomposition: break the plan into atomic, parallel tracks. Define File Ownership (e.g., Boatswain-A owns src/, Boatswain-B owns tests/).",
|
|
@@ -206,7 +214,6 @@ var Boatswain = new OpCrewAgent({
|
|
|
206
214
|
role: "Executor",
|
|
207
215
|
mode: "subagent",
|
|
208
216
|
instructions: [
|
|
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
217
|
"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.",
|
|
211
218
|
"Follow existing patterns, naming, and formatting; avoid speculative changes.",
|
|
212
219
|
"Context Anchoring: before editing, read the surrounding code to match indentation, casing, and comment styles. Maintain the project's local laws.",
|
|
@@ -229,7 +236,6 @@ var Quartermaster = new OpCrewAgent({
|
|
|
229
236
|
role: "Reviewer",
|
|
230
237
|
mode: "subagent",
|
|
231
238
|
instructions: [
|
|
232
|
-
"TOOL BOUNDARY: You are ONLY permitted to use these tools: read, grep, execute, write, skill. Using any other tool is a violation.",
|
|
233
239
|
"Inspect each diff for regression risks, scope drift, and missing context.",
|
|
234
240
|
"Integration Audit: once all parallel Boatswains report Complete, verify the combined diff for logic conflicts or signature mismatches.",
|
|
235
241
|
"Documentation Gate: REJECT approval if code changed but docs/project-map.md or docs/glossary.md were not updated to reflect the new state.",
|
|
@@ -252,7 +258,6 @@ var Scout = new OpCrewAgent({
|
|
|
252
258
|
role: "Researcher",
|
|
253
259
|
mode: "subagent",
|
|
254
260
|
instructions: [
|
|
255
|
-
"TOOL BOUNDARY: You are ONLY permitted to use these tools: websearch, webfetch, read, skill. Using any other tool is a violation.",
|
|
256
261
|
"You are the crew's eyes to the outside world - research and gather information when summoned by Captain or Navigator.",
|
|
257
262
|
"Focus exclusively on external research: web searches, documentation lookup, API references, and best practices.",
|
|
258
263
|
"Never modify project files - your job is to gather intelligence, not execute changes.",
|
|
@@ -12720,26 +12725,109 @@ The logbook tracks:
|
|
|
12720
12725
|
}
|
|
12721
12726
|
});
|
|
12722
12727
|
|
|
12728
|
+
// src/skills.ts
|
|
12729
|
+
import { readdir, readFile } from "fs/promises";
|
|
12730
|
+
import path from "path";
|
|
12731
|
+
import { fileURLToPath } from "url";
|
|
12732
|
+
var __dirname2 = path.dirname(fileURLToPath(import.meta.url));
|
|
12733
|
+
async function loadSkills() {
|
|
12734
|
+
const skillsDir = path.join(__dirname2, "skills");
|
|
12735
|
+
try {
|
|
12736
|
+
const entries = await readdir(skillsDir, { withFileTypes: true });
|
|
12737
|
+
const skills = [];
|
|
12738
|
+
for (const entry of entries) {
|
|
12739
|
+
if (!entry.isDirectory())
|
|
12740
|
+
continue;
|
|
12741
|
+
const skillPath = path.join(skillsDir, entry.name);
|
|
12742
|
+
const skillMdPath = path.join(skillPath, "SKILL.md");
|
|
12743
|
+
try {
|
|
12744
|
+
const content = await readFile(skillMdPath, "utf-8");
|
|
12745
|
+
const { name, description } = parseSkillFrontmatter(content);
|
|
12746
|
+
skills.push({
|
|
12747
|
+
name: name || entry.name,
|
|
12748
|
+
description: description || "",
|
|
12749
|
+
sourcePath: skillPath
|
|
12750
|
+
});
|
|
12751
|
+
} catch {
|
|
12752
|
+
console.warn(`Warning: No SKILL.md found in ${skillPath}`);
|
|
12753
|
+
}
|
|
12754
|
+
}
|
|
12755
|
+
return skills;
|
|
12756
|
+
} catch {
|
|
12757
|
+
return [];
|
|
12758
|
+
}
|
|
12759
|
+
}
|
|
12760
|
+
function parseSkillFrontmatter(content) {
|
|
12761
|
+
const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---/);
|
|
12762
|
+
if (!frontmatterMatch) {
|
|
12763
|
+
return {};
|
|
12764
|
+
}
|
|
12765
|
+
const frontmatter = frontmatterMatch[1];
|
|
12766
|
+
const result = {};
|
|
12767
|
+
const nameMatch = frontmatter.match(/^name:\s*(.+)$/m);
|
|
12768
|
+
if (nameMatch) {
|
|
12769
|
+
result.name = nameMatch[1].trim();
|
|
12770
|
+
}
|
|
12771
|
+
const descMatch = frontmatter.match(/^description:\s*(.+)$/m);
|
|
12772
|
+
if (descMatch) {
|
|
12773
|
+
result.description = descMatch[1].trim();
|
|
12774
|
+
}
|
|
12775
|
+
return result;
|
|
12776
|
+
}
|
|
12777
|
+
async function getSkillContent(skillName) {
|
|
12778
|
+
const skillMdPath = path.join(__dirname2, "skills", skillName, "SKILL.md");
|
|
12779
|
+
return readFile(skillMdPath, "utf-8");
|
|
12780
|
+
}
|
|
12781
|
+
|
|
12782
|
+
// src/tools/skill.ts
|
|
12783
|
+
var skillTool = tool({
|
|
12784
|
+
description: `Load and manage agent skills dynamically.
|
|
12785
|
+
|
|
12786
|
+
Actions:
|
|
12787
|
+
- list: List all available skills with names and descriptions
|
|
12788
|
+
- load: Load a specific skill's full content (SKILL.md)
|
|
12789
|
+
|
|
12790
|
+
Skills provide specialized instructions and workflows for specific tasks.
|
|
12791
|
+
Use this tool when you recognize that a task matches one of the available skills.`,
|
|
12792
|
+
args: {
|
|
12793
|
+
action: tool.schema.enum(["list", "load"]).describe("Action to perform"),
|
|
12794
|
+
name: tool.schema.string().optional().describe("Skill name (required for 'load' action)")
|
|
12795
|
+
},
|
|
12796
|
+
async execute(args) {
|
|
12797
|
+
switch (args.action) {
|
|
12798
|
+
case "list": {
|
|
12799
|
+
const skills = await loadSkills();
|
|
12800
|
+
if (skills.length === 0) {
|
|
12801
|
+
return "No skills available.";
|
|
12802
|
+
}
|
|
12803
|
+
const formattedList = skills.map((skill) => `- **${skill.name}**: ${skill.description}`).join(`
|
|
12804
|
+
`);
|
|
12805
|
+
return `Available skills:
|
|
12806
|
+
|
|
12807
|
+
${formattedList}`;
|
|
12808
|
+
}
|
|
12809
|
+
case "load": {
|
|
12810
|
+
if (!args.name) {
|
|
12811
|
+
return "Error: name is required for 'load' action";
|
|
12812
|
+
}
|
|
12813
|
+
try {
|
|
12814
|
+
const content = await getSkillContent(args.name);
|
|
12815
|
+
return content;
|
|
12816
|
+
} catch (error45) {
|
|
12817
|
+
const message = error45 instanceof Error ? error45.message : String(error45);
|
|
12818
|
+
return `Error: Failed to load skill '${args.name}': ${message}`;
|
|
12819
|
+
}
|
|
12820
|
+
}
|
|
12821
|
+
default:
|
|
12822
|
+
return `Error: Unknown action: ${args.action}`;
|
|
12823
|
+
}
|
|
12824
|
+
}
|
|
12825
|
+
});
|
|
12826
|
+
|
|
12723
12827
|
// 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;
|
|
12740
12828
|
var permissionByRole = {
|
|
12741
12829
|
Orchestrator: { edit: "deny", bash: "ask", webfetch: "deny" },
|
|
12742
|
-
Planner: { edit: "deny", bash: "
|
|
12830
|
+
Planner: { edit: "deny", bash: "allow", webfetch: "allow" },
|
|
12743
12831
|
Executor: { edit: "allow", bash: "allow", webfetch: "ask" },
|
|
12744
12832
|
Reviewer: { edit: "deny", bash: "allow", webfetch: "deny" },
|
|
12745
12833
|
Researcher: { edit: "deny", bash: "deny", webfetch: "allow" }
|
|
@@ -12747,102 +12835,57 @@ var permissionByRole = {
|
|
|
12747
12835
|
function toAgentId(agent) {
|
|
12748
12836
|
return agent.config.name.toLowerCase().replace(/\s+/g, "-");
|
|
12749
12837
|
}
|
|
12750
|
-
function buildPrompt(agent
|
|
12838
|
+
function buildPrompt(agent) {
|
|
12751
12839
|
const instructionsList = agent.config.instructions.map((instruction) => `- ${instruction}`).join(`
|
|
12752
12840
|
`);
|
|
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)"}`;
|
|
12758
12841
|
return `# ${agent.config.name}
|
|
12759
|
-
|
|
12760
|
-
${toolBoundariesSection}
|
|
12761
|
-
|
|
12762
12842
|
${instructionsList}
|
|
12763
12843
|
|
|
12764
12844
|
## Shared Context
|
|
12765
12845
|
Refer to \`.opcrew/logbook.json\` for the current voyage status.
|
|
12766
12846
|
`;
|
|
12767
12847
|
}
|
|
12768
|
-
function toOpenCodeAgentConfig(agent
|
|
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
|
-
}
|
|
12848
|
+
function toOpenCodeAgentConfig(agent) {
|
|
12775
12849
|
return {
|
|
12776
12850
|
description: agent.config.name,
|
|
12777
12851
|
mode: agent.config.mode,
|
|
12778
|
-
prompt: buildPrompt(agent
|
|
12852
|
+
prompt: buildPrompt(agent),
|
|
12779
12853
|
permission: permissionByRole[agent.config.role]
|
|
12780
12854
|
};
|
|
12781
12855
|
}
|
|
12782
|
-
|
|
12783
|
-
|
|
12784
|
-
|
|
12785
|
-
|
|
12786
|
-
|
|
12787
|
-
|
|
12788
|
-
|
|
12789
|
-
}
|
|
12790
|
-
|
|
12791
|
-
|
|
12792
|
-
|
|
12793
|
-
|
|
12794
|
-
|
|
12795
|
-
|
|
12796
|
-
|
|
12797
|
-
|
|
12798
|
-
|
|
12799
|
-
|
|
12800
|
-
|
|
12801
|
-
|
|
12802
|
-
|
|
12803
|
-
|
|
12804
|
-
|
|
12805
|
-
|
|
12806
|
-
|
|
12807
|
-
|
|
12808
|
-
|
|
12809
|
-
|
|
12810
|
-
|
|
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
|
-
};
|
|
12856
|
+
var OpCrewPlugin = async ({ client }) => {
|
|
12857
|
+
await client.app.log({
|
|
12858
|
+
body: {
|
|
12859
|
+
service: "opcrew",
|
|
12860
|
+
level: "info",
|
|
12861
|
+
message: "OpenCode plugin initialized"
|
|
12862
|
+
}
|
|
12863
|
+
});
|
|
12864
|
+
return {
|
|
12865
|
+
config: async (config2) => {
|
|
12866
|
+
const agents = config2.agent ?? {};
|
|
12867
|
+
agents["build"] = { ...agents["build"], disable: true };
|
|
12868
|
+
for (const agent of crew) {
|
|
12869
|
+
agents[toAgentId(agent)] = toOpenCodeAgentConfig(agent);
|
|
12870
|
+
}
|
|
12871
|
+
config2.agents = agents;
|
|
12872
|
+
await client.app.log({
|
|
12873
|
+
body: {
|
|
12874
|
+
service: "opcrew",
|
|
12875
|
+
level: "info",
|
|
12876
|
+
message: "Crew agents registered",
|
|
12877
|
+
extra: { agents: Object.keys(agents) }
|
|
12878
|
+
}
|
|
12879
|
+
});
|
|
12880
|
+
},
|
|
12881
|
+
tool: {
|
|
12882
|
+
edit_logbook: editLogbookTool,
|
|
12883
|
+
skill: skillTool
|
|
12884
|
+
}
|
|
12838
12885
|
};
|
|
12839
|
-
}
|
|
12840
|
-
var OpCrewPlugin = createOpCrewPlugin();
|
|
12886
|
+
};
|
|
12841
12887
|
var opencode_plugin_default = OpCrewPlugin;
|
|
12842
12888
|
export {
|
|
12843
12889
|
opencode_plugin_default as default,
|
|
12844
|
-
|
|
12845
|
-
OpCrewPlugin,
|
|
12846
|
-
DEFAULT_CANONICAL_TOOLS,
|
|
12847
|
-
ALL_AVAILABLE_TOOLS
|
|
12890
|
+
OpCrewPlugin
|
|
12848
12891
|
};
|