@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
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.compactConversation = compactConversation;
|
|
37
|
+
exports.uncompactConversation = uncompactConversation;
|
|
38
|
+
const tokens_1 = require("../utils/tokens");
|
|
39
|
+
const fs = __importStar(require("fs"));
|
|
40
|
+
const path = __importStar(require("path"));
|
|
41
|
+
const os = __importStar(require("os"));
|
|
42
|
+
const COMPACT_SYSTEM_PROMPT = `CRITICAL: Respond with TEXT ONLY. Do NOT call any tools.
|
|
43
|
+
|
|
44
|
+
Your task is to create a detailed summary of the conversation so far.
|
|
45
|
+
|
|
46
|
+
Include:
|
|
47
|
+
1. Primary Request and Intent: What the user asked for
|
|
48
|
+
2. Key Technical Concepts: Technologies, frameworks, patterns discussed
|
|
49
|
+
3. Files and Code Sections: Files examined, modified, or created (with key snippets)
|
|
50
|
+
4. Errors and Fixes: Problems encountered and how they were resolved
|
|
51
|
+
5. Problem Solving: What was accomplished
|
|
52
|
+
6. All User Messages: Summarize all non-tool-result user messages
|
|
53
|
+
7. Pending Tasks: Tasks explicitly requested but not yet done
|
|
54
|
+
8. Current Work: What was being worked on immediately before this summary
|
|
55
|
+
|
|
56
|
+
Respond ONLY with plain text. No XML tags. No tool calls.`;
|
|
57
|
+
async function compactConversation(client, model, conversation, sessionsDir) {
|
|
58
|
+
const messages = conversation.messages;
|
|
59
|
+
const preCompactTokens = (0, tokens_1.estimateTokens)(messages.map((m) => (typeof m.content === "string" ? m.content : JSON.stringify(m.content))).join(" "));
|
|
60
|
+
const preCompactCount = messages.length;
|
|
61
|
+
// Save raw history before compacting
|
|
62
|
+
saveRawHistory(conversation, sessionsDir);
|
|
63
|
+
// Build context for summary: last N messages
|
|
64
|
+
const contextMessages = messages.slice(-20);
|
|
65
|
+
const summaryRequestMessages = contextMessages
|
|
66
|
+
.filter((m) => m.role !== "system")
|
|
67
|
+
.map((m) => ({
|
|
68
|
+
role: m.role,
|
|
69
|
+
content: typeof m.content === "string" ? m.content : JSON.stringify(m.content),
|
|
70
|
+
}));
|
|
71
|
+
summaryRequestMessages.push({ role: "user", content: COMPACT_SYSTEM_PROMPT });
|
|
72
|
+
let summary = "";
|
|
73
|
+
try {
|
|
74
|
+
const response = await client.chat.completions.create({
|
|
75
|
+
model,
|
|
76
|
+
messages: summaryRequestMessages,
|
|
77
|
+
max_tokens: 4096,
|
|
78
|
+
temperature: 0,
|
|
79
|
+
});
|
|
80
|
+
summary = response.choices[0]?.message?.content?.trim() ?? "";
|
|
81
|
+
}
|
|
82
|
+
catch {
|
|
83
|
+
summary = buildFallbackSummary(messages);
|
|
84
|
+
}
|
|
85
|
+
if (!summary) {
|
|
86
|
+
summary = buildFallbackSummary(messages);
|
|
87
|
+
}
|
|
88
|
+
// Replace conversation with boundary marker + summary
|
|
89
|
+
const boundaryMsg = {
|
|
90
|
+
role: "system",
|
|
91
|
+
content: `[COMPACT BOUNDARY] Compacted at ${new Date().toISOString()}. ${preCompactCount} messages summarized. Raw history preserved.`,
|
|
92
|
+
};
|
|
93
|
+
const summaryMsg = {
|
|
94
|
+
role: "system",
|
|
95
|
+
content: `## Conversation Summary\n\n${summary}`,
|
|
96
|
+
};
|
|
97
|
+
// Keep system messages + boundary + summary
|
|
98
|
+
const systemMessages = messages.filter((m) => m.role === "system" && m === messages[0]);
|
|
99
|
+
conversation.messages.length = 0;
|
|
100
|
+
conversation.messages.push(...systemMessages, boundaryMsg, summaryMsg);
|
|
101
|
+
const postCompactTokens = (0, tokens_1.estimateTokens)(conversation.messages.map((m) => (typeof m.content === "string" ? m.content : JSON.stringify(m.content))).join(" "));
|
|
102
|
+
const postCompactCount = conversation.messages.length;
|
|
103
|
+
return {
|
|
104
|
+
tokensSaved: Math.max(0, preCompactTokens - postCompactTokens),
|
|
105
|
+
preCompactTokens,
|
|
106
|
+
postCompactTokens,
|
|
107
|
+
preCompactMessages: preCompactCount,
|
|
108
|
+
postCompactMessages: postCompactCount,
|
|
109
|
+
summary,
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
function uncompactConversation(conversation, sessionsDir) {
|
|
113
|
+
const dir = sessionsDir ?? path.join(os.homedir(), ".llmtune", "sessions");
|
|
114
|
+
const rawPath = path.join(dir, `${conversation.id}.raw.json`);
|
|
115
|
+
if (!fs.existsSync(rawPath)) {
|
|
116
|
+
return false;
|
|
117
|
+
}
|
|
118
|
+
try {
|
|
119
|
+
const raw = JSON.parse(fs.readFileSync(rawPath, "utf-8"));
|
|
120
|
+
conversation.messages.length = 0;
|
|
121
|
+
conversation.messages.push(...raw.messages);
|
|
122
|
+
return true;
|
|
123
|
+
}
|
|
124
|
+
catch {
|
|
125
|
+
return false;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
function saveRawHistory(conversation, sessionsDir) {
|
|
129
|
+
const dir = sessionsDir ?? path.join(os.homedir(), ".llmtune", "sessions");
|
|
130
|
+
if (!fs.existsSync(dir)) {
|
|
131
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
132
|
+
}
|
|
133
|
+
const rawPath = path.join(dir, `${conversation.id}.raw.json`);
|
|
134
|
+
if (!fs.existsSync(rawPath)) {
|
|
135
|
+
fs.writeFileSync(rawPath, JSON.stringify({ messages: conversation.messages }, null, 2), "utf-8");
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
function buildFallbackSummary(messages) {
|
|
139
|
+
const userMsgs = messages
|
|
140
|
+
.filter((m) => m.role === "user")
|
|
141
|
+
.map((m) => (typeof m.content === "string" ? m.content.slice(0, 200) : ""))
|
|
142
|
+
.filter(Boolean);
|
|
143
|
+
const toolNames = messages
|
|
144
|
+
.filter((m) => m.role === "assistant" && m.toolCalls)
|
|
145
|
+
.flatMap((m) => m.toolCalls?.map((tc) => tc.function.name) ?? []);
|
|
146
|
+
const parts = [`Conversation had ${messages.length} messages.`];
|
|
147
|
+
if (toolNames.length > 0) {
|
|
148
|
+
const unique = [...new Set(toolNames)];
|
|
149
|
+
parts.push(`Tools used: ${unique.join(", ")}`);
|
|
150
|
+
}
|
|
151
|
+
if (userMsgs.length > 0) {
|
|
152
|
+
parts.push(`Last user message: ${userMsgs[userMsgs.length - 1].slice(0, 150)}`);
|
|
153
|
+
}
|
|
154
|
+
return parts.join("\n");
|
|
155
|
+
}
|
|
156
|
+
//# sourceMappingURL=service.js.map
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { ToolSpec } from "../tools/protocol";
|
|
2
|
+
export interface ContextCategory {
|
|
3
|
+
name: string;
|
|
4
|
+
tokens: number;
|
|
5
|
+
}
|
|
6
|
+
export interface ContextAnalysis {
|
|
7
|
+
categories: ContextCategory[];
|
|
8
|
+
totalTokens: number;
|
|
9
|
+
maxTokens: number;
|
|
10
|
+
percentage: number;
|
|
11
|
+
model: string;
|
|
12
|
+
}
|
|
13
|
+
export declare function getContextWindowForModel(model: string): number;
|
|
14
|
+
export declare function analyzeContextUsage(options: {
|
|
15
|
+
systemPrompt: string;
|
|
16
|
+
toolSpecs: ToolSpec[];
|
|
17
|
+
messages: Array<{
|
|
18
|
+
role: string;
|
|
19
|
+
content: unknown;
|
|
20
|
+
}>;
|
|
21
|
+
skillsContent?: string;
|
|
22
|
+
memoryContent?: string;
|
|
23
|
+
model: string;
|
|
24
|
+
}): ContextAnalysis;
|
|
25
|
+
export declare function formatContextAnalysis(analysis: ContextAnalysis): string;
|
|
26
|
+
//# sourceMappingURL=analyzer.d.ts.map
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getContextWindowForModel = getContextWindowForModel;
|
|
4
|
+
exports.analyzeContextUsage = analyzeContextUsage;
|
|
5
|
+
exports.formatContextAnalysis = formatContextAnalysis;
|
|
6
|
+
const tokens_1 = require("../utils/tokens");
|
|
7
|
+
const MODEL_CONTEXT_WINDOWS = {
|
|
8
|
+
"gpt-4o": 128_000,
|
|
9
|
+
"gpt-4o-mini": 128_000,
|
|
10
|
+
"gpt-4-turbo": 128_000,
|
|
11
|
+
"glm-5.1": 128_000,
|
|
12
|
+
"glm-5-turbo": 128_000,
|
|
13
|
+
"glm-4.7-flash": 128_000,
|
|
14
|
+
"claude-sonnet": 200_000,
|
|
15
|
+
"claude-opus": 200_000,
|
|
16
|
+
"deepseek-r1": 128_000,
|
|
17
|
+
"qwen3": 128_000,
|
|
18
|
+
};
|
|
19
|
+
const DEFAULT_CONTEXT_WINDOW = 128_000;
|
|
20
|
+
function getContextWindowForModel(model) {
|
|
21
|
+
const lower = model.toLowerCase();
|
|
22
|
+
for (const [name, window] of Object.entries(MODEL_CONTEXT_WINDOWS)) {
|
|
23
|
+
if (lower.includes(name))
|
|
24
|
+
return window;
|
|
25
|
+
}
|
|
26
|
+
const kMatch = lower.match(/(\d+)k/);
|
|
27
|
+
if (kMatch)
|
|
28
|
+
return parseInt(kMatch[1], 10) * 1_000;
|
|
29
|
+
if (lower.includes("1m") || lower.includes("million"))
|
|
30
|
+
return 1_000_000;
|
|
31
|
+
return DEFAULT_CONTEXT_WINDOW;
|
|
32
|
+
}
|
|
33
|
+
function analyzeContextUsage(options) {
|
|
34
|
+
const categories = [];
|
|
35
|
+
const systemTokens = (0, tokens_1.estimateTokens)(options.systemPrompt);
|
|
36
|
+
if (systemTokens > 0) {
|
|
37
|
+
categories.push({ name: "System prompt", tokens: systemTokens });
|
|
38
|
+
}
|
|
39
|
+
let toolTokens = 0;
|
|
40
|
+
for (const spec of options.toolSpecs) {
|
|
41
|
+
toolTokens += (0, tokens_1.estimateTokens)(spec.description);
|
|
42
|
+
toolTokens += (0, tokens_1.estimateTokens)(JSON.stringify(spec.inputSchema));
|
|
43
|
+
}
|
|
44
|
+
if (toolTokens > 0) {
|
|
45
|
+
categories.push({ name: "Tool definitions", tokens: toolTokens });
|
|
46
|
+
}
|
|
47
|
+
if (options.skillsContent) {
|
|
48
|
+
const skillsTokens = (0, tokens_1.estimateTokens)(options.skillsContent);
|
|
49
|
+
if (skillsTokens > 0) {
|
|
50
|
+
categories.push({ name: "Skills", tokens: skillsTokens });
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
if (options.memoryContent) {
|
|
54
|
+
const memTokens = (0, tokens_1.estimateTokens)(options.memoryContent);
|
|
55
|
+
if (memTokens > 0) {
|
|
56
|
+
categories.push({ name: "Memory", tokens: memTokens });
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
let messageTokens = 0;
|
|
60
|
+
for (const msg of options.messages) {
|
|
61
|
+
const text = typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content ?? "");
|
|
62
|
+
messageTokens += (0, tokens_1.estimateTokens)(text);
|
|
63
|
+
}
|
|
64
|
+
if (messageTokens > 0) {
|
|
65
|
+
categories.push({ name: "Messages", tokens: messageTokens });
|
|
66
|
+
}
|
|
67
|
+
const totalTokens = categories.reduce((sum, c) => sum + c.tokens, 0);
|
|
68
|
+
const maxTokens = getContextWindowForModel(options.model);
|
|
69
|
+
const percentage = maxTokens > 0 ? Math.round((totalTokens / maxTokens) * 1000) / 10 : 0;
|
|
70
|
+
const freeTokens = Math.max(0, maxTokens - totalTokens);
|
|
71
|
+
categories.push({ name: "Free space", tokens: freeTokens });
|
|
72
|
+
return { categories, totalTokens, maxTokens, percentage, model: options.model };
|
|
73
|
+
}
|
|
74
|
+
function formatContextAnalysis(analysis) {
|
|
75
|
+
const lines = [
|
|
76
|
+
"## Context Usage",
|
|
77
|
+
"",
|
|
78
|
+
`**Model:** ${analysis.model}`,
|
|
79
|
+
`**Tokens:** ${analysis.totalTokens.toLocaleString()} / ${analysis.maxTokens.toLocaleString()} (${analysis.percentage}%)`,
|
|
80
|
+
"",
|
|
81
|
+
];
|
|
82
|
+
const visible = analysis.categories.filter((c) => c.tokens > 0 && c.name !== "Free space");
|
|
83
|
+
if (visible.length > 0) {
|
|
84
|
+
lines.push("| Category | Tokens | Percentage |");
|
|
85
|
+
lines.push("|----------|--------|------------|");
|
|
86
|
+
for (const cat of visible) {
|
|
87
|
+
const pct = analysis.maxTokens > 0 ? ((cat.tokens / analysis.maxTokens) * 100).toFixed(1) : "0.0";
|
|
88
|
+
lines.push(`| ${cat.name} | ${cat.tokens.toLocaleString()} | ${pct}% |`);
|
|
89
|
+
}
|
|
90
|
+
const free = analysis.categories.find((c) => c.name === "Free space");
|
|
91
|
+
if (free && free.tokens > 0) {
|
|
92
|
+
const pct = analysis.maxTokens > 0 ? ((free.tokens / analysis.maxTokens) * 100).toFixed(1) : "0.0";
|
|
93
|
+
lines.push(`| Free space | ${free.tokens.toLocaleString()} | ${pct}% |`);
|
|
94
|
+
}
|
|
95
|
+
lines.push("");
|
|
96
|
+
}
|
|
97
|
+
return lines.join("\n");
|
|
98
|
+
}
|
|
99
|
+
//# sourceMappingURL=analyzer.js.map
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export interface ContextResult {
|
|
2
|
+
prompt: string;
|
|
3
|
+
cacheKey: string;
|
|
4
|
+
cacheHit: boolean;
|
|
5
|
+
sections: string[];
|
|
6
|
+
}
|
|
7
|
+
export declare function computeCacheKey(workspaceRoot: string, cwd: string): string;
|
|
8
|
+
export declare function readCache(cacheKey: string): ContextResult | null;
|
|
9
|
+
export declare function writeCache(cacheKey: string, result: ContextResult): void;
|
|
10
|
+
export declare function buildContextPrompt(workspaceRoot: string, cwd: string, options?: {
|
|
11
|
+
useCache?: boolean;
|
|
12
|
+
}): Promise<ContextResult>;
|
|
13
|
+
//# sourceMappingURL=builder.d.ts.map
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.computeCacheKey = computeCacheKey;
|
|
37
|
+
exports.readCache = readCache;
|
|
38
|
+
exports.writeCache = writeCache;
|
|
39
|
+
exports.buildContextPrompt = buildContextPrompt;
|
|
40
|
+
const path = __importStar(require("path"));
|
|
41
|
+
const crypto = __importStar(require("crypto"));
|
|
42
|
+
const fs = __importStar(require("fs"));
|
|
43
|
+
const git_context_1 = require("./git-context");
|
|
44
|
+
const workspace_1 = require("./workspace");
|
|
45
|
+
const llmtune_md_1 = require("./llmtune-md");
|
|
46
|
+
const CACHE_DIR = () => {
|
|
47
|
+
const base = process.env.LLMTUNE_CACHE_DIR || path.join(process.env.HOME || process.env.USERPROFILE || "~", ".llmtune", "cache");
|
|
48
|
+
return base;
|
|
49
|
+
};
|
|
50
|
+
function getCachePath(cacheKey) {
|
|
51
|
+
return path.join(CACHE_DIR(), `${cacheKey}.json`);
|
|
52
|
+
}
|
|
53
|
+
function computeCacheKey(workspaceRoot, cwd) {
|
|
54
|
+
const git = (0, git_context_1.collectGitContext)(workspaceRoot);
|
|
55
|
+
const base = `${workspaceRoot}:${cwd}`;
|
|
56
|
+
if (git.available && git.recentCommit) {
|
|
57
|
+
return crypto.createHash("sha256").update(`${base}:${git.recentCommit}`).digest("hex").slice(0, 16);
|
|
58
|
+
}
|
|
59
|
+
return crypto.createHash("sha256").update(base).digest("hex").slice(0, 16);
|
|
60
|
+
}
|
|
61
|
+
function readCache(cacheKey) {
|
|
62
|
+
try {
|
|
63
|
+
const cachePath = getCachePath(cacheKey);
|
|
64
|
+
if (fs.existsSync(cachePath)) {
|
|
65
|
+
const stat = fs.statSync(cachePath);
|
|
66
|
+
const ageMs = Date.now() - stat.mtimeMs;
|
|
67
|
+
if (ageMs > 30 * 60 * 1000)
|
|
68
|
+
return null;
|
|
69
|
+
const data = JSON.parse(fs.readFileSync(cachePath, "utf-8"));
|
|
70
|
+
return { ...data, cacheHit: true };
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
catch { }
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
function writeCache(cacheKey, result) {
|
|
77
|
+
try {
|
|
78
|
+
const cacheDir = CACHE_DIR();
|
|
79
|
+
if (!fs.existsSync(cacheDir)) {
|
|
80
|
+
fs.mkdirSync(cacheDir, { recursive: true });
|
|
81
|
+
}
|
|
82
|
+
const cachePath = getCachePath(cacheKey);
|
|
83
|
+
fs.writeFileSync(cachePath, JSON.stringify({ ...result, cacheHit: false }, null, 2));
|
|
84
|
+
}
|
|
85
|
+
catch { }
|
|
86
|
+
}
|
|
87
|
+
async function buildContextPrompt(workspaceRoot, cwd, options) {
|
|
88
|
+
const cacheKey = computeCacheKey(workspaceRoot, cwd);
|
|
89
|
+
if (options?.useCache !== false) {
|
|
90
|
+
const cached = readCache(cacheKey);
|
|
91
|
+
if (cached)
|
|
92
|
+
return cached;
|
|
93
|
+
}
|
|
94
|
+
const sections = [];
|
|
95
|
+
// Workspace section (async)
|
|
96
|
+
const workspace = await (0, workspace_1.buildWorkspaceSnapshot)(workspaceRoot, cwd);
|
|
97
|
+
const workspaceSection = (0, workspace_1.renderWorkspaceSection)(workspace);
|
|
98
|
+
if (workspaceSection)
|
|
99
|
+
sections.push(workspaceSection);
|
|
100
|
+
// Git section
|
|
101
|
+
const git = (0, git_context_1.collectGitContext)(workspaceRoot);
|
|
102
|
+
const gitSection = renderGitSection(git);
|
|
103
|
+
if (gitSection)
|
|
104
|
+
sections.push(gitSection);
|
|
105
|
+
// LLMTUNE.md / CLAUDE.md section
|
|
106
|
+
const mdFiles = (0, llmtune_md_1.loadProjectInstructions)(workspaceRoot, cwd);
|
|
107
|
+
const mdSection = renderMdSection(mdFiles, workspaceRoot);
|
|
108
|
+
if (mdSection)
|
|
109
|
+
sections.push(mdSection);
|
|
110
|
+
const prompt = sections.join("\n\n");
|
|
111
|
+
const result = {
|
|
112
|
+
prompt,
|
|
113
|
+
cacheKey,
|
|
114
|
+
cacheHit: false,
|
|
115
|
+
sections,
|
|
116
|
+
};
|
|
117
|
+
writeCache(cacheKey, result);
|
|
118
|
+
return result;
|
|
119
|
+
}
|
|
120
|
+
function renderGitSection(git) {
|
|
121
|
+
if (!git.available)
|
|
122
|
+
return "";
|
|
123
|
+
const lines = ["## Git Context"];
|
|
124
|
+
if (git.branch)
|
|
125
|
+
lines.push(`- Current branch: ${git.branch}`);
|
|
126
|
+
if (git.recentCommit)
|
|
127
|
+
lines.push(`- Latest commit: ${git.recentCommit}`);
|
|
128
|
+
if (git.status) {
|
|
129
|
+
lines.push("- Git status snapshot:", "```text", git.status.slice(0, 2000), "```");
|
|
130
|
+
}
|
|
131
|
+
return lines.join("\n");
|
|
132
|
+
}
|
|
133
|
+
function renderMdSection(files, workspaceRoot) {
|
|
134
|
+
if (files.length === 0)
|
|
135
|
+
return "";
|
|
136
|
+
const lines = ["## Project Instructions"];
|
|
137
|
+
for (const f of files) {
|
|
138
|
+
const relative = path.relative(workspaceRoot, f.path);
|
|
139
|
+
const label = relative ? `./${relative}` : f.path;
|
|
140
|
+
lines.push(`### ${label}`, "```md", f.content.slice(0, 4000), "```");
|
|
141
|
+
}
|
|
142
|
+
return lines.join("\n");
|
|
143
|
+
}
|
|
144
|
+
//# sourceMappingURL=builder.js.map
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getContext = getContext;
|
|
4
|
+
const builder_1 = require("./builder");
|
|
5
|
+
async function getContext(workspaceRoot, cwd, options) {
|
|
6
|
+
return (0, builder_1.buildContextPrompt)(workspaceRoot, cwd, options);
|
|
7
|
+
}
|
|
8
|
+
//# sourceMappingURL=cache.js.map
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export interface GitContext {
|
|
2
|
+
available: boolean;
|
|
3
|
+
repoRoot: string | null;
|
|
4
|
+
branch: string | null;
|
|
5
|
+
recentCommit: string | null;
|
|
6
|
+
status: string | null;
|
|
7
|
+
}
|
|
8
|
+
export declare function collectGitContext(workspaceRoot: string): GitContext;
|
|
9
|
+
//# sourceMappingURL=git-context.d.ts.map
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.collectGitContext = collectGitContext;
|
|
4
|
+
const child_process_1 = require("child_process");
|
|
5
|
+
function collectGitContext(workspaceRoot) {
|
|
6
|
+
try {
|
|
7
|
+
const gitDir = (0, child_process_1.execSync)("git rev-parse --show-toplevel 2>/dev/null", {
|
|
8
|
+
cwd: workspaceRoot,
|
|
9
|
+
encoding: "utf-8",
|
|
10
|
+
timeout: 5000,
|
|
11
|
+
}).trim();
|
|
12
|
+
if (!gitDir) {
|
|
13
|
+
return { available: false, repoRoot: null, branch: null, recentCommit: null, status: null };
|
|
14
|
+
}
|
|
15
|
+
const branch = (0, child_process_1.execSync)("git rev-parse --abbrev-ref HEAD 2>/dev/null", {
|
|
16
|
+
cwd: gitDir,
|
|
17
|
+
encoding: "utf-8",
|
|
18
|
+
timeout: 5000,
|
|
19
|
+
}).trim();
|
|
20
|
+
const recentCommit = (0, child_process_1.execSync)("git log -1 --oneline 2>/dev/null", {
|
|
21
|
+
cwd: gitDir,
|
|
22
|
+
encoding: "utf-8",
|
|
23
|
+
timeout: 5000,
|
|
24
|
+
}).trim();
|
|
25
|
+
let status = null;
|
|
26
|
+
try {
|
|
27
|
+
const raw = (0, child_process_1.execSync)("git status --short 2>/dev/null", {
|
|
28
|
+
cwd: gitDir,
|
|
29
|
+
encoding: "utf-8",
|
|
30
|
+
timeout: 5000,
|
|
31
|
+
}).trim();
|
|
32
|
+
if (raw) {
|
|
33
|
+
const lines = raw.split("\n").slice(0, 30);
|
|
34
|
+
status = lines.join("\n");
|
|
35
|
+
if (raw.split("\n").length > 30) {
|
|
36
|
+
status += `\n... ${raw.split("\n").length - 30} more files`;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
// status unavailable, non-critical
|
|
42
|
+
}
|
|
43
|
+
return { available: true, repoRoot: gitDir, branch, recentCommit, status };
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
return { available: false, repoRoot: null, branch: null, recentCommit: null, status: null };
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=git-context.js.map
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.loadProjectInstructions = loadProjectInstructions;
|
|
37
|
+
const fs = __importStar(require("fs"));
|
|
38
|
+
const path = __importStar(require("path"));
|
|
39
|
+
const FILENAMES = ["LLMTUNE.md", "CLAUDE.md", ".llmtune", ".claude"];
|
|
40
|
+
function loadProjectInstructions(workspaceRoot, cwd) {
|
|
41
|
+
const files = [];
|
|
42
|
+
const seen = new Set();
|
|
43
|
+
const dirsToCheck = [cwd];
|
|
44
|
+
let current = cwd;
|
|
45
|
+
while (current !== workspaceRoot && current !== path.dirname(current)) {
|
|
46
|
+
current = path.dirname(current);
|
|
47
|
+
dirsToCheck.push(current);
|
|
48
|
+
}
|
|
49
|
+
dirsToCheck.push(workspaceRoot);
|
|
50
|
+
for (const dir of dirsToCheck) {
|
|
51
|
+
for (const filename of FILENAMES) {
|
|
52
|
+
const filePath = path.join(dir, filename);
|
|
53
|
+
const resolved = path.resolve(filePath);
|
|
54
|
+
if (seen.has(resolved))
|
|
55
|
+
continue;
|
|
56
|
+
try {
|
|
57
|
+
const stat = fs.statSync(filePath);
|
|
58
|
+
if (stat.isFile()) {
|
|
59
|
+
const content = fs.readFileSync(filePath, "utf-8");
|
|
60
|
+
if (content.trim()) {
|
|
61
|
+
seen.add(resolved);
|
|
62
|
+
files.push({ path: resolved, content: content.trim() });
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
// file doesn't exist, skip
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return files;
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=llmtune-md.js.map
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export interface WorkspaceSnapshot {
|
|
2
|
+
workspace_root: string;
|
|
3
|
+
current_directory: string;
|
|
4
|
+
top_level_entries: string[];
|
|
5
|
+
key_files: string[];
|
|
6
|
+
total_files: number;
|
|
7
|
+
file_type_counts: Record<string, number>;
|
|
8
|
+
}
|
|
9
|
+
export declare function buildWorkspaceSnapshot(workspaceRoot: string, cwd?: string): Promise<WorkspaceSnapshot>;
|
|
10
|
+
export declare function renderWorkspaceSection(snapshot: WorkspaceSnapshot): string;
|
|
11
|
+
//# sourceMappingURL=workspace.d.ts.map
|