bros-harness 0.1.1 → 0.1.2
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 +4 -0
- package/package.json +1 -1
- package/src/plugin.mjs +87 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.1.2 - 2026-06-03
|
|
4
|
+
|
|
5
|
+
- Added package plugin agent registration so raw OpenCode installs can load BROS agents from `plugin: ["bros-harness"]`.
|
|
6
|
+
|
|
3
7
|
## 0.1.1 - 2026-06-03
|
|
4
8
|
|
|
5
9
|
- Fixed npm CLI bin packaging posture for `bros` by ensuring the packaged `bin/bros.mjs` entry is executable with a valid Node shebang.
|
package/package.json
CHANGED
package/src/plugin.mjs
CHANGED
|
@@ -43,6 +43,80 @@ function parseCommandMarkdown(markdown) {
|
|
|
43
43
|
};
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
+
function parseYamlScalar(value) {
|
|
47
|
+
const trimmed = value.trim();
|
|
48
|
+
if (trimmed === "") return "";
|
|
49
|
+
if (trimmed === "true") return true;
|
|
50
|
+
if (trimmed === "false") return false;
|
|
51
|
+
if ((trimmed.startsWith('"') && trimmed.endsWith('"')) || (trimmed.startsWith("'") && trimmed.endsWith("'"))) {
|
|
52
|
+
return trimmed.slice(1, -1);
|
|
53
|
+
}
|
|
54
|
+
return trimmed;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function parseSimpleYamlObject(yaml) {
|
|
58
|
+
const root = {};
|
|
59
|
+
const stack = [{ indent: -1, value: root }];
|
|
60
|
+
|
|
61
|
+
for (const rawLine of yaml.split("\n")) {
|
|
62
|
+
if (!rawLine.trim() || rawLine.trim().startsWith("#")) continue;
|
|
63
|
+
|
|
64
|
+
const indent = rawLine.match(/^ */)?.[0].length ?? 0;
|
|
65
|
+
const line = rawLine.trim();
|
|
66
|
+
const match = line.match(/^(.+?):(?:\s*(.*))?$/);
|
|
67
|
+
if (!match) continue;
|
|
68
|
+
|
|
69
|
+
const key = match[1].trim().replace(/^['"]|['"]$/g, "");
|
|
70
|
+
const rawValue = match[2] ?? "";
|
|
71
|
+
|
|
72
|
+
while (stack.length > 1 && indent <= stack[stack.length - 1].indent) {
|
|
73
|
+
stack.pop();
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const parent = stack[stack.length - 1].value;
|
|
77
|
+
if (rawValue === "") {
|
|
78
|
+
parent[key] = {};
|
|
79
|
+
stack.push({ indent, value: parent[key] });
|
|
80
|
+
} else {
|
|
81
|
+
parent[key] = parseYamlScalar(rawValue);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return root;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function parseAgentMarkdown(markdown) {
|
|
89
|
+
const match = markdown.match(/^---\n([\s\S]*?)\n---\n?([\s\S]*)$/);
|
|
90
|
+
if (!match) return null;
|
|
91
|
+
|
|
92
|
+
const frontmatter = parseSimpleYamlObject(match[1]);
|
|
93
|
+
const { name, ...agent } = frontmatter;
|
|
94
|
+
if (!name) return null;
|
|
95
|
+
|
|
96
|
+
return {
|
|
97
|
+
name,
|
|
98
|
+
agent: {
|
|
99
|
+
...agent,
|
|
100
|
+
prompt: match[2].trim()
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
async function loadPackagedAgents() {
|
|
106
|
+
const files = (await readdir(brosHarness.agentsDir))
|
|
107
|
+
.filter((file) => file.endsWith(".md"))
|
|
108
|
+
.filter((file) => file !== "README.md")
|
|
109
|
+
.sort();
|
|
110
|
+
|
|
111
|
+
const agents = {};
|
|
112
|
+
for (const file of files) {
|
|
113
|
+
const markdown = await readFile(join(brosHarness.agentsDir, file), "utf8");
|
|
114
|
+
const parsed = parseAgentMarkdown(markdown);
|
|
115
|
+
if (parsed) agents[parsed.name] = parsed.agent;
|
|
116
|
+
}
|
|
117
|
+
return agents;
|
|
118
|
+
}
|
|
119
|
+
|
|
46
120
|
async function loadPackagedCommands() {
|
|
47
121
|
const files = (await readdir(brosHarness.commandsDir))
|
|
48
122
|
.filter((file) => file.endsWith(".md"))
|
|
@@ -85,13 +159,26 @@ function mergeCommands(cfg, commands) {
|
|
|
85
159
|
}
|
|
86
160
|
}
|
|
87
161
|
|
|
162
|
+
function mergeAgents(cfg, agents) {
|
|
163
|
+
if (cfg.agent !== undefined && (cfg.agent === null || typeof cfg.agent !== "object" || Array.isArray(cfg.agent))) {
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
cfg.agent ??= {};
|
|
168
|
+
for (const [name, agent] of Object.entries(agents)) {
|
|
169
|
+
cfg.agent[name] ??= agent;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
88
173
|
export default async function brosHarnessPlugin(_input = {}, _options = {}) {
|
|
89
174
|
await verifyBrosHarnessAssets();
|
|
175
|
+
const agents = await loadPackagedAgents();
|
|
90
176
|
const commands = await loadPackagedCommands();
|
|
91
177
|
|
|
92
178
|
return {
|
|
93
179
|
config(cfg) {
|
|
94
180
|
mergeSkillsPath(cfg);
|
|
181
|
+
mergeAgents(cfg, agents);
|
|
95
182
|
mergeCommands(cfg, commands);
|
|
96
183
|
}
|
|
97
184
|
};
|