perchai-cli 2.4.45 → 2.4.46

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.
Files changed (2) hide show
  1. package/dist/perch.mjs +255 -22
  2. package/package.json +1 -1
package/dist/perch.mjs CHANGED
@@ -75566,7 +75566,6 @@ var init_payroll = __esm({
75566
75566
  // lib/perchBusinessTools/index.ts
75567
75567
  var init_perchBusinessTools = __esm({
75568
75568
  "lib/perchBusinessTools/index.ts"() {
75569
- "use strict";
75570
75569
  init_generateAPAuditPacket();
75571
75570
  init_inventoryFolder();
75572
75571
  init_loadBusinessTables();
@@ -75918,7 +75917,6 @@ function isTurnAbortedError(error) {
75918
75917
  var TURN_STOPPED_BY_USER_MESSAGE;
75919
75918
  var init_turnAbort = __esm({
75920
75919
  "features/perchTerminal/runtime/turnAbort.ts"() {
75921
- "use strict";
75922
75920
  TURN_STOPPED_BY_USER_MESSAGE = "Turn stopped by user.";
75923
75921
  }
75924
75922
  });
@@ -76224,7 +76222,6 @@ function getToolDisplayName(toolName) {
76224
76222
  var NON_MODULE_TOOL_OWNERS, TOOL_RISK, TOOL_DISPLAY_NAMES;
76225
76223
  var init_catalog = __esm({
76226
76224
  "features/perchTerminal/runtime/toolSystem/catalog.ts"() {
76227
- "use strict";
76228
76225
  init_toolNames();
76229
76226
  NON_MODULE_TOOL_OWNERS = {
76230
76227
  [TOOL_NAMES.listSources]: "lane",
@@ -86739,6 +86736,7 @@ function truncateHistoryLine(value, max2) {
86739
86736
  }
86740
86737
  var init_operatorTruth = __esm({
86741
86738
  "features/perchTerminal/runtime/operatorTruth.ts"() {
86739
+ "use strict";
86742
86740
  }
86743
86741
  });
86744
86742
 
@@ -93085,7 +93083,7 @@ var init_agentSkillRegistry = __esm({
93085
93083
  "name": "wait-operator",
93086
93084
  "description": "Watch bounded external or local conditions, poll deployment and job status, wait through short-running tasks, and resume with verified state instead of guessing.",
93087
93085
  "path": "features/perchTerminal/agentPlatform/skills/wait-operator/SKILL.md",
93088
- "body": "\n# wait-operator\n\nUse this skill when the user asks Perch to wait, watch, poll, monitor, check back, or keep an eye on a deployment, CI run, background job, file creation, local server, browser page, or other condition.\n\n## Operating Rules\n\n- Treat waiting as an active bounded loop: define the condition, timeout, interval, and failure states before polling.\n- Never sleep indefinitely. Use short intervals and a clear maximum wait unless the user explicitly asks for a longer watch.\n- Prefer read-only checks: status commands, existing logs, HTTP status endpoints, browser observe/wait, or file existence checks.\n- For deployments and CI, distinguish pending, ready/success, failed, canceled, timed out, and unknown. Do not call a run ready from elapsed time alone.\n- Report the last observed state if the wait times out or credentials/session state blocks verification.\n- Avoid noisy transcript spam. Group repeated polling into a short progress summary unless the user asks to see every check.\n- If the user asks for a real follow-up after this turn rather than an in-turn wait, create or suggest the appropriate automation instead of pretending the current turn can run forever.\n\n## Verification\n\nFinish only after the watched condition is directly verified or a blocker is named. Include the evidence source: command status, page state, URL, log line, file path, or timestamped last check.",
93086
+ "body": "\n# wait-operator\n\nUse this skill when the user asks Perch to wait, watch, poll, monitor, check back, or keep an eye on a deployment, CI run, background job, file creation, local server, browser page, or other condition.\n\n## Operating Rules\n\n- **Background tasks (run_in_background):** after backgrounding a long-running command, call wait_for_background_task(taskId) to end your turn and be automatically woken with the result. NEVER busy-poll check_background_task in a loop \u2014 that wastes iterations and never triggers the wake path. One check is fine for a specific reason (e.g. reporting current progress on user request); repeated polling is always wrong.\n- Treat waiting as an active bounded loop: define the condition, timeout, interval, and failure states before polling.\n- Never sleep indefinitely. Use short intervals and a clear maximum wait unless the user explicitly asks for a longer watch.\n- Prefer read-only checks: status commands, existing logs, HTTP status endpoints, browser observe/wait, or file existence checks.\n- For deployments and CI, distinguish pending, ready/success, failed, canceled, timed out, and unknown. Do not call a run ready from elapsed time alone.\n- Report the last observed state if the wait times out or credentials/session state blocks verification.\n- Avoid noisy transcript spam. Group repeated polling into a short progress summary unless the user asks to see every check.\n- If the user asks for a real follow-up after this turn rather than an in-turn wait, create or suggest the appropriate automation instead of pretending the current turn can run forever.\n\n## Verification\n\nFinish only after the watched condition is directly verified or a blocker is named. Include the evidence source: command status, page state, URL, log line, file path, or timestamped last check.",
93089
93087
  "surface": {
93090
93088
  "gui": "supported",
93091
93089
  "cli": "supported"
@@ -135384,7 +135382,7 @@ function getDesktopToolDefinitions() {
135384
135382
  type: "function",
135385
135383
  function: {
135386
135384
  name: TOOL_NAMES.runInBackground,
135387
- description: "Launch a long-running shell command in the background. Returns immediately with a taskId; the command keeps running detached. Use check_background_task to poll, or wait_for_background_task to be woken when it finishes. Permission mode gates execution.",
135385
+ description: "Launch a long-running shell command in the background. Returns immediately with a taskId; the command keeps running detached. After launching, call wait_for_background_task(taskId) to end your turn and be automatically re-invoked when it finishes. Permission mode gates execution.",
135388
135386
  parameters: {
135389
135387
  type: "object",
135390
135388
  properties: {
@@ -135406,7 +135404,7 @@ function getDesktopToolDefinitions() {
135406
135404
  type: "function",
135407
135405
  function: {
135408
135406
  name: TOOL_NAMES.checkBackgroundTask,
135409
- description: "Check the status and recent output of a background task by taskId. Returns status (running/done/failed/killed/unknown), exit code, runtime, and an output preview.",
135407
+ description: "One-off status check for a background task. Returns status, exit code, runtime, and output preview. Do NOT call this repeatedly to wait for completion \u2014 use wait_for_background_task instead.",
135410
135408
  parameters: {
135411
135409
  type: "object",
135412
135410
  properties: {
@@ -135448,7 +135446,7 @@ function getDesktopToolDefinitions() {
135448
135446
  type: "function",
135449
135447
  function: {
135450
135448
  name: TOOL_NAMES.waitForBackgroundTask,
135451
- description: "Ask to be woken when a background task finishes. This does NOT block: it registers a wake, and you should end your turn. The runtime will re-invoke you with a system-initiated turn carrying the task result once it completes. Auto-wake only fires in autonomous permission modes.",
135449
+ description: "After launching long-running work, call this to end your turn and be automatically re-invoked when it finishes. This is how you wait \u2014 do NOT poll check_background_task in a loop. The runtime delivers a system-initiated turn with the result once the task completes. Auto-wake fires in autonomous permission modes (auto_review / take_the_wheel).",
135452
135450
  parameters: {
135453
135451
  type: "object",
135454
135452
  properties: {
@@ -197126,6 +197124,7 @@ function recordDispatchBatchResult(tasks, result2, ctx, opts = {}) {
197126
197124
  var lifecycleByRun, MAX_LIFECYCLE_RUNS;
197127
197125
  var init_workerLifecycle = __esm({
197128
197126
  "features/perchTerminal/runtime/workers/workerLifecycle.ts"() {
197127
+ "use strict";
197129
197128
  init_workerManifest();
197130
197129
  lifecycleByRun = /* @__PURE__ */ new Map();
197131
197130
  MAX_LIFECYCLE_RUNS = 200;
@@ -212049,9 +212048,13 @@ var init_backgroundWake = __esm({
212049
212048
  // features/perchTerminal/runtime/toolSystem/tools/background/index.ts
212050
212049
  var background_exports = {};
212051
212050
  __export(background_exports, {
212051
+ __resetPollTrackerForTests: () => __resetPollTrackerForTests,
212052
212052
  backgroundTools: () => backgroundTools
212053
212053
  });
212054
- var UNAVAILABLE, runInBackgroundTool, checkBackgroundTaskTool, listBackgroundTasksTool, killBackgroundTaskTool, waitForBackgroundTaskTool, backgroundTools;
212054
+ function __resetPollTrackerForTests() {
212055
+ pollTracker.clear();
212056
+ }
212057
+ var UNAVAILABLE, pollTracker, POLL_REDIRECT_THRESHOLD, runInBackgroundTool, checkBackgroundTaskTool, listBackgroundTasksTool, killBackgroundTaskTool, waitForBackgroundTaskTool, backgroundTools;
212055
212058
  var init_background = __esm({
212056
212059
  "features/perchTerminal/runtime/toolSystem/tools/background/index.ts"() {
212057
212060
  "use strict";
@@ -212060,6 +212063,8 @@ var init_background = __esm({
212060
212063
  init_backgroundWake();
212061
212064
  init_toolNames();
212062
212065
  UNAVAILABLE = "Background execution is unavailable on this surface. It requires the Perch CLI or Perch Desktop.";
212066
+ pollTracker = /* @__PURE__ */ new Map();
212067
+ POLL_REDIRECT_THRESHOLD = 2;
212063
212068
  runInBackgroundTool = {
212064
212069
  name: TOOL_NAMES.runInBackground,
212065
212070
  classification: { native: false },
@@ -212079,11 +212084,26 @@ var init_background = __esm({
212079
212084
  checkBackgroundTaskTool = {
212080
212085
  name: TOOL_NAMES.checkBackgroundTask,
212081
212086
  classification: { native: false },
212082
- handler: async (args) => {
212087
+ handler: async (args, ctx) => {
212083
212088
  const taskId = String(args.taskId ?? "").trim();
212084
212089
  if (!taskId) return { ok: false, error: "taskId must be a non-empty string." };
212085
212090
  const snapshot = await checkBackgroundTask({ taskId });
212086
212091
  if (snapshot === null) return { ok: false, error: `No background task found for ${taskId}.` };
212092
+ if (snapshot.status === "running") {
212093
+ const key = taskId;
212094
+ const count = (pollTracker.get(key) ?? 0) + 1;
212095
+ pollTracker.set(key, count);
212096
+ if (count >= POLL_REDIRECT_THRESHOLD) {
212097
+ const mode = normalizePermissionMode(ctx.permissionMode);
212098
+ const wakeAvailable = WAKE_ALLOWED_PERMISSION_MODES.has(mode);
212099
+ return {
212100
+ ok: true,
212101
+ task: snapshot,
212102
+ _pollRedirect: true,
212103
+ note: wakeAvailable ? `Stop polling \u2014 call wait_for_background_task("${taskId}") to end this turn and be automatically woken on completion.` : `This task is still running. Auto-wake is unavailable in "${mode}" mode. Tell the user to switch to auto_review or take_the_wheel for auto-wake, or end this turn and check back later.`
212104
+ };
212105
+ }
212106
+ }
212087
212107
  return { ok: true, task: snapshot };
212088
212108
  }
212089
212109
  };
@@ -231787,7 +231807,7 @@ function runBackgroundCommand2(input) {
231787
231807
  const shell = input.shell || process.env.SHELL || "/bin/zsh";
231788
231808
  let child;
231789
231809
  try {
231790
- child = spawn2(shell, ["-lc", command], {
231810
+ child = spawn2(shell, ["-ilc", command], {
231791
231811
  cwd: input.cwd,
231792
231812
  env: process.env,
231793
231813
  stdio: ["ignore", "pipe", "pipe"],
@@ -233811,6 +233831,37 @@ var init_cliStandaloneOAuth = __esm({
233811
233831
  }
233812
233832
  });
233813
233833
 
233834
+ // lib/perchTerminal/statusLabels.ts
233835
+ function sectionLabel(sectionKey) {
233836
+ if (STATUS_LABELS[sectionKey]) return STATUS_LABELS[sectionKey];
233837
+ return sectionKey.replace(/_/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
233838
+ }
233839
+ var STATUS_LABELS;
233840
+ var init_statusLabels = __esm({
233841
+ "lib/perchTerminal/statusLabels.ts"() {
233842
+ STATUS_LABELS = {
233843
+ not_found: "Not found",
233844
+ not_initialized: "Not initialized",
233845
+ sent_raw: "Included",
233846
+ sent_compacted: "Summarized",
233847
+ output_reserve: "Reserved for response",
233848
+ excluded: "Excluded",
233849
+ skipped: "Skipped",
233850
+ reserved: "Reserved",
233851
+ generated: "Generated",
233852
+ error: "Error",
233853
+ completed: "Completed",
233854
+ failed: "Failed",
233855
+ running: "Running",
233856
+ pending: "Pending",
233857
+ blocked: "Blocked",
233858
+ in_progress: "In progress",
233859
+ sent: "Sent",
233860
+ compacted: "Summarized"
233861
+ };
233862
+ }
233863
+ });
233864
+
233814
233865
  // features/perchTerminal/runtime/contextMeterDisplay.ts
233815
233866
  function positiveInt3(value) {
233816
233867
  if (typeof value !== "number" || !Number.isFinite(value) || value <= 0) return null;
@@ -233870,13 +233921,182 @@ function resolveContextMeterDisplay(snapshot, activeModel) {
233870
233921
  hasWorkers
233871
233922
  };
233872
233923
  }
233873
- var ADMITTED_ROW_STATUSES2;
233924
+ function displayCategoryOrder(key) {
233925
+ if (key.startsWith("mcp_tools")) {
233926
+ const idx2 = DISPLAY_CATEGORY_ORDER.indexOf("mcp_tools");
233927
+ return idx2;
233928
+ }
233929
+ const idx = DISPLAY_CATEGORY_ORDER.indexOf(key);
233930
+ return idx === -1 ? DISPLAY_CATEGORY_ORDER.length : idx;
233931
+ }
233932
+ function displayCategoryForLane(lane) {
233933
+ if (lane.startsWith("mcp_tools")) {
233934
+ return { key: lane, label: sectionLabel(lane) };
233935
+ }
233936
+ switch (lane) {
233937
+ case "system":
233938
+ case "system_prompt":
233939
+ case "selected_model":
233940
+ return { key: "system_prompt", label: "System prompt" };
233941
+ case "tools":
233942
+ case "tool_definitions":
233943
+ case "system_tools":
233944
+ return { key: "system_tools", label: "System tools" };
233945
+ case "mcp_tools":
233946
+ return { key: "mcp_tools", label: "MCP tools" };
233947
+ case "skills":
233948
+ case "agent_skills":
233949
+ return { key: "skills", label: "Skills" };
233950
+ case "output_response":
233951
+ return { key: "output_response", label: "Output response" };
233952
+ case "output_reserve":
233953
+ case "reserved_output":
233954
+ return { key: "reserved_output", label: "Reserved output" };
233955
+ case "deferred":
233956
+ return { key: "deferred", label: "Deferred" };
233957
+ case "messages":
233958
+ case "conversation":
233959
+ case "summarized_conversation":
233960
+ return { key: "conversation", label: "Conversation" };
233961
+ case "tool_transcript":
233962
+ return { key: "tool_transcript", label: "Tool transcript" };
233963
+ case "project_memory":
233964
+ case "permanent_memory":
233965
+ case "rules_project_memory":
233966
+ case "durable_memory":
233967
+ return { key: "memory_rules", label: "Memory & rules" };
233968
+ case "tool_evidence":
233969
+ case "thread_evidence":
233970
+ case "retrieved_evidence":
233971
+ case "files":
233972
+ case "selected_source":
233973
+ return { key: "evidence", label: "Evidence" };
233974
+ case "workers":
233975
+ return { key: "workers", label: "Workers" };
233976
+ case "desktop":
233977
+ case "workspace_context":
233978
+ return { key: "workspace_context", label: "Workspace context" };
233979
+ case "wire_overhead":
233980
+ return { key: "overhead", label: "Overhead" };
233981
+ default:
233982
+ return { key: lane, label: sectionLabel(lane) };
233983
+ }
233984
+ }
233985
+ function isCacheKey(key) {
233986
+ return key.includes("cache");
233987
+ }
233988
+ function segmentsFromCategories(categories) {
233989
+ return categories.filter((category) => category.tokens > 0 && !isCacheKey(category.key)).map((category) => ({
233990
+ key: category.key,
233991
+ label: category.label,
233992
+ tokens: category.tokens
233993
+ }));
233994
+ }
233995
+ function segmentsFromLaneTotals(laneTotals) {
233996
+ return laneTotals.filter((lane) => lane.lane !== "output_reserve" && lane.tokens > 0 && !isCacheKey(lane.lane)).map((lane) => ({ key: lane.lane, label: sectionLabel(lane.lane), tokens: lane.tokens }));
233997
+ }
233998
+ function segmentsFromRows(rows) {
233999
+ const byLane = /* @__PURE__ */ new Map();
234000
+ for (const row of rows) {
234001
+ if (row.lane === "output_reserve") continue;
234002
+ if (isCacheKey(row.lane)) continue;
234003
+ if (!ADMITTED_ROW_STATUSES2.has(row.status)) continue;
234004
+ byLane.set(row.lane, (byLane.get(row.lane) ?? 0) + row.estimatedTokens);
234005
+ }
234006
+ return Array.from(byLane.entries()).filter(([, tokens]) => tokens > 0).map(([key, tokens]) => ({ key, label: sectionLabel(key), tokens }));
234007
+ }
234008
+ function segmentsFromBreakdown(breakdown) {
234009
+ return Object.entries(breakdown).filter(([key, val]) => val.estimatedTokens > 0 && !isCacheKey(key)).map(([key, val]) => ({ key, label: sectionLabel(key), tokens: val.estimatedTokens }));
234010
+ }
234011
+ function groupDisplaySegments(segments) {
234012
+ const grouped = /* @__PURE__ */ new Map();
234013
+ for (const segment of segments) {
234014
+ const display = displayCategoryForLane(segment.key);
234015
+ const existing = grouped.get(display.key);
234016
+ if (existing) {
234017
+ existing.tokens += segment.tokens;
234018
+ } else {
234019
+ grouped.set(display.key, { key: display.key, label: display.label, tokens: segment.tokens });
234020
+ }
234021
+ }
234022
+ return Array.from(grouped.values());
234023
+ }
234024
+ function buildContentSegments(snapshot) {
234025
+ if (!snapshot) return [];
234026
+ if (snapshot.contextCategories?.length) {
234027
+ return segmentsFromCategories(snapshot.contextCategories);
234028
+ }
234029
+ const source = snapshot.laneTotals?.length ? segmentsFromLaneTotals(snapshot.laneTotals) : snapshot.rows ? segmentsFromRows(snapshot.rows) : segmentsFromBreakdown(snapshot.sectionBreakdown ?? {});
234030
+ return groupDisplaySegments(source);
234031
+ }
234032
+ function resolveTruncationState(snapshot) {
234033
+ if (snapshot?.truncated) return "truncated";
234034
+ if (snapshot?.compacted) return "compacted";
234035
+ return "none";
234036
+ }
234037
+ function buildContextComposition(snapshot, activeModel) {
234038
+ const meter = resolveContextMeterDisplay(snapshot, activeModel);
234039
+ const totalTokens = Math.max(1, meter.limitTokens);
234040
+ const reservedOutputTokens = Math.max(0, meter.reservedOutputTokens);
234041
+ const segments = buildContentSegments(snapshot);
234042
+ if (reservedOutputTokens > 0) {
234043
+ segments.push({ key: "reserved_output", label: "Reserved output", tokens: reservedOutputTokens });
234044
+ }
234045
+ const ordered = segments.filter((segment) => segment.tokens > 0).sort((a, b2) => displayCategoryOrder(a.key) - displayCategoryOrder(b2.key));
234046
+ const usedTokens = Math.min(
234047
+ totalTokens,
234048
+ ordered.reduce((sum, segment) => sum + segment.tokens, 0)
234049
+ );
234050
+ const freeSpaceTokens = Math.max(0, totalTokens - usedTokens);
234051
+ const slices = ordered.map((segment) => ({
234052
+ key: segment.key,
234053
+ label: segment.label,
234054
+ tokens: segment.tokens,
234055
+ pct: segment.tokens / totalTokens * 100
234056
+ }));
234057
+ return {
234058
+ slices,
234059
+ freeSpaceTokens,
234060
+ freeSpacePct: freeSpaceTokens / totalTokens * 100,
234061
+ usedTokens,
234062
+ totalTokens,
234063
+ usedPercent: usedTokens / totalTokens * 100,
234064
+ truncationState: resolveTruncationState(snapshot)
234065
+ };
234066
+ }
234067
+ function contextTruncationNotice(state) {
234068
+ switch (state) {
234069
+ case "compacted":
234070
+ return "\u26A0 Context full \u2014 older messages were compacted to fit";
234071
+ case "truncated":
234072
+ return "\u26A0 Context full \u2014 older messages were dropped to fit";
234073
+ default:
234074
+ return "";
234075
+ }
234076
+ }
234077
+ var ADMITTED_ROW_STATUSES2, DISPLAY_CATEGORY_ORDER;
233874
234078
  var init_contextMeterDisplay = __esm({
233875
234079
  "features/perchTerminal/runtime/contextMeterDisplay.ts"() {
233876
234080
  "use strict";
234081
+ init_statusLabels();
233877
234082
  init_contextMeter();
233878
234083
  init_contextThresholds();
233879
234084
  ADMITTED_ROW_STATUSES2 = /* @__PURE__ */ new Set(["sent_raw", "sent_compacted"]);
234085
+ DISPLAY_CATEGORY_ORDER = [
234086
+ "system_prompt",
234087
+ "system_tools",
234088
+ "mcp_tools",
234089
+ "skills",
234090
+ "conversation",
234091
+ "tool_transcript",
234092
+ "memory_rules",
234093
+ "evidence",
234094
+ "workspace_context",
234095
+ "workers",
234096
+ "overhead",
234097
+ "deferred",
234098
+ "reserved_output"
234099
+ ];
233880
234100
  }
233881
234101
  });
233882
234102
 
@@ -290603,20 +290823,33 @@ function renderCliContextMeter(snapshot, activeModel) {
290603
290823
  if (snapshot.compacted || compacted > 0) parts.push(`compacted ${compacted}`);
290604
290824
  return parts.join(" \xB7 ");
290605
290825
  }
290826
+ function renderCliBlockBar(percent2, width) {
290827
+ const clamped = Math.max(0, Math.min(100, percent2));
290828
+ const filled = Math.round(clamped / 100 * width);
290829
+ return `\u2595${"\u2588".repeat(filled)}${"\u2591".repeat(Math.max(0, width - filled))}\u258F`;
290830
+ }
290606
290831
  function renderCliContextDetails(snapshot, activeModel) {
290607
290832
  if (!snapshot) return "context no meter yet\n";
290608
- const meter = resolveContextMeterDisplay(snapshot, activeModel);
290609
- const percent2 = snapshot.contextPercentage ?? Math.round(meter.committedTokens / Math.max(1, meter.limitTokens) * 100);
290610
- const lines = [
290611
- ["context", renderCliContextMeter(snapshot, activeModel)],
290612
- ["thread", `${formatCliTokenCount(meter.threadTokens)} tokens`],
290613
- ["limit", `${formatCliTokenCount(meter.limitTokens)} tokens`],
290614
- ["fill", `${Math.max(0, Math.min(100, percent2))}%`],
290615
- ["compacted", snapshot.compacted || (snapshot.compactedRowCount ?? 0) > 0 ? `${snapshot.compactedRowCount ?? 0} row(s)` : "no"],
290616
- ["not-found", `${snapshot.notFoundRowCount ?? 0} row(s)`]
290617
- ];
290618
290833
  const color = shouldUseCliColor();
290619
- return lines.map(([key, value]) => `${paint(key.padEnd(11), "muted", color)} ${value}`).join("\n") + "\n";
290834
+ const composition = buildContextComposition(snapshot, activeModel);
290835
+ const percent2 = Math.max(0, Math.min(100, Math.round(composition.usedPercent)));
290836
+ const bar = renderCliBlockBar(composition.usedPercent, 24);
290837
+ const header = `${paint("context".padEnd(11), "muted", color)} ${paint(bar, percent2 >= 90 ? "red" : "accent", color)} ${formatCliTokenCount(composition.usedTokens)} / ${formatCliTokenCount(composition.totalTokens)} (${percent2}%)`;
290838
+ const rows = [
290839
+ ...composition.slices.map((slice2) => ({ label: slice2.label, tokens: slice2.tokens, pct: slice2.pct })),
290840
+ { label: "Free space", tokens: composition.freeSpaceTokens, pct: composition.freeSpacePct }
290841
+ ];
290842
+ const labelWidth = Math.max(...rows.map((row) => row.label.length));
290843
+ const tokenWidth = Math.max(...rows.map((row) => formatCliTokenCount(row.tokens).length));
290844
+ const lines = rows.map((row) => {
290845
+ const label = paint(` ${row.label.padEnd(labelWidth)}`, "muted", color);
290846
+ const tokens = formatCliTokenCount(row.tokens).padStart(tokenWidth);
290847
+ return `${label} ${tokens} \xB7 ${row.pct.toFixed(1).padStart(4)}%`;
290848
+ });
290849
+ const out = [header, ...lines];
290850
+ const notice = contextTruncationNotice(composition.truncationState);
290851
+ if (notice) out.push(paint(notice, "red", color));
290852
+ return out.join("\n") + "\n";
290620
290853
  }
290621
290854
  function formatCliTokenCount(value) {
290622
290855
  if (value < 1e3) return `${Math.max(0, Math.round(value))}`;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "perchai-cli",
3
- "version": "2.4.45",
3
+ "version": "2.4.46",
4
4
  "description": "Perch AI command-line interface",
5
5
  "bin": {
6
6
  "perch": "bin/perch"