@mariozechner/pi-coding-agent 0.6.2
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/README.md +485 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +21 -0
- package/dist/cli.js.map +1 -0
- package/dist/export-html.d.ts +7 -0
- package/dist/export-html.d.ts.map +1 -0
- package/dist/export-html.js +650 -0
- package/dist/export-html.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -0
- package/dist/main.d.ts +2 -0
- package/dist/main.d.ts.map +1 -0
- package/dist/main.js +514 -0
- package/dist/main.js.map +1 -0
- package/dist/session-manager.d.ts +70 -0
- package/dist/session-manager.d.ts.map +1 -0
- package/dist/session-manager.js +323 -0
- package/dist/session-manager.js.map +1 -0
- package/dist/tools/bash.d.ts +7 -0
- package/dist/tools/bash.d.ts.map +1 -0
- package/dist/tools/bash.js +130 -0
- package/dist/tools/bash.js.map +1 -0
- package/dist/tools/edit.d.ts +9 -0
- package/dist/tools/edit.d.ts.map +1 -0
- package/dist/tools/edit.js +207 -0
- package/dist/tools/edit.js.map +1 -0
- package/dist/tools/index.d.ts +19 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +10 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/read.d.ts +9 -0
- package/dist/tools/read.d.ts.map +1 -0
- package/dist/tools/read.js +165 -0
- package/dist/tools/read.js.map +1 -0
- package/dist/tools/write.d.ts +8 -0
- package/dist/tools/write.d.ts.map +1 -0
- package/dist/tools/write.js +81 -0
- package/dist/tools/write.js.map +1 -0
- package/dist/tui/assistant-message.d.ts +11 -0
- package/dist/tui/assistant-message.d.ts.map +1 -0
- package/dist/tui/assistant-message.js +53 -0
- package/dist/tui/assistant-message.js.map +1 -0
- package/dist/tui/custom-editor.d.ts +10 -0
- package/dist/tui/custom-editor.d.ts.map +1 -0
- package/dist/tui/custom-editor.js +24 -0
- package/dist/tui/custom-editor.js.map +1 -0
- package/dist/tui/footer.d.ts +11 -0
- package/dist/tui/footer.d.ts.map +1 -0
- package/dist/tui/footer.js +101 -0
- package/dist/tui/footer.js.map +1 -0
- package/dist/tui/model-selector.d.ts +23 -0
- package/dist/tui/model-selector.d.ts.map +1 -0
- package/dist/tui/model-selector.js +157 -0
- package/dist/tui/model-selector.js.map +1 -0
- package/dist/tui/session-selector.d.ts +37 -0
- package/dist/tui/session-selector.d.ts.map +1 -0
- package/dist/tui/session-selector.js +176 -0
- package/dist/tui/session-selector.js.map +1 -0
- package/dist/tui/thinking-selector.d.ts +11 -0
- package/dist/tui/thinking-selector.d.ts.map +1 -0
- package/dist/tui/thinking-selector.js +48 -0
- package/dist/tui/thinking-selector.js.map +1 -0
- package/dist/tui/tool-execution.d.ts +26 -0
- package/dist/tui/tool-execution.d.ts.map +1 -0
- package/dist/tui/tool-execution.js +246 -0
- package/dist/tui/tool-execution.js.map +1 -0
- package/dist/tui/tui-renderer.d.ts +44 -0
- package/dist/tui/tui-renderer.d.ts.map +1 -0
- package/dist/tui/tui-renderer.js +539 -0
- package/dist/tui/tui-renderer.js.map +1 -0
- package/dist/tui/user-message.d.ts +9 -0
- package/dist/tui/user-message.d.ts.map +1 -0
- package/dist/tui/user-message.js +18 -0
- package/dist/tui/user-message.js.map +1 -0
- package/package.json +53 -0
package/dist/main.js
ADDED
|
@@ -0,0 +1,514 @@
|
|
|
1
|
+
import { Agent, ProviderTransport } from "@mariozechner/pi-agent";
|
|
2
|
+
import { getModel } from "@mariozechner/pi-ai";
|
|
3
|
+
import { ProcessTerminal, TUI } from "@mariozechner/pi-tui";
|
|
4
|
+
import chalk from "chalk";
|
|
5
|
+
import { existsSync, readFileSync } from "fs";
|
|
6
|
+
import { homedir } from "os";
|
|
7
|
+
import { dirname, join, resolve } from "path";
|
|
8
|
+
import { fileURLToPath } from "url";
|
|
9
|
+
import { SessionManager } from "./session-manager.js";
|
|
10
|
+
import { codingTools } from "./tools/index.js";
|
|
11
|
+
import { SessionSelectorComponent } from "./tui/session-selector.js";
|
|
12
|
+
import { TuiRenderer } from "./tui/tui-renderer.js";
|
|
13
|
+
// Get version from package.json
|
|
14
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
15
|
+
const __dirname = dirname(__filename);
|
|
16
|
+
const packageJson = JSON.parse(readFileSync(join(__dirname, "../package.json"), "utf-8"));
|
|
17
|
+
const VERSION = packageJson.version;
|
|
18
|
+
const envApiKeyMap = {
|
|
19
|
+
google: ["GEMINI_API_KEY"],
|
|
20
|
+
openai: ["OPENAI_API_KEY"],
|
|
21
|
+
anthropic: ["ANTHROPIC_OAUTH_TOKEN", "ANTHROPIC_API_KEY"],
|
|
22
|
+
xai: ["XAI_API_KEY"],
|
|
23
|
+
groq: ["GROQ_API_KEY"],
|
|
24
|
+
cerebras: ["CEREBRAS_API_KEY"],
|
|
25
|
+
openrouter: ["OPENROUTER_API_KEY"],
|
|
26
|
+
zai: ["ZAI_API_KEY"],
|
|
27
|
+
};
|
|
28
|
+
function parseArgs(args) {
|
|
29
|
+
const result = {
|
|
30
|
+
messages: [],
|
|
31
|
+
};
|
|
32
|
+
for (let i = 0; i < args.length; i++) {
|
|
33
|
+
const arg = args[i];
|
|
34
|
+
if (arg === "--help" || arg === "-h") {
|
|
35
|
+
result.help = true;
|
|
36
|
+
}
|
|
37
|
+
else if (arg === "--mode" && i + 1 < args.length) {
|
|
38
|
+
const mode = args[++i];
|
|
39
|
+
if (mode === "text" || mode === "json" || mode === "rpc") {
|
|
40
|
+
result.mode = mode;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
else if (arg === "--continue" || arg === "-c") {
|
|
44
|
+
result.continue = true;
|
|
45
|
+
}
|
|
46
|
+
else if (arg === "--resume" || arg === "-r") {
|
|
47
|
+
result.resume = true;
|
|
48
|
+
}
|
|
49
|
+
else if (arg === "--provider" && i + 1 < args.length) {
|
|
50
|
+
result.provider = args[++i];
|
|
51
|
+
}
|
|
52
|
+
else if (arg === "--model" && i + 1 < args.length) {
|
|
53
|
+
result.model = args[++i];
|
|
54
|
+
}
|
|
55
|
+
else if (arg === "--api-key" && i + 1 < args.length) {
|
|
56
|
+
result.apiKey = args[++i];
|
|
57
|
+
}
|
|
58
|
+
else if (arg === "--system-prompt" && i + 1 < args.length) {
|
|
59
|
+
result.systemPrompt = args[++i];
|
|
60
|
+
}
|
|
61
|
+
else if (arg === "--no-session") {
|
|
62
|
+
result.noSession = true;
|
|
63
|
+
}
|
|
64
|
+
else if (arg === "--session" && i + 1 < args.length) {
|
|
65
|
+
result.session = args[++i];
|
|
66
|
+
}
|
|
67
|
+
else if (!arg.startsWith("-")) {
|
|
68
|
+
result.messages.push(arg);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return result;
|
|
72
|
+
}
|
|
73
|
+
function printHelp() {
|
|
74
|
+
console.log(`${chalk.bold("coding-agent")} - AI coding assistant with read, bash, edit, write tools
|
|
75
|
+
|
|
76
|
+
${chalk.bold("Usage:")}
|
|
77
|
+
coding-agent [options] [messages...]
|
|
78
|
+
|
|
79
|
+
${chalk.bold("Options:")}
|
|
80
|
+
--provider <name> Provider name (default: google)
|
|
81
|
+
--model <id> Model ID (default: gemini-2.5-flash)
|
|
82
|
+
--api-key <key> API key (defaults to env vars)
|
|
83
|
+
--system-prompt <text> System prompt (default: coding assistant prompt)
|
|
84
|
+
--mode <mode> Output mode: text (default), json, or rpc
|
|
85
|
+
--continue, -c Continue previous session
|
|
86
|
+
--resume, -r Select a session to resume
|
|
87
|
+
--session <path> Use specific session file
|
|
88
|
+
--no-session Don't save session (ephemeral)
|
|
89
|
+
--help, -h Show this help
|
|
90
|
+
|
|
91
|
+
${chalk.bold("Examples:")}
|
|
92
|
+
# Interactive mode (no messages = interactive TUI)
|
|
93
|
+
coding-agent
|
|
94
|
+
|
|
95
|
+
# Single message
|
|
96
|
+
coding-agent "List all .ts files in src/"
|
|
97
|
+
|
|
98
|
+
# Multiple messages
|
|
99
|
+
coding-agent "Read package.json" "What dependencies do we have?"
|
|
100
|
+
|
|
101
|
+
# Continue previous session
|
|
102
|
+
coding-agent --continue "What did we discuss?"
|
|
103
|
+
|
|
104
|
+
# Use different model
|
|
105
|
+
coding-agent --provider openai --model gpt-4o-mini "Help me refactor this code"
|
|
106
|
+
|
|
107
|
+
${chalk.bold("Environment Variables:")}
|
|
108
|
+
GEMINI_API_KEY - Google Gemini API key
|
|
109
|
+
OPENAI_API_KEY - OpenAI API key
|
|
110
|
+
ANTHROPIC_API_KEY - Anthropic API key
|
|
111
|
+
CODING_AGENT_DIR - Session storage directory (default: ~/.coding-agent)
|
|
112
|
+
|
|
113
|
+
${chalk.bold("Available Tools:")}
|
|
114
|
+
read - Read file contents
|
|
115
|
+
bash - Execute bash commands
|
|
116
|
+
edit - Edit files with find/replace
|
|
117
|
+
write - Write files (creates/overwrites)
|
|
118
|
+
`);
|
|
119
|
+
}
|
|
120
|
+
function buildSystemPrompt(customPrompt) {
|
|
121
|
+
// Check if customPrompt is a file path that exists
|
|
122
|
+
if (customPrompt && existsSync(customPrompt)) {
|
|
123
|
+
try {
|
|
124
|
+
customPrompt = readFileSync(customPrompt, "utf-8");
|
|
125
|
+
}
|
|
126
|
+
catch (error) {
|
|
127
|
+
console.error(chalk.yellow(`Warning: Could not read system prompt file ${customPrompt}: ${error}`));
|
|
128
|
+
// Fall through to use as literal string
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
if (customPrompt) {
|
|
132
|
+
// Use custom prompt as base, then add context/datetime
|
|
133
|
+
const now = new Date();
|
|
134
|
+
const dateTime = now.toLocaleString("en-US", {
|
|
135
|
+
weekday: "long",
|
|
136
|
+
year: "numeric",
|
|
137
|
+
month: "long",
|
|
138
|
+
day: "numeric",
|
|
139
|
+
hour: "2-digit",
|
|
140
|
+
minute: "2-digit",
|
|
141
|
+
second: "2-digit",
|
|
142
|
+
timeZoneName: "short",
|
|
143
|
+
});
|
|
144
|
+
let prompt = customPrompt;
|
|
145
|
+
// Append project context files
|
|
146
|
+
const contextFiles = loadProjectContextFiles();
|
|
147
|
+
if (contextFiles.length > 0) {
|
|
148
|
+
prompt += "\n\n# Project Context\n\n";
|
|
149
|
+
prompt += "The following project context files have been loaded:\n\n";
|
|
150
|
+
for (const { path: filePath, content } of contextFiles) {
|
|
151
|
+
prompt += `## ${filePath}\n\n${content}\n\n`;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
// Add date/time and working directory last
|
|
155
|
+
prompt += `\nCurrent date and time: ${dateTime}`;
|
|
156
|
+
prompt += `\nCurrent working directory: ${process.cwd()}`;
|
|
157
|
+
return prompt;
|
|
158
|
+
}
|
|
159
|
+
const now = new Date();
|
|
160
|
+
const dateTime = now.toLocaleString("en-US", {
|
|
161
|
+
weekday: "long",
|
|
162
|
+
year: "numeric",
|
|
163
|
+
month: "long",
|
|
164
|
+
day: "numeric",
|
|
165
|
+
hour: "2-digit",
|
|
166
|
+
minute: "2-digit",
|
|
167
|
+
second: "2-digit",
|
|
168
|
+
timeZoneName: "short",
|
|
169
|
+
});
|
|
170
|
+
let prompt = `You are an expert coding assistant. You help users with coding tasks by reading files, executing commands, editing code, and writing new files.
|
|
171
|
+
|
|
172
|
+
Available tools:
|
|
173
|
+
- read: Read file contents
|
|
174
|
+
- bash: Execute bash commands (ls, grep, find, etc.)
|
|
175
|
+
- edit: Make surgical edits to files (find exact text and replace)
|
|
176
|
+
- write: Create or overwrite files
|
|
177
|
+
|
|
178
|
+
Guidelines:
|
|
179
|
+
- Always use bash tool for file operations like ls, grep, find
|
|
180
|
+
- Use read to examine files before editing
|
|
181
|
+
- Use edit for precise changes (old text must match exactly)
|
|
182
|
+
- Use write only for new files or complete rewrites
|
|
183
|
+
- Be concise in your responses
|
|
184
|
+
- Show file paths clearly when working with files`;
|
|
185
|
+
// Append project context files
|
|
186
|
+
const contextFiles = loadProjectContextFiles();
|
|
187
|
+
if (contextFiles.length > 0) {
|
|
188
|
+
prompt += "\n\n# Project Context\n\n";
|
|
189
|
+
prompt += "The following project context files have been loaded:\n\n";
|
|
190
|
+
for (const { path: filePath, content } of contextFiles) {
|
|
191
|
+
prompt += `## ${filePath}\n\n${content}\n\n`;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
// Add date/time and working directory last
|
|
195
|
+
prompt += `\nCurrent date and time: ${dateTime}`;
|
|
196
|
+
prompt += `\nCurrent working directory: ${process.cwd()}`;
|
|
197
|
+
return prompt;
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Look for AGENT.md or CLAUDE.md in a directory (prefers AGENT.md)
|
|
201
|
+
*/
|
|
202
|
+
function loadContextFileFromDir(dir) {
|
|
203
|
+
const candidates = ["AGENT.md", "CLAUDE.md"];
|
|
204
|
+
for (const filename of candidates) {
|
|
205
|
+
const filePath = join(dir, filename);
|
|
206
|
+
if (existsSync(filePath)) {
|
|
207
|
+
try {
|
|
208
|
+
return {
|
|
209
|
+
path: filePath,
|
|
210
|
+
content: readFileSync(filePath, "utf-8"),
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
catch (error) {
|
|
214
|
+
console.error(chalk.yellow(`Warning: Could not read ${filePath}: ${error}`));
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
return null;
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* Load all project context files in order:
|
|
222
|
+
* 1. Global: ~/.pi/agent/AGENT.md or CLAUDE.md
|
|
223
|
+
* 2. Parent directories (top-most first) down to cwd
|
|
224
|
+
* Each returns {path, content} for separate messages
|
|
225
|
+
*/
|
|
226
|
+
function loadProjectContextFiles() {
|
|
227
|
+
const contextFiles = [];
|
|
228
|
+
// 1. Load global context from ~/.pi/agent/
|
|
229
|
+
const homeDir = homedir();
|
|
230
|
+
const globalContextDir = resolve(process.env.CODING_AGENT_DIR || join(homeDir, ".pi/agent/"));
|
|
231
|
+
const globalContext = loadContextFileFromDir(globalContextDir);
|
|
232
|
+
if (globalContext) {
|
|
233
|
+
contextFiles.push(globalContext);
|
|
234
|
+
}
|
|
235
|
+
// 2. Walk up from cwd to root, collecting all context files
|
|
236
|
+
const cwd = process.cwd();
|
|
237
|
+
const ancestorContextFiles = [];
|
|
238
|
+
let currentDir = cwd;
|
|
239
|
+
const root = resolve("/");
|
|
240
|
+
while (true) {
|
|
241
|
+
const contextFile = loadContextFileFromDir(currentDir);
|
|
242
|
+
if (contextFile) {
|
|
243
|
+
// Add to beginning so we get top-most parent first
|
|
244
|
+
ancestorContextFiles.unshift(contextFile);
|
|
245
|
+
}
|
|
246
|
+
// Stop if we've reached root
|
|
247
|
+
if (currentDir === root)
|
|
248
|
+
break;
|
|
249
|
+
// Move up one directory
|
|
250
|
+
const parentDir = resolve(currentDir, "..");
|
|
251
|
+
if (parentDir === currentDir)
|
|
252
|
+
break; // Safety check
|
|
253
|
+
currentDir = parentDir;
|
|
254
|
+
}
|
|
255
|
+
// Add ancestor files in order (top-most → cwd)
|
|
256
|
+
contextFiles.push(...ancestorContextFiles);
|
|
257
|
+
return contextFiles;
|
|
258
|
+
}
|
|
259
|
+
async function selectSession(sessionManager) {
|
|
260
|
+
return new Promise((resolve) => {
|
|
261
|
+
const ui = new TUI(new ProcessTerminal());
|
|
262
|
+
let resolved = false;
|
|
263
|
+
const selector = new SessionSelectorComponent(sessionManager, (path) => {
|
|
264
|
+
if (!resolved) {
|
|
265
|
+
resolved = true;
|
|
266
|
+
ui.stop();
|
|
267
|
+
resolve(path);
|
|
268
|
+
}
|
|
269
|
+
}, () => {
|
|
270
|
+
if (!resolved) {
|
|
271
|
+
resolved = true;
|
|
272
|
+
ui.stop();
|
|
273
|
+
resolve(null);
|
|
274
|
+
}
|
|
275
|
+
});
|
|
276
|
+
ui.addChild(selector);
|
|
277
|
+
ui.setFocus(selector.getSessionList());
|
|
278
|
+
ui.start();
|
|
279
|
+
});
|
|
280
|
+
}
|
|
281
|
+
async function runInteractiveMode(agent, sessionManager, version) {
|
|
282
|
+
const renderer = new TuiRenderer(agent, sessionManager, version);
|
|
283
|
+
// Initialize TUI
|
|
284
|
+
await renderer.init();
|
|
285
|
+
// Set interrupt callback
|
|
286
|
+
renderer.setInterruptCallback(() => {
|
|
287
|
+
agent.abort();
|
|
288
|
+
});
|
|
289
|
+
// Render any existing messages (from --continue mode)
|
|
290
|
+
renderer.renderInitialMessages(agent.state);
|
|
291
|
+
// Subscribe to agent events
|
|
292
|
+
agent.subscribe(async (event) => {
|
|
293
|
+
// Pass all events to the renderer
|
|
294
|
+
await renderer.handleEvent(event, agent.state);
|
|
295
|
+
});
|
|
296
|
+
// Interactive loop
|
|
297
|
+
while (true) {
|
|
298
|
+
const userInput = await renderer.getUserInput();
|
|
299
|
+
// Process the message - agent.prompt will add user message and trigger state updates
|
|
300
|
+
try {
|
|
301
|
+
await agent.prompt(userInput);
|
|
302
|
+
}
|
|
303
|
+
catch (error) {
|
|
304
|
+
// Display error in the TUI by adding an error message to the chat
|
|
305
|
+
renderer.showError(error.message || "Unknown error occurred");
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
async function runSingleShotMode(agent, _sessionManager, messages, mode) {
|
|
310
|
+
if (mode === "json") {
|
|
311
|
+
// Subscribe to all events and output as JSON
|
|
312
|
+
agent.subscribe((event) => {
|
|
313
|
+
// Output event as JSON (same format as session manager)
|
|
314
|
+
console.log(JSON.stringify(event));
|
|
315
|
+
});
|
|
316
|
+
}
|
|
317
|
+
for (const message of messages) {
|
|
318
|
+
await agent.prompt(message);
|
|
319
|
+
}
|
|
320
|
+
// In text mode, only output the final assistant message
|
|
321
|
+
if (mode === "text") {
|
|
322
|
+
const lastMessage = agent.state.messages[agent.state.messages.length - 1];
|
|
323
|
+
if (lastMessage.role === "assistant") {
|
|
324
|
+
for (const content of lastMessage.content) {
|
|
325
|
+
if (content.type === "text") {
|
|
326
|
+
console.log(content.text);
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
async function runRpcMode(agent, _sessionManager) {
|
|
333
|
+
// Subscribe to all events and output as JSON
|
|
334
|
+
agent.subscribe((event) => {
|
|
335
|
+
console.log(JSON.stringify(event));
|
|
336
|
+
});
|
|
337
|
+
// Listen for JSON input on stdin
|
|
338
|
+
const readline = await import("readline");
|
|
339
|
+
const rl = readline.createInterface({
|
|
340
|
+
input: process.stdin,
|
|
341
|
+
output: process.stdout,
|
|
342
|
+
terminal: false,
|
|
343
|
+
});
|
|
344
|
+
rl.on("line", async (line) => {
|
|
345
|
+
try {
|
|
346
|
+
const input = JSON.parse(line);
|
|
347
|
+
// Handle different RPC commands
|
|
348
|
+
if (input.type === "prompt" && input.message) {
|
|
349
|
+
await agent.prompt(input.message);
|
|
350
|
+
}
|
|
351
|
+
else if (input.type === "abort") {
|
|
352
|
+
agent.abort();
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
catch (error) {
|
|
356
|
+
// Output error as JSON
|
|
357
|
+
console.log(JSON.stringify({ type: "error", error: error.message }));
|
|
358
|
+
}
|
|
359
|
+
});
|
|
360
|
+
// Keep process alive
|
|
361
|
+
return new Promise(() => { });
|
|
362
|
+
}
|
|
363
|
+
export async function main(args) {
|
|
364
|
+
const parsed = parseArgs(args);
|
|
365
|
+
if (parsed.help) {
|
|
366
|
+
printHelp();
|
|
367
|
+
return;
|
|
368
|
+
}
|
|
369
|
+
// Setup session manager
|
|
370
|
+
const sessionManager = new SessionManager(parsed.continue && !parsed.resume, parsed.session);
|
|
371
|
+
// Disable session saving if --no-session flag is set
|
|
372
|
+
if (parsed.noSession) {
|
|
373
|
+
sessionManager.disable();
|
|
374
|
+
}
|
|
375
|
+
// Handle --resume flag: show session selector
|
|
376
|
+
if (parsed.resume) {
|
|
377
|
+
const selectedSession = await selectSession(sessionManager);
|
|
378
|
+
if (!selectedSession) {
|
|
379
|
+
console.log(chalk.dim("No session selected"));
|
|
380
|
+
return;
|
|
381
|
+
}
|
|
382
|
+
// Set the selected session as the active session
|
|
383
|
+
sessionManager.setSessionFile(selectedSession);
|
|
384
|
+
}
|
|
385
|
+
// Determine provider and model
|
|
386
|
+
const provider = (parsed.provider || "anthropic");
|
|
387
|
+
const modelId = parsed.model || "claude-sonnet-4-5";
|
|
388
|
+
// Helper function to get API key for a provider
|
|
389
|
+
const getApiKeyForProvider = (providerName) => {
|
|
390
|
+
// Check if API key was provided via command line
|
|
391
|
+
if (parsed.apiKey) {
|
|
392
|
+
return parsed.apiKey;
|
|
393
|
+
}
|
|
394
|
+
const envVars = envApiKeyMap[providerName];
|
|
395
|
+
// Check each environment variable in priority order
|
|
396
|
+
for (const envVar of envVars) {
|
|
397
|
+
const key = process.env[envVar];
|
|
398
|
+
if (key) {
|
|
399
|
+
return key;
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
return undefined;
|
|
403
|
+
};
|
|
404
|
+
// Get initial API key
|
|
405
|
+
const initialApiKey = getApiKeyForProvider(provider);
|
|
406
|
+
if (!initialApiKey) {
|
|
407
|
+
const envVars = envApiKeyMap[provider];
|
|
408
|
+
const envVarList = envVars.join(" or ");
|
|
409
|
+
console.error(chalk.red(`Error: No API key found for provider "${provider}"`));
|
|
410
|
+
console.error(chalk.dim(`Set ${envVarList} environment variable or use --api-key flag`));
|
|
411
|
+
process.exit(1);
|
|
412
|
+
}
|
|
413
|
+
// Create agent
|
|
414
|
+
const model = getModel(provider, modelId);
|
|
415
|
+
const systemPrompt = buildSystemPrompt(parsed.systemPrompt);
|
|
416
|
+
const agent = new Agent({
|
|
417
|
+
initialState: {
|
|
418
|
+
systemPrompt,
|
|
419
|
+
model,
|
|
420
|
+
thinkingLevel: "off",
|
|
421
|
+
tools: codingTools,
|
|
422
|
+
},
|
|
423
|
+
transport: new ProviderTransport({
|
|
424
|
+
// Dynamic API key lookup based on current model's provider
|
|
425
|
+
getApiKey: async () => {
|
|
426
|
+
const currentProvider = agent.state.model.provider;
|
|
427
|
+
const key = getApiKeyForProvider(currentProvider);
|
|
428
|
+
if (!key) {
|
|
429
|
+
throw new Error(`No API key found for provider "${currentProvider}". Please set the appropriate environment variable.`);
|
|
430
|
+
}
|
|
431
|
+
return key;
|
|
432
|
+
},
|
|
433
|
+
}),
|
|
434
|
+
});
|
|
435
|
+
// Determine mode early to know if we should print messages
|
|
436
|
+
const isInteractive = parsed.messages.length === 0;
|
|
437
|
+
const mode = parsed.mode || "text";
|
|
438
|
+
const shouldPrintMessages = isInteractive || mode === "text";
|
|
439
|
+
// Load previous messages if continuing or resuming
|
|
440
|
+
if (parsed.continue || parsed.resume) {
|
|
441
|
+
const messages = sessionManager.loadMessages();
|
|
442
|
+
if (messages.length > 0) {
|
|
443
|
+
if (shouldPrintMessages) {
|
|
444
|
+
console.log(chalk.dim(`Loaded ${messages.length} messages from previous session`));
|
|
445
|
+
}
|
|
446
|
+
agent.replaceMessages(messages);
|
|
447
|
+
}
|
|
448
|
+
// Load and restore model
|
|
449
|
+
const savedModel = sessionManager.loadModel();
|
|
450
|
+
if (savedModel) {
|
|
451
|
+
// Parse provider/modelId from saved model string (format: "provider/modelId")
|
|
452
|
+
const [savedProvider, savedModelId] = savedModel.split("/");
|
|
453
|
+
if (savedProvider && savedModelId) {
|
|
454
|
+
try {
|
|
455
|
+
const restoredModel = getModel(savedProvider, savedModelId);
|
|
456
|
+
agent.setModel(restoredModel);
|
|
457
|
+
if (shouldPrintMessages) {
|
|
458
|
+
console.log(chalk.dim(`Restored model: ${savedModel}`));
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
catch (error) {
|
|
462
|
+
if (shouldPrintMessages) {
|
|
463
|
+
console.error(chalk.yellow(`Warning: Could not restore model ${savedModel}: ${error.message}`));
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
// Load and restore thinking level
|
|
469
|
+
const thinkingLevel = sessionManager.loadThinkingLevel();
|
|
470
|
+
if (thinkingLevel) {
|
|
471
|
+
agent.setThinkingLevel(thinkingLevel);
|
|
472
|
+
if (shouldPrintMessages) {
|
|
473
|
+
console.log(chalk.dim(`Restored thinking level: ${thinkingLevel}`));
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
// Note: Session will be started lazily after first user+assistant message exchange
|
|
478
|
+
// (unless continuing/resuming, in which case it's already initialized)
|
|
479
|
+
// Log loaded context files (they're already in the system prompt)
|
|
480
|
+
if (shouldPrintMessages && !parsed.continue && !parsed.resume) {
|
|
481
|
+
const contextFiles = loadProjectContextFiles();
|
|
482
|
+
if (contextFiles.length > 0) {
|
|
483
|
+
console.log(chalk.dim("Loaded project context from:"));
|
|
484
|
+
for (const { path: filePath } of contextFiles) {
|
|
485
|
+
console.log(chalk.dim(` - ${filePath}`));
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
// Subscribe to agent events to save messages
|
|
490
|
+
agent.subscribe((event) => {
|
|
491
|
+
// Save messages on completion
|
|
492
|
+
if (event.type === "message_end") {
|
|
493
|
+
sessionManager.saveMessage(event.message);
|
|
494
|
+
// Check if we should initialize session now (after first user+assistant exchange)
|
|
495
|
+
if (sessionManager.shouldInitializeSession(agent.state.messages)) {
|
|
496
|
+
sessionManager.startSession(agent.state);
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
});
|
|
500
|
+
// Route to appropriate mode
|
|
501
|
+
if (mode === "rpc") {
|
|
502
|
+
// RPC mode - headless operation
|
|
503
|
+
await runRpcMode(agent, sessionManager);
|
|
504
|
+
}
|
|
505
|
+
else if (isInteractive) {
|
|
506
|
+
// No messages and not RPC - use TUI
|
|
507
|
+
await runInteractiveMode(agent, sessionManager, VERSION);
|
|
508
|
+
}
|
|
509
|
+
else {
|
|
510
|
+
// CLI mode with messages
|
|
511
|
+
await runSingleShotMode(agent, sessionManager, parsed.messages, mode);
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
//# sourceMappingURL=main.js.map
|
package/dist/main.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"main.js","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":"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","sourcesContent":["import { Agent, ProviderTransport, type ThinkingLevel } from \"@mariozechner/pi-agent\";\nimport { getModel, type KnownProvider } from \"@mariozechner/pi-ai\";\nimport { ProcessTerminal, TUI } from \"@mariozechner/pi-tui\";\nimport chalk from \"chalk\";\nimport { existsSync, readFileSync } from \"fs\";\nimport { homedir } from \"os\";\nimport { dirname, join, resolve } from \"path\";\nimport { fileURLToPath } from \"url\";\nimport { SessionManager } from \"./session-manager.js\";\nimport { codingTools } from \"./tools/index.js\";\nimport { SessionSelectorComponent } from \"./tui/session-selector.js\";\nimport { TuiRenderer } from \"./tui/tui-renderer.js\";\n\n// Get version from package.json\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = dirname(__filename);\nconst packageJson = JSON.parse(readFileSync(join(__dirname, \"../package.json\"), \"utf-8\"));\nconst VERSION = packageJson.version;\n\nconst envApiKeyMap: Record<KnownProvider, string[]> = {\n\tgoogle: [\"GEMINI_API_KEY\"],\n\topenai: [\"OPENAI_API_KEY\"],\n\tanthropic: [\"ANTHROPIC_OAUTH_TOKEN\", \"ANTHROPIC_API_KEY\"],\n\txai: [\"XAI_API_KEY\"],\n\tgroq: [\"GROQ_API_KEY\"],\n\tcerebras: [\"CEREBRAS_API_KEY\"],\n\topenrouter: [\"OPENROUTER_API_KEY\"],\n\tzai: [\"ZAI_API_KEY\"],\n};\n\ntype Mode = \"text\" | \"json\" | \"rpc\";\n\ninterface Args {\n\tprovider?: string;\n\tmodel?: string;\n\tapiKey?: string;\n\tsystemPrompt?: string;\n\tcontinue?: boolean;\n\tresume?: boolean;\n\thelp?: boolean;\n\tmode?: Mode;\n\tnoSession?: boolean;\n\tsession?: string;\n\tmessages: string[];\n}\n\nfunction parseArgs(args: string[]): Args {\n\tconst result: Args = {\n\t\tmessages: [],\n\t};\n\n\tfor (let i = 0; i < args.length; i++) {\n\t\tconst arg = args[i];\n\n\t\tif (arg === \"--help\" || arg === \"-h\") {\n\t\t\tresult.help = true;\n\t\t} else if (arg === \"--mode\" && i + 1 < args.length) {\n\t\t\tconst mode = args[++i];\n\t\t\tif (mode === \"text\" || mode === \"json\" || mode === \"rpc\") {\n\t\t\t\tresult.mode = mode;\n\t\t\t}\n\t\t} else if (arg === \"--continue\" || arg === \"-c\") {\n\t\t\tresult.continue = true;\n\t\t} else if (arg === \"--resume\" || arg === \"-r\") {\n\t\t\tresult.resume = true;\n\t\t} else if (arg === \"--provider\" && i + 1 < args.length) {\n\t\t\tresult.provider = args[++i];\n\t\t} else if (arg === \"--model\" && i + 1 < args.length) {\n\t\t\tresult.model = args[++i];\n\t\t} else if (arg === \"--api-key\" && i + 1 < args.length) {\n\t\t\tresult.apiKey = args[++i];\n\t\t} else if (arg === \"--system-prompt\" && i + 1 < args.length) {\n\t\t\tresult.systemPrompt = args[++i];\n\t\t} else if (arg === \"--no-session\") {\n\t\t\tresult.noSession = true;\n\t\t} else if (arg === \"--session\" && i + 1 < args.length) {\n\t\t\tresult.session = args[++i];\n\t\t} else if (!arg.startsWith(\"-\")) {\n\t\t\tresult.messages.push(arg);\n\t\t}\n\t}\n\n\treturn result;\n}\n\nfunction printHelp() {\n\tconsole.log(`${chalk.bold(\"coding-agent\")} - AI coding assistant with read, bash, edit, write tools\n\n${chalk.bold(\"Usage:\")}\n coding-agent [options] [messages...]\n\n${chalk.bold(\"Options:\")}\n --provider <name> Provider name (default: google)\n --model <id> Model ID (default: gemini-2.5-flash)\n --api-key <key> API key (defaults to env vars)\n --system-prompt <text> System prompt (default: coding assistant prompt)\n --mode <mode> Output mode: text (default), json, or rpc\n --continue, -c Continue previous session\n --resume, -r Select a session to resume\n --session <path> Use specific session file\n --no-session Don't save session (ephemeral)\n --help, -h Show this help\n\n${chalk.bold(\"Examples:\")}\n # Interactive mode (no messages = interactive TUI)\n coding-agent\n\n # Single message\n coding-agent \"List all .ts files in src/\"\n\n # Multiple messages\n coding-agent \"Read package.json\" \"What dependencies do we have?\"\n\n # Continue previous session\n coding-agent --continue \"What did we discuss?\"\n\n # Use different model\n coding-agent --provider openai --model gpt-4o-mini \"Help me refactor this code\"\n\n${chalk.bold(\"Environment Variables:\")}\n GEMINI_API_KEY - Google Gemini API key\n OPENAI_API_KEY - OpenAI API key\n ANTHROPIC_API_KEY - Anthropic API key\n CODING_AGENT_DIR - Session storage directory (default: ~/.coding-agent)\n\n${chalk.bold(\"Available Tools:\")}\n read - Read file contents\n bash - Execute bash commands\n edit - Edit files with find/replace\n write - Write files (creates/overwrites)\n`);\n}\n\nfunction buildSystemPrompt(customPrompt?: string): string {\n\t// Check if customPrompt is a file path that exists\n\tif (customPrompt && existsSync(customPrompt)) {\n\t\ttry {\n\t\t\tcustomPrompt = readFileSync(customPrompt, \"utf-8\");\n\t\t} catch (error) {\n\t\t\tconsole.error(chalk.yellow(`Warning: Could not read system prompt file ${customPrompt}: ${error}`));\n\t\t\t// Fall through to use as literal string\n\t\t}\n\t}\n\n\tif (customPrompt) {\n\t\t// Use custom prompt as base, then add context/datetime\n\t\tconst now = new Date();\n\t\tconst dateTime = now.toLocaleString(\"en-US\", {\n\t\t\tweekday: \"long\",\n\t\t\tyear: \"numeric\",\n\t\t\tmonth: \"long\",\n\t\t\tday: \"numeric\",\n\t\t\thour: \"2-digit\",\n\t\t\tminute: \"2-digit\",\n\t\t\tsecond: \"2-digit\",\n\t\t\ttimeZoneName: \"short\",\n\t\t});\n\n\t\tlet prompt = customPrompt;\n\n\t\t// Append project context files\n\t\tconst contextFiles = loadProjectContextFiles();\n\t\tif (contextFiles.length > 0) {\n\t\t\tprompt += \"\\n\\n# Project Context\\n\\n\";\n\t\t\tprompt += \"The following project context files have been loaded:\\n\\n\";\n\t\t\tfor (const { path: filePath, content } of contextFiles) {\n\t\t\t\tprompt += `## ${filePath}\\n\\n${content}\\n\\n`;\n\t\t\t}\n\t\t}\n\n\t\t// Add date/time and working directory last\n\t\tprompt += `\\nCurrent date and time: ${dateTime}`;\n\t\tprompt += `\\nCurrent working directory: ${process.cwd()}`;\n\n\t\treturn prompt;\n\t}\n\n\tconst now = new Date();\n\tconst dateTime = now.toLocaleString(\"en-US\", {\n\t\tweekday: \"long\",\n\t\tyear: \"numeric\",\n\t\tmonth: \"long\",\n\t\tday: \"numeric\",\n\t\thour: \"2-digit\",\n\t\tminute: \"2-digit\",\n\t\tsecond: \"2-digit\",\n\t\ttimeZoneName: \"short\",\n\t});\n\n\tlet prompt = `You are an expert coding assistant. You help users with coding tasks by reading files, executing commands, editing code, and writing new files.\n\nAvailable tools:\n- read: Read file contents\n- bash: Execute bash commands (ls, grep, find, etc.)\n- edit: Make surgical edits to files (find exact text and replace)\n- write: Create or overwrite files\n\nGuidelines:\n- Always use bash tool for file operations like ls, grep, find\n- Use read to examine files before editing\n- Use edit for precise changes (old text must match exactly)\n- Use write only for new files or complete rewrites\n- Be concise in your responses\n- Show file paths clearly when working with files`;\n\n\t// Append project context files\n\tconst contextFiles = loadProjectContextFiles();\n\tif (contextFiles.length > 0) {\n\t\tprompt += \"\\n\\n# Project Context\\n\\n\";\n\t\tprompt += \"The following project context files have been loaded:\\n\\n\";\n\t\tfor (const { path: filePath, content } of contextFiles) {\n\t\t\tprompt += `## ${filePath}\\n\\n${content}\\n\\n`;\n\t\t}\n\t}\n\n\t// Add date/time and working directory last\n\tprompt += `\\nCurrent date and time: ${dateTime}`;\n\tprompt += `\\nCurrent working directory: ${process.cwd()}`;\n\n\treturn prompt;\n}\n\n/**\n * Look for AGENT.md or CLAUDE.md in a directory (prefers AGENT.md)\n */\nfunction loadContextFileFromDir(dir: string): { path: string; content: string } | null {\n\tconst candidates = [\"AGENT.md\", \"CLAUDE.md\"];\n\tfor (const filename of candidates) {\n\t\tconst filePath = join(dir, filename);\n\t\tif (existsSync(filePath)) {\n\t\t\ttry {\n\t\t\t\treturn {\n\t\t\t\t\tpath: filePath,\n\t\t\t\t\tcontent: readFileSync(filePath, \"utf-8\"),\n\t\t\t\t};\n\t\t\t} catch (error) {\n\t\t\t\tconsole.error(chalk.yellow(`Warning: Could not read ${filePath}: ${error}`));\n\t\t\t}\n\t\t}\n\t}\n\treturn null;\n}\n\n/**\n * Load all project context files in order:\n * 1. Global: ~/.pi/agent/AGENT.md or CLAUDE.md\n * 2. Parent directories (top-most first) down to cwd\n * Each returns {path, content} for separate messages\n */\nfunction loadProjectContextFiles(): Array<{ path: string; content: string }> {\n\tconst contextFiles: Array<{ path: string; content: string }> = [];\n\n\t// 1. Load global context from ~/.pi/agent/\n\tconst homeDir = homedir();\n\tconst globalContextDir = resolve(process.env.CODING_AGENT_DIR || join(homeDir, \".pi/agent/\"));\n\tconst globalContext = loadContextFileFromDir(globalContextDir);\n\tif (globalContext) {\n\t\tcontextFiles.push(globalContext);\n\t}\n\n\t// 2. Walk up from cwd to root, collecting all context files\n\tconst cwd = process.cwd();\n\tconst ancestorContextFiles: Array<{ path: string; content: string }> = [];\n\n\tlet currentDir = cwd;\n\tconst root = resolve(\"/\");\n\n\twhile (true) {\n\t\tconst contextFile = loadContextFileFromDir(currentDir);\n\t\tif (contextFile) {\n\t\t\t// Add to beginning so we get top-most parent first\n\t\t\tancestorContextFiles.unshift(contextFile);\n\t\t}\n\n\t\t// Stop if we've reached root\n\t\tif (currentDir === root) break;\n\n\t\t// Move up one directory\n\t\tconst parentDir = resolve(currentDir, \"..\");\n\t\tif (parentDir === currentDir) break; // Safety check\n\t\tcurrentDir = parentDir;\n\t}\n\n\t// Add ancestor files in order (top-most → cwd)\n\tcontextFiles.push(...ancestorContextFiles);\n\n\treturn contextFiles;\n}\n\nasync function selectSession(sessionManager: SessionManager): Promise<string | null> {\n\treturn new Promise((resolve) => {\n\t\tconst ui = new TUI(new ProcessTerminal());\n\t\tlet resolved = false;\n\n\t\tconst selector = new SessionSelectorComponent(\n\t\t\tsessionManager,\n\t\t\t(path: string) => {\n\t\t\t\tif (!resolved) {\n\t\t\t\t\tresolved = true;\n\t\t\t\t\tui.stop();\n\t\t\t\t\tresolve(path);\n\t\t\t\t}\n\t\t\t},\n\t\t\t() => {\n\t\t\t\tif (!resolved) {\n\t\t\t\t\tresolved = true;\n\t\t\t\t\tui.stop();\n\t\t\t\t\tresolve(null);\n\t\t\t\t}\n\t\t\t},\n\t\t);\n\n\t\tui.addChild(selector);\n\t\tui.setFocus(selector.getSessionList());\n\t\tui.start();\n\t});\n}\n\nasync function runInteractiveMode(agent: Agent, sessionManager: SessionManager, version: string): Promise<void> {\n\tconst renderer = new TuiRenderer(agent, sessionManager, version);\n\n\t// Initialize TUI\n\tawait renderer.init();\n\n\t// Set interrupt callback\n\trenderer.setInterruptCallback(() => {\n\t\tagent.abort();\n\t});\n\n\t// Render any existing messages (from --continue mode)\n\trenderer.renderInitialMessages(agent.state);\n\n\t// Subscribe to agent events\n\tagent.subscribe(async (event) => {\n\t\t// Pass all events to the renderer\n\t\tawait renderer.handleEvent(event, agent.state);\n\t});\n\n\t// Interactive loop\n\twhile (true) {\n\t\tconst userInput = await renderer.getUserInput();\n\n\t\t// Process the message - agent.prompt will add user message and trigger state updates\n\t\ttry {\n\t\t\tawait agent.prompt(userInput);\n\t\t} catch (error: any) {\n\t\t\t// Display error in the TUI by adding an error message to the chat\n\t\t\trenderer.showError(error.message || \"Unknown error occurred\");\n\t\t}\n\t}\n}\n\nasync function runSingleShotMode(\n\tagent: Agent,\n\t_sessionManager: SessionManager,\n\tmessages: string[],\n\tmode: \"text\" | \"json\",\n): Promise<void> {\n\tif (mode === \"json\") {\n\t\t// Subscribe to all events and output as JSON\n\t\tagent.subscribe((event) => {\n\t\t\t// Output event as JSON (same format as session manager)\n\t\t\tconsole.log(JSON.stringify(event));\n\t\t});\n\t}\n\n\tfor (const message of messages) {\n\t\tawait agent.prompt(message);\n\t}\n\n\t// In text mode, only output the final assistant message\n\tif (mode === \"text\") {\n\t\tconst lastMessage = agent.state.messages[agent.state.messages.length - 1];\n\t\tif (lastMessage.role === \"assistant\") {\n\t\t\tfor (const content of lastMessage.content) {\n\t\t\t\tif (content.type === \"text\") {\n\t\t\t\t\tconsole.log(content.text);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\nasync function runRpcMode(agent: Agent, _sessionManager: SessionManager): Promise<void> {\n\t// Subscribe to all events and output as JSON\n\tagent.subscribe((event) => {\n\t\tconsole.log(JSON.stringify(event));\n\t});\n\n\t// Listen for JSON input on stdin\n\tconst readline = await import(\"readline\");\n\tconst rl = readline.createInterface({\n\t\tinput: process.stdin,\n\t\toutput: process.stdout,\n\t\tterminal: false,\n\t});\n\n\trl.on(\"line\", async (line: string) => {\n\t\ttry {\n\t\t\tconst input = JSON.parse(line);\n\n\t\t\t// Handle different RPC commands\n\t\t\tif (input.type === \"prompt\" && input.message) {\n\t\t\t\tawait agent.prompt(input.message);\n\t\t\t} else if (input.type === \"abort\") {\n\t\t\t\tagent.abort();\n\t\t\t}\n\t\t} catch (error: any) {\n\t\t\t// Output error as JSON\n\t\t\tconsole.log(JSON.stringify({ type: \"error\", error: error.message }));\n\t\t}\n\t});\n\n\t// Keep process alive\n\treturn new Promise(() => {});\n}\n\nexport async function main(args: string[]) {\n\tconst parsed = parseArgs(args);\n\n\tif (parsed.help) {\n\t\tprintHelp();\n\t\treturn;\n\t}\n\n\t// Setup session manager\n\tconst sessionManager = new SessionManager(parsed.continue && !parsed.resume, parsed.session);\n\n\t// Disable session saving if --no-session flag is set\n\tif (parsed.noSession) {\n\t\tsessionManager.disable();\n\t}\n\n\t// Handle --resume flag: show session selector\n\tif (parsed.resume) {\n\t\tconst selectedSession = await selectSession(sessionManager);\n\t\tif (!selectedSession) {\n\t\t\tconsole.log(chalk.dim(\"No session selected\"));\n\t\t\treturn;\n\t\t}\n\t\t// Set the selected session as the active session\n\t\tsessionManager.setSessionFile(selectedSession);\n\t}\n\n\t// Determine provider and model\n\tconst provider = (parsed.provider || \"anthropic\") as any;\n\tconst modelId = parsed.model || \"claude-sonnet-4-5\";\n\n\t// Helper function to get API key for a provider\n\tconst getApiKeyForProvider = (providerName: string): string | undefined => {\n\t\t// Check if API key was provided via command line\n\t\tif (parsed.apiKey) {\n\t\t\treturn parsed.apiKey;\n\t\t}\n\n\t\tconst envVars = envApiKeyMap[providerName as KnownProvider];\n\n\t\t// Check each environment variable in priority order\n\t\tfor (const envVar of envVars) {\n\t\t\tconst key = process.env[envVar];\n\t\t\tif (key) {\n\t\t\t\treturn key;\n\t\t\t}\n\t\t}\n\n\t\treturn undefined;\n\t};\n\n\t// Get initial API key\n\tconst initialApiKey = getApiKeyForProvider(provider);\n\tif (!initialApiKey) {\n\t\tconst envVars = envApiKeyMap[provider as KnownProvider];\n\t\tconst envVarList = envVars.join(\" or \");\n\t\tconsole.error(chalk.red(`Error: No API key found for provider \"${provider}\"`));\n\t\tconsole.error(chalk.dim(`Set ${envVarList} environment variable or use --api-key flag`));\n\t\tprocess.exit(1);\n\t}\n\n\t// Create agent\n\tconst model = getModel(provider, modelId);\n\tconst systemPrompt = buildSystemPrompt(parsed.systemPrompt);\n\n\tconst agent = new Agent({\n\t\tinitialState: {\n\t\t\tsystemPrompt,\n\t\t\tmodel,\n\t\t\tthinkingLevel: \"off\",\n\t\t\ttools: codingTools,\n\t\t},\n\t\ttransport: new ProviderTransport({\n\t\t\t// Dynamic API key lookup based on current model's provider\n\t\t\tgetApiKey: async () => {\n\t\t\t\tconst currentProvider = agent.state.model.provider;\n\t\t\t\tconst key = getApiKeyForProvider(currentProvider);\n\t\t\t\tif (!key) {\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t`No API key found for provider \"${currentProvider}\". Please set the appropriate environment variable.`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\treturn key;\n\t\t\t},\n\t\t}),\n\t});\n\n\t// Determine mode early to know if we should print messages\n\tconst isInteractive = parsed.messages.length === 0;\n\tconst mode = parsed.mode || \"text\";\n\tconst shouldPrintMessages = isInteractive || mode === \"text\";\n\n\t// Load previous messages if continuing or resuming\n\tif (parsed.continue || parsed.resume) {\n\t\tconst messages = sessionManager.loadMessages();\n\t\tif (messages.length > 0) {\n\t\t\tif (shouldPrintMessages) {\n\t\t\t\tconsole.log(chalk.dim(`Loaded ${messages.length} messages from previous session`));\n\t\t\t}\n\t\t\tagent.replaceMessages(messages);\n\t\t}\n\n\t\t// Load and restore model\n\t\tconst savedModel = sessionManager.loadModel();\n\t\tif (savedModel) {\n\t\t\t// Parse provider/modelId from saved model string (format: \"provider/modelId\")\n\t\t\tconst [savedProvider, savedModelId] = savedModel.split(\"/\");\n\t\t\tif (savedProvider && savedModelId) {\n\t\t\t\ttry {\n\t\t\t\t\tconst restoredModel = getModel(savedProvider as any, savedModelId);\n\t\t\t\t\tagent.setModel(restoredModel);\n\t\t\t\t\tif (shouldPrintMessages) {\n\t\t\t\t\t\tconsole.log(chalk.dim(`Restored model: ${savedModel}`));\n\t\t\t\t\t}\n\t\t\t\t} catch (error: any) {\n\t\t\t\t\tif (shouldPrintMessages) {\n\t\t\t\t\t\tconsole.error(chalk.yellow(`Warning: Could not restore model ${savedModel}: ${error.message}`));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Load and restore thinking level\n\t\tconst thinkingLevel = sessionManager.loadThinkingLevel() as ThinkingLevel;\n\t\tif (thinkingLevel) {\n\t\t\tagent.setThinkingLevel(thinkingLevel);\n\t\t\tif (shouldPrintMessages) {\n\t\t\t\tconsole.log(chalk.dim(`Restored thinking level: ${thinkingLevel}`));\n\t\t\t}\n\t\t}\n\t}\n\n\t// Note: Session will be started lazily after first user+assistant message exchange\n\t// (unless continuing/resuming, in which case it's already initialized)\n\n\t// Log loaded context files (they're already in the system prompt)\n\tif (shouldPrintMessages && !parsed.continue && !parsed.resume) {\n\t\tconst contextFiles = loadProjectContextFiles();\n\t\tif (contextFiles.length > 0) {\n\t\t\tconsole.log(chalk.dim(\"Loaded project context from:\"));\n\t\t\tfor (const { path: filePath } of contextFiles) {\n\t\t\t\tconsole.log(chalk.dim(` - ${filePath}`));\n\t\t\t}\n\t\t}\n\t}\n\n\t// Subscribe to agent events to save messages\n\tagent.subscribe((event) => {\n\t\t// Save messages on completion\n\t\tif (event.type === \"message_end\") {\n\t\t\tsessionManager.saveMessage(event.message);\n\n\t\t\t// Check if we should initialize session now (after first user+assistant exchange)\n\t\t\tif (sessionManager.shouldInitializeSession(agent.state.messages)) {\n\t\t\t\tsessionManager.startSession(agent.state);\n\t\t\t}\n\t\t}\n\t});\n\n\t// Route to appropriate mode\n\tif (mode === \"rpc\") {\n\t\t// RPC mode - headless operation\n\t\tawait runRpcMode(agent, sessionManager);\n\t} else if (isInteractive) {\n\t\t// No messages and not RPC - use TUI\n\t\tawait runInteractiveMode(agent, sessionManager, VERSION);\n\t} else {\n\t\t// CLI mode with messages\n\t\tawait runSingleShotMode(agent, sessionManager, parsed.messages, mode);\n\t}\n}\n"]}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import type { AgentState } from "@mariozechner/pi-agent";
|
|
2
|
+
export interface SessionHeader {
|
|
3
|
+
type: "session";
|
|
4
|
+
id: string;
|
|
5
|
+
timestamp: string;
|
|
6
|
+
cwd: string;
|
|
7
|
+
model: string;
|
|
8
|
+
thinkingLevel: string;
|
|
9
|
+
}
|
|
10
|
+
export interface SessionMessageEntry {
|
|
11
|
+
type: "message";
|
|
12
|
+
timestamp: string;
|
|
13
|
+
message: any;
|
|
14
|
+
}
|
|
15
|
+
export interface ThinkingLevelChangeEntry {
|
|
16
|
+
type: "thinking_level_change";
|
|
17
|
+
timestamp: string;
|
|
18
|
+
thinkingLevel: string;
|
|
19
|
+
}
|
|
20
|
+
export interface ModelChangeEntry {
|
|
21
|
+
type: "model_change";
|
|
22
|
+
timestamp: string;
|
|
23
|
+
model: string;
|
|
24
|
+
}
|
|
25
|
+
export declare class SessionManager {
|
|
26
|
+
private sessionId;
|
|
27
|
+
private sessionFile;
|
|
28
|
+
private sessionDir;
|
|
29
|
+
private enabled;
|
|
30
|
+
private sessionInitialized;
|
|
31
|
+
private pendingMessages;
|
|
32
|
+
constructor(continueSession?: boolean, customSessionPath?: string);
|
|
33
|
+
/** Disable session saving (for --no-session mode) */
|
|
34
|
+
disable(): void;
|
|
35
|
+
private getSessionDirectory;
|
|
36
|
+
private initNewSession;
|
|
37
|
+
private findMostRecentlyModifiedSession;
|
|
38
|
+
private loadSessionId;
|
|
39
|
+
startSession(state: AgentState): void;
|
|
40
|
+
saveMessage(message: any): void;
|
|
41
|
+
saveThinkingLevelChange(thinkingLevel: string): void;
|
|
42
|
+
saveModelChange(model: string): void;
|
|
43
|
+
loadMessages(): any[];
|
|
44
|
+
loadThinkingLevel(): string;
|
|
45
|
+
loadModel(): string | null;
|
|
46
|
+
getSessionId(): string;
|
|
47
|
+
getSessionFile(): string;
|
|
48
|
+
/**
|
|
49
|
+
* Load all sessions for the current directory with metadata
|
|
50
|
+
*/
|
|
51
|
+
loadAllSessions(): Array<{
|
|
52
|
+
path: string;
|
|
53
|
+
id: string;
|
|
54
|
+
created: Date;
|
|
55
|
+
modified: Date;
|
|
56
|
+
messageCount: number;
|
|
57
|
+
firstMessage: string;
|
|
58
|
+
allMessagesText: string;
|
|
59
|
+
}>;
|
|
60
|
+
/**
|
|
61
|
+
* Set the session file to an existing session
|
|
62
|
+
*/
|
|
63
|
+
setSessionFile(path: string): void;
|
|
64
|
+
/**
|
|
65
|
+
* Check if we should initialize the session based on message history.
|
|
66
|
+
* Session is initialized when we have at least 1 user message and 1 assistant message.
|
|
67
|
+
*/
|
|
68
|
+
shouldInitializeSession(messages: any[]): boolean;
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=session-manager.d.ts.map
|