@link-assistant/agent 0.0.9 → 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.
- package/EXAMPLES.md +36 -0
- package/MODELS.md +72 -24
- package/README.md +59 -2
- package/TOOLS.md +20 -0
- package/package.json +35 -2
- package/src/agent/agent.ts +68 -54
- package/src/auth/claude-oauth.ts +426 -0
- package/src/auth/index.ts +28 -26
- package/src/auth/plugins.ts +876 -0
- package/src/bun/index.ts +53 -43
- package/src/bus/global.ts +5 -5
- package/src/bus/index.ts +59 -53
- package/src/cli/bootstrap.js +12 -12
- package/src/cli/bootstrap.ts +6 -6
- package/src/cli/cmd/agent.ts +97 -92
- package/src/cli/cmd/auth.ts +468 -0
- package/src/cli/cmd/cmd.ts +2 -2
- package/src/cli/cmd/export.ts +41 -41
- package/src/cli/cmd/mcp.ts +144 -119
- package/src/cli/cmd/models.ts +30 -29
- package/src/cli/cmd/run.ts +269 -213
- package/src/cli/cmd/stats.ts +185 -146
- package/src/cli/error.ts +17 -13
- package/src/cli/ui.ts +39 -24
- package/src/command/index.ts +26 -26
- package/src/config/config.ts +528 -288
- package/src/config/markdown.ts +15 -15
- package/src/file/ripgrep.ts +201 -169
- package/src/file/time.ts +21 -18
- package/src/file/watcher.ts +51 -42
- package/src/file.ts +1 -1
- package/src/flag/flag.ts +26 -11
- package/src/format/formatter.ts +206 -162
- package/src/format/index.ts +61 -61
- package/src/global/index.ts +21 -21
- package/src/id/id.ts +47 -33
- package/src/index.js +346 -199
- package/src/json-standard/index.ts +67 -51
- package/src/mcp/index.ts +135 -128
- package/src/patch/index.ts +336 -267
- package/src/project/bootstrap.ts +15 -15
- package/src/project/instance.ts +43 -36
- package/src/project/project.ts +47 -47
- package/src/project/state.ts +37 -33
- package/src/provider/models-macro.ts +5 -5
- package/src/provider/models.ts +32 -32
- package/src/provider/opencode.js +19 -19
- package/src/provider/provider.ts +518 -277
- package/src/provider/transform.ts +143 -102
- package/src/server/project.ts +21 -21
- package/src/server/server.ts +111 -105
- package/src/session/agent.js +66 -60
- package/src/session/compaction.ts +136 -111
- package/src/session/index.ts +189 -156
- package/src/session/message-v2.ts +312 -268
- package/src/session/message.ts +73 -57
- package/src/session/processor.ts +180 -166
- package/src/session/prompt.ts +678 -533
- package/src/session/retry.ts +26 -23
- package/src/session/revert.ts +76 -62
- package/src/session/status.ts +26 -26
- package/src/session/summary.ts +97 -76
- package/src/session/system.ts +77 -63
- package/src/session/todo.ts +22 -16
- package/src/snapshot/index.ts +92 -76
- package/src/storage/storage.ts +157 -120
- package/src/tool/bash.ts +116 -106
- package/src/tool/batch.ts +73 -59
- package/src/tool/codesearch.ts +60 -53
- package/src/tool/edit.ts +319 -263
- package/src/tool/glob.ts +32 -28
- package/src/tool/grep.ts +72 -53
- package/src/tool/invalid.ts +7 -7
- package/src/tool/ls.ts +77 -64
- package/src/tool/multiedit.ts +30 -21
- package/src/tool/patch.ts +121 -94
- package/src/tool/read.ts +140 -122
- package/src/tool/registry.ts +38 -38
- package/src/tool/task.ts +93 -60
- package/src/tool/todo.ts +16 -16
- package/src/tool/tool.ts +45 -36
- package/src/tool/webfetch.ts +97 -74
- package/src/tool/websearch.ts +78 -64
- package/src/tool/write.ts +21 -15
- package/src/util/binary.ts +27 -19
- package/src/util/context.ts +8 -8
- package/src/util/defer.ts +7 -5
- package/src/util/error.ts +24 -19
- package/src/util/eventloop.ts +16 -10
- package/src/util/filesystem.ts +37 -33
- package/src/util/fn.ts +11 -8
- package/src/util/iife.ts +1 -1
- package/src/util/keybind.ts +44 -44
- package/src/util/lazy.ts +7 -7
- package/src/util/locale.ts +20 -16
- package/src/util/lock.ts +43 -38
- package/src/util/log.ts +95 -85
- package/src/util/queue.ts +8 -8
- package/src/util/rpc.ts +35 -23
- package/src/util/scrap.ts +4 -4
- package/src/util/signal.ts +5 -5
- package/src/util/timeout.ts +6 -6
- package/src/util/token.ts +2 -2
- package/src/util/wildcard.ts +38 -27
package/TOOLS.md
CHANGED
|
@@ -7,24 +7,28 @@ This document lists all tools supported by `@link-assistant/agent`. All tools ar
|
|
|
7
7
|
## File Operations
|
|
8
8
|
|
|
9
9
|
### read
|
|
10
|
+
|
|
10
11
|
Reads file contents from the filesystem.
|
|
11
12
|
|
|
12
13
|
**Status:** ✅ Fully supported and tested
|
|
13
14
|
**Test:** [tests/read.tools.test.js](tests/read.tools.test.js)
|
|
14
15
|
|
|
15
16
|
### write
|
|
17
|
+
|
|
16
18
|
Writes content to files in the filesystem.
|
|
17
19
|
|
|
18
20
|
**Status:** ✅ Fully supported and tested
|
|
19
21
|
**Test:** [tests/write.tools.test.js](tests/write.tools.test.js)
|
|
20
22
|
|
|
21
23
|
### edit
|
|
24
|
+
|
|
22
25
|
Performs exact string replacements in files.
|
|
23
26
|
|
|
24
27
|
**Status:** ✅ Fully supported and tested
|
|
25
28
|
**Test:** [tests/edit.tools.test.js](tests/edit.tools.test.js)
|
|
26
29
|
|
|
27
30
|
### list (ls)
|
|
31
|
+
|
|
28
32
|
Lists files and directories.
|
|
29
33
|
|
|
30
34
|
**Status:** ✅ Fully supported and tested
|
|
@@ -33,18 +37,21 @@ Lists files and directories.
|
|
|
33
37
|
## Search Tools
|
|
34
38
|
|
|
35
39
|
### glob
|
|
40
|
+
|
|
36
41
|
Fast file pattern matching tool that works with any codebase size. Supports glob patterns like `**/*.js` or `src/**/*.ts`.
|
|
37
42
|
|
|
38
43
|
**Status:** ✅ Fully supported and tested
|
|
39
44
|
**Test:** [tests/glob.tools.test.js](tests/glob.tools.test.js)
|
|
40
45
|
|
|
41
46
|
### grep
|
|
47
|
+
|
|
42
48
|
Powerful search tool built on ripgrep. Supports full regex syntax and can filter by file type or glob pattern.
|
|
43
49
|
|
|
44
50
|
**Status:** ✅ Fully supported and tested
|
|
45
51
|
**Test:** [tests/grep.tools.test.js](tests/grep.tools.test.js)
|
|
46
52
|
|
|
47
53
|
### websearch
|
|
54
|
+
|
|
48
55
|
Searches the web using Exa API for current information. Always enabled, no environment variables required.
|
|
49
56
|
|
|
50
57
|
**Status:** ✅ Fully supported and tested
|
|
@@ -52,6 +59,7 @@ Searches the web using Exa API for current information. Always enabled, no envir
|
|
|
52
59
|
**OpenCode Compatibility:** ✅ 100% compatible
|
|
53
60
|
|
|
54
61
|
### codesearch
|
|
62
|
+
|
|
55
63
|
Searches code repositories and documentation using Exa API. Always enabled.
|
|
56
64
|
|
|
57
65
|
**Status:** ✅ Fully supported and tested
|
|
@@ -61,18 +69,21 @@ Searches code repositories and documentation using Exa API. Always enabled.
|
|
|
61
69
|
## Execution Tools
|
|
62
70
|
|
|
63
71
|
### bash
|
|
72
|
+
|
|
64
73
|
Executes bash commands in a persistent shell session with optional timeout.
|
|
65
74
|
|
|
66
75
|
**Status:** ✅ Fully supported and tested
|
|
67
76
|
**Test:** [tests/bash.tools.test.js](tests/bash.tools.test.js)
|
|
68
77
|
|
|
69
78
|
### batch
|
|
79
|
+
|
|
70
80
|
Batches multiple tool calls together for optimal performance. Executes multiple tools in a single operation. Always enabled.
|
|
71
81
|
|
|
72
82
|
**Status:** ✅ Fully supported and tested
|
|
73
83
|
**Test:** [tests/batch.tools.test.js](tests/batch.tools.test.js)
|
|
74
84
|
|
|
75
85
|
### task
|
|
86
|
+
|
|
76
87
|
Launches specialized agents to handle complex, multi-step tasks autonomously.
|
|
77
88
|
|
|
78
89
|
**Status:** ✅ Fully supported and tested
|
|
@@ -81,12 +92,14 @@ Launches specialized agents to handle complex, multi-step tasks autonomously.
|
|
|
81
92
|
## Utility Tools
|
|
82
93
|
|
|
83
94
|
### todo (todowrite/todoread)
|
|
95
|
+
|
|
84
96
|
Reads and writes TODO items for task tracking during execution.
|
|
85
97
|
|
|
86
98
|
**Status:** ✅ Fully supported and tested
|
|
87
99
|
**Test:** [tests/todo.tools.test.js](tests/todo.tools.test.js)
|
|
88
100
|
|
|
89
101
|
### webfetch
|
|
102
|
+
|
|
90
103
|
Fetches content from a specified URL and processes it using an AI model.
|
|
91
104
|
|
|
92
105
|
**Status:** ✅ Fully supported and tested
|
|
@@ -95,18 +108,22 @@ Fetches content from a specified URL and processes it using an AI model.
|
|
|
95
108
|
## Testing
|
|
96
109
|
|
|
97
110
|
### Run All Tool Tests
|
|
111
|
+
|
|
98
112
|
```bash
|
|
99
113
|
bun test tests/*.tools.test.js
|
|
100
114
|
```
|
|
101
115
|
|
|
102
116
|
### Run Specific Tool Test
|
|
117
|
+
|
|
103
118
|
```bash
|
|
104
119
|
bun test tests/bash.tools.test.js
|
|
105
120
|
bun test tests/websearch.tools.test.js
|
|
106
121
|
```
|
|
107
122
|
|
|
108
123
|
### Test Coverage
|
|
124
|
+
|
|
109
125
|
Each tool test verifies:
|
|
126
|
+
|
|
110
127
|
- ✅ Correct JSON output structure
|
|
111
128
|
- ✅ OpenCode compatibility (where applicable)
|
|
112
129
|
- ✅ Proper input/output handling
|
|
@@ -115,16 +132,19 @@ Each tool test verifies:
|
|
|
115
132
|
## Key Features
|
|
116
133
|
|
|
117
134
|
### No Configuration Required
|
|
135
|
+
|
|
118
136
|
- All tools work without environment variables or configuration files
|
|
119
137
|
- WebSearch and CodeSearch work without `OPENCODE_EXPERIMENTAL_EXA`
|
|
120
138
|
- Batch tool is always enabled, no experimental flag needed
|
|
121
139
|
|
|
122
140
|
### OpenCode Compatible
|
|
141
|
+
|
|
123
142
|
- All tools produce JSON output compatible with OpenCode's format
|
|
124
143
|
- WebSearch and CodeSearch tools are 100% compatible with OpenCode output
|
|
125
144
|
- Tool event structure matches OpenCode specifications
|
|
126
145
|
|
|
127
146
|
### Plain Text Input Support
|
|
147
|
+
|
|
128
148
|
`@link-assistant/agent` also accepts plain text input (not just JSON):
|
|
129
149
|
|
|
130
150
|
```bash
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@link-assistant/agent",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.11",
|
|
4
4
|
"description": "A minimal, public domain AI CLI agent compatible with OpenCode's JSON interface. Bun-only runtime.",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -8,7 +8,19 @@
|
|
|
8
8
|
"agent": "./src/index.js"
|
|
9
9
|
},
|
|
10
10
|
"scripts": {
|
|
11
|
-
"dev": "bun run src/index.js"
|
|
11
|
+
"dev": "bun run src/index.js",
|
|
12
|
+
"test": "bun test",
|
|
13
|
+
"lint": "eslint .",
|
|
14
|
+
"lint:fix": "eslint . --fix",
|
|
15
|
+
"format": "prettier --write .",
|
|
16
|
+
"format:check": "prettier --check .",
|
|
17
|
+
"check:file-size": "node scripts/check-file-size.mjs",
|
|
18
|
+
"check": "npm run lint && npm run format:check && npm run check:file-size",
|
|
19
|
+
"prepare": "husky || true",
|
|
20
|
+
"changeset": "changeset",
|
|
21
|
+
"changeset:version": "node scripts/changeset-version.mjs",
|
|
22
|
+
"changeset:publish": "changeset publish",
|
|
23
|
+
"changeset:status": "changeset status --since=origin/main"
|
|
12
24
|
},
|
|
13
25
|
"engines": {
|
|
14
26
|
"bun": ">=1.0.0"
|
|
@@ -86,5 +98,26 @@
|
|
|
86
98
|
"yargs": "^18.0.0",
|
|
87
99
|
"zod": "^4.1.12"
|
|
88
100
|
},
|
|
101
|
+
"devDependencies": {
|
|
102
|
+
"@changesets/cli": "^2.29.7",
|
|
103
|
+
"@eslint/js": "^9.18.0",
|
|
104
|
+
"eslint": "^9.38.0",
|
|
105
|
+
"eslint-config-prettier": "^10.1.8",
|
|
106
|
+
"eslint-plugin-prettier": "^5.5.4",
|
|
107
|
+
"husky": "^9.1.7",
|
|
108
|
+
"lint-staged": "^16.2.6",
|
|
109
|
+
"prettier": "^3.6.2"
|
|
110
|
+
},
|
|
111
|
+
"lint-staged": {
|
|
112
|
+
"*.{js,mjs,cjs}": [
|
|
113
|
+
"eslint --fix --max-warnings 0",
|
|
114
|
+
"prettier --write",
|
|
115
|
+
"prettier --check"
|
|
116
|
+
],
|
|
117
|
+
"*.md": [
|
|
118
|
+
"prettier --write",
|
|
119
|
+
"prettier --check"
|
|
120
|
+
]
|
|
121
|
+
},
|
|
89
122
|
"license": "Unlicense"
|
|
90
123
|
}
|
package/src/agent/agent.ts
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
import { Config } from
|
|
2
|
-
import z from
|
|
3
|
-
import { Provider } from
|
|
4
|
-
import { generateObject, type ModelMessage } from
|
|
5
|
-
import PROMPT_GENERATE from
|
|
6
|
-
import { SystemPrompt } from
|
|
7
|
-
import { Instance } from
|
|
8
|
-
import { mergeDeep } from
|
|
1
|
+
import { Config } from '../config/config';
|
|
2
|
+
import z from 'zod';
|
|
3
|
+
import { Provider } from '../provider/provider';
|
|
4
|
+
import { generateObject, type ModelMessage } from 'ai';
|
|
5
|
+
import PROMPT_GENERATE from './generate.txt';
|
|
6
|
+
import { SystemPrompt } from '../session/system';
|
|
7
|
+
import { Instance } from '../project/instance';
|
|
8
|
+
import { mergeDeep } from 'remeda';
|
|
9
9
|
|
|
10
10
|
export namespace Agent {
|
|
11
11
|
export const Info = z
|
|
12
12
|
.object({
|
|
13
13
|
name: z.string(),
|
|
14
14
|
description: z.string().optional(),
|
|
15
|
-
mode: z.enum([
|
|
15
|
+
mode: z.enum(['subagent', 'primary', 'all']),
|
|
16
16
|
builtIn: z.boolean(),
|
|
17
17
|
topP: z.number().optional(),
|
|
18
18
|
temperature: z.number().optional(),
|
|
@@ -28,112 +28,126 @@ export namespace Agent {
|
|
|
28
28
|
options: z.record(z.string(), z.any()),
|
|
29
29
|
})
|
|
30
30
|
.meta({
|
|
31
|
-
ref:
|
|
32
|
-
})
|
|
33
|
-
export type Info = z.infer<typeof Info
|
|
31
|
+
ref: 'Agent',
|
|
32
|
+
});
|
|
33
|
+
export type Info = z.infer<typeof Info>;
|
|
34
34
|
|
|
35
35
|
const state = Instance.state(async () => {
|
|
36
|
-
const cfg = await Config.get()
|
|
37
|
-
const defaultTools = cfg.tools ?? {}
|
|
36
|
+
const cfg = await Config.get();
|
|
37
|
+
const defaultTools = cfg.tools ?? {};
|
|
38
38
|
|
|
39
39
|
const result: Record<string, Info> = {
|
|
40
40
|
general: {
|
|
41
|
-
name:
|
|
41
|
+
name: 'general',
|
|
42
42
|
description:
|
|
43
|
-
|
|
43
|
+
'General-purpose agent for researching complex questions, searching for code, and executing multi-step tasks. When you are searching for a keyword or file and are not confident that you will find the right match in the first few tries use this agent to perform the search for you.',
|
|
44
44
|
tools: {
|
|
45
45
|
todoread: false,
|
|
46
46
|
todowrite: false,
|
|
47
47
|
...defaultTools,
|
|
48
48
|
},
|
|
49
49
|
options: {},
|
|
50
|
-
mode:
|
|
50
|
+
mode: 'subagent',
|
|
51
51
|
builtIn: true,
|
|
52
52
|
},
|
|
53
53
|
build: {
|
|
54
|
-
name:
|
|
54
|
+
name: 'build',
|
|
55
55
|
tools: { ...defaultTools },
|
|
56
56
|
options: {},
|
|
57
|
-
mode:
|
|
57
|
+
mode: 'primary',
|
|
58
58
|
builtIn: true,
|
|
59
59
|
},
|
|
60
60
|
plan: {
|
|
61
|
-
name:
|
|
61
|
+
name: 'plan',
|
|
62
62
|
options: {},
|
|
63
63
|
tools: {
|
|
64
64
|
...defaultTools,
|
|
65
65
|
},
|
|
66
|
-
mode:
|
|
66
|
+
mode: 'primary',
|
|
67
67
|
builtIn: true,
|
|
68
68
|
},
|
|
69
|
-
}
|
|
69
|
+
};
|
|
70
70
|
for (const [key, value] of Object.entries(cfg.agent ?? {})) {
|
|
71
71
|
if (value.disable) {
|
|
72
|
-
delete result[key]
|
|
73
|
-
continue
|
|
72
|
+
delete result[key];
|
|
73
|
+
continue;
|
|
74
74
|
}
|
|
75
|
-
let item = result[key]
|
|
75
|
+
let item = result[key];
|
|
76
76
|
if (!item)
|
|
77
77
|
item = result[key] = {
|
|
78
78
|
name: key,
|
|
79
|
-
mode:
|
|
79
|
+
mode: 'all',
|
|
80
80
|
options: {},
|
|
81
81
|
tools: {},
|
|
82
82
|
builtIn: false,
|
|
83
|
-
}
|
|
84
|
-
const {
|
|
83
|
+
};
|
|
84
|
+
const {
|
|
85
|
+
name,
|
|
86
|
+
model,
|
|
87
|
+
prompt,
|
|
88
|
+
tools,
|
|
89
|
+
description,
|
|
90
|
+
temperature,
|
|
91
|
+
top_p,
|
|
92
|
+
mode,
|
|
93
|
+
color,
|
|
94
|
+
...extra
|
|
95
|
+
} = value;
|
|
85
96
|
item.options = {
|
|
86
97
|
...item.options,
|
|
87
98
|
...extra,
|
|
88
|
-
}
|
|
89
|
-
if (model) item.model = Provider.parseModel(model)
|
|
90
|
-
if (prompt) item.prompt = prompt
|
|
99
|
+
};
|
|
100
|
+
if (model) item.model = Provider.parseModel(model);
|
|
101
|
+
if (prompt) item.prompt = prompt;
|
|
91
102
|
if (tools)
|
|
92
103
|
item.tools = {
|
|
93
104
|
...item.tools,
|
|
94
105
|
...tools,
|
|
95
|
-
}
|
|
106
|
+
};
|
|
96
107
|
item.tools = {
|
|
97
108
|
...defaultTools,
|
|
98
109
|
...item.tools,
|
|
99
|
-
}
|
|
100
|
-
if (description) item.description = description
|
|
101
|
-
if (temperature != undefined) item.temperature = temperature
|
|
102
|
-
if (top_p != undefined) item.topP = top_p
|
|
103
|
-
if (mode) item.mode = mode
|
|
104
|
-
if (color) item.color = color
|
|
110
|
+
};
|
|
111
|
+
if (description) item.description = description;
|
|
112
|
+
if (temperature != undefined) item.temperature = temperature;
|
|
113
|
+
if (top_p != undefined) item.topP = top_p;
|
|
114
|
+
if (mode) item.mode = mode;
|
|
115
|
+
if (color) item.color = color;
|
|
105
116
|
// just here for consistency & to prevent it from being added as an option
|
|
106
|
-
if (name) item.name = name
|
|
117
|
+
if (name) item.name = name;
|
|
107
118
|
}
|
|
108
|
-
return result
|
|
109
|
-
})
|
|
119
|
+
return result;
|
|
120
|
+
});
|
|
110
121
|
|
|
111
122
|
export async function get(agent: string) {
|
|
112
|
-
return state().then((x) => x[agent])
|
|
123
|
+
return state().then((x) => x[agent]);
|
|
113
124
|
}
|
|
114
125
|
|
|
115
126
|
export async function list() {
|
|
116
|
-
return state().then((x) => Object.values(x))
|
|
127
|
+
return state().then((x) => Object.values(x));
|
|
117
128
|
}
|
|
118
129
|
|
|
119
130
|
export async function generate(input: { description: string }) {
|
|
120
|
-
const defaultModel = await Provider.defaultModel()
|
|
121
|
-
const model = await Provider.getModel(
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
131
|
+
const defaultModel = await Provider.defaultModel();
|
|
132
|
+
const model = await Provider.getModel(
|
|
133
|
+
defaultModel.providerID,
|
|
134
|
+
defaultModel.modelID
|
|
135
|
+
);
|
|
136
|
+
const system = SystemPrompt.header(defaultModel.providerID);
|
|
137
|
+
system.push(PROMPT_GENERATE);
|
|
138
|
+
const existing = await list();
|
|
125
139
|
const result = await generateObject({
|
|
126
140
|
temperature: 0.3,
|
|
127
141
|
prompt: [
|
|
128
142
|
...system.map(
|
|
129
143
|
(item): ModelMessage => ({
|
|
130
|
-
role:
|
|
144
|
+
role: 'system',
|
|
131
145
|
content: item,
|
|
132
|
-
})
|
|
146
|
+
})
|
|
133
147
|
),
|
|
134
148
|
{
|
|
135
|
-
role:
|
|
136
|
-
content: `Create an agent configuration based on this request: \"${input.description}\".\n\nIMPORTANT: The following identifiers already exist and must NOT be used: ${existing.map((i) => i.name).join(
|
|
149
|
+
role: 'user',
|
|
150
|
+
content: `Create an agent configuration based on this request: \"${input.description}\".\n\nIMPORTANT: The following identifiers already exist and must NOT be used: ${existing.map((i) => i.name).join(', ')}\n Return ONLY the JSON object, no other text, do not wrap in backticks`,
|
|
137
151
|
},
|
|
138
152
|
],
|
|
139
153
|
model: model.language,
|
|
@@ -142,8 +156,8 @@ export namespace Agent {
|
|
|
142
156
|
whenToUse: z.string(),
|
|
143
157
|
systemPrompt: z.string(),
|
|
144
158
|
}),
|
|
145
|
-
})
|
|
146
|
-
return result.object
|
|
159
|
+
});
|
|
160
|
+
return result.object;
|
|
147
161
|
}
|
|
148
162
|
}
|
|
149
163
|
|