@pikaa-ai/pikaa 0.3.19 → 0.3.20

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/cli.js CHANGED
@@ -3,7 +3,7 @@
3
3
 
4
4
  // src/cli/index.ts
5
5
  import { resolve as resolve21 } from "path";
6
- import { existsSync as existsSync22 } from "fs";
6
+ import { existsSync as existsSync23 } from "fs";
7
7
  import { createInterface } from "readline";
8
8
 
9
9
  // src/auth/store.ts
@@ -235,12 +235,15 @@ class DefaultModelClientSession {
235
235
  };
236
236
  return;
237
237
  }
238
- const messages = [
239
- {
240
- role: "system",
241
- content: params.systemPrompt
242
- }
243
- ];
238
+ const enablePromptCache = params.enablePromptCache ?? this.config.enablePromptCache ?? true;
239
+ const systemMessage = {
240
+ role: "system",
241
+ content: params.systemPrompt
242
+ };
243
+ if (enablePromptCache) {
244
+ systemMessage.cache_control = { type: "ephemeral" };
245
+ }
246
+ const messages = [systemMessage];
244
247
  for (let i = 0;i < params.history.length; i++) {
245
248
  const item = params.history[i];
246
249
  if (item.type === "user_message") {
@@ -324,14 +327,27 @@ class DefaultModelClientSession {
324
327
  temperature: params.temperature ?? 0.2
325
328
  };
326
329
  if (toolsPayload && toolsPayload.length > 0) {
327
- body.tools = toolsPayload;
330
+ if (enablePromptCache && toolsPayload.length > 0) {
331
+ const clonedTools = [...toolsPayload];
332
+ const lastIdx = clonedTools.length - 1;
333
+ if (clonedTools[lastIdx] && typeof clonedTools[lastIdx] === "object") {
334
+ clonedTools[lastIdx] = {
335
+ ...clonedTools[lastIdx],
336
+ cache_control: { type: "ephemeral" }
337
+ };
338
+ }
339
+ body.tools = clonedTools;
340
+ } else {
341
+ body.tools = toolsPayload;
342
+ }
328
343
  body.tool_choice = "auto";
329
344
  }
330
345
  const maxRetries = params.maxRetries ?? this.config.maxRetries ?? 10;
331
346
  let attempt = 0;
332
347
  let response = null;
333
348
  const headers = {
334
- "Content-Type": "application/json"
349
+ "Content-Type": "application/json",
350
+ "anthropic-beta": "prompt-caching-2024-07-25"
335
351
  };
336
352
  if (apiKey) {
337
353
  headers["Authorization"] = `Bearer ${apiKey}`;
@@ -441,7 +457,8 @@ class DefaultModelClientSession {
441
457
  type: "done",
442
458
  inputTokens: usageMetrics.inputTokens,
443
459
  outputTokens: usageMetrics.outputTokens,
444
- totalTokens: usageMetrics.totalTokens
460
+ totalTokens: usageMetrics.totalTokens,
461
+ cachedTokens: usageMetrics.cachedTokens
445
462
  };
446
463
  return;
447
464
  }
@@ -452,10 +469,12 @@ class DefaultModelClientSession {
452
469
  continue;
453
470
  }
454
471
  if (parsed.usage) {
472
+ const cached = parsed.usage.prompt_tokens_details?.cached_tokens ?? parsed.usage.cache_read_input_tokens ?? parsed.usage.cached_content_token_count ?? 0;
455
473
  usageMetrics = {
456
474
  inputTokens: parsed.usage.prompt_tokens,
457
475
  outputTokens: parsed.usage.completion_tokens,
458
- totalTokens: parsed.usage.total_tokens
476
+ totalTokens: parsed.usage.total_tokens,
477
+ cachedTokens: cached > 0 ? cached : undefined
459
478
  };
460
479
  }
461
480
  const choice = parsed.choices?.[0];
@@ -673,6 +692,8 @@ class TurnAbortedError extends GroupyError {
673
692
  }
674
693
 
675
694
  // src/context/compactor.ts
695
+ var DEFAULT_MAX_CONTEXT_TOKENS = 256000;
696
+ var DEFAULT_AUTO_COMPACT_THRESHOLD_TOKENS = 180000;
676
697
  function estimateItemTokens(item) {
677
698
  const text = item.type === "user_message" ? item.content : item.type === "agent_message" ? item.content : item.type === "reasoning" ? item.content : item.type === "function_call" ? JSON.stringify(item.arguments) : item.type === "function_call_output" ? item.output : "";
678
699
  return Math.ceil(text.length / 4) + 4;
@@ -886,80 +907,205 @@ class AgentsMdLoader {
886
907
  var globalAgentsMdLoader = new AgentsMdLoader;
887
908
 
888
909
  // src/context/instructions.ts
889
- function buildSystemPrompt(params) {
890
- const sections = [];
910
+ function wrapXmlTag(tag, content, attrs = {}) {
911
+ const attrStr = Object.entries(attrs).filter(([_, v]) => Boolean(v)).map(([k, v]) => ` ${k}="${v}"`).join("");
912
+ return `<${tag}${attrStr}>
913
+ ${content.trim()}
914
+ </${tag}>`;
915
+ }
916
+ function buildStructuredSystemPrompt(params) {
891
917
  const cwd = params.cwd || process.cwd();
892
918
  const mode = params.collaborationMode || "default";
893
- if (params.basePrompt) {
894
- sections.push(params.basePrompt);
895
- } else {
919
+ const blocks = [];
920
+ let baseContent = params.basePrompt;
921
+ if (!baseContent) {
896
922
  const templateName = params.basePromptTemplate || "base/groupy_prompt.md";
897
- const baseContent = globalPromptLoader.loadTemplate(templateName, {}, cwd);
898
- if (baseContent) {
899
- sections.push(baseContent.trim());
900
- } else {
901
- sections.push("You are Groupy, an expert autonomous AI coding assistant. You think step-by-step, act surgically, and write clean, correct code.");
902
- }
923
+ baseContent = globalPromptLoader.loadTemplate(templateName, {}, cwd) || "You are Groupy, an expert autonomous AI coding assistant. You think step-by-step, act surgically, and write clean, correct code.";
903
924
  }
925
+ blocks.push({
926
+ tag: "system_identity",
927
+ content: wrapXmlTag("system_identity", baseContent),
928
+ cacheable: true
929
+ });
904
930
  if (params.personality) {
905
931
  const personalityContent = globalPromptLoader.loadTemplate(`personalities/${params.personality}.md`, {}, cwd);
906
932
  if (personalityContent) {
907
- sections.push(personalityContent.trim());
933
+ blocks.push({
934
+ tag: "personality",
935
+ content: wrapXmlTag("personality", personalityContent, { kind: params.personality }),
936
+ cacheable: true
937
+ });
908
938
  }
909
939
  }
910
940
  if (params.isOrchestrator) {
911
941
  const orchestratorContent = globalPromptLoader.loadTemplate("agents/orchestrator.md", {}, cwd);
912
942
  if (orchestratorContent) {
913
- sections.push(orchestratorContent.trim());
943
+ blocks.push({
944
+ tag: "orchestrator_guidelines",
945
+ content: wrapXmlTag("orchestrator_guidelines", orchestratorContent),
946
+ cacheable: true
947
+ });
914
948
  }
915
949
  }
916
- const modeTemplate = globalPromptLoader.loadTemplate(`modes/${mode}.md`, {
917
- KNOWN_MODE_NAMES: "default, plan, review"
918
- }, cwd);
950
+ const modeTemplate = globalPromptLoader.loadTemplate(`modes/${mode}.md`, { KNOWN_MODE_NAMES: "default, plan, review" }, cwd);
919
951
  if (modeTemplate) {
920
- sections.push(modeTemplate.trim());
952
+ blocks.push({
953
+ tag: "collaboration_mode",
954
+ content: wrapXmlTag("collaboration_mode", modeTemplate, { name: mode }),
955
+ cacheable: true
956
+ });
921
957
  }
922
958
  if (params.sandboxMode) {
923
- const sandboxTemplate = globalPromptLoader.loadTemplate(`permissions/sandbox_mode/${params.sandboxMode}.md`, {
924
- network_access: params.networkAccess ? "enabled" : "disabled"
925
- }, cwd);
959
+ const sandboxTemplate = globalPromptLoader.loadTemplate(`permissions/sandbox_mode/${params.sandboxMode}.md`, { network_access: params.networkAccess ? "enabled" : "disabled" }, cwd);
926
960
  if (sandboxTemplate) {
927
- sections.push(sandboxTemplate.trim());
961
+ blocks.push({
962
+ tag: "sandbox_policy",
963
+ content: wrapXmlTag("sandbox_policy", sandboxTemplate, { mode: params.sandboxMode }),
964
+ cacheable: true
965
+ });
928
966
  }
929
967
  }
930
968
  if (params.approvalPolicy) {
931
969
  const approvalTemplate = globalPromptLoader.loadTemplate(`permissions/approval_policy/${params.approvalPolicy}.md`, {}, cwd);
932
970
  if (approvalTemplate) {
933
- sections.push(approvalTemplate.trim());
971
+ blocks.push({
972
+ tag: "approval_policy",
973
+ content: wrapXmlTag("approval_policy", approvalTemplate, { policy: params.approvalPolicy }),
974
+ cacheable: true
975
+ });
934
976
  }
935
977
  }
936
978
  const projectInstructions = globalAgentsMdLoader.loadProjectInstructions(cwd);
937
979
  if (projectInstructions) {
938
- sections.push(`## Project Instructions (AGENTS.md)
939
-
940
- ${projectInstructions.content.trim()}`);
980
+ blocks.push({
981
+ tag: "project_instructions",
982
+ content: wrapXmlTag("project_instructions", projectInstructions.content, { source: "AGENTS.md" }),
983
+ cacheable: true
984
+ });
941
985
  }
942
986
  if (params.memoriesPrompt) {
943
- sections.push(params.memoriesPrompt.trim());
987
+ blocks.push({
988
+ tag: "persistent_memories",
989
+ content: wrapXmlTag("persistent_memories", params.memoriesPrompt),
990
+ cacheable: true
991
+ });
944
992
  }
945
993
  if (params.skillsPrompt) {
946
- sections.push(params.skillsPrompt.trim());
994
+ blocks.push({
995
+ tag: "domain_skills",
996
+ content: wrapXmlTag("domain_skills", params.skillsPrompt),
997
+ cacheable: true
998
+ });
947
999
  }
948
1000
  if (params.mcpPrompt) {
949
- sections.push(params.mcpPrompt.trim());
1001
+ blocks.push({
1002
+ tag: "mcp_servers",
1003
+ content: wrapXmlTag("mcp_servers", params.mcpPrompt),
1004
+ cacheable: true
1005
+ });
950
1006
  }
951
1007
  if (params.developerInstructions) {
952
- sections.push(`## Developer Instructions
953
- ${params.developerInstructions}`);
1008
+ blocks.push({
1009
+ tag: "developer_instructions",
1010
+ content: wrapXmlTag("developer_instructions", params.developerInstructions),
1011
+ cacheable: true
1012
+ });
954
1013
  }
1014
+ const dynamicBlocks = [];
955
1015
  if (params.worldStatePrompt) {
956
- sections.push(`## Environment Context
957
- ${params.worldStatePrompt}`);
1016
+ dynamicBlocks.push({
1017
+ tag: "runtime_environment",
1018
+ content: wrapXmlTag("runtime_environment", params.worldStatePrompt),
1019
+ cacheable: false
1020
+ });
958
1021
  }
959
- return sections.join(`
1022
+ const staticPrefix = blocks.map((b) => b.content).join(`
960
1023
 
961
1024
  `);
1025
+ const dynamicSuffix = dynamicBlocks.map((b) => b.content).join(`
1026
+
1027
+ `);
1028
+ const allBlocks = [...blocks, ...dynamicBlocks];
1029
+ const text = allBlocks.map((b) => b.content).join(`
1030
+
1031
+ `);
1032
+ return {
1033
+ text,
1034
+ staticPrefix,
1035
+ dynamicSuffix,
1036
+ blocks: allBlocks
1037
+ };
1038
+ }
1039
+ function buildSystemPrompt(params) {
1040
+ return buildStructuredSystemPrompt(params).text;
1041
+ }
1042
+
1043
+ // src/workspace/ephemeral.ts
1044
+ import { mkdirSync as mkdirSync3, rmSync, existsSync as existsSync5, readdirSync as readdirSync2, statSync as statSync2 } from "fs";
1045
+ import { join as join4 } from "path";
1046
+ import { tmpdir } from "os";
1047
+
1048
+ class EphemeralWorkspaceManager {
1049
+ activeScratchpads = new Set;
1050
+ createScratchpad(turnId) {
1051
+ const uniqueId = `groupy_scratch_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
1052
+ const baseDir = join4(tmpdir(), "groupy-ephemeral", turnId ? `turn_${turnId}` : "general");
1053
+ const scratchPath = join4(baseDir, uniqueId);
1054
+ mkdirSync3(scratchPath, { recursive: true });
1055
+ this.activeScratchpads.add(scratchPath);
1056
+ return scratchPath;
1057
+ }
1058
+ cleanup(scratchPath) {
1059
+ if (!scratchPath || !this.activeScratchpads.has(scratchPath))
1060
+ return;
1061
+ try {
1062
+ if (existsSync5(scratchPath)) {
1063
+ rmSync(scratchPath, { recursive: true, force: true });
1064
+ }
1065
+ } catch {} finally {
1066
+ this.activeScratchpads.delete(scratchPath);
1067
+ }
1068
+ }
1069
+ cleanupTurn(turnId) {
1070
+ const turnDir = join4(tmpdir(), "groupy-ephemeral", `turn_${turnId}`);
1071
+ try {
1072
+ if (existsSync5(turnDir)) {
1073
+ rmSync(turnDir, { recursive: true, force: true });
1074
+ }
1075
+ } catch {}
1076
+ }
1077
+ cleanRootResidue(cwd) {
1078
+ const cleaned = [];
1079
+ try {
1080
+ const files = readdirSync2(cwd);
1081
+ const tempPatterns = [
1082
+ /^tmp_/i,
1083
+ /^draft_/i,
1084
+ /^scratch_/i,
1085
+ /^temp_/i,
1086
+ /^preview_.*\.html$/i,
1087
+ /\.tmp$/i,
1088
+ /\.bak$/i,
1089
+ /~$/i
1090
+ ];
1091
+ for (const file of files) {
1092
+ if (tempPatterns.some((p) => p.test(file))) {
1093
+ const fullPath = join4(cwd, file);
1094
+ const stat = statSync2(fullPath);
1095
+ if (stat.isFile()) {
1096
+ rmSync(fullPath, { force: true });
1097
+ cleaned.push(file);
1098
+ } else if (stat.isDirectory() && (file.startsWith("tmp_") || file.startsWith("scratch_") || file.startsWith("temp_"))) {
1099
+ rmSync(fullPath, { recursive: true, force: true });
1100
+ cleaned.push(file);
1101
+ }
1102
+ }
1103
+ }
1104
+ } catch {}
1105
+ return cleaned;
1106
+ }
962
1107
  }
1108
+ var globalEphemeralWorkspace = new EphemeralWorkspaceManager;
963
1109
 
964
1110
  // src/session/turn.ts
965
1111
  async function runTurn(session, turnContext, input) {
@@ -970,7 +1116,7 @@ async function runTurn(session, turnContext, input) {
970
1116
  });
971
1117
  const currentHistory = session.getHistory();
972
1118
  const estimatedTokens = estimateTotalTokens(currentHistory);
973
- const maxTokenLimit = 80000;
1119
+ const maxTokenLimit = DEFAULT_AUTO_COMPACT_THRESHOLD_TOKENS;
974
1120
  if (estimatedTokens > maxTokenLimit) {
975
1121
  const compacted = compactHistory(currentHistory);
976
1122
  session.setHistory(compacted);
@@ -1009,6 +1155,7 @@ async function runTurn(session, turnContext, input) {
1009
1155
  let iteration = 0;
1010
1156
  let accumulatedInputTokens = 0;
1011
1157
  let accumulatedOutputTokens = 0;
1158
+ let accumulatedCachedTokens = 0;
1012
1159
  const clientSession = session.modelClient.newSession();
1013
1160
  try {
1014
1161
  while (iteration < turnContext.maxIterations) {
@@ -1025,7 +1172,8 @@ async function runTurn(session, turnContext, input) {
1025
1172
  systemPrompt: effectiveSystemPrompt,
1026
1173
  history: session.getHistory(),
1027
1174
  tools: turnContext.tools,
1028
- signal
1175
+ signal,
1176
+ enablePromptCache: true
1029
1177
  });
1030
1178
  for await (const chunk of stream) {
1031
1179
  if (signal.aborted) {
@@ -1051,6 +1199,8 @@ async function runTurn(session, turnContext, input) {
1051
1199
  iterInputTokens = chunk.inputTokens;
1052
1200
  if (chunk.outputTokens !== undefined)
1053
1201
  iterOutputTokens = chunk.outputTokens;
1202
+ if (chunk.cachedTokens !== undefined)
1203
+ accumulatedCachedTokens += chunk.cachedTokens;
1054
1204
  } else if (chunk.type === "error") {
1055
1205
  throw chunk.error;
1056
1206
  }
@@ -1162,7 +1312,10 @@ async function runTurn(session, turnContext, input) {
1162
1312
  session.addHistoryItem({
1163
1313
  id: `msg_nudge_${Date.now()}`,
1164
1314
  type: "user_message",
1165
- content: "Please proceed with executing the task. Provide your complete analysis or call the required tools now.",
1315
+ content: `[Systematic ReAct Nudge]: No tool actions or answers were produced in this iteration.
1316
+ 1. Review the user's objective and determine the immediate next action.
1317
+ 2. If more context is required, invoke an exploration tool ('read_file', 'list_dir', 'grep_search', 'find_files').
1318
+ 3. If ready to answer or implement, call the required mutating tool or deliver your full, concrete response now.`,
1166
1319
  createdAt: Date.now()
1167
1320
  });
1168
1321
  continue;
@@ -1171,13 +1324,14 @@ async function runTurn(session, turnContext, input) {
1171
1324
  break;
1172
1325
  }
1173
1326
  const totalContextTokens = estimateTotalTokens(session.getHistory()) + Math.ceil(effectiveSystemPrompt.length / 4);
1174
- const maxContextTokens = 128000;
1327
+ const maxContextTokens = DEFAULT_MAX_CONTEXT_TOKENS;
1175
1328
  session.emitEvent({
1176
1329
  type: "TurnCompleted",
1177
1330
  turnId,
1178
1331
  inputTokens: accumulatedInputTokens,
1179
1332
  outputTokens: accumulatedOutputTokens,
1180
1333
  totalTokens: accumulatedInputTokens + accumulatedOutputTokens,
1334
+ cachedTokens: accumulatedCachedTokens > 0 ? accumulatedCachedTokens : undefined,
1181
1335
  contextTokens: totalContextTokens,
1182
1336
  maxContextTokens
1183
1337
  });
@@ -1192,6 +1346,8 @@ async function runTurn(session, turnContext, input) {
1192
1346
  });
1193
1347
  } finally {
1194
1348
  session.clearActiveTurn(turnId);
1349
+ globalEphemeralWorkspace.cleanupTurn(turnId);
1350
+ globalEphemeralWorkspace.cleanRootResidue(turnContext.environment.cwd);
1195
1351
  }
1196
1352
  }
1197
1353
 
@@ -1291,9 +1447,9 @@ class ExecPolicy {
1291
1447
  shouldPromptFileEdit(filePath) {
1292
1448
  if (this.mode === "plan") {
1293
1449
  return {
1294
- prompt: false,
1450
+ prompt: true,
1295
1451
  isPlanBlocked: true,
1296
- reason: "Plan Mode is active. Mutating files is not allowed while planning."
1452
+ reason: `[Plan Mode Gate] Approval required to mutate '${filePath || "file"}' and proceed with implementation.`
1297
1453
  };
1298
1454
  }
1299
1455
  if (this.mode === "manual") {
@@ -1307,11 +1463,14 @@ class ExecPolicy {
1307
1463
  evaluate(command) {
1308
1464
  const trimmed = command.trim();
1309
1465
  if (this.mode === "plan") {
1310
- const isReadOnly = /^(git\s+(status|log|diff|branch|show)|ls|dir|cat|type|grep|rg|find|pwd|which|where)\b/i.test(trimmed);
1466
+ const isReadOnly = /^(git\s+(status|log|diff|branch|show|rev-parse)|ls|dir|cat|type|grep|rg|find|pwd|which|where)\b/i.test(trimmed);
1311
1467
  if (isReadOnly) {
1312
1468
  return { decision: "allow", reason: "Read-only inspection allowed in Plan mode" };
1313
1469
  }
1314
- return { decision: "deny", reason: "Cannot execute mutating shell commands in Plan mode" };
1470
+ return {
1471
+ decision: "prompt",
1472
+ reason: `[Plan Mode Gate] Approval required to execute shell command '${trimmed}' in Plan Mode`
1473
+ };
1315
1474
  }
1316
1475
  if (this.mode === "manual") {
1317
1476
  return {
@@ -1582,9 +1741,9 @@ class Session {
1582
1741
  }
1583
1742
  }
1584
1743
  // src/tools/handlers/apply-patch.ts
1585
- import { existsSync as existsSync5, readFileSync as readFileSync4, writeFileSync as writeFileSync2 } from "fs";
1744
+ import { existsSync as existsSync6, readFileSync as readFileSync4, writeFileSync as writeFileSync2 } from "fs";
1586
1745
  import { resolve as resolve5, dirname as dirname3 } from "path";
1587
- import { mkdirSync as mkdirSync3 } from "fs";
1746
+ import { mkdirSync as mkdirSync4 } from "fs";
1588
1747
  var applyPatchTool = {
1589
1748
  name: "apply_patch",
1590
1749
  description: "Apply precise multi-line modifications to an existing file or create a new file. TargetContent must match the file content exactly.",
@@ -1616,34 +1775,40 @@ var applyPatchTool = {
1616
1775
  const replacementContent = String(args.replacementContent ?? "");
1617
1776
  if (ctx.execPolicy) {
1618
1777
  const evalResult = ctx.execPolicy.shouldPromptFileEdit(rawPath);
1619
- if (evalResult.isPlanBlocked || ctx.mode === "plan") {
1620
- return {
1621
- output: "Error: Cannot mutate files while in Plan Mode. Please present the implementation plan first.",
1622
- isError: true
1623
- };
1624
- }
1625
1778
  if (evalResult.prompt && ctx.requestApproval) {
1626
- const approval = await ctx.requestApproval(`Apply patch to: ${rawPath}`, `apply_patch ${rawPath}`);
1779
+ const approval = await ctx.requestApproval(evalResult.reason || `Apply patch to: ${rawPath}`, `apply_patch ${rawPath}`);
1627
1780
  const allowed = typeof approval === "object" ? approval.allowed : Boolean(approval);
1628
1781
  if (!allowed) {
1629
- return { output: `Action rejected by user: apply_patch '${rawPath}'`, isError: true };
1782
+ return {
1783
+ output: `[Plan Mode Gate]: File modification declined by user for '${rawPath}'. Please refine your implementation plan or ask the user for guidance.`,
1784
+ isError: true
1785
+ };
1630
1786
  }
1787
+ } else if (evalResult.isPlanBlocked || ctx.mode === "plan") {
1788
+ return {
1789
+ output: `[Plan Mode Gate]: Cannot mutate '${rawPath}' while in Plan Mode without user approval. Please present your implementation plan first.`,
1790
+ isError: true
1791
+ };
1631
1792
  }
1632
1793
  }
1633
- if (!existsSync5(filePath)) {
1794
+ if (!existsSync6(filePath)) {
1634
1795
  if (targetContent) {
1635
1796
  return {
1636
- output: `Error: Target file '${rawPath}' does not exist, but targetContent was provided.`,
1797
+ output: `Error: Target file '${rawPath}' does not exist, but targetContent was provided.
1798
+ [Systematic Error Recovery Checklist]:
1799
+ 1. Root Cause: Trying to patch a non-existent file with targetContent.
1800
+ 2. Fix: For creating new files, leave 'targetContent' empty and provide full contents in 'replacementContent'.
1801
+ 3. Alternatively, check if the file path '${rawPath}' was mistyped.`,
1637
1802
  isError: true
1638
1803
  };
1639
1804
  }
1640
1805
  try {
1641
- mkdirSync3(dirname3(filePath), { recursive: true });
1806
+ mkdirSync4(dirname3(filePath), { recursive: true });
1642
1807
  writeFileSync2(filePath, replacementContent, "utf8");
1643
1808
  return { output: `Successfully created new file '${rawPath}'` };
1644
1809
  } catch (err) {
1645
1810
  return {
1646
- output: `Failed to create file: ${err instanceof Error ? err.message : String(err)}`,
1811
+ output: `Failed to create file '${rawPath}': ${err instanceof Error ? err.message : String(err)}`,
1647
1812
  isError: true
1648
1813
  };
1649
1814
  }
@@ -1652,21 +1817,33 @@ var applyPatchTool = {
1652
1817
  const originalFileContent = readFileSync4(filePath, "utf8");
1653
1818
  if (!targetContent) {
1654
1819
  return {
1655
- output: `Error: File '${rawPath}' already exists. Specify targetContent to replace specific lines or use overwrite.`,
1820
+ output: `Error: File '${rawPath}' already exists, but targetContent was empty.
1821
+ [Systematic Error Recovery Checklist]:
1822
+ 1. Root Cause: An existing file requires targetContent to specify which lines to replace.
1823
+ 2. Fix: Call 'read_file' on '${rawPath}', extract the exact target lines, and provide them in 'targetContent'.
1824
+ 3. To overwrite the whole file, use the 'write_file' tool instead.`,
1656
1825
  isError: true
1657
1826
  };
1658
1827
  }
1659
1828
  const firstIndex = originalFileContent.indexOf(targetContent);
1660
1829
  if (firstIndex === -1) {
1661
1830
  return {
1662
- output: `Error: targetContent was not found in '${rawPath}'. Please verify file contents before editing.`,
1831
+ output: `Error: targetContent was not found in '${rawPath}'.
1832
+ [Systematic Error Recovery Checklist]:
1833
+ 1. Root Cause: The snippet in targetContent does not match the actual file content (differences in whitespace, indentation, line endings, or prior edits).
1834
+ 2. Action: Call 'read_file' on '${rawPath}' to inspect current exact lines and indentation.
1835
+ 3. Fix: Provide the exact matching lines (including leading spaces) or wider context, then retry 'apply_patch'.`,
1663
1836
  isError: true
1664
1837
  };
1665
1838
  }
1666
1839
  const secondIndex = originalFileContent.indexOf(targetContent, firstIndex + 1);
1667
1840
  if (secondIndex !== -1) {
1668
1841
  return {
1669
- output: `Error: targetContent matched multiple locations in '${rawPath}'. Provide more surrounding context lines to ensure uniqueness.`,
1842
+ output: `Error: targetContent matched multiple locations in '${rawPath}'.
1843
+ [Systematic Error Recovery Checklist]:
1844
+ 1. Root Cause: targetContent is ambiguous and occurs multiple times in the file.
1845
+ 2. Action: Include 2-3 additional surrounding lines (before or after the target block) to make the target snippet uniquely identifiable.
1846
+ 3. Fix: Re-run 'apply_patch' with the extended unique block.`,
1670
1847
  isError: true
1671
1848
  };
1672
1849
  }
@@ -1677,7 +1854,7 @@ var applyPatchTool = {
1677
1854
  };
1678
1855
  } catch (err) {
1679
1856
  return {
1680
- output: `Failed to apply patch: ${err instanceof Error ? err.message : String(err)}`,
1857
+ output: `Failed to apply patch to '${rawPath}': ${err instanceof Error ? err.message : String(err)}`,
1681
1858
  isError: true
1682
1859
  };
1683
1860
  }
@@ -1764,7 +1941,7 @@ class WindowsSandbox {
1764
1941
  }
1765
1942
 
1766
1943
  // src/security/kernel/linux.ts
1767
- import { existsSync as existsSync6 } from "fs";
1944
+ import { existsSync as existsSync7 } from "fs";
1768
1945
 
1769
1946
  class LinuxSandbox {
1770
1947
  hasBwrap = false;
@@ -1775,7 +1952,7 @@ class LinuxSandbox {
1775
1952
  if (process.platform !== "linux") {
1776
1953
  return;
1777
1954
  }
1778
- this.hasBwrap = existsSync6("/usr/bin/bwrap") || existsSync6("/bin/bwrap") || existsSync6("/usr/local/bin/bwrap");
1955
+ this.hasBwrap = existsSync7("/usr/bin/bwrap") || existsSync7("/bin/bwrap") || existsSync7("/usr/local/bin/bwrap");
1779
1956
  }
1780
1957
  wrapCommand(cmd, profile) {
1781
1958
  if (!this.hasBwrap || profile.kind === "danger-unrestricted") {
@@ -1809,7 +1986,7 @@ class LinuxSandbox {
1809
1986
  }
1810
1987
 
1811
1988
  // src/security/kernel/macos.ts
1812
- import { existsSync as existsSync7 } from "fs";
1989
+ import { existsSync as existsSync8 } from "fs";
1813
1990
 
1814
1991
  class MacOSSandbox {
1815
1992
  hasSandboxExec = false;
@@ -1820,7 +1997,7 @@ class MacOSSandbox {
1820
1997
  if (process.platform !== "darwin") {
1821
1998
  return;
1822
1999
  }
1823
- this.hasSandboxExec = existsSync7("/usr/bin/sandbox-exec");
2000
+ this.hasSandboxExec = existsSync8("/usr/bin/sandbox-exec");
1824
2001
  }
1825
2002
  generateProfile(profile) {
1826
2003
  const rules = [
@@ -1930,7 +2107,7 @@ var globalKernelSandbox = new KernelSandboxManager;
1930
2107
 
1931
2108
  // src/storage/prefix-rules-store.ts
1932
2109
  import { Database } from "bun:sqlite";
1933
- import { existsSync as existsSync8, mkdirSync as mkdirSync4 } from "fs";
2110
+ import { existsSync as existsSync9, mkdirSync as mkdirSync5 } from "fs";
1934
2111
  import { dirname as dirname4, resolve as resolve7 } from "path";
1935
2112
  class PrefixRulesStore {
1936
2113
  db;
@@ -1941,8 +2118,8 @@ class PrefixRulesStore {
1941
2118
  const effectivePath = dbOrPath || getPrefixRulesDbPath();
1942
2119
  if (effectivePath !== ":memory:") {
1943
2120
  const dir = dirname4(effectivePath);
1944
- if (!existsSync8(dir)) {
1945
- mkdirSync4(dir, { recursive: true });
2121
+ if (!existsSync9(dir)) {
2122
+ mkdirSync5(dir, { recursive: true });
1946
2123
  }
1947
2124
  }
1948
2125
  this.db = new Database(effectivePath);
@@ -2131,6 +2308,7 @@ function createShellTool(policy = new ExecPolicy) {
2131
2308
  const timeoutMs = typeof args.timeoutMs === "number" ? args.timeoutMs : 30000;
2132
2309
  const isWindows = process.platform === "win32";
2133
2310
  const baseCmd = isWindows ? ["cmd.exe", "/d", "/s", "/c", command] : ["/bin/sh", "-c", command];
2311
+ const ephemeralScratchpad = globalEphemeralWorkspace.createScratchpad(ctx.turnId);
2134
2312
  const sandboxProfile = globalKernelSandbox.buildDefaultProfile(ctx.cwd);
2135
2313
  if (isEscalated) {
2136
2314
  sandboxProfile.allowNetwork = true;
@@ -2141,6 +2319,10 @@ function createShellTool(policy = new ExecPolicy) {
2141
2319
  cwd: ctx.cwd,
2142
2320
  env: {
2143
2321
  ...process.env,
2322
+ TMPDIR: ephemeralScratchpad,
2323
+ TEMP: ephemeralScratchpad,
2324
+ TMP: ephemeralScratchpad,
2325
+ GROUPY_SCRATCH_DIR: ephemeralScratchpad,
2144
2326
  ...ctx.proxyEnv
2145
2327
  },
2146
2328
  stdout: "pipe",
@@ -2177,9 +2359,14 @@ function createShellTool(policy = new ExecPolicy) {
2177
2359
  if (result.stderr)
2178
2360
  outputParts.push(`STDERR:
2179
2361
  ${result.stderr.trim()}`);
2180
- if (result.code !== 0)
2362
+ if (result.code !== 0) {
2181
2363
  outputParts.push(`
2182
- [Process exited with code ${result.code}]`);
2364
+ [Process exited with non-zero code ${result.code}]`);
2365
+ outputParts.push(`[Systematic Error Recovery Checklist]:
2366
+ 1. Inspect STDERR above to pinpoint syntax errors, failed test assertions, or missing dependencies.
2367
+ 2. If this is a test failure, trace the failure in source code and fix the root cause before re-running.
2368
+ 3. If this is a missing command/module, install or configure the prerequisite.`);
2369
+ }
2183
2370
  const output = outputParts.join(`
2184
2371
  `) || "[Command completed with no output]";
2185
2372
  return {
@@ -2188,16 +2375,21 @@ ${result.stderr.trim()}`);
2188
2375
  };
2189
2376
  } catch (err) {
2190
2377
  return {
2191
- output: `Execution error: ${err instanceof Error ? err.message : String(err)}`,
2378
+ output: `Execution error: ${err instanceof Error ? err.message : String(err)}
2379
+ [Systematic Error Recovery Checklist]:
2380
+ 1. Verify command syntax, arguments, and executable availability in PATH.
2381
+ 2. Check if the current working directory ('${ctx.cwd}') is valid.`,
2192
2382
  isError: true
2193
2383
  };
2384
+ } finally {
2385
+ globalEphemeralWorkspace.cleanup(ephemeralScratchpad);
2194
2386
  }
2195
2387
  }
2196
2388
  };
2197
2389
  }
2198
2390
  var shellTool = createShellTool();
2199
2391
  // src/tools/handlers/file-ops.ts
2200
- import { readdirSync as readdirSync2, readFileSync as readFileSync5, writeFileSync as writeFileSync3, existsSync as existsSync9, statSync as statSync2, mkdirSync as mkdirSync5 } from "fs";
2392
+ import { readdirSync as readdirSync3, readFileSync as readFileSync5, writeFileSync as writeFileSync3, existsSync as existsSync10, statSync as statSync3, mkdirSync as mkdirSync6 } from "fs";
2201
2393
  import { resolve as resolve8, dirname as dirname5 } from "path";
2202
2394
  var readFileTool = {
2203
2395
  name: "read_file",
@@ -2211,7 +2403,7 @@ var readFileTool = {
2211
2403
  },
2212
2404
  async execute(args, ctx) {
2213
2405
  const filePath = resolve8(ctx.cwd, String(args.path || ""));
2214
- if (!existsSync9(filePath)) {
2406
+ if (!existsSync10(filePath)) {
2215
2407
  return { output: `Error: File not found: '${args.path}'`, isError: true };
2216
2408
  }
2217
2409
  try {
@@ -2233,14 +2425,14 @@ var listDirTool = {
2233
2425
  },
2234
2426
  async execute(args, ctx) {
2235
2427
  const dirPath = resolve8(ctx.cwd, String(args.path || "."));
2236
- if (!existsSync9(dirPath)) {
2428
+ if (!existsSync10(dirPath)) {
2237
2429
  return { output: `Error: Directory not found: '${args.path}'`, isError: true };
2238
2430
  }
2239
2431
  try {
2240
- const entries = readdirSync2(dirPath);
2432
+ const entries = readdirSync3(dirPath);
2241
2433
  const formatted = entries.map((entry) => {
2242
2434
  const full = resolve8(dirPath, entry);
2243
- const isDir = statSync2(full).isDirectory();
2435
+ const isDir = statSync3(full).isDirectory();
2244
2436
  return `${isDir ? "[DIR]" : "[FILE]"} ${entry}`;
2245
2437
  });
2246
2438
  return { output: formatted.join(`
@@ -2266,26 +2458,35 @@ var writeFileTool = {
2266
2458
  const filePath = resolve8(ctx.cwd, rawPath);
2267
2459
  if (ctx.execPolicy) {
2268
2460
  const evalResult = ctx.execPolicy.shouldPromptFileEdit(rawPath);
2269
- if (evalResult.isPlanBlocked || ctx.mode === "plan") {
2270
- return {
2271
- output: "Error: Cannot write or mutate files while in Plan Mode. Please present the implementation plan first.",
2272
- isError: true
2273
- };
2274
- }
2275
2461
  if (evalResult.prompt && ctx.requestApproval) {
2276
- const approval = await ctx.requestApproval(`Write file: ${rawPath}`, `write_file ${rawPath}`);
2462
+ const approval = await ctx.requestApproval(evalResult.reason || `Write file: ${rawPath}`, `write_file ${rawPath}`);
2277
2463
  const allowed = typeof approval === "object" ? approval.allowed : Boolean(approval);
2278
2464
  if (!allowed) {
2279
- return { output: `Action rejected by user: write_file '${rawPath}'`, isError: true };
2465
+ return {
2466
+ output: `[Plan Mode Gate]: File creation declined by user for '${rawPath}'. Please refine your implementation plan or ask the user for guidance.`,
2467
+ isError: true
2468
+ };
2280
2469
  }
2470
+ } else if (evalResult.isPlanBlocked || ctx.mode === "plan") {
2471
+ return {
2472
+ output: `[Plan Mode Gate]: Cannot write or mutate '${rawPath}' while in Plan Mode without user approval. Please present your implementation plan first.`,
2473
+ isError: true
2474
+ };
2281
2475
  }
2282
2476
  }
2283
2477
  try {
2284
- mkdirSync5(dirname5(filePath), { recursive: true });
2478
+ mkdirSync6(dirname5(filePath), { recursive: true });
2285
2479
  writeFileSync3(filePath, String(args.content ?? ""), "utf8");
2286
2480
  return { output: `Successfully wrote to '${args.path}'` };
2287
2481
  } catch (err) {
2288
- return { output: `Failed to write file: ${err instanceof Error ? err.message : String(err)}`, isError: true };
2482
+ return {
2483
+ output: `Failed to write file '${rawPath}': ${err instanceof Error ? err.message : String(err)}
2484
+ [Systematic Error Recovery Checklist]:
2485
+ 1. Check directory permissions and ensure the path is valid within the workspace.
2486
+ 2. If the path contains non-existent nested folders, they should be auto-created.
2487
+ 3. Verify that the file is not locked by another active process.`,
2488
+ isError: true
2489
+ };
2289
2490
  }
2290
2491
  }
2291
2492
  };
@@ -2404,8 +2605,8 @@ var updatePlanTool = {
2404
2605
  }
2405
2606
  };
2406
2607
  // src/search/engine.ts
2407
- import { readdirSync as readdirSync3, readFileSync as readFileSync6, statSync as statSync3, existsSync as existsSync10 } from "fs";
2408
- import { resolve as resolve9, relative, join as join4, extname } from "path";
2608
+ import { readdirSync as readdirSync4, readFileSync as readFileSync6, statSync as statSync4, existsSync as existsSync11 } from "fs";
2609
+ import { resolve as resolve9, relative, join as join5, extname } from "path";
2409
2610
  var DEFAULT_IGNORE_DIRS = new Set([
2410
2611
  ".git",
2411
2612
  "node_modules",
@@ -2451,7 +2652,7 @@ var BINARY_EXTENSIONS = new Set([
2451
2652
  class FileSearchEngine {
2452
2653
  grep(cwd, options) {
2453
2654
  const searchRoot = resolve9(cwd, options.path || ".");
2454
- if (!existsSync10(searchRoot)) {
2655
+ if (!existsSync11(searchRoot)) {
2455
2656
  return { matches: [], totalMatches: 0, truncated: false };
2456
2657
  }
2457
2658
  const maxResults = options.maxResults || 50;
@@ -2501,7 +2702,7 @@ class FileSearchEngine {
2501
2702
  }
2502
2703
  findFiles(cwd, options) {
2503
2704
  const searchRoot = resolve9(cwd, options.path || ".");
2504
- if (!existsSync10(searchRoot))
2705
+ if (!existsSync11(searchRoot))
2505
2706
  return [];
2506
2707
  const maxResults = options.maxResults || 100;
2507
2708
  const gitignoreRules = this.loadGitignoreRules(searchRoot);
@@ -2540,8 +2741,8 @@ class FileSearchEngine {
2540
2741
  }
2541
2742
  loadGitignoreRules(root) {
2542
2743
  const rules = new Set;
2543
- const gitignorePath = join4(root, ".gitignore");
2544
- if (existsSync10(gitignorePath)) {
2744
+ const gitignorePath = join5(root, ".gitignore");
2745
+ if (existsSync11(gitignorePath)) {
2545
2746
  try {
2546
2747
  const lines = readFileSync6(gitignorePath, "utf8").split(`
2547
2748
  `);
@@ -2558,7 +2759,7 @@ class FileSearchEngine {
2558
2759
  collectFiles(dir, root, gitignoreRules, includePattern) {
2559
2760
  const results = [];
2560
2761
  try {
2561
- const stat = statSync3(dir);
2762
+ const stat = statSync4(dir);
2562
2763
  if (!stat.isDirectory()) {
2563
2764
  if (!this.isBinary(dir)) {
2564
2765
  results.push(dir);
@@ -2572,9 +2773,9 @@ class FileSearchEngine {
2572
2773
  while (queue.length > 0) {
2573
2774
  const currentDir = queue.shift();
2574
2775
  try {
2575
- const entries = readdirSync3(currentDir, { withFileTypes: true });
2776
+ const entries = readdirSync4(currentDir, { withFileTypes: true });
2576
2777
  for (const entry of entries) {
2577
- const fullPath = join4(currentDir, entry.name);
2778
+ const fullPath = join5(currentDir, entry.name);
2578
2779
  const relToRoot = relative(root, fullPath).replace(/\\/g, "/");
2579
2780
  if (this.isIgnored(entry.name, relToRoot, gitignoreRules)) {
2580
2781
  continue;
@@ -3273,8 +3474,8 @@ function createDefaultTools(options = {}) {
3273
3474
  }
3274
3475
 
3275
3476
  // src/agents/roles.ts
3276
- import { existsSync as existsSync11, readdirSync as readdirSync4, readFileSync as readFileSync7 } from "fs";
3277
- import { resolve as resolve10, join as join5 } from "path";
3477
+ import { existsSync as existsSync12, readdirSync as readdirSync5, readFileSync as readFileSync7 } from "fs";
3478
+ import { resolve as resolve10, join as join6 } from "path";
3278
3479
 
3279
3480
  class AgentRoleRegistry {
3280
3481
  roles = new Map;
@@ -3359,13 +3560,13 @@ class AgentRoleRegistry {
3359
3560
  }
3360
3561
  loadRolesFromDir(dirPath) {
3361
3562
  const fullPath = resolve10(dirPath);
3362
- if (!existsSync11(fullPath))
3563
+ if (!existsSync12(fullPath))
3363
3564
  return;
3364
- const entries = readdirSync4(fullPath);
3565
+ const entries = readdirSync5(fullPath);
3365
3566
  for (const entry of entries) {
3366
3567
  if (entry.endsWith(".json")) {
3367
3568
  try {
3368
- const content = readFileSync7(join5(fullPath, entry), "utf8");
3569
+ const content = readFileSync7(join6(fullPath, entry), "utf8");
3369
3570
  const parsed = JSON.parse(content);
3370
3571
  if (parsed.name && parsed.systemPrompt) {
3371
3572
  this.registerRole(parsed);
@@ -3418,7 +3619,7 @@ function createAgentIdentity(parentId, harnessId = "groupy-harness-v1") {
3418
3619
  // src/agents/graph-store.ts
3419
3620
  import { Database as Database2 } from "bun:sqlite";
3420
3621
  import { resolve as resolve11 } from "path";
3421
- import { existsSync as existsSync12, mkdirSync as mkdirSync6 } from "fs";
3622
+ import { existsSync as existsSync13, mkdirSync as mkdirSync7 } from "fs";
3422
3623
  class AgentGraphStore {
3423
3624
  db;
3424
3625
  constructor(dbPathOrDb) {
@@ -3428,8 +3629,8 @@ class AgentGraphStore {
3428
3629
  const dbPath = dbPathOrDb || getAgentGraphDbPath();
3429
3630
  if (dbPath !== ":memory:") {
3430
3631
  const dir = resolve11(dbPath, "..");
3431
- if (!existsSync12(dir)) {
3432
- mkdirSync6(dir, { recursive: true });
3632
+ if (!existsSync13(dir)) {
3633
+ mkdirSync7(dir, { recursive: true });
3433
3634
  }
3434
3635
  }
3435
3636
  this.db = new Database2(dbPath);
@@ -3853,8 +4054,8 @@ function registerMultiAgentTools(router2, spawner) {
3853
4054
  }
3854
4055
 
3855
4056
  // src/mcp/manager.ts
3856
- import { existsSync as existsSync13, readFileSync as readFileSync8, writeFileSync as writeFileSync4, mkdirSync as mkdirSync7 } from "fs";
3857
- import { resolve as resolve12, dirname as dirname6, join as join6 } from "path";
4057
+ import { existsSync as existsSync14, readFileSync as readFileSync8, writeFileSync as writeFileSync4, mkdirSync as mkdirSync8 } from "fs";
4058
+ import { resolve as resolve12, dirname as dirname6, join as join7 } from "path";
3858
4059
 
3859
4060
  // src/mcp/client.ts
3860
4061
  class McpClient {
@@ -4470,7 +4671,7 @@ class McpManager {
4470
4671
  }
4471
4672
  async loadConfigFile(filePath) {
4472
4673
  const fullPath = resolve12(filePath);
4473
- if (!existsSync13(fullPath))
4674
+ if (!existsSync14(fullPath))
4474
4675
  return;
4475
4676
  this.loadedConfigFiles.add(fullPath);
4476
4677
  try {
@@ -4725,11 +4926,11 @@ class McpManager {
4725
4926
  saveServerToConfigFile(filePath, name, config) {
4726
4927
  const fullPath = resolve12(filePath);
4727
4928
  const dir = dirname6(fullPath);
4728
- if (!existsSync13(dir)) {
4729
- mkdirSync7(dir, { recursive: true });
4929
+ if (!existsSync14(dir)) {
4930
+ mkdirSync8(dir, { recursive: true });
4730
4931
  }
4731
4932
  let existing = { mcpServers: {} };
4732
- if (existsSync13(fullPath)) {
4933
+ if (existsSync14(fullPath)) {
4733
4934
  try {
4734
4935
  const content = readFileSync8(fullPath, "utf8");
4735
4936
  existing = JSON.parse(content);
@@ -4744,7 +4945,7 @@ class McpManager {
4744
4945
  }
4745
4946
  removeServerFromConfigFile(filePath, name) {
4746
4947
  const fullPath = resolve12(filePath);
4747
- if (!existsSync13(fullPath))
4948
+ if (!existsSync14(fullPath))
4748
4949
  return false;
4749
4950
  try {
4750
4951
  const content = readFileSync8(fullPath, "utf8");
@@ -4774,11 +4975,11 @@ class McpManager {
4774
4975
  }
4775
4976
  }
4776
4977
  getDefaultConfigFile(cwd = process.cwd()) {
4777
- const workspaceConfig = join6(cwd, ".mcp.json");
4778
- if (existsSync13(workspaceConfig))
4978
+ const workspaceConfig = join7(cwd, ".mcp.json");
4979
+ if (existsSync14(workspaceConfig))
4779
4980
  return workspaceConfig;
4780
- const altConfig = join6(cwd, "mcp_config.json");
4781
- if (existsSync13(altConfig))
4981
+ const altConfig = join7(cwd, "mcp_config.json");
4982
+ if (existsSync14(altConfig))
4782
4983
  return altConfig;
4783
4984
  return workspaceConfig;
4784
4985
  }
@@ -4799,7 +5000,7 @@ class McpManager {
4799
5000
 
4800
5001
  // src/storage/sqlite-store.ts
4801
5002
  import { Database as Database3 } from "bun:sqlite";
4802
- import { existsSync as existsSync14, mkdirSync as mkdirSync8 } from "fs";
5003
+ import { existsSync as existsSync15, mkdirSync as mkdirSync9 } from "fs";
4803
5004
  import { dirname as dirname7 } from "path";
4804
5005
  class SqliteThreadStore {
4805
5006
  db;
@@ -4807,8 +5008,8 @@ class SqliteThreadStore {
4807
5008
  const effectivePath = dbPath || this.getDefaultDbPath();
4808
5009
  if (effectivePath !== ":memory:") {
4809
5010
  const dir = dirname7(effectivePath);
4810
- if (!existsSync14(dir)) {
4811
- mkdirSync8(dir, { recursive: true });
5011
+ if (!existsSync15(dir)) {
5012
+ mkdirSync9(dir, { recursive: true });
4812
5013
  }
4813
5014
  }
4814
5015
  this.db = new Database3(effectivePath);
@@ -5042,8 +5243,8 @@ class SessionPersistenceManager {
5042
5243
  }
5043
5244
 
5044
5245
  // src/skills/loader.ts
5045
- import { existsSync as existsSync15, readdirSync as readdirSync5, readFileSync as readFileSync9 } from "fs";
5046
- import { resolve as resolve13, join as join7 } from "path";
5246
+ import { existsSync as existsSync16, readdirSync as readdirSync6, readFileSync as readFileSync9 } from "fs";
5247
+ import { resolve as resolve13, join as join8 } from "path";
5047
5248
  import { homedir as homedir3 } from "os";
5048
5249
  var __dirname = "/home/runner/work/agent-cli/agent-cli/src/skills";
5049
5250
 
@@ -5114,7 +5315,7 @@ class SkillsLoader {
5114
5315
  resolve13(cwd, "skills")
5115
5316
  ];
5116
5317
  for (const cand of candidates) {
5117
- if (existsSync15(cand) && !roots.includes(cand)) {
5318
+ if (existsSync16(cand) && !roots.includes(cand)) {
5118
5319
  roots.push(cand);
5119
5320
  }
5120
5321
  }
@@ -5123,7 +5324,7 @@ class SkillsLoader {
5123
5324
  roots.push(getGlobalSkillsDir(), resolve13(homedir3(), ".gemini", "config", "skills"));
5124
5325
  }
5125
5326
  roots.push(...this.customRoots.map((r) => resolve13(r)));
5126
- return roots.filter((r) => existsSync15(r));
5327
+ return roots.filter((r) => existsSync16(r));
5127
5328
  }
5128
5329
  discoverSkills(cwd, options) {
5129
5330
  return this.listSkills(cwd, options);
@@ -5139,12 +5340,12 @@ class SkillsLoader {
5139
5340
  const discovered = new Map;
5140
5341
  for (const root of roots) {
5141
5342
  try {
5142
- const entries = readdirSync5(root, { withFileTypes: true });
5343
+ const entries = readdirSync6(root, { withFileTypes: true });
5143
5344
  for (const entry of entries) {
5144
5345
  if (entry.isDirectory()) {
5145
- const skillDir = join7(root, entry.name);
5146
- const skillFilePath = join7(skillDir, "SKILL.md");
5147
- if (existsSync15(skillFilePath)) {
5346
+ const skillDir = join8(root, entry.name);
5347
+ const skillFilePath = join8(skillDir, "SKILL.md");
5348
+ if (existsSync16(skillFilePath)) {
5148
5349
  const meta = this.parseSkillFrontmatter(skillFilePath, entry.name, root, cwd);
5149
5350
  if (meta && !discovered.has(meta.name)) {
5150
5351
  meta.enabled = !this.isSkillDisabled(meta.name);
@@ -5157,7 +5358,7 @@ class SkillsLoader {
5157
5358
  }
5158
5359
  } catch {}
5159
5360
  }
5160
- const result = Array.from(discovered.values());
5361
+ const result = Array.from(discovered.values()).sort((a, b) => a.name.localeCompare(b.name));
5161
5362
  this.skillsCache.set(cacheKey, { timestamp: now, skills: result });
5162
5363
  return result;
5163
5364
  }
@@ -5246,10 +5447,10 @@ class SkillsLoader {
5246
5447
  const skills = this.listSkills(cwd, { includeDisabled: false });
5247
5448
  if (skills.length === 0)
5248
5449
  return "";
5249
- const workspaceSkills = skills.filter((s) => s.scope === "workspace");
5250
- const builtInSkills = skills.filter((s) => s.scope === "built-in");
5251
- const otherSkills = skills.filter((s) => s.scope !== "workspace" && s.scope !== "built-in");
5252
- const selectedSkills = [...workspaceSkills, ...builtInSkills, ...otherSkills].slice(0, 150);
5450
+ const workspaceSkills = skills.filter((s) => s.scope === "workspace").sort((a, b) => a.name.localeCompare(b.name));
5451
+ const builtInSkills = skills.filter((s) => s.scope === "built-in").sort((a, b) => a.name.localeCompare(b.name));
5452
+ const otherSkills = skills.filter((s) => s.scope !== "workspace" && s.scope !== "built-in").sort((a, b) => a.name.localeCompare(b.name));
5453
+ const selectedSkills = [...workspaceSkills, ...builtInSkills, ...otherSkills].slice(0, 300);
5253
5454
  const lines = selectedSkills.map((s) => {
5254
5455
  const desc = s.shortDescription || s.description;
5255
5456
  return `- **${s.name}**: ${desc}`;
@@ -5267,8 +5468,8 @@ When tackling complex specialized tasks that match any of these skills, autonomo
5267
5468
  }
5268
5469
 
5269
5470
  // src/memories/store.ts
5270
- import { existsSync as existsSync16, readFileSync as readFileSync10, writeFileSync as writeFileSync5, mkdirSync as mkdirSync9, readdirSync as readdirSync6 } from "fs";
5271
- import { resolve as resolve14, join as join8, basename, dirname as dirname8 } from "path";
5471
+ import { existsSync as existsSync17, readFileSync as readFileSync10, writeFileSync as writeFileSync5, mkdirSync as mkdirSync10, readdirSync as readdirSync7 } from "fs";
5472
+ import { resolve as resolve14, join as join9, basename, dirname as dirname8 } from "path";
5272
5473
  import { createHash } from "crypto";
5273
5474
  class MemoryStore {
5274
5475
  globalPath;
@@ -5280,7 +5481,7 @@ class MemoryStore {
5280
5481
  findProjectRoot(cwd) {
5281
5482
  let current = resolve14(cwd);
5282
5483
  while (true) {
5283
- if (existsSync16(join8(current, ".git"))) {
5484
+ if (existsSync17(join9(current, ".git"))) {
5284
5485
  return current;
5285
5486
  }
5286
5487
  const parent = dirname8(current);
@@ -5299,24 +5500,24 @@ class MemoryStore {
5299
5500
  getProjectMemoryDir(cwd) {
5300
5501
  if (this.customWorkspacePath) {
5301
5502
  const dir2 = resolve14(this.customWorkspacePath);
5302
- if (!existsSync16(dir2)) {
5503
+ if (!existsSync17(dir2)) {
5303
5504
  try {
5304
- mkdirSync9(dir2, { recursive: true });
5505
+ mkdirSync10(dir2, { recursive: true });
5305
5506
  } catch {}
5306
5507
  }
5307
5508
  return dir2;
5308
5509
  }
5309
5510
  const slug = this.getProjectSlug(cwd);
5310
- const dir = join8(getProjectsDir(), slug, "memory");
5311
- if (!existsSync16(dir)) {
5511
+ const dir = join9(getProjectsDir(), slug, "memory");
5512
+ if (!existsSync17(dir)) {
5312
5513
  try {
5313
- mkdirSync9(dir, { recursive: true });
5514
+ mkdirSync10(dir, { recursive: true });
5314
5515
  } catch {}
5315
5516
  }
5316
5517
  return dir;
5317
5518
  }
5318
5519
  getMemoryIndexPath(cwd) {
5319
- return join8(this.getProjectMemoryDir(cwd), "MEMORY.md");
5520
+ return join9(this.getProjectMemoryDir(cwd), "MEMORY.md");
5320
5521
  }
5321
5522
  normalizeCategory(raw) {
5322
5523
  const cat = raw.toLowerCase().trim();
@@ -5335,7 +5536,7 @@ class MemoryStore {
5335
5536
  const sanitizedName = params.name.toLowerCase().trim().replace(/[^a-z0-9_-]/g, "_").replace(/^_+|_+$/g, "") || `note_${Date.now()}`;
5336
5537
  const memoryDir = this.getProjectMemoryDir(params.cwd);
5337
5538
  const fileName = `${type}_${sanitizedName}.md`;
5338
- const filePath = join8(memoryDir, fileName);
5539
+ const filePath = join9(memoryDir, fileName);
5339
5540
  const nowIso = new Date().toISOString();
5340
5541
  const cleanContent = params.content.trim();
5341
5542
  const desc = (params.description || cleanContent.split(`
@@ -5370,17 +5571,17 @@ class MemoryStore {
5370
5571
  }
5371
5572
  readTopicMemory(topicNameOrFile, cwd) {
5372
5573
  const memoryDir = this.getProjectMemoryDir(cwd);
5373
- let targetPath = join8(memoryDir, topicNameOrFile);
5374
- if (!existsSync16(targetPath)) {
5574
+ let targetPath = join9(memoryDir, topicNameOrFile);
5575
+ if (!existsSync17(targetPath)) {
5375
5576
  if (!topicNameOrFile.endsWith(".md")) {
5376
- targetPath = join8(memoryDir, `${topicNameOrFile}.md`);
5577
+ targetPath = join9(memoryDir, `${topicNameOrFile}.md`);
5377
5578
  }
5378
5579
  }
5379
- if (!existsSync16(targetPath)) {
5380
- const files = readdirSync6(memoryDir);
5580
+ if (!existsSync17(targetPath)) {
5581
+ const files = readdirSync7(memoryDir);
5381
5582
  const match = files.find((f) => f.includes(topicNameOrFile));
5382
5583
  if (match) {
5383
- targetPath = join8(memoryDir, match);
5584
+ targetPath = join9(memoryDir, match);
5384
5585
  } else {
5385
5586
  return null;
5386
5587
  }
@@ -5441,12 +5642,12 @@ class MemoryStore {
5441
5642
  }
5442
5643
  syncMemoryIndex(cwd) {
5443
5644
  const memoryDir = this.getProjectMemoryDir(cwd);
5444
- const indexPath = join8(memoryDir, "MEMORY.md");
5445
- const files = existsSync16(memoryDir) ? readdirSync6(memoryDir).filter((f) => f.endsWith(".md") && f !== "MEMORY.md") : [];
5645
+ const indexPath = join9(memoryDir, "MEMORY.md");
5646
+ const files = existsSync17(memoryDir) ? readdirSync7(memoryDir).filter((f) => f.endsWith(".md") && f !== "MEMORY.md") : [];
5446
5647
  const items = [];
5447
5648
  for (const f of files) {
5448
5649
  try {
5449
- const full = join8(memoryDir, f);
5650
+ const full = join9(memoryDir, f);
5450
5651
  const parsed = this.parseTopicFile(readFileSync10(full, "utf8"), full);
5451
5652
  items.push({
5452
5653
  type: parsed.type,
@@ -5473,7 +5674,7 @@ class MemoryStore {
5473
5674
  }
5474
5675
  loadMemoryIndex(cwd) {
5475
5676
  const indexPath = this.getMemoryIndexPath(cwd);
5476
- if (!existsSync16(indexPath))
5677
+ if (!existsSync17(indexPath))
5477
5678
  return "";
5478
5679
  try {
5479
5680
  const raw = readFileSync10(indexPath, "utf8");
@@ -5489,13 +5690,13 @@ class MemoryStore {
5489
5690
  }
5490
5691
  listProjectMemories(cwd) {
5491
5692
  const memoryDir = this.getProjectMemoryDir(cwd);
5492
- if (!existsSync16(memoryDir))
5693
+ if (!existsSync17(memoryDir))
5493
5694
  return [];
5494
- const files = readdirSync6(memoryDir).filter((f) => f.endsWith(".md") && f !== "MEMORY.md");
5695
+ const files = readdirSync7(memoryDir).filter((f) => f.endsWith(".md") && f !== "MEMORY.md");
5495
5696
  const list = [];
5496
5697
  for (const f of files) {
5497
5698
  try {
5498
- const full = join8(memoryDir, f);
5699
+ const full = join9(memoryDir, f);
5499
5700
  list.push(this.parseTopicFile(readFileSync10(full, "utf8"), full));
5500
5701
  } catch {}
5501
5702
  }
@@ -5547,8 +5748,8 @@ class MemoryStore {
5547
5748
  }
5548
5749
 
5549
5750
  // src/worktree/manager.ts
5550
- import { resolve as resolve16, join as join9 } from "path";
5551
- import { existsSync as existsSync17, mkdirSync as mkdirSync10, writeFileSync as writeFileSync6, readFileSync as readFileSync11 } from "fs";
5751
+ import { resolve as resolve16, join as join10 } from "path";
5752
+ import { existsSync as existsSync18, mkdirSync as mkdirSync11, writeFileSync as writeFileSync6, readFileSync as readFileSync11 } from "fs";
5552
5753
 
5553
5754
  // src/worktree/git.ts
5554
5755
  import { resolve as resolve15 } from "path";
@@ -5698,15 +5899,15 @@ class WorktreeManager {
5698
5899
  const branchName = options.branch || `groupy/${taskId}`;
5699
5900
  const targetDir = options.worktreePath || (this.baseStorageDir ? resolve16(this.baseStorageDir, branchName.replace(/\//g, "_")) : resolve16(repoRoot, ".groupy", "worktrees", branchName.replace(/\//g, "_")));
5700
5901
  const worktreeParent = resolve16(targetDir, "..");
5701
- if (!existsSync17(worktreeParent)) {
5702
- mkdirSync10(worktreeParent, { recursive: true });
5902
+ if (!existsSync18(worktreeParent)) {
5903
+ mkdirSync11(worktreeParent, { recursive: true });
5703
5904
  }
5704
5905
  const baseBranch = options.baseBranch || await getCurrentBranch(repoRoot);
5705
5906
  const result = await createWorktreeGit(repoRoot, targetDir, branchName, baseBranch);
5706
5907
  if (!result.success) {
5707
5908
  throw new Error(`Failed to create git worktree: ${result.error}`);
5708
5909
  }
5709
- const metaPath = join9(targetDir, "groupy-thread.json");
5910
+ const metaPath = join10(targetDir, "groupy-thread.json");
5710
5911
  try {
5711
5912
  writeFileSync6(metaPath, JSON.stringify({
5712
5913
  version: 1,
@@ -5732,8 +5933,8 @@ class WorktreeManager {
5732
5933
  return [];
5733
5934
  const worktrees = await listWorktreesGit(repoRoot);
5734
5935
  return worktrees.map((wt) => {
5735
- const metaPath = join9(wt.path, "groupy-thread.json");
5736
- if (existsSync17(metaPath)) {
5936
+ const metaPath = join10(wt.path, "groupy-thread.json");
5937
+ if (existsSync18(metaPath)) {
5737
5938
  try {
5738
5939
  const raw = JSON.parse(readFileSync11(metaPath, "utf8"));
5739
5940
  return { ...wt, threadId: raw.ownerThreadId || raw.threadId };
@@ -6431,7 +6632,7 @@ function parsePatch(oldSrc, newSrc, contextLines = 3) {
6431
6632
  // package.json
6432
6633
  var package_default = {
6433
6634
  name: "@pikaa-ai/pikaa",
6434
- version: "0.3.19",
6635
+ version: "0.3.20",
6435
6636
  description: "PIKAA CLI - AI coding agent that runs locally in your terminal.",
6436
6637
  main: "./dist/index.js",
6437
6638
  module: "./dist/index.js",
@@ -6806,7 +7007,12 @@ ${preview}${more}`);
6806
7007
  if (metrics.inputTokens !== undefined || metrics.outputTokens !== undefined) {
6807
7008
  const inStr = metrics.inputTokens !== undefined ? formatTokens(metrics.inputTokens) : "0";
6808
7009
  const outStr = metrics.outputTokens !== undefined ? formatTokens(metrics.outputTokens) : "0";
6809
- parts.push(`${style.cyan(`${inStr} in`)} ${style.dim("/")} ${style.cyan(`${outStr} out`)}`);
7010
+ if (metrics.cachedTokens !== undefined && metrics.cachedTokens > 0) {
7011
+ const cachedStr = formatTokens(metrics.cachedTokens);
7012
+ parts.push(`${style.cyan(`${inStr} in`)} ${style.dim(`(${cachedStr} cached)`)} ${style.dim("/")} ${style.cyan(`${outStr} out`)}`);
7013
+ } else {
7014
+ parts.push(`${style.cyan(`${inStr} in`)} ${style.dim("/")} ${style.cyan(`${outStr} out`)}`);
7015
+ }
6810
7016
  } else if (metrics.totalTokens !== undefined && metrics.totalTokens > 0) {
6811
7017
  parts.push(`${style.dim(`${formatTokens(metrics.totalTokens)} tokens`)}`);
6812
7018
  }
@@ -7923,8 +8129,8 @@ async function promptInteractiveList(config) {
7923
8129
  }
7924
8130
 
7925
8131
  // src/security/scanner.ts
7926
- import { existsSync as existsSync18, readdirSync as readdirSync7, readFileSync as readFileSync12, statSync as statSync4 } from "fs";
7927
- import { join as join10, relative as relative2, resolve as resolve17 } from "path";
8132
+ import { existsSync as existsSync19, readdirSync as readdirSync8, readFileSync as readFileSync12, statSync as statSync5 } from "fs";
8133
+ import { join as join11, relative as relative2, resolve as resolve17 } from "path";
7928
8134
  var SECURITY_RULES = [
7929
8135
  {
7930
8136
  id: "SEC-001",
@@ -8062,21 +8268,21 @@ async function runSecurityScan(targetDir, options = {}) {
8062
8268
  const findings = [];
8063
8269
  let scannedCount = 0;
8064
8270
  function walk(current) {
8065
- if (scannedCount >= maxFiles || !existsSync18(current))
8271
+ if (scannedCount >= maxFiles || !existsSync19(current))
8066
8272
  return;
8067
8273
  let entries;
8068
8274
  try {
8069
- entries = readdirSync7(current);
8275
+ entries = readdirSync8(current);
8070
8276
  } catch {
8071
8277
  return;
8072
8278
  }
8073
8279
  for (const entry of entries) {
8074
8280
  if (scannedCount >= maxFiles)
8075
8281
  break;
8076
- const fullPath = join10(current, entry);
8282
+ const fullPath = join11(current, entry);
8077
8283
  let stat;
8078
8284
  try {
8079
- stat = statSync4(fullPath);
8285
+ stat = statSync5(fullPath);
8080
8286
  } catch {
8081
8287
  continue;
8082
8288
  }
@@ -8181,8 +8387,8 @@ var __dirname = "/home/runner/work/agent-cli/agent-cli/src/mcp/servers/sqlite";
8181
8387
  var SQLITE_MCP_SERVER_PATH = resolve20(__dirname, "server.ts");
8182
8388
 
8183
8389
  // src/init/project-analyzer.ts
8184
- import { existsSync as existsSync19, readFileSync as readFileSync13, readdirSync as readdirSync8 } from "fs";
8185
- import { join as join11, basename as basename3 } from "path";
8390
+ import { existsSync as existsSync20, readFileSync as readFileSync13, readdirSync as readdirSync9 } from "fs";
8391
+ import { join as join12, basename as basename3 } from "path";
8186
8392
 
8187
8393
  class ProjectAnalyzer {
8188
8394
  cwd;
@@ -8200,8 +8406,8 @@ class ProjectAnalyzer {
8200
8406
  const architectureNotes = [];
8201
8407
  const codeConventions = [];
8202
8408
  let description = readmeInfo.description;
8203
- const pkgPath = join11(this.cwd, "package.json");
8204
- if (existsSync19(pkgPath)) {
8409
+ const pkgPath = join12(this.cwd, "package.json");
8410
+ if (existsSync20(pkgPath)) {
8205
8411
  try {
8206
8412
  const pkg = JSON.parse(readFileSync13(pkgPath, "utf8"));
8207
8413
  if (!description && pkg.description)
@@ -8274,8 +8480,8 @@ class ProjectAnalyzer {
8274
8480
  }
8275
8481
  } catch {}
8276
8482
  }
8277
- const tsconfigPath = join11(this.cwd, "tsconfig.json");
8278
- if (existsSync19(tsconfigPath)) {
8483
+ const tsconfigPath = join12(this.cwd, "tsconfig.json");
8484
+ if (existsSync20(tsconfigPath)) {
8279
8485
  try {
8280
8486
  const tsconfig = JSON.parse(readFileSync13(tsconfigPath, "utf8"));
8281
8487
  if (tsconfig.compilerOptions?.strict) {
@@ -8286,8 +8492,8 @@ class ProjectAnalyzer {
8286
8492
  }
8287
8493
  } catch {}
8288
8494
  }
8289
- const cargoPath = join11(this.cwd, "Cargo.toml");
8290
- if (existsSync19(cargoPath)) {
8495
+ const cargoPath = join12(this.cwd, "Cargo.toml");
8496
+ if (existsSync20(cargoPath)) {
8291
8497
  try {
8292
8498
  commands.dev = commands.dev || "cargo run";
8293
8499
  commands.build = commands.build || "cargo build";
@@ -8296,8 +8502,8 @@ class ProjectAnalyzer {
8296
8502
  frameworks.push("Rust Cargo");
8297
8503
  } catch {}
8298
8504
  }
8299
- const goModPath = join11(this.cwd, "go.mod");
8300
- if (existsSync19(goModPath)) {
8505
+ const goModPath = join12(this.cwd, "go.mod");
8506
+ if (existsSync20(goModPath)) {
8301
8507
  try {
8302
8508
  commands.dev = commands.dev || "go run .";
8303
8509
  commands.build = commands.build || "go build ./...";
@@ -8306,37 +8512,37 @@ class ProjectAnalyzer {
8306
8512
  frameworks.push("Go Modules");
8307
8513
  } catch {}
8308
8514
  }
8309
- const pyprojectPath = join11(this.cwd, "pyproject.toml");
8310
- const requirementsPath = join11(this.cwd, "requirements.txt");
8311
- if (existsSync19(pyprojectPath) || existsSync19(requirementsPath)) {
8515
+ const pyprojectPath = join12(this.cwd, "pyproject.toml");
8516
+ const requirementsPath = join12(this.cwd, "requirements.txt");
8517
+ if (existsSync20(pyprojectPath) || existsSync20(requirementsPath)) {
8312
8518
  commands.test = commands.test || "pytest";
8313
8519
  commands.lint = commands.lint || "ruff check .";
8314
- if (existsSync19(join11(this.cwd, "uv.lock"))) {
8520
+ if (existsSync20(join12(this.cwd, "uv.lock"))) {
8315
8521
  frameworks.push("uv");
8316
8522
  commands.test = "uv run pytest";
8317
- } else if (existsSync19(join11(this.cwd, "poetry.lock"))) {
8523
+ } else if (existsSync20(join12(this.cwd, "poetry.lock"))) {
8318
8524
  frameworks.push("Poetry");
8319
8525
  commands.test = "poetry run pytest";
8320
8526
  }
8321
8527
  }
8322
- if (existsSync19(join11(this.cwd, "Dockerfile"))) {
8528
+ if (existsSync20(join12(this.cwd, "Dockerfile"))) {
8323
8529
  infrastructure.push("Docker");
8324
8530
  const sanitizedName = projectName.toLowerCase().replace(/[^a-z0-9_-]/g, "-").replace(/^-+|-+$/g, "");
8325
8531
  commands.dockerBuild = `docker build -t ${sanitizedName || "app"} .`;
8326
8532
  }
8327
- if (existsSync19(join11(this.cwd, "nginx.conf"))) {
8533
+ if (existsSync20(join12(this.cwd, "nginx.conf"))) {
8328
8534
  infrastructure.push("Nginx");
8329
8535
  }
8330
- if (existsSync19(join11(this.cwd, "src/api.ts")) || existsSync19(join11(this.cwd, "src/api"))) {
8536
+ if (existsSync20(join12(this.cwd, "src/api.ts")) || existsSync20(join12(this.cwd, "src/api"))) {
8331
8537
  architectureNotes.push("Backend API endpoints and network client logic are centralized in `src/api`.");
8332
8538
  }
8333
- if (existsSync19(join11(this.cwd, "src/components"))) {
8539
+ if (existsSync20(join12(this.cwd, "src/components"))) {
8334
8540
  architectureNotes.push("Reusable UI presentation components live in `src/components/`.");
8335
8541
  }
8336
- if (existsSync19(join11(this.cwd, "src/types.ts")) || existsSync19(join11(this.cwd, "src/types"))) {
8542
+ if (existsSync20(join12(this.cwd, "src/types.ts")) || existsSync20(join12(this.cwd, "src/types"))) {
8337
8543
  architectureNotes.push("Shared TypeScript data models and interfaces are defined in `src/types`.");
8338
8544
  }
8339
- if (existsSync19(join11(this.cwd, ".env.example"))) {
8545
+ if (existsSync20(join12(this.cwd, ".env.example"))) {
8340
8546
  architectureNotes.push("Environment configuration template is in `.env.example`.");
8341
8547
  }
8342
8548
  if (commands.typecheck || commands.lint || commands.test) {
@@ -8353,7 +8559,7 @@ class ProjectAnalyzer {
8353
8559
  let hasExistingInstructions = false;
8354
8560
  let existingInstructionFile;
8355
8561
  for (const f of instructionFiles) {
8356
- if (existsSync19(join11(this.cwd, f))) {
8562
+ if (existsSync20(join12(this.cwd, f))) {
8357
8563
  hasExistingInstructions = true;
8358
8564
  existingInstructionFile = f;
8359
8565
  break;
@@ -8434,8 +8640,8 @@ class ProjectAnalyzer {
8434
8640
  extractReadmeMetadata() {
8435
8641
  const readmeFiles = ["README.md", "readme.md", "README.MD"];
8436
8642
  for (const file of readmeFiles) {
8437
- const fullPath = join11(this.cwd, file);
8438
- if (existsSync19(fullPath)) {
8643
+ const fullPath = join12(this.cwd, file);
8644
+ if (existsSync20(fullPath)) {
8439
8645
  try {
8440
8646
  const content = readFileSync13(fullPath, "utf8");
8441
8647
  const lines = content.split(`
@@ -8460,8 +8666,8 @@ class ProjectAnalyzer {
8460
8666
  return {};
8461
8667
  }
8462
8668
  detectProjectName() {
8463
- const pkgPath = join11(this.cwd, "package.json");
8464
- if (existsSync19(pkgPath)) {
8669
+ const pkgPath = join12(this.cwd, "package.json");
8670
+ if (existsSync20(pkgPath)) {
8465
8671
  try {
8466
8672
  const pkg = JSON.parse(readFileSync13(pkgPath, "utf8"));
8467
8673
  if (pkg.name && pkg.name !== "frontend" && pkg.name !== "backend" && pkg.name !== "app") {
@@ -8469,16 +8675,16 @@ class ProjectAnalyzer {
8469
8675
  }
8470
8676
  } catch {}
8471
8677
  }
8472
- const cargoPath = join11(this.cwd, "Cargo.toml");
8473
- if (existsSync19(cargoPath)) {
8678
+ const cargoPath = join12(this.cwd, "Cargo.toml");
8679
+ if (existsSync20(cargoPath)) {
8474
8680
  try {
8475
8681
  const match = readFileSync13(cargoPath, "utf8").match(/name\s*=\s*"([^"]+)"/);
8476
8682
  if (match?.[1])
8477
8683
  return match[1];
8478
8684
  } catch {}
8479
8685
  }
8480
- const goModPath = join11(this.cwd, "go.mod");
8481
- if (existsSync19(goModPath)) {
8686
+ const goModPath = join12(this.cwd, "go.mod");
8687
+ if (existsSync20(goModPath)) {
8482
8688
  try {
8483
8689
  const match = readFileSync13(goModPath, "utf8").match(/module\s+([^\s]+)/);
8484
8690
  if (match?.[1])
@@ -8489,53 +8695,53 @@ class ProjectAnalyzer {
8489
8695
  }
8490
8696
  detectLanguages() {
8491
8697
  const langs = new Set;
8492
- if (existsSync19(join11(this.cwd, "tsconfig.json")) || this.hasFileWithExtension(".ts", ".tsx")) {
8698
+ if (existsSync20(join12(this.cwd, "tsconfig.json")) || this.hasFileWithExtension(".ts", ".tsx")) {
8493
8699
  langs.add("TypeScript");
8494
8700
  }
8495
- if (existsSync19(join11(this.cwd, "package.json")) || this.hasFileWithExtension(".js", ".jsx", ".mjs")) {
8701
+ if (existsSync20(join12(this.cwd, "package.json")) || this.hasFileWithExtension(".js", ".jsx", ".mjs")) {
8496
8702
  langs.add("JavaScript");
8497
8703
  }
8498
- if (existsSync19(join11(this.cwd, "Cargo.toml")) || this.hasFileWithExtension(".rs")) {
8704
+ if (existsSync20(join12(this.cwd, "Cargo.toml")) || this.hasFileWithExtension(".rs")) {
8499
8705
  langs.add("Rust");
8500
8706
  }
8501
- if (existsSync19(join11(this.cwd, "go.mod")) || this.hasFileWithExtension(".go")) {
8707
+ if (existsSync20(join12(this.cwd, "go.mod")) || this.hasFileWithExtension(".go")) {
8502
8708
  langs.add("Go");
8503
8709
  }
8504
- if (existsSync19(join11(this.cwd, "pyproject.toml")) || existsSync19(join11(this.cwd, "requirements.txt")) || this.hasFileWithExtension(".py")) {
8710
+ if (existsSync20(join12(this.cwd, "pyproject.toml")) || existsSync20(join12(this.cwd, "requirements.txt")) || this.hasFileWithExtension(".py")) {
8505
8711
  langs.add("Python");
8506
8712
  }
8507
- if (existsSync19(join11(this.cwd, "pom.xml")) || existsSync19(join11(this.cwd, "build.gradle")) || this.hasFileWithExtension(".java")) {
8713
+ if (existsSync20(join12(this.cwd, "pom.xml")) || existsSync20(join12(this.cwd, "build.gradle")) || this.hasFileWithExtension(".java")) {
8508
8714
  langs.add("Java");
8509
8715
  }
8510
- if (existsSync19(join11(this.cwd, "CMakeLists.txt")) || this.hasFileWithExtension(".cpp", ".c", ".h", ".hpp")) {
8716
+ if (existsSync20(join12(this.cwd, "CMakeLists.txt")) || this.hasFileWithExtension(".cpp", ".c", ".h", ".hpp")) {
8511
8717
  langs.add("C/C++");
8512
8718
  }
8513
8719
  return Array.from(langs);
8514
8720
  }
8515
8721
  detectPackageManager() {
8516
- if (existsSync19(join11(this.cwd, "bun.lockb")) || existsSync19(join11(this.cwd, "bun.lock")))
8722
+ if (existsSync20(join12(this.cwd, "bun.lockb")) || existsSync20(join12(this.cwd, "bun.lock")))
8517
8723
  return "bun";
8518
- if (existsSync19(join11(this.cwd, "pnpm-lock.yaml")))
8724
+ if (existsSync20(join12(this.cwd, "pnpm-lock.yaml")))
8519
8725
  return "pnpm";
8520
- if (existsSync19(join11(this.cwd, "yarn.lock")))
8726
+ if (existsSync20(join12(this.cwd, "yarn.lock")))
8521
8727
  return "yarn";
8522
- if (existsSync19(join11(this.cwd, "package-lock.json")))
8728
+ if (existsSync20(join12(this.cwd, "package-lock.json")))
8523
8729
  return "npm";
8524
- if (existsSync19(join11(this.cwd, "Cargo.lock")) || existsSync19(join11(this.cwd, "Cargo.toml")))
8730
+ if (existsSync20(join12(this.cwd, "Cargo.lock")) || existsSync20(join12(this.cwd, "Cargo.toml")))
8525
8731
  return "cargo";
8526
- if (existsSync19(join11(this.cwd, "uv.lock")))
8732
+ if (existsSync20(join12(this.cwd, "uv.lock")))
8527
8733
  return "uv";
8528
- if (existsSync19(join11(this.cwd, "poetry.lock")))
8734
+ if (existsSync20(join12(this.cwd, "poetry.lock")))
8529
8735
  return "poetry";
8530
- if (existsSync19(join11(this.cwd, "go.sum")) || existsSync19(join11(this.cwd, "go.mod")))
8736
+ if (existsSync20(join12(this.cwd, "go.sum")) || existsSync20(join12(this.cwd, "go.mod")))
8531
8737
  return "go";
8532
- if (existsSync19(join11(this.cwd, "package.json")))
8738
+ if (existsSync20(join12(this.cwd, "package.json")))
8533
8739
  return "npm";
8534
8740
  return;
8535
8741
  }
8536
8742
  hasFileWithExtension(...exts) {
8537
8743
  try {
8538
- const entries = readdirSync8(this.cwd);
8744
+ const entries = readdirSync9(this.cwd);
8539
8745
  return entries.some((e) => exts.some((ext) => e.endsWith(ext)));
8540
8746
  } catch {
8541
8747
  return false;
@@ -8543,16 +8749,16 @@ class ProjectAnalyzer {
8543
8749
  }
8544
8750
  }
8545
8751
  // src/init/init-command.ts
8546
- import { existsSync as existsSync20, writeFileSync as writeFileSync7 } from "fs";
8547
- import { join as join12 } from "path";
8752
+ import { existsSync as existsSync21, writeFileSync as writeFileSync7 } from "fs";
8753
+ import { join as join13 } from "path";
8548
8754
  function runProjectInit(options = {}) {
8549
8755
  const cwd = options.cwd || process.cwd();
8550
8756
  const filename = options.filename || "AGENTS.md";
8551
- const targetPath = join12(cwd, filename);
8757
+ const targetPath = join13(cwd, filename);
8552
8758
  const analyzer = new ProjectAnalyzer(cwd);
8553
8759
  const analysis = analyzer.analyze();
8554
8760
  const content = analyzer.generateAgentsMarkdown(analysis);
8555
- const alreadyExists = existsSync20(targetPath);
8761
+ const alreadyExists = existsSync21(targetPath);
8556
8762
  writeFileSync7(targetPath, content, "utf8");
8557
8763
  return {
8558
8764
  success: true,
@@ -9326,7 +9532,7 @@ function printSessionStats(ctx) {
9326
9532
  const turns = ctx.repl?.turnCount ?? 0;
9327
9533
  const history = ctx.session.getHistory();
9328
9534
  const historyTokens = estimateTotalTokens(history);
9329
- const maxTokens = 128000;
9535
+ const maxTokens = DEFAULT_MAX_CONTEXT_TOKENS;
9330
9536
  const contextPct = Math.round(historyTokens / maxTokens * 100);
9331
9537
  const colorFn = contextPct < 50 ? style.green : contextPct < 80 ? style.yellow : style.red;
9332
9538
  const agents = ctx.spawner?.listAgents() || [];
@@ -10017,9 +10223,9 @@ class MarkdownHighlighter {
10017
10223
  }
10018
10224
 
10019
10225
  // src/cli/update-checker.ts
10020
- import { existsSync as existsSync21, mkdirSync as mkdirSync11, readFileSync as readFileSync14, writeFileSync as writeFileSync8 } from "fs";
10226
+ import { existsSync as existsSync22, mkdirSync as mkdirSync12, readFileSync as readFileSync14, writeFileSync as writeFileSync8 } from "fs";
10021
10227
  import { homedir as homedir5 } from "os";
10022
- import { join as join13 } from "path";
10228
+ import { join as join14 } from "path";
10023
10229
  var CHECK_INTERVAL_MS = 12 * 60 * 60 * 1000;
10024
10230
  function parseSemver(v) {
10025
10231
  const clean = v.replace(/^v/, "").trim();
@@ -10040,8 +10246,8 @@ function isNewerVersion(current, remote) {
10040
10246
  return remPatch > curPatch;
10041
10247
  }
10042
10248
  function getUpdateCachePath() {
10043
- const baseDir = process.env.PIKAA_HOME || process.env.GROUPY_HOME || join13(homedir5(), ".pikaa");
10044
- return join13(baseDir, "update-cache.json");
10249
+ const baseDir = process.env.PIKAA_HOME || process.env.GROUPY_HOME || join14(homedir5(), ".pikaa");
10250
+ return join14(baseDir, "update-cache.json");
10045
10251
  }
10046
10252
  async function fetchLatestNpmVersion(packageName, timeoutMs = 1500) {
10047
10253
  const url = `https://registry.npmjs.org/${encodeURIComponent(packageName)}/latest`;
@@ -10074,7 +10280,7 @@ async function checkForUpdates(options = {}) {
10074
10280
  const cachePath = options.cachePath || getUpdateCachePath();
10075
10281
  const now = Date.now();
10076
10282
  let cached = null;
10077
- if (!options.force && existsSync21(cachePath)) {
10283
+ if (!options.force && existsSync22(cachePath)) {
10078
10284
  try {
10079
10285
  const raw = JSON.parse(readFileSync14(cachePath, "utf8"));
10080
10286
  if (raw && typeof raw.lastChecked === "number" && typeof raw.latestVersion === "string") {
@@ -10104,9 +10310,9 @@ async function checkForUpdates(options = {}) {
10104
10310
  return null;
10105
10311
  }
10106
10312
  try {
10107
- const parentDir = join13(cachePath, "..");
10108
- if (!existsSync21(parentDir)) {
10109
- mkdirSync11(parentDir, { recursive: true });
10313
+ const parentDir = join14(cachePath, "..");
10314
+ if (!existsSync22(parentDir)) {
10315
+ mkdirSync12(parentDir, { recursive: true });
10110
10316
  }
10111
10317
  const cacheData = {
10112
10318
  lastChecked: now,
@@ -10270,6 +10476,7 @@ class CliRepl {
10270
10476
  inputTokens: msg.inputTokens,
10271
10477
  outputTokens: msg.outputTokens || (this.turnCharsOut > 0 ? Math.round(this.turnCharsOut / 3.8) : undefined),
10272
10478
  totalTokens: msg.totalTokens,
10479
+ cachedTokens: msg.cachedTokens,
10273
10480
  contextTokens: msg.contextTokens,
10274
10481
  maxContextTokens: msg.maxContextTokens,
10275
10482
  sessionUptimeMs,
@@ -10528,7 +10735,7 @@ async function main() {
10528
10735
  resolve21(cwd, "mcp_config.json")
10529
10736
  ].filter(Boolean);
10530
10737
  for (const cfg of candidateConfigs) {
10531
- if (existsSync22(cfg)) {
10738
+ if (existsSync23(cfg)) {
10532
10739
  try {
10533
10740
  await mcpManager.loadConfigFile(cfg);
10534
10741
  mcpManager.registerToolsIntoRouter(tools4);