@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.
- package/README.md +1 -1
- package/dist/agent/conversation.d.ts +42 -0
- package/dist/agent/conversation.js +105 -0
- package/dist/agent/loop.d.ts +19 -0
- package/dist/agent/loop.js +185 -0
- package/dist/agent/planner.d.ts +8 -0
- package/dist/agent/planner.js +43 -0
- package/dist/auth/client.d.ts +4 -0
- package/dist/auth/client.js +24 -0
- package/dist/auth/config.d.ts +21 -0
- package/dist/auth/config.js +83 -0
- package/dist/commands/chat.d.ts +5 -0
- package/dist/commands/chat.js +27 -0
- package/dist/commands/config.d.ts +2 -0
- package/dist/commands/config.js +37 -0
- package/dist/commands/login.d.ts +2 -0
- package/dist/commands/login.js +93 -0
- package/dist/commands/marketplace.d.ts +6 -0
- package/dist/commands/marketplace.js +213 -0
- package/dist/commands/models.d.ts +2 -0
- package/dist/commands/models.js +53 -0
- package/dist/compact/history-store.d.ts +29 -0
- package/dist/compact/history-store.js +110 -0
- package/dist/compact/microcompact.d.ts +10 -0
- package/dist/compact/microcompact.js +43 -0
- package/dist/compact/service.d.ts +13 -0
- package/dist/compact/service.js +156 -0
- package/dist/context/analyzer.d.ts +26 -0
- package/dist/context/analyzer.js +99 -0
- package/dist/context/builder.d.ts +13 -0
- package/dist/context/builder.js +144 -0
- package/dist/context/cache.d.ts +6 -0
- package/dist/context/cache.js +8 -0
- package/dist/context/git-context.d.ts +9 -0
- package/dist/context/git-context.js +39 -0
- package/dist/context/llmtune-md.d.ts +6 -0
- package/dist/context/llmtune-md.js +73 -0
- package/dist/context/workspace.d.ts +11 -0
- package/dist/context/workspace.js +115 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +3 -2
- package/dist/marketplace/client.d.ts +52 -0
- package/dist/marketplace/client.js +86 -0
- package/dist/memory/files.d.ts +14 -0
- package/dist/memory/files.js +116 -0
- package/dist/memory/service.d.ts +22 -0
- package/dist/memory/service.js +146 -0
- package/dist/repl/repl.d.ts +8 -0
- package/dist/repl/repl.js +375 -0
- package/dist/skills/args.d.ts +10 -0
- package/dist/skills/args.js +37 -0
- package/dist/skills/frontmatter.d.ts +6 -0
- package/dist/skills/frontmatter.js +44 -0
- package/dist/skills/loader.d.ts +5 -0
- package/dist/skills/loader.js +59 -0
- package/dist/skills/registry.d.ts +27 -0
- package/dist/skills/registry.js +162 -0
- package/dist/skills/signing/signer.d.ts +19 -0
- package/dist/skills/signing/signer.js +110 -0
- package/dist/skills/trust.d.ts +11 -0
- package/dist/skills/trust.js +42 -0
- package/dist/telemetry/logger.d.ts +51 -0
- package/dist/telemetry/logger.js +135 -0
- package/dist/tools/permissions.d.ts +20 -0
- package/dist/tools/permissions.js +58 -0
- package/dist/tools/protocol.d.ts +22 -0
- package/dist/tools/protocol.js +3 -0
- package/dist/tools/registry.d.ts +20 -0
- package/dist/tools/registry.js +77 -0
- package/dist/tools/sandbox/docker.d.ts +16 -0
- package/dist/tools/sandbox/docker.js +240 -0
- package/dist/tools/sandbox/index.d.ts +18 -0
- package/dist/tools/sandbox/index.js +80 -0
- package/dist/tools/tools/ask-user.d.ts +3 -0
- package/dist/tools/tools/ask-user.js +56 -0
- package/dist/tools/tools/bash.d.ts +3 -0
- package/dist/tools/tools/bash.js +85 -0
- package/dist/tools/tools/edit.d.ts +3 -0
- package/dist/tools/tools/edit.js +138 -0
- package/dist/tools/tools/glob.d.ts +3 -0
- package/dist/tools/tools/glob.js +63 -0
- package/dist/tools/tools/grep.d.ts +3 -0
- package/dist/tools/tools/grep.js +148 -0
- package/dist/tools/tools/read.d.ts +3 -0
- package/dist/tools/tools/read.js +85 -0
- package/dist/tools/tools/web-fetch.d.ts +3 -0
- package/dist/tools/tools/web-fetch.js +143 -0
- package/dist/tools/tools/write.d.ts +3 -0
- package/dist/tools/tools/write.js +84 -0
- package/dist/tools/validation.d.ts +13 -0
- package/dist/tools/validation.js +142 -0
- package/dist/utils/markdown.d.ts +9 -0
- package/dist/utils/markdown.js +89 -0
- package/dist/utils/streaming.d.ts +10 -0
- package/dist/utils/streaming.js +63 -0
- package/dist/utils/tokens.d.ts +12 -0
- package/dist/utils/tokens.js +44 -0
- package/dist/version.d.ts +2 -0
- package/dist/version.js +9 -0
- package/package.json +2 -2
- package/dist/agent/conversation.d.ts.map +0 -1
- package/dist/agent/loop.d.ts.map +0 -1
- package/dist/agent/planner.d.ts.map +0 -1
- package/dist/auth/client.d.ts.map +0 -1
- package/dist/auth/config.d.ts.map +0 -1
- package/dist/commands/chat.d.ts.map +0 -1
- package/dist/commands/config.d.ts.map +0 -1
- package/dist/commands/login.d.ts.map +0 -1
- package/dist/commands/marketplace.d.ts.map +0 -1
- package/dist/commands/models.d.ts.map +0 -1
- package/dist/compact/history-store.d.ts.map +0 -1
- package/dist/compact/microcompact.d.ts.map +0 -1
- package/dist/compact/service.d.ts.map +0 -1
- package/dist/context/analyzer.d.ts.map +0 -1
- package/dist/context/builder.d.ts.map +0 -1
- package/dist/context/cache.d.ts.map +0 -1
- package/dist/context/git-context.d.ts.map +0 -1
- package/dist/context/llmtune-md.d.ts.map +0 -1
- package/dist/context/workspace.d.ts.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/marketplace/client.d.ts.map +0 -1
- package/dist/memory/files.d.ts.map +0 -1
- package/dist/memory/service.d.ts.map +0 -1
- package/dist/repl/repl.d.ts.map +0 -1
- package/dist/skills/args.d.ts.map +0 -1
- package/dist/skills/frontmatter.d.ts.map +0 -1
- package/dist/skills/loader.d.ts.map +0 -1
- package/dist/skills/registry.d.ts.map +0 -1
- package/dist/skills/signing/signer.d.ts.map +0 -1
- package/dist/skills/trust.d.ts.map +0 -1
- package/dist/telemetry/logger.d.ts.map +0 -1
- package/dist/tools/permissions.d.ts.map +0 -1
- package/dist/tools/protocol.d.ts.map +0 -1
- package/dist/tools/registry.d.ts.map +0 -1
- package/dist/tools/sandbox/docker.d.ts.map +0 -1
- package/dist/tools/sandbox/index.d.ts.map +0 -1
- package/dist/tools/tools/ask-user.d.ts.map +0 -1
- package/dist/tools/tools/bash.d.ts.map +0 -1
- package/dist/tools/tools/edit.d.ts.map +0 -1
- package/dist/tools/tools/glob.d.ts.map +0 -1
- package/dist/tools/tools/grep.d.ts.map +0 -1
- package/dist/tools/tools/read.d.ts.map +0 -1
- package/dist/tools/tools/web-fetch.d.ts.map +0 -1
- package/dist/tools/tools/write.d.ts.map +0 -1
- package/dist/tools/validation.d.ts.map +0 -1
- package/dist/utils/markdown.d.ts.map +0 -1
- package/dist/utils/streaming.d.ts.map +0 -1
- package/dist/utils/tokens.d.ts.map +0 -1
- package/src/agent/conversation.ts +0 -140
- package/src/agent/loop.ts +0 -215
- package/src/agent/planner.ts +0 -55
- package/src/auth/client.ts +0 -19
- package/src/auth/config.ts +0 -89
- package/src/commands/chat.ts +0 -28
- package/src/commands/config.ts +0 -36
- package/src/commands/login.ts +0 -63
- package/src/commands/marketplace.ts +0 -190
- package/src/commands/models.ts +0 -74
- package/src/compact/history-store.ts +0 -101
- package/src/compact/microcompact.ts +0 -49
- package/src/compact/service.ts +0 -154
- package/src/context/analyzer.ts +0 -127
- package/src/context/builder.ts +0 -123
- package/src/context/cache.ts +0 -11
- package/src/context/git-context.ts +0 -58
- package/src/context/llmtune-md.ts +0 -48
- package/src/context/workspace.ts +0 -139
- package/src/index.ts +0 -100
- package/src/marketplace/client.ts +0 -118
- package/src/memory/files.ts +0 -81
- package/src/memory/service.ts +0 -124
- package/src/repl/repl.ts +0 -400
- package/src/skills/args.ts +0 -35
- package/src/skills/builtin/explain-code/SKILL.md +0 -30
- package/src/skills/frontmatter.ts +0 -47
- package/src/skills/loader.ts +0 -25
- package/src/skills/registry.ts +0 -155
- package/src/skills/signing/signer.ts +0 -101
- package/src/skills/trust.ts +0 -50
- package/src/telemetry/logger.ts +0 -108
- package/src/tools/permissions.ts +0 -83
- package/src/tools/protocol.ts +0 -24
- package/src/tools/registry.ts +0 -93
- package/src/tools/sandbox/docker.ts +0 -225
- package/src/tools/sandbox/index.ts +0 -91
- package/src/tools/tools/ask-user.ts +0 -60
- package/src/tools/tools/bash.ts +0 -97
- package/src/tools/tools/edit.ts +0 -111
- package/src/tools/tools/glob.ts +0 -68
- package/src/tools/tools/grep.ts +0 -121
- package/src/tools/tools/read.ts +0 -57
- package/src/tools/tools/web-fetch.ts +0 -158
- package/src/tools/tools/write.ts +0 -52
- package/src/tools/validation.ts +0 -164
- package/src/utils/markdown.ts +0 -96
- package/src/utils/streaming.ts +0 -63
- package/src/utils/tokens.ts +0 -41
- 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
|
-
}
|
package/src/tools/tools/write.ts
DELETED
|
@@ -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
|
-
}
|
package/src/tools/validation.ts
DELETED
|
@@ -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
|
-
}
|
package/src/utils/markdown.ts
DELETED
|
@@ -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
|
-
}
|
package/src/utils/streaming.ts
DELETED
|
@@ -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
|
-
}
|
package/src/utils/tokens.ts
DELETED
|
@@ -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
|
-
}
|