@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
@@ -1,212 +1,237 @@
1
- import type { Argv } from "yargs"
2
- import { cmd } from "./cmd"
3
- import { Client } from "@modelcontextprotocol/sdk/client/index.js"
4
- import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js"
5
- import * as prompts from "@clack/prompts"
6
- import { UI } from "../ui"
7
- import { Global } from "../../global"
8
- import { Config } from "../../config/config"
9
- import path from "path"
10
- import fs from "fs/promises"
1
+ import type { Argv } from 'yargs';
2
+ import { cmd } from './cmd';
3
+ import { Client } from '@modelcontextprotocol/sdk/client/index.js';
4
+ import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
5
+ import * as prompts from '@clack/prompts';
6
+ import { UI } from '../ui';
7
+ import { Global } from '../../global';
8
+ import { Config } from '../../config/config';
9
+ import path from 'path';
10
+ import fs from 'fs/promises';
11
11
 
12
12
  export const McpCommand = cmd({
13
- command: "mcp",
14
- builder: (yargs) => yargs.command(McpAddCommand).command(McpListCommand).demandCommand(),
13
+ command: 'mcp',
14
+ builder: (yargs) =>
15
+ yargs.command(McpAddCommand).command(McpListCommand).demandCommand(),
15
16
  async handler() {},
16
- })
17
+ });
17
18
 
18
19
  async function loadGlobalConfig(): Promise<Config.Info> {
19
- const configPath = path.join(Global.Path.config, "opencode.json")
20
+ const configPath = path.join(Global.Path.config, 'opencode.json');
20
21
  try {
21
- const content = await Bun.file(configPath).text()
22
- return JSON.parse(content)
22
+ const content = await Bun.file(configPath).text();
23
+ return JSON.parse(content);
23
24
  } catch {
24
25
  return {
25
- $schema: "https://opencode.ai/config.json",
26
- }
26
+ $schema: 'https://opencode.ai/config.json',
27
+ };
27
28
  }
28
29
  }
29
30
 
30
31
  async function saveGlobalConfig(config: Config.Info): Promise<void> {
31
- const configPath = path.join(Global.Path.config, "opencode.json")
32
- await fs.mkdir(Global.Path.config, { recursive: true })
33
- await Bun.write(configPath, JSON.stringify(config, null, 2))
32
+ const configPath = path.join(Global.Path.config, 'opencode.json');
33
+ await fs.mkdir(Global.Path.config, { recursive: true });
34
+ await Bun.write(configPath, JSON.stringify(config, null, 2));
34
35
  }
35
36
 
