@secondcontext/btx-cli 0.0.3 → 0.0.6

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 CHANGED
@@ -36,7 +36,7 @@ Install the tarball into a clean repo or temp directory:
36
36
 
37
37
  ```bash
38
38
  npm init -y
39
- npm install /absolute/path/to/code/packages/btx-cli/secondcontext-btx-cli-0.0.3.tgz
39
+ npm install /absolute/path/to/code/packages/btx-cli/secondcontext-btx-cli-0.0.5.tgz
40
40
  ./node_modules/.bin/btx --help
41
41
  ./node_modules/.bin/btx repo status --json
42
42
  ```
@@ -50,6 +50,11 @@ btx logout
50
50
  btx setup
51
51
  btx setup codex
52
52
  btx projects
53
+ btx messages list
54
+ btx messages send "Shipped the fix"
55
+ btx messages send "Can you review this?" --peer faruk@secondcontext.com
56
+ btx messages conversations
57
+ btx messages members
53
58
  btx repo link <project-id>
54
59
  btx bootstrap all
55
60
  btx context get --json
@@ -61,9 +66,11 @@ btx session finish --summary "Recorded outcome and next steps"
61
66
 
62
67
  ## Auth and repo state
63
68
 
64
- - `btx login` imports an existing desktop auth session when available, or opens a browser flow that writes `~/.btx/auth-session.json`
69
+ - `btx login` reuses an existing desktop or standalone auth session when it is still valid, or opens a browser flow that writes `~/.btx/auth-session.json`
65
70
  - `btx login status` shows whether the CLI is authenticated and whether desktop fallback is enabled
66
71
  - `btx logout` clears the standalone CLI session and disables desktop fallback so the CLI stays logged out. Use `btx logout --all` to also clear the desktop session file
72
+ - `btx messages list` reads BTX project chat, with `--peer` for direct messages and `--conversation` for named group chats
73
+ - `btx messages send` sends a BTX project chat message from inline text or piped stdin
67
74
  - `btx setup` is the first-run path for linking a repo and choosing `codex`, `claude`, or both bootstrap targets
68
75
  - Auth is read from `~/.btx/auth-session.json`, with a fallback to the desktop app auth session store
69
76
  - Repo linkage is stored in `.btx/btx.json`
@@ -88,6 +95,7 @@ Expected manual npm steps:
88
95
 
89
96
  - Log in to npm with an account that can publish under `@secondcontext`
90
97
  - Set `NPM_TOKEN` in `code/packages/btx-cli/.env` or export it in your shell
91
- - The publish helper creates a temporary npm user config so workspace-local `.npmrc` files are not needed. Do not run raw `npm publish` from `code/packages/btx-cli`
98
+ - The publish helper in `code/scripts/publish-btx-cli.sh` creates a temporary npm user config so workspace-local `.npmrc` files are not needed. Do not run raw `npm publish` from `code/packages/btx-cli`
99
+ - If `npm run publish:btx-cli:dry-run --prefix code` succeeds but the real publish returns npm `404` for `@secondcontext/btx-cli`, the token is authenticated but does not have publish permission for the package. Fix the npm token or package access instead of retrying raw publish commands
92
100
  - Ensure the `@secondcontext` scope is configured for public packages
93
101
  - If you want an open-source package, add a real `LICENSE` file before publishing and replace the current `UNLICENSED` marker in `package.json`
