@hasna/oldpal 0.1.8 → 0.1.9
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/index.js +737 -38
- package/dist/index.js.map +12 -9
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -20450,12 +20450,12 @@ var exports_anthropic = {};
|
|
|
20450
20450
|
__export(exports_anthropic, {
|
|
20451
20451
|
AnthropicClient: () => AnthropicClient
|
|
20452
20452
|
});
|
|
20453
|
-
import { readFileSync as readFileSync2, existsSync as
|
|
20454
|
-
import { homedir as
|
|
20455
|
-
import { join as
|
|
20453
|
+
import { readFileSync as readFileSync2, existsSync as existsSync4 } from "fs";
|
|
20454
|
+
import { homedir as homedir5 } from "os";
|
|
20455
|
+
import { join as join6 } from "path";
|
|
20456
20456
|
function loadApiKeyFromSecrets() {
|
|
20457
|
-
const secretsPath =
|
|
20458
|
-
if (
|
|
20457
|
+
const secretsPath = join6(homedir5(), ".secrets");
|
|
20458
|
+
if (existsSync4(secretsPath)) {
|
|
20459
20459
|
try {
|
|
20460
20460
|
const content = readFileSync2(secretsPath, "utf-8");
|
|
20461
20461
|
const match = content.match(/export\s+ANTHROPIC_API_KEY\s*=\s*["']?([^"'\n]+)["']?/);
|
|
@@ -29643,7 +29643,609 @@ class HookExecutor {
|
|
|
29643
29643
|
return null;
|
|
29644
29644
|
}
|
|
29645
29645
|
}
|
|
29646
|
+
// packages/core/src/commands/loader.ts
|
|
29647
|
+
import { existsSync as existsSync2, readdirSync, statSync } from "fs";
|
|
29648
|
+
import { join as join4, basename, extname } from "path";
|
|
29649
|
+
import { homedir as homedir3 } from "os";
|
|
29650
|
+
|
|
29651
|
+
class CommandLoader {
|
|
29652
|
+
commands = new Map;
|
|
29653
|
+
cwd;
|
|
29654
|
+
constructor(cwd2) {
|
|
29655
|
+
this.cwd = cwd2 || process.cwd();
|
|
29656
|
+
}
|
|
29657
|
+
async loadAll() {
|
|
29658
|
+
this.commands.clear();
|
|
29659
|
+
const globalDir = join4(homedir3(), ".oldpal", "commands");
|
|
29660
|
+
await this.loadFromDirectory(globalDir, "global");
|
|
29661
|
+
const projectDir = join4(this.cwd, ".oldpal", "commands");
|
|
29662
|
+
await this.loadFromDirectory(projectDir, "project");
|
|
29663
|
+
}
|
|
29664
|
+
async loadFromDirectory(dir, source, prefix = "") {
|
|
29665
|
+
if (!existsSync2(dir))
|
|
29666
|
+
return;
|
|
29667
|
+
const entries = readdirSync(dir);
|
|
29668
|
+
for (const entry of entries) {
|
|
29669
|
+
const fullPath = join4(dir, entry);
|
|
29670
|
+
const stat = statSync(fullPath);
|
|
29671
|
+
if (stat.isDirectory()) {
|
|
29672
|
+
const newPrefix = prefix ? `${prefix}:${entry}` : entry;
|
|
29673
|
+
await this.loadFromDirectory(fullPath, source, newPrefix);
|
|
29674
|
+
} else if (stat.isFile() && extname(entry) === ".md") {
|
|
29675
|
+
const command = await this.loadCommandFile(fullPath, prefix);
|
|
29676
|
+
if (command) {
|
|
29677
|
+
this.commands.set(command.name, command);
|
|
29678
|
+
}
|
|
29679
|
+
}
|
|
29680
|
+
}
|
|
29681
|
+
}
|
|
29682
|
+
async loadCommandFile(filePath, prefix) {
|
|
29683
|
+
try {
|
|
29684
|
+
const content = await Bun.file(filePath).text();
|
|
29685
|
+
const { frontmatter, body } = this.parseFrontmatter(content);
|
|
29686
|
+
const fileName = basename(filePath, ".md");
|
|
29687
|
+
const name = frontmatter.name || (prefix ? `${prefix}:${fileName}` : fileName);
|
|
29688
|
+
return {
|
|
29689
|
+
name,
|
|
29690
|
+
description: frontmatter.description || `Run the ${name} command`,
|
|
29691
|
+
tags: frontmatter.tags,
|
|
29692
|
+
allowedTools: frontmatter["allowed-tools"]?.split(",").map((t) => t.trim()),
|
|
29693
|
+
content: body,
|
|
29694
|
+
filePath,
|
|
29695
|
+
builtin: false
|
|
29696
|
+
};
|
|
29697
|
+
} catch (error) {
|
|
29698
|
+
console.error(`Failed to load command from ${filePath}:`, error);
|
|
29699
|
+
return null;
|
|
29700
|
+
}
|
|
29701
|
+
}
|
|
29702
|
+
parseFrontmatter(content) {
|
|
29703
|
+
const frontmatterRegex = /^---\s*\n([\s\S]*?)\n---\s*\n([\s\S]*)$/;
|
|
29704
|
+
const match = content.match(frontmatterRegex);
|
|
29705
|
+
if (!match) {
|
|
29706
|
+
return { frontmatter: {}, body: content };
|
|
29707
|
+
}
|
|
29708
|
+
const [, yamlContent, body] = match;
|
|
29709
|
+
const frontmatter = {};
|
|
29710
|
+
const lines = yamlContent.split(`
|
|
29711
|
+
`);
|
|
29712
|
+
for (const line of lines) {
|
|
29713
|
+
const colonIndex = line.indexOf(":");
|
|
29714
|
+
if (colonIndex === -1)
|
|
29715
|
+
continue;
|
|
29716
|
+
const key = line.slice(0, colonIndex).trim();
|
|
29717
|
+
let value = line.slice(colonIndex + 1).trim();
|
|
29718
|
+
if (value.startsWith("[") && value.endsWith("]")) {
|
|
29719
|
+
const arrayContent = value.slice(1, -1);
|
|
29720
|
+
frontmatter[key] = arrayContent.split(",").map((v) => v.trim());
|
|
29721
|
+
} else {
|
|
29722
|
+
if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
|
|
29723
|
+
value = value.slice(1, -1);
|
|
29724
|
+
}
|
|
29725
|
+
frontmatter[key] = value;
|
|
29726
|
+
}
|
|
29727
|
+
}
|
|
29728
|
+
return { frontmatter, body: body.trim() };
|
|
29729
|
+
}
|
|
29730
|
+
register(command) {
|
|
29731
|
+
this.commands.set(command.name, command);
|
|
29732
|
+
}
|
|
29733
|
+
getCommand(name) {
|
|
29734
|
+
return this.commands.get(name);
|
|
29735
|
+
}
|
|
29736
|
+
getCommands() {
|
|
29737
|
+
return Array.from(this.commands.values());
|
|
29738
|
+
}
|
|
29739
|
+
hasCommand(name) {
|
|
29740
|
+
return this.commands.has(name);
|
|
29741
|
+
}
|
|
29742
|
+
findMatching(partial) {
|
|
29743
|
+
const lower = partial.toLowerCase();
|
|
29744
|
+
return this.getCommands().filter((cmd) => cmd.name.toLowerCase().startsWith(lower) || cmd.description.toLowerCase().includes(lower));
|
|
29745
|
+
}
|
|
29746
|
+
}
|
|
29747
|
+
// packages/core/src/commands/executor.ts
|
|
29748
|
+
class CommandExecutor {
|
|
29749
|
+
loader;
|
|
29750
|
+
constructor(loader) {
|
|
29751
|
+
this.loader = loader;
|
|
29752
|
+
}
|
|
29753
|
+
parseCommand(input) {
|
|
29754
|
+
const trimmed = input.trim();
|
|
29755
|
+
if (!trimmed.startsWith("/")) {
|
|
29756
|
+
return null;
|
|
29757
|
+
}
|
|
29758
|
+
const match = trimmed.match(/^\/(\S+)(?:\s+(.*))?$/);
|
|
29759
|
+
if (!match) {
|
|
29760
|
+
return null;
|
|
29761
|
+
}
|
|
29762
|
+
return {
|
|
29763
|
+
name: match[1],
|
|
29764
|
+
args: match[2] || ""
|
|
29765
|
+
};
|
|
29766
|
+
}
|
|
29767
|
+
isCommand(input) {
|
|
29768
|
+
return this.parseCommand(input) !== null;
|
|
29769
|
+
}
|
|
29770
|
+
async execute(input, context) {
|
|
29771
|
+
const parsed = this.parseCommand(input);
|
|
29772
|
+
if (!parsed) {
|
|
29773
|
+
return { handled: false };
|
|
29774
|
+
}
|
|
29775
|
+
const command = this.loader.getCommand(parsed.name);
|
|
29776
|
+
if (!command) {
|
|
29777
|
+
context.emit("text", `Unknown command: /${parsed.name}
|
|
29646
29778
|
|
|
29779
|
+
Use /help to see available commands.
|
|
29780
|
+
`);
|
|
29781
|
+
context.emit("done");
|
|
29782
|
+
return { handled: true };
|
|
29783
|
+
}
|
|
29784
|
+
if (command.selfHandled && command.handler) {
|
|
29785
|
+
return command.handler(parsed.args, context);
|
|
29786
|
+
}
|
|
29787
|
+
const prompt = await this.preparePrompt(command, parsed.args);
|
|
29788
|
+
return {
|
|
29789
|
+
handled: false,
|
|
29790
|
+
prompt
|
|
29791
|
+
};
|
|
29792
|
+
}
|
|
29793
|
+
async preparePrompt(command, args) {
|
|
29794
|
+
let content = command.content;
|
|
29795
|
+
content = content.replace(/\$ARGUMENTS/g, args || "(no arguments provided)");
|
|
29796
|
+
content = await this.processShellCommands(content);
|
|
29797
|
+
return content;
|
|
29798
|
+
}
|
|
29799
|
+
async processShellCommands(content) {
|
|
29800
|
+
const lines = content.split(`
|
|
29801
|
+
`);
|
|
29802
|
+
const processedLines = [];
|
|
29803
|
+
for (const line of lines) {
|
|
29804
|
+
const trimmed = line.trim();
|
|
29805
|
+
if (trimmed.startsWith("!")) {
|
|
29806
|
+
const command = trimmed.slice(1).trim();
|
|
29807
|
+
const output = await this.executeShell(command);
|
|
29808
|
+
processedLines.push(`\`\`\`
|
|
29809
|
+
${output}
|
|
29810
|
+
\`\`\``);
|
|
29811
|
+
} else {
|
|
29812
|
+
processedLines.push(line);
|
|
29813
|
+
}
|
|
29814
|
+
}
|
|
29815
|
+
return processedLines.join(`
|
|
29816
|
+
`);
|
|
29817
|
+
}
|
|
29818
|
+
async executeShell(command) {
|
|
29819
|
+
try {
|
|
29820
|
+
const proc = Bun.spawn(["bash", "-c", command], {
|
|
29821
|
+
stdout: "pipe",
|
|
29822
|
+
stderr: "pipe"
|
|
29823
|
+
});
|
|
29824
|
+
const [stdout, stderr] = await Promise.all([
|
|
29825
|
+
new Response(proc.stdout).text(),
|
|
29826
|
+
new Response(proc.stderr).text()
|
|
29827
|
+
]);
|
|
29828
|
+
const exitCode = await proc.exited;
|
|
29829
|
+
if (exitCode !== 0 && stderr) {
|
|
29830
|
+
return `Error (exit ${exitCode}):
|
|
29831
|
+
${stderr}`;
|
|
29832
|
+
}
|
|
29833
|
+
return stdout.trim() || "(no output)";
|
|
29834
|
+
} catch (error) {
|
|
29835
|
+
return `Error: ${error instanceof Error ? error.message : String(error)}`;
|
|
29836
|
+
}
|
|
29837
|
+
}
|
|
29838
|
+
getSuggestions(partial) {
|
|
29839
|
+
if (!partial.startsWith("/")) {
|
|
29840
|
+
return [];
|
|
29841
|
+
}
|
|
29842
|
+
const name = partial.slice(1).toLowerCase();
|
|
29843
|
+
return this.loader.findMatching(name);
|
|
29844
|
+
}
|
|
29845
|
+
}
|
|
29846
|
+
// packages/core/src/commands/builtin.ts
|
|
29847
|
+
import { join as join5 } from "path";
|
|
29848
|
+
import { homedir as homedir4 } from "os";
|
|
29849
|
+
import { existsSync as existsSync3, mkdirSync, writeFileSync } from "fs";
|
|
29850
|
+
|
|
29851
|
+
class BuiltinCommands {
|
|
29852
|
+
tokenUsage = {
|
|
29853
|
+
inputTokens: 0,
|
|
29854
|
+
outputTokens: 0,
|
|
29855
|
+
totalTokens: 0,
|
|
29856
|
+
maxContextTokens: 200000
|
|
29857
|
+
};
|
|
29858
|
+
registerAll(loader) {
|
|
29859
|
+
loader.register(this.helpCommand(loader));
|
|
29860
|
+
loader.register(this.clearCommand());
|
|
29861
|
+
loader.register(this.statusCommand());
|
|
29862
|
+
loader.register(this.compactCommand());
|
|
29863
|
+
loader.register(this.configCommand());
|
|
29864
|
+
loader.register(this.initCommand());
|
|
29865
|
+
loader.register(this.costCommand());
|
|
29866
|
+
loader.register(this.modelCommand());
|
|
29867
|
+
loader.register(this.memoryCommand());
|
|
29868
|
+
loader.register(this.bugCommand());
|
|
29869
|
+
loader.register(this.prCommand());
|
|
29870
|
+
loader.register(this.reviewCommand());
|
|
29871
|
+
}
|
|
29872
|
+
updateTokenUsage(usage) {
|
|
29873
|
+
Object.assign(this.tokenUsage, usage);
|
|
29874
|
+
}
|
|
29875
|
+
getTokenUsage() {
|
|
29876
|
+
return { ...this.tokenUsage };
|
|
29877
|
+
}
|
|
29878
|
+
helpCommand(loader) {
|
|
29879
|
+
return {
|
|
29880
|
+
name: "help",
|
|
29881
|
+
description: "Show available slash commands",
|
|
29882
|
+
builtin: true,
|
|
29883
|
+
selfHandled: true,
|
|
29884
|
+
content: "",
|
|
29885
|
+
handler: async (args, context) => {
|
|
29886
|
+
const commands = loader.getCommands();
|
|
29887
|
+
const builtinCmds = commands.filter((c) => c.builtin);
|
|
29888
|
+
const customCmds = commands.filter((c) => !c.builtin);
|
|
29889
|
+
let message = `
|
|
29890
|
+
**Available Slash Commands**
|
|
29891
|
+
|
|
29892
|
+
`;
|
|
29893
|
+
if (builtinCmds.length > 0) {
|
|
29894
|
+
message += `**Built-in Commands:**
|
|
29895
|
+
`;
|
|
29896
|
+
for (const cmd of builtinCmds.sort((a, b) => a.name.localeCompare(b.name))) {
|
|
29897
|
+
message += ` /${cmd.name} - ${cmd.description}
|
|
29898
|
+
`;
|
|
29899
|
+
}
|
|
29900
|
+
message += `
|
|
29901
|
+
`;
|
|
29902
|
+
}
|
|
29903
|
+
if (customCmds.length > 0) {
|
|
29904
|
+
message += `**Custom Commands:**
|
|
29905
|
+
`;
|
|
29906
|
+
for (const cmd of customCmds.sort((a, b) => a.name.localeCompare(b.name))) {
|
|
29907
|
+
message += ` /${cmd.name} - ${cmd.description}
|
|
29908
|
+
`;
|
|
29909
|
+
}
|
|
29910
|
+
message += `
|
|
29911
|
+
`;
|
|
29912
|
+
}
|
|
29913
|
+
message += `**Tips:**
|
|
29914
|
+
`;
|
|
29915
|
+
message += ` - Create custom commands in .oldpal/commands/*.md
|
|
29916
|
+
`;
|
|
29917
|
+
message += ` - Global commands go in ~/.oldpal/commands/*.md
|
|
29918
|
+
`;
|
|
29919
|
+
message += ` - Use /init to create a starter command
|
|
29920
|
+
`;
|
|
29921
|
+
context.emit("text", message);
|
|
29922
|
+
context.emit("done");
|
|
29923
|
+
return { handled: true };
|
|
29924
|
+
}
|
|
29925
|
+
};
|
|
29926
|
+
}
|
|
29927
|
+
clearCommand() {
|
|
29928
|
+
return {
|
|
29929
|
+
name: "clear",
|
|
29930
|
+
description: "Clear conversation history and start fresh",
|
|
29931
|
+
builtin: true,
|
|
29932
|
+
selfHandled: true,
|
|
29933
|
+
content: "",
|
|
29934
|
+
handler: async (args, context) => {
|
|
29935
|
+
context.clearMessages();
|
|
29936
|
+
this.tokenUsage.inputTokens = 0;
|
|
29937
|
+
this.tokenUsage.outputTokens = 0;
|
|
29938
|
+
this.tokenUsage.totalTokens = 0;
|
|
29939
|
+
context.emit("text", `Conversation cleared. Starting fresh.
|
|
29940
|
+
`);
|
|
29941
|
+
context.emit("done");
|
|
29942
|
+
return { handled: true, clearConversation: true };
|
|
29943
|
+
}
|
|
29944
|
+
};
|
|
29945
|
+
}
|
|
29946
|
+
statusCommand() {
|
|
29947
|
+
return {
|
|
29948
|
+
name: "status",
|
|
29949
|
+
description: "Show current session status and token usage",
|
|
29950
|
+
builtin: true,
|
|
29951
|
+
selfHandled: true,
|
|
29952
|
+
content: "",
|
|
29953
|
+
handler: async (args, context) => {
|
|
29954
|
+
const usage = this.tokenUsage;
|
|
29955
|
+
const usedPercent = Math.round(usage.totalTokens / usage.maxContextTokens * 100);
|
|
29956
|
+
let message = `
|
|
29957
|
+
**Session Status**
|
|
29958
|
+
|
|
29959
|
+
`;
|
|
29960
|
+
message += `**Working Directory:** ${context.cwd}
|
|
29961
|
+
`;
|
|
29962
|
+
message += `**Session ID:** ${context.sessionId}
|
|
29963
|
+
`;
|
|
29964
|
+
message += `**Messages:** ${context.messages.length}
|
|
29965
|
+
`;
|
|
29966
|
+
message += `**Available Tools:** ${context.tools.length}
|
|
29967
|
+
|
|
29968
|
+
`;
|
|
29969
|
+
message += `**Token Usage:**
|
|
29970
|
+
`;
|
|
29971
|
+
message += ` Input: ${usage.inputTokens.toLocaleString()}
|
|
29972
|
+
`;
|
|
29973
|
+
message += ` Output: ${usage.outputTokens.toLocaleString()}
|
|
29974
|
+
`;
|
|
29975
|
+
message += ` Total: ${usage.totalTokens.toLocaleString()} / ${usage.maxContextTokens.toLocaleString()} (${usedPercent}%)
|
|
29976
|
+
`;
|
|
29977
|
+
if (usage.cacheReadTokens || usage.cacheWriteTokens) {
|
|
29978
|
+
message += ` Cache Read: ${(usage.cacheReadTokens || 0).toLocaleString()}
|
|
29979
|
+
`;
|
|
29980
|
+
message += ` Cache Write: ${(usage.cacheWriteTokens || 0).toLocaleString()}
|
|
29981
|
+
`;
|
|
29982
|
+
}
|
|
29983
|
+
const barLength = 30;
|
|
29984
|
+
const filledLength = Math.round(usedPercent / 100 * barLength);
|
|
29985
|
+
const bar = "\u2588".repeat(filledLength) + "\u2591".repeat(barLength - filledLength);
|
|
29986
|
+
message += `
|
|
29987
|
+
[${bar}] ${usedPercent}%
|
|
29988
|
+
`;
|
|
29989
|
+
context.emit("text", message);
|
|
29990
|
+
context.emit("done");
|
|
29991
|
+
return { handled: true };
|
|
29992
|
+
}
|
|
29993
|
+
};
|
|
29994
|
+
}
|
|
29995
|
+
compactCommand() {
|
|
29996
|
+
return {
|
|
29997
|
+
name: "compact",
|
|
29998
|
+
description: "Summarize conversation to save context space",
|
|
29999
|
+
builtin: true,
|
|
30000
|
+
selfHandled: false,
|
|
30001
|
+
content: `Please summarize our conversation so far into a concise format that preserves:
|
|
30002
|
+
1. Key decisions made
|
|
30003
|
+
2. Important context about the codebase
|
|
30004
|
+
3. Current task/goal we're working on
|
|
30005
|
+
4. Any constraints or requirements mentioned
|
|
30006
|
+
|
|
30007
|
+
Format the summary as a brief bullet-point list. This summary will replace the conversation history to save context space.`
|
|
30008
|
+
};
|
|
30009
|
+
}
|
|
30010
|
+
configCommand() {
|
|
30011
|
+
return {
|
|
30012
|
+
name: "config",
|
|
30013
|
+
description: "Show current configuration",
|
|
30014
|
+
builtin: true,
|
|
30015
|
+
selfHandled: true,
|
|
30016
|
+
content: "",
|
|
30017
|
+
handler: async (args, context) => {
|
|
30018
|
+
const configPaths = [
|
|
30019
|
+
join5(context.cwd, ".oldpal", "config.json"),
|
|
30020
|
+
join5(homedir4(), ".oldpal", "config.json")
|
|
30021
|
+
];
|
|
30022
|
+
let message = `
|
|
30023
|
+
**Configuration**
|
|
30024
|
+
|
|
30025
|
+
`;
|
|
30026
|
+
message += `**Config File Locations:**
|
|
30027
|
+
`;
|
|
30028
|
+
for (const path of configPaths) {
|
|
30029
|
+
const exists = existsSync3(path);
|
|
30030
|
+
message += ` ${exists ? "\u2713" : "\u25CB"} ${path}
|
|
30031
|
+
`;
|
|
30032
|
+
}
|
|
30033
|
+
message += `
|
|
30034
|
+
**Commands Directories:**
|
|
30035
|
+
`;
|
|
30036
|
+
message += ` - Project: ${join5(context.cwd, ".oldpal", "commands")}
|
|
30037
|
+
`;
|
|
30038
|
+
message += ` - Global: ${join5(homedir4(), ".oldpal", "commands")}
|
|
30039
|
+
`;
|
|
30040
|
+
context.emit("text", message);
|
|
30041
|
+
context.emit("done");
|
|
30042
|
+
return { handled: true };
|
|
30043
|
+
}
|
|
30044
|
+
};
|
|
30045
|
+
}
|
|
30046
|
+
initCommand() {
|
|
30047
|
+
return {
|
|
30048
|
+
name: "init",
|
|
30049
|
+
description: "Initialize oldpal config and create example command",
|
|
30050
|
+
builtin: true,
|
|
30051
|
+
selfHandled: true,
|
|
30052
|
+
content: "",
|
|
30053
|
+
handler: async (args, context) => {
|
|
30054
|
+
const commandsDir = join5(context.cwd, ".oldpal", "commands");
|
|
30055
|
+
mkdirSync(commandsDir, { recursive: true });
|
|
30056
|
+
const exampleCommand = `---
|
|
30057
|
+
name: review
|
|
30058
|
+
description: Review code changes for issues and improvements
|
|
30059
|
+
tags: [code, review]
|
|
30060
|
+
---
|
|
30061
|
+
|
|
30062
|
+
# Code Review
|
|
30063
|
+
|
|
30064
|
+
Please review the current code changes and provide feedback on:
|
|
30065
|
+
|
|
30066
|
+
1. **Code Quality**
|
|
30067
|
+
- Readability and maintainability
|
|
30068
|
+
- Following project conventions
|
|
30069
|
+
- Proper error handling
|
|
30070
|
+
|
|
30071
|
+
2. **Potential Issues**
|
|
30072
|
+
- Security vulnerabilities
|
|
30073
|
+
- Performance concerns
|
|
30074
|
+
- Edge cases not handled
|
|
30075
|
+
|
|
30076
|
+
3. **Suggestions**
|
|
30077
|
+
- Improvements to consider
|
|
30078
|
+
- Best practices to apply
|
|
30079
|
+
- Documentation needs
|
|
30080
|
+
|
|
30081
|
+
If there are staged git changes, focus on those. Otherwise, ask what code to review.
|
|
30082
|
+
`;
|
|
30083
|
+
const examplePath = join5(commandsDir, "review.md");
|
|
30084
|
+
if (!existsSync3(examplePath)) {
|
|
30085
|
+
writeFileSync(examplePath, exampleCommand);
|
|
30086
|
+
}
|
|
30087
|
+
let message = `
|
|
30088
|
+
**Initialized oldpal**
|
|
30089
|
+
|
|
30090
|
+
`;
|
|
30091
|
+
message += `Created: ${commandsDir}
|
|
30092
|
+
`;
|
|
30093
|
+
message += `Example: ${examplePath}
|
|
30094
|
+
|
|
30095
|
+
`;
|
|
30096
|
+
message += `You can now:
|
|
30097
|
+
`;
|
|
30098
|
+
message += ` - Add custom commands to .oldpal/commands/
|
|
30099
|
+
`;
|
|
30100
|
+
message += ` - Use /review to try the example command
|
|
30101
|
+
`;
|
|
30102
|
+
message += ` - Run /help to see all available commands
|
|
30103
|
+
`;
|
|
30104
|
+
context.emit("text", message);
|
|
30105
|
+
context.emit("done");
|
|
30106
|
+
return { handled: true };
|
|
30107
|
+
}
|
|
30108
|
+
};
|
|
30109
|
+
}
|
|
30110
|
+
costCommand() {
|
|
30111
|
+
return {
|
|
30112
|
+
name: "cost",
|
|
30113
|
+
description: "Show estimated API cost for this session",
|
|
30114
|
+
builtin: true,
|
|
30115
|
+
selfHandled: true,
|
|
30116
|
+
content: "",
|
|
30117
|
+
handler: async (args, context) => {
|
|
30118
|
+
const usage = this.tokenUsage;
|
|
30119
|
+
const inputCostPer1M = 3;
|
|
30120
|
+
const outputCostPer1M = 15;
|
|
30121
|
+
const inputCost = usage.inputTokens / 1e6 * inputCostPer1M;
|
|
30122
|
+
const outputCost = usage.outputTokens / 1e6 * outputCostPer1M;
|
|
30123
|
+
const totalCost = inputCost + outputCost;
|
|
30124
|
+
const cacheReadCostPer1M = 0.3;
|
|
30125
|
+
const cacheSavings = usage.cacheReadTokens ? usage.cacheReadTokens / 1e6 * (inputCostPer1M - cacheReadCostPer1M) : 0;
|
|
30126
|
+
let message = `
|
|
30127
|
+
**Estimated Session Cost**
|
|
30128
|
+
|
|
30129
|
+
`;
|
|
30130
|
+
message += `Input tokens: ${usage.inputTokens.toLocaleString()} (~$${inputCost.toFixed(4)})
|
|
30131
|
+
`;
|
|
30132
|
+
message += `Output tokens: ${usage.outputTokens.toLocaleString()} (~$${outputCost.toFixed(4)})
|
|
30133
|
+
`;
|
|
30134
|
+
message += `**Total: ~$${totalCost.toFixed(4)}**
|
|
30135
|
+
`;
|
|
30136
|
+
if (cacheSavings > 0) {
|
|
30137
|
+
message += `
|
|
30138
|
+
Cache savings: ~$${cacheSavings.toFixed(4)}
|
|
30139
|
+
`;
|
|
30140
|
+
}
|
|
30141
|
+
message += `
|
|
30142
|
+
*Based on Claude 3.5 Sonnet pricing*
|
|
30143
|
+
`;
|
|
30144
|
+
context.emit("text", message);
|
|
30145
|
+
context.emit("done");
|
|
30146
|
+
return { handled: true };
|
|
30147
|
+
}
|
|
30148
|
+
};
|
|
30149
|
+
}
|
|
30150
|
+
modelCommand() {
|
|
30151
|
+
return {
|
|
30152
|
+
name: "model",
|
|
30153
|
+
description: "Show current model information",
|
|
30154
|
+
builtin: true,
|
|
30155
|
+
selfHandled: true,
|
|
30156
|
+
content: "",
|
|
30157
|
+
handler: async (args, context) => {
|
|
30158
|
+
let message = `
|
|
30159
|
+
**Model Information**
|
|
30160
|
+
|
|
30161
|
+
`;
|
|
30162
|
+
message += `Current model: claude-3-5-sonnet-20241022
|
|
30163
|
+
`;
|
|
30164
|
+
message += `Context window: 200,000 tokens
|
|
30165
|
+
`;
|
|
30166
|
+
message += `Max output: 8,192 tokens
|
|
30167
|
+
|
|
30168
|
+
`;
|
|
30169
|
+
message += `*Model selection coming in a future update*
|
|
30170
|
+
`;
|
|
30171
|
+
context.emit("text", message);
|
|
30172
|
+
context.emit("done");
|
|
30173
|
+
return { handled: true };
|
|
30174
|
+
}
|
|
30175
|
+
};
|
|
30176
|
+
}
|
|
30177
|
+
memoryCommand() {
|
|
30178
|
+
return {
|
|
30179
|
+
name: "memory",
|
|
30180
|
+
description: "Show conversation summary and key memories",
|
|
30181
|
+
builtin: true,
|
|
30182
|
+
selfHandled: false,
|
|
30183
|
+
content: `Please provide a summary of our conversation so far, including:
|
|
30184
|
+
|
|
30185
|
+
1. **Key Context** - What you know about this project/codebase
|
|
30186
|
+
2. **Current Task** - What we're working on
|
|
30187
|
+
3. **Decisions Made** - Any choices or agreements from our discussion
|
|
30188
|
+
4. **Open Items** - Things we mentioned but haven't addressed yet
|
|
30189
|
+
|
|
30190
|
+
Keep it concise but comprehensive.`
|
|
30191
|
+
};
|
|
30192
|
+
}
|
|
30193
|
+
bugCommand() {
|
|
30194
|
+
return {
|
|
30195
|
+
name: "bug",
|
|
30196
|
+
description: "Analyze and help fix a bug",
|
|
30197
|
+
builtin: true,
|
|
30198
|
+
selfHandled: false,
|
|
30199
|
+
content: `Help me debug an issue. $ARGUMENTS
|
|
30200
|
+
|
|
30201
|
+
Please:
|
|
30202
|
+
1. Understand the bug/error described
|
|
30203
|
+
2. Identify likely causes
|
|
30204
|
+
3. Search relevant code files
|
|
30205
|
+
4. Propose a fix with code changes
|
|
30206
|
+
|
|
30207
|
+
If no bug is described, ask me to describe the issue I'm experiencing.`
|
|
30208
|
+
};
|
|
30209
|
+
}
|
|
30210
|
+
prCommand() {
|
|
30211
|
+
return {
|
|
30212
|
+
name: "pr",
|
|
30213
|
+
description: "Create a pull request for current changes",
|
|
30214
|
+
builtin: true,
|
|
30215
|
+
selfHandled: false,
|
|
30216
|
+
content: `Help me create a pull request for the current changes.
|
|
30217
|
+
|
|
30218
|
+
1. First, check git status and staged changes
|
|
30219
|
+
2. Review the diff to understand what changed
|
|
30220
|
+
3. Write a clear PR title (max 72 chars)
|
|
30221
|
+
4. Write a description with:
|
|
30222
|
+
- Summary of changes
|
|
30223
|
+
- Motivation/context
|
|
30224
|
+
- Testing done
|
|
30225
|
+
- Any notes for reviewers
|
|
30226
|
+
|
|
30227
|
+
Then create the PR using the gh CLI.`
|
|
30228
|
+
};
|
|
30229
|
+
}
|
|
30230
|
+
reviewCommand() {
|
|
30231
|
+
return {
|
|
30232
|
+
name: "review",
|
|
30233
|
+
description: "Review code changes for issues",
|
|
30234
|
+
builtin: true,
|
|
30235
|
+
selfHandled: false,
|
|
30236
|
+
content: `Review the current code changes. $ARGUMENTS
|
|
30237
|
+
|
|
30238
|
+
Check for:
|
|
30239
|
+
1. **Bugs** - Logic errors, edge cases, null checks
|
|
30240
|
+
2. **Security** - Input validation, injection risks, secrets
|
|
30241
|
+
3. **Performance** - N+1 queries, unnecessary loops, memory leaks
|
|
30242
|
+
4. **Style** - Naming, formatting, code organization
|
|
30243
|
+
5. **Tests** - Coverage, edge cases, assertions
|
|
30244
|
+
|
|
30245
|
+
If there are staged changes, review those. Otherwise, ask what to review.`
|
|
30246
|
+
};
|
|
30247
|
+
}
|
|
30248
|
+
}
|
|
29647
30249
|
// packages/core/src/llm/client.ts
|
|
29648
30250
|
async function createLLMClient(config) {
|
|
29649
30251
|
if (config.provider === "anthropic") {
|
|
@@ -29654,8 +30256,8 @@ async function createLLMClient(config) {
|
|
|
29654
30256
|
}
|
|
29655
30257
|
|
|
29656
30258
|
// packages/core/src/config.ts
|
|
29657
|
-
import { join as
|
|
29658
|
-
import { homedir as
|
|
30259
|
+
import { join as join7 } from "path";
|
|
30260
|
+
import { homedir as homedir6 } from "os";
|
|
29659
30261
|
var DEFAULT_CONFIG = {
|
|
29660
30262
|
llm: {
|
|
29661
30263
|
provider: "anthropic",
|
|
@@ -29685,13 +30287,13 @@ var DEFAULT_CONFIG = {
|
|
|
29685
30287
|
]
|
|
29686
30288
|
};
|
|
29687
30289
|
function getConfigDir() {
|
|
29688
|
-
return
|
|
30290
|
+
return join7(homedir6(), ".oldpal");
|
|
29689
30291
|
}
|
|
29690
30292
|
function getConfigPath(filename) {
|
|
29691
|
-
return
|
|
30293
|
+
return join7(getConfigDir(), filename);
|
|
29692
30294
|
}
|
|
29693
30295
|
function getProjectConfigDir(cwd2 = process.cwd()) {
|
|
29694
|
-
return
|
|
30296
|
+
return join7(cwd2, ".oldpal");
|
|
29695
30297
|
}
|
|
29696
30298
|
async function loadConfig(cwd2 = process.cwd()) {
|
|
29697
30299
|
const config = { ...DEFAULT_CONFIG };
|
|
@@ -29704,7 +30306,7 @@ async function loadConfig(cwd2 = process.cwd()) {
|
|
|
29704
30306
|
if (userConfig.voice)
|
|
29705
30307
|
config.voice = { ...config.voice, ...userConfig.voice };
|
|
29706
30308
|
}
|
|
29707
|
-
const projectConfigPath =
|
|
30309
|
+
const projectConfigPath = join7(getProjectConfigDir(cwd2), "settings.json");
|
|
29708
30310
|
const projectConfig = await loadJsonFile(projectConfigPath);
|
|
29709
30311
|
if (projectConfig) {
|
|
29710
30312
|
Object.assign(config, projectConfig);
|
|
@@ -29713,7 +30315,7 @@ async function loadConfig(cwd2 = process.cwd()) {
|
|
|
29713
30315
|
if (projectConfig.voice)
|
|
29714
30316
|
config.voice = { ...config.voice, ...projectConfig.voice };
|
|
29715
30317
|
}
|
|
29716
|
-
const localConfigPath =
|
|
30318
|
+
const localConfigPath = join7(getProjectConfigDir(cwd2), "settings.local.json");
|
|
29717
30319
|
const localConfig = await loadJsonFile(localConfigPath);
|
|
29718
30320
|
if (localConfig) {
|
|
29719
30321
|
Object.assign(config, localConfig);
|
|
@@ -29731,7 +30333,7 @@ async function loadHooksConfig(cwd2 = process.cwd()) {
|
|
|
29731
30333
|
if (userHooks?.hooks) {
|
|
29732
30334
|
mergeHooks(hooks, userHooks.hooks);
|
|
29733
30335
|
}
|
|
29734
|
-
const projectHooksPath =
|
|
30336
|
+
const projectHooksPath = join7(getProjectConfigDir(cwd2), "hooks.json");
|
|
29735
30337
|
const projectHooks = await loadJsonFile(projectHooksPath);
|
|
29736
30338
|
if (projectHooks?.hooks) {
|
|
29737
30339
|
mergeHooks(hooks, projectHooks.hooks);
|
|
@@ -29767,16 +30369,22 @@ class AgentLoop {
|
|
|
29767
30369
|
skillExecutor;
|
|
29768
30370
|
hookLoader;
|
|
29769
30371
|
hookExecutor;
|
|
30372
|
+
commandLoader;
|
|
30373
|
+
commandExecutor;
|
|
30374
|
+
builtinCommands;
|
|
29770
30375
|
llmClient = null;
|
|
29771
30376
|
config = null;
|
|
29772
30377
|
cwd;
|
|
30378
|
+
sessionId;
|
|
29773
30379
|
isRunning = false;
|
|
29774
30380
|
shouldStop = false;
|
|
29775
30381
|
onChunk;
|
|
29776
30382
|
onToolStart;
|
|
29777
30383
|
onToolEnd;
|
|
30384
|
+
onTokenUsage;
|
|
29778
30385
|
constructor(options = {}) {
|
|
29779
30386
|
this.cwd = options.cwd || process.cwd();
|
|
30387
|
+
this.sessionId = options.sessionId || generateId();
|
|
29780
30388
|
this.context = new AgentContext;
|
|
29781
30389
|
this.toolRegistry = new ToolRegistry;
|
|
29782
30390
|
this.connectorBridge = new ConnectorBridge;
|
|
@@ -29784,9 +30392,13 @@ class AgentLoop {
|
|
|
29784
30392
|
this.skillExecutor = new SkillExecutor;
|
|
29785
30393
|
this.hookLoader = new HookLoader;
|
|
29786
30394
|
this.hookExecutor = new HookExecutor;
|
|
30395
|
+
this.commandLoader = new CommandLoader(this.cwd);
|
|
30396
|
+
this.commandExecutor = new CommandExecutor(this.commandLoader);
|
|
30397
|
+
this.builtinCommands = new BuiltinCommands;
|
|
29787
30398
|
this.onChunk = options.onChunk;
|
|
29788
30399
|
this.onToolStart = options.onToolStart;
|
|
29789
30400
|
this.onToolEnd = options.onToolEnd;
|
|
30401
|
+
this.onTokenUsage = options.onTokenUsage;
|
|
29790
30402
|
}
|
|
29791
30403
|
async initialize() {
|
|
29792
30404
|
this.config = await loadConfig(this.cwd);
|
|
@@ -29797,10 +30409,12 @@ class AgentLoop {
|
|
|
29797
30409
|
await this.connectorBridge.discover(this.config.connectors);
|
|
29798
30410
|
this.connectorBridge.registerAll(this.toolRegistry);
|
|
29799
30411
|
await this.skillLoader.loadAll(this.cwd);
|
|
30412
|
+
await this.commandLoader.loadAll();
|
|
30413
|
+
this.builtinCommands.registerAll(this.commandLoader);
|
|
29800
30414
|
const hooksConfig = await loadHooksConfig(this.cwd);
|
|
29801
30415
|
this.hookLoader.load(hooksConfig);
|
|
29802
30416
|
await this.hookExecutor.execute(this.hookLoader.getHooks("SessionStart"), {
|
|
29803
|
-
session_id:
|
|
30417
|
+
session_id: this.sessionId,
|
|
29804
30418
|
hook_event_name: "SessionStart",
|
|
29805
30419
|
cwd: this.cwd
|
|
29806
30420
|
});
|
|
@@ -29816,7 +30430,7 @@ class AgentLoop {
|
|
|
29816
30430
|
this.shouldStop = false;
|
|
29817
30431
|
try {
|
|
29818
30432
|
const promptHookResult = await this.hookExecutor.execute(this.hookLoader.getHooks("UserPromptSubmit"), {
|
|
29819
|
-
session_id:
|
|
30433
|
+
session_id: this.sessionId,
|
|
29820
30434
|
hook_event_name: "UserPromptSubmit",
|
|
29821
30435
|
cwd: this.cwd,
|
|
29822
30436
|
prompt: userMessage
|
|
@@ -29825,6 +30439,18 @@ class AgentLoop {
|
|
|
29825
30439
|
this.emit({ type: "error", error: promptHookResult.stopReason || "Blocked by hook" });
|
|
29826
30440
|
return;
|
|
29827
30441
|
}
|
|
30442
|
+
if (userMessage.startsWith("/")) {
|
|
30443
|
+
const commandResult = await this.handleCommand(userMessage);
|
|
30444
|
+
if (commandResult.handled) {
|
|
30445
|
+
if (commandResult.clearConversation) {
|
|
30446
|
+
this.context = new AgentContext;
|
|
30447
|
+
}
|
|
30448
|
+
return;
|
|
30449
|
+
}
|
|
30450
|
+
if (commandResult.prompt) {
|
|
30451
|
+
userMessage = commandResult.prompt;
|
|
30452
|
+
}
|
|
30453
|
+
}
|
|
29828
30454
|
if (userMessage.startsWith("/")) {
|
|
29829
30455
|
const handled = await this.handleSkillInvocation(userMessage);
|
|
29830
30456
|
if (handled)
|
|
@@ -29905,6 +30531,30 @@ class AgentLoop {
|
|
|
29905
30531
|
}
|
|
29906
30532
|
return results;
|
|
29907
30533
|
}
|
|
30534
|
+
async handleCommand(message) {
|
|
30535
|
+
const context = {
|
|
30536
|
+
cwd: this.cwd,
|
|
30537
|
+
sessionId: this.sessionId,
|
|
30538
|
+
messages: this.context.getMessages(),
|
|
30539
|
+
tools: this.toolRegistry.getTools(),
|
|
30540
|
+
clearMessages: () => {
|
|
30541
|
+
this.context = new AgentContext;
|
|
30542
|
+
},
|
|
30543
|
+
addSystemMessage: (content) => {
|
|
30544
|
+
this.context.addSystemMessage(content);
|
|
30545
|
+
},
|
|
30546
|
+
emit: (type, content) => {
|
|
30547
|
+
if (type === "text" && content) {
|
|
30548
|
+
this.emit({ type: "text", content });
|
|
30549
|
+
} else if (type === "done") {
|
|
30550
|
+
this.emit({ type: "done" });
|
|
30551
|
+
} else if (type === "error" && content) {
|
|
30552
|
+
this.emit({ type: "error", error: content });
|
|
30553
|
+
}
|
|
30554
|
+
}
|
|
30555
|
+
};
|
|
30556
|
+
return this.commandExecutor.execute(message, context);
|
|
30557
|
+
}
|
|
29908
30558
|
async handleSkillInvocation(message) {
|
|
29909
30559
|
const match = message.match(/^\/(\S+)(?:\s+(.*))?$/);
|
|
29910
30560
|
if (!match)
|
|
@@ -29936,18 +30586,34 @@ class AgentLoop {
|
|
|
29936
30586
|
getSkills() {
|
|
29937
30587
|
return this.skillLoader.getSkills();
|
|
29938
30588
|
}
|
|
30589
|
+
getCommands() {
|
|
30590
|
+
return this.commandLoader.getCommands();
|
|
30591
|
+
}
|
|
30592
|
+
getTokenUsage() {
|
|
30593
|
+
return this.builtinCommands.getTokenUsage();
|
|
30594
|
+
}
|
|
30595
|
+
updateTokenUsage(usage) {
|
|
30596
|
+
this.builtinCommands.updateTokenUsage(usage);
|
|
30597
|
+
this.onTokenUsage?.(this.builtinCommands.getTokenUsage());
|
|
30598
|
+
}
|
|
29939
30599
|
isProcessing() {
|
|
29940
30600
|
return this.isRunning;
|
|
29941
30601
|
}
|
|
30602
|
+
getSessionId() {
|
|
30603
|
+
return this.sessionId;
|
|
30604
|
+
}
|
|
30605
|
+
clearConversation() {
|
|
30606
|
+
this.context = new AgentContext;
|
|
30607
|
+
}
|
|
29942
30608
|
}
|
|
29943
30609
|
|
|
29944
30610
|
// packages/core/src/index.ts
|
|
29945
30611
|
init_anthropic();
|
|
29946
30612
|
|
|
29947
30613
|
// packages/core/src/logger.ts
|
|
29948
|
-
import { existsSync as
|
|
29949
|
-
import { join as
|
|
29950
|
-
import { homedir as
|
|
30614
|
+
import { existsSync as existsSync5, mkdirSync as mkdirSync2, appendFileSync } from "fs";
|
|
30615
|
+
import { join as join8 } from "path";
|
|
30616
|
+
import { homedir as homedir7 } from "os";
|
|
29951
30617
|
|
|
29952
30618
|
class Logger {
|
|
29953
30619
|
logDir;
|
|
@@ -29955,14 +30621,14 @@ class Logger {
|
|
|
29955
30621
|
sessionId;
|
|
29956
30622
|
constructor(sessionId) {
|
|
29957
30623
|
this.sessionId = sessionId;
|
|
29958
|
-
this.logDir =
|
|
30624
|
+
this.logDir = join8(homedir7(), ".oldpal", "logs");
|
|
29959
30625
|
this.ensureDir(this.logDir);
|
|
29960
30626
|
const date = new Date().toISOString().split("T")[0];
|
|
29961
|
-
this.logFile =
|
|
30627
|
+
this.logFile = join8(this.logDir, `${date}.log`);
|
|
29962
30628
|
}
|
|
29963
30629
|
ensureDir(dir) {
|
|
29964
|
-
if (!
|
|
29965
|
-
|
|
30630
|
+
if (!existsSync5(dir)) {
|
|
30631
|
+
mkdirSync2(dir, { recursive: true });
|
|
29966
30632
|
}
|
|
29967
30633
|
}
|
|
29968
30634
|
write(level, message, data) {
|
|
@@ -30001,13 +30667,13 @@ class SessionStorage {
|
|
|
30001
30667
|
sessionId;
|
|
30002
30668
|
constructor(sessionId) {
|
|
30003
30669
|
this.sessionId = sessionId;
|
|
30004
|
-
this.sessionsDir =
|
|
30670
|
+
this.sessionsDir = join8(homedir7(), ".oldpal", "sessions");
|
|
30005
30671
|
this.ensureDir(this.sessionsDir);
|
|
30006
|
-
this.sessionFile =
|
|
30672
|
+
this.sessionFile = join8(this.sessionsDir, `${sessionId}.json`);
|
|
30007
30673
|
}
|
|
30008
30674
|
ensureDir(dir) {
|
|
30009
|
-
if (!
|
|
30010
|
-
|
|
30675
|
+
if (!existsSync5(dir)) {
|
|
30676
|
+
mkdirSync2(dir, { recursive: true });
|
|
30011
30677
|
}
|
|
30012
30678
|
}
|
|
30013
30679
|
save(data) {
|
|
@@ -30020,16 +30686,16 @@ class SessionStorage {
|
|
|
30020
30686
|
}
|
|
30021
30687
|
}
|
|
30022
30688
|
function initOldpalDir() {
|
|
30023
|
-
const baseDir =
|
|
30689
|
+
const baseDir = join8(homedir7(), ".oldpal");
|
|
30024
30690
|
const dirs = [
|
|
30025
30691
|
baseDir,
|
|
30026
|
-
|
|
30027
|
-
|
|
30028
|
-
|
|
30692
|
+
join8(baseDir, "sessions"),
|
|
30693
|
+
join8(baseDir, "logs"),
|
|
30694
|
+
join8(baseDir, "skills")
|
|
30029
30695
|
];
|
|
30030
30696
|
for (const dir of dirs) {
|
|
30031
|
-
if (!
|
|
30032
|
-
|
|
30697
|
+
if (!existsSync5(dir)) {
|
|
30698
|
+
mkdirSync2(dir, { recursive: true });
|
|
30033
30699
|
}
|
|
30034
30700
|
}
|
|
30035
30701
|
}
|
|
@@ -30166,6 +30832,20 @@ class EmbeddedClient {
|
|
|
30166
30832
|
getSessionId() {
|
|
30167
30833
|
return this.session.getSessionId();
|
|
30168
30834
|
}
|
|
30835
|
+
async getCommands() {
|
|
30836
|
+
if (!this.initialized) {
|
|
30837
|
+
await this.initialize();
|
|
30838
|
+
}
|
|
30839
|
+
return this.agent.getCommands();
|
|
30840
|
+
}
|
|
30841
|
+
getTokenUsage() {
|
|
30842
|
+
return this.agent.getTokenUsage();
|
|
30843
|
+
}
|
|
30844
|
+
clearConversation() {
|
|
30845
|
+
this.agent.clearConversation();
|
|
30846
|
+
this.messages = [];
|
|
30847
|
+
this.logger.info("Conversation cleared");
|
|
30848
|
+
}
|
|
30169
30849
|
}
|
|
30170
30850
|
// packages/terminal/src/components/Input.tsx
|
|
30171
30851
|
var import_react23 = __toESM(require_react(), 1);
|
|
@@ -30516,10 +31196,17 @@ function truncate(text, maxLength) {
|
|
|
30516
31196
|
|
|
30517
31197
|
// packages/terminal/src/components/Status.tsx
|
|
30518
31198
|
var jsx_dev_runtime4 = __toESM(require_jsx_dev_runtime(), 1);
|
|
30519
|
-
function Status({ isProcessing, cwd: cwd2, queueLength = 0 }) {
|
|
30520
|
-
const maxCwdLength =
|
|
31199
|
+
function Status({ isProcessing, cwd: cwd2, queueLength = 0, tokenUsage }) {
|
|
31200
|
+
const maxCwdLength = 30;
|
|
30521
31201
|
const displayCwd = cwd2.length > maxCwdLength ? "..." + cwd2.slice(-(maxCwdLength - 3)) : cwd2;
|
|
30522
31202
|
const queueInfo = queueLength > 0 ? ` | ${queueLength} queued` : "";
|
|
31203
|
+
let tokenInfo = "";
|
|
31204
|
+
if (tokenUsage && tokenUsage.totalTokens > 0) {
|
|
31205
|
+
const used = Math.round(tokenUsage.totalTokens / 1000);
|
|
31206
|
+
const max = Math.round(tokenUsage.maxContextTokens / 1000);
|
|
31207
|
+
const percent = Math.round(tokenUsage.totalTokens / tokenUsage.maxContextTokens * 100);
|
|
31208
|
+
tokenInfo = ` | ${used}k/${max}k (${percent}%)`;
|
|
31209
|
+
}
|
|
30523
31210
|
return /* @__PURE__ */ jsx_dev_runtime4.jsxDEV(Box_default, {
|
|
30524
31211
|
marginTop: 1,
|
|
30525
31212
|
borderStyle: "single",
|
|
@@ -30537,6 +31224,10 @@ function Status({ isProcessing, cwd: cwd2, queueLength = 0 }) {
|
|
|
30537
31224
|
dimColor: !isProcessing,
|
|
30538
31225
|
children: isProcessing ? "\u25CF processing" : "\u25CF ready"
|
|
30539
31226
|
}, undefined, false, undefined, this),
|
|
31227
|
+
/* @__PURE__ */ jsx_dev_runtime4.jsxDEV(Text, {
|
|
31228
|
+
dimColor: true,
|
|
31229
|
+
children: tokenInfo
|
|
31230
|
+
}, undefined, false, undefined, this),
|
|
30540
31231
|
/* @__PURE__ */ jsx_dev_runtime4.jsxDEV(Text, {
|
|
30541
31232
|
dimColor: true,
|
|
30542
31233
|
children: queueInfo
|
|
@@ -30545,7 +31236,8 @@ function Status({ isProcessing, cwd: cwd2, queueLength = 0 }) {
|
|
|
30545
31236
|
dimColor: true,
|
|
30546
31237
|
children: [
|
|
30547
31238
|
" | ",
|
|
30548
|
-
isProcessing ? "Esc to stop" : "Ctrl+C to exit"
|
|
31239
|
+
isProcessing ? "Esc to stop" : "Ctrl+C to exit",
|
|
31240
|
+
" | /help"
|
|
30549
31241
|
]
|
|
30550
31242
|
}, undefined, true, undefined, this)
|
|
30551
31243
|
]
|
|
@@ -30611,6 +31303,7 @@ function App2({ cwd: cwd2 }) {
|
|
|
30611
31303
|
const [error, setError] = import_react25.useState(null);
|
|
30612
31304
|
const [messageQueue, setMessageQueue] = import_react25.useState([]);
|
|
30613
31305
|
const [activityLog, setActivityLog] = import_react25.useState([]);
|
|
31306
|
+
const [tokenUsage, setTokenUsage] = import_react25.useState();
|
|
30614
31307
|
const responseRef = import_react25.useRef("");
|
|
30615
31308
|
const clientRef = import_react25.useRef(null);
|
|
30616
31309
|
const toolCallsRef = import_react25.useRef([]);
|
|
@@ -30676,6 +31369,8 @@ function App2({ cwd: cwd2 }) {
|
|
|
30676
31369
|
} else if (chunk.type === "error" && chunk.error) {
|
|
30677
31370
|
setError(chunk.error);
|
|
30678
31371
|
setIsProcessing(false);
|
|
31372
|
+
} else if (chunk.type === "usage" && chunk.usage) {
|
|
31373
|
+
setTokenUsage(chunk.usage);
|
|
30679
31374
|
} else if (chunk.type === "done") {
|
|
30680
31375
|
if (responseRef.current || toolCallsRef.current.length > 0) {
|
|
30681
31376
|
setMessages((prev) => [
|
|
@@ -30697,6 +31392,9 @@ function App2({ cwd: cwd2 }) {
|
|
|
30697
31392
|
setCurrentToolCall(undefined);
|
|
30698
31393
|
setLastToolResult(undefined);
|
|
30699
31394
|
setIsProcessing(false);
|
|
31395
|
+
if (newClient) {
|
|
31396
|
+
setTokenUsage(newClient.getTokenUsage());
|
|
31397
|
+
}
|
|
30700
31398
|
}
|
|
30701
31399
|
});
|
|
30702
31400
|
newClient.onError((err) => {
|
|
@@ -30864,7 +31562,8 @@ function App2({ cwd: cwd2 }) {
|
|
|
30864
31562
|
/* @__PURE__ */ jsx_dev_runtime6.jsxDEV(Status, {
|
|
30865
31563
|
isProcessing,
|
|
30866
31564
|
cwd: cwd2,
|
|
30867
|
-
queueLength: messageQueue.length
|
|
31565
|
+
queueLength: messageQueue.length,
|
|
31566
|
+
tokenUsage
|
|
30868
31567
|
}, undefined, false, undefined, this)
|
|
30869
31568
|
]
|
|
30870
31569
|
}, undefined, true, undefined, this);
|
|
@@ -30879,7 +31578,7 @@ var options = {
|
|
|
30879
31578
|
help: args.includes("--help") || args.includes("-h")
|
|
30880
31579
|
};
|
|
30881
31580
|
if (options.version) {
|
|
30882
|
-
console.log("oldpal v0.1.
|
|
31581
|
+
console.log("oldpal v0.1.9");
|
|
30883
31582
|
process.exit(0);
|
|
30884
31583
|
}
|
|
30885
31584
|
if (options.help) {
|
|
@@ -30910,4 +31609,4 @@ waitUntilExit().then(() => {
|
|
|
30910
31609
|
process.exit(0);
|
|
30911
31610
|
});
|
|
30912
31611
|
|
|
30913
|
-
//# debugId=
|
|
31612
|
+
//# debugId=307434E7D5151E5864756E2164756E21
|