mirror of
https://github.com/garrytan/gbrain.git
synced 2026-07-27 22:15:33 +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>
52 lines
2.2 KiB
TypeScript
52 lines
2.2 KiB
TypeScript
/**
|
|
* Tests for src/core/advisor/apply.ts — the --apply allowlist + injection guard
|
|
* (E5/C5). The CLI confirms + spawns; this verifies WHAT is allowed to run.
|
|
*/
|
|
import { describe, test, expect } from 'bun:test';
|
|
import { resolveApplyTarget } from '../src/core/advisor/apply.ts';
|
|
import type { AdvisorFinding, AdvisorReport } from '../src/core/advisor/types.ts';
|
|
|
|
function report(findings: AdvisorFinding[]): AdvisorReport {
|
|
return { version: '0.43.0.0', generated_at: 'x', findings, worst: 'info' };
|
|
}
|
|
function f(over: Partial<AdvisorFinding>): AdvisorFinding {
|
|
return { id: 'x', severity: 'info', title: 't', fix: { command_argv: null }, collector: 'c', ask_user: true, ...over };
|
|
}
|
|
|
|
describe('resolveApplyTarget', () => {
|
|
test('resolves an allowlisted finding to its argv', () => {
|
|
const r = report([
|
|
f({ id: 'mig', fix: { command_argv: ['gbrain', 'apply-migrations', '--yes'], dispatch_id: 'apply_migrations' } }),
|
|
]);
|
|
const res = resolveApplyTarget(r, 'apply_migrations');
|
|
expect(res.ok).toBe(true);
|
|
if (res.ok) expect(res.argv).toEqual(['gbrain', 'apply-migrations', '--yes']);
|
|
});
|
|
|
|
test('rejects unknown id and lists runnable ids', () => {
|
|
const r = report([
|
|
f({ id: 'mig', fix: { command_argv: ['gbrain', 'apply-migrations', '--yes'], dispatch_id: 'apply_migrations' } }),
|
|
]);
|
|
const res = resolveApplyTarget(r, 'nope');
|
|
expect(res.ok).toBe(false);
|
|
if (!res.ok) expect(res.runnable).toEqual(['apply_migrations']);
|
|
});
|
|
|
|
test('a finding without dispatch_id is NOT runnable', () => {
|
|
const r = report([f({ id: 'v', fix: { command_argv: ['gbrain', 'upgrade'] } })]); // no dispatch_id
|
|
expect(resolveApplyTarget(r, 'v').ok).toBe(false);
|
|
});
|
|
|
|
test('rejects shell metacharacters in the argv (injection guard)', () => {
|
|
const r = report([
|
|
f({ id: 'evil', fix: { command_argv: ['gbrain', 'scaffold', 'foo; rm -rf /'], dispatch_id: 'evil' } }),
|
|
]);
|
|
expect(resolveApplyTarget(r, 'evil').ok).toBe(false);
|
|
});
|
|
|
|
test('rejects a fix that does not invoke gbrain', () => {
|
|
const r = report([f({ id: 'x', fix: { command_argv: ['rm', '-rf', '/'], dispatch_id: 'x' } })]);
|
|
expect(resolveApplyTarget(r, 'x').ok).toBe(false);
|
|
});
|
|
});
|