cliskill 1.0.9 → 1.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/dist/bootstrap/cli.js
CHANGED
|
@@ -5540,6 +5540,32 @@ var EnhancedMemoryStore = class {
|
|
|
5540
5540
|
}
|
|
5541
5541
|
return result;
|
|
5542
5542
|
}
|
|
5543
|
+
/**
|
|
5544
|
+
* Get a formatted summary of all memories for injection into system prompt.
|
|
5545
|
+
* Returns empty string if no memories stored.
|
|
5546
|
+
*/
|
|
5547
|
+
getSummary() {
|
|
5548
|
+
if (this.entries.size === 0) return "";
|
|
5549
|
+
const sections = [];
|
|
5550
|
+
for (const [topic, entries] of this.entries) {
|
|
5551
|
+
const latest = entries[entries.length - 1];
|
|
5552
|
+
if (!latest) continue;
|
|
5553
|
+
const age = Date.now() - latest.timestamp;
|
|
5554
|
+
const ageDays = Math.floor(age / (1e3 * 60 * 60 * 24));
|
|
5555
|
+
const ageStr = ageDays === 0 ? "today" : ageDays === 1 ? "yesterday" : `${ageDays}d ago`;
|
|
5556
|
+
sections.push(`- **${topic}**: ${latest.content} (${ageStr})`);
|
|
5557
|
+
}
|
|
5558
|
+
if (sections.length === 0) return "";
|
|
5559
|
+
return [
|
|
5560
|
+
"<global-memory>",
|
|
5561
|
+
"The following memories were saved in previous sessions and persist across conversations:",
|
|
5562
|
+
"",
|
|
5563
|
+
...sections,
|
|
5564
|
+
"",
|
|
5565
|
+
"Use the memory tool to save new facts or search/recall existing memories.",
|
|
5566
|
+
"</global-memory>"
|
|
5567
|
+
].join("\n");
|
|
5568
|
+
}
|
|
5543
5569
|
async persist(topic) {
|
|
5544
5570
|
const entries = this.entries.get(topic);
|
|
5545
5571
|
if (!entries || entries.length === 0) {
|
|
@@ -5555,6 +5581,25 @@ var EnhancedMemoryStore = class {
|
|
|
5555
5581
|
}
|
|
5556
5582
|
};
|
|
5557
5583
|
|
|
5584
|
+
// src/memory/global-memory.ts
|
|
5585
|
+
var cachedSummary = null;
|
|
5586
|
+
async function loadGlobalMemorySummary() {
|
|
5587
|
+
if (cachedSummary !== null) return cachedSummary;
|
|
5588
|
+
try {
|
|
5589
|
+
const dir = getGlobalMemoryDir();
|
|
5590
|
+
const store = new EnhancedMemoryStore(dir);
|
|
5591
|
+
await store.init();
|
|
5592
|
+
cachedSummary = store.getSummary();
|
|
5593
|
+
return cachedSummary;
|
|
5594
|
+
} catch {
|
|
5595
|
+
cachedSummary = "";
|
|
5596
|
+
return "";
|
|
5597
|
+
}
|
|
5598
|
+
}
|
|
5599
|
+
function invalidateGlobalMemoryCache() {
|
|
5600
|
+
cachedSummary = null;
|
|
5601
|
+
}
|
|
5602
|
+
|
|
5558
5603
|
// src/tools/builtins/memory-tool.ts
|
|
5559
5604
|
var memoryInputSchema = z19.object({
|
|
5560
5605
|
action: z19.enum(["save", "recall", "search", "list", "delete"]).describe(
|
|
@@ -5590,6 +5635,7 @@ var MemoryTool = class extends BaseTool {
|
|
|
5590
5635
|
return "Error: topic and content are required for save action";
|
|
5591
5636
|
}
|
|
5592
5637
|
await store.addMemory(input.topic, input.content, input.tags ?? [], "agent");
|
|
5638
|
+
invalidateGlobalMemoryCache();
|
|
5593
5639
|
return `\u2705 Saved to "${input.topic}": ${input.content.slice(0, 100)}${input.content.length > 100 ? "..." : ""}`;
|
|
5594
5640
|
}
|
|
5595
5641
|
case "recall": {
|
|
@@ -5637,6 +5683,7 @@ var MemoryTool = class extends BaseTool {
|
|
|
5637
5683
|
entry.entry.lastAccessed = 0;
|
|
5638
5684
|
}
|
|
5639
5685
|
await store.prune();
|
|
5686
|
+
invalidateGlobalMemoryCache();
|
|
5640
5687
|
return `\u{1F5D1}\uFE0F Deleted ${topicEntries.length} entries from "${input.topic}"`;
|
|
5641
5688
|
}
|
|
5642
5689
|
default:
|
|
@@ -6401,6 +6448,147 @@ ${m.content}`;
|
|
|
6401
6448
|
].join("\n");
|
|
6402
6449
|
}
|
|
6403
6450
|
|
|
6451
|
+
// src/services/session-recovery.ts
|
|
6452
|
+
import { readFile as readFile9, readdir as readdir5 } from "fs/promises";
|
|
6453
|
+
import { join as join10 } from "path";
|
|
6454
|
+
import { existsSync as existsSync5 } from "fs";
|
|
6455
|
+
async function recoverSession(filePath) {
|
|
6456
|
+
const raw = await readFile9(filePath, "utf-8");
|
|
6457
|
+
const lines = raw.split("\n").filter((line) => line.trim().length > 0);
|
|
6458
|
+
const entries = [];
|
|
6459
|
+
for (const line of lines) {
|
|
6460
|
+
try {
|
|
6461
|
+
const entry = JSON.parse(line);
|
|
6462
|
+
if (entry.type && entry.content !== void 0) {
|
|
6463
|
+
entries.push(entry);
|
|
6464
|
+
}
|
|
6465
|
+
} catch {
|
|
6466
|
+
}
|
|
6467
|
+
}
|
|
6468
|
+
const messages = reconstructMessages(entries);
|
|
6469
|
+
const firstEntry = entries[0];
|
|
6470
|
+
const timestamp = firstEntry?.timestamp ?? 0;
|
|
6471
|
+
return {
|
|
6472
|
+
sessionId: filePath,
|
|
6473
|
+
filePath,
|
|
6474
|
+
timestamp,
|
|
6475
|
+
messages,
|
|
6476
|
+
entryCount: entries.length
|
|
6477
|
+
};
|
|
6478
|
+
}
|
|
6479
|
+
async function listSessions(limit = 20) {
|
|
6480
|
+
const dir = getSessionsDir();
|
|
6481
|
+
if (!existsSync5(dir)) return [];
|
|
6482
|
+
const files = await readdir5(dir);
|
|
6483
|
+
const sessionFiles = files.filter((f) => f.startsWith("session-") && f.endsWith(".jsonl"));
|
|
6484
|
+
const sessions = [];
|
|
6485
|
+
for (const fileName of sessionFiles) {
|
|
6486
|
+
const filePath = join10(dir, fileName);
|
|
6487
|
+
try {
|
|
6488
|
+
const raw = await readFile9(filePath, "utf-8");
|
|
6489
|
+
const lines = raw.split("\n").filter((line) => line.trim().length > 0);
|
|
6490
|
+
let lastActivity = 0;
|
|
6491
|
+
let entryCount = 0;
|
|
6492
|
+
for (const line of lines) {
|
|
6493
|
+
try {
|
|
6494
|
+
const entry = JSON.parse(line);
|
|
6495
|
+
if (entry.timestamp && entry.timestamp > lastActivity) {
|
|
6496
|
+
lastActivity = entry.timestamp;
|
|
6497
|
+
}
|
|
6498
|
+
entryCount++;
|
|
6499
|
+
} catch {
|
|
6500
|
+
}
|
|
6501
|
+
}
|
|
6502
|
+
const dateStr = fileName.replace("session-", "").replace(".jsonl", "");
|
|
6503
|
+
const fileTimestamp = Date.parse(dateStr) || lastActivity;
|
|
6504
|
+
sessions.push({
|
|
6505
|
+
fileName,
|
|
6506
|
+
filePath,
|
|
6507
|
+
timestamp: fileTimestamp,
|
|
6508
|
+
entryCount,
|
|
6509
|
+
lastActivity
|
|
6510
|
+
});
|
|
6511
|
+
} catch {
|
|
6512
|
+
}
|
|
6513
|
+
}
|
|
6514
|
+
sessions.sort((a, b) => b.lastActivity - a.lastActivity);
|
|
6515
|
+
return sessions.slice(0, limit);
|
|
6516
|
+
}
|
|
6517
|
+
async function findLatestSession() {
|
|
6518
|
+
const sessions = await listSessions(1);
|
|
6519
|
+
return sessions[0] ?? null;
|
|
6520
|
+
}
|
|
6521
|
+
async function getLastSessionSummary() {
|
|
6522
|
+
try {
|
|
6523
|
+
const latest = await findLatestSession();
|
|
6524
|
+
if (!latest) return "";
|
|
6525
|
+
const raw = await readFile9(latest.filePath, "utf-8");
|
|
6526
|
+
const lines = raw.split("\n").filter((line) => line.trim().length > 0);
|
|
6527
|
+
const userMessages = [];
|
|
6528
|
+
const assistantTopics = [];
|
|
6529
|
+
for (const line of lines) {
|
|
6530
|
+
try {
|
|
6531
|
+
const entry = JSON.parse(line);
|
|
6532
|
+
if (entry.type === "user" && entry.content.trim()) {
|
|
6533
|
+
userMessages.push(entry.content.trim());
|
|
6534
|
+
} else if (entry.type === "assistant" && entry.content.trim()) {
|
|
6535
|
+
const preview = entry.content.trim().slice(0, 120);
|
|
6536
|
+
assistantTopics.push(preview);
|
|
6537
|
+
}
|
|
6538
|
+
} catch {
|
|
6539
|
+
}
|
|
6540
|
+
}
|
|
6541
|
+
if (userMessages.length === 0) return "";
|
|
6542
|
+
const date = new Date(latest.lastActivity).toLocaleString();
|
|
6543
|
+
const lastUserMsg = userMessages[userMessages.length - 1];
|
|
6544
|
+
const lastAssistant = assistantTopics.length > 0 ? assistantTopics[assistantTopics.length - 1] : "N/A";
|
|
6545
|
+
return [
|
|
6546
|
+
"<last-session-context>",
|
|
6547
|
+
`Previous session (${date}, ${lines.length} entries):`,
|
|
6548
|
+
` Last user message: ${lastUserMsg.slice(0, 200)}`,
|
|
6549
|
+
` Last assistant response: ${lastAssistant.slice(0, 200)}`,
|
|
6550
|
+
` Total user messages in session: ${userMessages.length}`,
|
|
6551
|
+
"",
|
|
6552
|
+
"Use the memory tool to recall specific details from past interactions.",
|
|
6553
|
+
"</last-session-context>"
|
|
6554
|
+
].join("\n");
|
|
6555
|
+
} catch {
|
|
6556
|
+
return "";
|
|
6557
|
+
}
|
|
6558
|
+
}
|
|
6559
|
+
function reconstructMessages(entries) {
|
|
6560
|
+
const messages = [];
|
|
6561
|
+
let i = 0;
|
|
6562
|
+
while (i < entries.length) {
|
|
6563
|
+
const entry = entries[i];
|
|
6564
|
+
if (entry.type === "user") {
|
|
6565
|
+
const content = [{ type: "text", text: entry.content }];
|
|
6566
|
+
messages.push({ role: "user", content });
|
|
6567
|
+
i++;
|
|
6568
|
+
} else if (entry.type === "assistant") {
|
|
6569
|
+
const content = [{ type: "text", text: entry.content }];
|
|
6570
|
+
messages.push({ role: "assistant", content });
|
|
6571
|
+
i++;
|
|
6572
|
+
} else if (entry.type === "tool_use") {
|
|
6573
|
+
const lastMsg = messages[messages.length - 1];
|
|
6574
|
+
if (lastMsg && lastMsg.role === "assistant") {
|
|
6575
|
+
}
|
|
6576
|
+
i++;
|
|
6577
|
+
} else if (entry.type === "tool_result") {
|
|
6578
|
+
const content = [{
|
|
6579
|
+
type: "tool_result",
|
|
6580
|
+
toolUseId: `recovered-${i}`,
|
|
6581
|
+
content: entry.content
|
|
6582
|
+
}];
|
|
6583
|
+
messages.push({ role: "user", content });
|
|
6584
|
+
i++;
|
|
6585
|
+
} else {
|
|
6586
|
+
i++;
|
|
6587
|
+
}
|
|
6588
|
+
}
|
|
6589
|
+
return messages;
|
|
6590
|
+
}
|
|
6591
|
+
|
|
6404
6592
|
// src/core/loop.ts
|
|
6405
6593
|
var MAX_TURNS = 50;
|
|
6406
6594
|
var MAX_CONSECUTIVE_TOOL_ERRORS = 3;
|
|
@@ -6463,7 +6651,9 @@ ${result.summary}` }]
|
|
|
6463
6651
|
});
|
|
6464
6652
|
const toolDefs = toolRegistry.getToolDefinitions();
|
|
6465
6653
|
const projectMemory = await loadProjectMemoryPrompt(cwd2);
|
|
6466
|
-
const
|
|
6654
|
+
const globalMemory = await loadGlobalMemorySummary();
|
|
6655
|
+
const lastSessionSummary = await getLastSessionSummary();
|
|
6656
|
+
const effectiveSystemPrompt = systemPrompt + fastMode.getSystemPromptModifier() + (projectMemory ? "\n\n" + projectMemory : "") + (globalMemory ? "\n\n" + globalMemory : "") + (lastSessionSummary ? "\n\n" + lastSessionSummary : "");
|
|
6467
6657
|
const toolContext = {
|
|
6468
6658
|
cwd: cwd2,
|
|
6469
6659
|
abortSignal,
|
|
@@ -6531,12 +6721,33 @@ ${result.summary}` }]
|
|
|
6531
6721
|
inputTokens: state.totalInputTokens,
|
|
6532
6722
|
outputTokens: state.totalOutputTokens
|
|
6533
6723
|
};
|
|
6724
|
+
const currentContextTokens = parser.usage.inputTokens;
|
|
6534
6725
|
const budget = checkTokenBudget(
|
|
6535
|
-
|
|
6536
|
-
|
|
6726
|
+
currentContextTokens,
|
|
6727
|
+
0,
|
|
6728
|
+
// output already included in next turn's input
|
|
6537
6729
|
contextWindow.getMaxTokens()
|
|
6538
6730
|
);
|
|
6539
6731
|
if (budget.shouldBlock) {
|
|
6732
|
+
const compactionInput = state.messages.map((m) => ({
|
|
6733
|
+
role: m.role,
|
|
6734
|
+
content: formatContentBlocks2(m.content)
|
|
6735
|
+
}));
|
|
6736
|
+
try {
|
|
6737
|
+
const compactResult = await compactor.compact(compactionInput, effectiveSystemPrompt);
|
|
6738
|
+
if (compactResult.removedMessages > 0) {
|
|
6739
|
+
const summaryMsg = {
|
|
6740
|
+
role: "user",
|
|
6741
|
+
content: [{ type: "text", text: `[Previous conversation summary]
|
|
6742
|
+
${compactResult.summary}` }]
|
|
6743
|
+
};
|
|
6744
|
+
const keptStart = state.messages.length - compactResult.keptMessages;
|
|
6745
|
+
state.messages = [summaryMsg, ...state.messages.slice(keptStart)];
|
|
6746
|
+
yield { type: "context_compacted", summary: compactResult.summary, removedCount: compactResult.removedMessages };
|
|
6747
|
+
continue;
|
|
6748
|
+
}
|
|
6749
|
+
} catch {
|
|
6750
|
+
}
|
|
6540
6751
|
yield {
|
|
6541
6752
|
type: "error",
|
|
6542
6753
|
error: new Error(
|
|
@@ -6928,7 +7139,7 @@ function createMissingToolResults(toolCalls, errorMessage) {
|
|
|
6928
7139
|
import { useState, useEffect, useCallback, useRef, useMemo, memo } from "react";
|
|
6929
7140
|
import { Box, Text, render, useInput, useApp, useStdout } from "ink";
|
|
6930
7141
|
import { readdirSync } from "fs";
|
|
6931
|
-
import { join as
|
|
7142
|
+
import { join as join11, basename as basename2, dirname as dirname3 } from "path";
|
|
6932
7143
|
import { exec as exec2, execSync as execSync2 } from "child_process";
|
|
6933
7144
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
6934
7145
|
var C = {
|
|
@@ -7522,10 +7733,10 @@ function InkApp({ model, toolCount, onSubmit, onCancel }) {
|
|
|
7522
7733
|
const selected = currentFiles[selectedIdx];
|
|
7523
7734
|
if (selected) {
|
|
7524
7735
|
if (selected.isDir) {
|
|
7525
|
-
setFileCwd(
|
|
7736
|
+
setFileCwd(join11(fileCwd, selected.name));
|
|
7526
7737
|
setSelectedIdx(0);
|
|
7527
7738
|
} else {
|
|
7528
|
-
openInEditor(
|
|
7739
|
+
openInEditor(join11(fileCwd, selected.name));
|
|
7529
7740
|
}
|
|
7530
7741
|
}
|
|
7531
7742
|
return;
|
|
@@ -8123,109 +8334,6 @@ async function registerMCPTools(mcpManager, registerFn) {
|
|
|
8123
8334
|
return registered;
|
|
8124
8335
|
}
|
|
8125
8336
|
|
|
8126
|
-
// src/services/session-recovery.ts
|
|
8127
|
-
import { readFile as readFile9, readdir as readdir5 } from "fs/promises";
|
|
8128
|
-
import { join as join11 } from "path";
|
|
8129
|
-
import { existsSync as existsSync5 } from "fs";
|
|
8130
|
-
async function recoverSession(filePath) {
|
|
8131
|
-
const raw = await readFile9(filePath, "utf-8");
|
|
8132
|
-
const lines = raw.split("\n").filter((line) => line.trim().length > 0);
|
|
8133
|
-
const entries = [];
|
|
8134
|
-
for (const line of lines) {
|
|
8135
|
-
try {
|
|
8136
|
-
const entry = JSON.parse(line);
|
|
8137
|
-
if (entry.type && entry.content !== void 0) {
|
|
8138
|
-
entries.push(entry);
|
|
8139
|
-
}
|
|
8140
|
-
} catch {
|
|
8141
|
-
}
|
|
8142
|
-
}
|
|
8143
|
-
const messages = reconstructMessages(entries);
|
|
8144
|
-
const firstEntry = entries[0];
|
|
8145
|
-
const timestamp = firstEntry?.timestamp ?? 0;
|
|
8146
|
-
return {
|
|
8147
|
-
sessionId: filePath,
|
|
8148
|
-
filePath,
|
|
8149
|
-
timestamp,
|
|
8150
|
-
messages,
|
|
8151
|
-
entryCount: entries.length
|
|
8152
|
-
};
|
|
8153
|
-
}
|
|
8154
|
-
async function listSessions(limit = 20) {
|
|
8155
|
-
const dir = getSessionsDir();
|
|
8156
|
-
if (!existsSync5(dir)) return [];
|
|
8157
|
-
const files = await readdir5(dir);
|
|
8158
|
-
const sessionFiles = files.filter((f) => f.startsWith("session-") && f.endsWith(".jsonl"));
|
|
8159
|
-
const sessions = [];
|
|
8160
|
-
for (const fileName of sessionFiles) {
|
|
8161
|
-
const filePath = join11(dir, fileName);
|
|
8162
|
-
try {
|
|
8163
|
-
const raw = await readFile9(filePath, "utf-8");
|
|
8164
|
-
const lines = raw.split("\n").filter((line) => line.trim().length > 0);
|
|
8165
|
-
let lastActivity = 0;
|
|
8166
|
-
let entryCount = 0;
|
|
8167
|
-
for (const line of lines) {
|
|
8168
|
-
try {
|
|
8169
|
-
const entry = JSON.parse(line);
|
|
8170
|
-
if (entry.timestamp && entry.timestamp > lastActivity) {
|
|
8171
|
-
lastActivity = entry.timestamp;
|
|
8172
|
-
}
|
|
8173
|
-
entryCount++;
|
|
8174
|
-
} catch {
|
|
8175
|
-
}
|
|
8176
|
-
}
|
|
8177
|
-
const dateStr = fileName.replace("session-", "").replace(".jsonl", "");
|
|
8178
|
-
const fileTimestamp = Date.parse(dateStr) || lastActivity;
|
|
8179
|
-
sessions.push({
|
|
8180
|
-
fileName,
|
|
8181
|
-
filePath,
|
|
8182
|
-
timestamp: fileTimestamp,
|
|
8183
|
-
entryCount,
|
|
8184
|
-
lastActivity
|
|
8185
|
-
});
|
|
8186
|
-
} catch {
|
|
8187
|
-
}
|
|
8188
|
-
}
|
|
8189
|
-
sessions.sort((a, b) => b.lastActivity - a.lastActivity);
|
|
8190
|
-
return sessions.slice(0, limit);
|
|
8191
|
-
}
|
|
8192
|
-
async function findLatestSession() {
|
|
8193
|
-
const sessions = await listSessions(1);
|
|
8194
|
-
return sessions[0] ?? null;
|
|
8195
|
-
}
|
|
8196
|
-
function reconstructMessages(entries) {
|
|
8197
|
-
const messages = [];
|
|
8198
|
-
let i = 0;
|
|
8199
|
-
while (i < entries.length) {
|
|
8200
|
-
const entry = entries[i];
|
|
8201
|
-
if (entry.type === "user") {
|
|
8202
|
-
const content = [{ type: "text", text: entry.content }];
|
|
8203
|
-
messages.push({ role: "user", content });
|
|
8204
|
-
i++;
|
|
8205
|
-
} else if (entry.type === "assistant") {
|
|
8206
|
-
const content = [{ type: "text", text: entry.content }];
|
|
8207
|
-
messages.push({ role: "assistant", content });
|
|
8208
|
-
i++;
|
|
8209
|
-
} else if (entry.type === "tool_use") {
|
|
8210
|
-
const lastMsg = messages[messages.length - 1];
|
|
8211
|
-
if (lastMsg && lastMsg.role === "assistant") {
|
|
8212
|
-
}
|
|
8213
|
-
i++;
|
|
8214
|
-
} else if (entry.type === "tool_result") {
|
|
8215
|
-
const content = [{
|
|
8216
|
-
type: "tool_result",
|
|
8217
|
-
toolUseId: `recovered-${i}`,
|
|
8218
|
-
content: entry.content
|
|
8219
|
-
}];
|
|
8220
|
-
messages.push({ role: "user", content });
|
|
8221
|
-
i++;
|
|
8222
|
-
} else {
|
|
8223
|
-
i++;
|
|
8224
|
-
}
|
|
8225
|
-
}
|
|
8226
|
-
return messages;
|
|
8227
|
-
}
|
|
8228
|
-
|
|
8229
8337
|
// src/ui/repl.ts
|
|
8230
8338
|
var SessionSaver = class {
|
|
8231
8339
|
filePath;
|
|
@@ -11532,4 +11640,4 @@ export {
|
|
|
11532
11640
|
MCPConnectionManager,
|
|
11533
11641
|
runCli
|
|
11534
11642
|
};
|
|
11535
|
-
//# sourceMappingURL=chunk-
|
|
11643
|
+
//# sourceMappingURL=chunk-PRO7DR3G.js.map
|