From 73f55f6341901147589c0b1d5ae11f317693d0bb Mon Sep 17 00:00:00 2001 From: Andre Ambrosio Date: Mon, 20 Apr 2026 15:50:13 -0300 Subject: [PATCH 1/2] feat(output): add global --output option (table | json) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds a global --output flag to the bsp CLI. When set to json, commands emit clean JSON to stdout — suitable for scripting and piping. Affected commands: beo resolve, ieo get, ieo list. All human-readable decorators (success, warn, info) are suppressed in json mode. Co-Authored-By: Claude Sonnet 4.6 --- src/commands/beo.ts | 6 +++++- src/commands/ieo.ts | 6 +++++- src/index.ts | 6 ++++++ src/lib/output.ts | 19 +++++++++++++++++++ 4 files changed, 35 insertions(+), 2 deletions(-) diff --git a/src/commands/beo.ts b/src/commands/beo.ts index 6e2484f..14e0861 100644 --- a/src/commands/beo.ts +++ b/src/commands/beo.ts @@ -2,7 +2,7 @@ import { Command } from 'commander' import { CryptoUtils } from '@biological-sovereignty-protocol/sdk' import { loadConfig } from '../lib/config.js' import * as api from '../lib/api.js' -import { success, error, table, info, warn, requireKey } from '../lib/output.js' +import { success, error, table, info, warn, requireKey, json, getOutputFormat } from '../lib/output.js' export function registerBEOCommands(program: Command) { const beo = program.command('create') @@ -51,6 +51,10 @@ export function registerBEOCommands(program: Command) { try { if (!domain.endsWith('.bsp')) domain += '.bsp' const result = await api.get(`/api/beos/domain/${encodeURIComponent(domain)}`) + if (getOutputFormat() === 'json') { + json(result.beo) + return + } success(`BEO found: ${domain}`) console.log() table({ diff --git a/src/commands/ieo.ts b/src/commands/ieo.ts index 1a4d30a..5b6a8e4 100644 --- a/src/commands/ieo.ts +++ b/src/commands/ieo.ts @@ -2,7 +2,7 @@ import { Command } from 'commander' import { CryptoUtils } from '@biological-sovereignty-protocol/sdk' import { loadConfig } from '../lib/config.js' import * as api from '../lib/api.js' -import { success, error, table, info, warn, requireKey } from '../lib/output.js' +import { success, error, table, info, warn, requireKey, json, getOutputFormat } from '../lib/output.js' export function registerIEOCommands(program: Command) { const ieo = program.command('ieo').description('Manage Institutional Entity Objects') @@ -130,6 +130,10 @@ export function registerIEOCommands(program: Command) { const qs = params.toString() const result = await api.get(`/api/ieos${qs ? '?' + qs : ''}`) + if (getOutputFormat() === 'json') { + json(result.ieos) + return + } success(`${result.count} IEO(s) found`) for (const ieo of result.ieos) { console.log() diff --git a/src/index.ts b/src/index.ts index aa6aefd..669f14e 100644 --- a/src/index.ts +++ b/src/index.ts @@ -6,6 +6,7 @@ import { registerConsentCommands } from './commands/consent.js' import { registerIEOCommands } from './commands/ieo.js' import { registerExchangeCommands } from './commands/exchange.js' import { registerConfigCommands } from './commands/config.js' +import { setOutputFormat } from './lib/output.js' const program = new Command() @@ -13,6 +14,11 @@ program .name('bsp') .version('1.0.0') .description('Biological Sovereignty Protocol — CLI\nhttps://biologicalsovereigntyprotocol.com') + .option('--output ', 'Output format: table, json', 'table') + .hook('preAction', (thisCommand) => { + const opts = thisCommand.opts() + if (opts.output) setOutputFormat(opts.output as 'table' | 'json') + }) registerBEOCommands(program) registerConsentCommands(program) diff --git a/src/lib/output.ts b/src/lib/output.ts index 9964e09..c77adbf 100644 --- a/src/lib/output.ts +++ b/src/lib/output.ts @@ -1,4 +1,17 @@ +type OutputFormat = 'table' | 'json' + +let _outputFormat: OutputFormat = 'table' + +export function setOutputFormat(fmt: OutputFormat): void { + _outputFormat = fmt +} + +export function getOutputFormat(): OutputFormat { + return _outputFormat +} + export function success(msg: string): void { + if (_outputFormat === 'json') return console.log(`\x1b[32m✓\x1b[0m ${msg}`) } @@ -7,14 +20,20 @@ export function error(msg: string): void { } export function warn(msg: string): void { + if (_outputFormat === 'json') return console.log(`\x1b[33m!\x1b[0m ${msg}`) } export function info(msg: string): void { + if (_outputFormat === 'json') return console.log(` ${msg}`) } export function table(rows: Record): void { + if (_outputFormat === 'json') { + console.log(JSON.stringify(rows, null, 2)) + return + } const maxKey = Math.max(...Object.keys(rows).map(k => k.length)) for (const [key, val] of Object.entries(rows)) { const label = key.padEnd(maxKey) From 4974f9d50b304df14bc670cc2cd60ba47a8959b1 Mon Sep 17 00:00:00 2001 From: Andre Ambrosio Date: Mon, 20 Apr 2026 16:23:51 -0300 Subject: [PATCH 2/2] feat: add shell completions subcommand (bash + zsh) Adds 'bsp completions [shell]' that prints a completion script for the top-level commands. Source with: eval "\$(bsp completions bash)" or zsh. Documents the install step in README. Adds .nvmrc (18). Co-Authored-By: Claude Sonnet 4.6 --- .nvmrc | 1 + README.md | 12 ++++++++++++ src/index.ts | 12 ++++++++++++ 3 files changed, 25 insertions(+) create mode 100644 .nvmrc diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 0000000..3c03207 --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +18 diff --git a/README.md b/README.md index f7db665..b899464 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,18 @@ Create and manage biological identities (BEOs), institutional entities (IEOs), c npm install -g bspctl ``` +### Shell completions + +After installing, add completions to your shell: + +```bash +# bash — add to ~/.bashrc +eval "$(bsp completions bash)" + +# zsh — add to ~/.zshrc +eval "$(bsp completions zsh)" +``` + Or run without installing: ```bash diff --git a/src/index.ts b/src/index.ts index 669f14e..d7a7918 100644 --- a/src/index.ts +++ b/src/index.ts @@ -26,4 +26,16 @@ registerIEOCommands(program) registerExchangeCommands(program) registerConfigCommands(program) +program + .command('completions [shell]') + .description('Print shell completion script (bash|zsh). Usage: eval "$(bsp completions bash)"') + .action((shell = 'bash') => { + const cmds = ['beo', 'ieo', 'consent', 'record', 'config', 'completions'] + if (shell === 'zsh') { + console.log(`compdef '_arguments "1: :(${cmds.join(' ')})"' bsp`) + } else { + console.log(`complete -W "${cmds.join(' ')}" bsp`) + } + }) + program.parse()