@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.
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 +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 +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
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.9",
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
  }
@@ -1,18 +1,18 @@
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"
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(["subagent", "primary", "all"]),
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: "Agent",
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: "general",
41
+ name: 'general',
42
42
  description:
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.",
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: "subagent",
50
+ mode: 'subagent',
51
51
  builtIn: true,
52
52
  },
53
53
  build: {
54
- name: "build",
54
+ name: 'build',
55
55
  tools: { ...defaultTools },
56
56
  options: {},
57
- mode: "primary",
57
+ mode: 'primary',
58
58
  builtIn: true,
59
59
  },
60
60
  plan: {
61
- name: "plan",
61
+ name: 'plan',
62
62
  options: {},
63
63
  tools: {
64
64
  ...defaultTools,
65
65
  },
66
- mode: "primary",
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: "all",
79
+ mode: 'all',
80
80
  options: {},
81
81
  tools: {},
82
82
  builtIn: false,
83
- }
84
- const { name, model, prompt, tools, description, temperature, top_p, mode, color, ...extra } = value
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(defaultModel.providerID, defaultModel.modelID)
122
- const system = SystemPrompt.header(defaultModel.providerID)
123
- system.push(PROMPT_GENERATE)
124
- const existing = await list()
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: "system",
144
+ role: 'system',
131
145
  content: item,
132
- }),
146
+ })
133
147
  ),
134
148
  {
135
- role: "user",
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(", ")}\n Return ONLY the JSON object, no other text, do not wrap in backticks`,
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