pi-acp 0.0.25 → 0.0.27

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  # pi-acp
2
2
 
3
- ACP ([Agent Client Protocol](https://agentclientprotocol.com/overview/introduction)) adapter for [`pi`](https://github.com/badlogic/pi-mono/tree/main/packages/coding-agent) coding agent (fka shitty coding agent).
3
+ ACP ([Agent Client Protocol](https://agentclientprotocol.com/overview/introduction)) adapter for [`pi`](https://github.com/earendil-works/pi) coding agent (fka shitty coding agent).
4
4
 
5
5
  `pi-acp` communicates **ACP JSON-RPC 2.0 over stdio** to an ACP client (e.g. Zed editor) and spawns `pi --mode rpc`, bridging requests/events between the two.
6
6
 
@@ -34,7 +34,7 @@ Expect some minor breaking changes.
34
34
  Make sure pi is installed
35
35
 
36
36
  ```bash
37
- npm install -g @mariozechner/pi-coding-agent
37
+ npm install -g @earendil-works/pi-coding-agent
38
38
  ```
39
39
 
40
40
  - Node.js 22+
@@ -109,6 +109,27 @@ Point your ACP client to the built `dist/index.js`:
109
109
  }
110
110
  ```
111
111
 
112
+ ### Environment variables
113
+
114
+ - `PI_ACP_ENABLE_EMBEDDED_CONTEXT=true` advertises ACP `promptCapabilities.embeddedContext` support to the client.
115
+ - Default: unset/any other value means `false`.
116
+ - When disabled, compliant ACP clients should avoid sending embedded `resource` blocks. If they send them anyway, `pi-acp` still degrades gracefully by converting them into plain-text prompt context.
117
+
118
+ You can add the environment variable in the Zed settings with:
119
+
120
+ ```json
121
+ "agent_servers": {
122
+ "pi": {
123
+ "type": "custom",
124
+ "command": "node",
125
+ "args": ["/path/to/pi-acp/dist/index.js"],
126
+ "env": {
127
+ "PI_ACP_ENABLE_EMBEDDED_CONTEXT": "true",
128
+ }
129
+ }
130
+ }
131
+ ```
132
+
112
133
  ### Slash commands
113
134
 
114
135
  `pi-acp` supports slash commands:
@@ -174,7 +195,7 @@ Project layout:
174
195
  ## Limitations
175
196
 
176
197
  - No ACP filesystem delegation (`fs/*`) and no ACP terminal delegation (`terminal/*`). pi reads/writes and executes locally.
177
- - MCP servers are accepted in ACP params and stored in session state, but not wired through to pi (see [why](https://mariozechner.at/posts/2025-11-02-what-if-you-dont-need-mcp/)). If you use [pi MCP adapter](https://github.com/nicobailon/pi-mcp-adapter) it will be available in the ACP client.
198
+ - MCP servers are accepted in ACP params and stored in session state, but not wired through to pi in this adapter. If you use [pi MCP adapter](https://github.com/nicobailon/pi-mcp-adapter) it will be available in the ACP client.
178
199
  - Assistant streaming is currently sent as `agent_message_chunk` (no separate thought stream).
179
200
  - Queue is implemented client-side and should work like pi's `one-at-a-time`
180
201
  - ~~ACP clients don't yet suport session history, but ACP sessions from `pi-acp` can be `/resume`d in pi directly~~
package/dist/index.js CHANGED
@@ -171,10 +171,10 @@ var PiRpcProcess = class _PiRpcProcess {
171
171
  shell: shouldUseShellForPiCommand(cmd)
172
172
  });
173
173
  try {
174
- await new Promise((resolve3, reject) => {
174
+ await new Promise((resolve4, reject) => {
175
175
  const onSpawn = () => {
176
176
  cleanup();
177
- resolve3();
177
+ resolve4();
178
178
  };
179
179
  const onError = (err) => {
180
180
  cleanup();
@@ -191,7 +191,7 @@ var PiRpcProcess = class _PiRpcProcess {
191
191
  const code = typeof e?.code === "string" ? e.code : void 0;
192
192
  if (code === "ENOENT") {
193
193
  throw new PiRpcSpawnError(
194
- `Could not start pi: executable not found (command: ${cmd}). Pi needs to be installed before it can run in ACP clients. Install it via \`npm install -g @mariozechner/pi-coding-agent\` or ensure \`pi\` is on your PATH. Then try again.`,
194
+ `Could not start pi: executable not found (command: ${cmd}). Pi needs to be installed before it can run in ACP clients. Install it via \`npm install -g @earendil-works/pi-coding-agent\` or ensure \`pi\` is on your PATH. Then try again.`,
195
195
  { code, cause: e }
196
196
  );
197
197
  }
@@ -313,8 +313,8 @@ var PiRpcProcess = class _PiRpcProcess {
313
313
  const id = crypto.randomUUID();
314
314
  const withId = { ...cmd, id };
315
315
  const line = JSON.stringify(withId) + "\n";
316
- return new Promise((resolve3, reject) => {
317
- this.pending.set(id, { resolve: resolve3, reject });
316
+ return new Promise((resolve4, reject) => {
317
+ this.pending.set(id, { resolve: resolve4, reject });
318
318
  try {
319
319
  this.child.stdin.write(line, (err) => {
320
320
  if (err) {
@@ -383,6 +383,12 @@ var SessionStore = class {
383
383
  };
384
384
  saveFile(this.path, db);
385
385
  }
386
+ delete(sessionId) {
387
+ const db = loadFile(this.path);
388
+ if (!db.sessions[sessionId]) return;
389
+ delete db.sessions[sessionId];
390
+ saveFile(this.path, db);
391
+ }
386
392
  };
387
393
 
388
394
  // src/acp/translate/pi-tools.ts
@@ -654,7 +660,8 @@ var PiAcpSession = class {
654
660
  cwd;
655
661
  mcpServers;
656
662
  startupInfo = null;
657
- startupInfoSent = false;
663
+ startupInfoSentOutOfTurn = false;
664
+ startupInfoSentInPrompt = false;
658
665
  proc;
659
666
  conn;
660
667
  fileCommands;
@@ -689,6 +696,8 @@ var PiAcpSession = class {
689
696
  }
690
697
  setStartupInfo(text) {
691
698
  this.startupInfo = text;
699
+ this.startupInfoSentOutOfTurn = false;
700
+ this.startupInfoSentInPrompt = false;
692
701
  }
693
702
  /**
694
703
  * Best-effort attempt to send startup info outside of a prompt turn.
@@ -696,17 +705,26 @@ var PiAcpSession = class {
696
705
  * callers can invoke this shortly after session/new returns.
697
706
  */
698
707
  sendStartupInfoIfPending() {
699
- if (this.startupInfoSent || !this.startupInfo) return;
700
- this.startupInfoSent = true;
708
+ if (this.startupInfoSentOutOfTurn || !this.startupInfo) return;
709
+ this.startupInfoSentOutOfTurn = true;
710
+ this.emit({
711
+ sessionUpdate: "agent_message_chunk",
712
+ content: { type: "text", text: this.startupInfo }
713
+ });
714
+ }
715
+ sendStartupInfoOnFirstPromptIfPending() {
716
+ if (this.startupInfoSentInPrompt || !this.startupInfo) return;
717
+ this.startupInfoSentInPrompt = true;
701
718
  this.emit({
702
719
  sessionUpdate: "agent_message_chunk",
703
720
  content: { type: "text", text: this.startupInfo }
704
721
  });
705
722
  }
706
723
  async prompt(message, images = []) {
724
+ this.sendStartupInfoOnFirstPromptIfPending();
707
725
  const expandedMessage = expandSlashCommand(message, this.fileCommands);
708
- const turnPromise = new Promise((resolve3, reject) => {
709
- const queued = { message: expandedMessage, images, resolve: resolve3, reject };
726
+ const turnPromise = new Promise((resolve4, reject) => {
727
+ const queued = { message: expandedMessage, images, resolve: resolve4, reject };
710
728
  if (this.pendingTurn) {
711
729
  this.turnQueue.push(queued);
712
730
  this.emit({
@@ -962,7 +980,10 @@ var PiAcpSession = class {
962
980
  case "auto_compaction_start": {
963
981
  this.emit({
964
982
  sessionUpdate: "agent_message_chunk",
965
- content: { type: "text", text: "Context nearing limit, running automatic compaction..." }
983
+ content: {
984
+ type: "text",
985
+ text: "Context nearing limit, running automatic compaction..."
986
+ }
966
987
  });
967
988
  break;
968
989
  }
@@ -1036,16 +1057,31 @@ function toToolKind(toolName) {
1036
1057
  }
1037
1058
 
1038
1059
  // src/acp/pi-sessions.ts
1039
- import { readdirSync as readdirSync2, readFileSync as readFileSync4, statSync, openSync, readSync, closeSync } from "fs";
1060
+ import { readdirSync as readdirSync2, readFileSync as readFileSync4, statSync, openSync, readSync, closeSync, existsSync as existsSync2 } from "fs";
1040
1061
  import { homedir as homedir3 } from "os";
1041
- import { join as join3 } from "path";
1062
+ import { join as join3, resolve as resolve2, isAbsolute as isAbsolute2 } from "path";
1042
1063
  var DEFAULT_TAIL_BYTES = 256 * 1024;
1043
1064
  var DEFAULT_HEAD_BYTES = 64 * 1024;
1044
1065
  function getPiAgentDir() {
1045
- return process.env.PI_CODING_AGENT_DIR ?? join3(homedir3(), ".pi", "agent");
1066
+ return process.env.PI_CODING_AGENT_DIR ? resolve2(process.env.PI_CODING_AGENT_DIR) : join3(homedir3(), ".pi", "agent");
1067
+ }
1068
+ function readSessionDirFromSettings(agentDir) {
1069
+ const settingsPath = join3(agentDir, "settings.json");
1070
+ try {
1071
+ if (!existsSync2(settingsPath)) return null;
1072
+ const raw = readFileSync4(settingsPath, "utf8");
1073
+ const data = JSON.parse(raw);
1074
+ if (!data || typeof data !== "object" || Array.isArray(data)) return null;
1075
+ const sessionDir = data.sessionDir;
1076
+ if (typeof sessionDir !== "string" || !sessionDir.trim()) return null;
1077
+ return isAbsolute2(sessionDir) ? sessionDir : resolve2(agentDir, sessionDir);
1078
+ } catch {
1079
+ return null;
1080
+ }
1046
1081
  }
1047
1082
  function getPiSessionsDir() {
1048
- return join3(getPiAgentDir(), "sessions");
1083
+ const agentDir = getPiAgentDir();
1084
+ return readSessionDirFromSettings(agentDir) ?? join3(agentDir, "sessions");
1049
1085
  }
1050
1086
  function walkJsonlFiles(dir, out) {
1051
1087
  let entries;
@@ -1339,9 +1375,9 @@ ${r.text}`;
1339
1375
  }
1340
1376
 
1341
1377
  // src/acp/pi-settings.ts
1342
- import { existsSync as existsSync2, readFileSync as readFileSync5 } from "fs";
1378
+ import { existsSync as existsSync3, readFileSync as readFileSync5 } from "fs";
1343
1379
  import { homedir as homedir4 } from "os";
1344
- import { join as join4, resolve as resolve2 } from "path";
1380
+ import { join as join4, resolve as resolve3 } from "path";
1345
1381
  function isObject(x) {
1346
1382
  return Boolean(x) && typeof x === "object" && !Array.isArray(x);
1347
1383
  }
@@ -1356,7 +1392,7 @@ function deepMerge(a, b) {
1356
1392
  }
1357
1393
  function readJsonFile(path) {
1358
1394
  try {
1359
- if (!existsSync2(path)) return {};
1395
+ if (!existsSync3(path)) return {};
1360
1396
  const raw = readFileSync5(path, "utf-8");
1361
1397
  const data = JSON.parse(raw);
1362
1398
  return isObject(data) ? data : {};
@@ -1366,13 +1402,13 @@ function readJsonFile(path) {
1366
1402
  }
1367
1403
  function getMergedSettings(cwd) {
1368
1404
  const globalSettingsPath = join4(getAgentDir(), "settings.json");
1369
- const projectSettingsPath = resolve2(cwd, ".pi", "settings.json");
1405
+ const projectSettingsPath = resolve3(cwd, ".pi", "settings.json");
1370
1406
  const global = readJsonFile(globalSettingsPath);
1371
1407
  const project = readJsonFile(projectSettingsPath);
1372
1408
  return deepMerge(global, project);
1373
1409
  }
1374
1410
  function getAgentDir() {
1375
- return process.env.PI_CODING_AGENT_DIR ? resolve2(process.env.PI_CODING_AGENT_DIR) : join4(homedir4(), ".pi", "agent");
1411
+ return process.env.PI_CODING_AGENT_DIR ? resolve3(process.env.PI_CODING_AGENT_DIR) : join4(homedir4(), ".pi", "agent");
1376
1412
  }
1377
1413
  function getEnableSkillCommands(cwd) {
1378
1414
  const merged = getMergedSettings(cwd);
@@ -1422,81 +1458,10 @@ function toAvailableCommandsFromPiGetCommands(data, opts) {
1422
1458
  }
1423
1459
 
1424
1460
  // src/acp/agent.ts
1425
- import { isAbsolute as isAbsolute2 } from "path";
1426
- import { existsSync as existsSync4, readFileSync as readFileSync7, realpathSync, readdirSync as readdirSync3, statSync as statSync2 } from "fs";
1427
- import { join as join6, dirname as dirname2, basename } from "path";
1461
+ import { isAbsolute as isAbsolute3 } from "path";
1462
+ import { existsSync as existsSync4, readFileSync as readFileSync6, realpathSync, readdirSync as readdirSync3, statSync as statSync2, unlinkSync } from "fs";
1463
+ import { join as join5, dirname as dirname2, basename } from "path";
1428
1464
  import { spawnSync } from "child_process";
1429
-
1430
- // src/pi-auth/status.ts
1431
- import { existsSync as existsSync3, readFileSync as readFileSync6 } from "fs";
1432
- import { homedir as homedir5 } from "os";
1433
- import { join as join5 } from "path";
1434
- function safeReadJson(path) {
1435
- try {
1436
- if (!existsSync3(path)) return null;
1437
- const raw = readFileSync6(path, "utf-8");
1438
- if (!raw.trim()) return null;
1439
- return JSON.parse(raw);
1440
- } catch {
1441
- return null;
1442
- }
1443
- }
1444
- function getPiAgentDir2() {
1445
- const envDir = process.env.PI_CODING_AGENT_DIR;
1446
- if (envDir) {
1447
- if (envDir === "~") return homedir5();
1448
- if (envDir.startsWith("~/")) return homedir5() + envDir.slice(1);
1449
- return envDir;
1450
- }
1451
- return join5(homedir5(), ".pi", "agent");
1452
- }
1453
- function hasAnyPiAuthConfigured() {
1454
- const agentDir = getPiAgentDir2();
1455
- const authPath = join5(agentDir, "auth.json");
1456
- const auth = safeReadJson(authPath);
1457
- if (auth && typeof auth === "object" && Object.keys(auth).length > 0) return true;
1458
- const modelsPath = join5(agentDir, "models.json");
1459
- const models = safeReadJson(modelsPath);
1460
- const providers = models?.providers;
1461
- if (providers && typeof providers === "object") {
1462
- for (const p of Object.values(providers)) {
1463
- if (p && typeof p === "object" && typeof p.apiKey === "string" && p.apiKey.trim()) {
1464
- return true;
1465
- }
1466
- }
1467
- }
1468
- const envVars = [
1469
- "OPENAI_API_KEY",
1470
- "AZURE_OPENAI_API_KEY",
1471
- "GEMINI_API_KEY",
1472
- "GROQ_API_KEY",
1473
- "CEREBRAS_API_KEY",
1474
- "XAI_API_KEY",
1475
- "OPENROUTER_API_KEY",
1476
- "AI_GATEWAY_API_KEY",
1477
- "ZAI_API_KEY",
1478
- "MISTRAL_API_KEY",
1479
- "MINIMAX_API_KEY",
1480
- "MINIMAX_CN_API_KEY",
1481
- "HF_TOKEN",
1482
- "OPENCODE_API_KEY",
1483
- "KIMI_API_KEY",
1484
- // Copilot/github
1485
- "COPILOT_GITHUB_TOKEN",
1486
- "GH_TOKEN",
1487
- "GITHUB_TOKEN",
1488
- // Anthropic oauth
1489
- "ANTHROPIC_OAUTH_TOKEN",
1490
- "ANTHROPIC_API_KEY"
1491
- ];
1492
- for (const k of envVars) {
1493
- const v = process.env[k];
1494
- if (typeof v === "string" && v.trim()) return true;
1495
- }
1496
- return false;
1497
- }
1498
-
1499
- // src/acp/agent.ts
1500
1465
  import { fileURLToPath } from "url";
1501
1466
  function builtinAvailableCommands() {
1502
1467
  return [
@@ -1563,6 +1528,17 @@ var PiAcpAgent = class {
1563
1528
  this.conn = conn;
1564
1529
  void _config;
1565
1530
  }
1531
+ cleanupFailedNewSession(sessionId, state) {
1532
+ this.sessions.close(sessionId);
1533
+ const sessionFile = typeof state?.sessionFile === "string" && state.sessionFile.trim() ? state.sessionFile : this.store.get(sessionId)?.sessionFile;
1534
+ if (typeof sessionFile === "string" && sessionFile.trim()) {
1535
+ try {
1536
+ if (existsSync4(sessionFile)) unlinkSync(sessionFile);
1537
+ } catch {
1538
+ }
1539
+ }
1540
+ this.store.delete(sessionId);
1541
+ }
1566
1542
  async initialize(params) {
1567
1543
  const supportedVersion = 1;
1568
1544
  const requested = params.protocolVersion;
@@ -1584,7 +1560,7 @@ var PiAcpAgent = class {
1584
1560
  promptCapabilities: {
1585
1561
  image: true,
1586
1562
  audio: false,
1587
- embeddedContext: false
1563
+ embeddedContext: process.env.PI_ACP_ENABLE_EMBEDDED_CONTEXT === "true"
1588
1564
  },
1589
1565
  sessionCapabilities: {
1590
1566
  // **UNSTABLE** ACP capability used by Zed's codex-acp adapter.
@@ -1595,16 +1571,10 @@ var PiAcpAgent = class {
1595
1571
  };
1596
1572
  }
1597
1573
  async newSession(params) {
1598
- if (!isAbsolute2(params.cwd)) {
1574
+ if (!isAbsolute3(params.cwd)) {
1599
1575
  throw RequestError3.invalidParams(`cwd must be an absolute path: ${params.cwd}`);
1600
1576
  }
1601
1577
  this.lastSessionCwd = params.cwd;
1602
- if (!hasAnyPiAuthConfigured()) {
1603
- throw RequestError3.authRequired(
1604
- { authMethods: getAuthMethods() },
1605
- "Configure an API key or log in with an OAuth provider."
1606
- );
1607
- }
1608
1578
  const fileCommands = loadSlashCommands(params.cwd);
1609
1579
  const enableSkillCommands = getEnableSkillCommands(params.cwd);
1610
1580
  const session = await this.sessions.create({
@@ -1616,24 +1586,41 @@ var PiAcpAgent = class {
1616
1586
  });
1617
1587
  let state = null;
1618
1588
  let availableModels = null;
1589
+ let stateErr = null;
1590
+ let availableModelsErr = null;
1619
1591
  await Promise.all([
1620
1592
  session.proc.getState().then((s) => {
1621
1593
  state = s;
1622
- }).catch(() => {
1594
+ }).catch((err) => {
1595
+ stateErr = err;
1623
1596
  state = null;
1624
1597
  }),
1625
1598
  session.proc.getAvailableModels().then((m) => {
1626
1599
  availableModels = m;
1627
- }).catch(() => {
1600
+ }).catch((err) => {
1601
+ availableModelsErr = err;
1628
1602
  availableModels = null;
1629
1603
  })
1630
1604
  ]);
1605
+ const availableModelsAuthErr = maybeAuthRequiredError(availableModelsErr);
1606
+ if (availableModelsAuthErr) {
1607
+ this.cleanupFailedNewSession(session.sessionId, state);
1608
+ throw availableModelsAuthErr;
1609
+ }
1610
+ if (availableModelsErr) {
1611
+ this.cleanupFailedNewSession(session.sessionId, state);
1612
+ throw RequestError3.internalError({}, String(availableModelsErr?.message ?? availableModelsErr));
1613
+ }
1631
1614
  const rawModelsCount = Array.isArray(availableModels?.models) ? availableModels.models.length : 0;
1632
1615
  if (rawModelsCount === 0) {
1633
- try {
1634
- session.proc.dispose?.();
1635
- } catch {
1636
- }
1616
+ this.cleanupFailedNewSession(session.sessionId, state);
1617
+ throw RequestError3.authRequired(
1618
+ { authMethods: getAuthMethods() },
1619
+ "Configure an API key or log in with an OAuth provider."
1620
+ );
1621
+ }
1622
+ if (stateErr && maybeAuthRequiredError(stateErr)) {
1623
+ this.cleanupFailedNewSession(session.sessionId, state);
1637
1624
  throw RequestError3.authRequired(
1638
1625
  { authMethods: getAuthMethods() },
1639
1626
  "Configure an API key or log in with an OAuth provider."
@@ -1885,7 +1872,7 @@ ${JSON.stringify(stats, null, 2)}`;
1885
1872
  if (piPath) {
1886
1873
  const resolved = realpathSync(piPath);
1887
1874
  const pkgRoot = dirname2(dirname2(resolved));
1888
- const p = join6(pkgRoot, "CHANGELOG.md");
1875
+ const p = join5(pkgRoot, "CHANGELOG.md");
1889
1876
  if (existsSync4(p)) return p;
1890
1877
  }
1891
1878
  } catch {
@@ -1894,7 +1881,7 @@ ${JSON.stringify(stats, null, 2)}`;
1894
1881
  const npmRoot = spawnSync("npm", ["root", "-g"], { encoding: "utf-8" });
1895
1882
  const root = String(npmRoot.stdout ?? "").trim();
1896
1883
  if (root) {
1897
- const p = join6(root, "@mariozechner", "pi-coding-agent", "CHANGELOG.md");
1884
+ const p = join5(root, "@earendil-works", "pi-coding-agent", "CHANGELOG.md");
1898
1885
  if (existsSync4(p)) return p;
1899
1886
  }
1900
1887
  } catch {
@@ -1914,7 +1901,7 @@ ${JSON.stringify(stats, null, 2)}`;
1914
1901
  }
1915
1902
  let text = "";
1916
1903
  try {
1917
- text = readFileSync7(changelogPath, "utf-8");
1904
+ text = readFileSync6(changelogPath, "utf-8");
1918
1905
  } catch (e) {
1919
1906
  await this.conn.sessionUpdate({
1920
1907
  sessionId: session.sessionId,
@@ -1954,7 +1941,7 @@ ${JSON.stringify(stats, null, 2)}`;
1954
1941
  return { stopReason: "end_turn" };
1955
1942
  }
1956
1943
  try {
1957
- const raw = readFileSync7(sessionFile, "utf-8");
1944
+ const raw = readFileSync6(sessionFile, "utf-8");
1958
1945
  if (raw.trim().length === 0) {
1959
1946
  await this.conn.sessionUpdate({
1960
1947
  sessionId: session.sessionId,
@@ -1982,7 +1969,7 @@ ${JSON.stringify(stats, null, 2)}`;
1982
1969
  return { stopReason: "end_turn" };
1983
1970
  }
1984
1971
  const safeSessionId = session.sessionId.replace(/[^a-zA-Z0-9_-]/g, "_");
1985
- const outputPath = join6(session.cwd, `pi-session-${safeSessionId}.html`);
1972
+ const outputPath = join5(session.cwd, `pi-session-${safeSessionId}.html`);
1986
1973
  let resultPath = "";
1987
1974
  try {
1988
1975
  const result2 = await session.proc.exportHtml(outputPath);
@@ -2089,7 +2076,7 @@ ${JSON.stringify(stats, null, 2)}`;
2089
2076
  return { sessions, nextCursor, _meta: {} };
2090
2077
  }
2091
2078
  async loadSession(params) {
2092
- if (!isAbsolute2(params.cwd)) {
2079
+ if (!isAbsolute3(params.cwd)) {
2093
2080
  throw RequestError3.invalidParams(`cwd must be an absolute path: ${params.cwd}`);
2094
2081
  }
2095
2082
  this.sessions.close(params.sessionId);
@@ -2349,16 +2336,19 @@ function compareSemver(a, b) {
2349
2336
  function buildUpdateNotice() {
2350
2337
  try {
2351
2338
  const piVersion = spawnSync("pi", ["--version"], { encoding: "utf-8" });
2352
- const installed = (String(piVersion.stdout ?? "").trim() || String(piVersion.stderr ?? "").trim()).replace(/^v/i, "");
2339
+ const installed = (String(piVersion.stdout ?? "").trim() || String(piVersion.stderr ?? "").trim()).replace(
2340
+ /^v/i,
2341
+ ""
2342
+ );
2353
2343
  if (!installed || !isSemver(installed)) return null;
2354
- const latestRes = spawnSync("npm", ["view", "@mariozechner/pi-coding-agent", "version"], {
2344
+ const latestRes = spawnSync("npm", ["view", "@earendil-works/pi-coding-agent", "version"], {
2355
2345
  encoding: "utf-8",
2356
2346
  timeout: 800
2357
2347
  });
2358
2348
  const latest = String(latestRes.stdout ?? "").trim().replace(/^v/i, "");
2359
2349
  if (!latest || !isSemver(latest)) return null;
2360
2350
  if (compareSemver(latest, installed) <= 0) return null;
2361
- return `New version available: v${latest} (installed v${installed}). Run: \`npm i -g @mariozechner/pi-coding-agent\``;
2351
+ return `New version available: v${latest} (installed v${installed}). Run: \`npm i -g @earendil-works/pi-coding-agent\``;
2362
2352
  } catch {
2363
2353
  return null;
2364
2354
  }
@@ -2368,7 +2358,10 @@ function buildStartupInfo(opts) {
2368
2358
  const md = [];
2369
2359
  try {
2370
2360
  const piVersion = spawnSync("pi", ["--version"], { encoding: "utf-8" });
2371
- const installed = (String(piVersion.stdout ?? "").trim() || String(piVersion.stderr ?? "").trim()).replace(/^v/i, "");
2361
+ const installed = (String(piVersion.stdout ?? "").trim() || String(piVersion.stderr ?? "").trim()).replace(
2362
+ /^v/i,
2363
+ ""
2364
+ );
2372
2365
  if (installed) {
2373
2366
  md.push(`pi v${installed}`);
2374
2367
  md.push("---");
@@ -2384,14 +2377,14 @@ function buildStartupInfo(opts) {
2384
2377
  md.push("");
2385
2378
  };
2386
2379
  const contextItems = [];
2387
- const contextPath = join6(opts.cwd, "AGENTS.md");
2380
+ const contextPath = join5(opts.cwd, "AGENTS.md");
2388
2381
  if (existsSync4(contextPath)) contextItems.push(contextPath);
2389
2382
  addSection("Context", contextItems);
2390
2383
  const skillsItems = [];
2391
2384
  const pushSkillFromRoot = (root) => {
2392
2385
  try {
2393
2386
  for (const e of readdirSync3(root)) {
2394
- const p = join6(root, e);
2387
+ const p = join5(root, e);
2395
2388
  try {
2396
2389
  const st = statSync2(p);
2397
2390
  if (st.isFile() && e.toLowerCase().endsWith(".md")) {
@@ -2411,7 +2404,7 @@ function buildStartupInfo(opts) {
2411
2404
  }
2412
2405
  for (const name of entries) {
2413
2406
  if (name === "node_modules" || name === ".git") continue;
2414
- const p = join6(dir, name);
2407
+ const p = join5(dir, name);
2415
2408
  let st;
2416
2409
  try {
2417
2410
  st = statSync2(p);
@@ -2428,15 +2421,15 @@ function buildStartupInfo(opts) {
2428
2421
  } catch {
2429
2422
  }
2430
2423
  };
2431
- const globalSkillsDir = join6(getAgentDir(), "skills");
2424
+ const globalSkillsDir = join5(getAgentDir(), "skills");
2432
2425
  pushSkillFromRoot(globalSkillsDir);
2433
- const legacyAgentsSkillsDir = join6(process.env.HOME ?? "", ".agents", "skills");
2426
+ const legacyAgentsSkillsDir = join5(process.env.HOME ?? "", ".agents", "skills");
2434
2427
  pushSkillFromRoot(legacyAgentsSkillsDir);
2435
- const projectSkillsDir = join6(opts.cwd, ".pi", "skills");
2428
+ const projectSkillsDir = join5(opts.cwd, ".pi", "skills");
2436
2429
  pushSkillFromRoot(projectSkillsDir);
2437
2430
  addSection("Skills", skillsItems);
2438
2431
  const promptsItems = [];
2439
- const promptsDir = join6(process.env.HOME ?? "", ".pi", "agent", "prompts");
2432
+ const promptsDir = join5(process.env.HOME ?? "", ".pi", "agent", "prompts");
2440
2433
  try {
2441
2434
  const prompts = readdirSync3(promptsDir).filter((f) => f.endsWith(".md"));
2442
2435
  for (const f of prompts) promptsItems.push(`/${basename(f, ".md")}`);
@@ -2444,15 +2437,15 @@ function buildStartupInfo(opts) {
2444
2437
  }
2445
2438
  addSection("Prompts", promptsItems);
2446
2439
  const extItems = [];
2447
- const extDir = join6(process.env.HOME ?? "", ".pi", "agent", "extensions");
2440
+ const extDir = join5(process.env.HOME ?? "", ".pi", "agent", "extensions");
2448
2441
  try {
2449
2442
  const exts = readdirSync3(extDir).filter((f) => f.endsWith(".ts") || f.endsWith(".js"));
2450
- for (const f of exts) extItems.push(join6(extDir, f));
2443
+ for (const f of exts) extItems.push(join5(extDir, f));
2451
2444
  } catch {
2452
2445
  }
2453
2446
  try {
2454
- const settingsPath = join6(process.env.HOME ?? "", ".pi", "agent", "settings.json");
2455
- const settings = JSON.parse(readFileSync7(settingsPath, "utf-8"));
2447
+ const settingsPath = join5(process.env.HOME ?? "", ".pi", "agent", "settings.json");
2448
+ const settings = JSON.parse(readFileSync6(settingsPath, "utf-8"));
2456
2449
  const pkgs = Array.isArray(settings?.packages) ? settings.packages : [];
2457
2450
  for (const pkg2 of pkgs) {
2458
2451
  const s = String(pkg2);
@@ -2477,9 +2470,9 @@ function readNearestPackageJson(metaUrl) {
2477
2470
  try {
2478
2471
  let dir = dirname2(fileURLToPath(metaUrl));
2479
2472
  for (let i = 0; i < 6; i++) {
2480
- const p = join6(dir, "package.json");
2473
+ const p = join5(dir, "package.json");
2481
2474
  if (existsSync4(p)) {
2482
- const json = JSON.parse(readFileSync7(p, "utf-8"));
2475
+ const json = JSON.parse(readFileSync6(p, "utf-8"));
2483
2476
  return { name: json?.name, version: json?.version };
2484
2477
  }
2485
2478
  dir = dirname2(dir);
@@ -2500,7 +2493,7 @@ if (process.argv.includes("--terminal-login")) {
2500
2493
  });
2501
2494
  if (res.error && res.error.code === "ENOENT") {
2502
2495
  process.stderr.write(
2503
- `pi-acp: could not start pi (command not found: ${cmd}). Install it via \`npm install -g @mariozechner/pi-coding-agent\` or ensure \`pi\` is on your PATH.
2496
+ `pi-acp: could not start pi (command not found: ${cmd}). Install it via \`npm install -g @earendil-works/pi-coding-agent\` or ensure \`pi\` is on your PATH.
2504
2497
  `
2505
2498
  );
2506
2499
  process.exit(1);
@@ -2509,15 +2502,15 @@ if (process.argv.includes("--terminal-login")) {
2509
2502
  }
2510
2503
  var input = new WritableStream({
2511
2504
  write(chunk) {
2512
- return new Promise((resolve3) => {
2513
- if (process.stdout.destroyed || !process.stdout.writable) return resolve3();
2505
+ return new Promise((resolve4) => {
2506
+ if (process.stdout.destroyed || !process.stdout.writable) return resolve4();
2514
2507
  try {
2515
2508
  process.stdout.write(chunk, (err) => {
2516
2509
  void err;
2517
- resolve3();
2510
+ resolve4();
2518
2511
  });
2519
2512
  } catch {
2520
- resolve3();
2513
+ resolve4();
2521
2514
  }
2522
2515
  });
2523
2516
  }