@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
package/src/util/error.ts CHANGED
@@ -1,10 +1,13 @@
1
- import z from "zod"
1
+ import z from 'zod';
2
2
 
3
3
  export abstract class NamedError extends Error {
4
- abstract schema(): z.core.$ZodType
5
- abstract toObject(): { name: string; data: any }
4
+ abstract schema(): z.core.$ZodType;
5
+ abstract toObject(): { name: string; data: any };
6
6
 
7
- static create<Name extends string, Data extends z.core.$ZodType>(name: Name, data: Data) {
7
+ static create<Name extends string, Data extends z.core.$ZodType>(
8
+ name: Name,
9
+ data: Data
10
+ ) {
8
11
  const schema = z
9
12
  .object({
10
13
  name: z.literal(name),
@@ -12,43 +15,45 @@ export abstract class NamedError extends Error {
12
15
  })
13
16
  .meta({
14
17
  ref: name,
15
- })
18
+ });
16
19
  const result = class extends NamedError {
17
- public static readonly Schema = schema
20
+ public static readonly Schema = schema;
18
21
 
19
- public override readonly name = name as Name
22
+ public override readonly name = name as Name;
20
23
 
21
24
  constructor(
22
25
  public readonly data: z.input<Data>,
23
- options?: ErrorOptions,
26
+ options?: ErrorOptions
24
27
  ) {
25
- super(name, options)
26
- this.name = name
28
+ super(name, options);
29
+ this.name = name;
27
30
  }
28
31
 
29
32
  static isInstance(input: any): input is InstanceType<typeof result> {
30
- return typeof input === "object" && "name" in input && input.name === name
33
+ return (
34
+ typeof input === 'object' && 'name' in input && input.name === name
35
+ );
31
36
  }
32
37
 
33
38
  schema() {
34
- return schema
39
+ return schema;
35
40
  }
36
41
 
37
42
  toObject() {
38
43
  return {
39
44
  name: name,
40
45
  data: this.data,
41
- }
46
+ };
42
47
  }
43
- }
44
- Object.defineProperty(result, "name", { value: name })
45
- return result
48
+ };
49
+ Object.defineProperty(result, 'name', { value: name });
50
+ return result;
46
51
  }
47
52
 
48
53
  public static readonly Unknown = NamedError.create(
49
- "UnknownError",
54
+ 'UnknownError',
50
55
  z.object({
51
56
  message: z.string(),
52
- }),
53
- )
57
+ })
58
+ );
54
59
  }
@@ -1,20 +1,26 @@
1
- import { Log } from "./log"
1
+ import { Log } from './log';
2
2
 
3
3
  export namespace EventLoop {
4
4
  export async function wait() {
5
5
  return new Promise<void>((resolve) => {
6
6
  const check = () => {
7
- const active = [...(process as any)._getActiveHandles(), ...(process as any)._getActiveRequests()]
8
- Log.Default.info("eventloop", {
7
+ const active = [
8
+ ...(process as any)._getActiveHandles(),
9
+ ...(process as any)._getActiveRequests(),
10
+ ];
11
+ Log.Default.info('eventloop', {
9
12
  active,
10
- })
11
- if ((process as any)._getActiveHandles().length === 0 && (process as any)._getActiveRequests().length === 0) {
12
- resolve()
13
+ });
14
+ if (
15
+ (process as any)._getActiveHandles().length === 0 &&
16
+ (process as any)._getActiveRequests().length === 0
17
+ ) {
18
+ resolve();
13
19
  } else {
14
- setImmediate(check)
20
+ setImmediate(check);
15
21
  }
16
- }
17
- check()
18
- })
22
+ };
23
+ check();
24
+ });
19
25
  }
20
26
  }
@@ -1,52 +1,56 @@
1
- import { exists } from "fs/promises"
2
- import { dirname, join, relative } from "path"
1
+ import { exists } from 'fs/promises';
2
+ import { dirname, join, relative } from 'path';
3
3
 
