@link-assistant/agent 0.0.8

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 (133) hide show
  1. package/EXAMPLES.md +383 -0
  2. package/LICENSE +24 -0
  3. package/MODELS.md +95 -0
  4. package/README.md +388 -0
  5. package/TOOLS.md +134 -0
  6. package/package.json +89 -0
  7. package/src/agent/agent.ts +150 -0
  8. package/src/agent/generate.txt +75 -0
  9. package/src/auth/index.ts +64 -0
  10. package/src/bun/index.ts +96 -0
  11. package/src/bus/global.ts +10 -0
  12. package/src/bus/index.ts +119 -0
  13. package/src/cli/bootstrap.js +41 -0
  14. package/src/cli/bootstrap.ts +17 -0
  15. package/src/cli/cmd/agent.ts +165 -0
  16. package/src/cli/cmd/cmd.ts +5 -0
  17. package/src/cli/cmd/export.ts +88 -0
  18. package/src/cli/cmd/mcp.ts +80 -0
  19. package/src/cli/cmd/models.ts +58 -0
  20. package/src/cli/cmd/run.ts +359 -0
  21. package/src/cli/cmd/stats.ts +276 -0
  22. package/src/cli/error.ts +27 -0
  23. package/src/command/index.ts +73 -0
  24. package/src/command/template/initialize.txt +10 -0
  25. package/src/config/config.ts +705 -0
  26. package/src/config/markdown.ts +41 -0
  27. package/src/file/ripgrep.ts +391 -0
  28. package/src/file/time.ts +38 -0
  29. package/src/file/watcher.ts +75 -0
  30. package/src/file.ts +6 -0
  31. package/src/flag/flag.ts +19 -0
  32. package/src/format/formatter.ts +248 -0
  33. package/src/format/index.ts +137 -0
  34. package/src/global/index.ts +52 -0
  35. package/src/id/id.ts +72 -0
  36. package/src/index.js +371 -0
  37. package/src/mcp/index.ts +289 -0
  38. package/src/patch/index.ts +622 -0
  39. package/src/project/bootstrap.ts +22 -0
  40. package/src/project/instance.ts +67 -0
  41. package/src/project/project.ts +105 -0
  42. package/src/project/state.ts +65 -0
  43. package/src/provider/models-macro.ts +11 -0
  44. package/src/provider/models.ts +98 -0
  45. package/src/provider/opencode.js +47 -0
  46. package/src/provider/provider.ts +636 -0
  47. package/src/provider/transform.ts +241 -0
  48. package/src/server/project.ts +48 -0
  49. package/src/server/server.ts +249 -0
  50. package/src/session/agent.js +204 -0
  51. package/src/session/compaction.ts +249 -0
  52. package/src/session/index.ts +380 -0
  53. package/src/session/message-v2.ts +758 -0
  54. package/src/session/message.ts +189 -0
  55. package/src/session/processor.ts +356 -0
  56. package/src/session/prompt/anthropic-20250930.txt +166 -0
  57. package/src/session/prompt/anthropic.txt +105 -0
  58. package/src/session/prompt/anthropic_spoof.txt +1 -0
  59. package/src/session/prompt/beast.txt +147 -0
  60. package/src/session/prompt/build-switch.txt +5 -0
  61. package/src/session/prompt/codex.txt +318 -0
  62. package/src/session/prompt/copilot-gpt-5.txt +143 -0
  63. package/src/session/prompt/gemini.txt +155 -0
  64. package/src/session/prompt/grok-code.txt +1 -0
  65. package/src/session/prompt/plan.txt +8 -0
  66. package/src/session/prompt/polaris.txt +107 -0
  67. package/src/session/prompt/qwen.txt +109 -0
  68. package/src/session/prompt/summarize-turn.txt +5 -0
  69. package/src/session/prompt/summarize.txt +10 -0
  70. package/src/session/prompt/title.txt +25 -0
  71. package/src/session/prompt.ts +1390 -0
  72. package/src/session/retry.ts +53 -0
  73. package/src/session/revert.ts +108 -0
  74. package/src/session/status.ts +75 -0
  75. package/src/session/summary.ts +179 -0
  76. package/src/session/system.ts +138 -0
  77. package/src/session/todo.ts +36 -0
  78. package/src/snapshot/index.ts +197 -0
  79. package/src/storage/storage.ts +226 -0
  80. package/src/tool/bash.ts +193 -0
  81. package/src/tool/bash.txt +121 -0
  82. package/src/tool/batch.ts +173 -0
  83. package/src/tool/batch.txt +28 -0
  84. package/src/tool/codesearch.ts +123 -0
  85. package/src/tool/codesearch.txt +12 -0
  86. package/src/tool/edit.ts +604 -0
  87. package/src/tool/edit.txt +10 -0
  88. package/src/tool/glob.ts +65 -0
  89. package/src/tool/glob.txt +6 -0
  90. package/src/tool/grep.ts +116 -0
  91. package/src/tool/grep.txt +8 -0
  92. package/src/tool/invalid.ts +17 -0
  93. package/src/tool/ls.ts +110 -0
  94. package/src/tool/ls.txt +1 -0
  95. package/src/tool/multiedit.ts +46 -0
  96. package/src/tool/multiedit.txt +41 -0
  97. package/src/tool/patch.ts +188 -0
  98. package/src/tool/patch.txt +1 -0
  99. package/src/tool/read.ts +201 -0
  100. package/src/tool/read.txt +12 -0
  101. package/src/tool/registry.ts +87 -0
  102. package/src/tool/task.ts +126 -0
  103. package/src/tool/task.txt +60 -0
  104. package/src/tool/todo.ts +39 -0
  105. package/src/tool/todoread.txt +14 -0
  106. package/src/tool/todowrite.txt +167 -0
  107. package/src/tool/tool.ts +66 -0
  108. package/src/tool/webfetch.ts +171 -0
  109. package/src/tool/webfetch.txt +14 -0
  110. package/src/tool/websearch.ts +133 -0
  111. package/src/tool/websearch.txt +11 -0
  112. package/src/tool/write.ts +33 -0
  113. package/src/tool/write.txt +8 -0
  114. package/src/util/binary.ts +41 -0
  115. package/src/util/context.ts +25 -0
  116. package/src/util/defer.ts +12 -0
  117. package/src/util/error.ts +54 -0
  118. package/src/util/eventloop.ts +20 -0
  119. package/src/util/filesystem.ts +69 -0
  120. package/src/util/fn.ts +11 -0
  121. package/src/util/iife.ts +3 -0
  122. package/src/util/keybind.ts +79 -0
  123. package/src/util/lazy.ts +11 -0
  124. package/src/util/locale.ts +39 -0
  125. package/src/util/lock.ts +98 -0
  126. package/src/util/log.ts +177 -0
  127. package/src/util/queue.ts +19 -0
  128. package/src/util/rpc.ts +42 -0
  129. package/src/util/scrap.ts +10 -0
  130. package/src/util/signal.ts +12 -0
  131. package/src/util/timeout.ts +14 -0
  132. package/src/util/token.ts +7 -0
  133. package/src/util/wildcard.ts +54 -0
