@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 +11 -3
- package/dist/api.d.ts.map +1 -1
- package/dist/api.js +1 -1
- package/dist/api.js.map +1 -1
- package/dist/bin.js +351 -21
- package/dist/bin.js.map +1 -1
- package/dist/bootstrap.d.ts.map +1 -1
- package/dist/bootstrap.js +31 -0
- package/dist/bootstrap.js.map +1 -1
- package/dist/login.d.ts.map +1 -1
- package/dist/login.js +103 -21
- package/dist/login.js.map +1 -1
- package/dist/messages.d.ts +127 -0
- package/dist/messages.d.ts.map +1 -0
- package/dist/messages.js +112 -0
- package/dist/messages.js.map +1 -0
- package/dist/runtime-cli.d.ts.map +1 -1
- package/dist/runtime-cli.js +391 -7
- package/dist/runtime-cli.js.map +1 -1
- package/package.json +1 -1
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.
|
|
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`
|
|
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;
|
|
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.
|
|
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":"AACA,OAAO,EACL,kBAAkB,EAClB,gBAAgB,EAEhB,sBAAsB,GACvB,MAAM,aAAa,CAAA;AAQpB,KAAK,UAAU,kBAAkB,CAAC,GAAkB;IAClD,IAAI,CAAC,GAAG,CAAC,iBAAiB,IAAI,CAAC,GAAG,CAAC,gBAAgB,IAAI,CAAC,GAAG,CAAC,qBAAqB;QAAE,OAAO,IAAI,CAAA;IAE9F,MAAM,QAAQ,GAAG,MAAM,KAAK,CAC1B,GAAG,GAAG,CAAC,gBAAgB,yCAAyC,EAChE;QACE,MAAM,EAAE,MAAM;QACd,OAAO,EAAE;YACP,cAAc,EAAE,kBAAkB;YAClC,MAAM,EAAE,GAAG,CAAC,qBAAqB;SAClC;QACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,aAAa,EAAE,GAAG,CAAC,iBAAiB,EAAE,CAAC;KAC/D,CACF,CAAA;IAED,IAAI,CAAC,QAAQ,CAAC,EAAE;QAAE,OAAO,IAAI,CAAA;IAE7B,MAAM,IAAI,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAIlC,CAAA;IAED,MAAM,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,CAAA;IAChD,MAAM,OAAO,GAAsB;QACjC,YAAY,EAAE,IAAI,CAAC,YAAY;QAC/B,aAAa,EAAE,IAAI,CAAC,aAAa;QACjC,UAAU,EAAE,UAAU,GAAG,IAAI,CAAC,UAAU;QACxC,YAAY,EAAE,GAAG,CAAC,gBAAgB;QAClC,iBAAiB,EAAE,GAAG,CAAC,qBAAqB;QAC5C,OAAO,EAAE,GAAG,CAAC,WAAW;KACzB,CAAA;IACD,sBAAsB,CAAC,OAAO,CAAC,CAAA;IAC/B,GAAG,CAAC,gBAAgB,GAAG,IAAI,CAAC,YAAY,CAAA;IACxC,GAAG,CAAC,iBAAiB,GAAG,IAAI,CAAC,aAAa,CAAA;IAC1C,OAAO,IAAI,CAAC,YAAY,CAAA;AAC1B,CAAC;AAED,KAAK,UAAU,eAAe,CAC5B,GAAkB,EAClB,GAAW,EACX,MAAmD,EACnD,IAAc;IAEd,MAAM,KAAK,GAAG,kBAAkB,CAAC,GAAG,CAAC,CAAA;IAErC,MAAM,SAAS,GAAG,KAAK,EAAE,WAAmB,EAAqB,EAAE,CACjE,KAAK,CAAC,GAAG,EAAE;QACT,MAAM;QACN,OAAO,EAAE;YACP,cAAc,EAAE,kBAAkB;YAClC,aAAa,EAAE,UAAU,WAAW,EAAE;SACvC;QACD,GAAG,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAC9D,CAAC,CAAA;IAEJ,IAAI,QAAQ,GAAG,MAAM,SAAS,CAAC,KAAK,CAAC,CAAA;IACrC,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;QAC5B,MAAM,SAAS,GAAG,MAAM,kBAAkB,CAAC,GAAG,CAAC,CAAA;QAC/C,IAAI,CAAC,SAAS,EAAE,CAAC;YACf,MAAM,IAAI,KAAK,
|
|
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
|
|
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
|
|
174
|
-
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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(
|
|
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 =
|
|
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 =
|
|
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,
|
|
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':
|