36
37
  export const McpAddCommand = cmd({
37
- command: "add [name] [command..]",
38
- describe: "add an MCP server",
38
+ command: 'add [name] [command..]',
39
+ describe: 'add an MCP server',
39
40
  builder: (yargs: Argv) => {
40
41
  return yargs
41
- .positional("name", {
42
- describe: "name of the MCP server",
43
- type: "string",
42
+ .positional('name', {
43
+ describe: 'name of the MCP server',
44
+ type: 'string',
44
45
  })
45
- .positional("command", {
46
- describe: "command and arguments to run the MCP server (e.g., npx @playwright/mcp@latest)",
47
- type: "string",
46
+ .positional('command', {
47
+ describe:
48
+ 'command and arguments to run the MCP server (e.g., npx @playwright/mcp@latest)',
49
+ type: 'string',
48
50
  array: true,
49
51
  })
50
- .option("url", {
51
- describe: "URL for remote MCP server",
52
- type: "string",
52
+ .option('url', {
53
+ describe: 'URL for remote MCP server',
54
+ type: 'string',
53
55
  })
54
- .option("enabled", {
55
- describe: "enable the MCP server",
56
- type: "boolean",
56
+ .option('enabled', {
57
+ describe: 'enable the MCP server',
58
+ type: 'boolean',
57
59
  default: true,
58
- })
60
+ });
59
61
  },
60
62
  async handler(args) {
61
63
  // If name and command are provided as CLI arguments, use non-interactive mode
62
64
  if (args.name && ((args.command && args.command.length > 0) || args.url)) {
63
65
  // Non-interactive mode: CLI arguments provided
64
- const config = await loadGlobalConfig()
65
- config.mcp = config.mcp || {}
66
+ const config = await loadGlobalConfig();
67
+ config.mcp = config.mcp || {};
66
68
 
67
69
  if (args.url) {
68
70
  // Remote MCP server
69
71
  config.mcp[args.name] = {
70
- type: "remote",
72
+ type: 'remote',
71
73
  url: args.url,
72
74
  enabled: args.enabled,
73
- }
74
- UI.success(`Remote MCP server "${args.name}" added with URL: ${args.url}`)
75
+ };
76
+ UI.success(
77
+ `Remote MCP server "${args.name}" added with URL: ${args.url}`
78
+ );
75
79
  } else if (args.command && args.command.length > 0) {
76
80
  // Local MCP server
77
81
  config.mcp[args.name] = {
78
- type: "local",
82
+ type: 'local',
79
83
  command: args.command,
80
84
  enabled: args.enabled,
81
- }
82
- UI.success(`Local MCP server "${args.name}" added with command: ${args.command.join(" ")}`)
85
+ };
86
+ UI.success(
87
+ `Local MCP server "${args.name}" added with command: ${args.command.join(' ')}`
88
+ );
83
89
  }
84
90
 
85
- await saveGlobalConfig(config)
86
- const configPath = path.join(Global.Path.config, "opencode.json")
87
- UI.info(`Configuration saved to: ${configPath}`)
88
- return
91
+ await saveGlobalConfig(config);
92
+ const configPath = path.join(Global.Path.config, 'opencode.json');
93
+ UI.info(`Configuration saved to: ${configPath}`);
94
+ return;
89
95
  }
90
96
 
91
97
  // Interactive mode: prompt for input
92
- UI.empty()
93
- prompts.intro("Add MCP server")
98
+ UI.empty();
99
+ prompts.intro('Add MCP server');
94
100
 
95
101
  const name = await prompts.text({
96
- message: "Enter MCP server name",
97
- validate: (x) => (x && x.length > 0 ? undefined : "Required"),
98
- })
99
- if (prompts.isCancel(name)) throw new UI.CancelledError()
102
+ message: 'Enter MCP server name',
103
+ validate: (x) => (x && x.length > 0 ? undefined : 'Required'),
104
+ });
105
+ if (prompts.isCancel(name)) throw new UI.CancelledError();
100
106
 
101
107
  const type = await prompts.select({
102
- message: "Select MCP server type",
108
+ message: 'Select MCP server type',
103
109
  options: [
104
110
  {
105
- label: "Local",
106
- value: "local",
107
- hint: "Run a local command",
111
+ label: 'Local',
112
+ value: 'local',
113
+ hint: 'Run a local command',
108
114
  },
109
115
  {
110
- label: "Remote",
111
- value: "remote",
112
- hint: "Connect to a remote URL",
116
+ label: 'Remote',
117
+ value: 'remote',
118
+ hint: 'Connect to a remote URL',
113
119
  },
114
120
  ],
115
- })
116
- if (prompts.isCancel(type)) throw new UI.CancelledError()
121
+ });
122
+ if (prompts.isCancel(type)) throw new UI.CancelledError();
117
123
 
118
- const config = await loadGlobalConfig()
119
- config.mcp = config.mcp || {}
124
+ const config = await loadGlobalConfig();
125
+ config.mcp = config.mcp || {};
120
126
 
121
- if (type === "local") {
127
+ if (type === 'local') {
122
128
  const command = await prompts.text({
123
- message: "Enter command to run",
124
- placeholder: "e.g., npx @playwright/mcp@latest",
125
- validate: (x) => (x && x.length > 0 ? undefined : "Required"),
126
- })
127
- if (prompts.isCancel(command)) throw new UI.CancelledError()
129
+ message: 'Enter command to run',
130
+ placeholder: 'e.g., npx @playwright/mcp@latest',
131
+ validate: (x) => (x && x.length > 0 ? undefined : 'Required'),
132
+ });
133
+ if (prompts.isCancel(command)) throw new UI.CancelledError();
128
134
 
129
135
  // Parse command into array
130
- const commandParts = command.split(/\s+/)
136
+ const commandParts = command.split(/\s+/);
131
137
  config.mcp[name] = {
132
- type: "local",
138
+ type: 'local',
133
139
  command: commandParts,
134
140
  enabled: true,
135
- }
141
+ };
136
142
 
137
- await saveGlobalConfig(config)
138
- prompts.log.info(`Local MCP server "${name}" configured with command: ${command}`)
143
+ await saveGlobalConfig(config);
144
+ prompts.log.info(
145
+ `Local MCP server "${name}" configured with command: ${command}`
146
+ );
139
147
  }
140
148
 
141
- if (type === "remote") {
149
+ if (type === 'remote') {
142
150
  const url = await prompts.text({
143
- message: "Enter MCP server URL",
144
- placeholder: "e.g., https://example.com/mcp",
151
+ message: 'Enter MCP server URL',
152
+ placeholder: 'e.g., https://example.com/mcp',
145
153
  validate: (x) => {
146
- if (!x) return "Required"
147
- if (x.length === 0) return "Required"
148
- const isValid = URL.canParse(x)
149
- return isValid ? undefined : "Invalid URL"
154
+ if (!x) return 'Required';
155
+ if (x.length === 0) return 'Required';
156
+ const isValid = URL.canParse(x);
157
+ return isValid ? undefined : 'Invalid URL';
150
158
  },
151
- })
152
- if (prompts.isCancel(url)) throw new UI.CancelledError()
159
+ });
160
+ if (prompts.isCancel(url)) throw new UI.CancelledError();
153
161
 
154
162
  // Test connection
155
163
  try {
156
164
  const client = new Client({
157
- name: "opencode",
158
- version: "1.0.0",
159
- })
160
- const transport = new StreamableHTTPClientTransport(new URL(url))
161
- await client.connect(transport)
162
- await client.close()
165
+ name: 'opencode',
166
+ version: '1.0.0',
167
+ });
168
+ const transport = new StreamableHTTPClientTransport(new URL(url));
169
+ await client.connect(transport);
170
+ await client.close();
163
171
  } catch (error) {
164
- prompts.log.warn(`Could not verify connection to ${url}, but saving configuration anyway`)
172
+ prompts.log.warn(
173
+ `Could not verify connection to ${url}, but saving configuration anyway`
174
+ );
165
175
  }
