pi-bar 0.3.26 → 0.3.28
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/extensions/status-footer.ts +199 -11
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -9,9 +9,9 @@
|
|
|
9
9
|
claude-opus-4.7 ❯ think:med ❯ 2.6% / 1.0M ❯ Inspecting package structure ❯ plan:active
|
|
10
10
|
```
|
|
11
11
|
|
|
12
|
-

|
|
13
|
+

|
|
14
|
+

|
|
15
15
|
|
|
16
16
|
## Why use it?
|
|
17
17
|
|
|
@@ -91,7 +91,9 @@ const TLDR_TARGET_SUMMARY_CHARS = 60;
|
|
|
91
91
|
// Burst debouncing on the generation side: coalesce rapid-fire activities into
|
|
92
92
|
// one model call. Quiet window catches short bursts; max wait surfaces progress
|
|
93
93
|
// during continuous activity.
|
|
94
|
-
|
|
94
|
+
// Quiet window long enough to let tool_result land before flushing, so the
|
|
95
|
+
// model summarizes the completed outcome instead of guessing from tool_call.
|
|
96
|
+
const NORMAL_CHECKPOINT_QUIET_MS = 1_500;
|
|
95
97
|
const NORMAL_CHECKPOINT_MAX_WAIT_MS = 2_500;
|
|
96
98
|
// Defensive ceiling: even if the model ignores the length instruction, never
|
|
97
99
|
// blast a large payload into the footer.
|
|
@@ -650,7 +652,14 @@ class FooterTldrEngine {
|
|
|
650
652
|
}
|
|
651
653
|
|
|
652
654
|
recordUserMessage(ctx: ExtensionContext, prompt: string): void {
|
|
655
|
+
// A new user turn replaces the entire visible TLDR context, so wipe both
|
|
656
|
+
// the rendered text and the prior-turn checkpoint context that feeds the
|
|
657
|
+
// model. Carrying old checkpoints biased the next turn's phrasing toward
|
|
658
|
+
// the previous task.
|
|
653
659
|
this.currentText = null;
|
|
660
|
+
this.lastRenderedText = "";
|
|
661
|
+
this.acceptedCheckpoints.splice(0);
|
|
662
|
+
this.latestAcceptedActivityIndex = 0;
|
|
654
663
|
this.requestRender();
|
|
655
664
|
this.enqueue(ctx, this.facts.recordUserMessage(prompt));
|
|
656
665
|
}
|
|
@@ -661,10 +670,14 @@ class FooterTldrEngine {
|
|
|
661
670
|
}
|
|
662
671
|
|
|
663
672
|
recordToolCall(
|
|
664
|
-
|
|
673
|
+
_ctx: ExtensionContext,
|
|
665
674
|
event: { toolName: string; input?: Record<string, unknown>; toolCallId?: string },
|
|
666
675
|
): void {
|
|
667
|
-
|
|
676
|
+
// Record the fact so a subsequent checkpoint sees the tool was started,
|
|
677
|
+
// but DO NOT enqueue. tool_call alone has no outcome; flushing here
|
|
678
|
+
// before tool_result arrives makes the model hallucinate "completed
|
|
679
|
+
// successfully". The tool_result event triggers the actual checkpoint.
|
|
680
|
+
this.facts.recordToolCall(event);
|
|
668
681
|
}
|
|
669
682
|
|
|
670
683
|
recordToolResult(
|
|
@@ -689,9 +702,33 @@ class FooterTldrEngine {
|
|
|
689
702
|
this.startFreshRun();
|
|
690
703
|
return;
|
|
691
704
|
}
|
|
705
|
+
|
|
706
|
+
// Failure/aborted finals have no narratable content. A model call here
|
|
707
|
+
// fabricates a reason; render a deterministic literal instead.
|
|
708
|
+
if (result.activityType === "assistant_failure") {
|
|
709
|
+
const stopReason = extractStopReason(message);
|
|
710
|
+
this.renderLiteralFinal(stopReason);
|
|
711
|
+
this.facts.resetConversation();
|
|
712
|
+
this.startFreshRun();
|
|
713
|
+
return;
|
|
714
|
+
}
|
|
715
|
+
|
|
692
716
|
this.enqueue(ctx, result);
|
|
693
717
|
}
|
|
694
718
|
|
|
719
|
+
private renderLiteralFinal(stopReason: string | undefined): void {
|
|
720
|
+
this.clearPendingDisplay();
|
|
721
|
+
this.clearPendingNormalCheckpoint();
|
|
722
|
+
this.checkpointQueue.splice(0);
|
|
723
|
+
this.abortInFlightCheckpoint();
|
|
724
|
+
const literal = stopReason === "aborted" ? "Aborted." : `Stopped: ${stopReason ?? "error"}.`;
|
|
725
|
+
this.currentText = literal;
|
|
726
|
+
this.lastRenderedText = literal;
|
|
727
|
+
this.lastRenderedActivityIndex = this.facts.latestActivityIndex();
|
|
728
|
+
this.lastDisplayAt = performance.now();
|
|
729
|
+
this.requestRender();
|
|
730
|
+
}
|
|
731
|
+
|
|
695
732
|
private startFreshRun(): void {
|
|
696
733
|
this.runId++;
|
|
697
734
|
this.cancelWork();
|
|
@@ -930,6 +967,13 @@ class FooterTldrEngine {
|
|
|
930
967
|
private renderCheckpoint(checkpoint: TldrCheckpoint): void {
|
|
931
968
|
if (checkpoint.activityIndex <= this.lastRenderedActivityIndex) return;
|
|
932
969
|
if (checkpoint.text === this.lastRenderedText) return;
|
|
970
|
+
// Skip near-duplicates: a burst of consecutive checkpoints often produces
|
|
971
|
+
// the same action fragment with only punctuation/casing differences. They
|
|
972
|
+
// flash visibly in the footer without adding information.
|
|
973
|
+
if (isNearDuplicateTldr(checkpoint.text, this.lastRenderedText)) {
|
|
974
|
+
this.lastRenderedActivityIndex = checkpoint.activityIndex;
|
|
975
|
+
return;
|
|
976
|
+
}
|
|
933
977
|
this.lastRenderedActivityIndex = checkpoint.activityIndex;
|
|
934
978
|
this.lastRenderedText = checkpoint.text;
|
|
935
979
|
this.lastDisplayAt = performance.now();
|
|
@@ -965,17 +1009,54 @@ class FooterTldrEngine {
|
|
|
965
1009
|
}
|
|
966
1010
|
|
|
967
1011
|
function checkpointSystemPrompt(job: TldrCheckpointJob): string {
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
1012
|
+
// Tense + exemplars vary by priority. Final TLDRs describe completed turns;
|
|
1013
|
+
// using -ing examples for final caused all final TLDRs to slip back into
|
|
1014
|
+
// present-progressive even though the literal instruction said past tense.
|
|
1015
|
+
// Immediate TLDRs cover a brand-new user request; they need a rephrase
|
|
1016
|
+
// directive instead of generic filler like "Continuing with task".
|
|
1017
|
+
let tenseInstruction: string;
|
|
1018
|
+
let goodExamples: string;
|
|
1019
|
+
if (job.displayPriority === "final") {
|
|
1020
|
+
tenseInstruction = "Start with a past-tense verb describing what was completed.";
|
|
1021
|
+
goodExamples = [
|
|
1022
|
+
"- Updated footer summary behavior",
|
|
1023
|
+
"- Investigated live TLDR regressions",
|
|
1024
|
+
"- Refined sanitizer for stray prefixes",
|
|
1025
|
+
"- Wrapped up extension release",
|
|
1026
|
+
].join("\n");
|
|
1027
|
+
} else if (job.displayPriority === "immediate") {
|
|
1028
|
+
tenseInstruction =
|
|
1029
|
+
"Rephrase the user's new request as a concise present-progressive task clause. Do not write \"Continuing\", \"Investigating user input\", or any generic filler.";
|
|
1030
|
+
goodExamples = [
|
|
1031
|
+
"- Reviewing footer summary behavior",
|
|
1032
|
+
"- Investigating live TLDR regressions",
|
|
1033
|
+
"- Refining sanitizer for stray prefixes",
|
|
1034
|
+
"- Preparing extension release",
|
|
1035
|
+
].join("\n");
|
|
1036
|
+
} else {
|
|
1037
|
+
tenseInstruction = "Start with a present-tense -ing verb describing current work.";
|
|
1038
|
+
goodExamples = [
|
|
1039
|
+
"- Reviewing footer summary behavior",
|
|
1040
|
+
"- Investigating live TLDR regressions",
|
|
1041
|
+
"- Refining sanitizer for stray prefixes",
|
|
1042
|
+
"- Wrapping up extension release",
|
|
1043
|
+
].join("\n");
|
|
1044
|
+
}
|
|
1045
|
+
|
|
972
1046
|
// Frame the model as a human developer describing visible work, not an agent
|
|
973
|
-
// summarizing its own mechanics.
|
|
974
|
-
//
|
|
1047
|
+
// summarizing its own mechanics. The banned phrases and few-shot examples
|
|
1048
|
+
// fix concrete regressions observed in backtests: tool-name verbs, success
|
|
1049
|
+
// suffixes, file path leaks, and markdown formatting.
|
|
975
1050
|
return `Write one plain-English TLDR for a Pi coding agent.
|
|
976
1051
|
Describe the work progress as if a human developer were doing it.
|
|
977
1052
|
Focus on the task activity and current outcome, not agent mechanics.
|
|
978
1053
|
Do not mention tools, tool calls, prompts, messages, model output, or implementation details.
|
|
1054
|
+
Do not start with tool-narration verbs such as Read, Reading, Grep, Listing, Counting, Extracting, Displaying, Editing, Writing, Running, Publishing, Capturing, Verifying, Validating, Checking, Confirming, Searching, Finding.
|
|
1055
|
+
Use human-developer verbs instead: Reviewing, Investigating, Exploring, Updating, Refining, Fixing, Implementing, Wrapping up, Bumping, Releasing.
|
|
1056
|
+
Do not use file paths, file extensions, code identifiers, package names, or version strings.
|
|
1057
|
+
Do not use backticks, asterisks, underscores, quotes, or any markdown formatting.
|
|
1058
|
+
Do not append filler suffixes such as "with success", "successfully", or "completed successfully".
|
|
1059
|
+
Do not claim progress or completion that is not present in the activity.
|
|
979
1060
|
Use the prior TLDRs for context and the new activity for the update.
|
|
980
1061
|
Summarize the current state of work; do not narrate the history.
|
|
981
1062
|
If context is sparse, still summarize the available activity.
|
|
@@ -986,9 +1067,44 @@ Prefer verb + direct object. Include outcome only if important.
|
|
|
986
1067
|
Do not address the user.
|
|
987
1068
|
Output only the status fragment itself. No prefixes, labels, bullets, or quotes.
|
|
988
1069
|
Plain text only; no markdown, JSON, code, file paths, or tool names.
|
|
1070
|
+
|
|
1071
|
+
Good examples:
|
|
1072
|
+
${goodExamples}
|
|
1073
|
+
|
|
1074
|
+
Bad examples:
|
|
1075
|
+
- Editing extensions/status-footer.ts with success.
|
|
1076
|
+
- Reading status-footer file completed successfully.
|
|
1077
|
+
- Publishing \`pi-bar@0.3.3\` to npm.
|
|
1078
|
+
- Grepping for sanitizeTldrText callers.
|
|
1079
|
+
- Continuing with task progression.
|
|
1080
|
+
- Investigating user input responses.
|
|
1081
|
+
|
|
989
1082
|
${tenseInstruction}`;
|
|
990
1083
|
}
|
|
991
1084
|
|
|
1085
|
+
function extractStopReason(message: unknown): string | undefined {
|
|
1086
|
+
if (!message || typeof message !== "object") return undefined;
|
|
1087
|
+
const record = message as Record<string, unknown>;
|
|
1088
|
+
const stopReason = record.stopReason;
|
|
1089
|
+
return typeof stopReason === "string" ? stopReason : undefined;
|
|
1090
|
+
}
|
|
1091
|
+
|
|
1092
|
+
// Two TLDRs are near-duplicates if their normalized action fragments (lowercase,
|
|
1093
|
+
// punctuation/whitespace collapsed) match. Catches bursts of cosmetic variants
|
|
1094
|
+
// like "Editing X." vs "Editing X" vs "editing x.".
|
|
1095
|
+
function normalizedActionFragment(text: string): string {
|
|
1096
|
+
return text
|
|
1097
|
+
.toLowerCase()
|
|
1098
|
+
.replace(/[\p{P}\p{S}]+/gu, " ")
|
|
1099
|
+
.replace(/\s+/gu, " ")
|
|
1100
|
+
.trim();
|
|
1101
|
+
}
|
|
1102
|
+
|
|
1103
|
+
function isNearDuplicateTldr(current: string, previous: string): boolean {
|
|
1104
|
+
if (!previous) return false;
|
|
1105
|
+
return normalizedActionFragment(current) === normalizedActionFragment(previous);
|
|
1106
|
+
}
|
|
1107
|
+
|
|
992
1108
|
function previousCheckpointLines(checkpoints: readonly TldrCheckpoint[]): string {
|
|
993
1109
|
if (checkpoints.length === 0) return "none";
|
|
994
1110
|
return checkpoints
|
|
@@ -1110,6 +1226,7 @@ function stripTerminalControls(text: string): string {
|
|
|
1110
1226
|
const LEAKED_PREFIX_PATTERN =
|
|
1111
1227
|
/^\s*(?:[-*•]\s*)?(?:(?:through\s+activity|activity|checkpoint)\s+\d+\s*[:.\-—–]\s*|(?:tldr|summary)\s*[:.\-—–]\s*)+/i;
|
|
1112
1228
|
const LEADING_PUNCT_PATTERN = /^[\s\-—–•*:#.,;]+/;
|
|
1229
|
+
const TRAILING_PUNCT_PATTERN = /[\s\-—–•*:#.,;]+$/;
|
|
1113
1230
|
|
|
1114
1231
|
function stripLeakedScaffolding(text: string): string {
|
|
1115
1232
|
let cleaned = text;
|
|
@@ -1122,13 +1239,84 @@ function stripLeakedScaffolding(text: string): string {
|
|
|
1122
1239
|
return cleaned;
|
|
1123
1240
|
}
|
|
1124
1241
|
|
|
1242
|
+
// Strip backticks (` and triple-backtick fences) and markdown emphasis markers
|
|
1243
|
+
// the model occasionally adds despite the prompt's plain-text rule. We keep
|
|
1244
|
+
// inner content verbatim; only the formatting characters disappear.
|
|
1245
|
+
function stripMarkdownFormatting(text: string): string {
|
|
1246
|
+
return text
|
|
1247
|
+
.replace(/```+/g, "")
|
|
1248
|
+
.replace(/`+/g, "")
|
|
1249
|
+
.replace(/(^|[^\\])([*_~]{1,3})(.+?)\2/g, "$1$3");
|
|
1250
|
+
}
|
|
1251
|
+
|
|
1252
|
+
// File-path / version / package leaks observed in backtests:
|
|
1253
|
+
// extensions/status-footer.ts, README.md, package.json, pi-bar@0.3.3, …
|
|
1254
|
+
const FILE_PATH_PATTERN =
|
|
1255
|
+
/(?:\b[\w./@-]+\.(?:ts|tsx|js|jsx|mjs|cjs|md|json|yml|yaml|toml|lock|sh|py|rs|go|html|css))\b/g;
|
|
1256
|
+
const PACKAGE_VERSION_PATTERN = /\b[\w./@-]+@\d[\w.+-]*\b/g;
|
|
1257
|
+
const VERSION_PATTERN = /\bv?\d+\.\d+(?:\.\d+(?:[-+][\w.]+)?)?\b/g;
|
|
1258
|
+
|
|
1259
|
+
function stripIdentifierLeaks(text: string): string {
|
|
1260
|
+
return text
|
|
1261
|
+
.replace(PACKAGE_VERSION_PATTERN, "")
|
|
1262
|
+
.replace(FILE_PATH_PATTERN, "")
|
|
1263
|
+
.replace(VERSION_PATTERN, "")
|
|
1264
|
+
.replace(/\s+/g, " ")
|
|
1265
|
+
.trim();
|
|
1266
|
+
}
|
|
1267
|
+
|
|
1268
|
+
// After identifier removal the sentence often has a dangling preposition like
|
|
1269
|
+
// "Bumping version to for publishing" (was "to 0.3.2 for publishing") or a
|
|
1270
|
+
// trailing "to"/"version". Run in a fix-point loop so chained leftovers (e.g.
|
|
1271
|
+
// "to version") collapse cleanly.
|
|
1272
|
+
const DANGLING_TRAILING_PREP_PATTERN =
|
|
1273
|
+
/\s+(?:to|at|as|of|by|for|in|on|with|from|version|v)\s*[.!?,;:]?\s*$/i;
|
|
1274
|
+
const DANGLING_PREP_CHAIN_PATTERN =
|
|
1275
|
+
/\b(to|at|as|of|by|from|version|v)\s+(for|in|on|with|from|after|before|during|to|at|as|of|by|and|but|or)\b/gi;
|
|
1276
|
+
|
|
1277
|
+
function stripDanglingPrepositions(text: string): string {
|
|
1278
|
+
let cleaned = text;
|
|
1279
|
+
let previous: string;
|
|
1280
|
+
do {
|
|
1281
|
+
previous = cleaned;
|
|
1282
|
+
cleaned = cleaned.replace(DANGLING_PREP_CHAIN_PATTERN, "$2");
|
|
1283
|
+
cleaned = cleaned.replace(DANGLING_TRAILING_PREP_PATTERN, "").trim();
|
|
1284
|
+
} while (cleaned !== previous && cleaned.length > 0);
|
|
1285
|
+
return cleaned;
|
|
1286
|
+
}
|
|
1287
|
+
|
|
1288
|
+
// Trailing filler suffixes observed in backtests:
|
|
1289
|
+
// "... with success.", "... completed successfully.", "... successfully."
|
|
1290
|
+
const SUCCESS_SUFFIX_PATTERN =
|
|
1291
|
+
/[\s,;:—–-]*(?:with\s+success|completed\s+successfully|finished\s+successfully|done\s+successfully|successfully\s+completed|successfully\s+finished|successfully)\s*[.!?]*\s*$/i;
|
|
1292
|
+
|
|
1293
|
+
function stripSuccessSuffix(text: string): string {
|
|
1294
|
+
let cleaned = text;
|
|
1295
|
+
let stripped = false;
|
|
1296
|
+
while (true) {
|
|
1297
|
+
const next = cleaned.replace(SUCCESS_SUFFIX_PATTERN, "").trim();
|
|
1298
|
+
if (next === cleaned || next.length === 0) break;
|
|
1299
|
+
cleaned = next;
|
|
1300
|
+
stripped = true;
|
|
1301
|
+
}
|
|
1302
|
+
// Only clean up trailing punctuation when an actual success suffix was
|
|
1303
|
+
// removed; otherwise we would strip the natural period from a normal
|
|
1304
|
+
// sentence like "Reviewing footer behavior.".
|
|
1305
|
+
if (stripped) cleaned = cleaned.replace(TRAILING_PUNCT_PATTERN, "").trim();
|
|
1306
|
+
return cleaned;
|
|
1307
|
+
}
|
|
1308
|
+
|
|
1125
1309
|
function sanitizeTldrText(
|
|
1126
1310
|
text: string,
|
|
1127
1311
|
maxChars = MAX_SAFE_TLDR_CHARS,
|
|
1128
1312
|
): string {
|
|
1129
1313
|
const stripped = stripTerminalControls(text);
|
|
1130
|
-
const
|
|
1131
|
-
|
|
1314
|
+
const withoutMarkdown = stripMarkdownFormatting(stripped);
|
|
1315
|
+
const withoutScaffolding = stripLeakedScaffolding(withoutMarkdown) || withoutMarkdown;
|
|
1316
|
+
const withoutLeaks = stripIdentifierLeaks(withoutScaffolding) || withoutScaffolding;
|
|
1317
|
+
const withoutDangling = stripDanglingPrepositions(withoutLeaks) || withoutLeaks;
|
|
1318
|
+
const withoutSuccess = stripSuccessSuffix(withoutDangling) || withoutDangling;
|
|
1319
|
+
return truncateText(withoutSuccess, maxChars);
|
|
1132
1320
|
}
|
|
1133
1321
|
|
|
1134
1322
|
function shouldShowStatus(key: string, filter: StatusFilter): boolean {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-bar",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.28",
|
|
4
4
|
"description": "Pi footer/statusline showing active model, thinking level, context pressure, live TLDR, and extension statuses.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"keywords": [
|
|
@@ -51,6 +51,6 @@
|
|
|
51
51
|
"extensions": [
|
|
52
52
|
"./extensions/status-footer.ts"
|
|
53
53
|
],
|
|
54
|
-
"image": "https://cdn.jsdelivr.net/npm/pi-bar@0.3.
|
|
54
|
+
"image": "https://cdn.jsdelivr.net/npm/pi-bar@0.3.28/assets/screenshot-yellow.png"
|
|
55
55
|
}
|
|
56
56
|
}
|