bros-harness 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +7 -0
- package/LICENSE +21 -0
- package/README.md +183 -0
- package/SECURITY.md +16 -0
- package/assets/agents.manifest.json +55 -0
- package/assets/commands.manifest.json +35 -0
- package/assets/docs.manifest.json +20 -0
- package/assets/import-report.md +25 -0
- package/assets/manifest.json +799 -0
- package/assets/opencode/agents/README.md +3 -0
- package/assets/opencode/agents/bro-build.md +256 -0
- package/assets/opencode/agents/bro-design.md +77 -0
- package/assets/opencode/agents/bro-docs.md +72 -0
- package/assets/opencode/agents/bro-explore.md +143 -0
- package/assets/opencode/agents/bro-ops.md +195 -0
- package/assets/opencode/agents/bro-shield.md +77 -0
- package/assets/opencode/agents/bro-test.md +204 -0
- package/assets/opencode/agents/bro-ui.md +135 -0
- package/assets/opencode/agents/mighty-bro.md +252 -0
- package/assets/opencode/commands/README.md +3 -0
- package/assets/opencode/commands/bros-assemble.md +32 -0
- package/assets/opencode/commands/bros-build.md +58 -0
- package/assets/opencode/commands/bros-plan.md +83 -0
- package/assets/opencode/commands/bros-review.md +38 -0
- package/assets/opencode/commands/bros-status.md +26 -0
- package/assets/opencode/docs/README.md +3 -0
- package/assets/opencode/docs/bros-builtin-skills.md +63 -0
- package/assets/opencode/docs/bros-harness.md +194 -0
- package/assets/opencode/skills/README.md +3 -0
- package/assets/opencode/skills/agent-architecture-audit/SKILL.md +256 -0
- package/assets/opencode/skills/agent-harness-construction/.openskills.json +7 -0
- package/assets/opencode/skills/agent-harness-construction/SKILL.md +73 -0
- package/assets/opencode/skills/agent-introspection-debugging/.openskills.json +7 -0
- package/assets/opencode/skills/agent-introspection-debugging/SKILL.md +153 -0
- package/assets/opencode/skills/api-design/.openskills.json +7 -0
- package/assets/opencode/skills/api-design/agents/openai.yaml +7 -0
- package/assets/opencode/skills/architecture-decision-records/.openskills.json +7 -0
- package/assets/opencode/skills/architecture-decision-records/SKILL.md +179 -0
- package/assets/opencode/skills/article-writing/.openskills.json +7 -0
- package/assets/opencode/skills/article-writing/SKILL.md +79 -0
- package/assets/opencode/skills/article-writing/agents/openai.yaml +7 -0
- package/assets/opencode/skills/automation-audit-ops/.openskills.json +7 -0
- package/assets/opencode/skills/automation-audit-ops/SKILL.md +142 -0
- package/assets/opencode/skills/backend-patterns/.openskills.json +7 -0
- package/assets/opencode/skills/backend-patterns/SKILL.md +561 -0
- package/assets/opencode/skills/backend-patterns/agents/openai.yaml +7 -0
- package/assets/opencode/skills/benchmark/.openskills.json +7 -0
- package/assets/opencode/skills/benchmark/SKILL.md +93 -0
- package/assets/opencode/skills/bros-orchestrate/SKILL.md +455 -0
- package/assets/opencode/skills/browser-qa/.openskills.json +7 -0
- package/assets/opencode/skills/browser-qa/SKILL.md +87 -0
- package/assets/opencode/skills/canary-watch/.openskills.json +7 -0
- package/assets/opencode/skills/canary-watch/SKILL.md +107 -0
- package/assets/opencode/skills/code-review-expert/SKILL.md +155 -0
- package/assets/opencode/skills/code-review-expert/agents/agent.yaml +7 -0
- package/assets/opencode/skills/code-review-expert/references/code-quality-checklist.md +130 -0
- package/assets/opencode/skills/code-review-expert/references/removal-plan.md +52 -0
- package/assets/opencode/skills/code-review-expert/references/security-checklist.md +118 -0
- package/assets/opencode/skills/code-review-expert/references/solid-checklist.md +65 -0
- package/assets/opencode/skills/code-tour/.openskills.json +7 -0
- package/assets/opencode/skills/code-tour/SKILL.md +236 -0
- package/assets/opencode/skills/coding-standards/.openskills.json +7 -0
- package/assets/opencode/skills/coding-standards/SKILL.md +549 -0
- package/assets/opencode/skills/coding-standards/agents/openai.yaml +7 -0
- package/assets/opencode/skills/context-budget/.openskills.json +7 -0
- package/assets/opencode/skills/context-budget/SKILL.md +135 -0
- package/assets/opencode/skills/database-migrations/.openskills.json +7 -0
- package/assets/opencode/skills/database-migrations/SKILL.md +429 -0
- package/assets/opencode/skills/deployment-patterns/.openskills.json +7 -0
- package/assets/opencode/skills/deployment-patterns/SKILL.md +427 -0
- package/assets/opencode/skills/design-system/.openskills.json +7 -0
- package/assets/opencode/skills/design-system/SKILL.md +82 -0
- package/assets/opencode/skills/docker-patterns/.openskills.json +7 -0
- package/assets/opencode/skills/docker-patterns/SKILL.md +364 -0
- package/assets/opencode/skills/documentation-lookup/.openskills.json +7 -0
- package/assets/opencode/skills/documentation-lookup/SKILL.md +90 -0
- package/assets/opencode/skills/documentation-lookup/agents/openai.yaml +7 -0
- package/assets/opencode/skills/e2e-testing/.openskills.json +7 -0
- package/assets/opencode/skills/e2e-testing/SKILL.md +326 -0
- package/assets/opencode/skills/e2e-testing/agents/openai.yaml +7 -0
- package/assets/opencode/skills/error-handling/SKILL.md +376 -0
- package/assets/opencode/skills/frontend-design/.openskills.json +7 -0
- package/assets/opencode/skills/frontend-design/SKILL.md +145 -0
- package/assets/opencode/skills/frontend-design-direction/SKILL.md +92 -0
- package/assets/opencode/skills/frontend-patterns/.openskills.json +7 -0
- package/assets/opencode/skills/frontend-patterns/SKILL.md +642 -0
- package/assets/opencode/skills/frontend-patterns/agents/openai.yaml +7 -0
- package/assets/opencode/skills/gateguard/.openskills.json +7 -0
- package/assets/opencode/skills/gateguard/SKILL.md +125 -0
- package/assets/opencode/skills/git-master/SKILL.md +60 -0
- package/assets/opencode/skills/golang-patterns/.openskills.json +7 -0
- package/assets/opencode/skills/golang-patterns/SKILL.md +674 -0
- package/assets/opencode/skills/golang-testing/.openskills.json +7 -0
- package/assets/opencode/skills/golang-testing/SKILL.md +720 -0
- package/assets/opencode/skills/grafana-dashboard-design/SKILL.md +65 -0
- package/assets/opencode/skills/hexagonal-architecture/.openskills.json +7 -0
- package/assets/opencode/skills/hexagonal-architecture/SKILL.md +276 -0
- package/assets/opencode/skills/java-coding-standards/.openskills.json +7 -0
- package/assets/opencode/skills/java-coding-standards/SKILL.md +383 -0
- package/assets/opencode/skills/jpa-patterns/.openskills.json +7 -0
- package/assets/opencode/skills/jpa-patterns/SKILL.md +151 -0
- package/assets/opencode/skills/knowledge-ops/.openskills.json +7 -0
- package/assets/opencode/skills/knowledge-ops/SKILL.md +154 -0
- package/assets/opencode/skills/make-interfaces-feel-better/SKILL.md +151 -0
- package/assets/opencode/skills/mysql-patterns/SKILL.md +412 -0
- package/assets/opencode/skills/nestjs-patterns/.openskills.json +7 -0
- package/assets/opencode/skills/nestjs-patterns/SKILL.md +230 -0
- package/assets/opencode/skills/nextjs-turbopack/.openskills.json +7 -0
- package/assets/opencode/skills/nextjs-turbopack/SKILL.md +57 -0
- package/assets/opencode/skills/nextjs-turbopack/agents/openai.yaml +7 -0
- package/assets/opencode/skills/parallel-execution-optimizer/SKILL.md +72 -0
- package/assets/opencode/skills/postgres-patterns/.openskills.json +7 -0
- package/assets/opencode/skills/postgres-patterns/SKILL.md +147 -0
- package/assets/opencode/skills/prisma-patterns/SKILL.md +371 -0
- package/assets/opencode/skills/product-capability/.openskills.json +7 -0
- package/assets/opencode/skills/product-capability/SKILL.md +141 -0
- package/assets/opencode/skills/product-lens/.openskills.json +7 -0
- package/assets/opencode/skills/product-lens/SKILL.md +92 -0
- package/assets/opencode/skills/production-audit/SKILL.md +206 -0
- package/assets/opencode/skills/python-patterns/.openskills.json +7 -0
- package/assets/opencode/skills/python-patterns/SKILL.md +750 -0
- package/assets/opencode/skills/python-testing/.openskills.json +7 -0
- package/assets/opencode/skills/python-testing/SKILL.md +816 -0
- package/assets/opencode/skills/redis-patterns/SKILL.md +403 -0
- package/assets/opencode/skills/requirements-clarity/README.md +260 -0
- package/assets/opencode/skills/requirements-clarity/SKILL.md +324 -0
- package/assets/opencode/skills/rust-patterns/.openskills.json +7 -0
- package/assets/opencode/skills/rust-patterns/SKILL.md +499 -0
- package/assets/opencode/skills/rust-testing/.openskills.json +7 -0
- package/assets/opencode/skills/rust-testing/SKILL.md +500 -0
- package/assets/opencode/skills/safety-guard/.openskills.json +7 -0
- package/assets/opencode/skills/safety-guard/SKILL.md +75 -0
- package/assets/opencode/skills/search-first/.openskills.json +7 -0
- package/assets/opencode/skills/search-first/SKILL.md +181 -0
- package/assets/opencode/skills/security-review/.openskills.json +7 -0
- package/assets/opencode/skills/security-review/agents/openai.yaml +7 -0
- package/assets/opencode/skills/security-review/cloud-infrastructure-security.md +361 -0
- package/assets/opencode/skills/security-scan/.openskills.json +7 -0
- package/assets/opencode/skills/security-scan/SKILL.md +165 -0
- package/assets/opencode/skills/springboot-patterns/.openskills.json +7 -0
- package/assets/opencode/skills/springboot-patterns/SKILL.md +314 -0
- package/assets/opencode/skills/springboot-tdd/.openskills.json +7 -0
- package/assets/opencode/skills/springboot-tdd/SKILL.md +158 -0
- package/assets/opencode/skills/springboot-verification/.openskills.json +7 -0
- package/assets/opencode/skills/springboot-verification/SKILL.md +231 -0
- package/assets/opencode/skills/strategic-compact/.openskills.json +7 -0
- package/assets/opencode/skills/strategic-compact/SKILL.md +131 -0
- package/assets/opencode/skills/strategic-compact/agents/openai.yaml +7 -0
- package/assets/opencode/skills/strategic-compact/suggest-compact.sh +54 -0
- package/assets/opencode/skills/tdd-workflow/.openskills.json +7 -0
- package/assets/opencode/skills/tdd-workflow/SKILL.md +463 -0
- package/assets/opencode/skills/tdd-workflow/agents/openai.yaml +7 -0
- package/assets/opencode/skills/verification-loop/.openskills.json +7 -0
- package/assets/opencode/skills/verification-loop/SKILL.md +126 -0
- package/assets/opencode/skills/verification-loop/agents/openai.yaml +7 -0
- package/assets/opencode/skills/vite-patterns/SKILL.md +449 -0
- package/assets/opencode/skills/web-doc-search/SKILL.md +51 -0
- package/assets/opencode/templates/README.md +3 -0
- package/assets/opencode/templates/bros/adr.md +39 -0
- package/assets/opencode/templates/bros/delivery-report.md +71 -0
- package/assets/opencode/templates/bros/explorer-evidence-packet.md +51 -0
- package/assets/opencode/templates/bros/prd.md +72 -0
- package/assets/opencode/templates/bros/security-review.md +48 -0
- package/assets/opencode/templates/bros/status-board.md +33 -0
- package/assets/opencode/templates/bros/task-packet.md +94 -0
- package/assets/opencode/templates/bros/test-strategy.md +57 -0
- package/assets/opencode/templates/bros/ui-implementation-packet.md +64 -0
- package/assets/skills.manifest.json +650 -0
- package/assets/templates.manifest.json +55 -0
- package/bin/bros.mjs +122 -0
- package/docs/compatibility.md +9 -0
- package/docs/installation.md +66 -0
- package/docs/integrations/claude.md +5 -0
- package/docs/integrations/codex.md +5 -0
- package/docs/integrations/opencode.md +39 -0
- package/docs/migration/from-local-opencode-config.md +10 -0
- package/docs/release-process.md +11 -0
- package/docs/repository-structure.md +15 -0
- package/docs/roadmap.md +20 -0
- package/docs/security.md +18 -0
- package/docs/testing.md +9 -0
- package/examples/opencode/README.md +11 -0
- package/examples/opencode/opencode.example.jsonc +4 -0
- package/package.json +43 -0
- package/scripts/validate-assets.mjs +22 -0
- package/scripts/verify-no-secrets.mjs +38 -0
- package/src/plugin.mjs +98 -0
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
{
|
|
2
|
+
"area": "templates",
|
|
3
|
+
"counts": {
|
|
4
|
+
"candidates": 9,
|
|
5
|
+
"imported": 9,
|
|
6
|
+
"skipped": 0
|
|
7
|
+
},
|
|
8
|
+
"entries": [
|
|
9
|
+
{
|
|
10
|
+
"area": "templates",
|
|
11
|
+
"path": "assets/opencode/templates/bros/adr.md",
|
|
12
|
+
"sourceRef": "opencode-template"
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"area": "templates",
|
|
16
|
+
"path": "assets/opencode/templates/bros/delivery-report.md",
|
|
17
|
+
"sourceRef": "opencode-template"
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
"area": "templates",
|
|
21
|
+
"path": "assets/opencode/templates/bros/explorer-evidence-packet.md",
|
|
22
|
+
"sourceRef": "opencode-template"
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
"area": "templates",
|
|
26
|
+
"path": "assets/opencode/templates/bros/prd.md",
|
|
27
|
+
"sourceRef": "opencode-template"
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
"area": "templates",
|
|
31
|
+
"path": "assets/opencode/templates/bros/security-review.md",
|
|
32
|
+
"sourceRef": "opencode-template"
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
"area": "templates",
|
|
36
|
+
"path": "assets/opencode/templates/bros/status-board.md",
|
|
37
|
+
"sourceRef": "opencode-template"
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
"area": "templates",
|
|
41
|
+
"path": "assets/opencode/templates/bros/task-packet.md",
|
|
42
|
+
"sourceRef": "opencode-template"
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
"area": "templates",
|
|
46
|
+
"path": "assets/opencode/templates/bros/test-strategy.md",
|
|
47
|
+
"sourceRef": "opencode-template"
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
"area": "templates",
|
|
51
|
+
"path": "assets/opencode/templates/bros/ui-implementation-packet.md",
|
|
52
|
+
"sourceRef": "opencode-template"
|
|
53
|
+
}
|
|
54
|
+
]
|
|
55
|
+
}
|
package/bin/bros.mjs
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { access, readFile, readdir } from "node:fs/promises";
|
|
3
|
+
import { dirname, join } from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
|
|
6
|
+
const packageRoot = dirname(dirname(fileURLToPath(import.meta.url)));
|
|
7
|
+
const assetRoot = join(packageRoot, "assets", "opencode");
|
|
8
|
+
const manifestPath = join(packageRoot, "assets", "manifest.json");
|
|
9
|
+
|
|
10
|
+
const commands = [
|
|
11
|
+
["help", "Show available BROS Harness commands."],
|
|
12
|
+
["snippet", "Print the package-first OpenCode plugin snippet."],
|
|
13
|
+
["doctor", "Validate package asset directories and manifest shape without mutation."],
|
|
14
|
+
["list-assets", "Summarize packaged OpenCode agent, command, skill, doc, and template counts."],
|
|
15
|
+
["agent-install-prompt", "Print a safe prompt an AI agent can follow to add the plugin snippet."]
|
|
16
|
+
];
|
|
17
|
+
|
|
18
|
+
const requiredPaths = [
|
|
19
|
+
"assets/opencode/agents",
|
|
20
|
+
"assets/opencode/commands",
|
|
21
|
+
"assets/opencode/skills",
|
|
22
|
+
"assets/opencode/templates",
|
|
23
|
+
"assets/opencode/docs",
|
|
24
|
+
"assets/manifest.json",
|
|
25
|
+
"src/plugin.mjs",
|
|
26
|
+
"package.json"
|
|
27
|
+
];
|
|
28
|
+
|
|
29
|
+
function printHelp() {
|
|
30
|
+
console.log("BROS Harness CLI");
|
|
31
|
+
console.log("");
|
|
32
|
+
console.log("Usage: bros <command>");
|
|
33
|
+
console.log("");
|
|
34
|
+
console.log("Commands:");
|
|
35
|
+
for (const [name, description] of commands) {
|
|
36
|
+
console.log(` ${name.padEnd(22)} ${description}`);
|
|
37
|
+
}
|
|
38
|
+
console.log("");
|
|
39
|
+
console.log("All commands are read-only. This CLI does not edit live OpenCode config.");
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function printSnippet() {
|
|
43
|
+
console.log(JSON.stringify({ plugin: ["bros-harness"] }, null, 2));
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
async function readJson(path) {
|
|
47
|
+
return JSON.parse(await readFile(path, "utf8"));
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
async function doctor() {
|
|
51
|
+
for (const relativePath of requiredPaths) {
|
|
52
|
+
await access(join(packageRoot, relativePath));
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const [manifest, packageJson] = await Promise.all([
|
|
56
|
+
readJson(manifestPath),
|
|
57
|
+
readJson(join(packageRoot, "package.json"))
|
|
58
|
+
]);
|
|
59
|
+
|
|
60
|
+
if (packageJson.name !== "bros-harness") throw new Error("package.json name must be bros-harness.");
|
|
61
|
+
if (packageJson.main !== "./src/plugin.mjs") throw new Error("package.json main must point to ./src/plugin.mjs.");
|
|
62
|
+
if (packageJson.exports?.["."] !== "./src/plugin.mjs") throw new Error("package.json exports must expose ./src/plugin.mjs.");
|
|
63
|
+
if (!packageJson.bin?.bros) throw new Error("package.json must expose the bros CLI bin.");
|
|
64
|
+
if (manifest.name !== "bros-harness" || !Array.isArray(manifest.entries)) throw new Error("Invalid asset manifest shape.");
|
|
65
|
+
if (manifest.counts?.skills?.skipped !== 3) throw new Error("Expected exactly 3 skipped skills to remain skipped.");
|
|
66
|
+
|
|
67
|
+
console.log("BROS Harness doctor: ok");
|
|
68
|
+
console.log(`Manifest entries: ${manifest.entries.length}`);
|
|
69
|
+
console.log("No filesystem changes were made.");
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
async function countMarkdownFiles(relativeDir) {
|
|
73
|
+
const files = await readdir(join(assetRoot, relativeDir));
|
|
74
|
+
return files.filter((file) => file.endsWith(".md")).length;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
async function listAssets() {
|
|
78
|
+
const manifest = await readJson(manifestPath);
|
|
79
|
+
const counts = manifest.counts ?? {};
|
|
80
|
+
console.log("BROS Harness packaged assets");
|
|
81
|
+
console.log(`Agents: ${counts.agents?.imported ?? await countMarkdownFiles("agents")}`);
|
|
82
|
+
console.log(`Commands: ${counts.commands?.imported ?? await countMarkdownFiles("commands")}`);
|
|
83
|
+
console.log(`Skills: ${counts.skills?.imported ?? "unknown"} imported, ${counts.skills?.skipped ?? "unknown"} skipped`);
|
|
84
|
+
console.log(`Docs: ${counts.docs?.imported ?? await countMarkdownFiles("docs")}`);
|
|
85
|
+
console.log(`Templates: ${counts.templates?.imported ?? "unknown"}`);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function printAgentInstallPrompt() {
|
|
89
|
+
console.log(`Add BROS Harness to OpenCode using the package plugin snippet only.\n\nConstraints:\n- Do not install dependencies unless the human explicitly approves it.\n- Do not publish packages.\n- Do not edit provider, MCP, permission, telemetry, secret, or credential settings.\n- Do not overwrite existing config. Merge only the plugin entry if OpenCode config exists.\n- If unsure, show the diff and ask.\n\nSnippet to add:\n{\n "plugin": ["bros-harness"]\n}\n\nAfter editing OpenCode config, tell the human to restart OpenCode because config is loaded at startup.`);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const command = process.argv[2] ?? "help";
|
|
93
|
+
|
|
94
|
+
try {
|
|
95
|
+
switch (command) {
|
|
96
|
+
case "help":
|
|
97
|
+
case "--help":
|
|
98
|
+
case "-h":
|
|
99
|
+
printHelp();
|
|
100
|
+
break;
|
|
101
|
+
case "snippet":
|
|
102
|
+
printSnippet();
|
|
103
|
+
break;
|
|
104
|
+
case "doctor":
|
|
105
|
+
await doctor();
|
|
106
|
+
break;
|
|
107
|
+
case "list-assets":
|
|
108
|
+
await listAssets();
|
|
109
|
+
break;
|
|
110
|
+
case "agent-install-prompt":
|
|
111
|
+
printAgentInstallPrompt();
|
|
112
|
+
break;
|
|
113
|
+
default:
|
|
114
|
+
console.error(`Unknown command: ${command}`);
|
|
115
|
+
printHelp();
|
|
116
|
+
process.exitCode = 1;
|
|
117
|
+
}
|
|
118
|
+
} catch (error) {
|
|
119
|
+
console.error(`BROS Harness ${command}: failed`);
|
|
120
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
121
|
+
process.exitCode = 1;
|
|
122
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# Compatibility
|
|
2
|
+
|
|
3
|
+
## OpenCode
|
|
4
|
+
|
|
5
|
+
OpenCode is the supported first target. Assets are organized under `assets/opencode/` using familiar agent, command, skill, template, and documentation areas.
|
|
6
|
+
|
|
7
|
+
## Claude, Codex, and IDEs
|
|
8
|
+
|
|
9
|
+
Claude, Codex, and IDE integrations are roadmap targets. The adapter SDK currently defines interfaces only and does not provide working installers for those environments.
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# Installation
|
|
2
|
+
|
|
3
|
+
BROS Harness is structured as a package-first OpenCode plugin. The primary configuration path is the package snippet, not copying local development folders.
|
|
4
|
+
|
|
5
|
+
## For humans
|
|
6
|
+
|
|
7
|
+
Add the plugin package to OpenCode config:
|
|
8
|
+
|
|
9
|
+
```json
|
|
10
|
+
{
|
|
11
|
+
"plugin": ["bros-harness"]
|
|
12
|
+
}
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Restart OpenCode after editing config. The running session keeps the config it loaded at startup.
|
|
16
|
+
|
|
17
|
+
Optional read-only checks after the package is available:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
bros snippet
|
|
21
|
+
bros doctor
|
|
22
|
+
bros list-assets
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## For AI agents
|
|
26
|
+
|
|
27
|
+
Use a bounded instruction:
|
|
28
|
+
|
|
29
|
+
```text
|
|
30
|
+
Configure BROS Harness with { "plugin": ["bros-harness"] }. Do not install dependencies, publish, mutate provider/MCP/permission/telemetry/secret settings, or overwrite existing config files. Merge only the plugin entry if approved, show the proposed diff, and remind the human to restart OpenCode.
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
The package helper can print this prompt:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
bros agent-install-prompt
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## What the plugin changes
|
|
40
|
+
|
|
41
|
+
- Uses OpenCode's in-memory `config(cfg)` hook at startup.
|
|
42
|
+
- Adds package-relative BROS skills to `skills.paths` only when the existing field shape is schema-compatible.
|
|
43
|
+
- Adds packaged BROS command prompt entries to `command` without replacing existing command keys.
|
|
44
|
+
|
|
45
|
+
## What the plugin does not change
|
|
46
|
+
|
|
47
|
+
- No providers.
|
|
48
|
+
- No MCP servers.
|
|
49
|
+
- No permission changes.
|
|
50
|
+
- No telemetry.
|
|
51
|
+
- No secrets or credential validation.
|
|
52
|
+
- No provider, MCP, permission, telemetry, or secret registration.
|
|
53
|
+
- No filesystem writes.
|
|
54
|
+
- No live user config file mutation; `opencode.json`, `.opencode/`, and global config files are not written by the package plugin.
|
|
55
|
+
|
|
56
|
+
## Local contributor checks
|
|
57
|
+
|
|
58
|
+
For repository development only:
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
npm run validate
|
|
62
|
+
node bin/bros.mjs doctor
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Publishing and dependency installation remain separate gated actions.
|
|
66
|
+
Asset import is maintainer-only source maintenance for repository asset refreshes, not part of package installation. Package users should rely on the plugin snippet and read-only CLI helpers above; import tooling is not exposed as an installed package command.
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# OpenCode Integration
|
|
2
|
+
|
|
3
|
+
OpenCode is the primary integration target for BROS Harness.
|
|
4
|
+
|
|
5
|
+
## Package snippet
|
|
6
|
+
|
|
7
|
+
Use the package plugin entry:
|
|
8
|
+
|
|
9
|
+
```json
|
|
10
|
+
{
|
|
11
|
+
"plugin": ["bros-harness"]
|
|
12
|
+
}
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
This is the preferred path for users and agents. Local path examples are contributor-only.
|
|
16
|
+
|
|
17
|
+
## Packaged assets
|
|
18
|
+
|
|
19
|
+
- Agents: `assets/opencode/agents/`
|
|
20
|
+
- Commands: `assets/opencode/commands/`
|
|
21
|
+
- Skills: `assets/opencode/skills/`
|
|
22
|
+
- Templates: `assets/opencode/templates/`
|
|
23
|
+
- Docs: `assets/opencode/docs/`
|
|
24
|
+
|
|
25
|
+
## Runtime behavior
|
|
26
|
+
|
|
27
|
+
The package plugin resolves assets relative to its own package root. It validates key asset directories, then uses OpenCode's in-memory `config(cfg)` hook only to add the package-relative skills directory to `skills.paths` when safe and add packaged command prompt entries without overwriting existing commands.
|
|
28
|
+
|
|
29
|
+
This runtime hook changes only the merged config object OpenCode passes to the plugin at startup. It is distinct from live user config file mutation: the plugin does not write `opencode.json`, `.opencode/`, global config files, or other filesystem config. The plugin does not register providers, MCP servers, permissions, telemetry, or secrets. Agent files are packaged as reviewed assets, but permission-bearing agent registration is intentionally not performed by the default plugin hook.
|
|
30
|
+
|
|
31
|
+
## Safe agent workflow
|
|
32
|
+
|
|
33
|
+
Agents should use:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
bros agent-install-prompt
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
The prompt instructs agents to merge only `plugin: ["bros-harness"]`, avoid sensitive config surfaces, and ask before writing.
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# Migrating From Local OpenCode Configuration
|
|
2
|
+
|
|
3
|
+
Do not copy raw local OpenCode configuration into this repository. Local config files can include provider keys, private endpoints, MCP credentials, or other sensitive values.
|
|
4
|
+
|
|
5
|
+
Recommended migration approach:
|
|
6
|
+
|
|
7
|
+
1. Inventory local agents, commands, skills, templates, and docs only.
|
|
8
|
+
2. Exclude `opencode.json`, `opencode.jsonc`, `.env*`, logs, caches, package artifacts, and shell history.
|
|
9
|
+
3. Replace any local-only values with placeholders before committing examples.
|
|
10
|
+
4. Run `npm run validate` before review.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# Release Process
|
|
2
|
+
|
|
3
|
+
Publishing is not approved in the initial scaffold.
|
|
4
|
+
|
|
5
|
+
Required release gates:
|
|
6
|
+
|
|
7
|
+
1. Asset review confirms no private data or local-only assumptions.
|
|
8
|
+
2. Security review approves package contents and examples.
|
|
9
|
+
3. QA validates CLI, manifests, and documentation.
|
|
10
|
+
4. Maintainers confirm package allowlist and changelog.
|
|
11
|
+
5. Release automation is enabled only after explicit approval.
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# Repository Structure
|
|
2
|
+
|
|
3
|
+
```text
|
|
4
|
+
assets/opencode/ Curated OpenCode assets
|
|
5
|
+
bin/ Executable CLI shim
|
|
6
|
+
docs/ User and maintainer documentation
|
|
7
|
+
examples/opencode/ Placeholder-only OpenCode examples
|
|
8
|
+
packages/adapter-sdk/ Future adapter interfaces
|
|
9
|
+
packages/cli/ CLI TypeScript source skeleton
|
|
10
|
+
packages/manifest/ Manifest schema and validation helpers
|
|
11
|
+
packages/opencode-plugin/ OpenCode plugin skeleton
|
|
12
|
+
scripts/ Dependency-free validation scripts
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Session records are stored under `.bros/` and are not included in the npm package allowlist.
|
package/docs/roadmap.md
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# Roadmap
|
|
2
|
+
|
|
3
|
+
## Now
|
|
4
|
+
|
|
5
|
+
- Sanitized OpenCode asset scaffold.
|
|
6
|
+
- Package metadata and safe CLI placeholder.
|
|
7
|
+
- Validation scripts and guarded CI.
|
|
8
|
+
|
|
9
|
+
## Next
|
|
10
|
+
|
|
11
|
+
- Review imported assets for public readability and policy clarity.
|
|
12
|
+
- Implement manifest-driven OpenCode install flow with explicit user confirmation.
|
|
13
|
+
- Add adapter implementations after design and security review.
|
|
14
|
+
|
|
15
|
+
## Later
|
|
16
|
+
|
|
17
|
+
- Claude adapter.
|
|
18
|
+
- Codex adapter.
|
|
19
|
+
- IDE extension guidance.
|
|
20
|
+
- Release automation after final approval.
|
package/docs/security.md
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# Security
|
|
2
|
+
|
|
3
|
+
This repository intentionally avoids raw provider configuration and secret validation. The scaffold includes only curated asset directories and placeholder examples.
|
|
4
|
+
|
|
5
|
+
## Guardrails
|
|
6
|
+
|
|
7
|
+
- No raw OpenCode config import.
|
|
8
|
+
- No dependency install, publish, deploy, or production access in the initial build.
|
|
9
|
+
- No examples with live credentials.
|
|
10
|
+
- Runtime plugin config changes are limited to OpenCode's in-memory `config(cfg)` hook for package-relative `skills.paths` and non-overwriting command prompt entries.
|
|
11
|
+
- No live user config file mutation: the package plugin does not write `opencode.json`, `.opencode/`, global config files, or other filesystem config.
|
|
12
|
+
- No provider, MCP, permission, telemetry, or secret registration by the package plugin.
|
|
13
|
+
- Mutating contributor import tooling is maintainer-only and excluded from the published package surface.
|
|
14
|
+
- Final publishing requires a fresh security review.
|
|
15
|
+
|
|
16
|
+
## Validation
|
|
17
|
+
|
|
18
|
+
`scripts/verify-no-secrets.mjs` provides dependency-free checks for common secret-like patterns. It is not a replacement for human security review.
|
package/docs/testing.md
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# OpenCode Example
|
|
2
|
+
|
|
3
|
+
The package-first example uses the BROS Harness plugin snippet:
|
|
4
|
+
|
|
5
|
+
```json
|
|
6
|
+
{
|
|
7
|
+
"plugin": ["bros-harness"]
|
|
8
|
+
}
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
This example does not include provider keys, private endpoints, MCP servers, permissions, telemetry, credentials, or local absolute paths. Restart OpenCode after applying an approved config change.
|
package/package.json
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "bros-harness",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Package-first OpenCode plugin for disciplined BROS agent harness assets.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"main": "./src/plugin.mjs",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": "./src/plugin.mjs",
|
|
10
|
+
"./plugin": "./src/plugin.mjs",
|
|
11
|
+
"./package.json": "./package.json"
|
|
12
|
+
},
|
|
13
|
+
"bin": {
|
|
14
|
+
"bros": "./bin/bros.mjs"
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"assets/",
|
|
18
|
+
"bin/",
|
|
19
|
+
"docs/",
|
|
20
|
+
"examples/",
|
|
21
|
+
"scripts/validate-assets.mjs",
|
|
22
|
+
"scripts/verify-no-secrets.mjs",
|
|
23
|
+
"src/",
|
|
24
|
+
"README.md",
|
|
25
|
+
"LICENSE",
|
|
26
|
+
"SECURITY.md",
|
|
27
|
+
"CHANGELOG.md"
|
|
28
|
+
],
|
|
29
|
+
"scripts": {
|
|
30
|
+
"validate": "node scripts/validate-assets.mjs && node scripts/verify-no-secrets.mjs",
|
|
31
|
+
"validate:assets": "node scripts/validate-assets.mjs",
|
|
32
|
+
"verify:no-secrets": "node scripts/verify-no-secrets.mjs"
|
|
33
|
+
},
|
|
34
|
+
"keywords": [
|
|
35
|
+
"opencode",
|
|
36
|
+
"agents",
|
|
37
|
+
"harness",
|
|
38
|
+
"cli"
|
|
39
|
+
],
|
|
40
|
+
"engines": {
|
|
41
|
+
"node": ">=20"
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { access, readFile } from "node:fs/promises";
|
|
3
|
+
|
|
4
|
+
const requiredPaths = [
|
|
5
|
+
"assets/opencode/agents",
|
|
6
|
+
"assets/opencode/commands",
|
|
7
|
+
"assets/opencode/skills",
|
|
8
|
+
"assets/opencode/templates",
|
|
9
|
+
"assets/opencode/docs",
|
|
10
|
+
"assets/manifest.json"
|
|
11
|
+
];
|
|
12
|
+
|
|
13
|
+
for (const path of requiredPaths) {
|
|
14
|
+
await access(path);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const manifest = JSON.parse(await readFile("assets/manifest.json", "utf8"));
|
|
18
|
+
if (manifest.name !== "bros-harness" || !Array.isArray(manifest.entries)) {
|
|
19
|
+
throw new Error("Invalid asset manifest shape.");
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
console.log(`Validated ${manifest.entries.length} manifest entries.`);
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { readdir, readFile } from "node:fs/promises";
|
|
3
|
+
import { join, relative } from "node:path";
|
|
4
|
+
|
|
5
|
+
const root = process.cwd();
|
|
6
|
+
const ignored = new Set([".git", "node_modules", "dist", "coverage"]);
|
|
7
|
+
const secretPatterns = [
|
|
8
|
+
/api[_-]?key\s*[:=]\s*['\"][^'\"]{8,}/i,
|
|
9
|
+
/authorization\s*[:=]\s*['\"]?bearer\s+[a-z0-9._-]{8,}/i,
|
|
10
|
+
/-----BEGIN (?:RSA |OPENSSH |EC |DSA )?PRIVATE KEY-----/,
|
|
11
|
+
/(?:token|secret|password)\s*[:=]\s*['\"][^'\"]{8,}/i
|
|
12
|
+
];
|
|
13
|
+
|
|
14
|
+
async function* walk(dir) {
|
|
15
|
+
for (const entry of await readdir(dir, { withFileTypes: true })) {
|
|
16
|
+
if (ignored.has(entry.name)) continue;
|
|
17
|
+
const path = join(dir, entry.name);
|
|
18
|
+
if (entry.isDirectory()) yield* walk(path);
|
|
19
|
+
else yield path;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const findings = [];
|
|
24
|
+
for await (const file of walk(root)) {
|
|
25
|
+
const rel = relative(root, file);
|
|
26
|
+
const text = await readFile(file, "utf8").catch(() => "");
|
|
27
|
+
for (const pattern of secretPatterns) {
|
|
28
|
+
if (pattern.test(text)) findings.push(rel);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (findings.length > 0) {
|
|
33
|
+
console.error("Potential secret-like content found in files:");
|
|
34
|
+
for (const file of findings) console.error(`- ${file}`);
|
|
35
|
+
process.exit(1);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
console.log("No secret-like content detected by scaffold patterns.");
|
package/src/plugin.mjs
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { access, readdir, readFile } from "node:fs/promises";
|
|
2
|
+
import { dirname, join } from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
|
|
5
|
+
const packageRoot = dirname(dirname(fileURLToPath(import.meta.url)));
|
|
6
|
+
const assetRoot = join(packageRoot, "assets", "opencode");
|
|
7
|
+
|
|
8
|
+
export const brosHarness = Object.freeze({
|
|
9
|
+
name: "bros-harness",
|
|
10
|
+
packageRoot,
|
|
11
|
+
assetRoot,
|
|
12
|
+
agentsDir: join(assetRoot, "agents"),
|
|
13
|
+
commandsDir: join(assetRoot, "commands"),
|
|
14
|
+
skillsDir: join(assetRoot, "skills"),
|
|
15
|
+
docsDir: join(assetRoot, "docs"),
|
|
16
|
+
templatesDir: join(assetRoot, "templates")
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
const requiredAssetDirs = [
|
|
20
|
+
brosHarness.agentsDir,
|
|
21
|
+
brosHarness.commandsDir,
|
|
22
|
+
brosHarness.skillsDir,
|
|
23
|
+
brosHarness.docsDir,
|
|
24
|
+
brosHarness.templatesDir
|
|
25
|
+
];
|
|
26
|
+
|
|
27
|
+
export async function verifyBrosHarnessAssets() {
|
|
28
|
+
await Promise.all(requiredAssetDirs.map((path) => access(path)));
|
|
29
|
+
return brosHarness;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function parseCommandMarkdown(markdown) {
|
|
33
|
+
const match = markdown.match(/^---\n([\s\S]*?)\n---\n?([\s\S]*)$/);
|
|
34
|
+
if (!match) {
|
|
35
|
+
return { description: "BROS Harness command.", prompt: markdown.trim() };
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const descriptionMatch = match[1].match(/^description:\s*(.+)$/m);
|
|
39
|
+
const description = descriptionMatch?.[1]?.replace(/^['\"]|['\"]$/g, "").trim();
|
|
40
|
+
return {
|
|
41
|
+
description: description || "BROS Harness command.",
|
|
42
|
+
prompt: match[2].trim()
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
async function loadPackagedCommands() {
|
|
47
|
+
const files = (await readdir(brosHarness.commandsDir))
|
|
48
|
+
.filter((file) => file.endsWith(".md"))
|
|
49
|
+
.filter((file) => file !== "README.md")
|
|
50
|
+
.sort();
|
|
51
|
+
|
|
52
|
+
const commands = {};
|
|
53
|
+
for (const file of files) {
|
|
54
|
+
const name = file.replace(/\.md$/, "");
|
|
55
|
+
const markdown = await readFile(join(brosHarness.commandsDir, file), "utf8");
|
|
56
|
+
commands[name] = parseCommandMarkdown(markdown);
|
|
57
|
+
}
|
|
58
|
+
return commands;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function mergeSkillsPath(cfg) {
|
|
62
|
+
if (cfg.skills !== undefined && (cfg.skills === null || typeof cfg.skills !== "object" || Array.isArray(cfg.skills))) {
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
cfg.skills ??= {};
|
|
67
|
+
if (cfg.skills.paths !== undefined && !Array.isArray(cfg.skills.paths)) {
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
cfg.skills.paths ??= [];
|
|
72
|
+
if (!cfg.skills.paths.includes(brosHarness.skillsDir)) {
|
|
73
|
+
cfg.skills.paths.push(brosHarness.skillsDir);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function mergeCommands(cfg, commands) {
|
|
78
|
+
if (cfg.command !== undefined && (cfg.command === null || typeof cfg.command !== "object" || Array.isArray(cfg.command))) {
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
cfg.command ??= {};
|
|
83
|
+
for (const [name, command] of Object.entries(commands)) {
|
|
84
|
+
cfg.command[name] ??= command;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export default async function brosHarnessPlugin(_input = {}, _options = {}) {
|
|
89
|
+
await verifyBrosHarnessAssets();
|
|
90
|
+
const commands = await loadPackagedCommands();
|
|
91
|
+
|
|
92
|
+
return {
|
|
93
|
+
config(cfg) {
|
|
94
|
+
mergeSkillsPath(cfg);
|
|
95
|
+
mergeCommands(cfg, commands);
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
}
|