@link-assistant/agent 0.0.8 → 0.0.11

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 +80 -1
  2. package/MODELS.md +72 -24
  3. package/README.md +95 -2
  4. package/TOOLS.md +20 -0
  5. package/package.json +36 -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 +468 -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 +210 -53
  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 +78 -0
  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 +554 -332
  38. package/src/json-standard/index.ts +173 -0
  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,80 +1,237 @@
1
- import { cmd } from "./cmd"
2
- import { Client } from "@modelcontextprotocol/sdk/client/index.js"
3
- import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js"
4
- import * as prompts from "@clack/prompts"
5
- import { UI } from "../ui"
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';
6
11
 
7
12
  export const McpCommand = cmd({
8
- command: "mcp",
9
- builder: (yargs) => yargs.command(McpAddCommand).demandCommand(),
13
+ command: 'mcp',
14
+ builder: (yargs) =>
15
+ yargs.command(McpAddCommand).command(McpListCommand).demandCommand(),
10
16
  async handler() {},
11
- })
17
+ });
18
+
19
+ async function loadGlobalConfig(): Promise<Config.Info> {
20
+ const configPath = path.join(Global.Path.config, 'opencode.json');
21
+ try {
22
+ const content = await Bun.file(configPath).text();
23
+ return JSON.parse(content);
24
+ } catch {
25
+ return {
26
+ $schema: 'https://opencode.ai/config.json',
27
+ };
28
+ }
29
+ }
30
+
31
+ async function saveGlobalConfig(config: Config.Info): Promise<void> {
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));
35
+ }
12
36
 
