mirror of
https://github.com/garrytan/gbrain.git
synced 2026-07-31 04:07:52 +00:00
* feat(skillpack): brain_resident manifest fields + init-brain-pack scaffolder + tools version-skew lint Add optional brain_resident/schema_pack to the v1 manifest (additive, forward-compatible). New runInitBrainPack scaffolds a brain-resident pack (brain_resident:true, exact gbrain_min_version, 5-section machine-parseable README) beside brain content; applyWritePlan factored out of init-scaffold. brain-pack-lint validates each skill's declared tools: against the serving op set (E6 version-skew). Wires gbrain skillpack init-brain-pack. * feat(skillpack): Topology A brain-pack discovery on sources add + bounded nag After 'gbrain sources add', if the source ships a brain_resident pack, print an agent-readable advisory (ask the user before scaffolding). nag-state.ts tracks declines per (source-repo brain_id, source, pack) with escalate-then-suppress; declines count only on CLI-interactive displays, never cron/MCP. Fail-open: a malformed/absent pack never breaks sources add. * feat(advisor,skillpack): list_brain_skillpack MCP tool + gbrain advisor Topology B: dedicated source-scoped list_brain_skillpack op + get_skill source_id disambiguation (brain-resident-locate.ts); git scaffold-spec never a server FS path; source-aware schema match. LEARN_INSTRUCTION + serve-http banner. gbrain advisor: read-only ranked actions from brain state (8 resilient collectors, shared renderer, JSONL history, --json severity exit codes, local-only argv --apply dispatcher). Exposed over MCP behind mcp.publish_advisor (default off, read-only on remote; workspace collectors no-op remotely). Generalizes post-install-advisory to a single current-state recommended set (install→scaffold). * feat(skills): bundle gbrain-advisor skill + weekly cron recipe + ranking eval skills/gbrain-advisor teaches a harness to run gbrain advisor on a cadence and ping the user (read-only; ask before fixing). Registered in manifest.json, RESOLVER.md, openclaw.plugin.json. E4 ranking-precision eval on seeded-defect fixtures (100%). * chore: bump version and changelog (v0.42.47.0) Brain-resident skillpacks + gbrain advisor (#2180). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs: sync CLAUDE.md + KEY_FILES for brain-resident skillpacks + advisor (#2180) Skill count 29->30, Skills section gains the brain-resident skillpacks + advisor capability, KEY_FILES gets current-state entries for the new modules. Regenerate llms bundles. * test,fix: align stale assertions with generalized advisory + advisor resolver triggers (#2180) - post-install-advisory.test.ts: install→scaffold wording (the install verb was removed); restore two-column in book-mirror copy; drop the removed skillpack-list line. - RESOLVER.md: gbrain-advisor trigger now fuzzy-matches a declared frontmatter trigger (resolver round-trip D5/C). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs: regenerate llms bundle for updated advisor resolver row (#2180) --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
128 lines
4.4 KiB
TypeScript
128 lines
4.4 KiB
TypeScript
/**
|
|
* advisor/render.ts — the shared `=`-bar agent-readable renderer.
|
|
*
|
|
* One renderer for BOTH `gbrain advisor` and the post-install advisory so the
|
|
* two surfaces never drift (eng-review: shared render). Print-never-execute: the
|
|
* output tells the harness to show the user and ask before acting.
|
|
*/
|
|
|
|
import type { AdvisorFinding, AdvisorReport, AdvisorSeverity } from './types.ts';
|
|
|
|
const BAR = '='.repeat(72);
|
|
|
|
const SEV_LABEL: Record<AdvisorSeverity, string> = {
|
|
critical: 'CRITICAL',
|
|
warn: 'WARN',
|
|
info: 'INFO',
|
|
};
|
|
|
|
function fixLine(f: AdvisorFinding): string | null {
|
|
if (!f.fix.command_argv || f.fix.command_argv.length === 0) return null;
|
|
return f.fix.command_argv.join(' ');
|
|
}
|
|
|
|
/** Render a full advisor report as the agent-readable `=`-bar block. */
|
|
export function renderAdvisorReport(report: AdvisorReport): string {
|
|
const lines: string[] = [];
|
|
lines.push('');
|
|
lines.push(BAR);
|
|
lines.push(`gbrain advisor — ${report.findings.length} thing${report.findings.length === 1 ? '' : 's'} worth your attention (gbrain ${report.version})`);
|
|
lines.push(BAR);
|
|
lines.push('');
|
|
|
|
if (report.findings.length === 0) {
|
|
lines.push('Nothing pressing — this brain looks healthy. Re-run `gbrain advisor` any time.');
|
|
lines.push(BAR);
|
|
lines.push('');
|
|
return lines.join('\n');
|
|
}
|
|
|
|
for (const f of report.findings) {
|
|
lines.push(`[${SEV_LABEL[f.severity]}] ${f.title}`);
|
|
if (f.detail) for (const wl of wrap(f.detail, 68, ' ')) lines.push(wl);
|
|
const fl = fixLine(f);
|
|
if (fl) lines.push(` fix: ${fl}`);
|
|
lines.push('');
|
|
}
|
|
|
|
lines.push('ACTION FOR THE AGENT:');
|
|
lines.push(' 1. Show this list to the user, highest-severity first.');
|
|
lines.push(' 2. Ask before running any fix. The user owns these decisions.');
|
|
lines.push(' 3. To run a single fix locally after they agree:');
|
|
lines.push('');
|
|
const appliable = report.findings.filter((f) => f.fix.dispatch_id);
|
|
if (appliable.length > 0) {
|
|
lines.push(` gbrain advisor --apply <id> # e.g. ${appliable[0]!.fix.dispatch_id}`);
|
|
} else {
|
|
lines.push(' (run the per-finding `fix:` command above)');
|
|
}
|
|
lines.push('');
|
|
lines.push(BAR);
|
|
lines.push('');
|
|
return lines.join('\n');
|
|
}
|
|
|
|
/**
|
|
* Render the post-install / upgrade flavor: the bundled-skill recommendations as
|
|
* a single advisor finding set. Kept here so post-install-advisory.ts and the
|
|
* advisor share one format.
|
|
*/
|
|
export function renderRecommendedSkills(opts: {
|
|
version: string;
|
|
context: 'init' | 'upgrade';
|
|
skills: Array<{ slug: string; description: string }>;
|
|
scaffoldAllCommand: string;
|
|
workspaceNotDetected?: boolean;
|
|
}): string {
|
|
const lines: string[] = [];
|
|
const verb = opts.context === 'init' ? 'installed' : 'upgraded to';
|
|
lines.push('');
|
|
lines.push(BAR);
|
|
lines.push(`gbrain ${opts.version} — RECOMMENDED SKILLS FOR THE AGENT TO INSTALL`);
|
|
lines.push(BAR);
|
|
lines.push('');
|
|
lines.push(
|
|
`The user just ${verb} gbrain ${opts.version}. ${opts.skills.length} ` +
|
|
`${opts.skills.length === 1 ? 'skill is' : 'skills are'} recommended but not yet installed.`,
|
|
);
|
|
lines.push('');
|
|
if (opts.workspaceNotDetected) {
|
|
lines.push('(NOTE: no workspace detected at the default location — pass --workspace to scaffold.)');
|
|
lines.push('');
|
|
}
|
|
lines.push('THE SKILLS:');
|
|
lines.push('');
|
|
for (const s of opts.skills) {
|
|
lines.push(` - ${s.slug}`);
|
|
for (const wl of wrap(s.description, 68, ' ')) lines.push(wl);
|
|
lines.push('');
|
|
}
|
|
lines.push('ACTION FOR THE AGENT:');
|
|
lines.push(' 1. Show this list to the user. Briefly explain each skill.');
|
|
lines.push(' 2. Ask the user explicitly: "Want me to install these now?"');
|
|
lines.push(' 3. If YES:');
|
|
lines.push('');
|
|
lines.push(` ${opts.scaffoldAllCommand}`);
|
|
lines.push('');
|
|
lines.push(' Do NOT scaffold without asking. The user owns this decision.');
|
|
lines.push(BAR);
|
|
lines.push('');
|
|
return lines.join('\n');
|
|
}
|
|
|
|
function wrap(text: string, width: number, indent: string): string[] {
|
|
const words = text.split(/\s+/);
|
|
const lines: string[] = [];
|
|
let current = indent;
|
|
for (const word of words) {
|
|
if ((current + (current === indent ? '' : ' ') + word).length > width + indent.length) {
|
|
lines.push(current.trimEnd());
|
|
current = indent + word;
|
|
} else {
|
|
current = current === indent ? indent + word : current + ' ' + word;
|
|
}
|
|
}
|
|
if (current.trim().length > 0) lines.push(current.trimEnd());
|
|
return lines;
|
|
}
|