4
4
  export namespace Filesystem {
5
5
  export function overlaps(a: string, b: string) {
6
- const relA = relative(a, b)
7
- const relB = relative(b, a)
8
- return !relA || !relA.startsWith("..") || !relB || !relB.startsWith("..")
6
+ const relA = relative(a, b);
7
+ const relB = relative(b, a);
8
+ return !relA || !relA.startsWith('..') || !relB || !relB.startsWith('..');
9
9
  }
10
10
 
11
11
  export function contains(parent: string, child: string) {
12
- return !relative(parent, child).startsWith("..")
12
+ return !relative(parent, child).startsWith('..');
13
13
  }
14
14
 
15
15
  export async function findUp(target: string, start: string, stop?: string) {
16
- let current = start
17
- const result = []
16
+ let current = start;
17
+ const result = [];
18
18
  while (true) {
19
- const search = join(current, target)
20
- if (await exists(search)) result.push(search)
21
- if (stop === current) break
22
- const parent = dirname(current)
23
- if (parent === current) break
24
- current = parent
19
+ const search = join(current, target);
20
+ if (await exists(search)) result.push(search);
21
+ if (stop === current) break;
22
+ const parent = dirname(current);
23
+ if (parent === current) break;
24
+ current = parent;
25
25
  }
26
- return result
26
+ return result;
27
27
  }
28
28
 
29
- export async function* up(options: { targets: string[]; start: string; stop?: string }) {
30
- const { targets, start, stop } = options
31
- let current = start
29
+ export async function* up(options: {
30
+ targets: string[];
31
+ start: string;
32
+ stop?: string;
33
+ }) {
34
+ const { targets, start, stop } = options;
35
+ let current = start;
32
36
  while (true) {
33
37
  for (const target of targets) {
34
- const search = join(current, target)
35
- if (await exists(search)) yield search
38
+ const search = join(current, target);
39
+ if (await exists(search)) yield search;
36
40
  }
37
- if (stop === current) break
38
- const parent = dirname(current)
39
- if (parent === current) break
40
- current = parent
41
+ if (stop === current) break;
42
+ const parent = dirname(current);
43
+ if (parent === current) break;
44
+ current = parent;
41
45
  }
42
46
  }
43
47
 
44
48
  export async function globUp(pattern: string, start: string, stop?: string) {
45
- let current = start
46
- const result = []
49
+ let current = start;
50
+ const result = [];
47
51
  while (true) {
48
52
  try {
49
- const glob = new Bun.Glob(pattern)
53
+ const glob = new Bun.Glob(pattern);
50
54
  for await (const match of glob.scan({
51
55
  cwd: current,
52
56
  absolute: true,
@@ -54,16 +58,16 @@ export namespace Filesystem {
54
58
  followSymlinks: true,
55
59
  dot: true,
56
60
  })) {
57
- result.push(match)
61
+ result.push(match);
58
62
  }
59
63
  } catch {
60
64
  // Skip invalid glob patterns
61
65
  }
62
- if (stop === current) break
63
- const parent = dirname(current)
64
- if (parent === current) break
65
- current = parent
66
+ if (stop === current) break;
67
+ const parent = dirname(current);
68
+ if (parent === current) break;
69
+ current = parent;
66
70
  }
67
- return result
71
+ return result;
68
72
  }
69
73
  }
package/src/util/fn.ts CHANGED
@@ -1,11 +1,14 @@
1
- import { z } from "zod"
1
+ import { z } from 'zod';
2
2
 
3
- export function fn<T extends z.ZodType, Result>(schema: T, cb: (input: z.infer<T>) => Result) {
3
+ export function fn<T extends z.ZodType, Result>(
4
+ schema: T,
5
+ cb: (input: z.infer<T>) => Result
6
+ ) {
4
7
  const result = (input: z.infer<T>) => {
5
- const parsed = schema.parse(input)
6
- return cb(parsed)
7
- }
8
- result.force = (input: z.infer<T>) => cb(input)
9
- result.schema = schema
10
- return result
8
+ const parsed = schema.parse(input);
9
+ return cb(parsed);
10
+ };
11
+ result.force = (input: z.infer<T>) => cb(input);
12
+ result.schema = schema;
13
+ return result;
11
14
  }
package/src/util/iife.ts CHANGED
@@ -1,3 +1,3 @@
1
1
  export function iife<T>(fn: () => T) {
2
- return fn()
2
+ return fn();
3
3
  }
@@ -1,79 +1,79 @@
1
- import { isDeepEqual } from "remeda"
1
+ import { isDeepEqual } from 'remeda';
2
2
 
3
3
  export namespace Keybind {
4
4
  export type Info = {
5
- ctrl: boolean
6
- meta: boolean
7
- shift: boolean
8
- leader: boolean
9
- name: string
10
- }
5
+ ctrl: boolean;
6
+ meta: boolean;
7
+ shift: boolean;
8
+ leader: boolean;
9
+ name: string;
10
+ };
11
11
 
12
12
  export function match(a: Info, b: Info): boolean {
13
- return isDeepEqual(a, b)
13
+ return isDeepEqual(a, b);
14
14
  }
15
15
 
16
16
  export function toString(info: Info): string {
17
- const parts: string[] = []
17
+ const parts: string[] = [];
18
18
 
19
- if (info.ctrl) parts.push("ctrl")
20
- if (info.meta) parts.push("alt")
21
- if (info.shift) parts.push("shift")
19
+ if (info.ctrl) parts.push('ctrl');
20
+ if (info.meta) parts.push('alt');
21
+ if (info.shift) parts.push('shift');
22
22
  if (info.name) {
23
- if (info.name === "delete") parts.push("del")
24
- else parts.push(info.name)
23
+ if (info.name === 'delete') parts.push('del');
24
+ else parts.push(info.name);
25
25
  }
26
26
 
27
- let result = parts.join("+")
27
+ let result = parts.join('+');
28
28
 
29
29
  if (info.leader) {
30
- result = result ? `<leader> ${result}` : `<leader>`
30
+ result = result ? `<leader> ${result}` : `<leader>`;
31
31
  }
32
32
 
33
- return result
33
+ return result;
34
34
  }
35
35
 
36
36
  export function parse(key: string): Info[] {
37
- if (key === "none") return []
37
+ if (key === 'none') return [];
38
38
 
39
- return key.split(",").map((combo) => {
39
+ return key.split(',').map((combo) => {
40
40
  // Handle <leader> syntax by replacing with leader+
41
- const normalized = combo.replace(/<leader>/g, "leader+")
42
- const parts = normalized.toLowerCase().split("+")
41
+ const normalized = combo.replace(/<leader>/g, 'leader+');
42
+ const parts = normalized.toLowerCase().split('+');
43
43
  const info: Info = {
44
44
  ctrl: false,
45
45
  meta: false,
46
46
  shift: false,
47
47
  leader: false,
48
- name: "",
49
- }
48
+ name: '',
49
+ };
50
50
 
51
51
  for (const part of parts) {
52
52
  switch (part) {
53
- case "ctrl":
54
- info.ctrl = true
55
- break
56
- case "alt":
57
- case "meta":
58
- case "option":
59
- info.meta = true
60
- break
61
- case "shift":
62
- info.shift = true
63
- break
64
- case "leader":
65
- info.leader = true
66
- break
67
- case "esc":
68
- info.name = "escape"
69
- break
53
+ case 'ctrl':
54
+ info.ctrl = true;
55
+ break;
56
+ case 'alt':
57
+ case 'meta':
58
+ case 'option':
59
+ info.meta = true;
60
+ break;
61
+ case 'shift':
62
+ info.shift = true;
63
+ break;
64
+ case 'leader':
65
+ info.leader = true;
66
+ break;
67
+ case 'esc':
68
+ info.name = 'escape';
69
+ break;
70
70
  default:
71
- info.name = part
72
- break
71
+ info.name = part;
72
+ break;
73
73
  }
74
74
  }
75
75
 
76
- return info
77
- })
76
+ return info;
77
+ });
78
78
  }
79
79
  }
