@llmtune/cli 0.1.0 → 0.1.1
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 +49 -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 +1 -1
- 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 +374 -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 +142 -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/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
package/src/context/builder.ts
DELETED
|
@@ -1,123 +0,0 @@
|
|
|
1
|
-
import * as path from "path"
|
|
2
|
-
import * as crypto from "crypto"
|
|
3
|
-
import * as fs from "fs"
|
|
4
|
-
import { collectGitContext, type GitContext } from "./git-context"
|
|
5
|
-
import { buildWorkspaceSnapshot, renderWorkspaceSection, type WorkspaceSnapshot } from "./workspace"
|
|
6
|
-
import { loadProjectInstructions } from "./llmtune-md"
|
|
7
|
-
|
|
8
|
-
export interface ContextResult {
|
|
9
|
-
prompt: string
|
|
10
|
-
cacheKey: string
|
|
11
|
-
cacheHit: boolean
|
|
12
|
-
sections: string[]
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
const CACHE_DIR = () => {
|
|
16
|
-
const base = process.env.LLMTUNE_CACHE_DIR || path.join(process.env.HOME || process.env.USERPROFILE || "~", ".llmtune", "cache")
|
|
17
|
-
return base
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
function getCachePath(cacheKey: string): string {
|
|
21
|
-
return path.join(CACHE_DIR(), `${cacheKey}.json`)
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export function computeCacheKey(workspaceRoot: string, cwd: string): string {
|
|
25
|
-
const git = collectGitContext(workspaceRoot)
|
|
26
|
-
const base = `${workspaceRoot}:${cwd}`
|
|
27
|
-
if (git.available && git.recentCommit) {
|
|
28
|
-
return crypto.createHash("sha256").update(`${base}:${git.recentCommit}`).digest("hex").slice(0, 16)
|
|
29
|
-
}
|
|
30
|
-
return crypto.createHash("sha256").update(base).digest("hex").slice(0, 16)
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export function readCache(cacheKey: string): ContextResult | null {
|
|
34
|
-
try {
|
|
35
|
-
const cachePath = getCachePath(cacheKey)
|
|
36
|
-
if (fs.existsSync(cachePath)) {
|
|
37
|
-
const stat = fs.statSync(cachePath)
|
|
38
|
-
const ageMs = Date.now() - stat.mtimeMs
|
|
39
|
-
if (ageMs > 30 * 60 * 1000) return null
|
|
40
|
-
const data = JSON.parse(fs.readFileSync(cachePath, "utf-8"))
|
|
41
|
-
return { ...data, cacheHit: true }
|
|
42
|
-
}
|
|
43
|
-
} catch {}
|
|
44
|
-
return null
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
export function writeCache(cacheKey: string, result: ContextResult): void {
|
|
48
|
-
try {
|
|
49
|
-
const cacheDir = CACHE_DIR()
|
|
50
|
-
if (!fs.existsSync(cacheDir)) {
|
|
51
|
-
fs.mkdirSync(cacheDir, { recursive: true })
|
|
52
|
-
}
|
|
53
|
-
const cachePath = getCachePath(cacheKey)
|
|
54
|
-
fs.writeFileSync(cachePath, JSON.stringify({ ...result, cacheHit: false }, null, 2))
|
|
55
|
-
} catch {}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
export async function buildContextPrompt(
|
|
59
|
-
workspaceRoot: string,
|
|
60
|
-
cwd: string,
|
|
61
|
-
options?: { useCache?: boolean }
|
|
62
|
-
): Promise<ContextResult> {
|
|
63
|
-
const cacheKey = computeCacheKey(workspaceRoot, cwd)
|
|
64
|
-
|
|
65
|
-
if (options?.useCache !== false) {
|
|
66
|
-
const cached = readCache(cacheKey)
|
|
67
|
-
if (cached) return cached
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
const sections: string[] = []
|
|
71
|
-
|
|
72
|
-
// Workspace section (async)
|
|
73
|
-
const workspace = await buildWorkspaceSnapshot(workspaceRoot, cwd)
|
|
74
|
-
const workspaceSection = renderWorkspaceSection(workspace)
|
|
75
|
-
if (workspaceSection) sections.push(workspaceSection)
|
|
76
|
-
|
|
77
|
-
// Git section
|
|
78
|
-
const git = collectGitContext(workspaceRoot)
|
|
79
|
-
const gitSection = renderGitSection(git)
|
|
80
|
-
if (gitSection) sections.push(gitSection)
|
|
81
|
-
|
|
82
|
-
// LLMTUNE.md / CLAUDE.md section
|
|
83
|
-
const mdFiles = loadProjectInstructions(workspaceRoot, cwd)
|
|
84
|
-
const mdSection = renderMdSection(mdFiles, workspaceRoot)
|
|
85
|
-
if (mdSection) sections.push(mdSection)
|
|
86
|
-
|
|
87
|
-
const prompt = sections.join("\n\n")
|
|
88
|
-
|
|
89
|
-
const result: ContextResult = {
|
|
90
|
-
prompt,
|
|
91
|
-
cacheKey,
|
|
92
|
-
cacheHit: false,
|
|
93
|
-
sections,
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
writeCache(cacheKey, result)
|
|
97
|
-
return result
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
function renderGitSection(git: GitContext): string {
|
|
101
|
-
if (!git.available) return ""
|
|
102
|
-
const lines = ["## Git Context"]
|
|
103
|
-
if (git.branch) lines.push(`- Current branch: ${git.branch}`)
|
|
104
|
-
if (git.recentCommit) lines.push(`- Latest commit: ${git.recentCommit}`)
|
|
105
|
-
if (git.status) {
|
|
106
|
-
lines.push("- Git status snapshot:", "```text", git.status.slice(0, 2000), "```")
|
|
107
|
-
}
|
|
108
|
-
return lines.join("\n")
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
function renderMdSection(
|
|
112
|
-
files: Array<{ path: string; content: string }>,
|
|
113
|
-
workspaceRoot: string
|
|
114
|
-
): string {
|
|
115
|
-
if (files.length === 0) return ""
|
|
116
|
-
const lines = ["## Project Instructions"]
|
|
117
|
-
for (const f of files) {
|
|
118
|
-
const relative = path.relative(workspaceRoot, f.path)
|
|
119
|
-
const label = relative ? `./${relative}` : f.path
|
|
120
|
-
lines.push(`### ${label}`, "```md", f.content.slice(0, 4000), "```")
|
|
121
|
-
}
|
|
122
|
-
return lines.join("\n")
|
|
123
|
-
}
|
package/src/context/cache.ts
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import { buildContextPrompt, type ContextResult } from "./builder"
|
|
2
|
-
|
|
3
|
-
export type { ContextResult }
|
|
4
|
-
|
|
5
|
-
export async function getContext(
|
|
6
|
-
workspaceRoot: string,
|
|
7
|
-
cwd: string,
|
|
8
|
-
options?: { useCache?: boolean }
|
|
9
|
-
): Promise<ContextResult> {
|
|
10
|
-
return buildContextPrompt(workspaceRoot, cwd, options)
|
|
11
|
-
}
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
import { execSync } from "child_process";
|
|
2
|
-
import { join } from "path";
|
|
3
|
-
|
|
4
|
-
export interface GitContext {
|
|
5
|
-
available: boolean;
|
|
6
|
-
repoRoot: string | null;
|
|
7
|
-
branch: string | null;
|
|
8
|
-
recentCommit: string | null;
|
|
9
|
-
status: string | null;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export function collectGitContext(workspaceRoot: string): GitContext {
|
|
13
|
-
try {
|
|
14
|
-
const gitDir = execSync("git rev-parse --show-toplevel 2>/dev/null", {
|
|
15
|
-
cwd: workspaceRoot,
|
|
16
|
-
encoding: "utf-8",
|
|
17
|
-
timeout: 5000,
|
|
18
|
-
}).trim();
|
|
19
|
-
|
|
20
|
-
if (!gitDir) {
|
|
21
|
-
return { available: false, repoRoot: null, branch: null, recentCommit: null, status: null };
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
const branch = execSync("git rev-parse --abbrev-ref HEAD 2>/dev/null", {
|
|
25
|
-
cwd: gitDir,
|
|
26
|
-
encoding: "utf-8",
|
|
27
|
-
timeout: 5000,
|
|
28
|
-
}).trim();
|
|
29
|
-
|
|
30
|
-
const recentCommit = execSync("git log -1 --oneline 2>/dev/null", {
|
|
31
|
-
cwd: gitDir,
|
|
32
|
-
encoding: "utf-8",
|
|
33
|
-
timeout: 5000,
|
|
34
|
-
}).trim();
|
|
35
|
-
|
|
36
|
-
let status: string | null = null;
|
|
37
|
-
try {
|
|
38
|
-
const raw = execSync("git status --short 2>/dev/null", {
|
|
39
|
-
cwd: gitDir,
|
|
40
|
-
encoding: "utf-8",
|
|
41
|
-
timeout: 5000,
|
|
42
|
-
}).trim();
|
|
43
|
-
if (raw) {
|
|
44
|
-
const lines = raw.split("\n").slice(0, 30);
|
|
45
|
-
status = lines.join("\n");
|
|
46
|
-
if (raw.split("\n").length > 30) {
|
|
47
|
-
status += `\n... ${raw.split("\n").length - 30} more files`;
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
} catch {
|
|
51
|
-
// status unavailable, non-critical
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
return { available: true, repoRoot: gitDir, branch, recentCommit, status };
|
|
55
|
-
} catch {
|
|
56
|
-
return { available: false, repoRoot: null, branch: null, recentCommit: null, status: null };
|
|
57
|
-
}
|
|
58
|
-
}
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
import * as fs from "fs";
|
|
2
|
-
import * as path from "path";
|
|
3
|
-
|
|
4
|
-
export interface MarkdownFile {
|
|
5
|
-
path: string;
|
|
6
|
-
content: string;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
const FILENAMES = ["LLMTUNE.md", "CLAUDE.md", ".llmtune", ".claude"];
|
|
10
|
-
|
|
11
|
-
export function loadProjectInstructions(
|
|
12
|
-
workspaceRoot: string,
|
|
13
|
-
cwd: string
|
|
14
|
-
): MarkdownFile[] {
|
|
15
|
-
const files: MarkdownFile[] = [];
|
|
16
|
-
const seen = new Set<string>();
|
|
17
|
-
|
|
18
|
-
const dirsToCheck = [cwd];
|
|
19
|
-
let current = cwd;
|
|
20
|
-
while (current !== workspaceRoot && current !== path.dirname(current)) {
|
|
21
|
-
current = path.dirname(current);
|
|
22
|
-
dirsToCheck.push(current);
|
|
23
|
-
}
|
|
24
|
-
dirsToCheck.push(workspaceRoot);
|
|
25
|
-
|
|
26
|
-
for (const dir of dirsToCheck) {
|
|
27
|
-
for (const filename of FILENAMES) {
|
|
28
|
-
const filePath = path.join(dir, filename);
|
|
29
|
-
const resolved = path.resolve(filePath);
|
|
30
|
-
if (seen.has(resolved)) continue;
|
|
31
|
-
|
|
32
|
-
try {
|
|
33
|
-
const stat = fs.statSync(filePath);
|
|
34
|
-
if (stat.isFile()) {
|
|
35
|
-
const content = fs.readFileSync(filePath, "utf-8");
|
|
36
|
-
if (content.trim()) {
|
|
37
|
-
seen.add(resolved);
|
|
38
|
-
files.push({ path: resolved, content: content.trim() });
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
} catch {
|
|
42
|
-
// file doesn't exist, skip
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
return files;
|
|
48
|
-
}
|
package/src/context/workspace.ts
DELETED
|
@@ -1,139 +0,0 @@
|
|
|
1
|
-
import { readdir, stat } from "fs/promises";
|
|
2
|
-
import { join, relative, extname } from "path";
|
|
3
|
-
|
|
4
|
-
export interface WorkspaceSnapshot {
|
|
5
|
-
workspace_root: string;
|
|
6
|
-
current_directory: string;
|
|
7
|
-
top_level_entries: string[];
|
|
8
|
-
key_files: string[];
|
|
9
|
-
total_files: number;
|
|
10
|
-
file_type_counts: Record<string, number>;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
const KEY_FILE_NAMES = new Set([
|
|
14
|
-
"package.json",
|
|
15
|
-
"tsconfig.json",
|
|
16
|
-
"Cargo.toml",
|
|
17
|
-
"go.mod",
|
|
18
|
-
"pyproject.toml",
|
|
19
|
-
"requirements.txt",
|
|
20
|
-
"Makefile",
|
|
21
|
-
"Dockerfile",
|
|
22
|
-
"docker-compose.yml",
|
|
23
|
-
"README.md",
|
|
24
|
-
"CLAUDE.md",
|
|
25
|
-
"LLMTUNE.md",
|
|
26
|
-
".env.example",
|
|
27
|
-
".gitignore",
|
|
28
|
-
"prisma",
|
|
29
|
-
"schema.prisma",
|
|
30
|
-
]);
|
|
31
|
-
|
|
32
|
-
const IGNORED_DIRS = new Set([
|
|
33
|
-
"node_modules",
|
|
34
|
-
".git",
|
|
35
|
-
"__pycache__",
|
|
36
|
-
".next",
|
|
37
|
-
"dist",
|
|
38
|
-
"build",
|
|
39
|
-
".venv",
|
|
40
|
-
"venv",
|
|
41
|
-
".tox",
|
|
42
|
-
"target",
|
|
43
|
-
".cache",
|
|
44
|
-
".turbo",
|
|
45
|
-
"coverage",
|
|
46
|
-
]);
|
|
47
|
-
|
|
48
|
-
export async function buildWorkspaceSnapshot(
|
|
49
|
-
workspaceRoot: string,
|
|
50
|
-
cwd?: string,
|
|
51
|
-
): Promise<WorkspaceSnapshot> {
|
|
52
|
-
const root = workspaceRoot;
|
|
53
|
-
const current = cwd ?? workspaceRoot;
|
|
54
|
-
|
|
55
|
-
const topEntries = await safeReaddir(root);
|
|
56
|
-
const topLevelEntries = topEntries.map((e) => e.name);
|
|
57
|
-
|
|
58
|
-
const keyFiles: string[] = [];
|
|
59
|
-
const fileTypeCounts: Record<string, number> = {};
|
|
60
|
-
let totalFiles = 0;
|
|
61
|
-
|
|
62
|
-
await walkDir(root, "", async (name, relPath, isDir) => {
|
|
63
|
-
if (isDir) return;
|
|
64
|
-
totalFiles++;
|
|
65
|
-
const ext = extname(name).toLowerCase();
|
|
66
|
-
fileTypeCounts[ext] = (fileTypeCounts[ext] ?? 0) + 1;
|
|
67
|
-
if (KEY_FILE_NAMES.has(name)) {
|
|
68
|
-
keyFiles.push(relative(root, join(root, relPath, name)));
|
|
69
|
-
}
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
return {
|
|
73
|
-
workspace_root: root,
|
|
74
|
-
current_directory: current,
|
|
75
|
-
top_level_entries: topLevelEntries.slice(0, 30),
|
|
76
|
-
key_files: keyFiles.slice(0, 20),
|
|
77
|
-
total_files: totalFiles,
|
|
78
|
-
file_type_counts: fileTypeCounts,
|
|
79
|
-
};
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
export function renderWorkspaceSection(snapshot: WorkspaceSnapshot): string {
|
|
83
|
-
const today = new Date().toISOString().split("T")[0];
|
|
84
|
-
const lines = [
|
|
85
|
-
"## Runtime Context",
|
|
86
|
-
`- Today's date: ${today}`,
|
|
87
|
-
`- Workspace root: ${snapshot.workspace_root}`,
|
|
88
|
-
`- Current directory: ${snapshot.current_directory}`,
|
|
89
|
-
`- Total files: ${snapshot.total_files}`,
|
|
90
|
-
];
|
|
91
|
-
|
|
92
|
-
const topExts = Object.entries(snapshot.file_type_counts)
|
|
93
|
-
.sort((a, b) => b[1] - a[1])
|
|
94
|
-
.slice(0, 5)
|
|
95
|
-
.map(([ext, count]) => `${ext || "none"}: ${count}`);
|
|
96
|
-
if (topExts.length > 0) {
|
|
97
|
-
lines.push(`- Top file types: ${topExts.join(", ")}`);
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
if (snapshot.key_files.length > 0) {
|
|
101
|
-
lines.push(`- Key files: ${snapshot.key_files.join(", ")}`);
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
if (snapshot.top_level_entries.length > 0) {
|
|
105
|
-
lines.push(
|
|
106
|
-
`- Top-level entries: ${snapshot.top_level_entries.slice(0, 15).join(", ")}`,
|
|
107
|
-
);
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
return lines.join("\n");
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
async function walkDir(
|
|
114
|
-
root: string,
|
|
115
|
-
relDir: string,
|
|
116
|
-
visitor: (name: string, relPath: string, isDir: boolean) => Promise<void>,
|
|
117
|
-
depth = 0,
|
|
118
|
-
): Promise<void> {
|
|
119
|
-
if (depth > 3) return;
|
|
120
|
-
const entries = await safeReaddir(join(root, relDir));
|
|
121
|
-
for (const entry of entries) {
|
|
122
|
-
if (IGNORED_DIRS.has(entry.name)) continue;
|
|
123
|
-
const relPath = relDir ? `${relDir}/${entry.name}` : entry.name;
|
|
124
|
-
const isDir = entry.isDirectory();
|
|
125
|
-
await visitor(entry.name, relDir, isDir);
|
|
126
|
-
if (isDir && depth < 3) {
|
|
127
|
-
await walkDir(root, relPath, visitor, depth + 1);
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
async function safeReaddir(dirPath: string) {
|
|
133
|
-
try {
|
|
134
|
-
const entries = await readdir(dirPath, { withFileTypes: true });
|
|
135
|
-
return entries;
|
|
136
|
-
} catch {
|
|
137
|
-
return [];
|
|
138
|
-
}
|
|
139
|
-
}
|
package/src/index.ts
DELETED
|
@@ -1,100 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import { Command } from "commander"
|
|
3
|
-
import chalk from "chalk"
|
|
4
|
-
import { loadConfig, isLoggedIn } from "./auth/config"
|
|
5
|
-
import { createClient } from "./auth/client"
|
|
6
|
-
import { startRepl } from "./repl/repl"
|
|
7
|
-
|
|
8
|
-
const program = new Command()
|
|
9
|
-
|
|
10
|
-
program
|
|
11
|
-
.name("llmtune")
|
|
12
|
-
.description("LLMTune CLI - AI coding agent powered by api.llmtune.io")
|
|
13
|
-
.version("0.1.0")
|
|
14
|
-
|
|
15
|
-
program
|
|
16
|
-
.command("login")
|
|
17
|
-
.description("Configure API key and settings")
|
|
18
|
-
.action(async () => {
|
|
19
|
-
const { loginCommand } = await import("./commands/login")
|
|
20
|
-
await loginCommand()
|
|
21
|
-
})
|
|
22
|
-
|
|
23
|
-
program
|
|
24
|
-
.command("chat")
|
|
25
|
-
.description("Start interactive coding session")
|
|
26
|
-
.option("-m, --model <model>", "Model to use")
|
|
27
|
-
.option("--no-stream", "Disable streaming")
|
|
28
|
-
.action(async (options: { model?: string; stream?: boolean }) => {
|
|
29
|
-
if (!isLoggedIn()) {
|
|
30
|
-
console.log(chalk.red('Not logged in. Run "llmtune login" first.'))
|
|
31
|
-
process.exit(1)
|
|
32
|
-
}
|
|
33
|
-
const config = loadConfig()
|
|
34
|
-
const client = createClient()
|
|
35
|
-
const model = options.model ?? (config.defaultModel as string) ?? "z-ai/GLM-5.1"
|
|
36
|
-
const apiBase = (config.apiBase as string) ?? "https://api.llmtune.io/api/agent/v1"
|
|
37
|
-
|
|
38
|
-
console.log(chalk.cyan("\nLLMTune CLI"))
|
|
39
|
-
console.log(chalk.dim(`Connected to ${apiBase}`))
|
|
40
|
-
console.log(chalk.dim(`Model: ${model}`))
|
|
41
|
-
console.log(chalk.dim("Type /help for commands, /exit to quit\n"))
|
|
42
|
-
|
|
43
|
-
await startRepl({ client, model, stream: options.stream !== false })
|
|
44
|
-
})
|
|
45
|
-
|
|
46
|
-
program
|
|
47
|
-
.command("models")
|
|
48
|
-
.description("List available models")
|
|
49
|
-
.action(async () => {
|
|
50
|
-
const { modelsCommand } = await import("./commands/models")
|
|
51
|
-
await modelsCommand()
|
|
52
|
-
})
|
|
53
|
-
|
|
54
|
-
program
|
|
55
|
-
.command("config")
|
|
56
|
-
.description("Show current configuration")
|
|
57
|
-
.action(async () => {
|
|
58
|
-
const { showConfig } = await import("./commands/config")
|
|
59
|
-
showConfig()
|
|
60
|
-
})
|
|
61
|
-
|
|
62
|
-
// Skills marketplace commands
|
|
63
|
-
const skillsCmd = program.command("skills").description("Manage skills (list, install, publish, sign)")
|
|
64
|
-
|
|
65
|
-
skillsCmd
|
|
66
|
-
.command("list")
|
|
67
|
-
.description("List available skills from marketplace")
|
|
68
|
-
.action(async () => {
|
|
69
|
-
const { listSkillsCommand } = await import("./commands/marketplace")
|
|
70
|
-
await listSkillsCommand()
|
|
71
|
-
})
|
|
72
|
-
|
|
73
|
-
skillsCmd
|
|
74
|
-
.command("install")
|
|
75
|
-
.description("Install a skill from the marketplace")
|
|
76
|
-
.argument("<name>", "Skill name to install")
|
|
77
|
-
.action(async (name: string) => {
|
|
78
|
-
const { installSkillCommand } = await import("./commands/marketplace")
|
|
79
|
-
await installSkillCommand(name)
|
|
80
|
-
})
|
|
81
|
-
|
|
82
|
-
skillsCmd
|
|
83
|
-
.command("publish")
|
|
84
|
-
.description("Publish a local skill to the marketplace")
|
|
85
|
-
.argument("<path>", "Path to skill directory")
|
|
86
|
-
.action(async (skillPath: string) => {
|
|
87
|
-
const { publishSkillCommand } = await import("./commands/marketplace")
|
|
88
|
-
await publishSkillCommand(skillPath)
|
|
89
|
-
})
|
|
90
|
-
|
|
91
|
-
skillsCmd
|
|
92
|
-
.command("sign")
|
|
93
|
-
.description("Cryptographically sign a skill")
|
|
94
|
-
.argument("<path>", "Path to skill directory")
|
|
95
|
-
.action(async (skillPath: string) => {
|
|
96
|
-
const { signSkillCommand } = await import("./commands/marketplace")
|
|
97
|
-
await signSkillCommand(skillPath)
|
|
98
|
-
})
|
|
99
|
-
|
|
100
|
-
program.parse()
|
|
@@ -1,118 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Skill marketplace client.
|
|
3
|
-
* Interacts with the infra's skill registry API to list, search, install, and publish skills.
|
|
4
|
-
*/
|
|
5
|
-
import type { AppConfig } from "../auth/config"
|
|
6
|
-
|
|
7
|
-
export interface MarketplaceSkill {
|
|
8
|
-
name: string
|
|
9
|
-
description: string
|
|
10
|
-
trustLevel: "local" | "community" | "verified" | "signed"
|
|
11
|
-
allowedTools: string[]
|
|
12
|
-
author?: string
|
|
13
|
-
version?: string
|
|
14
|
-
installs?: number
|
|
15
|
-
rating?: number
|
|
16
|
-
tags?: string[]
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export interface SearchResult {
|
|
20
|
-
skills: MarketplaceSkill[]
|
|
21
|
-
total: number
|
|
22
|
-
page: number
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
function getApiBase(config: AppConfig): string {
|
|
26
|
-
return String(config.apiBase ?? "https://api.llmtune.io/api/agent/v1").replace(/\/$/, "")
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
function getHeaders(config: AppConfig): Record<string, string> {
|
|
30
|
-
return {
|
|
31
|
-
"Content-Type": "application/json",
|
|
32
|
-
Authorization: `Bearer ${config.apiKey}`,
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* List all skills in the marketplace.
|
|
38
|
-
*/
|
|
39
|
-
export async function listSkills(
|
|
40
|
-
config: AppConfig,
|
|
41
|
-
options?: { search?: string; tag?: string; page?: number; limit?: number },
|
|
42
|
-
): Promise<SearchResult> {
|
|
43
|
-
const base = getApiBase(config)
|
|
44
|
-
const params = new URLSearchParams()
|
|
45
|
-
if (options?.search) params.set("search", options.search)
|
|
46
|
-
if (options?.tag) params.set("tag", options.tag)
|
|
47
|
-
if (options?.page) params.set("page", String(options.page))
|
|
48
|
-
if (options?.limit) params.set("limit", String(options.limit))
|
|
49
|
-
|
|
50
|
-
const url = `${base}/skills?${params.toString()}`
|
|
51
|
-
const response = await fetch(url, { headers: getHeaders(config) })
|
|
52
|
-
|
|
53
|
-
if (!response.ok) {
|
|
54
|
-
throw new Error(`Failed to list skills: HTTP ${response.status}`)
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
return (await response.json()) as SearchResult
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* Get details for a specific skill.
|
|
62
|
-
*/
|
|
63
|
-
export async function getSkillDetails(config: AppConfig, name: string): Promise<MarketplaceSkill> {
|
|
64
|
-
const base = getApiBase(config)
|
|
65
|
-
const url = `${base}/skills/${encodeURIComponent(name)}`
|
|
66
|
-
const response = await fetch(url, { headers: getHeaders(config) })
|
|
67
|
-
|
|
68
|
-
if (!response.ok) {
|
|
69
|
-
if (response.status === 404) throw new Error(`Skill not found: ${name}`)
|
|
70
|
-
throw new Error(`Failed to get skill: HTTP ${response.status}`)
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
return (await response.json()) as MarketplaceSkill
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
/**
|
|
77
|
-
* Install a skill from the marketplace to the local skills directory.
|
|
78
|
-
*/
|
|
79
|
-
export async function installSkill(config: AppConfig, name: string): Promise<string> {
|
|
80
|
-
const base = getApiBase(config)
|
|
81
|
-
const url = `${base}/skills/install`
|
|
82
|
-
const response = await fetch(url, {
|
|
83
|
-
method: "POST",
|
|
84
|
-
headers: getHeaders(config),
|
|
85
|
-
body: JSON.stringify({ skillName: name }),
|
|
86
|
-
})
|
|
87
|
-
|
|
88
|
-
if (!response.ok) {
|
|
89
|
-
const body = await response.text()
|
|
90
|
-
throw new Error(`Failed to install skill: ${body}`)
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
const data = (await response.json()) as { installPath: string; skillName: string }
|
|
94
|
-
return data.installPath
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
/**
|
|
98
|
-
* Publish a local skill to the marketplace.
|
|
99
|
-
*/
|
|
100
|
-
export async function publishSkill(
|
|
101
|
-
config: AppConfig,
|
|
102
|
-
skill: { name: string; description: string; content: string; trustLevel: string; allowedTools: string[] },
|
|
103
|
-
): Promise<{ published: boolean; skillName: string }> {
|
|
104
|
-
const base = getApiBase(config)
|
|
105
|
-
const url = `${base}/skills`
|
|
106
|
-
const response = await fetch(url, {
|
|
107
|
-
method: "POST",
|
|
108
|
-
headers: getHeaders(config),
|
|
109
|
-
body: JSON.stringify(skill),
|
|
110
|
-
})
|
|
111
|
-
|
|
112
|
-
if (!response.ok) {
|
|
113
|
-
const body = await response.text()
|
|
114
|
-
throw new Error(`Failed to publish skill: ${body}`)
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
return (await response.json()) as { published: boolean; skillName: string }
|
|
118
|
-
}
|
package/src/memory/files.ts
DELETED
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
import * as fs from "fs"
|
|
2
|
-
import * as path from "path"
|
|
3
|
-
import { getConfigDir } from "../auth/config"
|
|
4
|
-
|
|
5
|
-
export interface MemoryCategory {
|
|
6
|
-
key: string
|
|
7
|
-
filename: string
|
|
8
|
-
description: string
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export const MEMORY_CATEGORIES: MemoryCategory[] = [
|
|
12
|
-
{ key: "preferences", filename: "preferences.md", description: "User preferences (language, style, conventions)" },
|
|
13
|
-
{ key: "project", filename: "project-notes.md", description: "Project knowledge (architecture, patterns, key decisions)" },
|
|
14
|
-
{ key: "decisions", filename: "decisions.md", description: "Past decisions and rationale" },
|
|
15
|
-
{ key: "architecture", filename: "architecture.md", description: "System architecture notes" },
|
|
16
|
-
]
|
|
17
|
-
|
|
18
|
-
export function getMemoryDir(): string {
|
|
19
|
-
return path.join(getConfigDir(), "memory")
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export function ensureMemoryDir(): void {
|
|
23
|
-
const dir = getMemoryDir()
|
|
24
|
-
if (!fs.existsSync(dir)) {
|
|
25
|
-
fs.mkdirSync(dir, { recursive: true })
|
|
26
|
-
}
|
|
27
|
-
for (const cat of MEMORY_CATEGORIES) {
|
|
28
|
-
const filePath = path.join(dir, cat.filename)
|
|
29
|
-
if (!fs.existsSync(filePath)) {
|
|
30
|
-
fs.writeFileSync(filePath, `# ${cat.description}\n\n`, "utf-8")
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
export function readMemory(key: string): string {
|
|
36
|
-
const cat = MEMORY_CATEGORIES.find((c) => c.key === key)
|
|
37
|
-
if (!cat) return ""
|
|
38
|
-
const filePath = path.join(getMemoryDir(), cat.filename)
|
|
39
|
-
try {
|
|
40
|
-
return fs.readFileSync(filePath, "utf-8").trim()
|
|
41
|
-
} catch {
|
|
42
|
-
return ""
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
export function writeMemory(key: string, content: string): void {
|
|
47
|
-
const cat = MEMORY_CATEGORIES.find((c) => c.key === key)
|
|
48
|
-
if (!cat) return
|
|
49
|
-
ensureMemoryDir()
|
|
50
|
-
const filePath = path.join(getMemoryDir(), cat.filename)
|
|
51
|
-
fs.writeFileSync(filePath, content, "utf-8")
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
export function appendMemory(key: string, line: string): void {
|
|
55
|
-
const current = readMemory(key)
|
|
56
|
-
writeMemory(key, current + "\n" + line)
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
export function readAllMemory(): Record<string, string> {
|
|
60
|
-
const result: Record<string, string> = {}
|
|
61
|
-
for (const cat of MEMORY_CATEGORIES) {
|
|
62
|
-
const content = readMemory(cat.key)
|
|
63
|
-
if (content) result[cat.key] = content
|
|
64
|
-
}
|
|
65
|
-
return result
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
export function buildMemoryPrompt(): string {
|
|
69
|
-
const memories = readAllMemory()
|
|
70
|
-
const entries = Object.entries(memories)
|
|
71
|
-
if (entries.length === 0) return ""
|
|
72
|
-
const lines = ["## User Memory"]
|
|
73
|
-
for (const [key, content] of entries) {
|
|
74
|
-
const cat = MEMORY_CATEGORIES.find((c) => c.key === key)
|
|
75
|
-
if (cat && content.trim()) {
|
|
76
|
-
lines.push(`### ${cat.description}`)
|
|
77
|
-
lines.push(content)
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
return lines.join("\n")
|
|
81
|
-
}
|