@@ -0,0 +1,165 @@
1
+ import { cmd } from "./cmd"
2
+ import * as prompts from "@clack/prompts"
3
+ import { UI } from "../ui"
4
+ import { Global } from "../../global"
5
+ import { Agent } from "../../agent/agent"
6
+ import path from "path"
7
+ import matter from "gray-matter"
8
+ import { Instance } from "../../project/instance"
9
+ import { EOL } from "os"
10
+
11
+ const AgentCreateCommand = cmd({
12
+ command: "create",
13
+ describe: "create a new agent",
14
+ async handler() {
15
+ await Instance.provide({
16
+ directory: process.cwd(),
17
+ async fn() {
18
+ UI.empty()
19
+ prompts.intro("Create agent")
20
+ const project = Instance.project
21
+
22
+ let scope: "global" | "project" = "global"
23
+ if (project.vcs === "git") {
24
+ const scopeResult = await prompts.select({
25
+ message: "Location",
26
+ options: [
27
+ {
28
+ label: "Current project",
29
+ value: "project" as const,
30
+ hint: Instance.worktree,
31
+ },
32
+ {
33
+ label: "Global",
34
+ value: "global" as const,
35
+ hint: Global.Path.config,
36
+ },
37
+ ],
38
+ })
39
+ if (prompts.isCancel(scopeResult)) throw new UI.CancelledError()
40
+ scope = scopeResult
41
+ }
42
+
43
+ const query = await prompts.text({
44
+ message: "Description",
45
+ placeholder: "What should this agent do?",
46
+ validate: (x) => (x && x.length > 0 ? undefined : "Required"),
47
+ })
48
+ if (prompts.isCancel(query)) throw new UI.CancelledError()
49
+
50
+ const spinner = prompts.spinner()
51
+
52
+ spinner.start("Generating agent configuration...")
53
+ const generated = await Agent.generate({ description: query }).catch((error) => {
54
+ spinner.stop(`LLM failed to generate agent: ${error.message}`, 1)
55
+ throw new UI.CancelledError()
56
+ })
57
+ spinner.stop(`Agent ${generated.identifier} generated`)
58
+
59
+ const availableTools = [
60
+ "bash",
61
+ "read",
62
+ "write",
63
+ "edit",
64
+ "list",
65
+ "glob",
66
+ "grep",
67
+ "webfetch",
68
+ "task",
69
+ "todowrite",
70
+ "todoread",
71
+ ]
72
+
73
+ const selectedTools = await prompts.multiselect({
74
+ message: "Select tools to enable",
75
+ options: availableTools.map((tool) => ({
76
+ label: tool,
77
+ value: tool,
78
+ })),
79
+ initialValues: availableTools,
80
+ })
81
+ if (prompts.isCancel(selectedTools)) throw new UI.CancelledError()
82
+
83
+ const modeResult = await prompts.select({
84
+ message: "Agent mode",
85
+ options: [
86
+ {
87
+ label: "All",
88
+ value: "all" as const,
89
+ hint: "Can function in both primary and subagent roles",
90
+ },
91
+ {
92
+ label: "Primary",
93
+ value: "primary" as const,
94
+ hint: "Acts as a primary/main agent",
95
+ },
96
+ {
97
+ label: "Subagent",
98
+ value: "subagent" as const,
99
+ hint: "Can be used as a subagent by other agents",
100
+ },
101
+ ],
102
+ initialValue: "all",
103
+ })
104
+ if (prompts.isCancel(modeResult)) throw new UI.CancelledError()
105
+
106
+ const tools: Record<string, boolean> = {}
107
+ for (const tool of availableTools) {
108
+ if (!selectedTools.includes(tool)) {
109
+ tools[tool] = false
110
+ }
111
+ }
112
+
113
+ const frontmatter: any = {
114
+ description: generated.whenToUse,
115
+ mode: modeResult,
116
+ }
117
+ if (Object.keys(tools).length > 0) {
118
+ frontmatter.tools = tools
119
+ }
120
+
121
+ const content = matter.stringify(generated.systemPrompt, frontmatter)
122
+ const filePath = path.join(
123
+ scope === "global" ? Global.Path.config : path.join(Instance.worktree, ".opencode"),
124
+ `agent`,
125
+ `${generated.identifier}.md`,
126
+ )
127
+
128
+ await Bun.write(filePath, content)
129
+
130
+ prompts.log.success(`Agent created: ${filePath}`)
131
+ prompts.outro("Done")
132
+ },
133
+ })
134
+ },
135
+ })
136
+
137
+ const AgentListCommand = cmd({
138
+ command: "list",
139
+ describe: "list all available agents",
140
+ async handler() {
141
+ await Instance.provide({
142
+ directory: process.cwd(),
143
+ async fn() {
144
+ const agents = await Agent.list()
145
+ const sortedAgents = agents.sort((a, b) => {
146
+ if (a.builtIn !== b.builtIn) {
147
+ return a.builtIn ? -1 : 1
148
+ }
149
+ return a.name.localeCompare(b.name)
150
+ })
151
+
152
+ for (const agent of sortedAgents) {
153
+ process.stdout.write(`${agent.name} (${agent.mode})${EOL}`)
154
+ }
155
+ },
156
+ })
157
+ },
158
+ })
159
+
160
+ export const AgentCommand = cmd({
161
+ command: "agent",
162
+ describe: "manage agents",
163
+ builder: (yargs) => yargs.command(AgentCreateCommand).command(AgentListCommand).demandCommand(),
164
+ async handler() {},
165
+ })
@@ -0,0 +1,5 @@
1
+ import type { CommandModule } from "yargs"
2
+
3
+ export function cmd<T, U>(input: CommandModule<T, U>) {
4
+ return input
5
+ }
@@ -0,0 +1,88 @@
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
+
9
+ export const ExportCommand = cmd({
10
+ command: "export [sessionID]",
11
+ describe: "export session data as JSON",
12
+ builder: (yargs: Argv) => {
13
+ return yargs.positional("sessionID", {
14
+ describe: "session id to export",
15
+ type: "string",
16
+ })
17
+ },
18
+ handler: async (args) => {
19
+ await bootstrap(process.cwd(), async () => {
20
+ let sessionID = args.sessionID
21
+ process.stderr.write(`Exporting session: ${sessionID ?? "latest"}`)
22
+
23
+ if (!sessionID) {
24
+ UI.empty()
25
+ prompts.intro("Export session", {
26
+ output: process.stderr,
27
+ })
28
+
29
+ const sessions = []
30
+ for await (const session of Session.list()) {
31
+ sessions.push(session)
32
+ }
33
+
34
+ if (sessions.length === 0) {
35
+ prompts.log.error("No sessions found", {
36
+ output: process.stderr,
37
+ })
38
+ prompts.outro("Done", {
39
+ output: process.stderr,
40
+ })
41
+ return
42
+ }
43
+
44
+ sessions.sort((a, b) => b.time.updated - a.time.updated)
45
+
46
+ const selectedSession = await prompts.autocomplete({
47
+ message: "Select session to export",
48
+ maxItems: 10,
49
+ options: sessions.map((session) => ({
50
+ label: session.title,
51
+ value: session.id,
52
+ hint: `${new Date(session.time.updated).toLocaleString()} • ${session.id.slice(-8)}`,
53
+ })),
54
+ output: process.stderr,
55
+ })
56
+
57
+ if (prompts.isCancel(selectedSession)) {
58
+ throw new UI.CancelledError()
59
+ }
60
+
61
+ sessionID = selectedSession as string
62
+
63
+ prompts.outro("Exporting session...", {
64
+ output: process.stderr,
65
+ })
66
+ }
67
+
68
+ try {
69
+ const sessionInfo = await Session.get(sessionID!)
70
+ const messages = await Session.messages({ sessionID: sessionID! })
71
+
72
+ const exportData = {
73
+ info: sessionInfo,
74
+ messages: messages.map((msg) => ({
75
+ info: msg.info,
76
+ parts: msg.parts,
77
+ })),
78
+ }
79
+
80
+ process.stdout.write(JSON.stringify(exportData, null, 2))
81
+ process.stdout.write(EOL)
82
+ } catch (error) {
83
+ UI.error(`Session not found: ${sessionID!}`)
84
+ process.exit(1)
85
+ }
86
+ })
87
+ },
88
+ })
@@ -0,0 +1,80 @@
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"
6
+
7
+ export const McpCommand = cmd({
8
+ command: "mcp",
9
+ builder: (yargs) => yargs.command(McpAddCommand).demandCommand(),
10
+ async handler() {},
11
+ })
12
+
13
+ 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")
19
+
20
+ 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()
25
+
26
+ const type = await prompts.select({
27
+ message: "Select MCP server type",
28
+ options: [
29
+ {
30
+ label: "Local",
31
+ value: "local",
32
+ hint: "Run a local command",
33
+ },
34
+ {
35
+ label: "Remote",
36
+ value: "remote",
37
+ hint: "Connect to a remote URL",
38
+ },
39
+ ],
40
+ })
41
+ if (prompts.isCancel(type)) throw new UI.CancelledError()
42
+
43
+ if (type === "local") {
44
+ 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()
50
+
51
+ prompts.log.info(`Local MCP server "${name}" configured with command: ${command}`)
52
+ prompts.outro("MCP server added successfully")
53
+ return
54
+ }
55
+
56
+ if (type === "remote") {
57
+ const url = await prompts.text({
58
+ message: "Enter MCP server URL",
59
+ placeholder: "e.g., https://example.com/mcp",
60
+ 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"
65
+ },
66
+ })
67
+ if (prompts.isCancel(url)) throw new UI.CancelledError()
68
+
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}`)
76
+ }
77
+
78
+ prompts.outro("MCP server added successfully")
79
+ },
80
+ })
@@ -0,0 +1,58 @@
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
+
8
+ export const ModelsCommand = cmd({
9
+ command: "models [provider]",
10
+ describe: "list all available models",
11
+ builder: (yargs: Argv) => {
12
+ return yargs
13
+ .positional("provider", {
14
+ describe: "provider ID to filter models by",
15
+ type: "string",
16
+ array: false,
17
+ })
18
+ .option("verbose", {
19
+ describe: "use more verbose model output (includes metadata like costs)",
20
+ type: "boolean",
21
+ })
22
+ },
23
+ handler: async (args) => {
24
+ await Instance.provide({
25
+ directory: process.cwd(),
26
+ async fn() {
27
+ const providers = await Provider.list()
28
+
29
+ function printModels(providerID: string, verbose?: boolean) {
30
+ const provider = providers[providerID]
31
+ for (const [modelID, model] of Object.entries(provider.info.models)) {
32
+ process.stdout.write(`${providerID}/${modelID}`)
33
+ process.stdout.write(EOL)
34
+ if (verbose) {
35
+ process.stdout.write(JSON.stringify(model, null, 2))
36
+ process.stdout.write(EOL)
37
+ }
38
+ }
39
+ }
40
+
41
+ if (args.provider) {
42
+ const provider = providers[args.provider]
43
+ if (!provider) {
44
+ UI.error(`Provider not found: ${args.provider}`)
45
+ return
46
+ }
47
+
48
+ printModels(args.provider, args.verbose)
49
+ return
50
+ }
51
+
52
+ for (const providerID of Object.keys(providers)) {
53
+ printModels(providerID, args.verbose)
54
+ }
55
+ },
56
+ })
57
+ },
58
+ })