package/src/util/lazy.ts CHANGED
@@ -1,11 +1,11 @@
1
1
  export function lazy<T>(fn: () => T) {
2
- let value: T | undefined
3
- let loaded = false
2
+ let value: T | undefined;
3
+ let loaded = false;
4
4
 
5
5
  return (): T => {
6
- if (loaded) return value as T
7
- loaded = true
8
- value = fn()
9
- return value as T
10
- }
6
+ if (loaded) return value as T;
7
+ loaded = true;
8
+ value = fn();
9
+ return value as T;
10
+ };
11
11
  }
@@ -1,39 +1,43 @@
1
1
  export namespace Locale {
2
2
  export function titlecase(str: string) {
3
- return str.replace(/\b\w/g, (c) => c.toUpperCase())
3
+ return str.replace(/\b\w/g, (c) => c.toUpperCase());
4
4
  }
5
5
 
6
6
  export function time(input: number) {
7
- const date = new Date(input)
8
- return date.toLocaleTimeString()
7
+ const date = new Date(input);
8
+ return date.toLocaleTimeString();
9
9
  }
10
10
 
11
11
  export function number(num: number): string {
12
12
  if (num >= 1000000) {
13
- return (num / 1000000).toFixed(1) + "M"
13
+ return (num / 1000000).toFixed(1) + 'M';
14
14
  } else if (num >= 1000) {
15
- return (num / 1000).toFixed(1) + "K"
15
+ return (num / 1000).toFixed(1) + 'K';
16
16
  }
17
- return num.toString()
17
+ return num.toString();
18
18
  }
19
19
 
20
20
  export function truncate(str: string, len: number): string {
21
- if (str.length <= len) return str
22
- return str.slice(0, len - 1) + ""
21
+ if (str.length <= len) return str;
22
+ return str.slice(0, len - 1) + '';
23
23
  }
24
24
 
25
25
  export function truncateMiddle(str: string, maxLength: number = 35): string {
26
- if (str.length <= maxLength) return str
26
+ if (str.length <= maxLength) return str;
27
27
 
28
- const ellipsis = ""
29
- const keepStart = Math.ceil((maxLength - ellipsis.length) / 2)
30
- const keepEnd = Math.floor((maxLength - ellipsis.length) / 2)
28
+ const ellipsis = '';
29
+ const keepStart = Math.ceil((maxLength - ellipsis.length) / 2);
30
+ const keepEnd = Math.floor((maxLength - ellipsis.length) / 2);
31
31
 
32
- return str.slice(0, keepStart) + ellipsis + str.slice(-keepEnd)
32
+ return str.slice(0, keepStart) + ellipsis + str.slice(-keepEnd);
33
33
  }
34
34
 
35
- export function pluralize(count: number, singular: string, plural: string): string {
36
- const template = count === 1 ? singular : plural
37
- return template.replace("{}", count.toString())
35
+ export function pluralize(
36
+ count: number,
37
+ singular: string,
38
+ plural: string
39
+ ): string {
40
+ const template = count === 1 ? singular : plural;
41
+ return template.replace('{}', count.toString());
38
42
  }
39
43
  }
package/src/util/lock.ts CHANGED
@@ -2,12 +2,12 @@ export namespace Lock {
2
2
  const locks = new Map<
3
3
  string,
4
4
  {
5
- readers: number
6
- writer: boolean
7
- waitingReaders: (() => void)[]
8
- waitingWriters: (() => void)[]
5
+ readers: number;
6
+ writer: boolean;
7
+ waitingReaders: (() => void)[];
8
+ waitingWriters: (() => void)[];
9
9
  }
10
- >()
10
+ >();
11
11
 
12
12
  function get(key: string) {
13
13
  if (!locks.has(key)) {
@@ -16,83 +16,88 @@ export namespace Lock {
16
16
  writer: false,
17
17
  waitingReaders: [],
18
18
  waitingWriters: [],
19
- })
19
+ });
20
20
  }
