pi-ui-extend 0.1.33 → 0.1.34

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/app/icons.js CHANGED
@@ -43,7 +43,7 @@ const NERD_FONT_ICONS = {
43
43
  down: "↓",
44
44
  };
45
45
  const FALLBACK_ICONS = {
46
- alert: "!",
46
+ alert: "",
47
47
  autoFix: "*",
48
48
  check: "✓",
49
49
  checkCircle: "✓",
@@ -7,13 +7,14 @@ export type ModelUsageDescriptor = BaseModelUsageDescriptor & ({
7
7
  } | {
8
8
  readonly kind: "google-antigravity";
9
9
  readonly quotaModelKey: string;
10
- readonly account: AntigravityQuotaAccount;
10
+ readonly account?: AntigravityQuotaAccount;
11
11
  readonly accounts?: readonly AntigravityQuotaAccount[];
12
12
  });
13
13
  export type ModelUsageLimitWindow = {
14
14
  readonly remainingPercent: number;
15
15
  readonly resetAt: number;
16
16
  readonly windowSeconds: number;
17
+ readonly hasKnownWindowDuration?: boolean;
17
18
  };
18
19
  export type ModelUsageStatus = {
19
20
  readonly modelKey: string;
@@ -1,9 +1,9 @@
1
1
  import { createHash } from "node:crypto";
2
- import { readFileSync } from "node:fs";
3
2
  import { readFile } from "node:fs/promises";
4
3
  import { homedir } from "node:os";
5
4
  import { join } from "node:path";
6
5
  import { formatCompactProgressBar } from "../../context-progress-bar.js";
6
+ import { APP_ICONS } from "../icons.js";
7
7
  const OPENAI_USAGE_URL = "https://chatgpt.com/backend-api/wham/usage";
8
8
  const ZAI_QUOTA_URL = "https://api.z.ai/api/monitor/usage/quota/limit";
9
9
  const ZHIPU_QUOTA_URL = "https://bigmodel.cn/api/monitor/usage/quota/limit";
@@ -35,16 +35,12 @@ export function modelUsageDescriptor(model) {
35
35
  }
36
36
  if (ANTIGRAVITY_QUOTA_PROVIDERS.has(provider)) {
37
37
  const quotaModelKey = resolveAntigravityQuotaModelKey(model);
38
- const accounts = readAllAntigravityQuotaAccounts();
39
- const account = readActiveAntigravityQuotaAccount(accounts);
40
- if (!quotaModelKey || !account)
38
+ if (!quotaModelKey)
41
39
  return undefined;
42
40
  return {
43
41
  kind: "google-antigravity",
44
- modelKey: `${model.provider}/${model.id}@all:${accounts.map((item) => item.cacheKey).join(",")}`,
42
+ modelKey: `${model.provider}/${model.id}`,
45
43
  quotaModelKey,
46
- account,
47
- accounts,
48
44
  };
49
45
  }
50
46
  return undefined;
@@ -395,7 +391,7 @@ export function googleAntigravityUsageStatusFromResponse(data, descriptor, now =
395
391
  modelKey: descriptor.modelKey,
396
392
  provider: "google-antigravity",
397
393
  updatedAt: now,
398
- ...(descriptor.account.email ? { accountEmail: descriptor.account.email } : {}),
394
+ ...(descriptor.account?.email ? { accountEmail: descriptor.account.email } : {}),
399
395
  ...(weekly ? { weekly } : {}),
400
396
  ...(hourly ? { hourly } : {}),
401
397
  };
@@ -413,7 +409,9 @@ function googleAntigravityWindowFromResponse(data, quotaModelKey, now) {
413
409
  }
414
410
  async function queryGoogleAntigravityModelUsage(descriptor) {
415
411
  const now = Date.now();
416
- const accounts = descriptor.accounts?.length ? descriptor.accounts : [descriptor.account];
412
+ const accounts = await readAllAntigravityQuotaAccounts();
413
+ if (accounts.length === 0)
414
+ return undefined;
417
415
  const windows = (await Promise.all(accounts.map(async (account) => {
418
416
  try {
419
417
  const response = await fetchGoogleAntigravityQuotaForAccount(account, now);
@@ -451,7 +449,7 @@ const GOOGLE_ACCOUNT_QUOTA_WINDOWS = [
451
449
  { label: "G3 Pro", quotaModelKey: "gemini-3.1-pro-low" },
452
450
  ];
453
451
  async function queryGoogleAntigravityAccountUsage(now) {
454
- const accounts = readAllAntigravityQuotaAccounts();
452
+ const accounts = await readAllAntigravityQuotaAccounts();
455
453
  const results = await Promise.all(accounts.map(async (account) => {
456
454
  const accountLabel = account.email ?? maskCredential(account.refreshToken);
457
455
  try {
@@ -474,12 +472,8 @@ async function queryGoogleAntigravityAccountUsage(now) {
474
472
  }));
475
473
  return results;
476
474
  }
477
- function readActiveAntigravityQuotaAccount(accounts = readAllAntigravityQuotaAccounts()) {
478
- const credential = readPiAuthSync().antigravity;
479
- return accounts[clampAccountIndex(credential?.activeIndex, accounts.length)];
480
- }
481
- function readAllAntigravityQuotaAccounts() {
482
- const credential = readPiAuthSync().antigravity;
475
+ async function readAllAntigravityQuotaAccounts() {
476
+ const credential = (await readPiAuth()).antigravity;
483
477
  if (!credential)
484
478
  return [];
485
479
  const credentialClient = getGoogleOAuthClientCredentials(credential);
@@ -504,14 +498,6 @@ function readAllAntigravityQuotaAccounts() {
504
498
  }) : undefined;
505
499
  return account ? [account] : [];
506
500
  }
507
- function readPiAuthSync() {
508
- try {
509
- return JSON.parse(readFileSync(getPiAuthPath(), "utf8"));
510
- }
511
- catch {
512
- return {};
513
- }
514
- }
515
501
  function getAccountRefreshToken(account) {
516
502
  if (account.refreshToken)
517
503
  return account.refreshToken;
@@ -844,6 +830,7 @@ function modelUsageWindow(window, now) {
844
830
  remainingPercent: clampPercent(Math.round(100 - window.used_percent)),
845
831
  resetAt: now + Math.max(0, Math.round(window.reset_after_seconds)) * 1000,
846
832
  windowSeconds: Math.max(0, Math.round(window.limit_window_seconds)),
833
+ hasKnownWindowDuration: true,
847
834
  };
848
835
  }
849
836
  function accountWindowFromRateLimit(window, now) {
@@ -930,5 +917,26 @@ function maskCredential(value) {
930
917
  return `${visible.slice(0, 4)}****${visible.slice(-4)}`;
931
918
  }
932
919
  function formatUsageWindow(_prefix, window, now) {
933
- return `${window.remainingPercent}% ${formatCompactProgressBar(window.remainingPercent)} ${formatDurationShort(window.resetAt, now)}`;
920
+ const warning = modelUsageWindowWillExhaustBeforeReset(window, now) ? ` ${APP_ICONS.alert}` : "";
921
+ return `${window.remainingPercent}% ${formatCompactProgressBar(window.remainingPercent)}${warning} ${formatDurationShort(window.resetAt, now)}`;
922
+ }
923
+ function modelUsageWindowWillExhaustBeforeReset(window, now) {
924
+ if (!window.hasKnownWindowDuration)
925
+ return false;
926
+ if (window.windowSeconds <= DAY_SECONDS)
927
+ return false;
928
+ if (window.remainingPercent <= 0)
929
+ return false;
930
+ const timeUntilResetSeconds = Math.max(0, (window.resetAt - now) / 1000);
931
+ const elapsedSeconds = Math.max(0, window.windowSeconds - timeUntilResetSeconds);
932
+ if (elapsedSeconds <= 0)
933
+ return false;
934
+ const total = 100;
935
+ const used = total - window.remainingPercent;
936
+ if (used <= 0)
937
+ return false;
938
+ const remaining = total - used;
939
+ const averageRate = used / elapsedSeconds;
940
+ const projectedSecondsUntilExhaustion = remaining / averageRate;
941
+ return projectedSecondsUntilExhaustion < timeUntilResetSeconds;
934
942
  }
@@ -71,6 +71,7 @@ export declare class StatusLineRenderer {
71
71
  private pushVoiceWidgetSegment;
72
72
  private pushWorkspaceSegments;
73
73
  private pushModelUsageSegments;
74
+ private modelUsageHasWarning;
74
75
  private modelUsageResetLength;
75
76
  private pushSegment;
76
77
  private pushContextBarSegments;
@@ -491,11 +491,25 @@ export class StatusLineRenderer {
491
491
  fill: color,
492
492
  track: this.host.theme.colors.statusDotBase,
493
493
  }, MODEL_USAGE_PROGRESS_BAR_WIDTH));
494
- const resetStart = barStart + MODEL_USAGE_PROGRESS_BAR_WIDTH + 1;
494
+ const defaultResetStart = barStart + MODEL_USAGE_PROGRESS_BAR_WIDTH + 1;
495
+ const warningStart = this.modelUsageHasWarning(modelUsageLabel, defaultResetStart - labelStart)
496
+ ? defaultResetStart
497
+ : undefined;
498
+ const resetStart = warningStart === undefined
499
+ ? defaultResetStart
500
+ : warningStart + APP_ICONS.alert.length + 1;
501
+ if (warningStart !== undefined) {
502
+ this.pushSegment(segments, warningStart, APP_ICONS.alert.length, this.host.theme.colors.warning);
503
+ }
495
504
  const resetLength = this.modelUsageResetLength(modelUsageLabel, resetStart - labelStart);
496
505
  this.pushSegment(segments, resetStart, resetLength, this.host.theme.colors.muted);
497
506
  }
498
507
  }
508
+ modelUsageHasWarning(modelUsageLabel, localStart) {
509
+ if (localStart < 0 || localStart >= modelUsageLabel.length)
510
+ return false;
511
+ return modelUsageLabel.startsWith(APP_ICONS.alert, localStart);
512
+ }
499
513
  modelUsageResetLength(modelUsageLabel, localStart) {
500
514
  if (localStart < 0 || localStart >= modelUsageLabel.length)
501
515
  return 0;
@@ -195,7 +195,7 @@ export class AppSessionEventController {
195
195
  this.host.updateQueuedMessageStatus();
196
196
  break;
197
197
  case "message_update":
198
- this.handleMessageUpdate(event.assistantMessageEvent);
198
+ this.handleMessageUpdate(event);
199
199
  break;
200
200
  case "tool_execution_start":
201
201
  this.finishCurrentThinkingEntry();
@@ -506,7 +506,8 @@ export class AppSessionEventController {
506
506
  return;
507
507
  this.host.recordWorkspaceMutationForUserEntry(prepared.userEntryId, mutation);
508
508
  }
509
- handleMessageUpdate(assistantEvent) {
509
+ handleMessageUpdate(event) {
510
+ const assistantEvent = event.assistantMessageEvent;
510
511
  if (this.assistantMessageClosed && assistantEvent.type !== "done")
511
512
  return;
512
513
  this.assistantMessageClosed = false;
@@ -522,7 +523,13 @@ export class AppSessionEventController {
522
523
  case "text_delta":
523
524
  this.finishCurrentThinkingEntry();
524
525
  this.host.setSessionActivity("running");
525
- this.appendAssistantText(assistantEvent.delta, assistantEvent.contentIndex);
526
+ {
527
+ const snapshotText = assistantTextSnapshotForContentIndex(event.message, assistantEvent.partial, assistantEvent.contentIndex);
528
+ if (snapshotText === undefined)
529
+ this.appendAssistantText(assistantEvent.delta, assistantEvent.contentIndex);
530
+ else
531
+ this.reconcileAssistantTextBlock(snapshotText, assistantEvent.contentIndex, { keepOpen: true });
532
+ }
526
533
  break;
527
534
  case "text_end":
528
535
  this.finishCurrentThinkingEntry();
@@ -616,7 +623,7 @@ export class AppSessionEventController {
616
623
  entry.text += visibleText;
617
624
  this.touchEntry(entry);
618
625
  }
619
- reconcileAssistantTextBlock(content, contentIndex) {
626
+ reconcileAssistantTextBlock(content, contentIndex, options = {}) {
620
627
  this.flushAssistantTextBuffer(true);
621
628
  const hasVisibleTextBeforeBlock = this.hasVisibleTextBeforeCurrentAssistantBlock();
622
629
  // C.11: normalise CRLF in the final block content (see appendAssistantText).
@@ -667,9 +674,16 @@ export class AppSessionEventController {
667
674
  this.currentAssistantEntryId = entry.id;
668
675
  if (contentIndex !== undefined)
669
676
  this.assistantTextBlocksByContentIndex.set(contentIndex, visibleText);
670
- this.currentAssistantTextBlockEntryId = undefined;
671
- this.currentAssistantTextBlockStartLength = undefined;
672
- this.currentAssistantTextBlockContentIndex = undefined;
677
+ if (options.keepOpen) {
678
+ this.currentAssistantTextBlockEntryId = entry.id;
679
+ this.currentAssistantTextBlockStartLength = startLength;
680
+ this.currentAssistantTextBlockContentIndex = contentIndex;
681
+ }
682
+ else {
683
+ this.currentAssistantTextBlockEntryId = undefined;
684
+ this.currentAssistantTextBlockStartLength = undefined;
685
+ this.currentAssistantTextBlockContentIndex = undefined;
686
+ }
673
687
  this.assistantTextBuffer = "";
674
688
  }
675
689
  ensureAssistantTextBlockStarted(entry) {
@@ -891,6 +905,17 @@ function partialToolCallAt(partial, contentIndex) {
891
905
  const block = partial.content[contentIndex];
892
906
  return isRecord(block) && block.type === "toolCall" ? block : undefined;
893
907
  }
908
+ function assistantTextSnapshotForContentIndex(message, partial, contentIndex) {
909
+ if (contentIndex === undefined)
910
+ return undefined;
911
+ return assistantTextContentAt(message, contentIndex) ?? assistantTextContentAt(partial, contentIndex);
912
+ }
913
+ function assistantTextContentAt(value, contentIndex) {
914
+ if (!isRecord(value) || !Array.isArray(value.content))
915
+ return undefined;
916
+ const block = value.content[contentIndex];
917
+ return isRecord(block) && block.type === "text" && typeof block.text === "string" ? block.text : undefined;
918
+ }
894
919
  function assistantStreamVisibleTextForCompleteBlock(text, hasVisibleTextBeforeBlock) {
895
920
  let buffer = text;
896
921
  let visibleText = "";
@@ -72,52 +72,38 @@ export function asyncSubagentToolDescriptions(options: ToolDescriptionSetOptions
72
72
  label: "Subagents",
73
73
  description: [
74
74
  "Manage isolated async sub-agents for large, parallel, context-heavy work.",
75
- "Sub-agent presets defined in async-subagents config and selected with /subagent-preset can choose per-role model/thinking/extra-arg configurations for future spawns across sessions until changed; AGENTS_PRESET or /subagent-preset session <name> overrides the saved preset for the current process/session; /subagent-preset init copies the bundled sample when no config exists.",
76
- "When subagentType is omitted, async-subagents asks a lightweight router model to choose the configured role from the current config; prefer omitting it unless the user or task constraints require an explicit role.",
75
+ "Presets from async-subagents config and /subagent-preset choose role model/thinking/args; AGENTS_PRESET or /subagent-preset session <name> overrides the current session; /subagent-preset init creates a sample config.",
76
+ "Omit subagentType so the router chooses a configured role unless the user or task requires a role, vision, or deterministic override.",
77
77
  repoDiscovery
78
78
  ? "Use for broad independent tracks, review axes, or hypotheses even though repo_* tools are available."
79
79
  : "Use first for broad codebase discovery split into tracks, review axes, or incident-triage hypotheses when repo_* tools are unavailable.",
80
- "Use action=spawn/status/wait/result/stop/cleanup with the matching options; spawned runs are registered under project .pi/subagents while the main session is alive so status/wait/stop can omit runDir for the latest run and result can resolve runDir by agentId. The parent session receives a follow-up system/custom message for each background agent when it finishes or fails, so the parent can stop after spawning instead of polling for completion.",
81
- "Collect results only when the parent task needs them; result reads are always compact and link to artifacts instead of inlining raw logs.",
82
- "Spawned agents run in isolated background pi processes with extensions disabled to prevent recursive sub-agent spawning.",
83
- "Each agent has a wall-clock watchdog timeout (default 30 minutes); spawn timeoutSeconds or task timeoutSeconds can shorten it for tests or bounded probes.",
84
- "Concurrency is limited project-wide by maxConcurrent (default 5); excess agents queue automatically.",
85
- "Failed agents can be auto-retried with exponential backoff when retry is configured per type or globally.",
86
- "Preset fallbackModels can switch future sub-agent spawns in the current process/session to a fallback provider/model after quota/rate-limit failures; Antigravity account rotation is allowed to exhaust all accounts for the model first.",
80
+ "Use action=spawn/status/wait/result/stop/cleanup; .pi/subagents tracks runs so status/wait/stop can omit the latest runDir and result can resolve by agentId. Parent sessions receive completion/failure follow-ups, so spawn can return without polling.",
81
+ "Results are compact with artifact links. Agents run isolated pi processes with extensions disabled to prevent recursive spawning; spawn/task timeoutSeconds can shorten the default 30m watchdog, project concurrency queues excess agents, and retry backoff/fallback models/Antigravity account rotation are config-driven.",
87
82
  ].join(" "),
88
83
  promptSnippet:
89
- "Use subagents action='spawn' when multiple independent agents are useful, the user asks to delegate/parallelize/split work, or one large review/deep investigation should stay out of the main context. " +
90
- "Default to omitting subagentType so the configured router chooses from the live role config; set it only when the user explicitly named a role, vision/image handling is required, or a deterministic technical override is needed. Avoid trivial reads/edits, and do not call status/wait immediately after spawn just for progress. " +
84
+ "Use subagents action='spawn' for multiple independent agents, explicit delegate/parallelize/split work requests, or one large review/debug track that should stay out of the parent context. " +
85
+ "Usually omit subagentType so the router chooses; set it only for user-named roles, vision/image handling, deterministic tests, or another concrete override. Avoid trivial reads/edits and do not call status/wait immediately after spawn just for progress. " +
91
86
  (repoDiscovery
92
- ? "For one semantic code-discovery question, use repo_search directly instead; for independent tracks/hypotheses/review axes, delegate even when repo_* tools are available. Use result only after completion when findings are needed in the parent context; it returns compact output with artifact links."
93
- : "For one focused code-discovery question, use direct read/grep tools instead. When the user asks for broad discovery split into tracks, hypotheses, incident triage, release readiness, risk strategy, or parallel reviews and indexed discovery is unavailable, spawn several focused scan/quick agents first before parent-context file search. Use result only after completion when findings are needed in the parent context; it returns compact output with artifact links."),
87
+ ? "For one semantic code-discovery question, use repo_search; for independent tracks/hypotheses/review axes, delegate even when repo_* tools exist. Read result only after completion when findings are needed."
88
+ : "For one focused code-discovery question, use direct read/grep. Without repo_* tools, spawn several focused scan/quick agents first for broad multi-track discovery, incident triage, release readiness, risk strategy, or parallel reviews. Read result only after completion when findings are needed."),
94
89
  promptGuidelines: [
95
90
  "Use action='spawn' only for LARGE or PARALLEL tasks: independent investigations, repo-wide sweeps, deep debugging, or code review/audit that would bloat the parent context.",
96
91
  "Treat explicit requests to delegate, use sub-agents, run parallel agents, split into independent tracks, investigate hypotheses, or run separate review axes as spawn triggers; spawn first unless the request is trivial or clearly single-file.",
97
92
  repoDiscovery
98
- ? "Do not spawn merely because code is unfamiliar; make one direct repo_search call for a single discovery question, and spawn only when several separate questions or review axes should run independently."
99
- : "Do not spawn merely because code is unfamiliar; use direct read/grep tools for a single discovery question, and spawn only when several separate questions or review axes should run independently.",
93
+ ? "For one discovery question, use repo_search; spawn only for several independent tracks/hypotheses/review axes, and do not let repo_* availability suppress delegation."
94
+ : "For one discovery question, use direct read/grep; without repo_* tools, spawn a small focused swarm before parent-context search when the task spans several files/modules/hypotheses/tracks.",
100
95
  repoDiscovery
101
- ? "When repo_search answers one discovery question, prefer it over a swarm; do not let repo_* availability suppress delegation for multi-track reviews, independent hypotheses, explicit parallelism, or deep isolated review."
102
- : "When indexed discovery is unavailable and the task spans multiple files, modules, hypotheses, or explicitly separate tracks, spawn a small swarm of focused scan/quick agents before serial grep/read in the parent context, even for a small project.",
103
- repoDiscovery
104
- ? "For incident triage, release-readiness, or risk/test-strategy prompts with separate hypotheses or review tracks, prefer spawning focused review agents over doing every track serially in the parent context."
105
- : "For incident triage, release-readiness, or risk/test-strategy prompts with separate hypotheses or review tracks and no repo_* tools, call action='spawn' as the first discovery step; direct read/grep can follow after delegation if needed.",
106
- repoDiscovery
107
- ? "Do not use subagents for exact-string lookups, known-file edits, typo/text replacements, or obvious one-file changes; use the cheapest direct path instead."
108
- : "Do not use subagents for exact-string lookups, known-file edits, typo/text replacements, or obvious one-file changes; use the cheapest direct path instead.",
109
- "Spawn multiple focused agents in one action='spawn' call when they investigate independent questions.",
110
- "For synthetic tests or intentionally bounded probes, pass timeoutSeconds slightly above the expected runtime so hung sub-agents are stopped automatically.",
111
- "For subagents action='spawn', default to leaving subagentType unset and let the lightweight router choose from configured role descriptions. Do not choose a role just because a built-in example seems to fit; the router has the current user config and presets. Set subagentType explicitly only when the user named the role, image inspection requires vision, tests need deterministic routing, or there is another concrete technical reason to bypass the router.",
112
- "Use subagentType='vision' with imagePaths and optional focus when a text-only/blind parent model needs a visual description of screenshots, UI state, diagrams, or other images.",
96
+ ? "For incident triage, release readiness, or risk/test strategy with separate hypotheses or review tracks, prefer focused review agents over serial parent-context work."
97
+ : "For incident triage, release readiness, or risk/test strategy with separate hypotheses or review tracks and no repo_* tools, call action='spawn' as the first discovery step; direct read/grep can follow.",
98
+ "Do not use subagents for exact-string lookups, known-file edits, typo/text replacements, obvious one-file changes, or interactive user input; use the cheapest direct path.",
99
+ "Spawn multiple focused agents in one action='spawn' call for independent questions; for synthetic tests or bounded probes, pass timeoutSeconds slightly above expected runtime.",
100
+ "For spawn, leave subagentType unset so the router chooses from configured roles. Set it only for user-named roles, vision/image handling, deterministic tests, or another concrete override.",
101
+ "Use subagentType='vision' with imagePaths and optional focus when images/screenshots/diagrams need visual inspection.",
113
102
  "If the user asks to start, run, launch, or test parallel sub-agents, call action='spawn' and then stop; completion/failure notifications will wake the parent so do not immediately call action='status' or action='wait' just to see whether agents finished.",
114
- "Use action='status' for a non-blocking progress check or to recover after reload/crash.",
115
- "After spawn, project-local .pi/subagents/registry.json records latest runDir and agentId mappings until normal main-session shutdown; if runDir is missing after compaction/reload, call status without runDir or result with agentId instead of failing solely because runDir was lost.",
116
- "Use action='wait' only when the user asks to wait/collect now, or your next parent step depends on completion.",
117
- "Use action='result' only after status/wait confirms completion; raw output is not inlined, so inspect linked artifacts only when full details are truly necessary.",
103
+ "Use status for non-blocking progress/recovery, wait only when requested or needed, and result only after completion; registry mappings can recover latest runDir/agentId after reload.",
104
+ "Result output is compact with artifact links; inspect raw artifacts only when full details are necessary.",
118
105
  "Use action='stop' when the user asks to stop, cancel, or kill running sub-agents.",
119
106
  "Use action='cleanup' with delete=true after collecting all results to free disk space.",
120
- "Do NOT use subagents for trivial tasks, single file reads, simple edits, or interactive user input.",
121
107
  ],
122
108
  },
123
109
  spawnAction: {
@@ -143,9 +129,8 @@ export function asyncSubagentToolDescriptions(options: ToolDescriptionSetOptions
143
129
  label: "Subagent Result Action",
144
130
  description: [
145
131
  "Read output from one async sub-agent after it completes.",
146
- "Always returns a compact structured summary/findings/files/risks/next actions plus artifact paths, not the full raw result text or stderr.",
147
- "A result.json with machine-readable structured output is written alongside the raw result.md on completion.",
148
- "Compact output avoids polluting the parent context; full result.md and stderr.log paths are included for manual inspection.",
132
+ "Returns compact structured summary/findings/files/risks/next actions plus artifact paths, not raw result text or stderr.",
133
+ "Writes structured result.json alongside raw result.md; full result.md and stderr.log paths are included for manual inspection.",
149
134
  ].join(" "),
150
135
  },
151
136
  stopAction: {
@@ -209,11 +194,9 @@ export const REPO_DISCOVERY_TOOLS: RepoDiscoveryToolDescription[] = [
209
194
  promptGuidelines: [
210
195
  "Phrase target as the behavior to locate, not a whitespace-separated pile of near-synonyms; keep exact identifiers only as useful anchors.",
211
196
  "For initial unknown-file behavior searches, prefer the default hybrid ranking; pass --mode semantic only when lexical/symbol terms are misleading or the query is purely conceptual.",
212
- "Do not launch several broad repo_search calls for the same question before reading results. Make one targeted search, read the best returned ranges, then refine only if evidence is missing.",
213
- "Write a specific conceptual target, not a generic word. Add --path-prefix, --max-files, --dedupe-file, or --exclude-tests in args to reduce noise.",
214
- "After repo_search, read the returned ranges instead of launching another broad search unless a specific gap remains; prefer read over --include-content for full evidence unless you only need a compact preview.",
215
- "For mutation-site, bug-location, or behavior-cause questions, the assignment/write/branch/call that directly causes the behavior is the answer. If a read range contains that exact evidence, stop searching and answer from it.",
216
- "Avoid repeated near-duplicate searches after finding direct source evidence. Continue only for a named gap such as callers, persistence path, tests, or user-requested impact analysis, and make the follow-up query narrower.",
197
+ "Make one targeted search, read the best returned ranges (prefer read over --include-content for full evidence), then refine only for a named gap; do not launch duplicate broad searches.",
198
+ "Use a specific conceptual target and narrow with --path-prefix, --max-files, --dedupe-file, or --exclude-tests when useful.",
199
+ "For bug/cause questions, stop when a read range shows the causal assignment/write/branch/call; continue only for a named gap such as callers, persistence, tests, or requested impact, and narrow follow-ups.",
217
200
  ],
218
201
  targetDescription: "Natural-language behavior query, e.g. auth session token validation.",
219
202
  },
@@ -248,25 +231,23 @@ export const REPO_DISCOVERY_TOOL_NAMES = REPO_DISCOVERY_TOOLS.map((tool) => tool
248
231
  export const TODO_TOOL_DESCRIPTION: ToolDescription = {
249
232
  name: "todo",
250
233
  label: "Todo",
251
- description: "Track and keep in sync non-trivial multi-step work as todos. Actions: create, update, batch_create, batch_update, list, get, delete, clear, export, import. Supports parent/subtask hierarchy, blockers, deferred out-of-scope items, dependencies, and replace:true on create/batch_create/import for intentionally replacing an obsolete plan; skip trivial or chat-only requests. Resynchronize the plan when requirements are added, canceled, or become obsolete, whether from user input or discovered facts. For multi-step plans, include a final user-facing report todo in the initial create/batch_create plan when possible. Keep exactly one task in_progress and complete it only after verification.",
234
+ description: "Track and synchronize non-trivial multi-step work. Actions: create, update, batch_create, batch_update, list, get, delete, clear, export, import. Supports hierarchy, blockers, deferred/out-of-scope items, dependencies, and replace:true for replacing obsolete plans. Skip trivial or chat-only requests; resync when requirements or discovered facts change. For multi-step plans, include a final user-facing report todo and keep exactly one task in_progress until verified.",
252
235
  promptSnippet: "Track/sync non-trivial multi-step work; include final report item and close it before sending the report; resync when requirements change; keep one task in_progress",
253
236
  promptGuidelines: [
254
237
  "Use `todo` for complex work with 3+ steps, explicit user task lists, or new non-trivial requirements. Skip single trivial tasks and purely conversational requests.",
255
- "For any multi-step implementation/debugging plan, include a final todo item in the initial create/batch_create plan for the user-facing final report. Give it explicit description/acceptance criteria covering changed files/behavior, verification commands/results, remaining manual actions, and never substitute a compression/housekeeping note for the final report. Close that report todo immediately before sending the final report to the user.",
256
- "When the user adds, removes, cancels, reprioritizes, or changes the goal, scope, requirements, constraints, or chosen approach, use `todo` before continuing to synchronize the plan: update still-relevant tasks, defer or delete obsolete tasks, add new required tasks, and adjust dependencies/order.",
257
- "When your own investigation or verification discovers new facts that make the current todo plan stale, incomplete, impossible, unsafe, or no longer the best approach, use `todo` to revise the plan immediately instead of following outdated tasks.",
258
- "Update todos as part of the workflow, not as end-of-task cleanup: whenever you start, finish, block, split, abandon, or materially change a step, call `todo` immediately before continuing.",
259
- "Before any non-trivial read/edit/test/tool work on a planned task, mark exactly one task in_progress with activeForm (present-continuous label); do this immediately after creating a plan if no task is active. Mark it completed immediately after the required verification, never in batches.",
238
+ "For multi-step implementation/debugging plans, include a final user-facing report todo in the initial plan with acceptance criteria for changed files/behavior, verification results, and remaining manual actions; close it immediately before the final response, never via compression.",
239
+ "Resync before continuing when the user or new findings change scope, requirements, safety, feasibility, approach, dependencies, or order; update relevant tasks, add required tasks/blockers, and defer obsolete work.",
240
+ "Update todos when you start, finish, block, split, abandon, or materially change a step; do not leave todo maintenance for end-of-task cleanup.",
241
+ "Before non-trivial planned work, mark exactly one task in_progress with activeForm. Complete it only after required verification, never in batches.",
260
242
  "If implementation is partial, tests fail, or a blocker remains, keep the task in_progress and add/update a blocker task instead of completing it.",
261
243
  "Never use `clear`, `delete`, or batch deletion to hide unfinished, stale, or forgotten todos. Defer obsolete items or update them with the reason; only delete when the user explicitly asks or the item was created by mistake.",
262
244
  "Before giving a final response for work that used todos, ensure every visible todo is completed, deferred, or intentionally still in_progress with a blocker/explanation. Do not leave any just-finished todo item in_progress after you stop.",
263
245
  "Keep subjects short and imperative; put details in description only when needed. Use parentId for large plans; use blockedBy on create and addBlockedBy/removeBlockedBy on update for dependencies.",
264
246
  "Use batch_create/batch_update for large explicit plans, but still keep exactly one visible task in_progress unless the user asks otherwise.",
265
247
  "When starting a new plan that supersedes existing unfinished todos, use batch_create with replace:true instead of appending; only omit replace when intentionally extending the current plan.",
266
- "list hides deleted tombstones unless includeDeleted:true; pass status or blockedOnly only when you need a filtered list.",
267
- "Use export/import for handoff or plan migration; import with replace:true only when the user explicitly wants to overwrite the current todo state.",
248
+ "list hides deleted tombstones unless includeDeleted:true; use status/blockedOnly filters only when needed. Use export/import for handoff or migration; import with replace:true only when explicitly overwriting todo state.",
268
249
  "When every visible todo is completed, todo state clears automatically; do not call clear afterward just to remove completed tasks.",
269
- "Optional project persistence is controlled by `/todos persist on|off|status` or the discoverable `/todos-persist on|off|status` alias; when resuming a persisted plan, ask the user which items are in scope and run `/todos scope <id...>` or `/todos-scope <id...>` so out-of-scope active tasks become deferred.",
250
+ "Persistence uses `/todos persist on|off|status` or `/todos-persist on|off|status`; when resuming, ask which ids are in scope and run `/todos scope <id...>` or `/todos-scope <id...>` so out-of-scope active tasks are deferred.",
270
251
  ],
271
252
  };
272
253
 
@@ -348,12 +329,12 @@ export const CODEX_ALIAS_TOOL_DESCRIPTIONS = {
348
329
  shellCommand: {
349
330
  name: "shell",
350
331
  label: "shell",
351
- description: "Run shell commands for builds, tests, package managers, git, and project CLIs. Set workdir/cwd instead of cd; prefer read for simple file reads.",
332
+ description: "Run shell commands for builds, tests, package managers, git, and project CLIs. Prefer tail for long test output. Set workdir/cwd instead of cd; prefer read for simple file reads.",
352
333
  },
353
334
  applyPatch: {
354
335
  name: "apply_patch",
355
336
  label: "apply_patch",
356
- description: `Apply file edits with a relative-path patch or a standard unified diff. Use for creating, updating, moving, or deleting files; keep each patch focused.
337
+ description: `Apply file edits with a relative-path patch or standard unified diff. Use for creating, updating, moving, or deleting files; keep each patch focused.
357
338
 
358
339
  Begin-patch format:
359
340
  *** Begin Patch
@@ -363,8 +344,8 @@ Begin-patch format:
363
344
  +new text
364
345
  *** End Patch
365
346
 
366
- Begin-patch sections: *** Add File (new lines start with +), *** Update File (may include *** Move to: new/path), and *** Delete File. A single Begin Patch block may contain multiple file sections and modify multiple files; use one multi-file patch for tightly related edits, while keeping unrelated changes separate. Update hunks may use @@ optional context, omit the first @@, and use *** End of File. The whole begin patch may be wrapped in <<EOF ... EOF. Matching tolerates trailing-space, trim, and common Unicode punctuation differences.
347
+ Sections: *** Add File (new lines start with +), *** Update File (optionally *** Move to: new/path), and *** Delete File. One Begin Patch may edit multiple tightly related files; keep unrelated changes separate. Update hunks may use optional @@ context, omit line numbers/the first @@, use *** End of File, and be wrapped in <<EOF ... EOF. Matching tolerates trailing-space, trim, and common Unicode punctuation differences.
367
348
 
368
- Unified diff is also supported (for example, git diff output with ---/+++ headers). Paths must be workspace-relative, never absolute. Provide the complete patch in input.`,
349
+ Unified diff with ---/+++ headers is also supported. Paths must be workspace-relative, never absolute. Provide the complete patch in input.`,
369
350
  },
370
351
  } satisfies Record<string, ToolDescription>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-ui-extend",
3
- "version": "0.1.33",
3
+ "version": "0.1.34",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "bin": {