package/dist/api.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAA;AA4FrD,wBAAsB,MAAM,CAAC,CAAC,EAAE,GAAG,EAAE,aAAa,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,CAE5E;AAED,wBAAsB,aAAa,CAAC,CAAC,EAAE,GAAG,EAAE,aAAa,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,CAGnF;AAED,wBAAsB,cAAc,CAAC,CAAC,EACpC,GAAG,EAAE,aAAa,EAClB,IAAI,EAAE,MAAM,EACZ,IAAI,CAAC,EAAE,OAAO,GACb,OAAO,CAAC,CAAC,CAAC,CAGZ;AAED,wBAAsB,eAAe,CAAC,CAAC,EACrC,GAAG,EAAE,aAAa,EAClB,IAAI,EAAE,MAAM,EACZ,IAAI,CAAC,EAAE,OAAO,GACb,OAAO,CAAC,CAAC,CAAC,CAGZ"}
1
+ {"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAA;AA8FrD,wBAAsB,MAAM,CAAC,CAAC,EAAE,GAAG,EAAE,aAAa,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,CAE5E;AAED,wBAAsB,aAAa,CAAC,CAAC,EAAE,GAAG,EAAE,aAAa,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,CAGnF;AAED,wBAAsB,cAAc,CAAC,CAAC,EACpC,GAAG,EAAE,aAAa,EAClB,IAAI,EAAE,MAAM,EACZ,IAAI,CAAC,EAAE,OAAO,GACb,OAAO,CAAC,CAAC,CAAC,CAGZ;AAED,wBAAsB,eAAe,CAAC,CAAC,EACrC,GAAG,EAAE,aAAa,EAClB,IAAI,EAAE,MAAM,EACZ,IAAI,CAAC,EAAE,OAAO,GACb,OAAO,CAAC,CAAC,CAAC,CAGZ"}
package/dist/api.js CHANGED
@@ -41,7 +41,7 @@ async function requestWithAuth(env, url, method, body) {
41
41
  if (response.status === 401) {
42
42
  const refreshed = await refreshAccessToken(env);
43
43
  if (!refreshed) {
44
- throw new Error('BTX auth token expired. Sign in again through BTX desktop or provide a fresh token.');
44
+ throw new Error('BTX auth token expired. Run `btx login` to refresh the standalone CLI session, or provide a fresh token.');
45
45
  }
46
46
  response = await doRequest(refreshed);
47
47
  }
package/dist/api.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"api.js","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"api.js","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"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"}
package/dist/bin.js CHANGED
@@ -7,13 +7,27 @@ import { apiGet, projectApiPatch, projectApiPost } from './api.js';
7
7
  import { bootstrapAgents } from './bootstrap.js';
8
8
  import { clearStoredAuthSession, DEFAULT_API_URL, clearActiveSession, findRepoRoot, getActiveSession, isDesktopFallbackEnabled, loadRepoConfig, resolveRuntimeEnv, resolveStoredAuthSessionSource, setActiveSession, writeRepoConfig, } from './config.js';
9
9
  import { loginToBtx } from './login.js';
10
+ import { listChatConversations, listChatMessages, listProjectMembers, resolveMessageScope, sendChatMessage, } from './messages.js';
10
11
  import { runRuntimeCli } from './runtime-cli.js';
