@llmtune/cli 0.1.0 → 0.1.2

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 (198) hide show
  1. package/README.md +1 -1
  2. package/dist/agent/conversation.d.ts +42 -0
  3. package/dist/agent/conversation.js +105 -0
  4. package/dist/agent/loop.d.ts +19 -0
  5. package/dist/agent/loop.js +185 -0
  6. package/dist/agent/planner.d.ts +8 -0
  7. package/dist/agent/planner.js +43 -0
  8. package/dist/auth/client.d.ts +4 -0
  9. package/dist/auth/client.js +24 -0
  10. package/dist/auth/config.d.ts +21 -0
  11. package/dist/auth/config.js +83 -0
  12. package/dist/commands/chat.d.ts +5 -0
  13. package/dist/commands/chat.js +27 -0
  14. package/dist/commands/config.d.ts +2 -0
  15. package/dist/commands/config.js +37 -0
  16. package/dist/commands/login.d.ts +2 -0
  17. package/dist/commands/login.js +93 -0
  18. package/dist/commands/marketplace.d.ts +6 -0
  19. package/dist/commands/marketplace.js +213 -0
  20. package/dist/commands/models.d.ts +2 -0
  21. package/dist/commands/models.js +53 -0
  22. package/dist/compact/history-store.d.ts +29 -0
  23. package/dist/compact/history-store.js +110 -0
  24. package/dist/compact/microcompact.d.ts +10 -0
  25. package/dist/compact/microcompact.js +43 -0
  26. package/dist/compact/service.d.ts +13 -0
  27. package/dist/compact/service.js +156 -0
  28. package/dist/context/analyzer.d.ts +26 -0
  29. package/dist/context/analyzer.js +99 -0
  30. package/dist/context/builder.d.ts +13 -0
  31. package/dist/context/builder.js +144 -0
  32. package/dist/context/cache.d.ts +6 -0
  33. package/dist/context/cache.js +8 -0
  34. package/dist/context/git-context.d.ts +9 -0
  35. package/dist/context/git-context.js +39 -0
  36. package/dist/context/llmtune-md.d.ts +6 -0
  37. package/dist/context/llmtune-md.js +73 -0
  38. package/dist/context/workspace.d.ts +11 -0
  39. package/dist/context/workspace.js +115 -0
  40. package/dist/index.d.ts +3 -0
  41. package/dist/index.js +3 -2
  42. package/dist/marketplace/client.d.ts +52 -0
  43. package/dist/marketplace/client.js +86 -0
  44. package/dist/memory/files.d.ts +14 -0
  45. package/dist/memory/files.js +116 -0
  46. package/dist/memory/service.d.ts +22 -0
  47. package/dist/memory/service.js +146 -0
  48. package/dist/repl/repl.d.ts +8 -0
  49. package/dist/repl/repl.js +375 -0
  50. package/dist/skills/args.d.ts +10 -0
  51. package/dist/skills/args.js +37 -0
  52. package/dist/skills/frontmatter.d.ts +6 -0
  53. package/dist/skills/frontmatter.js +44 -0
  54. package/dist/skills/loader.d.ts +5 -0
  55. package/dist/skills/loader.js +59 -0
  56. package/dist/skills/registry.d.ts +27 -0
  57. package/dist/skills/registry.js +162 -0
  58. package/dist/skills/signing/signer.d.ts +19 -0
  59. package/dist/skills/signing/signer.js +110 -0
  60. package/dist/skills/trust.d.ts +11 -0
  61. package/dist/skills/trust.js +42 -0
  62. package/dist/telemetry/logger.d.ts +51 -0
  63. package/dist/telemetry/logger.js +135 -0
  64. package/dist/tools/permissions.d.ts +20 -0
  65. package/dist/tools/permissions.js +58 -0
  66. package/dist/tools/protocol.d.ts +22 -0
  67. package/dist/tools/protocol.js +3 -0
  68. package/dist/tools/registry.d.ts +20 -0
  69. package/dist/tools/registry.js +77 -0
  70. package/dist/tools/sandbox/docker.d.ts +16 -0
  71. package/dist/tools/sandbox/docker.js +240 -0
  72. package/dist/tools/sandbox/index.d.ts +18 -0
  73. package/dist/tools/sandbox/index.js +80 -0
  74. package/dist/tools/tools/ask-user.d.ts +3 -0
  75. package/dist/tools/tools/ask-user.js +56 -0
  76. package/dist/tools/tools/bash.d.ts +3 -0
  77. package/dist/tools/tools/bash.js +85 -0
  78. package/dist/tools/tools/edit.d.ts +3 -0
  79. package/dist/tools/tools/edit.js +138 -0
  80. package/dist/tools/tools/glob.d.ts +3 -0
  81. package/dist/tools/tools/glob.js +63 -0
  82. package/dist/tools/tools/grep.d.ts +3 -0
  83. package/dist/tools/tools/grep.js +148 -0
  84. package/dist/tools/tools/read.d.ts +3 -0
  85. package/dist/tools/tools/read.js +85 -0
  86. package/dist/tools/tools/web-fetch.d.ts +3 -0
  87. package/dist/tools/tools/web-fetch.js +143 -0
  88. package/dist/tools/tools/write.d.ts +3 -0
  89. package/dist/tools/tools/write.js +84 -0
  90. package/dist/tools/validation.d.ts +13 -0
  91. package/dist/tools/validation.js +142 -0
  92. package/dist/utils/markdown.d.ts +9 -0
  93. package/dist/utils/markdown.js +89 -0
  94. package/dist/utils/streaming.d.ts +10 -0
  95. package/dist/utils/streaming.js +63 -0
  96. package/dist/utils/tokens.d.ts +12 -0
  97. package/dist/utils/tokens.js +44 -0
  98. package/dist/version.d.ts +2 -0
  99. package/dist/version.js +9 -0
  100. package/package.json +2 -2
  101. package/dist/agent/conversation.d.ts.map +0 -1
  102. package/dist/agent/loop.d.ts.map +0 -1
  103. package/dist/agent/planner.d.ts.map +0 -1
  104. package/dist/auth/client.d.ts.map +0 -1
  105. package/dist/auth/config.d.ts.map +0 -1
  106. package/dist/commands/chat.d.ts.map +0 -1
  107. package/dist/commands/config.d.ts.map +0 -1
  108. package/dist/commands/login.d.ts.map +0 -1
  109. package/dist/commands/marketplace.d.ts.map +0 -1
  110. package/dist/commands/models.d.ts.map +0 -1
  111. package/dist/compact/history-store.d.ts.map +0 -1
  112. package/dist/compact/microcompact.d.ts.map +0 -1
  113. package/dist/compact/service.d.ts.map +0 -1
  114. package/dist/context/analyzer.d.ts.map +0 -1
  115. package/dist/context/builder.d.ts.map +0 -1
  116. package/dist/context/cache.d.ts.map +0 -1
  117. package/dist/context/git-context.d.ts.map +0 -1
  118. package/dist/context/llmtune-md.d.ts.map +0 -1
  119. package/dist/context/workspace.d.ts.map +0 -1
  120. package/dist/index.d.ts.map +0 -1
  121. package/dist/marketplace/client.d.ts.map +0 -1
  122. package/dist/memory/files.d.ts.map +0 -1
  123. package/dist/memory/service.d.ts.map +0 -1
  124. package/dist/repl/repl.d.ts.map +0 -1
  125. package/dist/skills/args.d.ts.map +0 -1
  126. package/dist/skills/frontmatter.d.ts.map +0 -1
  127. package/dist/skills/loader.d.ts.map +0 -1
  128. package/dist/skills/registry.d.ts.map +0 -1
  129. package/dist/skills/signing/signer.d.ts.map +0 -1
  130. package/dist/skills/trust.d.ts.map +0 -1
  131. package/dist/telemetry/logger.d.ts.map +0 -1
  132. package/dist/tools/permissions.d.ts.map +0 -1
  133. package/dist/tools/protocol.d.ts.map +0 -1
  134. package/dist/tools/registry.d.ts.map +0 -1
  135. package/dist/tools/sandbox/docker.d.ts.map +0 -1
  136. package/dist/tools/sandbox/index.d.ts.map +0 -1
  137. package/dist/tools/tools/ask-user.d.ts.map +0 -1
  138. package/dist/tools/tools/bash.d.ts.map +0 -1
  139. package/dist/tools/tools/edit.d.ts.map +0 -1
  140. package/dist/tools/tools/glob.d.ts.map +0 -1
  141. package/dist/tools/tools/grep.d.ts.map +0 -1
  142. package/dist/tools/tools/read.d.ts.map +0 -1
  143. package/dist/tools/tools/web-fetch.d.ts.map +0 -1
  144. package/dist/tools/tools/write.d.ts.map +0 -1
  145. package/dist/tools/validation.d.ts.map +0 -1
  146. package/dist/utils/markdown.d.ts.map +0 -1
  147. package/dist/utils/streaming.d.ts.map +0 -1
  148. package/dist/utils/tokens.d.ts.map +0 -1
  149. package/src/agent/conversation.ts +0 -140
  150. package/src/agent/loop.ts +0 -215
  151. package/src/agent/planner.ts +0 -55
  152. package/src/auth/client.ts +0 -19
  153. package/src/auth/config.ts +0 -89
  154. package/src/commands/chat.ts +0 -28
  155. package/src/commands/config.ts +0 -36
  156. package/src/commands/login.ts +0 -63
  157. package/src/commands/marketplace.ts +0 -190
  158. package/src/commands/models.ts +0 -74
  159. package/src/compact/history-store.ts +0 -101
  160. package/src/compact/microcompact.ts +0 -49
  161. package/src/compact/service.ts +0 -154
  162. package/src/context/analyzer.ts +0 -127
  163. package/src/context/builder.ts +0 -123
  164. package/src/context/cache.ts +0 -11
  165. package/src/context/git-context.ts +0 -58
  166. package/src/context/llmtune-md.ts +0 -48
  167. package/src/context/workspace.ts +0 -139
  168. package/src/index.ts +0 -100
  169. package/src/marketplace/client.ts +0 -118
  170. package/src/memory/files.ts +0 -81
  171. package/src/memory/service.ts +0 -124
  172. package/src/repl/repl.ts +0 -400
  173. package/src/skills/args.ts +0 -35
  174. package/src/skills/builtin/explain-code/SKILL.md +0 -30
  175. package/src/skills/frontmatter.ts +0 -47
  176. package/src/skills/loader.ts +0 -25
  177. package/src/skills/registry.ts +0 -155
  178. package/src/skills/signing/signer.ts +0 -101
  179. package/src/skills/trust.ts +0 -50
  180. package/src/telemetry/logger.ts +0 -108
  181. package/src/tools/permissions.ts +0 -83
  182. package/src/tools/protocol.ts +0 -24
  183. package/src/tools/registry.ts +0 -93
  184. package/src/tools/sandbox/docker.ts +0 -225
  185. package/src/tools/sandbox/index.ts +0 -91
  186. package/src/tools/tools/ask-user.ts +0 -60
  187. package/src/tools/tools/bash.ts +0 -97
  188. package/src/tools/tools/edit.ts +0 -111
  189. package/src/tools/tools/glob.ts +0 -68
  190. package/src/tools/tools/grep.ts +0 -121
  191. package/src/tools/tools/read.ts +0 -57
  192. package/src/tools/tools/web-fetch.ts +0 -158
  193. package/src/tools/tools/write.ts +0 -52
  194. package/src/tools/validation.ts +0 -164
  195. package/src/utils/markdown.ts +0 -96
  196. package/src/utils/streaming.ts +0 -63
  197. package/src/utils/tokens.ts +0 -41
  198. package/tsconfig.json +0 -20
