mirror of
https://github.com/alirezarezvani/claude-skills.git
synced 2026-09-06 08:15:58 +00:00
Hivemind delegates grunt work to headless opencode workers (scout/coder/tester) while the orchestrating agent stays the planner, reviewer, and merger. - placed under engineering/ per repo layout; targets dev per CONTRIBUTING - frontmatter is name + description only; SKILL.md is 165 lines - anti-patterns and cross-references sections included - external opencode CLI dependency and free-model caveats documented up front - runtime state (.runs/) gitignored, not committed
79 lines
2.7 KiB
JavaScript
79 lines
2.7 KiB
JavaScript
import { readFileSync } from "node:fs";
|
|
|
|
function readInput() {
|
|
const args = process.argv.slice(2).filter(a => a !== "--findings-only");
|
|
const findingsOnly = process.argv.includes("--findings-only");
|
|
let raw = "";
|
|
if (args.length) {
|
|
for (const f of args) {
|
|
try { raw += readFileSync(f, "utf8") + "\n"; }
|
|
catch { console.log(JSON.stringify({ ok: false, stage: "aggregate", error: `cannot read file: ${f}` })); process.exit(0); }
|
|
}
|
|
} else raw = readFileSync(0, "utf8");
|
|
return { raw, findingsOnly };
|
|
}
|
|
|
|
function normalizeLine(s) {
|
|
return s.toLowerCase().replace(/[\s\p{P}]+/gu, " ").trim();
|
|
}
|
|
|
|
function extractFindings(text) {
|
|
const out = [];
|
|
for (const line of text.split(/\r?\n/)) {
|
|
const t = line.trim();
|
|
if (/^([-*•]|\d+[.)])\s+/.test(t) && t.length > 12) out.push(t.replace(/^([-*•]|\d+[.)])\s+/, "").trim());
|
|
else if (/^(file|path|src|lib)\b.*:\d+/i.test(t) && t.length > 12) out.push(t);
|
|
}
|
|
return out;
|
|
}
|
|
|
|
function main() {
|
|
const { raw, findingsOnly } = readInput();
|
|
const workers = [];
|
|
for (const line of raw.split(/\r?\n/).filter(l => l.trim())) {
|
|
try {
|
|
const j = JSON.parse(line.trim());
|
|
if (!("ok" in j)) continue;
|
|
workers.push(j);
|
|
} catch {}
|
|
}
|
|
if (!workers.length) { console.log(JSON.stringify({ ok: false, stage: "aggregate", error: "no worker JSON lines found in input" })); return; }
|
|
|
|
const ok = workers.filter(w => w.ok === true);
|
|
const failed = workers.filter(w => w.ok !== true);
|
|
const totalTokens = ok.reduce((n, w) => n + (w.tokens?.total ?? 0), 0);
|
|
|
|
const seen = new Map();
|
|
let anonIdx = 0;
|
|
for (const w of ok) {
|
|
const src = w.label || w.agent || `worker-${++anonIdx}`;
|
|
for (const f of extractFindings(w.result || "")) {
|
|
const key = normalizeLine(f);
|
|
if (!seen.has(key)) seen.set(key, { finding: f, sources: [src] });
|
|
else if (!seen.get(key).sources.includes(src)) seen.get(key).sources.push(src);
|
|
}
|
|
}
|
|
const unique = [...seen.values()].sort((a, b) => b.sources.length - a.sources.length).slice(0, 60);
|
|
|
|
const digest = {
|
|
ok: true,
|
|
workers: workers.length,
|
|
succeeded: ok.length,
|
|
failed: failed.length,
|
|
total_tokens: totalTokens,
|
|
unique_findings: unique.length,
|
|
failures: failed.map(w => ({ label: w.label || w.agent, error: (w.error || "unknown").slice(0, 120) })),
|
|
consensus: unique.filter(u => u.sources.length > 1),
|
|
findings: unique,
|
|
};
|
|
|
|
if (findingsOnly) {
|
|
console.log(digest.consensus.map(c => `[${c.sources.join("+")}] ${c.finding}`).join("\n"));
|
|
console.log("---");
|
|
console.log(digest.findings.filter(f => f.sources.length === 1).map(c => `[${c.sources[0]}] ${c.finding}`).join("\n"));
|
|
return;
|
|
}
|
|
console.log(JSON.stringify(digest, null, 1));
|
|
}
|
|
|
|
main();
|