feat(driver-skill): add bb-mail-shim + session-preflight mail delegate config (izs.5)

## What changed

### scripts/bb-mail-shim.mjs (new)
Translates bd mail delegate calls into bb agent coordination commands.
bd mail delegates by prepending the configured command to all args, so
this shim bridges the interface mismatch between bd mail (gt-mail style)
and bb agent (--agent/--from flags required).

Command mappings:
  bd mail inbox [...]            → bb agent inbox --agent $BB_AGENT [...]
  bd mail send --to foo [...]    → bb agent send --from $BB_AGENT --to foo [...]
  bd mail read <msg-id>          → bb agent read --agent $BB_AGENT --message <msg-id>
  bd mail ack <msg-id>           → bb agent ack --agent $BB_AGENT --message <msg-id>
  bd mail <other> [...]          → bb agent <other> [...]  (passthrough)

Agent identity injected automatically from BB_AGENT env var (primary) or
BD_ACTOR env var (fallback). Caller can override --from by supplying it
explicitly in bd mail send args. Falls back with clear error messages if
bb is not in PATH or BB_AGENT/BD_ACTOR is unset.

### scripts/session-preflight.mjs (updated)
Added mail delegate auto-configuration step after successful bb resolution:
- Calls: bd config set mail.delegate "node <abs-path-to-bb-mail-shim.mjs>"
- Uses absolute path to shim resolved relative to session-preflight.mjs
- Reports mail.configured + mail.delegate + usage note in output JSON
- Graceful failure if shim missing, bd config set fails, or bb not found
- Added mail: null to all error branches for consistent output shape

## Verification
Tested end-to-end on this machine:
  export BB_AGENT=silver-scribe
  node session-preflight.mjs    # → ok:true, mail.configured:true
  bd mail send --to silver-scribe --bead beadboard-izs.5 \
    --category INFO --subject "test" --body "pipeline verified"
  bd mail inbox                 # → Inbox (1): [msg_...] INFO: test
All commands exit 0. Delegate persisted via bd config get mail.delegate.

## Bead: beadboard-izs.5 (closed)
## Also closed: beadboard-izs.2 — bb agent already in global CLI (feat(cli) commit)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
ZenchantLive 2026-03-03 18:30:15 -08:00
parent 114c227874
commit dc7f20148c
2 changed files with 193 additions and 1 deletions

View file

@ -0,0 +1,135 @@
#!/usr/bin/env node
/**
* bb-mail-shim.mjs
* Translates bd mail delegate calls into bb agent coordination commands.
*
* bd mail delegates by prepending the configured delegate string to all args:
* `bd mail inbox` `node bb-mail-shim.mjs inbox`
* `bd mail send --to foo ...` `node bb-mail-shim.mjs send --to foo ...`
*
* Agent identity is read from (in order):
* 1. BB_AGENT env var (e.g., export BB_AGENT=silver-scribe)
* 2. BD_ACTOR env var (git actor name used by bd commands)
*
* Command mappings:
* bd mail inbox [--state s] [--bead b] [--limit n]
* bb agent inbox --agent <self> [...]
*
* bd mail send --to <agent> --bead <id> --category <cat> --subject <s> --body <b>
* bb agent send --from <self> --to <agent> --bead <id> --category <cat> --subject <s> --body <b>
* (--from injected automatically; omitted if caller already supplies it)
*
* bd mail read <message-id>
* bb agent read --agent <self> --message <message-id>
*
* bd mail ack <message-id>
* bb agent ack --agent <self> --message <message-id>
*
* bd mail <other> [...] (passthrough)
* bb agent <other> [...]
*
* To configure (one-time, or via session-preflight.mjs):
* bd config set mail.delegate "node /abs/path/to/bb-mail-shim.mjs"
*
* Note: bb agent commands must be available globally (installed via izs.2).
* Until then, call tools/bb.ts from the BeadBoard repo directly.
*/
import { spawnSync } from 'node:child_process';
function getAgentName() {
const agent = (process.env.BB_AGENT || process.env.BD_ACTOR || '').trim();
if (!agent) {
console.error(
'bb-mail-shim: agent identity required.\n' +
'Set BB_AGENT to your agent name before using bd mail:\n' +
' export BB_AGENT=silver-scribe\n' +
'Or ensure BD_ACTOR is set by your bd environment.',
);
process.exit(1);
}
return agent;
}
function runBbAgent(args) {
const result = spawnSync('bb', ['agent', ...args], { stdio: 'inherit', shell: false });
if (result.error) {
if (result.error.code === 'ENOENT') {
console.error(
'bb-mail-shim: bb command not found in PATH.\n' +
'Install the BeadBoard global CLI so bb agent commands are available.\n' +
'Interim: call tools/bb.ts directly from the BeadBoard repo:\n' +
' node --import tsx /path/to/beadboard/tools/bb.ts agent inbox --agent $BB_AGENT',
);
} else {
console.error(`bb-mail-shim: failed to spawn bb: ${result.error.message}`);
}
process.exit(1);
}
process.exit(result.status ?? 0);
}
const args = process.argv.slice(2);
const subcommand = args[0];
if (!subcommand || subcommand === '--help' || subcommand === '-h') {
console.log(`bb-mail-shim: bd mail → bb agent translation shim
Usage (via bd mail after delegate is configured):
bd mail inbox [--state unread|read|acked] [--bead <id>] [--limit <n>]
bd mail send --to <agent> --bead <id> --category HANDOFF|BLOCKED|DECISION|INFO --subject <text> --body <text>
bd mail read <message-id>
bd mail ack <message-id>
Requires: BB_AGENT or BD_ACTOR env var set to your agent name.
Configure once per project:
bd config set mail.delegate "node /abs/path/to/bb-mail-shim.mjs"
# or run session-preflight.mjs which sets this automatically`);
process.exit(0);
}
const agent = getAgentName();
switch (subcommand) {
case 'inbox': {
// bd mail inbox [...] → bb agent inbox --agent <self> [...]
runBbAgent(['inbox', '--agent', agent, ...args.slice(1)]);
break;
}
case 'send': {
// bd mail send [...] → bb agent send --from <self> [...]
// Inject --from only if caller hasn't already supplied it
const rest = args.slice(1);
const hasFrom = rest.includes('--from');
runBbAgent(['send', ...(hasFrom ? [] : ['--from', agent]), ...rest]);
break;
}
case 'read': {
// bd mail read <msg-id> [...] → bb agent read --agent <self> --message <msg-id> [...]
const msgId = args[1];
if (!msgId) {
console.error('bb-mail-shim: "read" requires a <message-id> argument');
process.exit(1);
}
runBbAgent(['read', '--agent', agent, '--message', msgId, ...args.slice(2)]);
break;
}
case 'ack': {
// bd mail ack <msg-id> [...] → bb agent ack --agent <self> --message <msg-id> [...]
const msgId = args[1];
if (!msgId) {
console.error('bb-mail-shim: "ack" requires a <message-id> argument');
process.exit(1);
}
runBbAgent(['ack', '--agent', agent, '--message', msgId, ...args.slice(2)]);
break;
}
default: {
// Passthrough for any other subcommand
runBbAgent([subcommand, ...args.slice(1)]);
}
}

