@mariozechner/pi-coding-agent 0.25.4 → 0.26.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +18 -0
- package/README.md +47 -5
- package/dist/cli/file-processor.d.ts.map +1 -1
- package/dist/cli/file-processor.js +1 -1
- package/dist/cli/file-processor.js.map +1 -1
- package/dist/cli/session-picker.d.ts +2 -2
- package/dist/cli/session-picker.d.ts.map +1 -1
- package/dist/cli/session-picker.js +2 -2
- package/dist/cli/session-picker.js.map +1 -1
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +1 -13
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/custom-tools/loader.d.ts +3 -2
- package/dist/core/custom-tools/loader.d.ts.map +1 -1
- package/dist/core/custom-tools/loader.js +5 -4
- package/dist/core/custom-tools/loader.js.map +1 -1
- package/dist/core/hooks/loader.d.ts +2 -5
- package/dist/core/hooks/loader.d.ts.map +1 -1
- package/dist/core/hooks/loader.js +4 -7
- package/dist/core/hooks/loader.js.map +1 -1
- package/dist/core/model-config.d.ts +5 -4
- package/dist/core/model-config.d.ts.map +1 -1
- package/dist/core/model-config.js +12 -19
- package/dist/core/model-config.js.map +1 -1
- package/dist/core/sdk.d.ts +211 -0
- package/dist/core/sdk.d.ts.map +1 -0
- package/dist/core/sdk.js +466 -0
- package/dist/core/sdk.js.map +1 -0
- package/dist/core/session-manager.d.ts +31 -91
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +187 -352
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/settings-manager.d.ts +12 -2
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +101 -37
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/skills.d.ts +7 -1
- package/dist/core/skills.d.ts.map +1 -1
- package/dist/core/skills.js +7 -5
- package/dist/core/skills.js.map +1 -1
- package/dist/core/slash-commands.d.ts +9 -3
- package/dist/core/slash-commands.d.ts.map +1 -1
- package/dist/core/slash-commands.js +10 -7
- package/dist/core/slash-commands.js.map +1 -1
- package/dist/core/system-prompt.d.ts +24 -2
- package/dist/core/system-prompt.d.ts.map +1 -1
- package/dist/core/system-prompt.js +18 -16
- package/dist/core/system-prompt.js.map +1 -1
- package/dist/core/tools/bash.d.ts +6 -1
- package/dist/core/tools/bash.d.ts.map +1 -1
- package/dist/core/tools/bash.js +149 -144
- package/dist/core/tools/bash.js.map +1 -1
- package/dist/core/tools/edit.d.ts +7 -1
- package/dist/core/tools/edit.d.ts.map +1 -1
- package/dist/core/tools/edit.js +105 -102
- package/dist/core/tools/edit.js.map +1 -1
- package/dist/core/tools/find.d.ts +7 -1
- package/dist/core/tools/find.d.ts.map +1 -1
- package/dist/core/tools/find.js +128 -124
- package/dist/core/tools/find.js.map +1 -1
- package/dist/core/tools/grep.d.ts +11 -1
- package/dist/core/tools/grep.d.ts.map +1 -1
- package/dist/core/tools/grep.js +198 -194
- package/dist/core/tools/grep.js.map +1 -1
- package/dist/core/tools/index.d.ts +31 -29
- package/dist/core/tools/index.d.ts.map +1 -1
- package/dist/core/tools/index.js +44 -16
- package/dist/core/tools/index.js.map +1 -1
- package/dist/core/tools/ls.d.ts +6 -1
- package/dist/core/tools/ls.d.ts.map +1 -1
- package/dist/core/tools/ls.js +90 -86
- package/dist/core/tools/ls.js.map +1 -1
- package/dist/core/tools/path-utils.d.ts +6 -1
- package/dist/core/tools/path-utils.d.ts.map +1 -1
- package/dist/core/tools/path-utils.js +17 -5
- package/dist/core/tools/path-utils.js.map +1 -1
- package/dist/core/tools/read.d.ts +7 -1
- package/dist/core/tools/read.d.ts.map +1 -1
- package/dist/core/tools/read.js +118 -115
- package/dist/core/tools/read.js.map +1 -1
- package/dist/core/tools/write.d.ts +6 -1
- package/dist/core/tools/write.d.ts.map +1 -1
- package/dist/core/tools/write.js +63 -59
- package/dist/core/tools/write.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +14 -0
- package/dist/index.js.map +1 -1
- package/dist/main.d.ts +4 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +142 -312
- package/dist/main.js.map +1 -1
- package/dist/modes/interactive/components/session-selector.d.ts +3 -12
- package/dist/modes/interactive/components/session-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/session-selector.js +1 -3
- package/dist/modes/interactive/components/session-selector.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +3 -2
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/utils/shell.d.ts.map +1 -1
- package/dist/utils/shell.js +1 -1
- package/dist/utils/shell.js.map +1 -1
- package/docs/sdk.md +864 -0
- package/examples/README.md +29 -0
- package/examples/sdk/01-minimal.ts +22 -0
- package/examples/sdk/02-custom-model.ts +36 -0
- package/examples/sdk/03-custom-prompt.ts +44 -0
- package/examples/sdk/04-skills.ts +44 -0
- package/examples/sdk/05-tools.ts +93 -0
- package/examples/sdk/06-hooks.ts +61 -0
- package/examples/sdk/07-context-files.ts +36 -0
- package/examples/sdk/08-slash-commands.ts +37 -0
- package/examples/sdk/09-api-keys-and-oauth.ts +45 -0
- package/examples/sdk/10-settings.ts +38 -0
- package/examples/sdk/11-sessions.ts +46 -0
- package/examples/sdk/12-full-control.ts +99 -0
- package/examples/sdk/README.md +138 -0
- package/package.json +4 -4
package/dist/main.js
CHANGED
|
@@ -1,58 +1,27 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Main entry point for the coding agent
|
|
2
|
+
* Main entry point for the coding agent CLI.
|
|
3
|
+
*
|
|
4
|
+
* This file handles CLI argument parsing and translates them into
|
|
5
|
+
* createAgentSession() options. The SDK does the heavy lifting.
|
|
3
6
|
*/
|
|
4
|
-
import {
|
|
5
|
-
import { setOAuthStorage, supportsXhigh } from "@mariozechner/pi-ai";
|
|
7
|
+
import { supportsXhigh } from "@mariozechner/pi-ai";
|
|
6
8
|
import chalk from "chalk";
|
|
7
|
-
import { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
8
|
-
import { dirname } from "path";
|
|
9
9
|
import { parseArgs, printHelp } from "./cli/args.js";
|
|
10
10
|
import { processFileArguments } from "./cli/file-processor.js";
|
|
11
11
|
import { listModels } from "./cli/list-models.js";
|
|
12
12
|
import { selectSession } from "./cli/session-picker.js";
|
|
13
|
-
import { getModelsPath,
|
|
14
|
-
import { AgentSession } from "./core/agent-session.js";
|
|
15
|
-
import { discoverAndLoadCustomTools } from "./core/custom-tools/index.js";
|
|
13
|
+
import { getModelsPath, VERSION } from "./config.js";
|
|
16
14
|
import { exportFromFile } from "./core/export-html.js";
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
19
|
-
import {
|
|
20
|
-
import { resolveModelScope, restoreModelFromSession } from "./core/model-resolver.js";
|
|
15
|
+
import { findModel } from "./core/model-config.js";
|
|
16
|
+
import { resolveModelScope } from "./core/model-resolver.js";
|
|
17
|
+
import { configureOAuthStorage, createAgentSession } from "./core/sdk.js";
|
|
21
18
|
import { SessionManager } from "./core/session-manager.js";
|
|
22
19
|
import { SettingsManager } from "./core/settings-manager.js";
|
|
23
|
-
import {
|
|
24
|
-
import { buildSystemPrompt } from "./core/system-prompt.js";
|
|
25
|
-
import { allTools, codingTools } from "./core/tools/index.js";
|
|
20
|
+
import { allTools } from "./core/tools/index.js";
|
|
26
21
|
import { InteractiveMode, runPrintMode, runRpcMode } from "./modes/index.js";
|
|
27
22
|
import { initTheme, stopThemeWatcher } from "./modes/interactive/theme/theme.js";
|
|
28
23
|
import { getChangelogPath, getNewEntries, parseChangelog } from "./utils/changelog.js";
|
|
29
24
|
import { ensureTool } from "./utils/tools-manager.js";
|
|
30
|
-
/** Configure OAuth storage to use the coding-agent's configurable path */
|
|
31
|
-
function configureOAuthStorage() {
|
|
32
|
-
const oauthPath = getOAuthPath();
|
|
33
|
-
setOAuthStorage({
|
|
34
|
-
load: () => {
|
|
35
|
-
if (!existsSync(oauthPath)) {
|
|
36
|
-
return {};
|
|
37
|
-
}
|
|
38
|
-
try {
|
|
39
|
-
return JSON.parse(readFileSync(oauthPath, "utf-8"));
|
|
40
|
-
}
|
|
41
|
-
catch {
|
|
42
|
-
return {};
|
|
43
|
-
}
|
|
44
|
-
},
|
|
45
|
-
save: (storage) => {
|
|
46
|
-
const dir = dirname(oauthPath);
|
|
47
|
-
if (!existsSync(dir)) {
|
|
48
|
-
mkdirSync(dir, { recursive: true, mode: 0o700 });
|
|
49
|
-
}
|
|
50
|
-
writeFileSync(oauthPath, JSON.stringify(storage, null, 2), "utf-8");
|
|
51
|
-
chmodSync(oauthPath, 0o600);
|
|
52
|
-
},
|
|
53
|
-
});
|
|
54
|
-
}
|
|
55
|
-
/** Check npm registry for new version (non-blocking) */
|
|
56
25
|
async function checkForNewVersion(currentVersion) {
|
|
57
26
|
try {
|
|
58
27
|
const response = await fetch("https://registry.npmjs.org/@mariozechner/pi-coding-agent/latest");
|
|
@@ -66,28 +35,21 @@ async function checkForNewVersion(currentVersion) {
|
|
|
66
35
|
return null;
|
|
67
36
|
}
|
|
68
37
|
catch {
|
|
69
|
-
// Silently fail - don't disrupt the user experience
|
|
70
38
|
return null;
|
|
71
39
|
}
|
|
72
40
|
}
|
|
73
|
-
/** Run interactive mode with TUI */
|
|
74
41
|
async function runInteractiveMode(session, version, changelogMarkdown, modelFallbackMessage, versionCheckPromise, initialMessages, customTools, setToolUIContext, initialMessage, initialAttachments, fdPath = null) {
|
|
75
42
|
const mode = new InteractiveMode(session, version, changelogMarkdown, customTools, setToolUIContext, fdPath);
|
|
76
|
-
// Initialize TUI (subscribes to agent events internally)
|
|
77
43
|
await mode.init();
|
|
78
|
-
// Handle version check result when it completes (don't block)
|
|
79
44
|
versionCheckPromise.then((newVersion) => {
|
|
80
45
|
if (newVersion) {
|
|
81
46
|
mode.showNewVersionNotification(newVersion);
|
|
82
47
|
}
|
|
83
48
|
});
|
|
84
|
-
// Render any existing messages (from --continue mode)
|
|
85
49
|
mode.renderInitialMessages(session.state);
|
|
86
|
-
// Show model fallback warning at the end of the chat if applicable
|
|
87
50
|
if (modelFallbackMessage) {
|
|
88
51
|
mode.showWarning(modelFallbackMessage);
|
|
89
52
|
}
|
|
90
|
-
// Process initial message with attachments if provided (from @file args)
|
|
91
53
|
if (initialMessage) {
|
|
92
54
|
try {
|
|
93
55
|
await session.prompt(initialMessage, { attachments: initialAttachments });
|
|
@@ -97,7 +59,6 @@ async function runInteractiveMode(session, version, changelogMarkdown, modelFall
|
|
|
97
59
|
mode.showError(errorMessage);
|
|
98
60
|
}
|
|
99
61
|
}
|
|
100
|
-
// Process remaining initial messages if provided (from CLI args)
|
|
101
62
|
for (const message of initialMessages) {
|
|
102
63
|
try {
|
|
103
64
|
await session.prompt(message);
|
|
@@ -107,10 +68,8 @@ async function runInteractiveMode(session, version, changelogMarkdown, modelFall
|
|
|
107
68
|
mode.showError(errorMessage);
|
|
108
69
|
}
|
|
109
70
|
}
|
|
110
|
-
// Interactive loop
|
|
111
71
|
while (true) {
|
|
112
72
|
const userInput = await mode.getUserInput();
|
|
113
|
-
// Process the message
|
|
114
73
|
try {
|
|
115
74
|
await session.prompt(userInput);
|
|
116
75
|
}
|
|
@@ -120,17 +79,15 @@ async function runInteractiveMode(session, version, changelogMarkdown, modelFall
|
|
|
120
79
|
}
|
|
121
80
|
}
|
|
122
81
|
}
|
|
123
|
-
/** Prepare initial message from @file arguments */
|
|
124
82
|
async function prepareInitialMessage(parsed) {
|
|
125
83
|
if (parsed.fileArgs.length === 0) {
|
|
126
84
|
return {};
|
|
127
85
|
}
|
|
128
86
|
const { textContent, imageAttachments } = await processFileArguments(parsed.fileArgs);
|
|
129
|
-
// Combine file content with first plain text message (if any)
|
|
130
87
|
let initialMessage;
|
|
131
88
|
if (parsed.messages.length > 0) {
|
|
132
89
|
initialMessage = textContent + parsed.messages[0];
|
|
133
|
-
parsed.messages.shift();
|
|
90
|
+
parsed.messages.shift();
|
|
134
91
|
}
|
|
135
92
|
else {
|
|
136
93
|
initialMessage = textContent;
|
|
@@ -140,9 +97,107 @@ async function prepareInitialMessage(parsed) {
|
|
|
140
97
|
initialAttachments: imageAttachments.length > 0 ? imageAttachments : undefined,
|
|
141
98
|
};
|
|
142
99
|
}
|
|
100
|
+
function getChangelogForDisplay(parsed, settingsManager) {
|
|
101
|
+
if (parsed.continue || parsed.resume) {
|
|
102
|
+
return null;
|
|
103
|
+
}
|
|
104
|
+
const lastVersion = settingsManager.getLastChangelogVersion();
|
|
105
|
+
const changelogPath = getChangelogPath();
|
|
106
|
+
const entries = parseChangelog(changelogPath);
|
|
107
|
+
if (!lastVersion) {
|
|
108
|
+
if (entries.length > 0) {
|
|
109
|
+
settingsManager.setLastChangelogVersion(VERSION);
|
|
110
|
+
return entries.map((e) => e.content).join("\n\n");
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
const newEntries = getNewEntries(entries, lastVersion);
|
|
115
|
+
if (newEntries.length > 0) {
|
|
116
|
+
settingsManager.setLastChangelogVersion(VERSION);
|
|
117
|
+
return newEntries.map((e) => e.content).join("\n\n");
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
122
|
+
function createSessionManager(parsed, cwd) {
|
|
123
|
+
if (parsed.noSession) {
|
|
124
|
+
return SessionManager.inMemory();
|
|
125
|
+
}
|
|
126
|
+
if (parsed.session) {
|
|
127
|
+
return SessionManager.open(parsed.session);
|
|
128
|
+
}
|
|
129
|
+
if (parsed.continue) {
|
|
130
|
+
return SessionManager.continueRecent(cwd);
|
|
131
|
+
}
|
|
132
|
+
// --resume is handled separately (needs picker UI)
|
|
133
|
+
// Default case (new session) returns null, SDK will create one
|
|
134
|
+
return null;
|
|
135
|
+
}
|
|
136
|
+
function buildSessionOptions(parsed, scopedModels, sessionManager) {
|
|
137
|
+
const options = {};
|
|
138
|
+
if (sessionManager) {
|
|
139
|
+
options.sessionManager = sessionManager;
|
|
140
|
+
}
|
|
141
|
+
// Model from CLI
|
|
142
|
+
if (parsed.provider && parsed.model) {
|
|
143
|
+
const { model, error } = findModel(parsed.provider, parsed.model);
|
|
144
|
+
if (error) {
|
|
145
|
+
console.error(chalk.red(error));
|
|
146
|
+
process.exit(1);
|
|
147
|
+
}
|
|
148
|
+
if (!model) {
|
|
149
|
+
console.error(chalk.red(`Model ${parsed.provider}/${parsed.model} not found`));
|
|
150
|
+
process.exit(1);
|
|
151
|
+
}
|
|
152
|
+
options.model = model;
|
|
153
|
+
}
|
|
154
|
+
else if (scopedModels.length > 0 && !parsed.continue && !parsed.resume) {
|
|
155
|
+
options.model = scopedModels[0].model;
|
|
156
|
+
}
|
|
157
|
+
// Thinking level
|
|
158
|
+
if (parsed.thinking) {
|
|
159
|
+
options.thinkingLevel = parsed.thinking;
|
|
160
|
+
}
|
|
161
|
+
else if (scopedModels.length > 0 && !parsed.continue && !parsed.resume) {
|
|
162
|
+
options.thinkingLevel = scopedModels[0].thinkingLevel;
|
|
163
|
+
}
|
|
164
|
+
// Scoped models for Ctrl+P cycling
|
|
165
|
+
if (scopedModels.length > 0) {
|
|
166
|
+
options.scopedModels = scopedModels;
|
|
167
|
+
}
|
|
168
|
+
// API key from CLI
|
|
169
|
+
if (parsed.apiKey) {
|
|
170
|
+
options.getApiKey = async () => parsed.apiKey;
|
|
171
|
+
}
|
|
172
|
+
// System prompt
|
|
173
|
+
if (parsed.systemPrompt && parsed.appendSystemPrompt) {
|
|
174
|
+
options.systemPrompt = `${parsed.systemPrompt}\n\n${parsed.appendSystemPrompt}`;
|
|
175
|
+
}
|
|
176
|
+
else if (parsed.systemPrompt) {
|
|
177
|
+
options.systemPrompt = parsed.systemPrompt;
|
|
178
|
+
}
|
|
179
|
+
else if (parsed.appendSystemPrompt) {
|
|
180
|
+
options.systemPrompt = (defaultPrompt) => `${defaultPrompt}\n\n${parsed.appendSystemPrompt}`;
|
|
181
|
+
}
|
|
182
|
+
// Tools
|
|
183
|
+
if (parsed.tools) {
|
|
184
|
+
options.tools = parsed.tools.map((name) => allTools[name]);
|
|
185
|
+
}
|
|
186
|
+
// Skills
|
|
187
|
+
if (parsed.noSkills) {
|
|
188
|
+
options.skills = [];
|
|
189
|
+
}
|
|
190
|
+
// Additional hook paths from CLI
|
|
191
|
+
if (parsed.hooks && parsed.hooks.length > 0) {
|
|
192
|
+
options.additionalHookPaths = parsed.hooks;
|
|
193
|
+
}
|
|
194
|
+
// Additional custom tool paths from CLI
|
|
195
|
+
if (parsed.customTools && parsed.customTools.length > 0) {
|
|
196
|
+
options.additionalCustomToolPaths = parsed.customTools;
|
|
197
|
+
}
|
|
198
|
+
return options;
|
|
199
|
+
}
|
|
143
200
|
export async function main(args) {
|
|
144
|
-
// Configure OAuth storage to use the coding-agent's configurable path
|
|
145
|
-
// This must happen before any OAuth operations
|
|
146
201
|
configureOAuthStorage();
|
|
147
202
|
const parsed = parseArgs(args);
|
|
148
203
|
if (parsed.version) {
|
|
@@ -153,13 +208,11 @@ export async function main(args) {
|
|
|
153
208
|
printHelp();
|
|
154
209
|
return;
|
|
155
210
|
}
|
|
156
|
-
// Handle --list-models flag: list available models and exit
|
|
157
211
|
if (parsed.listModels !== undefined) {
|
|
158
212
|
const searchPattern = typeof parsed.listModels === "string" ? parsed.listModels : undefined;
|
|
159
213
|
await listModels(searchPattern);
|
|
160
214
|
return;
|
|
161
215
|
}
|
|
162
|
-
// Handle --export flag: convert session file to HTML and exit
|
|
163
216
|
if (parsed.export) {
|
|
164
217
|
try {
|
|
165
218
|
const outputPath = parsed.messages.length > 0 ? parsed.messages[0] : undefined;
|
|
@@ -173,215 +226,64 @@ export async function main(args) {
|
|
|
173
226
|
process.exit(1);
|
|
174
227
|
}
|
|
175
228
|
}
|
|
176
|
-
// Validate: RPC mode doesn't support @file arguments
|
|
177
229
|
if (parsed.mode === "rpc" && parsed.fileArgs.length > 0) {
|
|
178
230
|
console.error(chalk.red("Error: @file arguments are not supported in RPC mode"));
|
|
179
231
|
process.exit(1);
|
|
180
232
|
}
|
|
181
|
-
|
|
233
|
+
const cwd = process.cwd();
|
|
182
234
|
const { initialMessage, initialAttachments } = await prepareInitialMessage(parsed);
|
|
183
|
-
// Determine if we're in interactive mode (needed for theme watcher)
|
|
184
235
|
const isInteractive = !parsed.print && parsed.mode === undefined;
|
|
185
|
-
|
|
186
|
-
const settingsManager =
|
|
187
|
-
|
|
188
|
-
initTheme(themeName, isInteractive);
|
|
189
|
-
// Setup session manager
|
|
190
|
-
const sessionManager = new SessionManager(parsed.continue && !parsed.resume, parsed.session);
|
|
191
|
-
if (parsed.noSession) {
|
|
192
|
-
sessionManager.disable();
|
|
193
|
-
}
|
|
194
|
-
// Handle --resume flag: show session selector
|
|
195
|
-
if (parsed.resume) {
|
|
196
|
-
const selectedSession = await selectSession(sessionManager);
|
|
197
|
-
if (!selectedSession) {
|
|
198
|
-
console.log(chalk.dim("No session selected"));
|
|
199
|
-
return;
|
|
200
|
-
}
|
|
201
|
-
sessionManager.setSessionFile(selectedSession);
|
|
202
|
-
}
|
|
203
|
-
// Resolve model scope early if provided
|
|
236
|
+
const mode = parsed.mode || "text";
|
|
237
|
+
const settingsManager = SettingsManager.create(cwd);
|
|
238
|
+
initTheme(settingsManager.getTheme(), isInteractive);
|
|
204
239
|
let scopedModels = [];
|
|
205
240
|
if (parsed.models && parsed.models.length > 0) {
|
|
206
241
|
scopedModels = await resolveModelScope(parsed.models);
|
|
207
242
|
}
|
|
208
|
-
//
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
initialThinking = scopedModels[0].thinkingLevel;
|
|
217
|
-
}
|
|
218
|
-
else {
|
|
219
|
-
// Try saved thinking level
|
|
220
|
-
const savedThinking = settingsManager.getDefaultThinkingLevel();
|
|
221
|
-
if (savedThinking) {
|
|
222
|
-
initialThinking = savedThinking;
|
|
243
|
+
// Create session manager based on CLI flags
|
|
244
|
+
let sessionManager = createSessionManager(parsed, cwd);
|
|
245
|
+
// Handle --resume: show session picker
|
|
246
|
+
if (parsed.resume) {
|
|
247
|
+
const sessions = SessionManager.list(cwd);
|
|
248
|
+
if (sessions.length === 0) {
|
|
249
|
+
console.log(chalk.dim("No sessions found"));
|
|
250
|
+
return;
|
|
223
251
|
}
|
|
252
|
+
const selectedPath = await selectSession(sessions);
|
|
253
|
+
if (!selectedPath) {
|
|
254
|
+
console.log(chalk.dim("No session selected"));
|
|
255
|
+
return;
|
|
256
|
+
}
|
|
257
|
+
sessionManager = SessionManager.open(selectedPath);
|
|
224
258
|
}
|
|
225
|
-
|
|
226
|
-
|
|
259
|
+
const sessionOptions = buildSessionOptions(parsed, scopedModels, sessionManager);
|
|
260
|
+
const { session, customToolsResult, modelFallbackMessage } = await createAgentSession(sessionOptions);
|
|
261
|
+
if (!isInteractive && !session.model) {
|
|
227
262
|
console.error(chalk.red("No models available."));
|
|
228
263
|
console.error(chalk.yellow("\nSet an API key environment variable:"));
|
|
229
264
|
console.error(" ANTHROPIC_API_KEY, OPENAI_API_KEY, GEMINI_API_KEY, etc.");
|
|
230
265
|
console.error(chalk.yellow(`\nOr create ${getModelsPath()}`));
|
|
231
266
|
process.exit(1);
|
|
232
267
|
}
|
|
233
|
-
//
|
|
234
|
-
if (
|
|
235
|
-
|
|
236
|
-
if (!
|
|
237
|
-
|
|
238
|
-
process.exit(1);
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
// Build system prompt
|
|
242
|
-
const skillsSettings = settingsManager.getSkillsSettings();
|
|
243
|
-
if (parsed.noSkills) {
|
|
244
|
-
skillsSettings.enabled = false;
|
|
245
|
-
}
|
|
246
|
-
if (parsed.skills && parsed.skills.length > 0) {
|
|
247
|
-
skillsSettings.includeSkills = parsed.skills;
|
|
248
|
-
}
|
|
249
|
-
const systemPrompt = buildSystemPrompt({
|
|
250
|
-
customPrompt: parsed.systemPrompt,
|
|
251
|
-
selectedTools: parsed.tools,
|
|
252
|
-
appendSystemPrompt: parsed.appendSystemPrompt,
|
|
253
|
-
skillsSettings,
|
|
254
|
-
});
|
|
255
|
-
// Handle session restoration
|
|
256
|
-
let modelFallbackMessage = null;
|
|
257
|
-
if (parsed.continue || parsed.resume || parsed.session) {
|
|
258
|
-
const savedModel = sessionManager.loadModel();
|
|
259
|
-
if (savedModel) {
|
|
260
|
-
const result = await restoreModelFromSession(savedModel.provider, savedModel.modelId, initialModel, shouldPrintMessages);
|
|
261
|
-
if (result.model) {
|
|
262
|
-
initialModel = result.model;
|
|
263
|
-
}
|
|
264
|
-
modelFallbackMessage = result.fallbackMessage;
|
|
265
|
-
}
|
|
266
|
-
// Load and restore thinking level
|
|
267
|
-
const thinkingLevel = sessionManager.loadThinkingLevel();
|
|
268
|
-
if (thinkingLevel) {
|
|
269
|
-
initialThinking = thinkingLevel;
|
|
270
|
-
if (shouldPrintMessages) {
|
|
271
|
-
console.log(chalk.dim(`Restored thinking level: ${thinkingLevel}`));
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
// CLI --thinking flag takes highest priority
|
|
276
|
-
if (parsed.thinking) {
|
|
277
|
-
initialThinking = parsed.thinking;
|
|
278
|
-
}
|
|
279
|
-
// Clamp thinking level to model capabilities
|
|
280
|
-
if (initialModel) {
|
|
281
|
-
if (!initialModel.reasoning) {
|
|
282
|
-
initialThinking = "off";
|
|
268
|
+
// Clamp thinking level to model capabilities (for CLI override case)
|
|
269
|
+
if (session.model && parsed.thinking) {
|
|
270
|
+
let effectiveThinking = parsed.thinking;
|
|
271
|
+
if (!session.model.reasoning) {
|
|
272
|
+
effectiveThinking = "off";
|
|
283
273
|
}
|
|
284
|
-
else if (
|
|
285
|
-
|
|
274
|
+
else if (effectiveThinking === "xhigh" && !supportsXhigh(session.model)) {
|
|
275
|
+
effectiveThinking = "high";
|
|
286
276
|
}
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
let selectedTools = parsed.tools ? parsed.tools.map((name) => allTools[name]) : codingTools;
|
|
290
|
-
// Discover and load hooks from:
|
|
291
|
-
// 1. ~/.pi/agent/hooks/*.ts (global)
|
|
292
|
-
// 2. cwd/.pi/hooks/*.ts (project-local)
|
|
293
|
-
// 3. Explicit paths in settings.json
|
|
294
|
-
// 4. CLI --hook flags
|
|
295
|
-
let hookRunner = null;
|
|
296
|
-
const cwd = process.cwd();
|
|
297
|
-
const configuredHookPaths = [...settingsManager.getHookPaths(), ...(parsed.hooks ?? [])];
|
|
298
|
-
const { hooks, errors } = await discoverAndLoadHooks(configuredHookPaths, cwd);
|
|
299
|
-
// Report hook loading errors
|
|
300
|
-
for (const { path, error } of errors) {
|
|
301
|
-
console.error(chalk.red(`Failed to load hook "${path}": ${error}`));
|
|
302
|
-
}
|
|
303
|
-
if (hooks.length > 0) {
|
|
304
|
-
const timeout = settingsManager.getHookTimeout();
|
|
305
|
-
hookRunner = new HookRunner(hooks, cwd, timeout);
|
|
306
|
-
}
|
|
307
|
-
// Discover and load custom tools from:
|
|
308
|
-
// 1. ~/.pi/agent/tools/*.ts (global)
|
|
309
|
-
// 2. cwd/.pi/tools/*.ts (project-local)
|
|
310
|
-
// 3. Explicit paths in settings.json
|
|
311
|
-
// 4. CLI --tool flags
|
|
312
|
-
const configuredToolPaths = [...settingsManager.getCustomToolPaths(), ...(parsed.customTools ?? [])];
|
|
313
|
-
const builtInToolNames = Object.keys(allTools);
|
|
314
|
-
const { tools: loadedCustomTools, errors: toolErrors, setUIContext: setToolUIContext, } = await discoverAndLoadCustomTools(configuredToolPaths, cwd, builtInToolNames);
|
|
315
|
-
// Report custom tool loading errors
|
|
316
|
-
for (const { path, error } of toolErrors) {
|
|
317
|
-
console.error(chalk.red(`Failed to load custom tool "${path}": ${error}`));
|
|
318
|
-
}
|
|
319
|
-
// Add custom tools to selected tools
|
|
320
|
-
if (loadedCustomTools.length > 0) {
|
|
321
|
-
const customToolInstances = loadedCustomTools.map((lt) => lt.tool);
|
|
322
|
-
selectedTools = [...selectedTools, ...customToolInstances];
|
|
323
|
-
}
|
|
324
|
-
// Wrap tools with hook callbacks (built-in and custom)
|
|
325
|
-
if (hookRunner) {
|
|
326
|
-
selectedTools = wrapToolsWithHooks(selectedTools, hookRunner);
|
|
327
|
-
}
|
|
328
|
-
// Create agent
|
|
329
|
-
const agent = new Agent({
|
|
330
|
-
initialState: {
|
|
331
|
-
systemPrompt,
|
|
332
|
-
model: initialModel, // Can be null in interactive mode
|
|
333
|
-
thinkingLevel: initialThinking,
|
|
334
|
-
tools: selectedTools,
|
|
335
|
-
},
|
|
336
|
-
messageTransformer,
|
|
337
|
-
queueMode: settingsManager.getQueueMode(),
|
|
338
|
-
transport: new ProviderTransport({
|
|
339
|
-
getApiKey: async () => {
|
|
340
|
-
const currentModel = agent.state.model;
|
|
341
|
-
if (!currentModel) {
|
|
342
|
-
throw new Error("No model selected");
|
|
343
|
-
}
|
|
344
|
-
if (parsed.apiKey) {
|
|
345
|
-
return parsed.apiKey;
|
|
346
|
-
}
|
|
347
|
-
const key = await getApiKeyForModel(currentModel);
|
|
348
|
-
if (!key) {
|
|
349
|
-
throw new Error(`No API key found for provider "${currentModel.provider}". Please set the appropriate environment variable or update ${getModelsPath()}`);
|
|
350
|
-
}
|
|
351
|
-
return key;
|
|
352
|
-
},
|
|
353
|
-
}),
|
|
354
|
-
});
|
|
355
|
-
// Load previous messages if continuing, resuming, or using --session
|
|
356
|
-
if (parsed.continue || parsed.resume || parsed.session) {
|
|
357
|
-
const messages = sessionManager.loadMessages();
|
|
358
|
-
if (messages.length > 0) {
|
|
359
|
-
agent.replaceMessages(messages);
|
|
277
|
+
if (effectiveThinking !== session.thinkingLevel) {
|
|
278
|
+
session.setThinkingLevel(effectiveThinking);
|
|
360
279
|
}
|
|
361
280
|
}
|
|
362
|
-
// Load file commands for slash command expansion
|
|
363
|
-
const fileCommands = loadSlashCommands();
|
|
364
|
-
// Create session
|
|
365
|
-
const session = new AgentSession({
|
|
366
|
-
agent,
|
|
367
|
-
sessionManager,
|
|
368
|
-
settingsManager,
|
|
369
|
-
scopedModels,
|
|
370
|
-
fileCommands,
|
|
371
|
-
hookRunner,
|
|
372
|
-
customTools: loadedCustomTools,
|
|
373
|
-
skillsSettings,
|
|
374
|
-
});
|
|
375
|
-
// Route to appropriate mode
|
|
376
281
|
if (mode === "rpc") {
|
|
377
282
|
await runRpcMode(session);
|
|
378
283
|
}
|
|
379
284
|
else if (isInteractive) {
|
|
380
|
-
// Check for new version in the background
|
|
381
285
|
const versionCheckPromise = checkForNewVersion(VERSION).catch(() => null);
|
|
382
|
-
// Check if we should show changelog
|
|
383
286
|
const changelogMarkdown = getChangelogForDisplay(parsed, settingsManager);
|
|
384
|
-
// Show model scope if provided
|
|
385
287
|
if (scopedModels.length > 0) {
|
|
386
288
|
const modelList = scopedModels
|
|
387
289
|
.map((sm) => {
|
|
@@ -391,88 +293,16 @@ export async function main(args) {
|
|
|
391
293
|
.join(", ");
|
|
392
294
|
console.log(chalk.dim(`Model scope: ${modelList} ${chalk.gray("(Ctrl+P to cycle)")}`));
|
|
393
295
|
}
|
|
394
|
-
// Ensure fd tool is available for file autocomplete
|
|
395
296
|
const fdPath = await ensureTool("fd");
|
|
396
|
-
await runInteractiveMode(session, VERSION, changelogMarkdown, modelFallbackMessage, versionCheckPromise, parsed.messages,
|
|
297
|
+
await runInteractiveMode(session, VERSION, changelogMarkdown, modelFallbackMessage, versionCheckPromise, parsed.messages, customToolsResult.tools, customToolsResult.setUIContext, initialMessage, initialAttachments, fdPath);
|
|
397
298
|
}
|
|
398
299
|
else {
|
|
399
|
-
// Non-interactive mode (--print flag or --mode flag)
|
|
400
300
|
await runPrintMode(session, mode, parsed.messages, initialMessage, initialAttachments);
|
|
401
|
-
// Clean up and exit (file watchers keep process alive)
|
|
402
301
|
stopThemeWatcher();
|
|
403
|
-
// Wait for stdout to fully flush before exiting
|
|
404
302
|
if (process.stdout.writableLength > 0) {
|
|
405
303
|
await new Promise((resolve) => process.stdout.once("drain", resolve));
|
|
406
304
|
}
|
|
407
305
|
process.exit(0);
|
|
408
306
|
}
|
|
409
307
|
}
|
|
410
|
-
/** Find initial model based on CLI args, scoped models, settings, or available models */
|
|
411
|
-
async function findInitialModelForSession(parsed, scopedModels, settingsManager) {
|
|
412
|
-
// 1. CLI args take priority
|
|
413
|
-
if (parsed.provider && parsed.model) {
|
|
414
|
-
const { model, error } = findModel(parsed.provider, parsed.model);
|
|
415
|
-
if (error) {
|
|
416
|
-
console.error(chalk.red(error));
|
|
417
|
-
process.exit(1);
|
|
418
|
-
}
|
|
419
|
-
if (!model) {
|
|
420
|
-
console.error(chalk.red(`Model ${parsed.provider}/${parsed.model} not found`));
|
|
421
|
-
process.exit(1);
|
|
422
|
-
}
|
|
423
|
-
return model;
|
|
424
|
-
}
|
|
425
|
-
// 2. Use first model from scoped models (skip if continuing/resuming)
|
|
426
|
-
if (scopedModels.length > 0 && !parsed.continue && !parsed.resume) {
|
|
427
|
-
return scopedModels[0].model;
|
|
428
|
-
}
|
|
429
|
-
// 3. Try saved default from settings
|
|
430
|
-
const defaultProvider = settingsManager.getDefaultProvider();
|
|
431
|
-
const defaultModelId = settingsManager.getDefaultModel();
|
|
432
|
-
if (defaultProvider && defaultModelId) {
|
|
433
|
-
const { model, error } = findModel(defaultProvider, defaultModelId);
|
|
434
|
-
if (error) {
|
|
435
|
-
console.error(chalk.red(error));
|
|
436
|
-
process.exit(1);
|
|
437
|
-
}
|
|
438
|
-
if (model) {
|
|
439
|
-
return model;
|
|
440
|
-
}
|
|
441
|
-
}
|
|
442
|
-
// 4. Try first available model with valid API key
|
|
443
|
-
const { models: availableModels, error } = await getAvailableModels();
|
|
444
|
-
if (error) {
|
|
445
|
-
console.error(chalk.red(error));
|
|
446
|
-
process.exit(1);
|
|
447
|
-
}
|
|
448
|
-
if (availableModels.length > 0) {
|
|
449
|
-
return availableModels[0];
|
|
450
|
-
}
|
|
451
|
-
return null;
|
|
452
|
-
}
|
|
453
|
-
/** Get changelog markdown to display (only for new sessions with updates) */
|
|
454
|
-
function getChangelogForDisplay(parsed, settingsManager) {
|
|
455
|
-
if (parsed.continue || parsed.resume) {
|
|
456
|
-
return null;
|
|
457
|
-
}
|
|
458
|
-
const lastVersion = settingsManager.getLastChangelogVersion();
|
|
459
|
-
const changelogPath = getChangelogPath();
|
|
460
|
-
const entries = parseChangelog(changelogPath);
|
|
461
|
-
if (!lastVersion) {
|
|
462
|
-
// First run - show all entries
|
|
463
|
-
if (entries.length > 0) {
|
|
464
|
-
settingsManager.setLastChangelogVersion(VERSION);
|
|
465
|
-
return entries.map((e) => e.content).join("\n\n");
|
|
466
|
-
}
|
|
467
|
-
}
|
|
468
|
-
else {
|
|
469
|
-
// Check for new entries since last version
|
|
470
|
-
const newEntries = getNewEntries(entries, lastVersion);
|
|
471
|
-
if (newEntries.length > 0) {
|
|
472
|
-
settingsManager.setLastChangelogVersion(VERSION);
|
|
473
|
-
return newEntries.map((e) => e.content).join("\n\n");
|
|
474
|
-
}
|
|
475
|
-
}
|
|
476
|
-
return null;
|
|
477
|
-
}
|
|
478
308
|
//# sourceMappingURL=main.js.map
|