@link-assistant/agent 0.0.9 → 0.0.12

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.
Files changed (104) hide show
  1. package/EXAMPLES.md +36 -0
  2. package/MODELS.md +72 -24
  3. package/README.md +59 -2
  4. package/TOOLS.md +20 -0
  5. package/package.json +35 -2
  6. package/src/agent/agent.ts +68 -54
  7. package/src/auth/claude-oauth.ts +426 -0
  8. package/src/auth/index.ts +28 -26
  9. package/src/auth/plugins.ts +876 -0
  10. package/src/bun/index.ts +53 -43
  11. package/src/bus/global.ts +5 -5
  12. package/src/bus/index.ts +59 -53
  13. package/src/cli/bootstrap.js +12 -12
  14. package/src/cli/bootstrap.ts +6 -6
  15. package/src/cli/cmd/agent.ts +97 -92
  16. package/src/cli/cmd/auth.ts +469 -0
  17. package/src/cli/cmd/cmd.ts +2 -2
  18. package/src/cli/cmd/export.ts +41 -41
  19. package/src/cli/cmd/mcp.ts +144 -119
  20. package/src/cli/cmd/models.ts +30 -29
  21. package/src/cli/cmd/run.ts +269 -213
  22. package/src/cli/cmd/stats.ts +185 -146
  23. package/src/cli/error.ts +17 -13
  24. package/src/cli/ui.ts +39 -24
  25. package/src/command/index.ts +26 -26
  26. package/src/config/config.ts +528 -288
  27. package/src/config/markdown.ts +15 -15
  28. package/src/file/ripgrep.ts +201 -169
  29. package/src/file/time.ts +21 -18
  30. package/src/file/watcher.ts +51 -42
  31. package/src/file.ts +1 -1
  32. package/src/flag/flag.ts +26 -11
  33. package/src/format/formatter.ts +206 -162
  34. package/src/format/index.ts +61 -61
  35. package/src/global/index.ts +21 -21
  36. package/src/id/id.ts +47 -33
  37. package/src/index.js +346 -199
  38. package/src/json-standard/index.ts +67 -51
  39. package/src/mcp/index.ts +135 -128
  40. package/src/patch/index.ts +336 -267
  41. package/src/project/bootstrap.ts +15 -15
  42. package/src/project/instance.ts +43 -36
  43. package/src/project/project.ts +47 -47
  44. package/src/project/state.ts +37 -33
  45. package/src/provider/models-macro.ts +5 -5
  46. package/src/provider/models.ts +32 -32
  47. package/src/provider/opencode.js +19 -19
  48. package/src/provider/provider.ts +518 -277
  49. package/src/provider/transform.ts +143 -102
  50. package/src/server/project.ts +21 -21
  51. package/src/server/server.ts +111 -105
  52. package/src/session/agent.js +66 -60
  53. package/src/session/compaction.ts +136 -111
  54. package/src/session/index.ts +189 -156
  55. package/src/session/message-v2.ts +312 -268
  56. package/src/session/message.ts +73 -57
  57. package/src/session/processor.ts +180 -166
  58. package/src/session/prompt.ts +678 -533
  59. package/src/session/retry.ts +26 -23
  60. package/src/session/revert.ts +76 -62
  61. package/src/session/status.ts +26 -26
  62. package/src/session/summary.ts +97 -76
  63. package/src/session/system.ts +77 -63
  64. package/src/session/todo.ts +22 -16
  65. package/src/snapshot/index.ts +92 -76
  66. package/src/storage/storage.ts +157 -120
  67. package/src/tool/bash.ts +116 -106
  68. package/src/tool/batch.ts +73 -59
  69. package/src/tool/codesearch.ts +60 -53
  70. package/src/tool/edit.ts +319 -263
  71. package/src/tool/glob.ts +32 -28
  72. package/src/tool/grep.ts +72 -53
  73. package/src/tool/invalid.ts +7 -7
  74. package/src/tool/ls.ts +77 -64
  75. package/src/tool/multiedit.ts +30 -21
  76. package/src/tool/patch.ts +121 -94
  77. package/src/tool/read.ts +140 -122
  78. package/src/tool/registry.ts +38 -38
  79. package/src/tool/task.ts +93 -60
  80. package/src/tool/todo.ts +16 -16
  81. package/src/tool/tool.ts +45 -36
  82. package/src/tool/webfetch.ts +97 -74
  83. package/src/tool/websearch.ts +78 -64
  84. package/src/tool/write.ts +21 -15
  85. package/src/util/binary.ts +27 -19
  86. package/src/util/context.ts +8 -8
  87. package/src/util/defer.ts +7 -5
  88. package/src/util/error.ts +24 -19
  89. package/src/util/eventloop.ts +16 -10
  90. package/src/util/filesystem.ts +37 -33
  91. package/src/util/fn.ts +11 -8
  92. package/src/util/iife.ts +1 -1
  93. package/src/util/keybind.ts +44 -44
  94. package/src/util/lazy.ts +7 -7
  95. package/src/util/locale.ts +20 -16
  96. package/src/util/lock.ts +43 -38
  97. package/src/util/log.ts +95 -85
  98. package/src/util/queue.ts +8 -8
  99. package/src/util/rpc.ts +35 -23
  100. package/src/util/scrap.ts +4 -4
  101. package/src/util/signal.ts +5 -5
  102. package/src/util/timeout.ts +6 -6
  103. package/src/util/token.ts +2 -2
  104. package/src/util/wildcard.ts +38 -27
