mirror of
https://github.com/garrytan/gbrain.git
synced 2026-07-31 04:07:52 +00:00
'gbrain takes list' printed 'No takes on list.' even when the brain held many takes: the CLI had no list subcommand, so cmdList treated the word 'list' as a page slug and looked up a page named 'list'. The failure read exactly like an empty takes table, so agents concluded there were no takes and moved on. 'takes list' now lists all active takes (CLI parity with the takes_list op), prefixing each row with its page slug. 'takes <slug>' unchanged. Co-authored-by: Garry Tan <garrytan@gmail.com> Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Garry Tan
Claude Opus 5
parent
176836f84d
commit
3fec2123d2
+15
-8
@@ -3,6 +3,7 @@
|
||||
*
|
||||
* Subcommands:
|
||||
* takes <slug> — list takes for a page
|
||||
* takes list — list all active takes (#2079)
|
||||
* takes search "<query>" [--who h] — keyword search across all takes
|
||||
* takes add <slug> ...flags — append a take (markdown + DB)
|
||||
* takes update <slug> --row N ...flags — update mutable fields
|
||||
@@ -129,11 +130,10 @@ function writeBody(path: string, body: string): void {
|
||||
// --- Subcommands ---
|
||||
|
||||
async function cmdList(engine: BrainEngine, args: string[]): Promise<void> {
|
||||
const slug = args[0];
|
||||
if (!slug) {
|
||||
console.error('Usage: gbrain takes <slug> [--json]');
|
||||
process.exit(1);
|
||||
}
|
||||
// #2079: slug is optional. `gbrain takes list` (no slug) lists ALL active
|
||||
// takes — CLI parity with the takes_list operation. A leading flag is not
|
||||
// a slug.
|
||||
const slug = args[0] && !args[0].startsWith('-') ? args[0] : undefined;
|
||||
const json = flagPresent(args, '--json');
|
||||
const holder = flagValue(args, '--who');
|
||||
const kind = flagValue(args, '--kind') as string | undefined;
|
||||
@@ -153,17 +153,19 @@ async function cmdList(engine: BrainEngine, args: string[]): Promise<void> {
|
||||
return;
|
||||
}
|
||||
|
||||
const scope = slug ?? 'this brain';
|
||||
if (takes.length === 0) {
|
||||
console.log(`No takes on ${slug}.`);
|
||||
console.log(`No takes on ${scope}.`);
|
||||
return;
|
||||
}
|
||||
console.log(`# Takes on ${slug}\n`);
|
||||
console.log(`# Takes on ${scope}\n`);
|
||||
for (const t of takes) {
|
||||
const tag = t.active ? '' : ' [superseded]';
|
||||
const w = Number(t.weight).toFixed(2);
|
||||
const since = t.since_date ?? '';
|
||||
const src = t.source ? ` — ${t.source}` : '';
|
||||
console.log(`#${t.row_num} [${t.kind} • ${t.holder} • w=${w}${since ? ` • ${since}` : ''}]${tag}\n ${t.claim}${src}\n`);
|
||||
const where = slug ? '' : `${t.page_slug} `;
|
||||
console.log(`${where}#${t.row_num} [${t.kind} • ${t.holder} • w=${w}${since ? ` • ${since}` : ''}]${tag}\n ${t.claim}${src}\n`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -555,6 +557,8 @@ export async function runTakes(engine: BrainEngine, args: string[]): Promise<voi
|
||||
Subcommands:
|
||||
takes <slug> [--json] [--who h] [--kind k] [--sort weight|since_date|created_at] [--expired]
|
||||
List takes for a page
|
||||
takes list [--json] [--who h] [--kind k] [--sort ...] [--expired]
|
||||
List all active takes across the brain (#2079)
|
||||
takes search "<query>" [--limit N] [--json]
|
||||
Keyword search across all takes
|
||||
takes add <slug> --claim "..." --kind <fact|take|bet|hunch> --who <holder>
|
||||
@@ -584,6 +588,9 @@ Common flags:
|
||||
const rest = args.slice(1);
|
||||
|
||||
switch (sub) {
|
||||
// #2079: `takes list` used to be parsed as page slug "list" and printed
|
||||
// "No takes on list." — reading exactly like an empty takes table.
|
||||
case 'list': return cmdList(engine, rest);
|
||||
case 'search': return cmdSearch(engine, rest);
|
||||
case 'add': return cmdAdd(engine, rest, await resolveTakesSourceId(engine));
|
||||
case 'update': return cmdUpdate(engine, rest, await resolveTakesSourceId(engine));
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
/**
|
||||
* #2079 — `gbrain takes list` used to parse "list" as a PAGE SLUG: cmdList
|
||||
* looked up a page named "list" and printed "No takes on list." even when the
|
||||
* brain held many takes — reading exactly like an empty takes table, so
|
||||
* agents concluded there were no takes and moved on.
|
||||
*
|
||||
* Fix: `list` is a real subcommand (CLI parity with the takes_list op).
|
||||
* Bare `takes <slug>` still lists per-page.
|
||||
*/
|
||||
|
||||
import { afterAll, beforeAll, describe, expect, test } from 'bun:test';
|
||||
import { PGLiteEngine } from '../src/core/pglite-engine.ts';
|
||||
import { runTakes } from '../src/commands/takes.ts';
|
||||
|
||||
let engine: PGLiteEngine;
|
||||
|
||||
async function captureStdout(fn: () => Promise<void>): Promise<string> {
|
||||
const lines: string[] = [];
|
||||
const orig = console.log;
|
||||
console.log = (...args: unknown[]) => { lines.push(args.join(' ')); };
|
||||
try {
|
||||
await fn();
|
||||
} finally {
|
||||
console.log = orig;
|
||||
}
|
||||
return lines.join('\n');
|
||||
}
|
||||
|
||||
beforeAll(async () => {
|
||||
engine = new PGLiteEngine();
|
||||
await engine.connect({});
|
||||
await engine.initSchema();
|
||||
await engine.putPage('companies/acme-example', {
|
||||
type: 'company',
|
||||
title: 'Acme Example',
|
||||
compiled_truth: 'Acme Example is a test company.',
|
||||
});
|
||||
const [row] = await engine.executeRaw<{ id: number }>(
|
||||
`SELECT id FROM pages WHERE slug = 'companies/acme-example'`,
|
||||
);
|
||||
await engine.addTakesBatch([{
|
||||
page_id: row.id,
|
||||
row_num: 1,
|
||||
claim: 'Acme will ship the widget by Q3.',
|
||||
kind: 'bet',
|
||||
holder: 'self',
|
||||
weight: 0.7,
|
||||
}]);
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await engine.disconnect();
|
||||
});
|
||||
|
||||
describe('gbrain takes list (#2079)', () => {
|
||||
test('`takes list` lists all takes instead of slug-ifying "list"', async () => {
|
||||
const out = await captureStdout(() => runTakes(engine, ['list']));
|
||||
expect(out).not.toContain('No takes on list.');
|
||||
expect(out).toContain('Acme will ship the widget by Q3.');
|
||||
expect(out).toContain('companies/acme-example');
|
||||
});
|
||||
|
||||
test('`takes list --json` returns the full take rows', async () => {
|
||||
const out = await captureStdout(() => runTakes(engine, ['list', '--json']));
|
||||
const parsed = JSON.parse(out);
|
||||
expect(Array.isArray(parsed)).toBe(true);
|
||||
expect(parsed.length).toBe(1);
|
||||
expect(parsed[0].claim).toContain('Acme will ship');
|
||||
});
|
||||
|
||||
test('per-page form still works: `takes <slug>`', async () => {
|
||||
const out = await captureStdout(() => runTakes(engine, ['companies/acme-example']));
|
||||
expect(out).toContain('# Takes on companies/acme-example');
|
||||
expect(out).toContain('Acme will ship the widget by Q3.');
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user