166
176
 
167
177
  config.mcp[name] = {
168
- type: "remote",
178
+ type: 'remote',
169
179
  url: url,
170
180
  enabled: true,
171
- }
181
+ };
172
182
 
173
- await saveGlobalConfig(config)
174
- prompts.log.info(`Remote MCP server "${name}" configured with URL: ${url}`)
183
+ await saveGlobalConfig(config);
184
+ prompts.log.info(
185
+ `Remote MCP server "${name}" configured with URL: ${url}`
186
+ );
175
187
  }
176
188
 
177
- const configPath = path.join(Global.Path.config, "opencode.json")
178
- prompts.log.info(`Configuration saved to: ${configPath}`)
179
- prompts.outro("MCP server added successfully")
189
+ const configPath = path.join(Global.Path.config, 'opencode.json');
190
+ prompts.log.info(`Configuration saved to: ${configPath}`);
191
+ prompts.outro('MCP server added successfully');
180
192
  },
181
- })
193
+ });
182
194
 
183
195
  export const McpListCommand = cmd({
184
- command: "list",
185
- describe: "list configured MCP servers",
196
+ command: 'list',
197
+ describe: 'list configured MCP servers',
186
198
  async handler() {
187
- const config = await loadGlobalConfig()
188
- const mcpServers = config.mcp || {}
199
+ const config = await loadGlobalConfig();
200
+ const mcpServers = config.mcp || {};
189
201
 
190
202
  if (Object.keys(mcpServers).length === 0) {
191
- UI.info("No MCP servers configured")
192
- return
203
+ UI.info('No MCP servers configured');
204
+ return;
193
205
  }
194
206
 
195
- UI.println(UI.Style.TEXT_BOLD + "Configured MCP servers:" + UI.Style.TEXT_NORMAL)
196
- UI.empty()
207
+ UI.println(
208
+ UI.Style.TEXT_BOLD + 'Configured MCP servers:' + UI.Style.TEXT_NORMAL
209
+ );
210
+ UI.empty();
197
211
 
198
212
  for (const [name, server] of Object.entries(mcpServers)) {
199
- const enabledStatus = server.enabled !== false ? UI.Style.TEXT_SUCCESS_BOLD + "[enabled]" : UI.Style.TEXT_DIM + "[disabled]"
200
- UI.println(UI.Style.TEXT_INFO_BOLD + ` ${name}` + UI.Style.TEXT_NORMAL + ` ${enabledStatus}` + UI.Style.TEXT_NORMAL)
201
-
202
- if (server.type === "local") {
203
- UI.println(UI.Style.TEXT_DIM + ` Type: local`)
204
- UI.println(UI.Style.TEXT_DIM + ` Command: ${server.command.join(" ")}`)
205
- } else if (server.type === "remote") {
206
- UI.println(UI.Style.TEXT_DIM + ` Type: remote`)
207
- UI.println(UI.Style.TEXT_DIM + ` URL: ${server.url}`)
213
+ const enabledStatus =
214
+ server.enabled !== false
215
+ ? UI.Style.TEXT_SUCCESS_BOLD + '[enabled]'
216
+ : UI.Style.TEXT_DIM + '[disabled]';
217
+ UI.println(
218
+ UI.Style.TEXT_INFO_BOLD +
219
+ ` ${name}` +
220
+ UI.Style.TEXT_NORMAL +
221
+ ` ${enabledStatus}` +
222
+ UI.Style.TEXT_NORMAL
223
+ );
224
+
225
+ if (server.type === 'local') {
226
+ UI.println(UI.Style.TEXT_DIM + ` Type: local`);
227
+ UI.println(
228
+ UI.Style.TEXT_DIM + ` Command: ${server.command.join(' ')}`
229
+ );
230
+ } else if (server.type === 'remote') {
231
+ UI.println(UI.Style.TEXT_DIM + ` Type: remote`);
232
+ UI.println(UI.Style.TEXT_DIM + ` URL: ${server.url}`);
208
233
  }
209
- UI.empty()
234
+ UI.empty();
210
235
  }
211
236
  },
212
- })
237
+ });
@@ -1,58 +1,59 @@
1
- import type { Argv } from "yargs"
2
- import { Instance } from "../../project/instance"
3
- import { Provider } from "../../provider/provider"
4
- import { cmd } from "./cmd"
5
- import { UI } from "../ui"
6
- import { EOL } from "os"
1
+ import type { Argv } from 'yargs';
2
+ import { Instance } from '../../project/instance';
3
+ import { Provider } from '../../provider/provider';
4
+ import { cmd } from './cmd';
5
+ import { UI } from '../ui';
6
+ import { EOL } from 'os';
7
7
 
