@hasna/oldpal 0.1.8 → 0.2.0
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 +902 -81
- package/dist/index.js.map +14 -11
- 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]+)["']?/);
|
|
@@ -29368,10 +29368,101 @@ function parseDuckDuckGoResults(html, maxResults) {
|
|
|
29368
29368
|
return results;
|
|
29369
29369
|
}
|
|
29370
29370
|
|
|
29371
|
+
class CurlTool {
|
|
29372
|
+
static tool = {
|
|
29373
|
+
name: "curl",
|
|
29374
|
+
description: "Fetch content from a URL (like curl). Returns text content from web pages, JSON from APIs, etc.",
|
|
29375
|
+
parameters: {
|
|
29376
|
+
type: "object",
|
|
29377
|
+
properties: {
|
|
29378
|
+
url: {
|
|
29379
|
+
type: "string",
|
|
29380
|
+
description: "The URL to fetch"
|
|
29381
|
+
},
|
|
29382
|
+
method: {
|
|
29383
|
+
type: "string",
|
|
29384
|
+
description: "HTTP method (GET, POST, PUT, DELETE). Defaults to GET.",
|
|
29385
|
+
enum: ["GET", "POST", "PUT", "DELETE"],
|
|
29386
|
+
default: "GET"
|
|
29387
|
+
},
|
|
29388
|
+
headers: {
|
|
29389
|
+
type: "object",
|
|
29390
|
+
description: "Optional headers to send with the request"
|
|
29391
|
+
},
|
|
29392
|
+
body: {
|
|
29393
|
+
type: "string",
|
|
29394
|
+
description: "Request body for POST/PUT requests"
|
|
29395
|
+
}
|
|
29396
|
+
},
|
|
29397
|
+
required: ["url"]
|
|
29398
|
+
}
|
|
29399
|
+
};
|
|
29400
|
+
static executor = async (input) => {
|
|
29401
|
+
const url = input.url;
|
|
29402
|
+
const method = input.method || "GET";
|
|
29403
|
+
const headers = input.headers || {};
|
|
29404
|
+
const body = input.body;
|
|
29405
|
+
const timeout = 30000;
|
|
29406
|
+
try {
|
|
29407
|
+
const parsedUrl = new URL(url);
|
|
29408
|
+
const hostname = parsedUrl.hostname;
|
|
29409
|
+
if (hostname === "localhost" || hostname === "127.0.0.1" || hostname.startsWith("192.168.") || hostname.startsWith("10.") || hostname.startsWith("172.")) {
|
|
29410
|
+
return "Error: Cannot fetch from local/private network addresses for security reasons";
|
|
29411
|
+
}
|
|
29412
|
+
const controller = new AbortController;
|
|
29413
|
+
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
|
29414
|
+
const response = await fetch(url, {
|
|
29415
|
+
method,
|
|
29416
|
+
signal: controller.signal,
|
|
29417
|
+
headers: {
|
|
29418
|
+
"User-Agent": "oldpal/1.0 (AI Assistant)",
|
|
29419
|
+
...headers
|
|
29420
|
+
},
|
|
29421
|
+
body: body && ["POST", "PUT"].includes(method) ? body : undefined
|
|
29422
|
+
});
|
|
29423
|
+
clearTimeout(timeoutId);
|
|
29424
|
+
const contentType = response.headers.get("content-type") || "";
|
|
29425
|
+
let responseBody;
|
|
29426
|
+
if (contentType.includes("application/json")) {
|
|
29427
|
+
try {
|
|
29428
|
+
const json = await response.json();
|
|
29429
|
+
responseBody = JSON.stringify(json, null, 2);
|
|
29430
|
+
} catch {
|
|
29431
|
+
responseBody = await response.text();
|
|
29432
|
+
}
|
|
29433
|
+
} else {
|
|
29434
|
+
responseBody = await response.text();
|
|
29435
|
+
if (contentType.includes("text/html")) {
|
|
29436
|
+
responseBody = extractReadableText(responseBody);
|
|
29437
|
+
}
|
|
29438
|
+
}
|
|
29439
|
+
const maxLength = 30000;
|
|
29440
|
+
if (responseBody.length > maxLength) {
|
|
29441
|
+
responseBody = responseBody.slice(0, maxLength) + `
|
|
29442
|
+
|
|
29443
|
+
[Content truncated...]`;
|
|
29444
|
+
}
|
|
29445
|
+
const statusLine = `HTTP ${response.status} ${response.statusText}`;
|
|
29446
|
+
return `${statusLine}
|
|
29447
|
+
|
|
29448
|
+
${responseBody || "(empty response)"}`;
|
|
29449
|
+
} catch (error) {
|
|
29450
|
+
if (error instanceof Error) {
|
|
29451
|
+
if (error.name === "AbortError") {
|
|
29452
|
+
return `Error: Request timed out after ${timeout}ms`;
|
|
29453
|
+
}
|
|
29454
|
+
return `Error: ${error.message}`;
|
|
29455
|
+
}
|
|
29456
|
+
return `Error: ${String(error)}`;
|
|
29457
|
+
}
|
|
29458
|
+
};
|
|
29459
|
+
}
|
|
29460
|
+
|
|
29371
29461
|
class WebTools {
|
|
29372
29462
|
static registerAll(registry) {
|
|
29373
29463
|
registry.register(WebFetchTool.tool, WebFetchTool.executor);
|
|
29374
29464
|
registry.register(WebSearchTool.tool, WebSearchTool.executor);
|
|
29465
|
+
registry.register(CurlTool.tool, CurlTool.executor);
|
|
29375
29466
|
}
|
|
29376
29467
|
}
|
|
29377
29468
|
|
|
@@ -29643,7 +29734,609 @@ class HookExecutor {
|
|
|
29643
29734
|
return null;
|
|
29644
29735
|
}
|
|
29645
29736
|
}
|
|
29737
|
+
// packages/core/src/commands/loader.ts
|
|
29738
|
+
import { existsSync as existsSync2, readdirSync, statSync } from "fs";
|
|
29739
|
+
import { join as join4, basename, extname } from "path";
|
|
29740
|
+
import { homedir as homedir3 } from "os";
|
|
29741
|
+
|
|
29742
|
+
class CommandLoader {
|
|
29743
|
+
commands = new Map;
|
|
29744
|
+
cwd;
|
|
29745
|
+
constructor(cwd2) {
|
|
29746
|
+
this.cwd = cwd2 || process.cwd();
|
|
29747
|
+
}
|
|
29748
|
+
async loadAll() {
|
|
29749
|
+
this.commands.clear();
|
|
29750
|
+
const globalDir = join4(homedir3(), ".oldpal", "commands");
|
|
29751
|
+
await this.loadFromDirectory(globalDir, "global");
|
|
29752
|
+
const projectDir = join4(this.cwd, ".oldpal", "commands");
|
|
29753
|
+
await this.loadFromDirectory(projectDir, "project");
|
|
29754
|
+
}
|
|
29755
|
+
async loadFromDirectory(dir, source, prefix = "") {
|
|
29756
|
+
if (!existsSync2(dir))
|
|
29757
|
+
return;
|
|
29758
|
+
const entries = readdirSync(dir);
|
|
29759
|
+
for (const entry of entries) {
|
|
29760
|
+
const fullPath = join4(dir, entry);
|
|
29761
|
+
const stat = statSync(fullPath);
|
|
29762
|
+
if (stat.isDirectory()) {
|
|
29763
|
+
const newPrefix = prefix ? `${prefix}:${entry}` : entry;
|
|
29764
|
+
await this.loadFromDirectory(fullPath, source, newPrefix);
|
|
29765
|
+
} else if (stat.isFile() && extname(entry) === ".md") {
|
|
29766
|
+
const command = await this.loadCommandFile(fullPath, prefix);
|
|
29767
|
+
if (command) {
|
|
29768
|
+
this.commands.set(command.name, command);
|
|
29769
|
+
}
|
|
29770
|
+
}
|
|
29771
|
+
}
|
|
29772
|
+
}
|
|
29773
|
+
async loadCommandFile(filePath, prefix) {
|
|
29774
|
+
try {
|
|
29775
|
+
const content = await Bun.file(filePath).text();
|
|
29776
|
+
const { frontmatter, body } = this.parseFrontmatter(content);
|
|
29777
|
+
const fileName = basename(filePath, ".md");
|
|
29778
|
+
const name = frontmatter.name || (prefix ? `${prefix}:${fileName}` : fileName);
|
|
29779
|
+
return {
|
|
29780
|
+
name,
|
|
29781
|
+
description: frontmatter.description || `Run the ${name} command`,
|
|
29782
|
+
tags: frontmatter.tags,
|
|
29783
|
+
allowedTools: frontmatter["allowed-tools"]?.split(",").map((t) => t.trim()),
|
|
29784
|
+
content: body,
|
|
29785
|
+
filePath,
|
|
29786
|
+
builtin: false
|
|
29787
|
+
};
|
|
29788
|
+
} catch (error) {
|
|
29789
|
+
console.error(`Failed to load command from ${filePath}:`, error);
|
|
29790
|
+
return null;
|
|
29791
|
+
}
|
|
29792
|
+
}
|
|
29793
|
+
parseFrontmatter(content) {
|
|
29794
|
+
const frontmatterRegex = /^---\s*\n([\s\S]*?)\n---\s*\n([\s\S]*)$/;
|
|
29795
|
+
const match = content.match(frontmatterRegex);
|
|
29796
|
+
if (!match) {
|
|
29797
|
+
return { frontmatter: {}, body: content };
|
|
29798
|
+
}
|
|
29799
|
+
const [, yamlContent, body] = match;
|
|
29800
|
+
const frontmatter = {};
|
|
29801
|
+
const lines = yamlContent.split(`
|
|
29802
|
+
`);
|
|
29803
|
+
for (const line of lines) {
|
|
29804
|
+
const colonIndex = line.indexOf(":");
|
|
29805
|
+
if (colonIndex === -1)
|
|
29806
|
+
continue;
|
|
29807
|
+
const key = line.slice(0, colonIndex).trim();
|
|
29808
|
+
let value = line.slice(colonIndex + 1).trim();
|
|
29809
|
+
if (value.startsWith("[") && value.endsWith("]")) {
|
|
29810
|
+
const arrayContent = value.slice(1, -1);
|
|
29811
|
+
frontmatter[key] = arrayContent.split(",").map((v) => v.trim());
|
|
29812
|
+
} else {
|
|
29813
|
+
if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
|
|
29814
|
+
value = value.slice(1, -1);
|
|
29815
|
+
}
|
|
29816
|
+
frontmatter[key] = value;
|
|
29817
|
+
}
|
|
29818
|
+
}
|
|
29819
|
+
return { frontmatter, body: body.trim() };
|
|
29820
|
+
}
|
|
29821
|
+
register(command) {
|
|
29822
|
+
this.commands.set(command.name, command);
|
|
29823
|
+
}
|
|
29824
|
+
getCommand(name) {
|
|
29825
|
+
return this.commands.get(name);
|
|
29826
|
+
}
|
|
29827
|
+
getCommands() {
|
|
29828
|
+
return Array.from(this.commands.values());
|
|
29829
|
+
}
|
|
29830
|
+
hasCommand(name) {
|
|
29831
|
+
return this.commands.has(name);
|
|
29832
|
+
}
|
|
29833
|
+
findMatching(partial) {
|
|
29834
|
+
const lower = partial.toLowerCase();
|
|
29835
|
+
return this.getCommands().filter((cmd) => cmd.name.toLowerCase().startsWith(lower) || cmd.description.toLowerCase().includes(lower));
|
|
29836
|
+
}
|
|
29837
|
+
}
|
|
29838
|
+
// packages/core/src/commands/executor.ts
|
|
29839
|
+
class CommandExecutor {
|
|
29840
|
+
loader;
|
|
29841
|
+
constructor(loader) {
|
|
29842
|
+
this.loader = loader;
|
|
29843
|
+
}
|
|
29844
|
+
parseCommand(input) {
|
|
29845
|
+
const trimmed = input.trim();
|
|
29846
|
+
if (!trimmed.startsWith("/")) {
|
|
29847
|
+
return null;
|
|
29848
|
+
}
|
|
29849
|
+
const match = trimmed.match(/^\/(\S+)(?:\s+(.*))?$/);
|
|
29850
|
+
if (!match) {
|
|
29851
|
+
return null;
|
|
29852
|
+
}
|
|
29853
|
+
return {
|
|
29854
|
+
name: match[1],
|
|
29855
|
+
args: match[2] || ""
|
|
29856
|
+
};
|
|
29857
|
+
}
|
|
29858
|
+
isCommand(input) {
|
|
29859
|
+
return this.parseCommand(input) !== null;
|
|
29860
|
+
}
|
|
29861
|
+
async execute(input, context) {
|
|
29862
|
+
const parsed = this.parseCommand(input);
|
|
29863
|
+
if (!parsed) {
|
|
29864
|
+
return { handled: false };
|
|
29865
|
+
}
|
|
29866
|
+
const command = this.loader.getCommand(parsed.name);
|
|
29867
|
+
if (!command) {
|
|
29868
|
+
context.emit("text", `Unknown command: /${parsed.name}
|
|
29869
|
+
|
|
29870
|
+
Use /help to see available commands.
|
|
29871
|
+
`);
|
|
29872
|
+
context.emit("done");
|
|
29873
|
+
return { handled: true };
|
|
29874
|
+
}
|
|
29875
|
+
if (command.selfHandled && command.handler) {
|
|
29876
|
+
return command.handler(parsed.args, context);
|
|
29877
|
+
}
|
|
29878
|
+
const prompt = await this.preparePrompt(command, parsed.args);
|
|
29879
|
+
return {
|
|
29880
|
+
handled: false,
|
|
29881
|
+
prompt
|
|
29882
|
+
};
|
|
29883
|
+
}
|
|
29884
|
+
async preparePrompt(command, args) {
|
|
29885
|
+
let content = command.content;
|
|
29886
|
+
content = content.replace(/\$ARGUMENTS/g, args || "(no arguments provided)");
|
|
29887
|
+
content = await this.processShellCommands(content);
|
|
29888
|
+
return content;
|
|
29889
|
+
}
|
|
29890
|
+
async processShellCommands(content) {
|
|
29891
|
+
const lines = content.split(`
|
|
29892
|
+
`);
|
|
29893
|
+
const processedLines = [];
|
|
29894
|
+
for (const line of lines) {
|
|
29895
|
+
const trimmed = line.trim();
|
|
29896
|
+
if (trimmed.startsWith("!")) {
|
|
29897
|
+
const command = trimmed.slice(1).trim();
|
|
29898
|
+
const output = await this.executeShell(command);
|
|
29899
|
+
processedLines.push(`\`\`\`
|
|
29900
|
+
${output}
|
|
29901
|
+
\`\`\``);
|
|
29902
|
+
} else {
|
|
29903
|
+
processedLines.push(line);
|
|
29904
|
+
}
|
|
29905
|
+
}
|
|
29906
|
+
return processedLines.join(`
|
|
29907
|
+
`);
|
|
29908
|
+
}
|
|
29909
|
+
async executeShell(command) {
|
|
29910
|
+
try {
|
|
29911
|
+
const proc = Bun.spawn(["bash", "-c", command], {
|
|
29912
|
+
stdout: "pipe",
|
|
29913
|
+
stderr: "pipe"
|
|
29914
|
+
});
|
|
29915
|
+
const [stdout, stderr] = await Promise.all([
|
|
29916
|
+
new Response(proc.stdout).text(),
|
|
29917
|
+
new Response(proc.stderr).text()
|
|
29918
|
+
]);
|
|
29919
|
+
const exitCode = await proc.exited;
|
|
29920
|
+
if (exitCode !== 0 && stderr) {
|
|
29921
|
+
return `Error (exit ${exitCode}):
|
|
29922
|
+
${stderr}`;
|
|
29923
|
+
}
|
|
29924
|
+
return stdout.trim() || "(no output)";
|
|
29925
|
+
} catch (error) {
|
|
29926
|
+
return `Error: ${error instanceof Error ? error.message : String(error)}`;
|
|
29927
|
+
}
|
|
29928
|
+
}
|
|
29929
|
+
getSuggestions(partial) {
|
|
29930
|
+
if (!partial.startsWith("/")) {
|
|
29931
|
+
return [];
|
|
29932
|
+
}
|
|
29933
|
+
const name = partial.slice(1).toLowerCase();
|
|
29934
|
+
return this.loader.findMatching(name);
|
|
29935
|
+
}
|
|
29936
|
+
}
|
|
29937
|
+
// packages/core/src/commands/builtin.ts
|
|
29938
|
+
import { join as join5 } from "path";
|
|
29939
|
+
import { homedir as homedir4 } from "os";
|
|
29940
|
+
import { existsSync as existsSync3, mkdirSync, writeFileSync } from "fs";
|
|
29941
|
+
|
|
29942
|
+
class BuiltinCommands {
|
|
29943
|
+
tokenUsage = {
|
|
29944
|
+
inputTokens: 0,
|
|
29945
|
+
outputTokens: 0,
|
|
29946
|
+
totalTokens: 0,
|
|
29947
|
+
maxContextTokens: 200000
|
|
29948
|
+
};
|
|
29949
|
+
registerAll(loader) {
|
|
29950
|
+
loader.register(this.helpCommand(loader));
|
|
29951
|
+
loader.register(this.clearCommand());
|
|
29952
|
+
loader.register(this.statusCommand());
|
|
29953
|
+
loader.register(this.compactCommand());
|
|
29954
|
+
loader.register(this.configCommand());
|
|
29955
|
+
loader.register(this.initCommand());
|
|
29956
|
+
loader.register(this.costCommand());
|
|
29957
|
+
loader.register(this.modelCommand());
|
|
29958
|
+
loader.register(this.memoryCommand());
|
|
29959
|
+
loader.register(this.bugCommand());
|
|
29960
|
+
loader.register(this.prCommand());
|
|
29961
|
+
loader.register(this.reviewCommand());
|
|
29962
|
+
}
|
|
29963
|
+
updateTokenUsage(usage) {
|
|
29964
|
+
Object.assign(this.tokenUsage, usage);
|
|
29965
|
+
}
|
|
29966
|
+
getTokenUsage() {
|
|
29967
|
+
return { ...this.tokenUsage };
|
|
29968
|
+
}
|
|
29969
|
+
helpCommand(loader) {
|
|
29970
|
+
return {
|
|
29971
|
+
name: "help",
|
|
29972
|
+
description: "Show available slash commands",
|
|
29973
|
+
builtin: true,
|
|
29974
|
+
selfHandled: true,
|
|
29975
|
+
content: "",
|
|
29976
|
+
handler: async (args, context) => {
|
|
29977
|
+
const commands = loader.getCommands();
|
|
29978
|
+
const builtinCmds = commands.filter((c) => c.builtin);
|
|
29979
|
+
const customCmds = commands.filter((c) => !c.builtin);
|
|
29980
|
+
let message = `
|
|
29981
|
+
**Available Slash Commands**
|
|
29982
|
+
|
|
29983
|
+
`;
|
|
29984
|
+
if (builtinCmds.length > 0) {
|
|
29985
|
+
message += `**Built-in Commands:**
|
|
29986
|
+
`;
|
|
29987
|
+
for (const cmd of builtinCmds.sort((a, b) => a.name.localeCompare(b.name))) {
|
|
29988
|
+
message += ` /${cmd.name} - ${cmd.description}
|
|
29989
|
+
`;
|
|
29990
|
+
}
|
|
29991
|
+
message += `
|
|
29992
|
+
`;
|
|
29993
|
+
}
|
|
29994
|
+
if (customCmds.length > 0) {
|
|
29995
|
+
message += `**Custom Commands:**
|
|
29996
|
+
`;
|
|
29997
|
+
for (const cmd of customCmds.sort((a, b) => a.name.localeCompare(b.name))) {
|
|
29998
|
+
message += ` /${cmd.name} - ${cmd.description}
|
|
29999
|
+
`;
|
|
30000
|
+
}
|
|
30001
|
+
message += `
|
|
30002
|
+
`;
|
|
30003
|
+
}
|
|
30004
|
+
message += `**Tips:**
|
|
30005
|
+
`;
|
|
30006
|
+
message += ` - Create custom commands in .oldpal/commands/*.md
|
|
30007
|
+
`;
|
|
30008
|
+
message += ` - Global commands go in ~/.oldpal/commands/*.md
|
|
30009
|
+
`;
|
|
30010
|
+
message += ` - Use /init to create a starter command
|
|
30011
|
+
`;
|
|
30012
|
+
context.emit("text", message);
|
|
30013
|
+
context.emit("done");
|
|
30014
|
+
return { handled: true };
|
|
30015
|
+
}
|
|
30016
|
+
};
|
|
30017
|
+
}
|
|
30018
|
+
clearCommand() {
|
|
30019
|
+
return {
|
|
30020
|
+
name: "clear",
|
|
30021
|
+
description: "Clear conversation history and start fresh",
|
|
30022
|
+
builtin: true,
|
|
30023
|
+
selfHandled: true,
|
|
30024
|
+
content: "",
|
|
30025
|
+
handler: async (args, context) => {
|
|
30026
|
+
context.clearMessages();
|
|
30027
|
+
this.tokenUsage.inputTokens = 0;
|
|
30028
|
+
this.tokenUsage.outputTokens = 0;
|
|
30029
|
+
this.tokenUsage.totalTokens = 0;
|
|
30030
|
+
context.emit("text", `Conversation cleared. Starting fresh.
|
|
30031
|
+
`);
|
|
30032
|
+
context.emit("done");
|
|
30033
|
+
return { handled: true, clearConversation: true };
|
|
30034
|
+
}
|
|
30035
|
+
};
|
|
30036
|
+
}
|
|
30037
|
+
statusCommand() {
|
|
30038
|
+
return {
|
|
30039
|
+
name: "status",
|
|
30040
|
+
description: "Show current session status and token usage",
|
|
30041
|
+
builtin: true,
|
|
30042
|
+
selfHandled: true,
|
|
30043
|
+
content: "",
|
|
30044
|
+
handler: async (args, context) => {
|
|
30045
|
+
const usage = this.tokenUsage;
|
|
30046
|
+
const usedPercent = Math.round(usage.totalTokens / usage.maxContextTokens * 100);
|
|
30047
|
+
let message = `
|
|
30048
|
+
**Session Status**
|
|
30049
|
+
|
|
30050
|
+
`;
|
|
30051
|
+
message += `**Working Directory:** ${context.cwd}
|
|
30052
|
+
`;
|
|
30053
|
+
message += `**Session ID:** ${context.sessionId}
|
|
30054
|
+
`;
|
|
30055
|
+
message += `**Messages:** ${context.messages.length}
|
|
30056
|
+
`;
|
|
30057
|
+
message += `**Available Tools:** ${context.tools.length}
|
|
30058
|
+
|
|
30059
|
+
`;
|
|
30060
|
+
message += `**Token Usage:**
|
|
30061
|
+
`;
|
|
30062
|
+
message += ` Input: ${usage.inputTokens.toLocaleString()}
|
|
30063
|
+
`;
|
|
30064
|
+
message += ` Output: ${usage.outputTokens.toLocaleString()}
|
|
30065
|
+
`;
|
|
30066
|
+
message += ` Total: ${usage.totalTokens.toLocaleString()} / ${usage.maxContextTokens.toLocaleString()} (${usedPercent}%)
|
|
30067
|
+
`;
|
|
30068
|
+
if (usage.cacheReadTokens || usage.cacheWriteTokens) {
|
|
30069
|
+
message += ` Cache Read: ${(usage.cacheReadTokens || 0).toLocaleString()}
|
|
30070
|
+
`;
|
|
30071
|
+
message += ` Cache Write: ${(usage.cacheWriteTokens || 0).toLocaleString()}
|
|
30072
|
+
`;
|
|
30073
|
+
}
|
|
30074
|
+
const barLength = 30;
|
|
30075
|
+
const filledLength = Math.round(usedPercent / 100 * barLength);
|
|
30076
|
+
const bar = "\u2588".repeat(filledLength) + "\u2591".repeat(barLength - filledLength);
|
|
30077
|
+
message += `
|
|
30078
|
+
[${bar}] ${usedPercent}%
|
|
30079
|
+
`;
|
|
30080
|
+
context.emit("text", message);
|
|
30081
|
+
context.emit("done");
|
|
30082
|
+
return { handled: true };
|
|
30083
|
+
}
|
|
30084
|
+
};
|
|
30085
|
+
}
|
|
30086
|
+
compactCommand() {
|
|
30087
|
+
return {
|
|
30088
|
+
name: "compact",
|
|
30089
|
+
description: "Summarize conversation to save context space",
|
|
30090
|
+
builtin: true,
|
|
30091
|
+
selfHandled: false,
|
|
30092
|
+
content: `Please summarize our conversation so far into a concise format that preserves:
|
|
30093
|
+
1. Key decisions made
|
|
30094
|
+
2. Important context about the codebase
|
|
30095
|
+
3. Current task/goal we're working on
|
|
30096
|
+
4. Any constraints or requirements mentioned
|
|
30097
|
+
|
|
30098
|
+
Format the summary as a brief bullet-point list. This summary will replace the conversation history to save context space.`
|
|
30099
|
+
};
|
|
30100
|
+
}
|
|
30101
|
+
configCommand() {
|
|
30102
|
+
return {
|
|
30103
|
+
name: "config",
|
|
30104
|
+
description: "Show current configuration",
|
|
30105
|
+
builtin: true,
|
|
30106
|
+
selfHandled: true,
|
|
30107
|
+
content: "",
|
|
30108
|
+
handler: async (args, context) => {
|
|
30109
|
+
const configPaths = [
|
|
30110
|
+
join5(context.cwd, ".oldpal", "config.json"),
|
|
30111
|
+
join5(homedir4(), ".oldpal", "config.json")
|
|
30112
|
+
];
|
|
30113
|
+
let message = `
|
|
30114
|
+
**Configuration**
|
|
30115
|
+
|
|
30116
|
+
`;
|
|
30117
|
+
message += `**Config File Locations:**
|
|
30118
|
+
`;
|
|
30119
|
+
for (const path of configPaths) {
|
|
30120
|
+
const exists = existsSync3(path);
|
|
30121
|
+
message += ` ${exists ? "\u2713" : "\u25CB"} ${path}
|
|
30122
|
+
`;
|
|
30123
|
+
}
|
|
30124
|
+
message += `
|
|
30125
|
+
**Commands Directories:**
|
|
30126
|
+
`;
|
|
30127
|
+
message += ` - Project: ${join5(context.cwd, ".oldpal", "commands")}
|
|
30128
|
+
`;
|
|
30129
|
+
message += ` - Global: ${join5(homedir4(), ".oldpal", "commands")}
|
|
30130
|
+
`;
|
|
30131
|
+
context.emit("text", message);
|
|
30132
|
+
context.emit("done");
|
|
30133
|
+
return { handled: true };
|
|
30134
|
+
}
|
|
30135
|
+
};
|
|
30136
|
+
}
|
|
30137
|
+
initCommand() {
|
|
30138
|
+
return {
|
|
30139
|
+
name: "init",
|
|
30140
|
+
description: "Initialize oldpal config and create example command",
|
|
30141
|
+
builtin: true,
|
|
30142
|
+
selfHandled: true,
|
|
30143
|
+
content: "",
|
|
30144
|
+
handler: async (args, context) => {
|
|
30145
|
+
const commandsDir = join5(context.cwd, ".oldpal", "commands");
|
|
30146
|
+
mkdirSync(commandsDir, { recursive: true });
|
|
30147
|
+
const exampleCommand = `---
|
|
30148
|
+
name: review
|
|
30149
|
+
description: Review code changes for issues and improvements
|
|
30150
|
+
tags: [code, review]
|
|
30151
|
+
---
|
|
30152
|
+
|
|
30153
|
+
# Code Review
|
|
30154
|
+
|
|
30155
|
+
Please review the current code changes and provide feedback on:
|
|
30156
|
+
|
|
30157
|
+
1. **Code Quality**
|
|
30158
|
+
- Readability and maintainability
|
|
30159
|
+
- Following project conventions
|
|
30160
|
+
- Proper error handling
|
|
30161
|
+
|
|
30162
|
+
2. **Potential Issues**
|
|
30163
|
+
- Security vulnerabilities
|
|
30164
|
+
- Performance concerns
|
|
30165
|
+
- Edge cases not handled
|
|
30166
|
+
|
|
30167
|
+
3. **Suggestions**
|
|
30168
|
+
- Improvements to consider
|
|
30169
|
+
- Best practices to apply
|
|
30170
|
+
- Documentation needs
|
|
30171
|
+
|
|
30172
|
+
If there are staged git changes, focus on those. Otherwise, ask what code to review.
|
|
30173
|
+
`;
|
|
30174
|
+
const examplePath = join5(commandsDir, "review.md");
|
|
30175
|
+
if (!existsSync3(examplePath)) {
|
|
30176
|
+
writeFileSync(examplePath, exampleCommand);
|
|
30177
|
+
}
|
|
30178
|
+
let message = `
|
|
30179
|
+
**Initialized oldpal**
|
|
30180
|
+
|
|
30181
|
+
`;
|
|
30182
|
+
message += `Created: ${commandsDir}
|
|
30183
|
+
`;
|
|
30184
|
+
message += `Example: ${examplePath}
|
|
29646
30185
|
|
|
30186
|
+
`;
|
|
30187
|
+
message += `You can now:
|
|
30188
|
+
`;
|
|
30189
|
+
message += ` - Add custom commands to .oldpal/commands/
|
|
30190
|
+
`;
|
|
30191
|
+
message += ` - Use /review to try the example command
|
|
30192
|
+
`;
|
|
30193
|
+
message += ` - Run /help to see all available commands
|
|
30194
|
+
`;
|
|
30195
|
+
context.emit("text", message);
|
|
30196
|
+
context.emit("done");
|
|
30197
|
+
return { handled: true };
|
|
30198
|
+
}
|
|
30199
|
+
};
|
|
30200
|
+
}
|
|
30201
|
+
costCommand() {
|
|
30202
|
+
return {
|
|
30203
|
+
name: "cost",
|
|
30204
|
+
description: "Show estimated API cost for this session",
|
|
30205
|
+
builtin: true,
|
|
30206
|
+
selfHandled: true,
|
|
30207
|
+
content: "",
|
|
30208
|
+
handler: async (args, context) => {
|
|
30209
|
+
const usage = this.tokenUsage;
|
|
30210
|
+
const inputCostPer1M = 3;
|
|
30211
|
+
const outputCostPer1M = 15;
|
|
30212
|
+
const inputCost = usage.inputTokens / 1e6 * inputCostPer1M;
|
|
30213
|
+
const outputCost = usage.outputTokens / 1e6 * outputCostPer1M;
|
|
30214
|
+
const totalCost = inputCost + outputCost;
|
|
30215
|
+
const cacheReadCostPer1M = 0.3;
|
|
30216
|
+
const cacheSavings = usage.cacheReadTokens ? usage.cacheReadTokens / 1e6 * (inputCostPer1M - cacheReadCostPer1M) : 0;
|
|
30217
|
+
let message = `
|
|
30218
|
+
**Estimated Session Cost**
|
|
30219
|
+
|
|
30220
|
+
`;
|
|
30221
|
+
message += `Input tokens: ${usage.inputTokens.toLocaleString()} (~$${inputCost.toFixed(4)})
|
|
30222
|
+
`;
|
|
30223
|
+
message += `Output tokens: ${usage.outputTokens.toLocaleString()} (~$${outputCost.toFixed(4)})
|
|
30224
|
+
`;
|
|
30225
|
+
message += `**Total: ~$${totalCost.toFixed(4)}**
|
|
30226
|
+
`;
|
|
30227
|
+
if (cacheSavings > 0) {
|
|
30228
|
+
message += `
|
|
30229
|
+
Cache savings: ~$${cacheSavings.toFixed(4)}
|
|
30230
|
+
`;
|
|
30231
|
+
}
|
|
30232
|
+
message += `
|
|
30233
|
+
*Based on Claude 3.5 Sonnet pricing*
|
|
30234
|
+
`;
|
|
30235
|
+
context.emit("text", message);
|
|
30236
|
+
context.emit("done");
|
|
30237
|
+
return { handled: true };
|
|
30238
|
+
}
|
|
30239
|
+
};
|
|
30240
|
+
}
|
|
30241
|
+
modelCommand() {
|
|
30242
|
+
return {
|
|
30243
|
+
name: "model",
|
|
30244
|
+
description: "Show current model information",
|
|
30245
|
+
builtin: true,
|
|
30246
|
+
selfHandled: true,
|
|
30247
|
+
content: "",
|
|
30248
|
+
handler: async (args, context) => {
|
|
30249
|
+
let message = `
|
|
30250
|
+
**Model Information**
|
|
30251
|
+
|
|
30252
|
+
`;
|
|
30253
|
+
message += `Current model: claude-3-5-sonnet-20241022
|
|
30254
|
+
`;
|
|
30255
|
+
message += `Context window: 200,000 tokens
|
|
30256
|
+
`;
|
|
30257
|
+
message += `Max output: 8,192 tokens
|
|
30258
|
+
|
|
30259
|
+
`;
|
|
30260
|
+
message += `*Model selection coming in a future update*
|
|
30261
|
+
`;
|
|
30262
|
+
context.emit("text", message);
|
|
30263
|
+
context.emit("done");
|
|
30264
|
+
return { handled: true };
|
|
30265
|
+
}
|
|
30266
|
+
};
|
|
30267
|
+
}
|
|
30268
|
+
memoryCommand() {
|
|
30269
|
+
return {
|
|
30270
|
+
name: "memory",
|
|
30271
|
+
description: "Show conversation summary and key memories",
|
|
30272
|
+
builtin: true,
|
|
30273
|
+
selfHandled: false,
|
|
30274
|
+
content: `Please provide a summary of our conversation so far, including:
|
|
30275
|
+
|
|
30276
|
+
1. **Key Context** - What you know about this project/codebase
|
|
30277
|
+
2. **Current Task** - What we're working on
|
|
30278
|
+
3. **Decisions Made** - Any choices or agreements from our discussion
|
|
30279
|
+
4. **Open Items** - Things we mentioned but haven't addressed yet
|
|
30280
|
+
|
|
30281
|
+
Keep it concise but comprehensive.`
|
|
30282
|
+
};
|
|
30283
|
+
}
|
|
30284
|
+
bugCommand() {
|
|
30285
|
+
return {
|
|
30286
|
+
name: "bug",
|
|
30287
|
+
description: "Analyze and help fix a bug",
|
|
30288
|
+
builtin: true,
|
|
30289
|
+
selfHandled: false,
|
|
30290
|
+
content: `Help me debug an issue. $ARGUMENTS
|
|
30291
|
+
|
|
30292
|
+
Please:
|
|
30293
|
+
1. Understand the bug/error described
|
|
30294
|
+
2. Identify likely causes
|
|
30295
|
+
3. Search relevant code files
|
|
30296
|
+
4. Propose a fix with code changes
|
|
30297
|
+
|
|
30298
|
+
If no bug is described, ask me to describe the issue I'm experiencing.`
|
|
30299
|
+
};
|
|
30300
|
+
}
|
|
30301
|
+
prCommand() {
|
|
30302
|
+
return {
|
|
30303
|
+
name: "pr",
|
|
30304
|
+
description: "Create a pull request for current changes",
|
|
30305
|
+
builtin: true,
|
|
30306
|
+
selfHandled: false,
|
|
30307
|
+
content: `Help me create a pull request for the current changes.
|
|
30308
|
+
|
|
30309
|
+
1. First, check git status and staged changes
|
|
30310
|
+
2. Review the diff to understand what changed
|
|
30311
|
+
3. Write a clear PR title (max 72 chars)
|
|
30312
|
+
4. Write a description with:
|
|
30313
|
+
- Summary of changes
|
|
30314
|
+
- Motivation/context
|
|
30315
|
+
- Testing done
|
|
30316
|
+
- Any notes for reviewers
|
|
30317
|
+
|
|
30318
|
+
Then create the PR using the gh CLI.`
|
|
30319
|
+
};
|
|
30320
|
+
}
|
|
30321
|
+
reviewCommand() {
|
|
30322
|
+
return {
|
|
30323
|
+
name: "review",
|
|
30324
|
+
description: "Review code changes for issues",
|
|
30325
|
+
builtin: true,
|
|
30326
|
+
selfHandled: false,
|
|
30327
|
+
content: `Review the current code changes. $ARGUMENTS
|
|
30328
|
+
|
|
30329
|
+
Check for:
|
|
30330
|
+
1. **Bugs** - Logic errors, edge cases, null checks
|
|
30331
|
+
2. **Security** - Input validation, injection risks, secrets
|
|
30332
|
+
3. **Performance** - N+1 queries, unnecessary loops, memory leaks
|
|
30333
|
+
4. **Style** - Naming, formatting, code organization
|
|
30334
|
+
5. **Tests** - Coverage, edge cases, assertions
|
|
30335
|
+
|
|
30336
|
+
If there are staged changes, review those. Otherwise, ask what to review.`
|
|
30337
|
+
};
|
|
30338
|
+
}
|
|
30339
|
+
}
|
|
29647
30340
|
// packages/core/src/llm/client.ts
|
|
29648
30341
|
async function createLLMClient(config) {
|
|
29649
30342
|
if (config.provider === "anthropic") {
|
|
@@ -29654,8 +30347,8 @@ async function createLLMClient(config) {
|
|
|
29654
30347
|
}
|
|
29655
30348
|
|
|
29656
30349
|
// packages/core/src/config.ts
|
|
29657
|
-
import { join as
|
|
29658
|
-
import { homedir as
|
|
30350
|
+
import { join as join7 } from "path";
|
|
30351
|
+
import { homedir as homedir6 } from "os";
|
|
29659
30352
|
var DEFAULT_CONFIG = {
|
|
29660
30353
|
llm: {
|
|
29661
30354
|
provider: "anthropic",
|
|
@@ -29685,13 +30378,13 @@ var DEFAULT_CONFIG = {
|
|
|
29685
30378
|
]
|
|
29686
30379
|
};
|
|
29687
30380
|
function getConfigDir() {
|
|
29688
|
-
return
|
|
30381
|
+
return join7(homedir6(), ".oldpal");
|
|
29689
30382
|
}
|
|
29690
30383
|
function getConfigPath(filename) {
|
|
29691
|
-
return
|
|
30384
|
+
return join7(getConfigDir(), filename);
|
|
29692
30385
|
}
|
|
29693
30386
|
function getProjectConfigDir(cwd2 = process.cwd()) {
|
|
29694
|
-
return
|
|
30387
|
+
return join7(cwd2, ".oldpal");
|
|
29695
30388
|
}
|
|
29696
30389
|
async function loadConfig(cwd2 = process.cwd()) {
|
|
29697
30390
|
const config = { ...DEFAULT_CONFIG };
|
|
@@ -29704,7 +30397,7 @@ async function loadConfig(cwd2 = process.cwd()) {
|
|
|
29704
30397
|
if (userConfig.voice)
|
|
29705
30398
|
config.voice = { ...config.voice, ...userConfig.voice };
|
|
29706
30399
|
}
|
|
29707
|
-
const projectConfigPath =
|
|
30400
|
+
const projectConfigPath = join7(getProjectConfigDir(cwd2), "settings.json");
|
|
29708
30401
|
const projectConfig = await loadJsonFile(projectConfigPath);
|
|
29709
30402
|
if (projectConfig) {
|
|
29710
30403
|
Object.assign(config, projectConfig);
|
|
@@ -29713,7 +30406,7 @@ async function loadConfig(cwd2 = process.cwd()) {
|
|
|
29713
30406
|
if (projectConfig.voice)
|
|
29714
30407
|
config.voice = { ...config.voice, ...projectConfig.voice };
|
|
29715
30408
|
}
|
|
29716
|
-
const localConfigPath =
|
|
30409
|
+
const localConfigPath = join7(getProjectConfigDir(cwd2), "settings.local.json");
|
|
29717
30410
|
const localConfig = await loadJsonFile(localConfigPath);
|
|
29718
30411
|
if (localConfig) {
|
|
29719
30412
|
Object.assign(config, localConfig);
|
|
@@ -29731,7 +30424,7 @@ async function loadHooksConfig(cwd2 = process.cwd()) {
|
|
|
29731
30424
|
if (userHooks?.hooks) {
|
|
29732
30425
|
mergeHooks(hooks, userHooks.hooks);
|
|
29733
30426
|
}
|
|
29734
|
-
const projectHooksPath =
|
|
30427
|
+
const projectHooksPath = join7(getProjectConfigDir(cwd2), "hooks.json");
|
|
29735
30428
|
const projectHooks = await loadJsonFile(projectHooksPath);
|
|
29736
30429
|
if (projectHooks?.hooks) {
|
|
29737
30430
|
mergeHooks(hooks, projectHooks.hooks);
|
|
@@ -29767,16 +30460,22 @@ class AgentLoop {
|
|
|
29767
30460
|
skillExecutor;
|
|
29768
30461
|
hookLoader;
|
|
29769
30462
|
hookExecutor;
|
|
30463
|
+
commandLoader;
|
|
30464
|
+
commandExecutor;
|
|
30465
|
+
builtinCommands;
|
|
29770
30466
|
llmClient = null;
|
|
29771
30467
|
config = null;
|
|
29772
30468
|
cwd;
|
|
30469
|
+
sessionId;
|
|
29773
30470
|
isRunning = false;
|
|
29774
30471
|
shouldStop = false;
|
|
29775
30472
|
onChunk;
|
|
29776
30473
|
onToolStart;
|
|
29777
30474
|
onToolEnd;
|
|
30475
|
+
onTokenUsage;
|
|
29778
30476
|
constructor(options = {}) {
|
|
29779
30477
|
this.cwd = options.cwd || process.cwd();
|
|
30478
|
+
this.sessionId = options.sessionId || generateId();
|
|
29780
30479
|
this.context = new AgentContext;
|
|
29781
30480
|
this.toolRegistry = new ToolRegistry;
|
|
29782
30481
|
this.connectorBridge = new ConnectorBridge;
|
|
@@ -29784,9 +30483,13 @@ class AgentLoop {
|
|
|
29784
30483
|
this.skillExecutor = new SkillExecutor;
|
|
29785
30484
|
this.hookLoader = new HookLoader;
|
|
29786
30485
|
this.hookExecutor = new HookExecutor;
|
|
30486
|
+
this.commandLoader = new CommandLoader(this.cwd);
|
|
30487
|
+
this.commandExecutor = new CommandExecutor(this.commandLoader);
|
|
30488
|
+
this.builtinCommands = new BuiltinCommands;
|
|
29787
30489
|
this.onChunk = options.onChunk;
|
|
29788
30490
|
this.onToolStart = options.onToolStart;
|
|
29789
30491
|
this.onToolEnd = options.onToolEnd;
|
|
30492
|
+
this.onTokenUsage = options.onTokenUsage;
|
|
29790
30493
|
}
|
|
29791
30494
|
async initialize() {
|
|
29792
30495
|
this.config = await loadConfig(this.cwd);
|
|
@@ -29797,10 +30500,12 @@ class AgentLoop {
|
|
|
29797
30500
|
await this.connectorBridge.discover(this.config.connectors);
|
|
29798
30501
|
this.connectorBridge.registerAll(this.toolRegistry);
|
|
29799
30502
|
await this.skillLoader.loadAll(this.cwd);
|
|
30503
|
+
await this.commandLoader.loadAll();
|
|
30504
|
+
this.builtinCommands.registerAll(this.commandLoader);
|
|
29800
30505
|
const hooksConfig = await loadHooksConfig(this.cwd);
|
|
29801
30506
|
this.hookLoader.load(hooksConfig);
|
|
29802
30507
|
await this.hookExecutor.execute(this.hookLoader.getHooks("SessionStart"), {
|
|
29803
|
-
session_id:
|
|
30508
|
+
session_id: this.sessionId,
|
|
29804
30509
|
hook_event_name: "SessionStart",
|
|
29805
30510
|
cwd: this.cwd
|
|
29806
30511
|
});
|
|
@@ -29816,7 +30521,7 @@ class AgentLoop {
|
|
|
29816
30521
|
this.shouldStop = false;
|
|
29817
30522
|
try {
|
|
29818
30523
|
const promptHookResult = await this.hookExecutor.execute(this.hookLoader.getHooks("UserPromptSubmit"), {
|
|
29819
|
-
session_id:
|
|
30524
|
+
session_id: this.sessionId,
|
|
29820
30525
|
hook_event_name: "UserPromptSubmit",
|
|
29821
30526
|
cwd: this.cwd,
|
|
29822
30527
|
prompt: userMessage
|
|
@@ -29825,6 +30530,18 @@ class AgentLoop {
|
|
|
29825
30530
|
this.emit({ type: "error", error: promptHookResult.stopReason || "Blocked by hook" });
|
|
29826
30531
|
return;
|
|
29827
30532
|
}
|
|
30533
|
+
if (userMessage.startsWith("/")) {
|
|
30534
|
+
const commandResult = await this.handleCommand(userMessage);
|
|
30535
|
+
if (commandResult.handled) {
|
|
30536
|
+
if (commandResult.clearConversation) {
|
|
30537
|
+
this.context = new AgentContext;
|
|
30538
|
+
}
|
|
30539
|
+
return;
|
|
30540
|
+
}
|
|
30541
|
+
if (commandResult.prompt) {
|
|
30542
|
+
userMessage = commandResult.prompt;
|
|
30543
|
+
}
|
|
30544
|
+
}
|
|
29828
30545
|
if (userMessage.startsWith("/")) {
|
|
29829
30546
|
const handled = await this.handleSkillInvocation(userMessage);
|
|
29830
30547
|
if (handled)
|
|
@@ -29905,6 +30622,30 @@ class AgentLoop {
|
|
|
29905
30622
|
}
|
|
29906
30623
|
return results;
|
|
29907
30624
|
}
|
|
30625
|
+
async handleCommand(message) {
|
|
30626
|
+
const context = {
|
|
30627
|
+
cwd: this.cwd,
|
|
30628
|
+
sessionId: this.sessionId,
|
|
30629
|
+
messages: this.context.getMessages(),
|
|
30630
|
+
tools: this.toolRegistry.getTools(),
|
|
30631
|
+
clearMessages: () => {
|
|
30632
|
+
this.context = new AgentContext;
|
|
30633
|
+
},
|
|
30634
|
+
addSystemMessage: (content) => {
|
|
30635
|
+
this.context.addSystemMessage(content);
|
|
30636
|
+
},
|
|
30637
|
+
emit: (type, content) => {
|
|
30638
|
+
if (type === "text" && content) {
|
|
30639
|
+
this.emit({ type: "text", content });
|
|
30640
|
+
} else if (type === "done") {
|
|
30641
|
+
this.emit({ type: "done" });
|
|
30642
|
+
} else if (type === "error" && content) {
|
|
30643
|
+
this.emit({ type: "error", error: content });
|
|
30644
|
+
}
|
|
30645
|
+
}
|
|
30646
|
+
};
|
|
30647
|
+
return this.commandExecutor.execute(message, context);
|
|
30648
|
+
}
|
|
29908
30649
|
async handleSkillInvocation(message) {
|
|
29909
30650
|
const match = message.match(/^\/(\S+)(?:\s+(.*))?$/);
|
|
29910
30651
|
if (!match)
|
|
@@ -29936,18 +30677,34 @@ class AgentLoop {
|
|
|
29936
30677
|
getSkills() {
|
|
29937
30678
|
return this.skillLoader.getSkills();
|
|
29938
30679
|
}
|
|
30680
|
+
getCommands() {
|
|
30681
|
+
return this.commandLoader.getCommands();
|
|
30682
|
+
}
|
|
30683
|
+
getTokenUsage() {
|
|
30684
|
+
return this.builtinCommands.getTokenUsage();
|
|
30685
|
+
}
|
|
30686
|
+
updateTokenUsage(usage) {
|
|
30687
|
+
this.builtinCommands.updateTokenUsage(usage);
|
|
30688
|
+
this.onTokenUsage?.(this.builtinCommands.getTokenUsage());
|
|
30689
|
+
}
|
|
29939
30690
|
isProcessing() {
|
|
29940
30691
|
return this.isRunning;
|
|
29941
30692
|
}
|
|
30693
|
+
getSessionId() {
|
|
30694
|
+
return this.sessionId;
|
|
30695
|
+
}
|
|
30696
|
+
clearConversation() {
|
|
30697
|
+
this.context = new AgentContext;
|
|
30698
|
+
}
|
|
29942
30699
|
}
|
|
29943
30700
|
|
|
29944
30701
|
// packages/core/src/index.ts
|
|
29945
30702
|
init_anthropic();
|
|
29946
30703
|
|
|
29947
30704
|
// packages/core/src/logger.ts
|
|
29948
|
-
import { existsSync as
|
|
29949
|
-
import { join as
|
|
29950
|
-
import { homedir as
|
|
30705
|
+
import { existsSync as existsSync5, mkdirSync as mkdirSync2, appendFileSync } from "fs";
|
|
30706
|
+
import { join as join8 } from "path";
|
|
30707
|
+
import { homedir as homedir7 } from "os";
|
|
29951
30708
|
|
|
29952
30709
|
class Logger {
|
|
29953
30710
|
logDir;
|
|
@@ -29955,14 +30712,14 @@ class Logger {
|
|
|
29955
30712
|
sessionId;
|
|
29956
30713
|
constructor(sessionId) {
|
|
29957
30714
|
this.sessionId = sessionId;
|
|
29958
|
-
this.logDir =
|
|
30715
|
+
this.logDir = join8(homedir7(), ".oldpal", "logs");
|
|
29959
30716
|
this.ensureDir(this.logDir);
|
|
29960
30717
|
const date = new Date().toISOString().split("T")[0];
|
|
29961
|
-
this.logFile =
|
|
30718
|
+
this.logFile = join8(this.logDir, `${date}.log`);
|
|
29962
30719
|
}
|
|
29963
30720
|
ensureDir(dir) {
|
|
29964
|
-
if (!
|
|
29965
|
-
|
|
30721
|
+
if (!existsSync5(dir)) {
|
|
30722
|
+
mkdirSync2(dir, { recursive: true });
|
|
29966
30723
|
}
|
|
29967
30724
|
}
|
|
29968
30725
|
write(level, message, data) {
|
|
@@ -30001,13 +30758,13 @@ class SessionStorage {
|
|
|
30001
30758
|
sessionId;
|
|
30002
30759
|
constructor(sessionId) {
|
|
30003
30760
|
this.sessionId = sessionId;
|
|
30004
|
-
this.sessionsDir =
|
|
30761
|
+
this.sessionsDir = join8(homedir7(), ".oldpal", "sessions");
|
|
30005
30762
|
this.ensureDir(this.sessionsDir);
|
|
30006
|
-
this.sessionFile =
|
|
30763
|
+
this.sessionFile = join8(this.sessionsDir, `${sessionId}.json`);
|
|
30007
30764
|
}
|
|
30008
30765
|
ensureDir(dir) {
|
|
30009
|
-
if (!
|
|
30010
|
-
|
|
30766
|
+
if (!existsSync5(dir)) {
|
|
30767
|
+
mkdirSync2(dir, { recursive: true });
|
|
30011
30768
|
}
|
|
30012
30769
|
}
|
|
30013
30770
|
save(data) {
|
|
@@ -30020,16 +30777,16 @@ class SessionStorage {
|
|
|
30020
30777
|
}
|
|
30021
30778
|
}
|
|
30022
30779
|
function initOldpalDir() {
|
|
30023
|
-
const baseDir =
|
|
30780
|
+
const baseDir = join8(homedir7(), ".oldpal");
|
|
30024
30781
|
const dirs = [
|
|
30025
30782
|
baseDir,
|
|
30026
|
-
|
|
30027
|
-
|
|
30028
|
-
|
|
30783
|
+
join8(baseDir, "sessions"),
|
|
30784
|
+
join8(baseDir, "logs"),
|
|
30785
|
+
join8(baseDir, "skills")
|
|
30029
30786
|
];
|
|
30030
30787
|
for (const dir of dirs) {
|
|
30031
|
-
if (!
|
|
30032
|
-
|
|
30788
|
+
if (!existsSync5(dir)) {
|
|
30789
|
+
mkdirSync2(dir, { recursive: true });
|
|
30033
30790
|
}
|
|
30034
30791
|
}
|
|
30035
30792
|
}
|
|
@@ -30166,6 +30923,20 @@ class EmbeddedClient {
|
|
|
30166
30923
|
getSessionId() {
|
|
30167
30924
|
return this.session.getSessionId();
|
|
30168
30925
|
}
|
|
30926
|
+
async getCommands() {
|
|
30927
|
+
if (!this.initialized) {
|
|
30928
|
+
await this.initialize();
|
|
30929
|
+
}
|
|
30930
|
+
return this.agent.getCommands();
|
|
30931
|
+
}
|
|
30932
|
+
getTokenUsage() {
|
|
30933
|
+
return this.agent.getTokenUsage();
|
|
30934
|
+
}
|
|
30935
|
+
clearConversation() {
|
|
30936
|
+
this.agent.clearConversation();
|
|
30937
|
+
this.messages = [];
|
|
30938
|
+
this.logger.info("Conversation cleared");
|
|
30939
|
+
}
|
|
30169
30940
|
}
|
|
30170
30941
|
// packages/terminal/src/components/Input.tsx
|
|
30171
30942
|
var import_react23 = __toESM(require_react(), 1);
|
|
@@ -30348,7 +31119,7 @@ function parseMarkdown(text) {
|
|
|
30348
31119
|
// packages/terminal/src/components/Messages.tsx
|
|
30349
31120
|
var jsx_dev_runtime3 = __toESM(require_jsx_dev_runtime(), 1);
|
|
30350
31121
|
function Messages4({ messages, currentResponse, currentToolCall, lastToolResult, activityLog = [] }) {
|
|
30351
|
-
const visibleMessages = messages.slice(-
|
|
31122
|
+
const visibleMessages = messages.slice(-10);
|
|
30352
31123
|
return /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Box_default, {
|
|
30353
31124
|
flexDirection: "column",
|
|
30354
31125
|
children: [
|
|
@@ -30356,59 +31127,49 @@ function Messages4({ messages, currentResponse, currentToolCall, lastToolResult,
|
|
|
30356
31127
|
message
|
|
30357
31128
|
}, message.id, false, undefined, this)),
|
|
30358
31129
|
activityLog.map((entry) => {
|
|
30359
|
-
if (entry.type === "
|
|
31130
|
+
if (entry.type === "text" && entry.content) {
|
|
30360
31131
|
return /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Box_default, {
|
|
30361
31132
|
marginY: 1,
|
|
30362
31133
|
children: [
|
|
30363
31134
|
/* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Text, {
|
|
30364
31135
|
dimColor: true,
|
|
30365
|
-
children: "\
|
|
31136
|
+
children: "\u25CF "
|
|
30366
31137
|
}, undefined, false, undefined, this),
|
|
30367
|
-
/* @__PURE__ */ jsx_dev_runtime3.jsxDEV(
|
|
30368
|
-
|
|
30369
|
-
children:
|
|
31138
|
+
/* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Box_default, {
|
|
31139
|
+
flexGrow: 1,
|
|
31140
|
+
children: /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Markdown, {
|
|
31141
|
+
content: entry.content
|
|
31142
|
+
}, undefined, false, undefined, this)
|
|
30370
31143
|
}, undefined, false, undefined, this)
|
|
30371
31144
|
]
|
|
30372
31145
|
}, entry.id, true, undefined, this);
|
|
30373
31146
|
}
|
|
31147
|
+
if (entry.type === "tool_call" && entry.toolCall) {
|
|
31148
|
+
return /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Box_default, {
|
|
31149
|
+
marginTop: 1,
|
|
31150
|
+
children: /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Text, {
|
|
31151
|
+
dimColor: true,
|
|
31152
|
+
children: [
|
|
31153
|
+
" \u25D0 ",
|
|
31154
|
+
formatToolCall(entry.toolCall)
|
|
31155
|
+
]
|
|
31156
|
+
}, undefined, true, undefined, this)
|
|
31157
|
+
}, entry.id, false, undefined, this);
|
|
31158
|
+
}
|
|
30374
31159
|
if (entry.type === "tool_result" && entry.toolResult) {
|
|
30375
31160
|
return /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Box_default, {
|
|
30376
|
-
|
|
31161
|
+
marginBottom: 1,
|
|
30377
31162
|
children: /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Text, {
|
|
30378
31163
|
dimColor: true,
|
|
30379
31164
|
children: [
|
|
30380
|
-
"\u2192 ",
|
|
30381
|
-
truncate(entry.toolResult.content,
|
|
31165
|
+
" \u2192 ",
|
|
31166
|
+
truncate(entry.toolResult.content, 80)
|
|
30382
31167
|
]
|
|
30383
31168
|
}, undefined, true, undefined, this)
|
|
30384
31169
|
}, entry.id, false, undefined, this);
|
|
30385
31170
|
}
|
|
30386
31171
|
return null;
|
|
30387
31172
|
}),
|
|
30388
|
-
currentToolCall && !activityLog.some((e) => e.toolCall?.id === currentToolCall.id) && /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Box_default, {
|
|
30389
|
-
marginY: 1,
|
|
30390
|
-
children: [
|
|
30391
|
-
/* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Text, {
|
|
30392
|
-
dimColor: true,
|
|
30393
|
-
children: "\u25D0 "
|
|
30394
|
-
}, undefined, false, undefined, this),
|
|
30395
|
-
/* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Text, {
|
|
30396
|
-
dimColor: true,
|
|
30397
|
-
children: formatToolCall(currentToolCall)
|
|
30398
|
-
}, undefined, false, undefined, this)
|
|
30399
|
-
]
|
|
30400
|
-
}, undefined, true, undefined, this),
|
|
30401
|
-
lastToolResult && !activityLog.some((e) => e.toolResult?.toolCallId === lastToolResult.toolCallId) && /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Box_default, {
|
|
30402
|
-
marginY: 1,
|
|
30403
|
-
marginLeft: 2,
|
|
30404
|
-
children: /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Text, {
|
|
30405
|
-
dimColor: true,
|
|
30406
|
-
children: [
|
|
30407
|
-
"\u2192 ",
|
|
30408
|
-
truncate(lastToolResult.content, 100)
|
|
30409
|
-
]
|
|
30410
|
-
}, undefined, true, undefined, this)
|
|
30411
|
-
}, undefined, false, undefined, this),
|
|
30412
31173
|
currentResponse && /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Box_default, {
|
|
30413
31174
|
marginY: 1,
|
|
30414
31175
|
children: [
|
|
@@ -30423,7 +31184,17 @@ function Messages4({ messages, currentResponse, currentToolCall, lastToolResult,
|
|
|
30423
31184
|
}, undefined, false, undefined, this)
|
|
30424
31185
|
}, undefined, false, undefined, this)
|
|
30425
31186
|
]
|
|
30426
|
-
}, undefined, true, undefined, this)
|
|
31187
|
+
}, undefined, true, undefined, this),
|
|
31188
|
+
currentToolCall && /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Box_default, {
|
|
31189
|
+
marginTop: 1,
|
|
31190
|
+
children: /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Text, {
|
|
31191
|
+
dimColor: true,
|
|
31192
|
+
children: [
|
|
31193
|
+
" \u25D0 ",
|
|
31194
|
+
formatToolCall(currentToolCall)
|
|
31195
|
+
]
|
|
31196
|
+
}, undefined, true, undefined, this)
|
|
31197
|
+
}, undefined, false, undefined, this)
|
|
30427
31198
|
]
|
|
30428
31199
|
}, undefined, true, undefined, this);
|
|
30429
31200
|
}
|
|
@@ -30484,6 +31255,12 @@ function formatToolCall(toolCall) {
|
|
|
30484
31255
|
switch (name) {
|
|
30485
31256
|
case "bash":
|
|
30486
31257
|
return `Running: ${truncate(String(input.command || ""), 60)}`;
|
|
31258
|
+
case "curl":
|
|
31259
|
+
return `Fetching: ${truncate(String(input.url || ""), 60)}`;
|
|
31260
|
+
case "web_fetch":
|
|
31261
|
+
return `Fetching: ${truncate(String(input.url || ""), 60)}`;
|
|
31262
|
+
case "web_search":
|
|
31263
|
+
return `Searching: ${truncate(String(input.query || ""), 60)}`;
|
|
30487
31264
|
case "read":
|
|
30488
31265
|
return `Reading: ${truncate(String(input.path || input.file_path || ""), 60)}`;
|
|
30489
31266
|
case "write":
|
|
@@ -30516,10 +31293,17 @@ function truncate(text, maxLength) {
|
|
|
30516
31293
|
|
|
30517
31294
|
// packages/terminal/src/components/Status.tsx
|
|
30518
31295
|
var jsx_dev_runtime4 = __toESM(require_jsx_dev_runtime(), 1);
|
|
30519
|
-
function Status({ isProcessing, cwd: cwd2, queueLength = 0 }) {
|
|
30520
|
-
const maxCwdLength =
|
|
31296
|
+
function Status({ isProcessing, cwd: cwd2, queueLength = 0, tokenUsage }) {
|
|
31297
|
+
const maxCwdLength = 30;
|
|
30521
31298
|
const displayCwd = cwd2.length > maxCwdLength ? "..." + cwd2.slice(-(maxCwdLength - 3)) : cwd2;
|
|
30522
31299
|
const queueInfo = queueLength > 0 ? ` | ${queueLength} queued` : "";
|
|
31300
|
+
let tokenInfo = "";
|
|
31301
|
+
if (tokenUsage && tokenUsage.totalTokens > 0) {
|
|
31302
|
+
const used = Math.round(tokenUsage.totalTokens / 1000);
|
|
31303
|
+
const max = Math.round(tokenUsage.maxContextTokens / 1000);
|
|
31304
|
+
const percent = Math.round(tokenUsage.totalTokens / tokenUsage.maxContextTokens * 100);
|
|
31305
|
+
tokenInfo = ` | ${used}k/${max}k (${percent}%)`;
|
|
31306
|
+
}
|
|
30523
31307
|
return /* @__PURE__ */ jsx_dev_runtime4.jsxDEV(Box_default, {
|
|
30524
31308
|
marginTop: 1,
|
|
30525
31309
|
borderStyle: "single",
|
|
@@ -30537,6 +31321,10 @@ function Status({ isProcessing, cwd: cwd2, queueLength = 0 }) {
|
|
|
30537
31321
|
dimColor: !isProcessing,
|
|
30538
31322
|
children: isProcessing ? "\u25CF processing" : "\u25CF ready"
|
|
30539
31323
|
}, undefined, false, undefined, this),
|
|
31324
|
+
/* @__PURE__ */ jsx_dev_runtime4.jsxDEV(Text, {
|
|
31325
|
+
dimColor: true,
|
|
31326
|
+
children: tokenInfo
|
|
31327
|
+
}, undefined, false, undefined, this),
|
|
30540
31328
|
/* @__PURE__ */ jsx_dev_runtime4.jsxDEV(Text, {
|
|
30541
31329
|
dimColor: true,
|
|
30542
31330
|
children: queueInfo
|
|
@@ -30545,7 +31333,8 @@ function Status({ isProcessing, cwd: cwd2, queueLength = 0 }) {
|
|
|
30545
31333
|
dimColor: true,
|
|
30546
31334
|
children: [
|
|
30547
31335
|
" | ",
|
|
30548
|
-
isProcessing ? "Esc to stop" : "Ctrl+C to exit"
|
|
31336
|
+
isProcessing ? "Esc to stop" : "Ctrl+C to exit",
|
|
31337
|
+
" | /help"
|
|
30549
31338
|
]
|
|
30550
31339
|
}, undefined, true, undefined, this)
|
|
30551
31340
|
]
|
|
@@ -30611,6 +31400,7 @@ function App2({ cwd: cwd2 }) {
|
|
|
30611
31400
|
const [error, setError] = import_react25.useState(null);
|
|
30612
31401
|
const [messageQueue, setMessageQueue] = import_react25.useState([]);
|
|
30613
31402
|
const [activityLog, setActivityLog] = import_react25.useState([]);
|
|
31403
|
+
const [tokenUsage, setTokenUsage] = import_react25.useState();
|
|
30614
31404
|
const responseRef = import_react25.useRef("");
|
|
30615
31405
|
const clientRef = import_react25.useRef(null);
|
|
30616
31406
|
const toolCallsRef = import_react25.useRef([]);
|
|
@@ -30648,6 +31438,19 @@ function App2({ cwd: cwd2 }) {
|
|
|
30648
31438
|
responseRef.current += chunk.content;
|
|
30649
31439
|
setCurrentResponse(responseRef.current);
|
|
30650
31440
|
} else if (chunk.type === "tool_use" && chunk.toolCall) {
|
|
31441
|
+
if (responseRef.current.trim()) {
|
|
31442
|
+
setActivityLog((prev) => [
|
|
31443
|
+
...prev,
|
|
31444
|
+
{
|
|
31445
|
+
id: generateId(),
|
|
31446
|
+
type: "text",
|
|
31447
|
+
content: responseRef.current,
|
|
31448
|
+
timestamp: now()
|
|
31449
|
+
}
|
|
31450
|
+
]);
|
|
31451
|
+
setCurrentResponse("");
|
|
31452
|
+
responseRef.current = "";
|
|
31453
|
+
}
|
|
30651
31454
|
toolCallsRef.current.push(chunk.toolCall);
|
|
30652
31455
|
setActivityLog((prev) => [
|
|
30653
31456
|
...prev,
|
|
@@ -30659,7 +31462,6 @@ function App2({ cwd: cwd2 }) {
|
|
|
30659
31462
|
}
|
|
30660
31463
|
]);
|
|
30661
31464
|
setCurrentToolCall(chunk.toolCall);
|
|
30662
|
-
setLastToolResult(undefined);
|
|
30663
31465
|
} else if (chunk.type === "tool_result" && chunk.toolResult) {
|
|
30664
31466
|
toolResultsRef.current.push(chunk.toolResult);
|
|
30665
31467
|
setActivityLog((prev) => [
|
|
@@ -30671,32 +31473,50 @@ function App2({ cwd: cwd2 }) {
|
|
|
30671
31473
|
timestamp: now()
|
|
30672
31474
|
}
|
|
30673
31475
|
]);
|
|
30674
|
-
setLastToolResult(chunk.toolResult);
|
|
30675
31476
|
setCurrentToolCall(undefined);
|
|
30676
31477
|
} else if (chunk.type === "error" && chunk.error) {
|
|
30677
31478
|
setError(chunk.error);
|
|
30678
31479
|
setIsProcessing(false);
|
|
31480
|
+
} else if (chunk.type === "usage" && chunk.usage) {
|
|
31481
|
+
setTokenUsage(chunk.usage);
|
|
30679
31482
|
} else if (chunk.type === "done") {
|
|
30680
|
-
if (responseRef.current
|
|
31483
|
+
if (responseRef.current.trim()) {
|
|
31484
|
+
setActivityLog((prev) => [
|
|
31485
|
+
...prev,
|
|
31486
|
+
{
|
|
31487
|
+
id: generateId(),
|
|
31488
|
+
type: "text",
|
|
31489
|
+
content: responseRef.current,
|
|
31490
|
+
timestamp: now()
|
|
31491
|
+
}
|
|
31492
|
+
]);
|
|
31493
|
+
}
|
|
31494
|
+
const fullContent = activityLog.filter((e) => e.type === "text").map((e) => e.content).join(`
|
|
31495
|
+
`) + (responseRef.current ? `
|
|
31496
|
+
` + responseRef.current : "");
|
|
31497
|
+
if (fullContent.trim() || toolCallsRef.current.length > 0) {
|
|
30681
31498
|
setMessages((prev) => [
|
|
30682
31499
|
...prev,
|
|
30683
31500
|
{
|
|
30684
31501
|
id: generateId(),
|
|
30685
31502
|
role: "assistant",
|
|
30686
|
-
content:
|
|
31503
|
+
content: fullContent.trim(),
|
|
30687
31504
|
timestamp: now(),
|
|
30688
31505
|
toolCalls: toolCallsRef.current.length > 0 ? [...toolCallsRef.current] : undefined,
|
|
30689
31506
|
toolResults: toolResultsRef.current.length > 0 ? [...toolResultsRef.current] : undefined
|
|
30690
31507
|
}
|
|
30691
31508
|
]);
|
|
30692
|
-
setCurrentResponse("");
|
|
30693
|
-
responseRef.current = "";
|
|
30694
|
-
toolCallsRef.current = [];
|
|
30695
|
-
toolResultsRef.current = [];
|
|
30696
31509
|
}
|
|
31510
|
+
setCurrentResponse("");
|
|
31511
|
+
responseRef.current = "";
|
|
31512
|
+
toolCallsRef.current = [];
|
|
31513
|
+
toolResultsRef.current = [];
|
|
30697
31514
|
setCurrentToolCall(undefined);
|
|
30698
|
-
|
|
31515
|
+
setActivityLog([]);
|
|
30699
31516
|
setIsProcessing(false);
|
|
31517
|
+
if (newClient) {
|
|
31518
|
+
setTokenUsage(newClient.getTokenUsage());
|
|
31519
|
+
}
|
|
30700
31520
|
}
|
|
30701
31521
|
});
|
|
30702
31522
|
newClient.onError((err) => {
|
|
@@ -30864,7 +31684,8 @@ function App2({ cwd: cwd2 }) {
|
|
|
30864
31684
|
/* @__PURE__ */ jsx_dev_runtime6.jsxDEV(Status, {
|
|
30865
31685
|
isProcessing,
|
|
30866
31686
|
cwd: cwd2,
|
|
30867
|
-
queueLength: messageQueue.length
|
|
31687
|
+
queueLength: messageQueue.length,
|
|
31688
|
+
tokenUsage
|
|
30868
31689
|
}, undefined, false, undefined, this)
|
|
30869
31690
|
]
|
|
30870
31691
|
}, undefined, true, undefined, this);
|
|
@@ -30879,7 +31700,7 @@ var options = {
|
|
|
30879
31700
|
help: args.includes("--help") || args.includes("-h")
|
|
30880
31701
|
};
|
|
30881
31702
|
if (options.version) {
|
|
30882
|
-
console.log("oldpal v0.
|
|
31703
|
+
console.log("oldpal v0.2.0");
|
|
30883
31704
|
process.exit(0);
|
|
30884
31705
|
}
|
|
30885
31706
|
if (options.help) {
|
|
@@ -30910,4 +31731,4 @@ waitUntilExit().then(() => {
|
|
|
30910
31731
|
process.exit(0);
|
|
30911
31732
|
});
|
|
30912
31733
|
|
|
30913
|
-
//# debugId=
|
|
31734
|
+
//# debugId=21C263FACC119E6E64756E2164756E21
|