mirror of
https://github.com/garrytan/gbrain.git
synced 2026-07-30 11:22:34 +00:00
Three CLI ergonomics fixes on the shared-op dispatch path: - parseOpArgs now hard-errors on undeclared flags instead of silently swallowing them into params (#380). A pass-through allowlist keeps the undeclared-but-honored flags working: --source (makeContext's source resolver), --brain (mount axis), --dry-run (ctx.dryRun), --json. Value-taking flags with no value also error instead of being dropped. - `gbrain put SLUG --file PATH` reads content from a file (#380, takeover of #856). Driven by the op's cliHints.stdin declaration rather than put_page hard-coding, so volunteer-context gets it too. Mutually exclusive with --content/stdin in either flag order; 5MB cap shared with stdin; unreadable path is a loud error instead of an empty page. put --help documents the flag. - `--json` on shared ops now actually emits raw JSON (docs already promised it); same seam on local-engine and thin-client routed paths. - list_pages declares `offset` (both engines already supported it on PageFilters) and the limit description discloses the 100-row cap (#2876), so `gbrain list` can paginate past 100 instead of silently truncating. Co-authored-by: Kage18 <Kage18@users.noreply.github.com> Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
58 lines
2.0 KiB
TypeScript
58 lines
2.0 KiB
TypeScript
import { describe, expect, test } from 'bun:test';
|
|
import { mkdtempSync, rmSync, writeFileSync } from 'fs';
|
|
import { tmpdir } from 'os';
|
|
import { join } from 'path';
|
|
import { parseOpArgs } from '../src/cli.ts';
|
|
import { operationsByName } from '../src/core/operations.ts';
|
|
|
|
describe('parseOpArgs', () => {
|
|
// #380: `gbrain put SLUG --file PATH` reads content from the file instead
|
|
// of silently swallowing the flag and creating an empty page.
|
|
test('put --file reads the stdin param (content) from a file', () => {
|
|
const dir = mkdtempSync(join(tmpdir(), 'gbrain-put-file-'));
|
|
try {
|
|
const pagePath = join(dir, 'page.md');
|
|
writeFileSync(pagePath, '# From file\n\nBody loaded from --file.\n');
|
|
const params = parseOpArgs(operationsByName.put_page, ['concepts/from-file', '--file', pagePath]);
|
|
expect(params.slug).toBe('concepts/from-file');
|
|
expect(params.content).toBe('# From file\n\nBody loaded from --file.\n');
|
|
} finally {
|
|
rmSync(dir, { recursive: true, force: true });
|
|
}
|
|
});
|
|
|
|
// #380 regression guard: undeclared-but-honored flags must keep passing
|
|
// through when unknown flags become hard errors (--source is read by
|
|
// makeContext; --json by the output seam; --dry-run by ctx.dryRun).
|
|
test('pass-through allowlist flags survive on ops that do not declare them', () => {
|
|
const params = parseOpArgs(operationsByName.get_page, [
|
|
'people/alice-example', '--source', 'wiki', '--json', '--dry-run',
|
|
]);
|
|
expect(params).toEqual({
|
|
slug: 'people/alice-example',
|
|
source: 'wiki',
|
|
json: true,
|
|
dry_run: true,
|
|
});
|
|
});
|
|
|
|
test('--no-<boolean> maps to false without consuming the next flag', () => {
|
|
const params = parseOpArgs(operationsByName.query, [
|
|
'freshEmbedSourceScope code source',
|
|
'--limit',
|
|
'8',
|
|
'--no-expand',
|
|
'--source-id',
|
|
'gstack-code-repo-0e4763c9',
|
|
]);
|
|
|
|
expect(params).toEqual({
|
|
query: 'freshEmbedSourceScope code source',
|
|
limit: 8,
|
|
expand: false,
|
|
source_id: 'gstack-code-repo-0e4763c9',
|
|
});
|
|
});
|
|
});
|
|
|