brain-colonies 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,67 @@
1
+ # brain-colonies — `bc` CLI
2
+
3
+ Wisdom-layer counsel for the terminal. Wrap any AI CLI for per-turn drift-protection, or talk to BC brains directly.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install -g brain-colonies
9
+ ```
10
+
11
+ ## Quick start
12
+
13
+ ```bash
14
+ # 1. Authenticate (paste your bc_live_ key)
15
+ bc auth
16
+
17
+ # 2. See your colonies
18
+ bc colonies
19
+ bc colonies --set-default <colony-id-or-name>
20
+
21
+ # 3. Check in before doing something
22
+ bc check "should i delete this migration?"
23
+
24
+ # 4. Talk to a specific brain
25
+ bc consult @security-engineer "is JWT decode-without-verify ever fine?"
26
+
27
+ # 5. Interactive chat with Alt-me
28
+ bc chat
29
+
30
+ # 6. THE KILLER FEATURE — wrap any AI CLI
31
+ bc wrap -- claude "should i refactor this auth code?"
32
+ bc wrap --inject -- aichat "review my plan to migrate to Postgres"
33
+ ```
34
+
35
+ ## What `bc wrap` does
36
+
37
+ For every turn, BC fires `bc.checkIn` against the user message **before** the wrapped CLI runs. The check-in returns:
38
+
39
+ - **drift signal** — high if you're about to repeat a known pattern
40
+ - **missing perspectives** — brains in your colony you haven't consulted that probably apply
41
+ - **pending reflections** — deferred patches waiting for your review
42
+ - **observations** — Alt-me's read on the message
43
+
44
+ Default mode prints the check-in to stderr (you see it, the wrapped CLI doesn't). `--inject` mode prepends the check-in block into the prompt — the wrapped LLM reads it inline.
45
+
46
+ ## Commands
47
+
48
+ | Command | Description |
49
+ |---|---|
50
+ | `bc auth` | Store API key in `~/.bc/config.json` |
51
+ | `bc check "<msg>"` | One-off `bc.checkIn` call |
52
+ | `bc consult @brain "<q>"` | Single-turn brain consult |
53
+ | `bc colonies` | List colonies + brains; `--set-default` to choose |
54
+ | `bc chat` | REPL against any brain (default: Alt-me) |
55
+ | `bc wrap -- <cli>` | Pre-turn check-in + forward to any CLI |
56
+
57
+ ## Config
58
+
59
+ Lives at `~/.bc/config.json` (0600). Contains `apiKey`, `apiBase`, `defaultColonyId`.
60
+
61
+ ## Development
62
+
63
+ ```bash
64
+ npm install
65
+ npm run dev -- check "test"
66
+ npm run build
67
+ ```
@@ -0,0 +1,43 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.authCommand = authCommand;
4
+ const promises_1 = require("node:readline/promises");
5
+ const node_process_1 = require("node:process");
6
+ const config_js_1 = require("../lib/config.js");
7
+ const api_js_1 = require("../lib/api.js");
8
+ const ui_js_1 = require("../lib/ui.js");
9
+ async function authCommand(opts) {
10
+ const rl = (0, promises_1.createInterface)({ input: node_process_1.stdin, output: node_process_1.stdout });
11
+ try {
12
+ const existing = (0, config_js_1.loadConfig)();
13
+ if (existing)
14
+ (0, ui_js_1.info)(`existing config: ${(0, config_js_1.configPath)()}`);
15
+ const apiBase = opts.apiBase || existing?.apiBase || (0, config_js_1.defaultApiBase)();
16
+ (0, ui_js_1.info)(`API base: ${apiBase}`);
17
+ const apiKey = (await rl.question('Paste your bc_live_ API key: ')).trim();
18
+ if (!apiKey.startsWith('bc_live_')) {
19
+ (0, ui_js_1.err)('API keys must start with bc_live_. Generate one at https://braincolonies.com/settings/developers');
20
+ process.exit(1);
21
+ }
22
+ let userId;
23
+ try {
24
+ const me = await (0, api_js_1.whoami)({ apiKey, apiBase, defaultColonyId: existing?.defaultColonyId });
25
+ userId = me.userId;
26
+ if (!userId) {
27
+ (0, ui_js_1.err)('Server returned no userId — please report this to support.');
28
+ process.exit(1);
29
+ }
30
+ }
31
+ catch (e) {
32
+ (0, ui_js_1.err)(`Key validation failed: ${e.message}`);
33
+ process.exit(1);
34
+ }
35
+ const cfg = { apiKey, apiBase, userId, defaultColonyId: existing?.defaultColonyId };
36
+ (0, config_js_1.saveConfig)(cfg);
37
+ (0, ui_js_1.ok)(`Authenticated as ${userId}`);
38
+ (0, ui_js_1.ok)(`Config saved to ${(0, config_js_1.configPath)()}`);
39
+ }
40
+ finally {
41
+ rl.close();
42
+ }
43
+ }
@@ -0,0 +1,51 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.chatCommand = chatCommand;
7
+ const promises_1 = require("node:readline/promises");
8
+ const node_process_1 = require("node:process");
9
+ const config_js_1 = require("../lib/config.js");
10
+ const api_js_1 = require("../lib/api.js");
11
+ const ui_js_1 = require("../lib/ui.js");
12
+ const kleur_1 = __importDefault(require("kleur"));
13
+ async function chatCommand(opts) {
14
+ const cfg = (0, config_js_1.requireConfig)();
15
+ const brain = opts.brain || 'alt-me';
16
+ process.stdout.write((0, ui_js_1.banner)() + '\n');
17
+ process.stdout.write(kleur_1.default.dim(`talking to @${brain}. type /quit to exit.\n\n`));
18
+ const rl = (0, promises_1.createInterface)({ input: node_process_1.stdin, output: node_process_1.stdout });
19
+ rl.on('SIGINT', () => { rl.close(); process.exit(0); });
20
+ try {
21
+ while (true) {
22
+ const userMsg = (await rl.question(kleur_1.default.bold().green('you: '))).trim();
23
+ if (!userMsg)
24
+ continue;
25
+ if (userMsg === '/quit' || userMsg === '/exit')
26
+ break;
27
+ if (!opts.noCheckIn) {
28
+ try {
29
+ const r = await (0, api_js_1.checkIn)(cfg, userMsg, opts.colony);
30
+ if (r.driftSignal !== 'none' || r.observations.length || r.missingPerspectives.length || r.mustReadAloud) {
31
+ process.stdout.write((0, ui_js_1.renderCheckIn)(r) + '\n');
32
+ }
33
+ }
34
+ catch (e) {
35
+ process.stderr.write(kleur_1.default.dim(`(check-in failed: ${e.message})\n`));
36
+ }
37
+ }
38
+ try {
39
+ const r = await (0, api_js_1.consult)(cfg, brain, userMsg, opts.colony);
40
+ process.stdout.write(kleur_1.default.bold().cyan(`${r.brainName || brain}: `));
41
+ process.stdout.write(r.text + '\n\n');
42
+ }
43
+ catch (e) {
44
+ (0, ui_js_1.err)(e.message);
45
+ }
46
+ }
47
+ }
48
+ finally {
49
+ rl.close();
50
+ }
51
+ }
@@ -0,0 +1,26 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.checkCommand = checkCommand;
4
+ const config_js_1 = require("../lib/config.js");
5
+ const api_js_1 = require("../lib/api.js");
6
+ const ui_js_1 = require("../lib/ui.js");
7
+ async function checkCommand(message, opts) {
8
+ const cfg = (0, config_js_1.requireConfig)();
9
+ if (!message || !message.trim()) {
10
+ (0, ui_js_1.err)('Usage: bc check "<message>"');
11
+ process.exit(1);
12
+ }
13
+ try {
14
+ const r = await (0, api_js_1.checkIn)(cfg, message, opts.colony);
15
+ if (opts.json) {
16
+ process.stdout.write(JSON.stringify(r, null, 2) + '\n');
17
+ }
18
+ else {
19
+ process.stdout.write((0, ui_js_1.renderCheckIn)(r) + '\n');
20
+ }
21
+ }
22
+ catch (e) {
23
+ (0, ui_js_1.err)(e.message);
24
+ process.exit(1);
25
+ }
26
+ }
@@ -0,0 +1,45 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.coloniesCommand = coloniesCommand;
7
+ const config_js_1 = require("../lib/config.js");
8
+ const api_js_1 = require("../lib/api.js");
9
+ const ui_js_1 = require("../lib/ui.js");
10
+ const kleur_1 = __importDefault(require("kleur"));
11
+ async function coloniesCommand(opts) {
12
+ const cfg = (0, config_js_1.requireConfig)();
13
+ try {
14
+ const cols = await (0, api_js_1.listColonies)(cfg);
15
+ if (opts.setDefault) {
16
+ const match = cols.find((c) => c.id === opts.setDefault || c.name === opts.setDefault);
17
+ if (!match) {
18
+ (0, ui_js_1.err)(`No colony matching '${opts.setDefault}'`);
19
+ process.exit(1);
20
+ }
21
+ (0, config_js_1.saveConfig)({ ...cfg, defaultColonyId: match.id });
22
+ (0, ui_js_1.ok)(`Default colony set to ${match.name} (${match.id})`);
23
+ return;
24
+ }
25
+ if (opts.json) {
26
+ process.stdout.write(JSON.stringify(cols, null, 2) + '\n');
27
+ return;
28
+ }
29
+ if (cols.length === 0) {
30
+ process.stdout.write(kleur_1.default.dim('No colonies yet.\n'));
31
+ return;
32
+ }
33
+ for (const c of cols) {
34
+ const tag = c.id === cfg.defaultColonyId ? kleur_1.default.green(' (default)') : '';
35
+ process.stdout.write(`${kleur_1.default.bold(c.name)} ${kleur_1.default.dim(c.id)}${tag}\n`);
36
+ for (const b of c.brains ?? []) {
37
+ process.stdout.write(` ${kleur_1.default.cyan('@' + b.id)} ${b.name}\n`);
38
+ }
39
+ }
40
+ }
41
+ catch (e) {
42
+ (0, ui_js_1.err)(e.message);
43
+ process.exit(1);
44
+ }
45
+ }
@@ -0,0 +1,33 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.consultCommand = consultCommand;
7
+ const config_js_1 = require("../lib/config.js");
8
+ const api_js_1 = require("../lib/api.js");
9
+ const ui_js_1 = require("../lib/ui.js");
10
+ const kleur_1 = __importDefault(require("kleur"));
11
+ async function consultCommand(brainSpec, question, opts) {
12
+ const cfg = (0, config_js_1.requireConfig)();
13
+ if (!brainSpec || !question?.trim()) {
14
+ (0, ui_js_1.err)('Usage: bc consult @brainId "<question>"');
15
+ process.exit(1);
16
+ }
17
+ const brainId = brainSpec.replace(/^@/, '');
18
+ (0, ui_js_1.info)(`→ @${brainId}`);
19
+ try {
20
+ const r = await (0, api_js_1.consult)(cfg, brainId, question, opts.colony);
21
+ if (opts.json) {
22
+ process.stdout.write(JSON.stringify(r, null, 2) + '\n');
23
+ }
24
+ else {
25
+ process.stdout.write(kleur_1.default.bold().cyan(r.brainName || brainId) + ':\n');
26
+ process.stdout.write(r.text + '\n');
27
+ }
28
+ }
29
+ catch (e) {
30
+ (0, ui_js_1.err)(e.message);
31
+ process.exit(1);
32
+ }
33
+ }
@@ -0,0 +1,84 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.wrapCommand = wrapCommand;
4
+ const node_child_process_1 = require("node:child_process");
5
+ const config_js_1 = require("../lib/config.js");
6
+ const api_js_1 = require("../lib/api.js");
7
+ const ui_js_1 = require("../lib/ui.js");
8
+ async function wrapCommand(rawArgs, opts) {
9
+ if (rawArgs.length === 0) {
10
+ (0, ui_js_1.err)('Usage: bc wrap [--inject] [--message "..."] -- <cli> [args...]');
11
+ (0, ui_js_1.err)('Example: bc wrap -- claude "should i refactor this auth code?"');
12
+ process.exit(1);
13
+ }
14
+ const cfg = (0, config_js_1.requireConfig)();
15
+ const target = rawArgs[0];
16
+ const targetArgs = rawArgs.slice(1);
17
+ let userMessage = opts.message;
18
+ let messageArgIdx = -1;
19
+ if (!userMessage) {
20
+ for (let i = targetArgs.length - 1; i >= 0; i--) {
21
+ const a = targetArgs[i];
22
+ if (a && !a.startsWith('-')) {
23
+ userMessage = a;
24
+ messageArgIdx = i;
25
+ break;
26
+ }
27
+ }
28
+ }
29
+ if (!userMessage) {
30
+ (0, ui_js_1.info)('bc wrap: no user message found in args — forwarding without check-in');
31
+ return forward(target, targetArgs);
32
+ }
33
+ try {
34
+ const r = await (0, api_js_1.checkIn)(cfg, userMessage, opts.colony);
35
+ if (!opts.silent) {
36
+ process.stderr.write((0, ui_js_1.renderCheckIn)(r) + '\n');
37
+ }
38
+ if (opts.inject && messageArgIdx >= 0) {
39
+ const block = formatInjectBlock(r);
40
+ if (block) {
41
+ targetArgs[messageArgIdx] = `${block}\n\n${userMessage}`;
42
+ }
43
+ }
44
+ }
45
+ catch (e) {
46
+ process.stderr.write(`⚠ bc.checkIn failed: ${e.message} — forwarding anyway\n`);
47
+ }
48
+ return forward(target, targetArgs);
49
+ }
50
+ function formatInjectBlock(r) {
51
+ const parts = [];
52
+ parts.push('[BrainColonies check-in — read before responding]');
53
+ parts.push(`drift signal: ${r.driftSignal}`);
54
+ if (r.missingPerspectives.length)
55
+ parts.push(`missing perspectives: ${r.missingPerspectives.join(', ')}`);
56
+ for (const o of r.observations)
57
+ parts.push(`observation: ${o}`);
58
+ if (r.message)
59
+ parts.push(r.mustReadAloud ? `MUST READ ALOUD: ${r.message}` : r.message);
60
+ parts.push('[end check-in]');
61
+ return parts.length > 2 ? parts.join('\n') : '';
62
+ }
63
+ function forward(target, args) {
64
+ return new Promise((resolve) => {
65
+ const child = (0, node_child_process_1.spawn)(target, args, { stdio: 'inherit' });
66
+ child.on('error', (e) => {
67
+ if (e.code === 'ENOENT') {
68
+ (0, ui_js_1.err)(`Command not found: ${target}`);
69
+ process.exit(127);
70
+ }
71
+ (0, ui_js_1.err)(`Failed to spawn ${target}: ${e.message}`);
72
+ process.exit(1);
73
+ });
74
+ child.on('exit', (code, signal) => {
75
+ if (signal) {
76
+ process.kill(process.pid, signal);
77
+ }
78
+ else {
79
+ process.exit(code ?? 0);
80
+ }
81
+ resolve();
82
+ });
83
+ });
84
+ }
package/dist/index.js ADDED
@@ -0,0 +1,63 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ const commander_1 = require("commander");
5
+ const auth_js_1 = require("./commands/auth.js");
6
+ const check_js_1 = require("./commands/check.js");
7
+ const consult_js_1 = require("./commands/consult.js");
8
+ const colonies_js_1 = require("./commands/colonies.js");
9
+ const wrap_js_1 = require("./commands/wrap.js");
10
+ const chat_js_1 = require("./commands/chat.js");
11
+ const program = new commander_1.Command();
12
+ program
13
+ .name('bc')
14
+ .description('BrainColonies — wisdom-layer counsel for the terminal')
15
+ .version('0.1.1');
16
+ program
17
+ .command('auth')
18
+ .description('Authenticate with a bc_live_ API key (stored in ~/.bc/config.json)')
19
+ .option('--api-base <url>', 'override API base URL')
20
+ .action(auth_js_1.authCommand);
21
+ program
22
+ .command('check <message...>')
23
+ .description('Call bc.checkIn for a user message — prints drift signal, missing perspectives, observations')
24
+ .option('-c, --colony <id>', 'colony id (defaults to your default colony)')
25
+ .option('--json', 'emit raw JSON instead of pretty output')
26
+ .action((message, opts) => (0, check_js_1.checkCommand)(message.join(' '), opts));
27
+ program
28
+ .command('consult <brain> <question...>')
29
+ .description('Ask a specific brain a question (single turn)')
30
+ .option('-c, --colony <id>', 'colony id (defaults to your default colony)')
31
+ .option('--json', 'emit raw JSON')
32
+ .action((brain, question, opts) => (0, consult_js_1.consultCommand)(brain, question.join(' '), opts));
33
+ program
34
+ .command('colonies')
35
+ .description('List your colonies and the brains inside each')
36
+ .option('--set-default <idOrName>', 'set a colony as the CLI default')
37
+ .option('--json', 'emit raw JSON')
38
+ .action(colonies_js_1.coloniesCommand);
39
+ program
40
+ .command('wrap')
41
+ .description('Wrap another CLI: bc.checkIn fires first, then forwards. Use -- to separate args.')
42
+ .option('-m, --message <text>', 'explicit user message (otherwise: last positional arg of wrapped cmd)')
43
+ .option('-i, --inject', "inject the check-in block INTO the wrapped CLI's prompt (default: print only)")
44
+ .option('-c, --colony <id>', 'colony id (defaults to your default colony)')
45
+ .option('-s, --silent', 'do not print the check-in block to stderr')
46
+ .allowUnknownOption(true)
47
+ .helpOption('-h, --help', 'show help')
48
+ .action(function () {
49
+ const opts = this.opts();
50
+ const raw = this.args ?? [];
51
+ (0, wrap_js_1.wrapCommand)(raw, opts);
52
+ });
53
+ program
54
+ .command('chat')
55
+ .description('Interactive chat with a BC brain — fires bc.checkIn per turn')
56
+ .option('-b, --brain <id>', 'brain id (default: alt-me)')
57
+ .option('-c, --colony <id>', 'colony id (defaults to your default colony)')
58
+ .option('--no-check-in', 'skip the per-turn bc.checkIn call')
59
+ .action(chat_js_1.chatCommand);
60
+ program.parseAsync(process.argv).catch((e) => {
61
+ process.stderr.write(`✖ ${e.message}\n`);
62
+ process.exit(1);
63
+ });
@@ -0,0 +1,95 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.BcApiError = void 0;
4
+ exports.bcFetch = bcFetch;
5
+ exports.checkIn = checkIn;
6
+ exports.consult = consult;
7
+ exports.listColonies = listColonies;
8
+ exports.whoami = whoami;
9
+ class BcApiError extends Error {
10
+ status;
11
+ body;
12
+ constructor(status, body, message) {
13
+ super(message);
14
+ this.status = status;
15
+ this.body = body;
16
+ this.name = 'BcApiError';
17
+ }
18
+ }
19
+ exports.BcApiError = BcApiError;
20
+ async function bcFetch(cfg, path, opts = {}) {
21
+ const url = `${cfg.apiBase.replace(/\/$/, '')}${path}`;
22
+ const res = await fetch(url, {
23
+ method: opts.method ?? (opts.body ? 'POST' : 'GET'),
24
+ headers: {
25
+ Authorization: `Bearer ${cfg.apiKey}`,
26
+ 'Content-Type': 'application/json',
27
+ },
28
+ body: opts.body ? JSON.stringify(opts.body) : undefined,
29
+ signal: opts.signal,
30
+ });
31
+ const text = await res.text();
32
+ let parsed = text;
33
+ try {
34
+ parsed = text ? JSON.parse(text) : null;
35
+ }
36
+ catch { }
37
+ if (!res.ok) {
38
+ const msg = (parsed && typeof parsed === 'object' && 'error' in parsed)
39
+ ? String(parsed.error)
40
+ : `HTTP ${res.status}`;
41
+ throw new BcApiError(res.status, parsed, msg);
42
+ }
43
+ return parsed;
44
+ }
45
+ async function checkIn(cfg, userMessage, colonyId) {
46
+ return bcFetch(cfg, '/mcp', {
47
+ method: 'POST',
48
+ body: {
49
+ jsonrpc: '2.0',
50
+ id: 1,
51
+ method: 'tools/call',
52
+ params: {
53
+ name: 'bc.checkIn',
54
+ arguments: { userMessage, colonyId: colonyId ?? cfg.defaultColonyId },
55
+ },
56
+ },
57
+ }).then((raw) => {
58
+ const r = raw;
59
+ const first = r?.result?.content?.[0];
60
+ if (first?.type === 'text' && first.text) {
61
+ try {
62
+ return JSON.parse(first.text);
63
+ }
64
+ catch { }
65
+ }
66
+ return {
67
+ driftSignal: 'none',
68
+ pendingReflections: 0,
69
+ missingPerspectives: [],
70
+ observations: [],
71
+ mustReadAloud: false,
72
+ };
73
+ });
74
+ }
75
+ async function consult(cfg, brainId, question, colonyId) {
76
+ const resolvedColony = colonyId ?? cfg.defaultColonyId;
77
+ if (!resolvedColony) {
78
+ throw new Error('No colony selected. Run `bc colonies` and set one as default, or pass --colony.');
79
+ }
80
+ return bcFetch(cfg, `/api/colony/${resolvedColony}/interact`, {
81
+ method: 'POST',
82
+ body: {
83
+ brainId,
84
+ mode: 'chat',
85
+ messages: [{ role: 'user', content: question }],
86
+ },
87
+ });
88
+ }
89
+ async function listColonies(cfg) {
90
+ const raw = await bcFetch(cfg, `/colonies?userId=${encodeURIComponent(cfg.userId)}`);
91
+ return Array.isArray(raw) ? raw : (raw?.colonies ?? []);
92
+ }
93
+ async function whoami(cfg) {
94
+ return bcFetch(cfg, '/api/me');
95
+ }
@@ -0,0 +1,52 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.loadConfig = loadConfig;
4
+ exports.saveConfig = saveConfig;
5
+ exports.configPath = configPath;
6
+ exports.defaultApiBase = defaultApiBase;
7
+ exports.requireConfig = requireConfig;
8
+ const node_os_1 = require("node:os");
9
+ const node_path_1 = require("node:path");
10
+ const node_fs_1 = require("node:fs");
11
+ const CONFIG_DIR = (0, node_path_1.join)((0, node_os_1.homedir)(), '.bc');
12
+ const CONFIG_PATH = (0, node_path_1.join)(CONFIG_DIR, 'config.json');
13
+ const DEFAULT_API_BASE = 'https://ma3yuktnyh.execute-api.us-east-1.amazonaws.com';
14
+ function loadConfig() {
15
+ if (!(0, node_fs_1.existsSync)(CONFIG_PATH))
16
+ return null;
17
+ try {
18
+ const raw = (0, node_fs_1.readFileSync)(CONFIG_PATH, 'utf8');
19
+ const parsed = JSON.parse(raw);
20
+ if (!parsed.apiKey || !parsed.userId)
21
+ return null;
22
+ return {
23
+ apiKey: parsed.apiKey,
24
+ apiBase: parsed.apiBase || DEFAULT_API_BASE,
25
+ userId: parsed.userId,
26
+ defaultColonyId: parsed.defaultColonyId,
27
+ };
28
+ }
29
+ catch {
30
+ return null;
31
+ }
32
+ }
33
+ function saveConfig(cfg) {
34
+ if (!(0, node_fs_1.existsSync)(CONFIG_DIR)) {
35
+ (0, node_fs_1.mkdirSync)(CONFIG_DIR, { recursive: true, mode: 0o700 });
36
+ }
37
+ (0, node_fs_1.writeFileSync)(CONFIG_PATH, JSON.stringify(cfg, null, 2), { mode: 0o600 });
38
+ (0, node_fs_1.chmodSync)(CONFIG_PATH, 0o600);
39
+ }
40
+ function configPath() {
41
+ return CONFIG_PATH;
42
+ }
43
+ function defaultApiBase() {
44
+ return DEFAULT_API_BASE;
45
+ }
46
+ function requireConfig() {
47
+ const cfg = loadConfig();
48
+ if (!cfg) {
49
+ throw new Error('Not authenticated. Run `bc auth` first.');
50
+ }
51
+ return cfg;
52
+ }
package/dist/lib/ui.js ADDED
@@ -0,0 +1,52 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.banner = banner;
7
+ exports.err = err;
8
+ exports.ok = ok;
9
+ exports.info = info;
10
+ exports.renderCheckIn = renderCheckIn;
11
+ const kleur_1 = __importDefault(require("kleur"));
12
+ function banner() {
13
+ return kleur_1.default.bold().cyan('🧠 BrainColonies');
14
+ }
15
+ function err(msg) {
16
+ process.stderr.write(kleur_1.default.red('✖ ') + msg + '\n');
17
+ }
18
+ function ok(msg) {
19
+ process.stdout.write(kleur_1.default.green('✓ ') + msg + '\n');
20
+ }
21
+ function info(msg) {
22
+ process.stdout.write(kleur_1.default.dim(msg) + '\n');
23
+ }
24
+ const DRIFT_COLORS = {
25
+ none: kleur_1.default.dim,
26
+ low: kleur_1.default.cyan,
27
+ medium: kleur_1.default.yellow,
28
+ high: kleur_1.default.red,
29
+ };
30
+ function renderCheckIn(r) {
31
+ const lines = [];
32
+ lines.push(kleur_1.default.bold().cyan('━━━ bc.checkIn ━━━'));
33
+ const colorFn = DRIFT_COLORS[r.driftSignal] ?? kleur_1.default.dim;
34
+ lines.push(`drift: ${colorFn(r.driftSignal.toUpperCase())}`);
35
+ if (r.pendingReflections > 0) {
36
+ lines.push(`pending reflections: ${kleur_1.default.yellow(String(r.pendingReflections))}`);
37
+ }
38
+ if (r.missingPerspectives.length > 0) {
39
+ lines.push(`missing perspectives: ${kleur_1.default.magenta(r.missingPerspectives.join(', '))}`);
40
+ }
41
+ if (r.observations.length > 0) {
42
+ lines.push(kleur_1.default.bold('observations:'));
43
+ for (const o of r.observations)
44
+ lines.push(` • ${o}`);
45
+ }
46
+ if (r.message) {
47
+ lines.push('');
48
+ lines.push(r.mustReadAloud ? kleur_1.default.bold().yellow(r.message) : r.message);
49
+ }
50
+ lines.push(kleur_1.default.dim('━━━━━━━━━━━━━━━━━━'));
51
+ return lines.join('\n');
52
+ }
package/package.json ADDED
@@ -0,0 +1,43 @@
1
+ {
2
+ "name": "brain-colonies",
3
+ "version": "0.1.1",
4
+ "description": "BrainColonies CLI — wisdom-layer counsel for the terminal. Wrap any AI CLI for per-turn drift-protection.",
5
+ "main": "dist/index.js",
6
+ "bin": {
7
+ "bc": "dist/index.js"
8
+ },
9
+ "scripts": {
10
+ "build": "tsc",
11
+ "dev": "tsx src/index.ts",
12
+ "prepublishOnly": "npm run build"
13
+ },
14
+ "license": "MIT",
15
+ "engines": {
16
+ "node": ">=18"
17
+ },
18
+ "files": ["dist", "README.md"],
19
+ "keywords": [
20
+ "ai",
21
+ "cli",
22
+ "brain-colonies",
23
+ "wisdom-layer",
24
+ "drift-protection",
25
+ "ontology",
26
+ "mcp"
27
+ ],
28
+ "homepage": "https://braincolonies.com",
29
+ "repository": {
30
+ "type": "git",
31
+ "url": "https://github.com/ganbarone/brain-colony-collective.git",
32
+ "directory": "cli"
33
+ },
34
+ "dependencies": {
35
+ "commander": "^12.1.0",
36
+ "kleur": "^4.1.5"
37
+ },
38
+ "devDependencies": {
39
+ "typescript": "^5.4.0",
40
+ "@types/node": "^20.11.0",
41
+ "tsx": "^4.7.0"
42
+ }
43
+ }