12
+ const SESSION_CHECKPOINT_TYPES = [
13
+ 'note',
14
+ 'progress',
15
+ 'decision',
16
+ 'risk',
17
+ 'blocker',
18
+ 'assumption',
19
+ 'summary',
20
+ 'question',
21
+ 'next_step',
22
+ ];
23
+ const SESSION_CHECKPOINT_USAGE = SESSION_CHECKPOINT_TYPES.join('|');
11
24
  const RUNTIME_RESOURCES = new Set([
12
25
  'tasks',
13
26
  'sessions',
14
27
  'contacts',
15
28
  'orgs',
16
29
  'meetings',
30
+ 'recordings',
17
31
  'leads',
18
32
  'user',
19
33
  'intro-paths',
@@ -69,16 +83,18 @@ Standalone commands:
69
83
  btx logout Remove stored standalone BTX auth credentials
70
84
  btx setup [agent] Link this repo and bootstrap AGENTS.md and/or CLAUDE.md
71
85
  btx projects List BTX projects you can access
86
+ btx ask "<prompt>" Send a freeform prompt to the general cloud agent
87
+ btx messages ... Read and send BTX project chat messages
72
88
  btx repo link <project-id> Link the current repo to a BTX project
73
89
  btx repo status Show current repo BTX status
74
90
  btx bootstrap <agent> Write BTX instructions into AGENTS.md and/or CLAUDE.md
75
91
  btx session start Start or resume the active BTX coding session for this repo
76
92
  btx session status Show the active BTX coding session
77
- btx session checkpoint Record progress, risk, decision, blocker, or note
93
+ btx session checkpoint Record durable session memory: decisions, assumptions, learnings, risks, next steps
78
94
  btx session finish Close the active BTX coding session
79
95
 
80
96
  BTX resources:
81
- tasks, contacts, orgs, leads, meetings, user, intro-paths,
97
+ tasks, contacts, orgs, leads, meetings, recordings, user, intro-paths,
82
98
  search, context, pages, sessions, hiring-types, hiring-candidates
83
99
 
84
100
  Examples:
@@ -88,17 +104,63 @@ Examples:
88
104
  btx setup
89
105
  btx setup codex
90
106
  btx projects
107
+ btx ask "Show me a count of tasks by status"
108
+ btx ask "Who should I follow up with this week?" --trace-tools
109
+ btx messages list
110
+ btx messages send "Shipped the fix"
111
+ btx messages send "Can you review this?" --peer faruk@secondcontext.com
112
+ btx messages conversations
91
113
  btx repo link <project-id>
92
114
  btx bootstrap all
93
115
  btx context get
116
+ btx recordings list --days 7
117
+ btx recordings transcript <recording-id>
118
+ btx sessions list --limit 10
119
+ btx sessions get <session-id>
94
120
  btx tasks list --json
95
121
  btx tasks list
96
122
  btx session start --runtime codex --label "Implement project telemetry"
123
+ btx session checkpoint --type decision --content "Decision: keep desktop and standalone CLI on one contract"
124
+ btx session checkpoint --type assumption --content "Assumption: users will review saved session intelligence in BTX"
125
+ btx session checkpoint --type note --content "Learning: product session activities feed long-term project memory"
97
126
  btx session checkpoint --type risk --content "Task meaning is still ambiguous across sessions"
98
127
  btx session finish --summary "Finished CLI bootstrap and repo linking flow"
99
128
 
100
129
  All commands that return BTX data support --json for machine-readable output.`);
101
130
  }
131
+ function printAskHelp() {
132
+ console.log(`Usage:
133
+ btx ask "<prompt>" [--session-id <id>] [--label <label>] [--trace-tools] [--json]
134
+
135
+ Examples:
136
+ btx ask "Show me a count of tasks by status"
137
+ btx ask "What should I focus on next?" --trace-tools
138
+ btx ask "Continue refining that plan" --session-id <session-id>
139
+ btx ask --prompt "Summarize our open risks" --json`);
140
+ }
141
+ function printMessagesHelp() {
142
+ console.log(`Usage:
143
+ btx messages list [--peer <member>] [--conversation <conversation>] [--limit <n>] [--before <iso>] [--json]
144
+ btx messages read [--peer <member>] [--conversation <conversation>] [--limit <n>] [--before <iso>] [--json]
145
+ btx messages send "<body>" [--peer <member>] [--conversation <conversation>] [--reply-to <message-id>] [--json]
146
+ btx messages members [--json]
147
+ btx messages conversations [--json]
148
+
149
+ Scope:
150
+ no scope flags Team chat
151
+ --peer <member> Direct messages with a project member
152
+ --conversation <conversation> Existing group conversation
153
+
154
+ Examples:
155
+ btx messages list
156
+ btx messages list --peer faruk@secondcontext.com
157
+ btx messages send "Shipped the fix in main"
158
+ btx messages send "Can you take a look?" --peer Faruk
159
+ btx messages send --conversation "Launch planning" "Review the latest blockers"
160
+ printf "Daily update: validation passed" | btx messages send
161
+ btx messages members
162
+ btx messages conversations --json`);
163
+ }
102
164
  function bestEffortGitBranch(repoRoot) {
103
165
  const result = spawnSync('git', ['rev-parse', '--abbrev-ref', 'HEAD'], {
104
166
  cwd: repoRoot,
@@ -170,8 +232,14 @@ function printLoginResult(authSource, apiUrl, browserOpened) {
170
232
  }
171
233
  console.log(`API base: ${apiUrl}`);
172
234
  }
173
- function authStatusPayload() {
174
- const env = resolveRuntimeEnv();
235
+ function resolveCliEnv(startDir = process.cwd(), flags = {}, overrides = {}) {
236
+ return resolveRuntimeEnv(startDir, {
237
+ ...(flags['api-url'] ? { BTX_API_URL: flags['api-url'] } : {}),
238
+ ...overrides,
239
+ });
240
+ }
241
+ function authStatusPayload(flags = {}) {
242
+ const env = resolveCliEnv(process.cwd(), flags);
175
243
  const hasEnvToken = !!process.env.BTX_ACCESS_TOKEN;
176
244
  const authSource = hasEnvToken ? 'env' : resolveStoredAuthSessionSource();
177
245
  return {
@@ -183,7 +251,7 @@ function authStatusPayload() {
183
251
  };
184
252
  }
185
253
  async function handleProjects(flags) {
186
- const env = resolveRuntimeEnv();
254
+ const env = resolveCliEnv(process.cwd(), flags);
187
255
  const projects = await listProjects(env);
188
256
  if (isJson(flags)) {
189
257
  printJson(projects);
@@ -204,7 +272,7 @@ async function handleProjects(flags) {
204
272
  async function handleRepoLink(projectId, flags) {
205
273
  if (!projectId)
206
274
  die('Usage: btx repo link <project-id>');
207
- const env = resolveRuntimeEnv();
275
+ const env = resolveCliEnv(process.cwd(), flags);
208
276
  const projects = await listProjects(env);
209
277
  const project = projects.find((item) => item.id === projectId);
210
278
  if (!project) {
@@ -265,8 +333,269 @@ function handleRepoStatus(flags) {
265
333
  console.log('Active session: none');
266
334
  }
267
335
  }
336
+ function formatAskToolTrace(toolCalls) {
337
+ return toolCalls.map((toolCall) => {
338
+ const status = toolCall.isError ? 'error' : toolCall.status;
339
+ const input = Object.keys(toolCall.input).length > 0
340
+ ? ` ${JSON.stringify(toolCall.input)}`
341
+ : '';
342
+ return `[${toolCall.kind}:${status}] ${toolCall.name}${input}`;
343
+ });
344
+ }
345
+ function parsePositiveIntFlag(rawValue, flagName) {
346
+ if (!rawValue)
347
+ return undefined;
348
+ const parsed = Number.parseInt(rawValue, 10);
349
+ if (!Number.isInteger(parsed) || parsed < 1) {
350
+ die(`\`${flagName}\` must be a positive integer.`);
351
+ }
352
+ return parsed;
353
+ }
354
+ function formatTimestamp(value) {
355
+ const date = new Date(value);
356
+ if (Number.isNaN(date.valueOf()))
357
+ return value;
358
+ return new Intl.DateTimeFormat(undefined, {
359
+ dateStyle: 'medium',
360
+ timeStyle: 'short',
361
+ }).format(date);
362
+ }
363
+ function summarizeText(value, maxLength = 120) {
364
+ const normalized = value.replace(/\s+/g, ' ').trim();
365
+ if (normalized.length <= maxLength)
366
+ return normalized;
367
+ return `${normalized.slice(0, maxLength - 1).trimEnd()}…`;
368
+ }
369
+ function printIndentedBlock(value, indent = ' ') {
370
+ for (const line of value.split(/\r?\n/)) {
371
+ console.log(`${indent}${line}`);
372
+ }
373
+ }
374
+ function describeMember(member) {
375
+ if (member.name && member.email)
376
+ return `${member.name} <${member.email}>`;
377
+ return member.name ?? member.email ?? member.userId;
378
+ }
379
+ function describeConversationMember(member) {
380
+ if (member.name && member.email)
381
+ return `${member.name} <${member.email}>`;
382
+ if (member.name)
383
+ return member.name;
384
+ if (member.email)
385
+ return member.email;
386
+ if (member.externalName && member.externalEmail)
387
+ return `${member.externalName} <${member.externalEmail}>`;
388
+ return member.externalName ?? member.externalEmail ?? member.userId ?? 'Unknown member';
389
+ }
390
+ function printMessage(message) {
391
+ const sender = message.senderName ?? message.senderEmail ?? message.userId;
392
+ console.log(`${formatTimestamp(message.createdAt)} ${sender}`);
393
+ console.log(` ID: ${message.id}`);
394
+ if (message.replyTo) {
395
+ console.log(` Replying to ${message.replyTo.senderName ?? 'Unknown sender'}: ${summarizeText(message.replyTo.body, 90)}`);
396
+ }
397
+ if (message.body.trim()) {
398
+ printIndentedBlock(message.body);
399
+ }
400
+ else if (message.attachments.length > 0) {
401
+ console.log(' Attachment-only message');
402
+ }
403
+ if (message.attachments.length > 0) {
404
+ console.log(` Attachments: ${message.attachments.length}`);
405
+ }
406
+ if (message.reactions.length > 0) {
407
+ console.log(` Reactions: ${message.reactions.map((reaction) => `${reaction.emoji} x${reaction.count}`).join(', ')}`);
408
+ }
409
+ }
410
+ async function readMessageBody(positionals, flags) {
411
+ const inlineBody = (flags.body ?? positionals.slice(2).join(' ')).trim();
412
+ if (inlineBody)
413
+ return inlineBody;
414
+ if (!process.stdin.isTTY) {
415
+ const chunks = [];
416
+ for await (const chunk of process.stdin) {
417
+ chunks.push(typeof chunk === 'string' ? chunk : chunk.toString('utf8'));
418
+ }
419
+ const pipedBody = chunks.join('').trim();
420
+ if (pipedBody)
421
+ return pipedBody;
422
+ }
423
+ return '';
424
+ }
425
+ function printScopeSummary(scope) {
426
+ console.log(`Scope: ${scope.label}`);
427
+ }
428
+ async function handleAsk(positionals, flags) {
429
+ if (flags.help === 'true') {
430
+ printAskHelp();
431
+ return;
432
+ }
433
+ const prompt = (flags.prompt ?? positionals.slice(1).join(' ')).trim();
434
+ if (!prompt) {
435
+ printAskHelp();
436
+ die('Prompt is required.');
437
+ }
438
+ const env = resolveCliEnv(process.cwd(), flags);
439
+ const result = await projectApiPost(env, '/ask', {
440
+ prompt,
441
+ ...(flags['session-id'] ? { sessionId: flags['session-id'] } : {}),
442
+ ...(flags.label ? { label: flags.label } : {}),
443
+ includeToolTrace: flags['trace-tools'] === 'true',
444
+ });
445
+ if (isJson(flags)) {
446
+ printJson(result);
447
+ return;
448
+ }
449
+ if (result.response) {
450
+ console.log(result.response);
451
+ }
452
+ if (result.question) {
453
+ if (result.response)
454
+ console.log();
455
+ console.log(`Question: ${result.question}`);
456
+ }
457
+ if (flags['trace-tools'] === 'true' && result.toolCalls && result.toolCalls.length > 0) {
458
+ if (result.response || result.question)
459
+ console.log();
460
+ console.log('Tools:');
461
+ for (const line of formatAskToolTrace(result.toolCalls)) {
462
+ console.log(` ${line}`);
463
+ }
464
+ }
465
+ }
466
+ async function handleMessagesMembers(flags) {
467
+ const env = resolveCliEnv(process.cwd(), flags);
468
+ const members = await listProjectMembers(env);
469
+ if (isJson(flags)) {
470
+ printJson(members);
471
+ return;
472
+ }
473
+ if (members.length === 0) {
474
+ console.log('No BTX project members found.');
475
+ return;
476
+ }
477
+ for (const member of members) {
478
+ console.log(describeMember(member));
479
+ console.log(` User ID: ${member.userId}`);
480
+ console.log(` Member ID: ${member.id}`);
481
+ console.log(` Role: ${member.role}`);
482
+ console.log(` Joined: ${formatTimestamp(member.joinedAt)}`);
483
+ console.log();
484
+ }
485
+ }
486
+ async function handleMessagesConversations(flags) {
487
+ const env = resolveCliEnv(process.cwd(), flags);
488
+ const result = await listChatConversations(env);
489
+ if (isJson(flags)) {
490
+ printJson(result);
491
+ return;
492
+ }
493
+ if (result.conversations.length === 0) {
494
+ console.log('No BTX chat conversations found.');
495
+ return;
496
+ }
497
+ for (const conversation of result.conversations) {
498
+ console.log(conversation.name);
499
+ console.log(` ID: ${conversation.id}`);
500
+ console.log(` Created: ${formatTimestamp(conversation.createdAt)}`);
501
+ if (conversation.members.length > 0) {
502
+ console.log(` Members: ${conversation.members.map(describeConversationMember).join(', ')}`);
503
+ }
504
+ if (conversation.lastMessage) {
505
+ const sender = conversation.lastMessage.senderName ?? 'Unknown sender';
506
+ console.log(` Last message: ${sender}: ${summarizeText(conversation.lastMessage.body)} (${formatTimestamp(conversation.lastMessage.createdAt)})`);
507
+ }
508
+ if (conversation.cursorAt) {
509
+ console.log(` Last read: ${formatTimestamp(conversation.cursorAt)}`);
510
+ }
511
+ console.log();
512
+ }
513
+ }
514
+ async function handleMessagesList(flags) {
515
+ const env = resolveCliEnv(process.cwd(), flags);
516
+ const scope = await resolveMessageScope(env, {
517
+ peer: flags.peer,
518
+ conversation: flags.conversation,
519
+ });
520
+ const result = await listChatMessages(env, scope, {
521
+ before: flags.before,
522
+ limit: parsePositiveIntFlag(flags.limit, '--limit'),
523
+ });
524
+ if (isJson(flags)) {
525
+ printJson({
526
+ scope,
527
+ ...result,
528
+ });
529
+ return;
530
+ }
531
+ printScopeSummary(scope);
532
+ if (result.lastReadAt) {
533
+ console.log(`Last read: ${formatTimestamp(result.lastReadAt)}`);
534
+ }
535
+ if (result.messages.length === 0) {
536
+ console.log('No messages found.');
537
+ return;
538
+ }
539
+ console.log();
540
+ for (const [index, message] of result.messages.entries()) {
541
+ printMessage(message);
542
+ if (index < result.messages.length - 1)
543
+ console.log();
544
+ }
545
+ if (result.hasMore) {
546
+ console.log();
547
+ console.log('More messages are available. Use `--before <iso>` to page backward.');
548
+ }
549
+ }
550
+ async function handleMessagesSend(positionals, flags) {
551
+ const env = resolveCliEnv(process.cwd(), flags);
552
+ const scope = await resolveMessageScope(env, {
553
+ peer: flags.peer,
554
+ conversation: flags.conversation,
555
+ });
556
+ const body = await readMessageBody(positionals, flags);
557
+ if (!body) {
558
+ die('Usage: btx messages send "<body>" [--peer <member>] [--conversation <conversation>] [--reply-to <message-id>]');
559
+ }
560
+ const result = await sendChatMessage(env, scope, {
561
+ body,
562
+ replyToId: flags['reply-to'] ?? null,
563
+ });
564
+ if (isJson(flags)) {
565
+ printJson({
566
+ ...result,
567
+ scope,
568
+ body,
569
+ replyToId: flags['reply-to'] ?? null,
570
+ });
571
+ return;
572
+ }
573
+ console.log(`Sent message ${result.id}`);
574
+ printScopeSummary(scope);
575
+ console.log(`Created: ${formatTimestamp(result.createdAt)}`);
576
+ }
577
+ async function handleMessages(positionals, flags) {
578
+ const subcommand = positionals[1];
579
+ if (flags.help === 'true' || !subcommand || subcommand === 'help') {
580
+ printMessagesHelp();
581
+ return;
582
+ }
583
+ switch (subcommand) {
584
+ case 'list':
585
+ case 'read':
586
+ return handleMessagesList(flags);
587
+ case 'send':
588
+ return handleMessagesSend(positionals, flags);
589
+ case 'members':
590
+ return handleMessagesMembers(flags);
591
+ case 'conversations':
592
+ return handleMessagesConversations(flags);
593
+ default:
594
+ die('Usage: btx messages <list|read|send|members|conversations> [...]');
595
+ }
596
+ }
268
597
  function handleLoginStatus(flags) {
269
- const status = authStatusPayload();
598
+ const status = authStatusPayload(flags);
270
599
  if (isJson(flags)) {
271
600
  printJson(status);
272
601
  return;
@@ -288,9 +617,7 @@ async function handleLogin(positionals, flags) {
288
617
  if (subcommand) {
289
618
  die('Usage: btx login [status] [flags]');
290
619
  }
291
- const env = resolveRuntimeEnv(process.cwd(), {
292
- ...(flags['api-url'] ? { BTX_API_URL: flags['api-url'] } : {}),
293
- });
620
+ const env = resolveCliEnv(process.cwd(), flags);
294
621
  const apiUrl = env.BTX_API_URL || DEFAULT_API_URL;
295
622
  const result = await loginToBtx({
296
623
  apiUrl,
@@ -392,9 +719,7 @@ async function selectBootstrapTarget(targetArg, flags) {
392
719
  }
393
720
  async function handleSetup(targetArg, flags) {
394
721
  const repoRoot = findRepoRoot();
395
- let env = resolveRuntimeEnv(repoRoot, {
396
- ...(flags['api-url'] ? { BTX_API_URL: flags['api-url'] } : {}),
397
- });
722
+ let env = resolveCliEnv(repoRoot, flags);
398
723
  const loginResult = await loginToBtx({
399
724
  apiUrl: env.BTX_API_URL || DEFAULT_API_URL,
400
725
  env,
@@ -410,9 +735,7 @@ async function handleSetup(targetArg, flags) {
410
735
  }
411
736
  },
412
737
  });
413
- env = resolveRuntimeEnv(repoRoot, {
414
- ...(flags['api-url'] ? { BTX_API_URL: flags['api-url'] } : {}),
415
- });
738
+ env = resolveCliEnv(repoRoot, flags);
416
739
  let repoConfig = loadRepoConfig(repoRoot);
417
740
  let linkedProjectName = repoConfig?.projectName ?? null;
418
741
  let linkedProjectId = repoConfig?.projectId ?? null;
@@ -469,7 +792,7 @@ function getCurrentSessionOrDie(repoRoot) {
469
792
  }
470
793
  async function handleSessionStart(flags) {
471
794
  const repoRoot = findRepoRoot();
472
- const env = resolveRuntimeEnv(repoRoot);
795
+ const env = resolveCliEnv(repoRoot, flags);
473
796
  const repoConfig = loadRepoConfig(repoRoot);
474
797
  if (!repoConfig) {
475
798
  die('This repo is not linked to BTX yet. Run `btx repo link <project-id>` first.');
@@ -546,9 +869,12 @@ async function handleSessionCheckpoint(flags) {
546
869
  const type = flags.type || 'note';
547
870
  const content = flags.content;
548
871
  if (!content) {
549
- die('Usage: btx session checkpoint --type note|risk|decision|blocker|progress|assumption --content "..."');
872
+ die(`Usage: btx session checkpoint --type ${SESSION_CHECKPOINT_USAGE} --content "..."`);
873
+ }
874
+ if (!SESSION_CHECKPOINT_TYPES.includes(type)) {
875
+ die(`Unsupported checkpoint type: ${type}. Use one of ${SESSION_CHECKPOINT_USAGE}.`);
550
876
  }
551
- const env = resolveRuntimeEnv(repoRoot, {
877
+ const env = resolveCliEnv(repoRoot, flags, {
552
878
  BTX_SESSION_ID: active.sessionId,
553
879
  BTX_PROJECT_ID: active.projectId,
554
880
  });
@@ -571,7 +897,7 @@ async function handleSessionCheckpoint(flags) {
571
897
  async function handleSessionFinish(flags) {
572
898
  const repoRoot = findRepoRoot();
573
899
  const active = getCurrentSessionOrDie(repoRoot);
574
- const env = resolveRuntimeEnv(repoRoot, {
900
+ const env = resolveCliEnv(repoRoot, flags, {
575
901
  BTX_SESSION_ID: active.sessionId,
576
902
  BTX_PROJECT_ID: active.projectId,
577
903
  });
@@ -624,7 +950,7 @@ async function main() {
624
950
  return;
625
951
  }
626
952
  if (RUNTIME_RESOURCES.has(command)) {
627
- await runRuntimeCli(argv, resolveRuntimeEnv());
953
+ await runRuntimeCli(argv, resolveCliEnv(process.cwd(), flags));
628
954
  return;
629
955
  }
630
956
  switch (command) {
@@ -636,6 +962,10 @@ async function main() {
636
962
  return handleSetup(positional[1], flags);
637
963
  case 'projects':
638
964
  return handleProjects(flags);
965
+ case 'ask':
966
+ return handleAsk(positional, flags);
967
+ case 'messages':
968
+ return handleMessages(positional, flags);
639
969
  case 'repo':
640
970
  switch (positional[1]) {
641
971
  case 'link':