@@ -1,158 +0,0 @@
1
- import https from "https"
2
- import http from "http"
3
- import type { Tool, ToolSpec, ToolResult, ToolContext } from "../protocol"
4
-
5
- const MAX_RESPONSE_SIZE = 500_000
6
- const TIMEOUT_MS = 30_000
7
-
8
- export const webFetchTool: Tool = {
9
- spec(): ToolSpec {
10
- return {
11
- name: "web-fetch",
12
- description:
13
- "Fetch content from a URL. Returns the response body as text. Supports HTTP and HTTPS. Use for reading web pages, API responses, or documentation.",
14
- inputSchema: {
15
- type: "object",
16
- properties: {
17
- url: {
18
- type: "string",
19
- description: "The URL to fetch (http:// or https://)",
20
- },
21
- method: {
22
- type: "string",
23
- description: "HTTP method (default: GET)",
24
- },
25
- headers: {
26
- type: "object",
27
- description: "Optional request headers as key-value pairs",
28
- },
29
- },
30
- required: ["url"],
31
- },
32
- isReadOnly: true,
33
- }
34
- },
35
-
36
- run(input: Record<string, unknown>, _ctx: ToolContext): Promise<ToolResult> {
37
- const url = String(input.url ?? "")
38
- const method = String(input.method ?? "GET").toUpperCase()
39
- const headers = (input.headers as Record<string, string>) ?? {}
40
-
41
- if (!url) {
42
- return Promise.resolve({
43
- name: "web-fetch",
44
- output: { error: "url is required" },
45
- isError: true,
46
- })
47
- }
48
-
49
- let parsedUrl: URL
50
- try {
51
- parsedUrl = new URL(url)
52
- } catch {
53
- return Promise.resolve({
54
- name: "web-fetch",
55
- output: { error: `Invalid URL: ${url}` },
56
- isError: true,
57
- })
58
- }
59
-
60
- if (parsedUrl.protocol !== "http:" && parsedUrl.protocol !== "https:") {
61
- return Promise.resolve({
62
- name: "web-fetch",
63
- output: { error: `Unsupported protocol: ${parsedUrl.protocol}. Use http:// or https://` },
64
- isError: true,
65
- })
66
- }
67
-
68
- return new Promise((resolve) => {
69
- const lib = parsedUrl.protocol === "https:" ? https : http
70
-
71
- const req = lib.request(
72
- url,
73
- {
74
- method,
75
- headers: {
76
- "User-Agent": "LLMTune-CLI/0.1.0",
77
- Accept: "text/html,application/json,text/plain,*/*",
78
- ...headers,
79
- },
80
- timeout: TIMEOUT_MS,
81
- },
82
- (res) => {
83
- // Follow redirects (up to 5)
84
- if (res.statusCode && res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
85
- const redirectUrl = new URL(res.headers.location, url).toString()
86
- return Promise.resolve(
87
- webFetchTool.run({ ...input, url: redirectUrl }, _ctx),
88
- ).then(resolve)
89
- }
90
-
91
- const chunks: Buffer[] = []
92
- let size = 0
93
-
94
- res.on("data", (chunk: Buffer) => {
95
- size += chunk.length
96
- if (size > MAX_RESPONSE_SIZE) {
97
- req.destroy()
98
- resolve({
99
- name: "web-fetch",
100
- output: {
101
- error: `Response too large (${(size / 1024).toFixed(0)}KB). Maximum is ${MAX_RESPONSE_SIZE / 1024}KB.`,
102
- },
103
- isError: true,
104
- })
105
- return
106
- }
107
- chunks.push(chunk)
108
- })
109
-
110
- res.on("end", () => {
111
- const body = Buffer.concat(chunks).toString("utf-8")
112
- const truncated = body.length > 50_000
113
- const content = truncated ? body.slice(0, 50_000) + "\n... (truncated)" : body
114
-
115
- resolve({
116
- name: "web-fetch",
117
- output: {
118
- url,
119
- status: res.statusCode ?? 0,
120
- contentType: res.headers["content-type"] ?? "unknown",
121
- content,
122
- truncated,
123
- },
124
- isError: false,
125
- })
126
- })
127
-
128
- res.on("error", (err) => {
129
- resolve({
130
- name: "web-fetch",
131
- output: { error: `Response error: ${err.message}` },
132
- isError: true,
133
- })
134
- })
135
- },
136
- )
137
-
138
- req.on("error", (err) => {
139
- resolve({
140
- name: "web-fetch",
141
- output: { error: `Request failed: ${err.message}` },
142
- isError: true,
143
- })
144
- })
145
-
146
- req.on("timeout", () => {
147
- req.destroy()
148
- resolve({
149
- name: "web-fetch",
150
- output: { error: `Request timed out after ${TIMEOUT_MS / 1000}s` },
151
- isError: true,
152
- })
153
- })
154
-
155
- req.end()
156
- })
157
- },
158
- }
@@ -1,52 +0,0 @@
1
- import * as fs from "fs"
2
- import * as path from "path"
3
- import type { Tool, ToolSpec, ToolResult, ToolContext } from "../protocol"
4
-
5
- export const writeTool: Tool = {
6
- spec(): ToolSpec {
7
- return {
8
- name: "write",
9
- description:
10
- "Create or overwrite a file with the given content. Path is relative to workspace root.",
11
- inputSchema: {
12
- type: "object",
13
- properties: {
14
- file_path: { type: "string", description: "Path to write (relative to workspace)" },
15
- content: { type: "string", description: "Content to write" },
16
- create_dirs: { type: "boolean", description: "Create parent dirs (default: true)" },
17
- },
18
- required: ["file_path", "content"],
19
- },
20
- isDestructive: true,
21
- }
22
- },
23
-
24
- run(input: Record<string, unknown>, ctx: ToolContext): ToolResult {
25
- const filePath = String(input.file_path ?? "")
26
- const content = String(input.content ?? "")
27
- const createDirs = input.create_dirs !== false
28
- const absPath = path.resolve(ctx.cwd, filePath)
29
-
30
- if (!absPath.startsWith(path.resolve(ctx.cwd))) {
31
- return { name: "write", output: { error: "Cannot write outside workspace" }, isError: true }
32
- }
33
-
34
- try {
35
- if (createDirs) {
36
- fs.mkdirSync(path.dirname(absPath), { recursive: true })
37
- }
38
- fs.writeFileSync(absPath, content, "utf-8")
39
- return {
40
- name: "write",
41
- output: { type: "file_written", filePath, bytesWritten: Buffer.byteLength(content) },
42
- isError: false,
43
- }
44
- } catch (err: unknown) {
45
- return {
46
- name: "write",
47
- output: { error: `Write failed: ${(err as Error).message}` },
48
- isError: true,
49
- }
50
- }
51
- },
52
- }
@@ -1,164 +0,0 @@
1
- /**
2
- * JSON Schema validation for tool inputs.
3
- * Ported from Clawd-Code's schema_validation.py.
4
- */
5
-
6
- export interface ValidationIssue {
7
- path: string
8
- message: string
9
- }
10
-
11
- function typeName(value: unknown): string {
12
- if (value === null) return "null"
13
- if (typeof value === "boolean") return "boolean"
14
- if (typeof value === "number") {
15
- if (Number.isInteger(value)) return "integer"
16
- return "number"
17
- }
18
- if (typeof value === "string") return "string"
19
- if (Array.isArray(value)) return "array"
20
- if (typeof value === "object") return "object"
21
- return typeof value
22
- }
23
-
24
- export class ToolInputError extends Error {
25
- constructor(message: string) {
26
- super(message)
27
- this.name = "ToolInputError"
28
- }
29
- }
30
-
31
- export function validateJsonSchema(
32
- value: unknown,
33
- schema: Record<string, unknown>,
34
- rootName = "input"
35
- ): void {
36
- const issues: ValidationIssue[] = []
37
- validateNode(value, schema, rootName, issues)
38
- if (issues.length > 0) {
39
- const rendered = issues
40
- .slice(0, 5)
41
- .map((i) => `${i.path}: ${i.message}`)
42
- .join("; ")
43
- const suffix = issues.length > 5 ? `; (+${issues.length - 5} more)` : ""
44
- throw new ToolInputError(rendered + suffix)
45
- }
46
- }
47
-
48
- function validateNode(
49
- value: unknown,
50
- schema: Record<string, unknown>,
51
- path: string,
52
- issues: ValidationIssue[]
53
- ): void {
54
- if ("oneOf" in schema) {
55
- const options = (schema.oneOf as Record<string, unknown>[]) || []
56
- if (options.some((opt) => isValid(value, opt))) return
57
- issues.push({ path, message: "does not match any allowed schema (oneOf)" })
58
- return
59
- }
60
-
61
- if ("anyOf" in schema) {
62
- const options = (schema.anyOf as Record<string, unknown>[]) || []
63
- if (options.some((opt) => isValid(value, opt))) return
64
- issues.push({ path, message: "does not match any allowed schema (anyOf)" })
65
- return
66
- }
67
-
68
- const expectedType = schema.type as string | undefined
69
-
70
- if (expectedType === "object") {
71
- if (typeof value !== "object" || value === null || Array.isArray(value)) {
72
- issues.push({ path, message: `expected object, got ${typeName(value)}` })
73
- return
74
- }
75
- validateObject(value as Record<string, unknown>, schema, path, issues)
76
- return
77
- }
78
-
79
- if (expectedType === "array") {
80
- if (!Array.isArray(value)) {
81
- issues.push({ path, message: `expected array, got ${typeName(value)}` })
82
- return
83
- }
84
- const itemSchema = schema.items as Record<string, unknown> | undefined
85
- if (itemSchema) {
86
- value.forEach((item, idx) => {
87
- validateNode(item, itemSchema, `${path}[${idx}]`, issues)
88
- })
89
- }
90
- return
91
- }
92
-
93
- if (expectedType === "string") {
94
- if (typeof value !== "string") {
95
- issues.push({ path, message: `expected string, got ${typeName(value)}` })
96
- }
97
- return
98
- }
99
-
100
- if (expectedType === "boolean") {
101
- if (typeof value !== "boolean") {
102
- issues.push({ path, message: `expected boolean, got ${typeName(value)}` })
103
- }
104
- return
105
- }
106
-
107
- if (expectedType === "number") {
108
- if (typeof value !== "number") {
109
- issues.push({ path, message: `expected number, got ${typeName(value)}` })
110
- }
111
- return
112
- }
113
-
114
- if (expectedType === "integer") {
115
- if (typeof value !== "number" || !Number.isInteger(value)) {
116
- issues.push({ path, message: `expected integer, got ${typeName(value)}` })
117
- }
118
- return
119
- }
120
-
121
- if ("enum" in schema) {
122
- const allowed = (schema.enum as unknown[]) || []
123
- if (!allowed.includes(value)) {
124
- issues.push({
125
- path,
126
- message: `expected one of ${JSON.stringify(allowed)}, got ${JSON.stringify(value)}`,
127
- })
128
- }
129
- }
130
- }
131
-
132
- function validateObject(
133
- value: Record<string, unknown>,
134
- schema: Record<string, unknown>,
135
- path: string,
136
- issues: ValidationIssue[]
137
- ): void {
138
- const required = new Set((schema.required as string[]) || [])
139
- const properties = (schema.properties as Record<string, Record<string, unknown>>) || {}
140
- const additional = schema.additionalProperties !== false
141
-
142
- for (const req of required) {
143
- if (!(req in value)) {
144
- issues.push({ path, message: `missing required field '${req}'` })
145
- }
146
- }
147
-
148
- for (const [key, val] of Object.entries(value)) {
149
- const propSchema = properties[key]
150
- if (!propSchema) {
151
- if (!additional) {
152
- issues.push({ path: `${path}.${key}`, message: "unexpected field" })
153
- }
154
- continue
155
- }
156
- validateNode(val, propSchema, `${path}.${key}`, issues)
157
- }
158
- }
159
-
160
- function isValid(value: unknown, schema: Record<string, unknown>): boolean {
161
- const issues: ValidationIssue[] = []
162
- validateNode(value, schema, "$", issues)
163
- return issues.length === 0
164
- }
@@ -1,96 +0,0 @@
1
- /**
2
- * Terminal markdown renderer.
3
- * Converts markdown text to chalk-formatted terminal output.
4
- */
5
- import chalk from "chalk"
6
-
7
- interface RenderOptions {
8
- maxWidth?: number
9
- indent?: string
10
- }
11
-
12
- export function renderMarkdown(text: string, options?: RenderOptions): string {
13
- const lines = text.split("\n")
14
- const result: string[] = []
15
-
16
- for (const line of lines) {
17
- result.push(renderLine(line, options))
18
- }
19
-
20
- return result.join("\n")
21
- }
22
-
23
- function renderLine(line: string, options?: RenderOptions): string {
24
- const trimmed = line.trimStart()
25
-
26
- // Headings
27
- if (trimmed.startsWith("###### ")) return chalk.bold.cyan(trimmed.slice(7))
28
- if (trimmed.startsWith("##### ")) return chalk.bold.cyan(trimmed.slice(6))
29
- if (trimmed.startsWith("#### ")) return chalk.bold.cyan(trimmed.slice(5))
30
- if (trimmed.startsWith("### ")) return chalk.bold.cyan(trimmed.slice(4))
31
- if (trimmed.startsWith("## ")) return chalk.bold.blue(trimmed.slice(3))
32
- if (trimmed.startsWith("# ")) return chalk.bold.white(trimmed.slice(2))
33
-
34
- // Code blocks
35
- if (trimmed.startsWith("```")) {
36
- return chalk.dim(trimmed)
37
- }
38
-
39
- // Blockquotes
40
- if (trimmed.startsWith("> ")) {
41
- return chalk.dim("│ " + renderInline(trimmed.slice(2)))
42
- }
43
-
44
- // Lists
45
- if (/^[-*+]\s/.test(trimmed)) {
46
- return chalk.cyan("•") + " " + renderInline(trimmed.slice(2))
47
- }
48
- if (/^\d+\.\s/.test(trimmed)) {
49
- const match = trimmed.match(/^(\d+\.)\s(.*)$/)
50
- if (match) return chalk.cyan(match[1]) + " " + renderInline(match[2])
51
- }
52
-
53
- // Horizontal rule
54
- if (/^[-*_]{3,}\s*$/.test(trimmed)) {
55
- return chalk.dim("─".repeat(options?.maxWidth ?? 60))
56
- }
57
-
58
- return renderInline(line)
59
- }
60
-
61
- function renderInline(text: string): string {
62
- // Bold
63
- text = text.replace(/\*\*(.+?)\*\*/g, (_, content) => chalk.bold(content))
64
- // Italic
65
- text = text.replace(/(?<!\*)\*(?!\*)(.+?)(?<!\*)\*(?!\*)/g, (_, content) => chalk.italic(content))
66
- // Inline code
67
- text = text.replace(/`([^`]+)`/g, (_, content) => chalk.bgBlackBright(content))
68
- // Links
69
- text = text.replace(/\[([^\]]+)\]\(([^)]+)\)/g, (_, label, url) => chalk.blue.underline(label) + chalk.dim(` (${url})`))
70
-
71
- return text
72
- }
73
-
74
- export function renderCodeBlock(code: string, language?: string): string {
75
- const header = language ? chalk.dim(`─ ${language} ─`) : ""
76
- const lines = code.split("\n").map((line, i) => {
77
- const num = String(i + 1).padStart(4)
78
- return chalk.dim(`${num} │`) + ` ${line}`
79
- })
80
- return header + (header ? "\n" : "") + lines.join("\n")
81
- }
82
-
83
- export function renderTable(headers: string[], rows: string[][]): string {
84
- const colWidths = headers.map((h, i) => {
85
- const maxDataLen = Math.max(...rows.map((r) => (r[i] ?? "").length))
86
- return Math.max(h.length, maxDataLen) + 2
87
- })
88
-
89
- const headerLine = headers.map((h, i) => chalk.bold(h.padEnd(colWidths[i]))).join("")
90
- const separator = colWidths.map((w) => chalk.dim("─".repeat(w))).join(chalk.dim("─"))
91
- const dataLines = rows.map((row) =>
92
- row.map((cell, i) => (cell ?? "").padEnd(colWidths[i])).join(""),
93
- )
94
-
95
- return [headerLine, separator, ...dataLines].join("\n")
96
- }
@@ -1,63 +0,0 @@
1
- import { Ora } from "ora"
2
-
3
- export function createStreamingDisplay() {
4
- let buffer = ""
5
- let lastUpdateTime = 0
6
- const UPDATE_INTERVAL_MS = 50
7
-
8
- return {
9
- onToken(token: string): void {
10
- buffer += token
11
- const now = Date.now()
12
- if (now - lastUpdateTime > UPDATE_INTERVAL_MS) {
13
- process.stdout.write(token)
14
- lastUpdateTime = now
15
- }
16
- },
17
- flush(): void {
18
- if (buffer) {
19
- process.stdout.write("\n")
20
- buffer = ""
21
- }
22
- },
23
- getBuffer(): string {
24
- return buffer
25
- },
26
- }
27
- }
28
-
29
- export function showSpinner(text: string): Ora {
30
- const ora = require("ora")
31
- return ora({ text, color: "cyan" }).start()
32
- }
33
-
34
- export function formatToolCall(name: string, input: Record<string, unknown>): string {
35
- const summary = summarizeToolInput(name, input)
36
- return ` \x1b[33m*\x1b[0m ${name}${summary ? `(${summary})` : ""}`
37
- }
38
-
39
- function summarizeToolInput(name: string, input: Record<string, unknown>): string {
40
- switch (name.toLowerCase()) {
41
- case "bash": {
42
- const cmd = String(input.command || "")
43
- return cmd.length > 60 ? cmd.slice(0, 57) + "..." : cmd
44
- }
45
- case "read":
46
- case "write":
47
- case "edit":
48
- return String(input.file_path || input.path || "")
49
- case "glob":
50
- return String(input.pattern || "")
51
- case "grep":
52
- return String(input.pattern || "")
53
- default:
54
- return ""
55
- }
56
- }
57
-
58
- export function formatToolResult(name: string, output: unknown): string {
59
- if (typeof output === "string") {
60
- return output.length > 200 ? output.slice(0, 197) + "..." : output
61
- }
62
- return JSON.stringify(output).slice(0, 200)
63
- }
@@ -1,41 +0,0 @@
1
- /**
2
- * Token estimation utilities.
3
- * Uses ~4 chars per token heuristic (works well for English + code).
4
- */
5
-
6
- export function estimateTokens(text: string): number {
7
- if (!text) return 0
8
- return Math.ceil(text.length / 4)
9
- }
10
-
11
- export function estimateMessagesTokens(
12
- messages: Array<{ role: string; content: unknown }>
13
- ): number {
14
- let total = 0
15
- for (const msg of messages) {
16
- const content =
17
- typeof msg.content === "string"
18
- ? msg.content
19
- : JSON.stringify(msg.content ?? "")
20
- total += estimateTokens(content)
21
- // Overhead per message (~4 tokens for role, separators)
22
- total += 4
23
- }
24
- return total
25
- }
26
-
27
- export function estimateToolSchemaTokens(schemas: Array<Record<string, unknown>>): number {
28
- let total = 0
29
- for (const schema of schemas) {
30
- total += estimateTokens(String(schema.description ?? ""))
31
- total += estimateTokens(String(schema.name ?? ""))
32
- total += estimateTokens(JSON.stringify(schema.input_schema ?? {}))
33
- }
34
- return total
35
- }
36
-
37
- export function formatTokenCount(tokens: number): string {
38
- if (tokens >= 1_000_000) return `${(tokens / 1_000_000).toFixed(1)}M`
39
- if (tokens >= 1_000) return `${(tokens / 1_000).toFixed(1)}K`
40
- return String(tokens)
41
- }
package/tsconfig.json DELETED
@@ -1,20 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "target": "ES2022",
4
- "module": "commonjs",
5
- "lib": ["ES2022"],
6
- "outDir": "./dist",
7
- "rootDir": "./src",
8
- "strict": true,
9
- "esModuleInterop": true,
10
- "skipLibCheck": true,
11
- "forceConsistentCasingInFileNames": true,
12
- "resolveJsonModule": true,
13
- "declaration": true,
14
- "declarationMap": true,
15
- "sourceMap": true,
16
- "moduleResolution": "node"
17
- },
18
- "include": ["src/**/*"],
19
- "exclude": ["node_modules", "dist"]
20
- }