@bike4mind/cli 0.2.21-feat-github-actions-tool.18208 → 0.2.21-feat-cli-context-compaction.18205
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.
|
@@ -38,6 +38,7 @@ var CliConfigSchema = z.object({
|
|
|
38
38
|
maxTokens: z.number(),
|
|
39
39
|
temperature: z.number(),
|
|
40
40
|
autoSave: z.boolean(),
|
|
41
|
+
autoCompact: z.boolean().optional().default(true),
|
|
41
42
|
theme: z.enum(["light", "dark"]),
|
|
42
43
|
exportFormat: z.enum(["markdown", "json"]),
|
|
43
44
|
maxIterations: z.number().nullable().default(10),
|
|
@@ -70,6 +71,7 @@ var ProjectConfigSchema = z.object({
|
|
|
70
71
|
maxTokens: z.number().optional(),
|
|
71
72
|
temperature: z.number().optional(),
|
|
72
73
|
autoSave: z.boolean().optional(),
|
|
74
|
+
autoCompact: z.boolean().optional(),
|
|
73
75
|
theme: z.enum(["light", "dark"]).optional(),
|
|
74
76
|
exportFormat: z.enum(["markdown", "json"]).optional(),
|
|
75
77
|
enableSkillTool: z.boolean().optional()
|
|
@@ -85,6 +87,7 @@ var ProjectLocalConfigSchema = z.object({
|
|
|
85
87
|
maxTokens: z.number().optional(),
|
|
86
88
|
temperature: z.number().optional(),
|
|
87
89
|
autoSave: z.boolean().optional(),
|
|
90
|
+
autoCompact: z.boolean().optional(),
|
|
88
91
|
theme: z.enum(["light", "dark"]).optional(),
|
|
89
92
|
exportFormat: z.enum(["markdown", "json"]).optional(),
|
|
90
93
|
enableSkillTool: z.boolean().optional()
|
|
@@ -112,6 +115,7 @@ var DEFAULT_CONFIG = {
|
|
|
112
115
|
maxTokens: 4096,
|
|
113
116
|
temperature: 0.7,
|
|
114
117
|
autoSave: true,
|
|
118
|
+
autoCompact: true,
|
|
115
119
|
theme: "dark",
|
|
116
120
|
exportFormat: "markdown",
|
|
117
121
|
maxIterations: 10,
|
package/dist/index.js
CHANGED
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
} from "./chunk-NAIQUTDD.js";
|
|
8
8
|
import {
|
|
9
9
|
ConfigStore
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-CC3VKAAL.js";
|
|
11
11
|
import "./chunk-4UPFBOWT.js";
|
|
12
12
|
import "./chunk-3NN5PD22.js";
|
|
13
13
|
import {
|
|
@@ -87,7 +87,7 @@ import "./chunk-BPFEGDC7.js";
|
|
|
87
87
|
import React19, { useState as useState8, useEffect as useEffect4, useCallback, useRef as useRef3 } from "react";
|
|
88
88
|
import { render, Box as Box18, Text as Text18, useApp, useInput as useInput7 } from "ink";
|
|
89
89
|
import { execSync } from "child_process";
|
|
90
|
-
import { v4 as
|
|
90
|
+
import { v4 as uuidv411 } from "uuid";
|
|
91
91
|
|
|
92
92
|
// src/components/App.tsx
|
|
93
93
|
import React13, { useState as useState4 } from "react";
|
|
@@ -628,6 +628,15 @@ var COMMANDS = [
|
|
|
628
628
|
{
|
|
629
629
|
name: "agents:reload",
|
|
630
630
|
description: "Reload agent definitions from disk"
|
|
631
|
+
},
|
|
632
|
+
{
|
|
633
|
+
name: "context",
|
|
634
|
+
description: "Show context window usage"
|
|
635
|
+
},
|
|
636
|
+
{
|
|
637
|
+
name: "compact",
|
|
638
|
+
description: "Compact conversation into new session",
|
|
639
|
+
args: "[instructions]"
|
|
631
640
|
}
|
|
632
641
|
];
|
|
633
642
|
function getAllCommandNames() {
|
|
@@ -1339,6 +1348,19 @@ function buildConfigItems(availableModels) {
|
|
|
1339
1348
|
}
|
|
1340
1349
|
})
|
|
1341
1350
|
},
|
|
1351
|
+
{
|
|
1352
|
+
key: "autoCompact",
|
|
1353
|
+
label: "Auto Compact",
|
|
1354
|
+
type: "boolean",
|
|
1355
|
+
getValue: (config) => config.preferences.autoCompact ?? true,
|
|
1356
|
+
setValue: (config, value) => ({
|
|
1357
|
+
...config,
|
|
1358
|
+
preferences: {
|
|
1359
|
+
...config.preferences,
|
|
1360
|
+
autoCompact: value
|
|
1361
|
+
}
|
|
1362
|
+
})
|
|
1363
|
+
},
|
|
1342
1364
|
{
|
|
1343
1365
|
key: "theme",
|
|
1344
1366
|
label: "Theme",
|
|
@@ -11092,6 +11114,19 @@ async function loadContextFiles(projectDir) {
|
|
|
11092
11114
|
errors
|
|
11093
11115
|
};
|
|
11094
11116
|
}
|
|
11117
|
+
function extractCompactInstructions(contextContent) {
|
|
11118
|
+
const regex = /^#{1,2}\s*Compact\s*Instructions\s*$/im;
|
|
11119
|
+
const match = contextContent.match(regex);
|
|
11120
|
+
if (!match || match.index === void 0) {
|
|
11121
|
+
return void 0;
|
|
11122
|
+
}
|
|
11123
|
+
const startIndex = match.index + match[0].length;
|
|
11124
|
+
const remainingContent = contextContent.slice(startIndex);
|
|
11125
|
+
const nextHeadingMatch = remainingContent.match(/^#{1,2}\s+\S/m);
|
|
11126
|
+
const endIndex = nextHeadingMatch?.index ?? remainingContent.length;
|
|
11127
|
+
const content = remainingContent.slice(0, endIndex).trim();
|
|
11128
|
+
return content || void 0;
|
|
11129
|
+
}
|
|
11095
11130
|
|
|
11096
11131
|
// src/utils/formatStep.ts
|
|
11097
11132
|
var MAX_INPUT_LENGTH = 500;
|
|
@@ -11169,6 +11204,139 @@ var formatStep = (step) => {
|
|
|
11169
11204
|
return step;
|
|
11170
11205
|
};
|
|
11171
11206
|
|
|
11207
|
+
// src/utils/tokenCounter.ts
|
|
11208
|
+
import { get_encoding } from "tiktoken";
|
|
11209
|
+
var DEFAULT_CONTEXT_WINDOW = 2e5;
|
|
11210
|
+
var TokenCounter = class {
|
|
11211
|
+
constructor() {
|
|
11212
|
+
this.encoder = null;
|
|
11213
|
+
}
|
|
11214
|
+
getEncoder() {
|
|
11215
|
+
if (!this.encoder) {
|
|
11216
|
+
this.encoder = get_encoding("cl100k_base");
|
|
11217
|
+
}
|
|
11218
|
+
return this.encoder;
|
|
11219
|
+
}
|
|
11220
|
+
/**
|
|
11221
|
+
* Count tokens in a text string
|
|
11222
|
+
*/
|
|
11223
|
+
countTokens(text) {
|
|
11224
|
+
return this.getEncoder().encode(text).length;
|
|
11225
|
+
}
|
|
11226
|
+
/**
|
|
11227
|
+
* Count tokens used in a session including system prompt
|
|
11228
|
+
*/
|
|
11229
|
+
countSessionTokens(session, systemPrompt) {
|
|
11230
|
+
const systemPromptTokens = this.countTokens(systemPrompt);
|
|
11231
|
+
const messageTokens = session.messages.reduce((sum2, msg) => sum2 + this.countTokens(msg.content), 0);
|
|
11232
|
+
return {
|
|
11233
|
+
systemPromptTokens,
|
|
11234
|
+
messageTokens,
|
|
11235
|
+
totalTokens: systemPromptTokens + messageTokens
|
|
11236
|
+
};
|
|
11237
|
+
}
|
|
11238
|
+
/**
|
|
11239
|
+
* Get context window size for a model
|
|
11240
|
+
* Falls back to DEFAULT_CONTEXT_WINDOW if model info not available
|
|
11241
|
+
*/
|
|
11242
|
+
getContextWindow(modelId, availableModels) {
|
|
11243
|
+
const model = availableModels?.find((m) => m.id === modelId);
|
|
11244
|
+
return model?.contextWindow || DEFAULT_CONTEXT_WINDOW;
|
|
11245
|
+
}
|
|
11246
|
+
/**
|
|
11247
|
+
* Free encoder resources when done
|
|
11248
|
+
*/
|
|
11249
|
+
dispose() {
|
|
11250
|
+
if (this.encoder) {
|
|
11251
|
+
this.encoder.free();
|
|
11252
|
+
this.encoder = null;
|
|
11253
|
+
}
|
|
11254
|
+
}
|
|
11255
|
+
};
|
|
11256
|
+
var tokenCounter = null;
|
|
11257
|
+
function getTokenCounter() {
|
|
11258
|
+
if (!tokenCounter) {
|
|
11259
|
+
tokenCounter = new TokenCounter();
|
|
11260
|
+
}
|
|
11261
|
+
return tokenCounter;
|
|
11262
|
+
}
|
|
11263
|
+
|
|
11264
|
+
// src/utils/compaction.ts
|
|
11265
|
+
import { v4 as uuidv410 } from "uuid";
|
|
11266
|
+
function buildCompactionPrompt(messages, options = {}) {
|
|
11267
|
+
const preserveCount = (options.preserveRecentExchanges ?? 2) * 2;
|
|
11268
|
+
if (messages.length <= preserveCount) {
|
|
11269
|
+
return {
|
|
11270
|
+
prompt: "",
|
|
11271
|
+
preservedMessages: messages
|
|
11272
|
+
};
|
|
11273
|
+
}
|
|
11274
|
+
const messagesToSummarize = messages.slice(0, -preserveCount);
|
|
11275
|
+
const preservedMessages = messages.slice(-preserveCount);
|
|
11276
|
+
let prompt = `You are summarizing a conversation for context continuity. Create a concise summary that captures:
|
|
11277
|
+
|
|
11278
|
+
- Key decisions made
|
|
11279
|
+
- Important context established
|
|
11280
|
+
- Files and code discussed
|
|
11281
|
+
- Current task state
|
|
11282
|
+
- Any pending items or next steps
|
|
11283
|
+
|
|
11284
|
+
`;
|
|
11285
|
+
if (options.claudeMdInstructions) {
|
|
11286
|
+
prompt += `Project-specific compaction instructions:
|
|
11287
|
+
${options.claudeMdInstructions}
|
|
11288
|
+
|
|
11289
|
+
`;
|
|
11290
|
+
}
|
|
11291
|
+
if (options.userInstructions) {
|
|
11292
|
+
prompt += `Additional focus: ${options.userInstructions}
|
|
11293
|
+
|
|
11294
|
+
`;
|
|
11295
|
+
}
|
|
11296
|
+
prompt += `CONVERSATION TO SUMMARIZE:
|
|
11297
|
+
|
|
11298
|
+
`;
|
|
11299
|
+
const roleLabels = {
|
|
11300
|
+
user: "User",
|
|
11301
|
+
assistant: "Assistant",
|
|
11302
|
+
system: "System"
|
|
11303
|
+
};
|
|
11304
|
+
for (const msg of messagesToSummarize) {
|
|
11305
|
+
const roleLabel = roleLabels[msg.role] || "System";
|
|
11306
|
+
const content = msg.content.length > 2e3 ? msg.content.slice(0, 2e3) + "...[truncated]" : msg.content;
|
|
11307
|
+
prompt += `**${roleLabel}:** ${content}
|
|
11308
|
+
|
|
11309
|
+
`;
|
|
11310
|
+
}
|
|
11311
|
+
prompt += `
|
|
11312
|
+
Provide a concise summary (aim for 500-1000 words) that an AI assistant can use to continue this conversation with full context.`;
|
|
11313
|
+
return { prompt, preservedMessages };
|
|
11314
|
+
}
|
|
11315
|
+
function createCompactedSession(originalSession, summary, preservedMessages) {
|
|
11316
|
+
const summaryMessage = {
|
|
11317
|
+
id: uuidv410(),
|
|
11318
|
+
role: "system",
|
|
11319
|
+
content: `[Previous conversation summary]
|
|
11320
|
+
|
|
11321
|
+
${summary}`,
|
|
11322
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
11323
|
+
};
|
|
11324
|
+
return {
|
|
11325
|
+
id: uuidv410(),
|
|
11326
|
+
name: `${originalSession.name} (compacted)`,
|
|
11327
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
11328
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
11329
|
+
model: originalSession.model,
|
|
11330
|
+
messages: [summaryMessage, ...preservedMessages],
|
|
11331
|
+
metadata: {
|
|
11332
|
+
totalTokens: 0,
|
|
11333
|
+
totalCost: 0,
|
|
11334
|
+
toolCallCount: 0,
|
|
11335
|
+
compactedFrom: originalSession.id
|
|
11336
|
+
}
|
|
11337
|
+
};
|
|
11338
|
+
}
|
|
11339
|
+
|
|
11172
11340
|
// src/utils/argumentSubstitution.ts
|
|
11173
11341
|
function substituteArguments(template, args) {
|
|
11174
11342
|
let result = template;
|
|
@@ -12262,7 +12430,7 @@ import { isAxiosError as isAxiosError2 } from "axios";
|
|
|
12262
12430
|
// package.json
|
|
12263
12431
|
var package_default = {
|
|
12264
12432
|
name: "@bike4mind/cli",
|
|
12265
|
-
version: "0.2.21-feat-
|
|
12433
|
+
version: "0.2.21-feat-cli-context-compaction.18205+57ad8b1ca",
|
|
12266
12434
|
type: "module",
|
|
12267
12435
|
description: "Interactive CLI tool for Bike4Mind with ReAct agents",
|
|
12268
12436
|
license: "UNLICENSED",
|
|
@@ -12369,10 +12537,10 @@ var package_default = {
|
|
|
12369
12537
|
},
|
|
12370
12538
|
devDependencies: {
|
|
12371
12539
|
"@bike4mind/agents": "0.1.0",
|
|
12372
|
-
"@bike4mind/common": "2.45.1-feat-
|
|
12373
|
-
"@bike4mind/mcp": "1.25.1-feat-
|
|
12374
|
-
"@bike4mind/services": "2.43.1-feat-
|
|
12375
|
-
"@bike4mind/utils": "2.3.1-feat-
|
|
12540
|
+
"@bike4mind/common": "2.45.1-feat-cli-context-compaction.18205+57ad8b1ca",
|
|
12541
|
+
"@bike4mind/mcp": "1.25.1-feat-cli-context-compaction.18205+57ad8b1ca",
|
|
12542
|
+
"@bike4mind/services": "2.43.1-feat-cli-context-compaction.18205+57ad8b1ca",
|
|
12543
|
+
"@bike4mind/utils": "2.3.1-feat-cli-context-compaction.18205+57ad8b1ca",
|
|
12376
12544
|
"@types/better-sqlite3": "^7.6.13",
|
|
12377
12545
|
"@types/diff": "^5.0.9",
|
|
12378
12546
|
"@types/jsonwebtoken": "^9.0.4",
|
|
@@ -12389,7 +12557,7 @@ var package_default = {
|
|
|
12389
12557
|
optionalDependencies: {
|
|
12390
12558
|
"@vscode/ripgrep": "^1.17.0"
|
|
12391
12559
|
},
|
|
12392
|
-
gitHead: "
|
|
12560
|
+
gitHead: "57ad8b1cad8edd3e767840eb74a458ef925d01e3"
|
|
12393
12561
|
};
|
|
12394
12562
|
|
|
12395
12563
|
// src/config/constants.ts
|
|
@@ -13216,7 +13384,8 @@ function CliApp() {
|
|
|
13216
13384
|
sessionSelector: null,
|
|
13217
13385
|
orchestrator: null,
|
|
13218
13386
|
agentStore: null,
|
|
13219
|
-
abortController: null
|
|
13387
|
+
abortController: null,
|
|
13388
|
+
contextContent: ""
|
|
13220
13389
|
});
|
|
13221
13390
|
const [isInitialized, setIsInitialized] = useState8(false);
|
|
13222
13391
|
const [initError, setInitError] = useState8(null);
|
|
@@ -13330,7 +13499,7 @@ function CliApp() {
|
|
|
13330
13499
|
if (!isAuthenticated) {
|
|
13331
13500
|
console.log("\u2139\uFE0F AI features disabled. Available commands: /login, /help, /config\n");
|
|
13332
13501
|
const minimalSession = {
|
|
13333
|
-
id:
|
|
13502
|
+
id: uuidv411(),
|
|
13334
13503
|
name: `Session ${(/* @__PURE__ */ new Date()).toLocaleString()}`,
|
|
13335
13504
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
13336
13505
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -13374,7 +13543,7 @@ function CliApp() {
|
|
|
13374
13543
|
}
|
|
13375
13544
|
llm.currentModel = modelInfo.id;
|
|
13376
13545
|
const newSession = {
|
|
13377
|
-
id:
|
|
13546
|
+
id: uuidv411(),
|
|
13378
13547
|
name: `Session ${(/* @__PURE__ */ new Date()).toLocaleString()}`,
|
|
13379
13548
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
13380
13549
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -13563,8 +13732,10 @@ ${contextResult.mergedContent}` : "";
|
|
|
13563
13732
|
// Store models for ConfigEditor
|
|
13564
13733
|
orchestrator,
|
|
13565
13734
|
// Store orchestrator for step handler updates
|
|
13566
|
-
agentStore
|
|
13735
|
+
agentStore,
|
|
13567
13736
|
// Store agent store for agent management commands
|
|
13737
|
+
contextContent: contextResult.mergedContent
|
|
13738
|
+
// Store raw context for compact instructions
|
|
13568
13739
|
}));
|
|
13569
13740
|
setStoreSession(newSession);
|
|
13570
13741
|
setIsInitialized(true);
|
|
@@ -13631,13 +13802,13 @@ ${contextResult.mergedContent}` : "";
|
|
|
13631
13802
|
messageContent = multimodalMessage.content;
|
|
13632
13803
|
}
|
|
13633
13804
|
const userMessage = {
|
|
13634
|
-
id:
|
|
13805
|
+
id: uuidv411(),
|
|
13635
13806
|
role: "user",
|
|
13636
13807
|
content: userMessageContent,
|
|
13637
13808
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
13638
13809
|
};
|
|
13639
13810
|
const pendingAssistantMessage = {
|
|
13640
|
-
id:
|
|
13811
|
+
id: uuidv411(),
|
|
13641
13812
|
role: "assistant",
|
|
13642
13813
|
content: "...",
|
|
13643
13814
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -13790,6 +13961,37 @@ ${contextResult.mergedContent}` : "";
|
|
|
13790
13961
|
console.log("\u{1F4A1} Run /login to authenticate with your B4M account.\n");
|
|
13791
13962
|
return;
|
|
13792
13963
|
}
|
|
13964
|
+
const config = state.config;
|
|
13965
|
+
let activeSession = state.session;
|
|
13966
|
+
if (config?.preferences.autoCompact !== false && activeSession.messages.length >= 6) {
|
|
13967
|
+
const tokenCounter2 = getTokenCounter();
|
|
13968
|
+
const contextWindow = tokenCounter2.getContextWindow(activeSession.model, state.availableModels);
|
|
13969
|
+
const threshold = contextWindow * 0.8;
|
|
13970
|
+
const systemPrompt = buildCoreSystemPrompt(state.contextContent || "");
|
|
13971
|
+
const contextUsage = tokenCounter2.countSessionTokens(activeSession, systemPrompt);
|
|
13972
|
+
if (contextUsage.totalTokens >= threshold) {
|
|
13973
|
+
console.log("\n\u26A0\uFE0F Context window 80% full. Auto-compacting...\n");
|
|
13974
|
+
useCliStore.getState().setIsThinking(true);
|
|
13975
|
+
try {
|
|
13976
|
+
const { prompt: compactionPrompt, preservedMessages } = buildCompactionPrompt(activeSession.messages, {
|
|
13977
|
+
claudeMdInstructions: extractCompactInstructions(state.contextContent || "")
|
|
13978
|
+
});
|
|
13979
|
+
if (compactionPrompt) {
|
|
13980
|
+
const result = await state.agent.run(compactionPrompt, { maxIterations: 1 });
|
|
13981
|
+
await state.sessionStore.save(activeSession);
|
|
13982
|
+
const newSession = createCompactedSession(activeSession, result.finalAnswer, preservedMessages);
|
|
13983
|
+
await logger.initialize(newSession.id);
|
|
13984
|
+
setState((prev) => ({ ...prev, session: newSession }));
|
|
13985
|
+
setStoreSession(newSession);
|
|
13986
|
+
useCliStore.getState().clearPendingMessages();
|
|
13987
|
+
console.log("\u2705 Auto-compacted. Continuing with your message...\n");
|
|
13988
|
+
activeSession = newSession;
|
|
13989
|
+
}
|
|
13990
|
+
} finally {
|
|
13991
|
+
useCliStore.getState().setIsThinking(false);
|
|
13992
|
+
}
|
|
13993
|
+
}
|
|
13994
|
+
}
|
|
13793
13995
|
useCliStore.getState().setIsThinking(true);
|
|
13794
13996
|
const abortController = new AbortController();
|
|
13795
13997
|
setState((prev) => ({ ...prev, abortController }));
|
|
@@ -13802,13 +14004,13 @@ ${contextResult.mergedContent}` : "";
|
|
|
13802
14004
|
userMessageContent = message;
|
|
13803
14005
|
}
|
|
13804
14006
|
const userMessage = {
|
|
13805
|
-
id:
|
|
14007
|
+
id: uuidv411(),
|
|
13806
14008
|
role: "user",
|
|
13807
14009
|
content: userMessageContent,
|
|
13808
14010
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
13809
14011
|
};
|
|
13810
14012
|
const pendingAssistantMessage = {
|
|
13811
|
-
id:
|
|
14013
|
+
id: uuidv411(),
|
|
13812
14014
|
role: "assistant",
|
|
13813
14015
|
content: "...",
|
|
13814
14016
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -13817,14 +14019,14 @@ ${contextResult.mergedContent}` : "";
|
|
|
13817
14019
|
}
|
|
13818
14020
|
};
|
|
13819
14021
|
const sessionWithUserMessage = {
|
|
13820
|
-
...
|
|
13821
|
-
messages: [...
|
|
14022
|
+
...activeSession,
|
|
14023
|
+
messages: [...activeSession.messages, userMessage],
|
|
13822
14024
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
13823
14025
|
};
|
|
13824
14026
|
setState((prev) => ({ ...prev, session: sessionWithUserMessage }));
|
|
13825
14027
|
setStoreSession(sessionWithUserMessage);
|
|
13826
14028
|
useCliStore.getState().addPendingMessage(pendingAssistantMessage);
|
|
13827
|
-
const recentMessages =
|
|
14029
|
+
const recentMessages = activeSession.messages.slice(-20);
|
|
13828
14030
|
const previousMessages = recentMessages.filter((msg) => msg.role === "user" || msg.role === "assistant").map((msg) => ({
|
|
13829
14031
|
role: msg.role,
|
|
13830
14032
|
content: msg.content
|
|
@@ -13876,7 +14078,7 @@ ${contextResult.mergedContent}` : "";
|
|
|
13876
14078
|
const currentSession = useCliStore.getState().session;
|
|
13877
14079
|
if (currentSession) {
|
|
13878
14080
|
const cancelMessage = {
|
|
13879
|
-
id:
|
|
14081
|
+
id: uuidv411(),
|
|
13880
14082
|
role: "assistant",
|
|
13881
14083
|
content: "\u26A0\uFE0F Operation cancelled by user",
|
|
13882
14084
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -13927,13 +14129,13 @@ ${contextResult.mergedContent}` : "";
|
|
|
13927
14129
|
isError = true;
|
|
13928
14130
|
}
|
|
13929
14131
|
const userMessage = {
|
|
13930
|
-
id:
|
|
14132
|
+
id: uuidv411(),
|
|
13931
14133
|
role: "user",
|
|
13932
14134
|
content: `$ ${command}`,
|
|
13933
14135
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
13934
14136
|
};
|
|
13935
14137
|
const assistantMessage = {
|
|
13936
|
-
id:
|
|
14138
|
+
id: uuidv411(),
|
|
13937
14139
|
role: "assistant",
|
|
13938
14140
|
content: isError ? `\u274C Error:
|
|
13939
14141
|
${output}` : output.trim() || "(no output)",
|
|
@@ -14376,7 +14578,7 @@ Custom Commands:
|
|
|
14376
14578
|
console.clear();
|
|
14377
14579
|
const model = state.session?.model || state.config?.defaultModel || "claude-sonnet";
|
|
14378
14580
|
const newSession = {
|
|
14379
|
-
id:
|
|
14581
|
+
id: uuidv411(),
|
|
14380
14582
|
name: `Session ${(/* @__PURE__ */ new Date()).toLocaleString()}`,
|
|
14381
14583
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
14382
14584
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -14559,6 +14761,75 @@ No usage data available for the last ${USAGE_DAYS} days.`);
|
|
|
14559
14761
|
}
|
|
14560
14762
|
break;
|
|
14561
14763
|
}
|
|
14764
|
+
case "context": {
|
|
14765
|
+
if (!state.session) {
|
|
14766
|
+
console.log("No active session");
|
|
14767
|
+
break;
|
|
14768
|
+
}
|
|
14769
|
+
const tokenCounter2 = getTokenCounter();
|
|
14770
|
+
const contextWindow = tokenCounter2.getContextWindow(state.session.model, state.availableModels);
|
|
14771
|
+
const systemPrompt = buildCoreSystemPrompt(state.contextContent || "");
|
|
14772
|
+
const usage = tokenCounter2.countSessionTokens(state.session, systemPrompt);
|
|
14773
|
+
const usagePercent = usage.totalTokens / contextWindow * 100;
|
|
14774
|
+
const barWidth = 40;
|
|
14775
|
+
const filledWidth = Math.min(Math.round(usagePercent / 100 * barWidth), barWidth);
|
|
14776
|
+
const emptyWidth = barWidth - filledWidth;
|
|
14777
|
+
const bar = "\u2588".repeat(filledWidth) + "\u2591".repeat(emptyWidth);
|
|
14778
|
+
console.log("\n\u{1F4CA} Context Usage:");
|
|
14779
|
+
console.log(`[${bar}] ${usagePercent.toFixed(1)}%`);
|
|
14780
|
+
console.log(`${(usage.totalTokens / 1e3).toFixed(1)}k / ${(contextWindow / 1e3).toFixed(0)}k tokens
|
|
14781
|
+
`);
|
|
14782
|
+
console.log("Breakdown:");
|
|
14783
|
+
console.log(` System Prompt: ${usage.systemPromptTokens.toLocaleString()} tokens`);
|
|
14784
|
+
console.log(` Messages: ${usage.messageTokens.toLocaleString()} tokens`);
|
|
14785
|
+
console.log(` Message Count: ${state.session.messages.length}`);
|
|
14786
|
+
if (usagePercent >= 80) {
|
|
14787
|
+
console.log(`
|
|
14788
|
+
\u26A0\uFE0F Warning: Context is ${usagePercent.toFixed(0)}% full`);
|
|
14789
|
+
console.log(" Run /compact to summarize and free space");
|
|
14790
|
+
}
|
|
14791
|
+
console.log("");
|
|
14792
|
+
break;
|
|
14793
|
+
}
|
|
14794
|
+
case "compact": {
|
|
14795
|
+
if (!state.session || !state.agent) {
|
|
14796
|
+
console.log("No active session");
|
|
14797
|
+
break;
|
|
14798
|
+
}
|
|
14799
|
+
if (state.session.messages.length < 6) {
|
|
14800
|
+
console.log("Not enough messages to compact (need at least 6)");
|
|
14801
|
+
break;
|
|
14802
|
+
}
|
|
14803
|
+
const userInstructions = args.join(" ") || void 0;
|
|
14804
|
+
const { prompt: compactionPrompt, preservedMessages } = buildCompactionPrompt(state.session.messages, {
|
|
14805
|
+
userInstructions,
|
|
14806
|
+
claudeMdInstructions: extractCompactInstructions(state.contextContent || "")
|
|
14807
|
+
});
|
|
14808
|
+
if (!compactionPrompt) {
|
|
14809
|
+
console.log("Not enough messages to compact");
|
|
14810
|
+
break;
|
|
14811
|
+
}
|
|
14812
|
+
console.log("\u{1F5DC}\uFE0F Compacting conversation...\n");
|
|
14813
|
+
useCliStore.getState().setIsThinking(true);
|
|
14814
|
+
try {
|
|
14815
|
+
const result = await state.agent.run(compactionPrompt, { maxIterations: 1 });
|
|
14816
|
+
const summary = result.finalAnswer;
|
|
14817
|
+
await state.sessionStore.save(state.session);
|
|
14818
|
+
const oldSessionName = state.session.name;
|
|
14819
|
+
const newSession = createCompactedSession(state.session, summary, preservedMessages);
|
|
14820
|
+
await logger.initialize(newSession.id);
|
|
14821
|
+
setState((prev) => ({ ...prev, session: newSession }));
|
|
14822
|
+
setStoreSession(newSession);
|
|
14823
|
+
useCliStore.getState().clearPendingMessages();
|
|
14824
|
+
console.log("\u2705 Conversation compacted");
|
|
14825
|
+
console.log(`\u{1F4DD} New session: ${newSession.name}`);
|
|
14826
|
+
console.log(`\u{1F4BE} Previous session preserved: ${oldSessionName}
|
|
14827
|
+
`);
|
|
14828
|
+
} finally {
|
|
14829
|
+
useCliStore.getState().setIsThinking(false);
|
|
14830
|
+
}
|
|
14831
|
+
break;
|
|
14832
|
+
}
|
|
14562
14833
|
case "project-config": {
|
|
14563
14834
|
const projectDir = state.configStore.getProjectConfigDir();
|
|
14564
14835
|
const config = await state.configStore.get();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bike4mind/cli",
|
|
3
|
-
"version": "0.2.21-feat-
|
|
3
|
+
"version": "0.2.21-feat-cli-context-compaction.18205+57ad8b1ca",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Interactive CLI tool for Bike4Mind with ReAct agents",
|
|
6
6
|
"license": "UNLICENSED",
|
|
@@ -107,10 +107,10 @@
|
|
|
107
107
|
},
|
|
108
108
|
"devDependencies": {
|
|
109
109
|
"@bike4mind/agents": "0.1.0",
|
|
110
|
-
"@bike4mind/common": "2.45.1-feat-
|
|
111
|
-
"@bike4mind/mcp": "1.25.1-feat-
|
|
112
|
-
"@bike4mind/services": "2.43.1-feat-
|
|
113
|
-
"@bike4mind/utils": "2.3.1-feat-
|
|
110
|
+
"@bike4mind/common": "2.45.1-feat-cli-context-compaction.18205+57ad8b1ca",
|
|
111
|
+
"@bike4mind/mcp": "1.25.1-feat-cli-context-compaction.18205+57ad8b1ca",
|
|
112
|
+
"@bike4mind/services": "2.43.1-feat-cli-context-compaction.18205+57ad8b1ca",
|
|
113
|
+
"@bike4mind/utils": "2.3.1-feat-cli-context-compaction.18205+57ad8b1ca",
|
|
114
114
|
"@types/better-sqlite3": "^7.6.13",
|
|
115
115
|
"@types/diff": "^5.0.9",
|
|
116
116
|
"@types/jsonwebtoken": "^9.0.4",
|
|
@@ -127,5 +127,5 @@
|
|
|
127
127
|
"optionalDependencies": {
|
|
128
128
|
"@vscode/ripgrep": "^1.17.0"
|
|
129
129
|
},
|
|
130
|
-
"gitHead": "
|
|
130
|
+
"gitHead": "57ad8b1cad8edd3e767840eb74a458ef925d01e3"
|
|
131
131
|
}
|