13
37
  export const McpAddCommand = cmd({
14
- command: "add",
15
- describe: "add an MCP server",
16
- async handler() {
17
- UI.empty()
18
- prompts.intro("Add MCP server")
38
+ command: 'add [name] [command..]',
39
+ describe: 'add an MCP server',
40
+ builder: (yargs: Argv) => {
41
+ return yargs
42
+ .positional('name', {
43
+ describe: 'name of the MCP server',
44
+ type: 'string',
45
+ })
46
+ .positional('command', {
47
+ describe:
48
+ 'command and arguments to run the MCP server (e.g., npx @playwright/mcp@latest)',
49
+ type: 'string',
50
+ array: true,
51
+ })
52
+ .option('url', {
53
+ describe: 'URL for remote MCP server',
54
+ type: 'string',
55
+ })
56
+ .option('enabled', {
57
+ describe: 'enable the MCP server',
58
+ type: 'boolean',
59
+ default: true,
60
+ });
61
+ },
62
+ async handler(args) {
63
+ // If name and command are provided as CLI arguments, use non-interactive mode
64
+ if (args.name && ((args.command && args.command.length > 0) || args.url)) {
65
+ // Non-interactive mode: CLI arguments provided
66
+ const config = await loadGlobalConfig();
67
+ config.mcp = config.mcp || {};
68
+
69
+ if (args.url) {
70
+ // Remote MCP server
71
+ config.mcp[args.name] = {
72
+ type: 'remote',
73
+ url: args.url,
74
+ enabled: args.enabled,
75
+ };
76
+ UI.success(
77
+ `Remote MCP server "${args.name}" added with URL: ${args.url}`
78
+ );
79
+ } else if (args.command && args.command.length > 0) {
80
+ // Local MCP server
81
+ config.mcp[args.name] = {
82
+ type: 'local',
83
+ command: args.command,
84
+ enabled: args.enabled,
85
+ };
86
+ UI.success(
87
+ `Local MCP server "${args.name}" added with command: ${args.command.join(' ')}`
88
+ );
89
+ }
90
+
91
+ await saveGlobalConfig(config);
92
+ const configPath = path.join(Global.Path.config, 'opencode.json');
93
+ UI.info(`Configuration saved to: ${configPath}`);
94
+ return;
95
+ }
96
+
97
+ // Interactive mode: prompt for input
98
+ UI.empty();
99
+ prompts.intro('Add MCP server');
19
100
 
20
101
  const name = await prompts.text({
21
- message: "Enter MCP server name",
22
- validate: (x) => (x && x.length > 0 ? undefined : "Required"),
23
- })
24
- 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();
25
106
 
26
107
  const type = await prompts.select({
27
- message: "Select MCP server type",
108
+ message: 'Select MCP server type',
28
109
  options: [
29
110
  {
30
- label: "Local",
31
- value: "local",
32
- hint: "Run a local command",
111
+ label: 'Local',
112
+ value: 'local',
113
+ hint: 'Run a local command',
33
114
  },
34
115
  {
35
- label: "Remote",
36
- value: "remote",
37
- hint: "Connect to a remote URL",
116
+ label: 'Remote',
117
+ value: 'remote',
118
+ hint: 'Connect to a remote URL',
38
119
  },
39
120
  ],
40
- })
41
- if (prompts.isCancel(type)) throw new UI.CancelledError()
121
+ });
122
+ if (prompts.isCancel(type)) throw new UI.CancelledError();
42
123
 
43
- if (type === "local") {
124
+ const config = await loadGlobalConfig();
125
+ config.mcp = config.mcp || {};
126
+
127
+ if (type === 'local') {
44
128
  const command = await prompts.text({
45
- message: "Enter command to run",
46
- placeholder: "e.g., opencode x @modelcontextprotocol/server-filesystem",
47
- validate: (x) => (x && x.length > 0 ? undefined : "Required"),
48
- })
49
- 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();
134
+
135
+ // Parse command into array
136
+ const commandParts = command.split(/\s+/);
137
+ config.mcp[name] = {
138
+ type: 'local',
139
+ command: commandParts,
140
+ enabled: true,
141
+ };
50
142
 
51
- prompts.log.info(`Local MCP server "${name}" configured with command: ${command}`)
52
- prompts.outro("MCP server added successfully")
53
- return
143
+ await saveGlobalConfig(config);
144
+ prompts.log.info(
145
+ `Local MCP server "${name}" configured with command: ${command}`
146
+ );
54
147
  }
55
148
 
56
- if (type === "remote") {
149
+ if (type === 'remote') {
57
150
  const url = await prompts.text({
58
- message: "Enter MCP server URL",
59
- placeholder: "e.g., https://example.com/mcp",
151
+ message: 'Enter MCP server URL',
152
+ placeholder: 'e.g., https://example.com/mcp',
60
153
  validate: (x) => {
61
- if (!x) return "Required"
62
- if (x.length === 0) return "Required"
63
- const isValid = URL.canParse(x)
64
- 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';
65
158
  },
66
- })
67
- if (prompts.isCancel(url)) throw new UI.CancelledError()
159
+ });
160
+ if (prompts.isCancel(url)) throw new UI.CancelledError();
68
161
 
69
- const client = new Client({
70
- name: "opencode",
71
- version: "1.0.0",
72
- })
73
- const transport = new StreamableHTTPClientTransport(new URL(url))
74
- await client.connect(transport)
75
- prompts.log.info(`Remote MCP server "${name}" configured with URL: ${url}`)
162
+ // Test connection
163
+ try {
164
+ const client = new Client({
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();
171
+ } catch (error) {
172
+ prompts.log.warn(
173
+ `Could not verify connection to ${url}, but saving configuration anyway`
174
+ );
175
+ }
176
+
177
+ config.mcp[name] = {
178
+ type: 'remote',
179
+ url: url,
180
+ enabled: true,
181
+ };
182
+
183
+ await saveGlobalConfig(config);
184
+ prompts.log.info(
185
+ `Remote MCP server "${name}" configured with URL: ${url}`
186
+ );
76
187
  }
77
188
 
78
- 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');
192
+ },
193
+ });
194
+
195
+ export const McpListCommand = cmd({
196
+ command: 'list',
197
+ describe: 'list configured MCP servers',
198
+ async handler() {
199
+ const config = await loadGlobalConfig();
200
+ const mcpServers = config.mcp || {};
201
+
202
+ if (Object.keys(mcpServers).length === 0) {
203
+ UI.info('No MCP servers configured');
204
+ return;
205
+ }
206
+
207
+ UI.println(
208
+ UI.Style.TEXT_BOLD + 'Configured MCP servers:' + UI.Style.TEXT_NORMAL
209
+ );
210
+ UI.empty();
211
+
212
+ for (const [name, server] of Object.entries(mcpServers)) {
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}`);
233
+ }
234
+ UI.empty();
235
+ }
79
236
  },
80
- })
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
+ });