@@ -0,0 +1,469 @@
1
+ import type { Argv } from 'yargs';
2
+ import { cmd } from './cmd';
3
+ import { UI } from '../ui';
4
+ import { Auth } from '../../auth';
5
+ import { AuthPlugins } from '../../auth/plugins';
6
+ import { ModelsDev } from '../../provider/models';
7
+ import * as prompts from '@clack/prompts';
8
+ import path from 'path';
9
+ import os from 'os';
10
+ import { Global } from '../../global';
11
+ import { map, pipe, sortBy, values } from 'remeda';
12
+
13
+ /**
14
+ * Auth Command
15
+ *
16
+ * Manages authentication for various providers.
17
+ * Based on OpenCode's auth implementation supporting:
18
+ * - OAuth providers (Anthropic Claude Pro/Max, GitHub Copilot)
19
+ * - API key providers (OpenAI, Anthropic API, etc.)
20
+ */
21
+
22
+ export const AuthListCommand = cmd({
23
+ command: 'list',
24
+ aliases: ['ls'],
25
+ describe: 'list configured credentials',
26
+ async handler() {
27
+ UI.empty();
28
+ const authPath = path.join(Global.Path.data, 'auth.json');
29
+ const homedir = os.homedir();
30
+ const displayPath = authPath.startsWith(homedir)
31
+ ? authPath.replace(homedir, '~')
32
+ : authPath;
33
+ prompts.intro(`Credentials ${UI.Style.TEXT_DIM}${displayPath}`);
34
+ const results = await Auth.all().then((x) => Object.entries(x));
35
+ const database = await ModelsDev.get();
36
+
37
+ for (const [providerID, result] of results) {
38
+ const name = database[providerID]?.name || providerID;
39
+ prompts.log.info(`${name} ${UI.Style.TEXT_DIM}${result.type}`);
40
+ }
41
+
42
+ prompts.outro(`${results.length} credentials`);
43
+
44
+ // Environment variables section
45
+ const activeEnvVars: Array<{ provider: string; envVar: string }> = [];
46
+
47
+ for (const [providerID, provider] of Object.entries(database)) {
48
+ for (const envVar of provider.env) {
49
+ if (process.env[envVar]) {
50
+ activeEnvVars.push({
51
+ provider: provider.name || providerID,
52
+ envVar,
53
+ });
54
+ }
55
+ }
56
+ }
57
+
58
+ if (activeEnvVars.length > 0) {
59
+ UI.empty();
60
+ prompts.intro('Environment');
61
+
62
+ for (const { provider, envVar } of activeEnvVars) {
63
+ prompts.log.info(`${provider} ${UI.Style.TEXT_DIM}${envVar}`);
64
+ }
65
+
66
+ prompts.outro(
67
+ `${activeEnvVars.length} environment variable` +
68
+ (activeEnvVars.length === 1 ? '' : 's')
69
+ );
70
+ }
71
+ },
72
+ });
73
+
74
+ export const AuthLoginCommand = cmd({
75
+ command: 'login [url]',
76
+ describe: 'log in to a provider',
77
+ builder: (yargs: Argv) =>
78
+ yargs.positional('url', {
79
+ describe: 'wellknown auth provider URL',
80
+ type: 'string',
81
+ }),
82
+ async handler(args) {
83
+ UI.empty();
84
+ prompts.intro('Add credential');
85
+
86
+ // Handle wellknown URL login
87
+ if (args.url) {
88
+ try {
89
+ const wellknown = await fetch(`${args.url}/.well-known/opencode`).then(
90
+ (x) => x.json() as any
91
+ );
92
+ prompts.log.info(`Running \`${wellknown.auth.command.join(' ')}\``);
93
+ const proc = Bun.spawn({
94
+ cmd: wellknown.auth.command,
95
+ stdout: 'pipe',
96
+ });
97
+ const exit = await proc.exited;
98
+ if (exit !== 0) {
99
+ prompts.log.error('Failed');
100
+ prompts.outro('Done');
101
+ return;
102
+ }
103
+ const token = await new Response(proc.stdout).text();
104
+ await Auth.set(args.url as string, {
105
+ type: 'wellknown',
106
+ key: wellknown.auth.env,
107
+ token: token.trim(),
108
+ });
109
+ prompts.log.success('Logged into ' + args.url);
110
+ prompts.outro('Done');
111
+ return;
112
+ } catch (error) {
113
+ prompts.log.error(
114
+ `Failed to fetch wellknown from ${args.url}: ${error}`
115
+ );
116
+ prompts.outro('Failed');
117
+ return;
118
+ }
119
+ }
120
+
121
+ // Refresh models database
122
+ await ModelsDev.refresh().catch(() => {});
123
+ const providers = await ModelsDev.get();
124
+
125
+ // Provider priority (lower = higher priority)
126
+ const priority: Record<string, number> = {
127
+ anthropic: 0,
128
+ 'github-copilot': 1,
129
+ openai: 2,
130
+ google: 3,
131
+ openrouter: 4,
132
+ vercel: 5,
133
+ };
134
+
135
+ // Note: Using `select` instead of `autocomplete` because `autocomplete` is only
136
+ // available in @clack/prompts v1.0.0-alpha.1+, while this package uses stable v0.11.0
137
+ let provider = await prompts.select({
138
+ message: 'Select provider',
139
+ options: [
140
+ ...pipe(
141
+ providers,
142
+ values(),
143
+ sortBy(
144
+ (x) => priority[x.id] ?? 99,
145
+ (x) => x.name ?? x.id
146
+ ),
147
+ map((x) => ({
148
+ label: x.name,
149
+ value: x.id,
150
+ hint: priority[x.id] === 0 ? 'recommended' : undefined,
151
+ }))
152
+ ),
153
+ {
154
+ value: 'other',
155
+ label: 'Other',
156
+ },
157
+ ],
158
+ });
159
+
160
+ if (prompts.isCancel(provider)) throw new UI.CancelledError();
161
+
162
+ // Check if there's a plugin for this provider
163
+ const plugin = AuthPlugins.getPlugin(provider);
164
+
165
+ if (plugin && plugin.methods.length > 0) {
166
+ // Select login method if multiple available
167
+ let methodIndex = 0;
168
+ if (plugin.methods.length > 1) {
169
+ const method = await prompts.select({
170
+ message: 'Login method',
171
+ options: plugin.methods.map((m, index) => ({
172
+ label: m.label,
173
+ value: index.toString(),
174
+ })),
175
+ });
176
+ if (prompts.isCancel(method)) throw new UI.CancelledError();
177
+ methodIndex = parseInt(method);
178
+ }
179
+
180
+ const method = plugin.methods[methodIndex];
181
+
182
+ // Handle prompts for auth method
183
+ await new Promise((resolve) => setTimeout(resolve, 10));
184
+ const inputs: Record<string, string> = {};
185
+
186
+ if (method.prompts) {
187
+ for (const prompt of method.prompts) {
188
+ if (prompt.condition && !prompt.condition(inputs)) {
189
+ continue;
190
+ }
191
+ if (prompt.type === 'select') {
192
+ const value = await prompts.select({
193
+ message: prompt.message,
194
+ options: prompt.options!,
195
+ });
196
+ if (prompts.isCancel(value)) throw new UI.CancelledError();
197
+ inputs[prompt.key] = value;
198
+ } else {
199
+ const value = await prompts.text({
200
+ message: prompt.message,
201
+ placeholder: prompt.placeholder,
202
+ validate: prompt.validate
203
+ ? (v) => prompt.validate!(v ?? '')
204
+ : undefined,
205
+ });
206
+ if (prompts.isCancel(value)) throw new UI.CancelledError();
207
+ inputs[prompt.key] = value;
208
+ }
209
+ }
210
+ }
211
+
212
+ if (method.type === 'oauth') {
213
+ const authorize = await method.authorize!(inputs);
214
+
215
+ if (authorize.url) {
216
+ prompts.log.info('Go to: ' + authorize.url);
217
+ }
218
+
219
+ if (authorize.method === 'auto') {
220
+ if (authorize.instructions) {
221
+ prompts.log.info(authorize.instructions);
222
+ }
223
+ const spinner = prompts.spinner();
224
+ spinner.start('Waiting for authorization...');
225
+ const result = await authorize.callback();
226
+ if (result.type === 'failed') {
227
+ spinner.stop('Failed to authorize', 1);
228
+ }
229
+ if (result.type === 'success') {
230
+ const saveProvider = result.provider ?? provider;
231
+ if ('refresh' in result) {
232
+ const {
233
+ type: _,
234
+ provider: __,
235
+ refresh,
236
+ access,
237
+ expires,
238
+ ...extraFields
239
+ } = result;
240
+ await Auth.set(saveProvider, {
241
+ type: 'oauth',
242
+ refresh,
243
+ access,
244
+ expires,
245
+ ...extraFields,
246
+ } as Auth.Info);
247
+ }
248
+ if ('key' in result) {
249
+ await Auth.set(saveProvider, {
250
+ type: 'api',
251
+ key: result.key,
252
+ });
253
+ }
254
+ spinner.stop('Login successful');
255
+ }
256
+ }
257
+
258
+ if (authorize.method === 'code') {
259
+ const code = await prompts.text({
260
+ message: 'Paste the authorization code here: ',
261
+ validate: (x) => (x && x.length > 0 ? undefined : 'Required'),
262
+ });
263
+ if (prompts.isCancel(code)) throw new UI.CancelledError();
264
+ const result = await authorize.callback(code);
265
+ if (result.type === 'failed') {
266
+ prompts.log.error('Failed to authorize');
267
+ }
268
+ if (result.type === 'success') {
269
+ const saveProvider = result.provider ?? provider;
270
+ if ('refresh' in result) {
271
+ const {
272
+ type: _,
273
+ provider: __,
274
+ refresh,
275
+ access,
276
+ expires,
277
+ ...extraFields
278
+ } = result;
279
+ await Auth.set(saveProvider, {
280
+ type: 'oauth',
281
+ refresh,
282
+ access,
283
+ expires,
284
+ ...extraFields,
285
+ } as Auth.Info);
286
+ }
287
+ if ('key' in result) {
288
+ await Auth.set(saveProvider, {
289
+ type: 'api',
290
+ key: result.key,
291
+ });
292
+ }
293
+ prompts.log.success('Login successful');
294
+ }
295
+ }
296
+
297
+ prompts.outro('Done');
298
+ return;
299
+ }
300
+
301
+ if (method.type === 'api' && method.authorize) {
302
+ const result = await method.authorize(inputs);
303
+ if (result.type === 'failed') {
304
+ prompts.log.error('Failed to authorize');
305
+ }
306
+ if (result.type === 'success') {
307
+ const saveProvider = result.provider ?? provider;
308
+ await Auth.set(saveProvider, {
309
+ type: 'api',
310
+ key: result.key!,
311
+ });
312
+ prompts.log.success('Login successful');
313
+ }
314
+ prompts.outro('Done');
315
+ return;
316
+ }
317
+ }
318
+
319
+ // Handle "other" provider
320
+ if (provider === 'other') {
321
+ const customProvider = await prompts.text({
322
+ message: 'Enter provider id',
323
+ validate: (x) =>
324
+ x && x.match(/^[0-9a-z-]+$/)
325
+ ? undefined
326
+ : 'a-z, 0-9 and hyphens only',
327
+ });
328
+ if (prompts.isCancel(customProvider)) throw new UI.CancelledError();
329
+ provider = customProvider.replace(/^@ai-sdk\//, '');
330
+ prompts.log.warn(
331
+ `This only stores a credential for ${provider} - you may need to configure it in your config file.`
332
+ );
333
+ }
334
+
335
+ // Amazon Bedrock uses environment variables
336
+ if (provider === 'amazon-bedrock') {
337
+ prompts.log.info(
338
+ 'Amazon Bedrock can be configured with standard AWS environment variables like AWS_BEARER_TOKEN_BEDROCK, AWS_PROFILE or AWS_ACCESS_KEY_ID'
339
+ );
340
+ prompts.outro('Done');
341
+ return;
342
+ }
343
+
344
+ // Provider-specific instructions for getting API keys
345
+ const apiKeyUrls: Record<string, string> = {
346
+ openai: 'https://platform.openai.com/api-keys',
347
+ anthropic: 'https://console.anthropic.com/settings/keys',
348
+ google: 'https://aistudio.google.com/app/apikey',
349
+ vercel: 'https://vercel.link/ai-gateway-token',
350
+ openrouter: 'https://openrouter.ai/keys',
351
+ groq: 'https://console.groq.com/keys',
352
+ mistral: 'https://console.mistral.ai/api-keys',
353
+ cohere: 'https://dashboard.cohere.com/api-keys',
354
+ perplexity: 'https://www.perplexity.ai/settings/api',
355
+ togetherai: 'https://api.together.xyz/settings/api-keys',
356
+ deepseek: 'https://platform.deepseek.com/api_keys',
357
+ xai: 'https://console.x.ai',
358
+ };
359
+
360
+ if (apiKeyUrls[provider]) {
361
+ prompts.log.info(`You can create an API key at ${apiKeyUrls[provider]}`);
362
+ }
363
+
364
+ // Fallback: prompt for API key
365
+ const key = await prompts.password({
366
+ message: 'Enter your API key',
367
+ validate: (x) => (x && x.length > 0 ? undefined : 'Required'),
368
+ });
369
+ if (prompts.isCancel(key)) throw new UI.CancelledError();
370
+ await Auth.set(provider, {
371
+ type: 'api',
372
+ key,
373
+ });
374
+
375
+ prompts.outro('Done');
376
+ },
377
+ });
378
+
379
+ export const AuthLogoutCommand = cmd({
380
+ command: 'logout',
381
+ describe: 'log out from a configured provider',
382
+ async handler() {
383
+ UI.empty();
384
+ const credentials = await Auth.all().then((x) => Object.entries(x));
385
+ prompts.intro('Remove credential');
386
+ if (credentials.length === 0) {
387
+ prompts.log.error('No credentials found');
388
+ prompts.outro('Done');
389
+ return;
390
+ }
391
+ const database = await ModelsDev.get();
392
+ const providerID = await prompts.select({
393
+ message: 'Select provider',
394
+ options: credentials.map(([key, value]) => ({
395
+ label:
396
+ (database[key]?.name || key) +
397
+ UI.Style.TEXT_DIM +
398
+ ' (' +
399
+ value.type +
400
+ ')',
401
+ value: key,
402
+ })),
403
+ });
404
+ if (prompts.isCancel(providerID)) throw new UI.CancelledError();
405
+ await Auth.remove(providerID);
406
+ prompts.outro('Logout successful');
407
+ },
408
+ });
409
+
410
+ export const AuthStatusCommand = cmd({
411
+ command: 'status',
412
+ describe: 'check authentication status for all providers (experimental)',
413
+ async handler() {
414
+ UI.empty();
415
+ prompts.intro('Authentication Status');
416
+
417
+ const credentials = await Auth.all();
418
+ const database = await ModelsDev.get();
419
+
420
+ if (Object.keys(credentials).length === 0) {
421
+ prompts.log.warning('No credentials configured');
422
+ prompts.outro("Run 'agent auth login' to authenticate");
423
+ return;
424
+ }
425
+
426
+ for (const [providerID, cred] of Object.entries(credentials)) {
427
+ const name = database[providerID]?.name || providerID;
428
+
429
+ if (cred.type === 'oauth') {
430
+ const isExpired = cred.expires < Date.now();
431
+ const expiresIn = cred.expires - Date.now();
432
+ const expiresStr = isExpired
433
+ ? 'expired'
434
+ : expiresIn < 3600000
435
+ ? `${Math.round(expiresIn / 60000)} minutes`
436
+ : `${Math.round(expiresIn / 3600000)} hours`;
437
+
438
+ if (isExpired) {
439
+ prompts.log.warning(
440
+ `${name}: OAuth token expired (will auto-refresh on next use)`
441
+ );
442
+ } else {
443
+ prompts.log.success(
444
+ `${name}: OAuth authenticated (expires in ${expiresStr})`
445
+ );
446
+ }
447
+ } else if (cred.type === 'api') {
448
+ prompts.log.success(`${name}: API key configured`);
449
+ } else if (cred.type === 'wellknown') {
450
+ prompts.log.success(`${name}: WellKnown token configured`);
451
+ }
452
+ }
453
+
454
+ prompts.outro('Done');
455
+ },
456
+ });
457
+
458
+ export const AuthCommand = cmd({
459
+ command: 'auth',
460
+ describe: 'manage credentials',
461
+ builder: (yargs) =>
462
+ yargs
463
+ .command(AuthLoginCommand)
464
+ .command(AuthLogoutCommand)
465
+ .command(AuthListCommand)
466
+ .command(AuthStatusCommand)
467
+ .demandCommand(1, 'Please specify a subcommand'),
468
+ async handler() {},
469
+ });
@@ -1,5 +1,5 @@
1
- import type { CommandModule } from "yargs"
1
+ import type { CommandModule } from 'yargs';
2
2
 
3
3
  export function cmd<T, U>(input: CommandModule<T, U>) {
4
- return input
4
+ return input;
5
5
  }
@@ -1,50 +1,50 @@
1
- import type { Argv } from "yargs"
2
- import { Session } from "../../session"
3
- import { cmd } from "./cmd"
4
- import { bootstrap } from "../bootstrap"
5
- import { UI } from "../ui"
6
- import * as prompts from "@clack/prompts"
7
- import { EOL } from "os"
1
+ import type { Argv } from 'yargs';
2
+ import { Session } from '../../session';
3
+ import { cmd } from './cmd';
4
+ import { bootstrap } from '../bootstrap';
5
+ import { UI } from '../ui';
6
+ import * as prompts from '@clack/prompts';
7
+ import { EOL } from 'os';
8
8
 
9
9
  export const ExportCommand = cmd({
10
- command: "export [sessionID]",
11
- describe: "export session data as JSON",
10
+ command: 'export [sessionID]',
11
+ describe: 'export session data as JSON',
12
12
  builder: (yargs: Argv) => {
13
- return yargs.positional("sessionID", {
14
- describe: "session id to export",
15
- type: "string",
16
- })
13
+ return yargs.positional('sessionID', {
14
+ describe: 'session id to export',
15
+ type: 'string',
16
+ });
17
17
  },
18
18
  handler: async (args) => {
19
19
  await bootstrap(process.cwd(), async () => {
20
- let sessionID = args.sessionID
21
- process.stderr.write(`Exporting session: ${sessionID ?? "latest"}`)
20
+ let sessionID = args.sessionID;
21
+ process.stderr.write(`Exporting session: ${sessionID ?? 'latest'}`);
22
22
 
23
23
  if (!sessionID) {
24
- UI.empty()
25
- prompts.intro("Export session", {
24
+ UI.empty();
25
+ prompts.intro('Export session', {
26
26
  output: process.stderr,
27
- })
27
+ });
28
28
 
29
- const sessions = []
29
+ const sessions = [];
30
30
  for await (const session of Session.list()) {
31
- sessions.push(session)
31
+ sessions.push(session);
32
32
  }
33
33
 
34
34
  if (sessions.length === 0) {
35
- prompts.log.error("No sessions found", {
35
+ prompts.log.error('No sessions found', {
36
36
  output: process.stderr,
37
- })
38
- prompts.outro("Done", {
37
+ });
38
+ prompts.outro('Done', {
39
39
  output: process.stderr,
40
- })
41
- return
40
+ });
41
+ return;
42
42
  }
43
43
 
44
- sessions.sort((a, b) => b.time.updated - a.time.updated)
44
+ sessions.sort((a, b) => b.time.updated - a.time.updated);
45
45
 
46
46
  const selectedSession = await prompts.autocomplete({
47
- message: "Select session to export",
47
+ message: 'Select session to export',
48
48
  maxItems: 10,
49
49
  options: sessions.map((session) => ({
50
50
  label: session.title,
@@ -52,22 +52,22 @@ export const ExportCommand = cmd({
52
52
  hint: `${new Date(session.time.updated).toLocaleString()} • ${session.id.slice(-8)}`,
53
53
  })),
54
54
  output: process.stderr,
55
- })
55
+ });
56
56
 
57
57
  if (prompts.isCancel(selectedSession)) {
58
- throw new UI.CancelledError()
58
+ throw new UI.CancelledError();
59
59
  }
60
60
 
61
- sessionID = selectedSession as string
61
+ sessionID = selectedSession as string;
62
62
 
63
- prompts.outro("Exporting session...", {
63
+ prompts.outro('Exporting session...', {
64
64
  output: process.stderr,
65
- })
65
+ });
66
66
  }
67
67
 
68
68
  try {
69
- const sessionInfo = await Session.get(sessionID!)
70
- const messages = await Session.messages({ sessionID: sessionID! })
69
+ const sessionInfo = await Session.get(sessionID!);
70
+ const messages = await Session.messages({ sessionID: sessionID! });
71
71
 
72
72
  const exportData = {
73
73
  info: sessionInfo,
@@ -75,14 +75,14 @@ export const ExportCommand = cmd({
75
75
  info: msg.info,
76
76
  parts: msg.parts,
77
77
  })),
78
- }
78
+ };
79
79
 
80
- process.stdout.write(JSON.stringify(exportData, null, 2))
81
- process.stdout.write(EOL)
80
+ process.stdout.write(JSON.stringify(exportData, null, 2));
81
+ process.stdout.write(EOL);
82
82
  } catch (error) {
83
- UI.error(`Session not found: ${sessionID!}`)
84
- process.exit(1)
83
+ UI.error(`Session not found: ${sessionID!}`);
84
+ process.exit(1);
85
85
  }
86
- })
86
+ });
87
87
  },
88
- })
88
+ });