21
- return locks.get(key)!
21
+ return locks.get(key)!;
22
22
  }
23
23
 
24
24
  function process(key: string) {
25
- const lock = locks.get(key)
26
- if (!lock || lock.writer || lock.readers > 0) return
25
+ const lock = locks.get(key);
26
+ if (!lock || lock.writer || lock.readers > 0) return;
27
27
 
28
28
  // Prioritize writers to prevent starvation
29
29
  if (lock.waitingWriters.length > 0) {
30
- const nextWriter = lock.waitingWriters.shift()!
31
- nextWriter()
32
- return
30
+ const nextWriter = lock.waitingWriters.shift()!;
31
+ nextWriter();
32
+ return;
33
33
  }
34
34
 
35
35
  // Wake up all waiting readers
36
36
  while (lock.waitingReaders.length > 0) {
37
- const nextReader = lock.waitingReaders.shift()!
38
- nextReader()
37
+ const nextReader = lock.waitingReaders.shift()!;
38
+ nextReader();
39
39
  }
40
40
 
41
41
  // Clean up empty locks
42
- if (lock.readers === 0 && !lock.writer && lock.waitingReaders.length === 0 && lock.waitingWriters.length === 0) {
43
- locks.delete(key)
42
+ if (
43
+ lock.readers === 0 &&
44
+ !lock.writer &&
45
+ lock.waitingReaders.length === 0 &&
46
+ lock.waitingWriters.length === 0
47
+ ) {
48
+ locks.delete(key);
44
49
  }
45
50
  }
46
51
 
47
52
  export async function read(key: string): Promise<Disposable> {
48
- const lock = get(key)
53
+ const lock = get(key);
49
54
 
50
55
  return new Promise((resolve) => {
51
56
  if (!lock.writer && lock.waitingWriters.length === 0) {
52
- lock.readers++
57
+ lock.readers++;
53
58
  resolve({
54
59
  [Symbol.dispose]: () => {
55
- lock.readers--
56
- process(key)
60
+ lock.readers--;
61
+ process(key);
57
62
  },
58
- })
63
+ });
59
64
  } else {
60
65
  lock.waitingReaders.push(() => {
61
- lock.readers++
66
+ lock.readers++;
62
67
  resolve({
63
68
  [Symbol.dispose]: () => {
64
- lock.readers--
65
- process(key)
69
+ lock.readers--;
70
+ process(key);
66
71
  },
67
- })
68
- })
72
+ });
73
+ });
69
74
  }
70
- })
75
+ });
71
76
  }
72
77
 
73
78
  export async function write(key: string): Promise<Disposable> {
74
- const lock = get(key)
79
+ const lock = get(key);
75
80
 
76
81
  return new Promise((resolve) => {
77
82
  if (!lock.writer && lock.readers === 0) {
78
- lock.writer = true
83
+ lock.writer = true;
79
84
  resolve({
80
85
  [Symbol.dispose]: () => {
81
- lock.writer = false
82
- process(key)
86
+ lock.writer = false;
87
+ process(key);
83
88
  },
84
- })
89
+ });
85
90
  } else {
86
91
  lock.waitingWriters.push(() => {
87
- lock.writer = true
92
+ lock.writer = true;
88
93
  resolve({
89
94
  [Symbol.dispose]: () => {
90
- lock.writer = false
91
- process(key)
95
+ lock.writer = false;
96
+ process(key);
92
97
  },
93
- })
94
- })
98
+ });
99
+ });
95
100
  }
96
- })
101
+ });
97
102
  }
98
103
  }