8
8
  export const ModelsCommand = cmd({
9
- command: "models [provider]",
10
- describe: "list all available models",
9
+ command: 'models [provider]',
10
+ describe: 'list all available models',
11
11
  builder: (yargs: Argv) => {
12
12
  return yargs
13
- .positional("provider", {
14
- describe: "provider ID to filter models by",
15
- type: "string",
13
+ .positional('provider', {
14
+ describe: 'provider ID to filter models by',
15
+ type: 'string',
16
16
  array: false,
17
17
  })
18
- .option("verbose", {
19
- describe: "use more verbose model output (includes metadata like costs)",
20
- type: "boolean",
21
- })
18
+ .option('verbose', {
19
+ describe:
20
+ 'use more verbose model output (includes metadata like costs)',
21
+ type: 'boolean',
22
+ });
22
23
  },
23
24
  handler: async (args) => {
24
25
  await Instance.provide({
25
26
  directory: process.cwd(),
26
27
  async fn() {
27
- const providers = await Provider.list()
28
+ const providers = await Provider.list();
28
29
 
29
30
  function printModels(providerID: string, verbose?: boolean) {
30
- const provider = providers[providerID]
31
+ const provider = providers[providerID];
31
32
  for (const [modelID, model] of Object.entries(provider.info.models)) {
32
- process.stdout.write(`${providerID}/${modelID}`)
33
- process.stdout.write(EOL)
33
+ process.stdout.write(`${providerID}/${modelID}`);
34
+ process.stdout.write(EOL);
34
35
  if (verbose) {
35
- process.stdout.write(JSON.stringify(model, null, 2))
36
- process.stdout.write(EOL)
36
+ process.stdout.write(JSON.stringify(model, null, 2));
37
+ process.stdout.write(EOL);
37
38
  }
38
39
  }
39
40
  }
40
41
 
41
42
  if (args.provider) {
42
- const provider = providers[args.provider]
43
+ const provider = providers[args.provider];
43
44
  if (!provider) {
44
- UI.error(`Provider not found: ${args.provider}`)
45
- return
45
+ UI.error(`Provider not found: ${args.provider}`);
46
+ return;
46
47
  }
47
48
 
48
- printModels(args.provider, args.verbose)
49
- return
49
+ printModels(args.provider, args.verbose);
50
+ return;
50
51
  }
51
52
 
52
53
  for (const providerID of Object.keys(providers)) {
53
- printModels(providerID, args.verbose)
54
+ printModels(providerID, args.verbose);
54
55
  }
55
56
  },
56
- })
57
+ });
57
58
  },
58
- })
59
+ });