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.
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  runCli
4
- } from "../chunk-MM3EHZQQ.js";
4
+ } from "../chunk-PRO7DR3G.js";
5
5
  import "../chunk-AJENHWD3.js";
6
6
  export {
7
7
  runCli
@@ -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 effectiveSystemPrompt = systemPrompt + fastMode.getSystemPromptModifier() + (projectMemory ? "\n\n" + projectMemory : "");
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
- state.totalInputTokens,
6536
- state.totalOutputTokens,
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 join10, basename as basename2, dirname as dirname3 } from "path";
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(join10(fileCwd, selected.name));
7736
+ setFileCwd(join11(fileCwd, selected.name));
7526
7737
  setSelectedIdx(0);
7527
7738
  } else {
7528
- openInEditor(join10(fileCwd, selected.name));
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-MM3EHZQQ.js.map
11643
+ //# sourceMappingURL=chunk-PRO7DR3G.js.map