free-pi-cli 0.3.0 → 0.4.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 +1 -1
- package/dist/{main-376chsck.js → main-nyekh9dy.js} +620 -35
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -44,7 +44,7 @@ function openBrowser(url, platform = process.platform, spawnImpl = spawn) {
|
|
|
44
44
|
// package.json
|
|
45
45
|
var package_default = {
|
|
46
46
|
name: "free-pi-cli",
|
|
47
|
-
version: "0.
|
|
47
|
+
version: "0.4.0",
|
|
48
48
|
description: "Free, ad-supported distribution of the pi coding agent. Inference paid for by ads.",
|
|
49
49
|
license: "MIT",
|
|
50
50
|
type: "module",
|
|
@@ -83,6 +83,8 @@ Usage:
|
|
|
83
83
|
free-pi-cli start the coding agent (first run: consent + GitHub sign-in)
|
|
84
84
|
free-pi-cli --session <id> resume a previous session
|
|
85
85
|
free-pi-cli logout remove the stored sign-in token; the next run signs in again
|
|
86
|
+
free-pi-cli mcp <command> add, remove, list, or sign in to your own MCP servers
|
|
87
|
+
(free-pi-cli mcp --help for details)
|
|
86
88
|
free-pi-cli --version print the CLI version
|
|
87
89
|
free-pi-cli --help show this help
|
|
88
90
|
|
|
@@ -97,6 +99,8 @@ function parseCliArgs(argv) {
|
|
|
97
99
|
const [first, ...rest] = argv;
|
|
98
100
|
if (first === undefined)
|
|
99
101
|
return { kind: "run" };
|
|
102
|
+
if (first === "mcp")
|
|
103
|
+
return { kind: "mcp", args: rest };
|
|
100
104
|
if (first === "--session") {
|
|
101
105
|
return rest.length > 1 ? { kind: "unknown", arg: rest[1] } : { kind: "run" };
|
|
102
106
|
}
|
|
@@ -163,21 +167,58 @@ async function runLogout(deps) {
|
|
|
163
167
|
return 0;
|
|
164
168
|
}
|
|
165
169
|
|
|
170
|
+
// src/mcp-command.ts
|
|
171
|
+
import { existsSync } from "node:fs";
|
|
172
|
+
import { dirname as dirname2, join } from "node:path";
|
|
173
|
+
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
174
|
+
function piMcpCliPath() {
|
|
175
|
+
return join(dirname2(fileURLToPath(import.meta.resolve("@earendil-works/pi-coding-agent"))), "extensions", "mcp", "cli.js");
|
|
176
|
+
}
|
|
177
|
+
async function loadPiRunner() {
|
|
178
|
+
const path = piMcpCliPath();
|
|
179
|
+
if (!existsSync(path)) {
|
|
180
|
+
throw new Error(`free-pi-cli mcp is unavailable: the bundled pi package has no ${path}. Reinstall free-pi-cli.`);
|
|
181
|
+
}
|
|
182
|
+
const mod = await import(pathToFileURL(path).href);
|
|
183
|
+
if (typeof mod.runMcpCommand !== "function") {
|
|
184
|
+
throw new Error(`free-pi-cli mcp is unavailable: ${path} does not export runMcpCommand. Reinstall free-pi-cli.`);
|
|
185
|
+
}
|
|
186
|
+
return mod.runMcpCommand;
|
|
187
|
+
}
|
|
188
|
+
function brandMcpLine(line) {
|
|
189
|
+
return line.replace(/\bpi (?=mcp\b)/g, "free-pi-cli ").replace(/~\/\.pi\/agent\b/g, "~/.free-pi/agent").replace(/\b([Ss]tart) pi\b/g, "$1 free-pi-cli");
|
|
190
|
+
}
|
|
191
|
+
async function runFreePiMcp(args, deps) {
|
|
192
|
+
try {
|
|
193
|
+
const run = await (deps.loadRunner ?? loadPiRunner)();
|
|
194
|
+
process.env.PI_CODING_AGENT_DIR = deps.agentDir;
|
|
195
|
+
return await run(args, {
|
|
196
|
+
cwd: deps.cwd,
|
|
197
|
+
agentDir: deps.agentDir,
|
|
198
|
+
log: (line) => deps.log(brandMcpLine(line)),
|
|
199
|
+
error: (line) => deps.error(brandMcpLine(line))
|
|
200
|
+
});
|
|
201
|
+
} catch (err) {
|
|
202
|
+
deps.error(err instanceof Error ? err.message : String(err));
|
|
203
|
+
return 1;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
166
207
|
// src/paths.ts
|
|
167
208
|
import { homedir } from "node:os";
|
|
168
|
-
import { join } from "node:path";
|
|
209
|
+
import { join as join2 } from "node:path";
|
|
169
210
|
var FREE_PI_DIR_NAME = ".free-pi";
|
|
170
211
|
function getFreePiAgentDir() {
|
|
171
|
-
return
|
|
212
|
+
return join2(homedir(), FREE_PI_DIR_NAME, "agent");
|
|
172
213
|
}
|
|
173
214
|
function getCredentialsPath(agentDir = getFreePiAgentDir()) {
|
|
174
|
-
return
|
|
215
|
+
return join2(agentDir, "credentials.json");
|
|
175
216
|
}
|
|
176
217
|
function getLastSeenVersionPath(agentDir = getFreePiAgentDir()) {
|
|
177
|
-
return
|
|
218
|
+
return join2(agentDir, "last-seen-version");
|
|
178
219
|
}
|
|
179
220
|
function getConfigPath() {
|
|
180
|
-
return
|
|
221
|
+
return join2(homedir(), FREE_PI_DIR_NAME, "config.json");
|
|
181
222
|
}
|
|
182
223
|
|
|
183
224
|
// src/pi-launch.ts
|
|
@@ -185,6 +226,9 @@ import {
|
|
|
185
226
|
createAgentSessionFromServices,
|
|
186
227
|
createAgentSessionRuntime,
|
|
187
228
|
createAgentSessionServices,
|
|
229
|
+
createCodemodeExtension,
|
|
230
|
+
createMcpExtension,
|
|
231
|
+
createToolSearchExtension,
|
|
188
232
|
InteractiveMode,
|
|
189
233
|
SessionManager,
|
|
190
234
|
SettingsManager
|
|
@@ -5290,7 +5334,7 @@ function createAdsExtension(opts) {
|
|
|
5290
5334
|
}
|
|
5291
5335
|
|
|
5292
5336
|
// src/pi-launch.ts
|
|
5293
|
-
import { join as
|
|
5337
|
+
import { join as join3, resolve as resolve2 } from "node:path";
|
|
5294
5338
|
|
|
5295
5339
|
// src/provider.ts
|
|
5296
5340
|
var PROVIDER_NAME = "free-pi";
|
|
@@ -5328,15 +5372,38 @@ var Metrics = {
|
|
|
5328
5372
|
update: 0
|
|
5329
5373
|
};
|
|
5330
5374
|
// ../../node_modules/.bun/typebox@1.3.16/node_modules/typebox/build/guard/guard.mjs
|
|
5375
|
+
function IsArray(value) {
|
|
5376
|
+
return Array.isArray(value);
|
|
5377
|
+
}
|
|
5378
|
+
function IsBigInt(value) {
|
|
5379
|
+
return IsEqual(typeof value, "bigint");
|
|
5380
|
+
}
|
|
5381
|
+
function IsBoolean(value) {
|
|
5382
|
+
return IsEqual(typeof value, "boolean");
|
|
5383
|
+
}
|
|
5331
5384
|
function IsNull(value) {
|
|
5332
5385
|
return IsEqual(value, null);
|
|
5333
5386
|
}
|
|
5387
|
+
function IsNumber(value) {
|
|
5388
|
+
return Number.isFinite(value);
|
|
5389
|
+
}
|
|
5334
5390
|
function IsObject(value) {
|
|
5335
5391
|
return IsEqual(typeof value, "object") && !IsNull(value);
|
|
5336
5392
|
}
|
|
5393
|
+
function IsString(value) {
|
|
5394
|
+
return IsEqual(typeof value, "string");
|
|
5395
|
+
}
|
|
5337
5396
|
function IsEqual(left, right) {
|
|
5338
5397
|
return left === right;
|
|
5339
5398
|
}
|
|
5399
|
+
function IsClassInstance(value) {
|
|
5400
|
+
if (!IsObject(value))
|
|
5401
|
+
return false;
|
|
5402
|
+
const proto = globalThis.Object.getPrototypeOf(value);
|
|
5403
|
+
if (IsNull(proto))
|
|
5404
|
+
return false;
|
|
5405
|
+
return IsEqual(typeof proto.constructor, "function") && !(IsEqual(proto.constructor, globalThis.Object) || IsEqual(proto.constructor.name, "Object"));
|
|
5406
|
+
}
|
|
5340
5407
|
function IsUnsafePropertyKey(key) {
|
|
5341
5408
|
return IsEqual(key, "__proto__") || IsEqual(key, "constructor") || IsEqual(key, "prototype");
|
|
5342
5409
|
}
|
|
@@ -5346,6 +5413,81 @@ function HasPropertyKey(value, key) {
|
|
|
5346
5413
|
function Keys(value) {
|
|
5347
5414
|
return Object.getOwnPropertyNames(value);
|
|
5348
5415
|
}
|
|
5416
|
+
function Symbols(value) {
|
|
5417
|
+
return Object.getOwnPropertySymbols(value);
|
|
5418
|
+
}
|
|
5419
|
+
// ../../node_modules/.bun/typebox@1.3.16/node_modules/typebox/build/guard/globals.mjs
|
|
5420
|
+
function IsTypeArray(value) {
|
|
5421
|
+
return globalThis.ArrayBuffer.isView(value);
|
|
5422
|
+
}
|
|
5423
|
+
function IsRegExp(value) {
|
|
5424
|
+
return value instanceof globalThis.RegExp;
|
|
5425
|
+
}
|
|
5426
|
+
function IsSet(value) {
|
|
5427
|
+
return value instanceof globalThis.Set;
|
|
5428
|
+
}
|
|
5429
|
+
function IsMap(value) {
|
|
5430
|
+
return value instanceof globalThis.Map;
|
|
5431
|
+
}
|
|
5432
|
+
// ../../node_modules/.bun/typebox@1.3.16/node_modules/typebox/build/system/memory/clone.mjs
|
|
5433
|
+
function FromClassInstance(value) {
|
|
5434
|
+
return value;
|
|
5435
|
+
}
|
|
5436
|
+
function IsSchemaObject(value) {
|
|
5437
|
+
return HasPropertyKey(value, "~kind") || HasPropertyKey(value, "~unsafe");
|
|
5438
|
+
}
|
|
5439
|
+
function FromSchemaObject(value) {
|
|
5440
|
+
const result = {};
|
|
5441
|
+
for (const key of Keys(value)) {
|
|
5442
|
+
if (IsUnsafePropertyKey(key))
|
|
5443
|
+
continue;
|
|
5444
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
5445
|
+
descriptor.value = FromValue(descriptor.value);
|
|
5446
|
+
if (IsEqual(descriptor.enumerable, true)) {
|
|
5447
|
+
result[key] = descriptor.value;
|
|
5448
|
+
} else {
|
|
5449
|
+
Object.defineProperty(result, key, descriptor);
|
|
5450
|
+
}
|
|
5451
|
+
}
|
|
5452
|
+
return result;
|
|
5453
|
+
}
|
|
5454
|
+
function FromPlainObject(value) {
|
|
5455
|
+
const result = {};
|
|
5456
|
+
for (const key of Keys(value)) {
|
|
5457
|
+
if (IsUnsafePropertyKey(key))
|
|
5458
|
+
continue;
|
|
5459
|
+
result[key] = FromValue(value[key]);
|
|
5460
|
+
}
|
|
5461
|
+
for (const key of Symbols(value)) {
|
|
5462
|
+
result[key] = FromValue(value[key]);
|
|
5463
|
+
}
|
|
5464
|
+
return result;
|
|
5465
|
+
}
|
|
5466
|
+
function FromObject(value) {
|
|
5467
|
+
return IsClassInstance(value) ? FromClassInstance(value) : IsSchemaObject(value) ? FromSchemaObject(value) : FromPlainObject(value);
|
|
5468
|
+
}
|
|
5469
|
+
function FromArray(value) {
|
|
5470
|
+
return value.map((element) => FromValue(element));
|
|
5471
|
+
}
|
|
5472
|
+
function FromTypedArray(value) {
|
|
5473
|
+
return value.slice();
|
|
5474
|
+
}
|
|
5475
|
+
function FromRegExp(value) {
|
|
5476
|
+
return new RegExp(value.source, value.flags);
|
|
5477
|
+
}
|
|
5478
|
+
function FromMap(value) {
|
|
5479
|
+
return new Map(FromValue([...value.entries()]));
|
|
5480
|
+
}
|
|
5481
|
+
function FromSet(value) {
|
|
5482
|
+
return new Set(FromValue([...value.values()]));
|
|
5483
|
+
}
|
|
5484
|
+
function FromValue(value) {
|
|
5485
|
+
return IsTypeArray(value) ? FromTypedArray(value) : IsRegExp(value) ? FromRegExp(value) : IsMap(value) ? FromMap(value) : IsSet(value) ? FromSet(value) : IsArray(value) ? FromArray(value) : IsObject(value) ? FromObject(value) : value;
|
|
5486
|
+
}
|
|
5487
|
+
function Clone(value) {
|
|
5488
|
+
Metrics.clone += 1;
|
|
5489
|
+
return FromValue(value);
|
|
5490
|
+
}
|
|
5349
5491
|
// ../../node_modules/.bun/typebox@1.3.16/node_modules/typebox/build/system/settings/settings.mjs
|
|
5350
5492
|
var settings = {
|
|
5351
5493
|
immutableTypes: false,
|
|
@@ -5382,12 +5524,52 @@ function Create(hidden, enumerable, options = {}) {
|
|
|
5382
5524
|
const withHidden = settings.enumerableKind ? Merge(withOptions, hidden) : MergeHidden(withOptions, hidden);
|
|
5383
5525
|
return settings.immutableTypes ? Object.freeze(withHidden) : withHidden;
|
|
5384
5526
|
}
|
|
5527
|
+
// ../../node_modules/.bun/typebox@1.3.16/node_modules/typebox/build/system/memory/update.mjs
|
|
5528
|
+
function Update(current, hidden, enumerable) {
|
|
5529
|
+
Metrics.update += 1;
|
|
5530
|
+
const settings = Get();
|
|
5531
|
+
const result = Clone(current);
|
|
5532
|
+
for (const key of Object.keys(hidden)) {
|
|
5533
|
+
Object.defineProperty(result, key, {
|
|
5534
|
+
configurable: true,
|
|
5535
|
+
writable: true,
|
|
5536
|
+
enumerable: settings.enumerableKind,
|
|
5537
|
+
value: hidden[key]
|
|
5538
|
+
});
|
|
5539
|
+
}
|
|
5540
|
+
for (const key of Object.keys(enumerable)) {
|
|
5541
|
+
Object.defineProperty(result, key, {
|
|
5542
|
+
configurable: true,
|
|
5543
|
+
enumerable: true,
|
|
5544
|
+
writable: true,
|
|
5545
|
+
value: enumerable[key]
|
|
5546
|
+
});
|
|
5547
|
+
}
|
|
5548
|
+
return result;
|
|
5549
|
+
}
|
|
5385
5550
|
// ../../node_modules/.bun/typebox@1.3.16/node_modules/typebox/build/type/types/schema.mjs
|
|
5386
5551
|
function IsSchema(value) {
|
|
5387
5552
|
return IsObject(value);
|
|
5388
5553
|
}
|
|
5389
5554
|
|
|
5555
|
+
// ../../node_modules/.bun/typebox@1.3.16/node_modules/typebox/build/type/engine/optional/instantiate_add.mjs
|
|
5556
|
+
function AddOptionalOperation(type) {
|
|
5557
|
+
return Update(type, { "~optional": true }, {});
|
|
5558
|
+
}
|
|
5559
|
+
function AddOptionalAction(type, options) {
|
|
5560
|
+
const result = Update(AddOptionalOperation(type), {}, options);
|
|
5561
|
+
return result;
|
|
5562
|
+
}
|
|
5563
|
+
|
|
5564
|
+
// ../../node_modules/.bun/typebox@1.3.16/node_modules/typebox/build/type/action/_add_optional.mjs
|
|
5565
|
+
function AddOptional(type, options = {}) {
|
|
5566
|
+
return AddOptionalAction(type, options);
|
|
5567
|
+
}
|
|
5568
|
+
|
|
5390
5569
|
// ../../node_modules/.bun/typebox@1.3.16/node_modules/typebox/build/type/types/_optional.mjs
|
|
5570
|
+
function Optional(type) {
|
|
5571
|
+
return AddOptional(type);
|
|
5572
|
+
}
|
|
5391
5573
|
function IsOptional(value) {
|
|
5392
5574
|
return IsSchema(value) && HasPropertyKey(value, "~optional");
|
|
5393
5575
|
}
|
|
@@ -5430,10 +5612,41 @@ var F64Out = new Uint8Array(F64.buffer);
|
|
|
5430
5612
|
var encoder = new TextEncoder;
|
|
5431
5613
|
// ../../node_modules/.bun/typebox@1.3.16/node_modules/typebox/build/type/types/integer.mjs
|
|
5432
5614
|
var IntegerPattern = "-?(?:0|[1-9][0-9]*)";
|
|
5615
|
+
// ../../node_modules/.bun/typebox@1.3.16/node_modules/typebox/build/type/types/literal.mjs
|
|
5616
|
+
class InvalidLiteralValue extends Error {
|
|
5617
|
+
constructor(value) {
|
|
5618
|
+
super(`Invalid Literal value`);
|
|
5619
|
+
Object.defineProperty(this, "cause", {
|
|
5620
|
+
value: { value },
|
|
5621
|
+
writable: false,
|
|
5622
|
+
configurable: false,
|
|
5623
|
+
enumerable: false
|
|
5624
|
+
});
|
|
5625
|
+
}
|
|
5626
|
+
}
|
|
5627
|
+
function LiteralTypeName(value) {
|
|
5628
|
+
return IsBigInt(value) ? "bigint" : IsBoolean(value) ? "boolean" : IsNumber(value) ? "number" : IsString(value) ? "string" : (() => {
|
|
5629
|
+
throw new InvalidLiteralValue(value);
|
|
5630
|
+
})();
|
|
5631
|
+
}
|
|
5632
|
+
function Literal(value, options) {
|
|
5633
|
+
return Create({ "~kind": "Literal" }, { type: LiteralTypeName(value), const: value }, options);
|
|
5634
|
+
}
|
|
5433
5635
|
// ../../node_modules/.bun/typebox@1.3.16/node_modules/typebox/build/type/types/number.mjs
|
|
5434
5636
|
var NumberPattern = "-?(?:0|[1-9][0-9]*)(?:\\.[0-9]+)?";
|
|
5637
|
+
function Number2(options) {
|
|
5638
|
+
return Create({ "~kind": "Number" }, { type: "number" }, options);
|
|
5639
|
+
}
|
|
5435
5640
|
// ../../node_modules/.bun/typebox@1.3.16/node_modules/typebox/build/type/types/string.mjs
|
|
5436
5641
|
var StringPattern = ".*";
|
|
5642
|
+
function String2(options) {
|
|
5643
|
+
return Create({ "~kind": "String" }, { type: "string" }, options);
|
|
5644
|
+
}
|
|
5645
|
+
|
|
5646
|
+
// ../../node_modules/.bun/typebox@1.3.16/node_modules/typebox/build/type/types/union.mjs
|
|
5647
|
+
function Union(anyOf, options = {}) {
|
|
5648
|
+
return Create({ "~kind": "Union" }, { anyOf }, options);
|
|
5649
|
+
}
|
|
5437
5650
|
|
|
5438
5651
|
// ../../node_modules/.bun/typebox@1.3.16/node_modules/typebox/build/type/types/record.mjs
|
|
5439
5652
|
var IntegerKey = `^${IntegerPattern}$`;
|
|
@@ -5617,6 +5830,33 @@ first. Purchased credits cover use beyond it and do not expire.
|
|
|
5617
5830
|
- \`/close-other-session\` — free a stuck session on another machine
|
|
5618
5831
|
- \`/update\` — get the latest free-pi
|
|
5619
5832
|
- \`/whats-new\` — what changed in recent releases
|
|
5833
|
+
- \`/todos\` — show the agent's todo list
|
|
5834
|
+
- \`/mcp\` — manage your MCP servers (connect, sign in, turn on or off)
|
|
5835
|
+
|
|
5836
|
+
## Todo list
|
|
5837
|
+
The agent keeps its own todo list with the \`todo\` tool, to track multi-step
|
|
5838
|
+
work. While any item is open, the list shows live above the input box, and
|
|
5839
|
+
each todo change prints a progress line in the conversation. \`/todos\` shows
|
|
5840
|
+
the full list. The list belongs to the session and comes back when you resume
|
|
5841
|
+
it.
|
|
5842
|
+
|
|
5843
|
+
## MCP servers
|
|
5844
|
+
MCP (Model Context Protocol) servers give the agent extra tools, such as a
|
|
5845
|
+
database, a browser, or an internal API. free-pi includes none; you add your
|
|
5846
|
+
own. The agent reaches their tools by writing a short script for its
|
|
5847
|
+
\`codemode\` tool.
|
|
5848
|
+
Servers live in \`~/.free-pi/agent/mcp.json\`. A project can have its own
|
|
5849
|
+
\`.pi/mcp.json\`; it is read only after you trust the project when asked.
|
|
5850
|
+
Add a server from a shell, or ask the agent to run the command for you with
|
|
5851
|
+
its bash tool:
|
|
5852
|
+
\`free-pi-cli mcp add files -- npx -y @modelcontextprotocol/server-filesystem .\`
|
|
5853
|
+
\`free-pi-cli mcp add docs --url https://example.com/mcp\`
|
|
5854
|
+
Other commands: \`free-pi-cli mcp list\` (add \`--json\` for machine output),
|
|
5855
|
+
\`free-pi-cli mcp remove <server>\`, \`free-pi-cli mcp login <server>\` to sign
|
|
5856
|
+
in to a server that uses OAuth, and \`free-pi-cli mcp logout <server>\`. Add
|
|
5857
|
+
\`-l\` to add or remove in the current project instead of globally.
|
|
5858
|
+
Inside a session, \`/mcp\` shows each server and its tools, and lets you sign
|
|
5859
|
+
in, reconnect, or turn a server on or off.
|
|
5620
5860
|
|
|
5621
5861
|
## Support
|
|
5622
5862
|
Today's sponsor keeps free-pi free. When the user asks how to help the
|
|
@@ -5648,6 +5888,257 @@ function createDocsToolExtension() {
|
|
|
5648
5888
|
};
|
|
5649
5889
|
}
|
|
5650
5890
|
|
|
5891
|
+
// src/todo-view.ts
|
|
5892
|
+
var PLAIN_STYLE = { bold: (s) => s, dim: (s) => s, accent: (s) => s, error: (s) => s };
|
|
5893
|
+
var GAP = " ";
|
|
5894
|
+
function truncate2(text, max) {
|
|
5895
|
+
if (max <= 0)
|
|
5896
|
+
return "";
|
|
5897
|
+
if ([...text].length <= max)
|
|
5898
|
+
return text;
|
|
5899
|
+
return [...text].slice(0, Math.max(0, max - 1)).join("") + "…";
|
|
5900
|
+
}
|
|
5901
|
+
function counts(todos) {
|
|
5902
|
+
return { done: todos.filter((t) => t.done).length, total: todos.length };
|
|
5903
|
+
}
|
|
5904
|
+
function widgetLines(todos, width, st = PLAIN_STYLE) {
|
|
5905
|
+
if (todos.length === 0)
|
|
5906
|
+
return [];
|
|
5907
|
+
const { done, total } = counts(todos);
|
|
5908
|
+
const header = `Todos ${done}/${total}`;
|
|
5909
|
+
const firstOpen = todos.findIndex((t) => !t.done);
|
|
5910
|
+
const marks = todos.map((t, i) => t.done ? `✓ ${t.text}` : i === firstOpen ? `▸ ${t.text}` : `○ ${t.text}`);
|
|
5911
|
+
const compactLength = [...[header, ...marks].join(GAP)].length;
|
|
5912
|
+
if (compactLength <= width) {
|
|
5913
|
+
return [
|
|
5914
|
+
[
|
|
5915
|
+
st.bold(header),
|
|
5916
|
+
...todos.map((t, i) => t.done ? st.dim(marks[i]) : i === firstOpen ? st.accent(st.bold(marks[i])) : st.dim(marks[i]))
|
|
5917
|
+
].join(GAP)
|
|
5918
|
+
];
|
|
5919
|
+
}
|
|
5920
|
+
const lines = [st.bold(truncate2(header, width))];
|
|
5921
|
+
const open = todos.filter((t) => !t.done);
|
|
5922
|
+
if (open.length === 0) {
|
|
5923
|
+
lines.push(st.dim(truncate2("✓ All done", width)));
|
|
5924
|
+
return lines;
|
|
5925
|
+
}
|
|
5926
|
+
open.slice(0, 3).forEach((t, i) => {
|
|
5927
|
+
const line = truncate2(`${i === 0 ? "▸" : "○"} ${t.text}`, width);
|
|
5928
|
+
lines.push(i === 0 ? st.accent(st.bold(line)) : st.dim(line));
|
|
5929
|
+
});
|
|
5930
|
+
const more = open.length - 3;
|
|
5931
|
+
if (more > 0)
|
|
5932
|
+
lines.push(st.dim(truncate2(`+${more} more · /todos for all`, width)));
|
|
5933
|
+
return lines;
|
|
5934
|
+
}
|
|
5935
|
+
function resultLines(d, st = PLAIN_STYLE) {
|
|
5936
|
+
if (d.error)
|
|
5937
|
+
return [st.error(`Error: ${d.error}`)];
|
|
5938
|
+
if (d.action === "clear")
|
|
5939
|
+
return [`${st.accent("●")} ${st.bold("todo")} cleared`];
|
|
5940
|
+
const { done, total } = counts(d.todos);
|
|
5941
|
+
const head = `${st.accent("●")} ${st.bold("todo")} ${done}/${total}`;
|
|
5942
|
+
let first = head;
|
|
5943
|
+
if (d.item && d.action === "add")
|
|
5944
|
+
first = `${head}${GAP}+ ${d.item.text}`;
|
|
5945
|
+
if (d.item && d.action === "toggle")
|
|
5946
|
+
first = `${head}${GAP}${d.item.done ? "✓" : "○"} ${d.item.text}`;
|
|
5947
|
+
const lines = [first];
|
|
5948
|
+
const next = d.todos.find((t) => !t.done);
|
|
5949
|
+
if ((d.action === "add" || d.action === "toggle") && next && next.id !== d.item?.id)
|
|
5950
|
+
lines.push(st.dim(`${GAP}next: ${next.text}`));
|
|
5951
|
+
return lines;
|
|
5952
|
+
}
|
|
5953
|
+
|
|
5954
|
+
// src/todo-tool.ts
|
|
5955
|
+
var TODO_TOOL_NAME = "todo";
|
|
5956
|
+
var TODO_WIDGET_ID = "freepi-todo";
|
|
5957
|
+
var TODO_ENTRY_TYPE = "free-pi-todo";
|
|
5958
|
+
var ALL_DONE_LINGER_MS = 3000;
|
|
5959
|
+
var REASSERT_ORDER_MS = 15000;
|
|
5960
|
+
var realTimers = {
|
|
5961
|
+
setTimeout: (fn, ms) => {
|
|
5962
|
+
const h = setTimeout(fn, ms);
|
|
5963
|
+
h.unref?.();
|
|
5964
|
+
return h;
|
|
5965
|
+
},
|
|
5966
|
+
clearTimeout: (h) => clearTimeout(h),
|
|
5967
|
+
setInterval: (fn, ms) => {
|
|
5968
|
+
const h = setInterval(fn, ms);
|
|
5969
|
+
h.unref?.();
|
|
5970
|
+
return h;
|
|
5971
|
+
},
|
|
5972
|
+
clearInterval: (h) => clearInterval(h)
|
|
5973
|
+
};
|
|
5974
|
+
var PARAMS4 = _Object_({
|
|
5975
|
+
action: Union([Literal("list"), Literal("add"), Literal("toggle"), Literal("clear")]),
|
|
5976
|
+
text: Optional(String2({ description: "Todo text (for add)" })),
|
|
5977
|
+
id: Optional(Number2({ description: "Todo ID (for toggle)" }))
|
|
5978
|
+
});
|
|
5979
|
+
function asSnapshot(value) {
|
|
5980
|
+
const v = value;
|
|
5981
|
+
if (!v || !Array.isArray(v.todos) || typeof v.nextId !== "number")
|
|
5982
|
+
return;
|
|
5983
|
+
return { todos: v.todos, nextId: v.nextId };
|
|
5984
|
+
}
|
|
5985
|
+
function formatTodos(todos) {
|
|
5986
|
+
return todos.length ? todos.map((t) => `[${t.done ? "x" : " "}] #${t.id}: ${t.text}`).join(`
|
|
5987
|
+
`) : "No todos";
|
|
5988
|
+
}
|
|
5989
|
+
function styleFrom(theme) {
|
|
5990
|
+
return {
|
|
5991
|
+
bold: (s) => theme.bold(s),
|
|
5992
|
+
dim: (s) => theme.fg("dim", s),
|
|
5993
|
+
accent: (s) => theme.fg("accent", s),
|
|
5994
|
+
error: (s) => theme.fg("error", s)
|
|
5995
|
+
};
|
|
5996
|
+
}
|
|
5997
|
+
function createTodoToolExtension(options = {}) {
|
|
5998
|
+
const timers = options.timers ?? realTimers;
|
|
5999
|
+
return {
|
|
6000
|
+
name: "free-pi-todo",
|
|
6001
|
+
factory(pi) {
|
|
6002
|
+
let todos = [];
|
|
6003
|
+
let nextId = 1;
|
|
6004
|
+
let widgetCtx;
|
|
6005
|
+
let lingerTimer;
|
|
6006
|
+
let orderTimer;
|
|
6007
|
+
const stopTimers = () => {
|
|
6008
|
+
if (lingerTimer !== undefined)
|
|
6009
|
+
timers.clearTimeout(lingerTimer);
|
|
6010
|
+
if (orderTimer !== undefined)
|
|
6011
|
+
timers.clearInterval(orderTimer);
|
|
6012
|
+
lingerTimer = orderTimer = undefined;
|
|
6013
|
+
};
|
|
6014
|
+
const hideWidget = () => {
|
|
6015
|
+
stopTimers();
|
|
6016
|
+
try {
|
|
6017
|
+
widgetCtx?.ui.setWidget(TODO_WIDGET_ID, undefined);
|
|
6018
|
+
} catch {}
|
|
6019
|
+
};
|
|
6020
|
+
const showWidget = () => {
|
|
6021
|
+
try {
|
|
6022
|
+
widgetCtx?.ui.setWidget(TODO_WIDGET_ID, (_tui, theme) => {
|
|
6023
|
+
const style = styleFrom(theme);
|
|
6024
|
+
return {
|
|
6025
|
+
render: (width) => widgetLines(todos, Math.max(1, width - 2), style).map((l) => ` ${l}`),
|
|
6026
|
+
invalidate() {}
|
|
6027
|
+
};
|
|
6028
|
+
});
|
|
6029
|
+
} catch {}
|
|
6030
|
+
};
|
|
6031
|
+
const refreshWidget = (ctx, replay = false) => {
|
|
6032
|
+
if (ctx?.mode !== "tui")
|
|
6033
|
+
return;
|
|
6034
|
+
widgetCtx = ctx;
|
|
6035
|
+
const hasOpen = todos.some((t) => !t.done);
|
|
6036
|
+
if (todos.length === 0 || !hasOpen && replay) {
|
|
6037
|
+
hideWidget();
|
|
6038
|
+
return;
|
|
6039
|
+
}
|
|
6040
|
+
if (lingerTimer !== undefined) {
|
|
6041
|
+
timers.clearTimeout(lingerTimer);
|
|
6042
|
+
lingerTimer = undefined;
|
|
6043
|
+
}
|
|
6044
|
+
showWidget();
|
|
6045
|
+
if (orderTimer === undefined)
|
|
6046
|
+
orderTimer = timers.setInterval(showWidget, REASSERT_ORDER_MS);
|
|
6047
|
+
if (!hasOpen) {
|
|
6048
|
+
lingerTimer = timers.setTimeout(() => {
|
|
6049
|
+
lingerTimer = undefined;
|
|
6050
|
+
hideWidget();
|
|
6051
|
+
}, ALL_DONE_LINGER_MS);
|
|
6052
|
+
}
|
|
6053
|
+
};
|
|
6054
|
+
const reconstructState = (ctx) => {
|
|
6055
|
+
todos = [];
|
|
6056
|
+
nextId = 1;
|
|
6057
|
+
for (const entry of ctx.sessionManager.getBranch()) {
|
|
6058
|
+
let snap;
|
|
6059
|
+
if (entry.type === "custom" && entry.customType === TODO_ENTRY_TYPE)
|
|
6060
|
+
snap = asSnapshot(entry.data);
|
|
6061
|
+
else if (entry.type === "message" && entry.message.role === "toolResult" && entry.message.toolName === TODO_TOOL_NAME)
|
|
6062
|
+
snap = asSnapshot(entry.message.details);
|
|
6063
|
+
if (snap) {
|
|
6064
|
+
todos = snap.todos.map((t) => ({ ...t }));
|
|
6065
|
+
nextId = snap.nextId;
|
|
6066
|
+
}
|
|
6067
|
+
}
|
|
6068
|
+
refreshWidget(ctx, true);
|
|
6069
|
+
};
|
|
6070
|
+
pi.on("session_start", async (_event, ctx) => reconstructState(ctx));
|
|
6071
|
+
pi.on("session_tree", async (_event, ctx) => reconstructState(ctx));
|
|
6072
|
+
pi.on("session_shutdown", async () => {
|
|
6073
|
+
stopTimers();
|
|
6074
|
+
widgetCtx = undefined;
|
|
6075
|
+
});
|
|
6076
|
+
const result = (ctx, text, action, error, item) => {
|
|
6077
|
+
const snapshot = { todos: todos.map((t) => ({ ...t })), nextId };
|
|
6078
|
+
if (action !== "list" && !error)
|
|
6079
|
+
pi.appendEntry(TODO_ENTRY_TYPE, snapshot);
|
|
6080
|
+
if (action !== "list" && !error)
|
|
6081
|
+
refreshWidget(ctx);
|
|
6082
|
+
return {
|
|
6083
|
+
content: [{ type: "text", text }],
|
|
6084
|
+
details: { action, ...snapshot, ...item ? { item: { ...item } } : {}, ...error ? { error } : {} }
|
|
6085
|
+
};
|
|
6086
|
+
};
|
|
6087
|
+
pi.registerTool({
|
|
6088
|
+
name: TODO_TOOL_NAME,
|
|
6089
|
+
label: "Todo",
|
|
6090
|
+
description: "Manage a todo list. Actions: list, add (text), toggle (id), clear",
|
|
6091
|
+
promptSnippet: "Track your own multi-step work in a todo list: add items, toggle them done, list them",
|
|
6092
|
+
parameters: PARAMS4,
|
|
6093
|
+
async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
|
|
6094
|
+
switch (params.action) {
|
|
6095
|
+
case "list":
|
|
6096
|
+
return result(ctx, formatTodos(todos), "list");
|
|
6097
|
+
case "add": {
|
|
6098
|
+
if (!params.text)
|
|
6099
|
+
return result(ctx, "Error: text required for add", "add", "text required");
|
|
6100
|
+
const todo = { id: nextId++, text: params.text, done: false };
|
|
6101
|
+
todos.push(todo);
|
|
6102
|
+
return result(ctx, `Added todo #${todo.id}: ${todo.text}`, "add", undefined, todo);
|
|
6103
|
+
}
|
|
6104
|
+
case "toggle": {
|
|
6105
|
+
if (params.id === undefined)
|
|
6106
|
+
return result(ctx, "Error: id required for toggle", "toggle", "id required");
|
|
6107
|
+
const todo = todos.find((t) => t.id === params.id);
|
|
6108
|
+
if (!todo)
|
|
6109
|
+
return result(ctx, `Todo #${params.id} not found`, "toggle", `#${params.id} not found`);
|
|
6110
|
+
todo.done = !todo.done;
|
|
6111
|
+
return result(ctx, `Todo #${todo.id} ${todo.done ? "completed" : "uncompleted"}`, "toggle", undefined, todo);
|
|
6112
|
+
}
|
|
6113
|
+
case "clear": {
|
|
6114
|
+
const count = todos.length;
|
|
6115
|
+
todos = [];
|
|
6116
|
+
nextId = 1;
|
|
6117
|
+
return result(ctx, `Cleared ${count} todos`, "clear");
|
|
6118
|
+
}
|
|
6119
|
+
default:
|
|
6120
|
+
return result(ctx, `Unknown action: ${String(params.action)}`, "list", "unknown action");
|
|
6121
|
+
}
|
|
6122
|
+
},
|
|
6123
|
+
renderCall() {
|
|
6124
|
+
return { render: () => [], invalidate() {} };
|
|
6125
|
+
},
|
|
6126
|
+
renderResult(res, _opts, theme) {
|
|
6127
|
+
const details = res.details;
|
|
6128
|
+
const lines = details?.action ? resultLines(details, styleFrom(theme)) : [res.content[0]?.type === "text" ? res.content[0].text : ""];
|
|
6129
|
+
return { render: () => lines, invalidate() {} };
|
|
6130
|
+
}
|
|
6131
|
+
});
|
|
6132
|
+
pi.registerCommand("todos", {
|
|
6133
|
+
description: "Show all todos on the current branch",
|
|
6134
|
+
handler: async (_args, ctx) => {
|
|
6135
|
+
ctx.ui.notify(formatTodos(todos), "info");
|
|
6136
|
+
}
|
|
6137
|
+
});
|
|
6138
|
+
}
|
|
6139
|
+
};
|
|
6140
|
+
}
|
|
6141
|
+
|
|
5651
6142
|
// src/error-notice.ts
|
|
5652
6143
|
var NOTICE_DEDUPE_MS = 15000;
|
|
5653
6144
|
function parseErrorEnvelope(errorMessage) {
|
|
@@ -5703,12 +6194,16 @@ async function reportToolGuardTrigger(opts, toolName) {
|
|
|
5703
6194
|
});
|
|
5704
6195
|
} catch {}
|
|
5705
6196
|
}
|
|
6197
|
+
var MCP_RESOURCE_TOOLS = ["list_mcp_resources", "list_mcp_resource_templates", "read_mcp_resource"];
|
|
6198
|
+
function isAllowed(opts, name) {
|
|
6199
|
+
return opts.allowedTools.includes(name) || name.startsWith("mcp__") || MCP_RESOURCE_TOOLS.includes(name);
|
|
6200
|
+
}
|
|
5706
6201
|
function createToolGuardExtension(opts) {
|
|
5707
6202
|
return {
|
|
5708
6203
|
name: "free-pi-tool-guard",
|
|
5709
6204
|
factory(pi) {
|
|
5710
6205
|
pi.on("tool_call", (event) => {
|
|
5711
|
-
if (opts
|
|
6206
|
+
if (isAllowed(opts, event.toolName))
|
|
5712
6207
|
return;
|
|
5713
6208
|
reportToolGuardTrigger(opts, event.toolName);
|
|
5714
6209
|
return { block: true, reason: "tool not permitted in the free-pi distro" };
|
|
@@ -5786,6 +6281,13 @@ function registerBugAutocomplete(pi) {
|
|
|
5786
6281
|
|
|
5787
6282
|
// src/changelog.ts
|
|
5788
6283
|
var CHANGELOG_HIGHLIGHTS = [
|
|
6284
|
+
{
|
|
6285
|
+
version: "0.4.0",
|
|
6286
|
+
highlights: [
|
|
6287
|
+
"The agent now keeps a todo list for multi-step work: it shows live above the input box, and /todos shows the full list.",
|
|
6288
|
+
"Add your own MCP servers: `npx free-pi-cli mcp add`, then /mcp in a session to manage them."
|
|
6289
|
+
]
|
|
6290
|
+
},
|
|
5789
6291
|
{
|
|
5790
6292
|
version: "0.3.0",
|
|
5791
6293
|
highlights: [
|
|
@@ -6068,12 +6570,12 @@ function updateBannerLines(theme, latest, width) {
|
|
|
6068
6570
|
return [
|
|
6069
6571
|
"",
|
|
6070
6572
|
border,
|
|
6071
|
-
theme.bold(theme.fg("warning",
|
|
6072
|
-
theme.fg("dim",
|
|
6573
|
+
theme.bold(theme.fg("warning", truncate3(headline, inner))),
|
|
6574
|
+
theme.fg("dim", truncate3(detail, inner)),
|
|
6073
6575
|
border
|
|
6074
6576
|
];
|
|
6075
6577
|
}
|
|
6076
|
-
function
|
|
6578
|
+
function truncate3(text, maxWidth) {
|
|
6077
6579
|
if (maxWidth <= 0)
|
|
6078
6580
|
return "";
|
|
6079
6581
|
if (text.length <= maxWidth)
|
|
@@ -6084,7 +6586,7 @@ function truncate2(text, maxWidth) {
|
|
|
6084
6586
|
}
|
|
6085
6587
|
function headerLines(theme, modelName, expanded, width = Number.POSITIVE_INFINITY, updateLatest) {
|
|
6086
6588
|
const inner = width - 1;
|
|
6087
|
-
const fit = (plain, styled) => plain.length <= inner ? styled() : theme.fg("dim",
|
|
6589
|
+
const fit = (plain, styled) => plain.length <= inner ? styled() : theme.fg("dim", truncate3(plain, inner));
|
|
6088
6590
|
const titleRest = ` · ${modelName} · ctrl+o for help`;
|
|
6089
6591
|
const title = fit(`free-pi${titleRest}`, () => `${theme.bold(theme.fg("accent", "free-pi"))}${theme.fg("dim", titleRest)}`);
|
|
6090
6592
|
const commandLines = COMMANDS.map(({ name, description }) => {
|
|
@@ -6166,6 +6668,48 @@ function patchResumeLine(stream) {
|
|
|
6166
6668
|
};
|
|
6167
6669
|
}
|
|
6168
6670
|
|
|
6671
|
+
// src/project-trust.ts
|
|
6672
|
+
import { createInterface } from "node:readline/promises";
|
|
6673
|
+
import { resolve } from "node:path";
|
|
6674
|
+
import { hasTrustRequiringProjectResources, ProjectTrustStore } from "@earendil-works/pi-coding-agent";
|
|
6675
|
+
var PROJECT_TRUST_PROMPT = "This folder has project settings for free-pi (.pi/ or .agents/skills), which can run code such as MCP servers and extensions. Trust this folder? [y/N] ";
|
|
6676
|
+
async function decideProjectTrust(cwd, agentDir, deps) {
|
|
6677
|
+
if (!(deps.hasTrustRequiringResources ?? hasTrustRequiringProjectResources)(cwd))
|
|
6678
|
+
return true;
|
|
6679
|
+
const store = new ProjectTrustStore(agentDir);
|
|
6680
|
+
const saved = store.get(cwd);
|
|
6681
|
+
if (typeof saved === "boolean")
|
|
6682
|
+
return saved;
|
|
6683
|
+
if (!deps.stdinIsTTY) {
|
|
6684
|
+
deps.log("This folder has project settings (.pi/ or .agents/skills); they are ignored because it is not trusted.");
|
|
6685
|
+
return false;
|
|
6686
|
+
}
|
|
6687
|
+
const decision = /^y(es)?$/i.test((await deps.askLine(PROJECT_TRUST_PROMPT)).trim());
|
|
6688
|
+
store.set(cwd, decision);
|
|
6689
|
+
return decision;
|
|
6690
|
+
}
|
|
6691
|
+
function decideProjectTrustInteractive(cwd, agentDir) {
|
|
6692
|
+
return decideProjectTrust(cwd, agentDir, {
|
|
6693
|
+
stdinIsTTY: process.stdin.isTTY === true,
|
|
6694
|
+
log: (message) => console.log(message),
|
|
6695
|
+
askLine: async (prompt) => {
|
|
6696
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
6697
|
+
try {
|
|
6698
|
+
return await rl.question(prompt);
|
|
6699
|
+
} finally {
|
|
6700
|
+
rl.close();
|
|
6701
|
+
}
|
|
6702
|
+
}
|
|
6703
|
+
});
|
|
6704
|
+
}
|
|
6705
|
+
function projectTrustForCwd(cwd, launch, agentDir, hasTrustRequiringResources = hasTrustRequiringProjectResources) {
|
|
6706
|
+
if (resolve(cwd) === resolve(launch.cwd))
|
|
6707
|
+
return launch.trusted;
|
|
6708
|
+
if (!hasTrustRequiringResources(cwd))
|
|
6709
|
+
return true;
|
|
6710
|
+
return new ProjectTrustStore(agentDir).get(cwd) === true;
|
|
6711
|
+
}
|
|
6712
|
+
|
|
6169
6713
|
// src/pi-launch.ts
|
|
6170
6714
|
function catalogModelsFor(opts) {
|
|
6171
6715
|
if (opts.models && opts.models.length > 0)
|
|
@@ -6173,11 +6717,42 @@ function catalogModelsFor(opts) {
|
|
|
6173
6717
|
const id = opts.model || MODEL_ID;
|
|
6174
6718
|
return [{ id, name: id }];
|
|
6175
6719
|
}
|
|
6176
|
-
var ALLOWED_TOOL_NAMES = [
|
|
6720
|
+
var ALLOWED_TOOL_NAMES = [
|
|
6721
|
+
...SAFE_TOOLS,
|
|
6722
|
+
USAGE_TOOL_NAME,
|
|
6723
|
+
BUY_TOOL_NAME,
|
|
6724
|
+
DOCS_TOOL_NAME,
|
|
6725
|
+
TODO_TOOL_NAME,
|
|
6726
|
+
"codemode",
|
|
6727
|
+
"tool_search"
|
|
6728
|
+
];
|
|
6729
|
+
var SDK_TOOLS = [
|
|
6730
|
+
...ALLOWED_TOOL_NAMES,
|
|
6731
|
+
"mcp__*",
|
|
6732
|
+
"list_mcp_resources",
|
|
6733
|
+
"list_mcp_resource_templates",
|
|
6734
|
+
"read_mcp_resource"
|
|
6735
|
+
];
|
|
6177
6736
|
function resolveModelName(opts) {
|
|
6178
6737
|
return opts.models?.[0]?.name || opts.model || MODEL_ID;
|
|
6179
6738
|
}
|
|
6739
|
+
function buildSettingsManager(opts, models, projectTrusted) {
|
|
6740
|
+
return SettingsManager.inMemory({
|
|
6741
|
+
defaultProvider: PROVIDER_NAME,
|
|
6742
|
+
defaultModel: models[0].id,
|
|
6743
|
+
defaultTools: [...SAFE_TOOLS, TODO_TOOL_NAME],
|
|
6744
|
+
packages: [],
|
|
6745
|
+
quietStartup: true,
|
|
6746
|
+
tuiMode: "regular",
|
|
6747
|
+
theme: "light/dark",
|
|
6748
|
+
enableInstallTelemetry: false
|
|
6749
|
+
}, { projectTrusted });
|
|
6750
|
+
}
|
|
6751
|
+
function settingsForRuntime(opts, rtCwd, launch) {
|
|
6752
|
+
return buildSettingsManager(opts, catalogModelsFor(opts), projectTrustForCwd(rtCwd, launch, opts.agentDir));
|
|
6753
|
+
}
|
|
6180
6754
|
function buildRuntimeOptions(opts, sessionId) {
|
|
6755
|
+
process.env.PI_CODING_AGENT_DIR = opts.agentDir;
|
|
6181
6756
|
const models = catalogModelsFor(opts);
|
|
6182
6757
|
const providerExtension = {
|
|
6183
6758
|
name: "free-pi-provider",
|
|
@@ -6218,6 +6793,10 @@ function buildRuntimeOptions(opts, sessionId) {
|
|
|
6218
6793
|
modelName: resolveModelName(opts),
|
|
6219
6794
|
updateLatest: opts.updateLatest
|
|
6220
6795
|
});
|
|
6796
|
+
const todoToolExtension = createTodoToolExtension();
|
|
6797
|
+
const mcpExtension = { name: "pi-mcp", factory: createMcpExtension() };
|
|
6798
|
+
const codemodeExtension = { name: "pi-codemode", factory: createCodemodeExtension({ models: false }) };
|
|
6799
|
+
const toolSearchExtension = { name: "pi-tool-search", factory: createToolSearchExtension() };
|
|
6221
6800
|
const extensionFactories = [
|
|
6222
6801
|
providerExtension,
|
|
6223
6802
|
adsExtension,
|
|
@@ -6227,29 +6806,24 @@ function buildRuntimeOptions(opts, sessionId) {
|
|
|
6227
6806
|
errorNoticeExtension,
|
|
6228
6807
|
toolGuardExtension,
|
|
6229
6808
|
commandsExtension,
|
|
6230
|
-
headerExtension
|
|
6809
|
+
headerExtension,
|
|
6810
|
+
todoToolExtension,
|
|
6811
|
+
mcpExtension,
|
|
6812
|
+
codemodeExtension,
|
|
6813
|
+
toolSearchExtension
|
|
6231
6814
|
];
|
|
6232
|
-
const settingsManager =
|
|
6233
|
-
defaultProvider: PROVIDER_NAME,
|
|
6234
|
-
defaultModel: models[0].id,
|
|
6235
|
-
defaultTools: [...SAFE_TOOLS],
|
|
6236
|
-
packages: [],
|
|
6237
|
-
quietStartup: true,
|
|
6238
|
-
tuiMode: "regular",
|
|
6239
|
-
theme: "light/dark",
|
|
6240
|
-
enableInstallTelemetry: false
|
|
6241
|
-
});
|
|
6815
|
+
const settingsManager = buildSettingsManager(opts, models, opts.projectTrusted === true);
|
|
6242
6816
|
return {
|
|
6243
6817
|
settingsManager,
|
|
6244
6818
|
resourceLoaderOptions: { extensionFactories },
|
|
6245
6819
|
extensionNames: extensionFactories.map((e) => e.name),
|
|
6246
|
-
tools:
|
|
6820
|
+
tools: SDK_TOOLS
|
|
6247
6821
|
};
|
|
6248
6822
|
}
|
|
6249
6823
|
function getSessionDir(cwd, agentDir) {
|
|
6250
|
-
const resolvedCwd =
|
|
6824
|
+
const resolvedCwd = resolve2(cwd);
|
|
6251
6825
|
const safePath = `--${resolvedCwd.replace(/^[/\\]/, "").replace(/[/\\:]/g, "-")}--`;
|
|
6252
|
-
return
|
|
6826
|
+
return join3(resolve2(agentDir), "sessions", safePath);
|
|
6253
6827
|
}
|
|
6254
6828
|
async function createSessionManager(cwd, sessionDir, sessionId, api = SessionManager) {
|
|
6255
6829
|
if (sessionId) {
|
|
@@ -6271,7 +6845,9 @@ async function launchPi(opts) {
|
|
|
6271
6845
|
const sm = await createSessionManager(cwd, sessionDir, opts.session);
|
|
6272
6846
|
const sessionId = sm.getSessionId();
|
|
6273
6847
|
const scopeIds = catalogModelsFor(opts).map((m) => m.id);
|
|
6274
|
-
const
|
|
6848
|
+
const projectTrusted = await decideProjectTrustInteractive(cwd, opts.agentDir);
|
|
6849
|
+
const { resourceLoaderOptions, tools } = buildRuntimeOptions({ ...opts, projectTrusted }, sessionId);
|
|
6850
|
+
const launchTrust = { cwd, trusted: projectTrusted };
|
|
6275
6851
|
const createRuntime = async ({
|
|
6276
6852
|
cwd: rtCwd,
|
|
6277
6853
|
sessionManager,
|
|
@@ -6280,7 +6856,7 @@ async function launchPi(opts) {
|
|
|
6280
6856
|
const services = await createAgentSessionServices({
|
|
6281
6857
|
cwd: rtCwd,
|
|
6282
6858
|
agentDir: opts.agentDir,
|
|
6283
|
-
settingsManager,
|
|
6859
|
+
settingsManager: settingsForRuntime(opts, rtCwd, launchTrust),
|
|
6284
6860
|
resourceLoaderOptions
|
|
6285
6861
|
});
|
|
6286
6862
|
const scopedModels = resolveFreePiScope(services.modelRuntime, PROVIDER_NAME, scopeIds);
|
|
@@ -6311,7 +6887,7 @@ async function launchPi(opts) {
|
|
|
6311
6887
|
}
|
|
6312
6888
|
|
|
6313
6889
|
// src/prompt.ts
|
|
6314
|
-
import { createInterface } from "node:readline/promises";
|
|
6890
|
+
import { createInterface as createInterface2 } from "node:readline/promises";
|
|
6315
6891
|
|
|
6316
6892
|
// src/consent.ts
|
|
6317
6893
|
var CONSENT_TEXT = `
|
|
@@ -6361,7 +6937,7 @@ async function promptConsentInteractive() {
|
|
|
6361
6937
|
stdinIsTTY: process.stdin.isTTY === true,
|
|
6362
6938
|
log: (message) => console.log(message),
|
|
6363
6939
|
askLine: async (prompt) => {
|
|
6364
|
-
const rl =
|
|
6940
|
+
const rl = createInterface2({ input: process.stdin, output: process.stdout });
|
|
6365
6941
|
try {
|
|
6366
6942
|
return await rl.question(prompt);
|
|
6367
6943
|
} finally {
|
|
@@ -6496,12 +7072,12 @@ ${INTRO_TEXT}
|
|
|
6496
7072
|
}
|
|
6497
7073
|
|
|
6498
7074
|
// src/whats-new.ts
|
|
6499
|
-
import { existsSync, mkdirSync, readFileSync as readFileSync2, writeFileSync } from "node:fs";
|
|
6500
|
-
import { dirname as
|
|
7075
|
+
import { existsSync as existsSync2, mkdirSync, readFileSync as readFileSync2, writeFileSync } from "node:fs";
|
|
7076
|
+
import { dirname as dirname3 } from "node:path";
|
|
6501
7077
|
function maybeWhatsNew(cliVersion, agentDir) {
|
|
6502
7078
|
try {
|
|
6503
7079
|
const path = getLastSeenVersionPath(agentDir);
|
|
6504
|
-
const lastSeen =
|
|
7080
|
+
const lastSeen = existsSync2(path) ? readFileSync2(path, "utf-8").trim() : undefined;
|
|
6505
7081
|
writeLastSeen(path, cliVersion);
|
|
6506
7082
|
if (lastSeen === undefined)
|
|
6507
7083
|
return;
|
|
@@ -6517,7 +7093,7 @@ function maybeWhatsNew(cliVersion, agentDir) {
|
|
|
6517
7093
|
}
|
|
6518
7094
|
}
|
|
6519
7095
|
function writeLastSeen(path, version) {
|
|
6520
|
-
mkdirSync(
|
|
7096
|
+
mkdirSync(dirname3(path), { recursive: true });
|
|
6521
7097
|
writeFileSync(path, version, "utf-8");
|
|
6522
7098
|
}
|
|
6523
7099
|
|
|
@@ -6706,6 +7282,15 @@ ${HELP_TEXT}`);
|
|
|
6706
7282
|
});
|
|
6707
7283
|
process.exit(logoutCode);
|
|
6708
7284
|
}
|
|
7285
|
+
if (command.kind === "mcp") {
|
|
7286
|
+
const mcpCode = await runFreePiMcp(command.args, {
|
|
7287
|
+
cwd: process.cwd(),
|
|
7288
|
+
agentDir: getFreePiAgentDir(),
|
|
7289
|
+
log: (line) => console.log(line),
|
|
7290
|
+
error: (line) => console.error(line)
|
|
7291
|
+
});
|
|
7292
|
+
process.exit(mcpCode);
|
|
7293
|
+
}
|
|
6709
7294
|
const agentDir = getFreePiAgentDir();
|
|
6710
7295
|
const code = await run({
|
|
6711
7296
|
baseUrl: resolveBaseUrl(),
|