View file

@ -1,8 +1,56 @@
#!/usr/bin/env node
import { spawnSync } from 'node:child_process';
import { existsSync } from 'node:fs';
import { fileURLToPath } from 'node:url';
import { dirname, join } from 'node:path';
import { findCommandInPath, resolveBbPath } from './lib/driver-lib.mjs';
const __dirname = dirname(fileURLToPath(import.meta.url));
/**
* Configures bd mail delegate to use bb-mail-shim.mjs.
* Runs `bd config set mail.delegate "node <shim-path>"` in the current directory.
*
* @param {string} bdPath - Resolved path to the bd binary.
* @param {string} shimPath - Absolute path to bb-mail-shim.mjs.
* @returns {object} Mail delegate config result.
*/
function configureMailDelegate(bdPath, shimPath) {
if (!existsSync(shimPath)) {
return {
configured: false,
reason: `shim not found at ${shimPath} — skill installation may be incomplete`,
};
}
const delegateCmd = `node ${shimPath}`;
const result = spawnSync(bdPath, ['config', 'set', 'mail.delegate', delegateCmd], {
stdio: 'pipe',
shell: false,
});
if (result.status !== 0) {
const stderr = result.stderr?.toString().trim() || '';
return {
configured: false,
reason: `bd config set failed: ${stderr || 'non-zero exit'}`,
delegate: delegateCmd,
};
}
return {
configured: true,
delegate: delegateCmd,
shim_path: shimPath,
note: 'Set BB_AGENT env var to your agent name before calling bd mail (e.g., export BB_AGENT=silver-scribe)',
};
}
async function main() {
const shimPath = join(__dirname, 'bb-mail-shim.mjs');
try {
const bdPath = await findCommandInPath('bd');
if (!bdPath) {
@ -17,6 +65,7 @@ async function main() {
bd: { available: false, path: null },
},
bb: null,
mail: null,
},
null,
2,
@ -38,6 +87,10 @@ async function main() {
bd: { available: true, path: bdPath },
},
bb,
mail: {
configured: false,
reason: 'bb not available — mail delegate requires bb agent commands (see izs.2)',
},
},
null,
2,
@ -46,6 +99,8 @@ async function main() {
return;
}
const mail = configureMailDelegate(bdPath, shimPath);
process.stdout.write(
`${JSON.stringify(
{
@ -55,6 +110,7 @@ async function main() {
bd: { available: true, path: bdPath },
},
bb,
mail,
},
null,
2,
@ -67,11 +123,12 @@ async function main() {
ok: false,
error_code: 'PREFLIGHT_INTERNAL_ERROR',
reason: error instanceof Error ? error.message : String(error),
remediation: 'Inspect session-preflight.js and retry.',
remediation: 'Inspect session-preflight.mjs and retry.',
tools: {
bd: { available: false, path: null },
},
bb: null,
mail: null,
},
null,
2,