pi-bar 0.3.27 → 0.3.29
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 +225 -14
- 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
|
|
|
@@ -1008,21 +1008,139 @@ class FooterTldrEngine {
|
|
|
1008
1008
|
}
|
|
1009
1009
|
}
|
|
1010
1010
|
|
|
1011
|
+
// First-word allow/ban lists are duplicated between the prompt and the
|
|
1012
|
+
// sanitizer fallback. They live here so both stay aligned automatically when
|
|
1013
|
+
// new verbs get added or moved.
|
|
1014
|
+
const ALLOWED_FIRST_WORDS_PROGRESSIVE = [
|
|
1015
|
+
"Reviewing",
|
|
1016
|
+
"Investigating",
|
|
1017
|
+
"Exploring",
|
|
1018
|
+
"Updating",
|
|
1019
|
+
"Refining",
|
|
1020
|
+
"Fixing",
|
|
1021
|
+
"Implementing",
|
|
1022
|
+
"Wrapping up",
|
|
1023
|
+
"Bumping",
|
|
1024
|
+
"Releasing",
|
|
1025
|
+
"Preparing",
|
|
1026
|
+
"Drafting",
|
|
1027
|
+
"Resuming",
|
|
1028
|
+
"Pulling",
|
|
1029
|
+
"Surveying",
|
|
1030
|
+
"Recording",
|
|
1031
|
+
] as const;
|
|
1032
|
+
const ALLOWED_FIRST_WORDS_PAST = [
|
|
1033
|
+
"Reviewed",
|
|
1034
|
+
"Investigated",
|
|
1035
|
+
"Explored",
|
|
1036
|
+
"Updated",
|
|
1037
|
+
"Refined",
|
|
1038
|
+
"Fixed",
|
|
1039
|
+
"Implemented",
|
|
1040
|
+
"Wrapped up",
|
|
1041
|
+
"Bumped",
|
|
1042
|
+
"Released",
|
|
1043
|
+
"Prepared",
|
|
1044
|
+
"Drafted",
|
|
1045
|
+
"Resumed",
|
|
1046
|
+
"Pulled",
|
|
1047
|
+
"Surveyed",
|
|
1048
|
+
"Recorded",
|
|
1049
|
+
] as const;
|
|
1050
|
+
const BANNED_FIRST_WORDS = [
|
|
1051
|
+
"Read",
|
|
1052
|
+
"Reading",
|
|
1053
|
+
"Grep",
|
|
1054
|
+
"Grepping",
|
|
1055
|
+
"Listing",
|
|
1056
|
+
"List",
|
|
1057
|
+
"Counting",
|
|
1058
|
+
"Counted",
|
|
1059
|
+
"Extracting",
|
|
1060
|
+
"Extracted",
|
|
1061
|
+
"Displaying",
|
|
1062
|
+
"Displayed",
|
|
1063
|
+
"Editing",
|
|
1064
|
+
"Edited",
|
|
1065
|
+
"Writing",
|
|
1066
|
+
"Wrote",
|
|
1067
|
+
"Running",
|
|
1068
|
+
"Ran",
|
|
1069
|
+
"Publishing",
|
|
1070
|
+
"Published",
|
|
1071
|
+
"Capturing",
|
|
1072
|
+
"Captured",
|
|
1073
|
+
"Verifying",
|
|
1074
|
+
"Verified",
|
|
1075
|
+
"Verify",
|
|
1076
|
+
"Validating",
|
|
1077
|
+
"Validated",
|
|
1078
|
+
"Validate",
|
|
1079
|
+
"Checking",
|
|
1080
|
+
"Checked",
|
|
1081
|
+
"Check",
|
|
1082
|
+
"Confirming",
|
|
1083
|
+
"Confirmed",
|
|
1084
|
+
"Confirm",
|
|
1085
|
+
"Searching",
|
|
1086
|
+
"Searched",
|
|
1087
|
+
"Search",
|
|
1088
|
+
"Finding",
|
|
1089
|
+
"Found",
|
|
1090
|
+
"Find",
|
|
1091
|
+
] as const;
|
|
1092
|
+
|
|
1011
1093
|
function checkpointSystemPrompt(job: TldrCheckpointJob): string {
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1094
|
+
// Tense + exemplars vary by priority. Final TLDRs describe completed turns;
|
|
1095
|
+
// using -ing examples for final caused all final TLDRs to slip back into
|
|
1096
|
+
// present-progressive even though the literal instruction said past tense.
|
|
1097
|
+
// Immediate TLDRs cover a brand-new user request; they need a rephrase
|
|
1098
|
+
// directive instead of generic filler like "Continuing with task".
|
|
1099
|
+
let tenseInstruction: string;
|
|
1100
|
+
let goodExamples: string;
|
|
1101
|
+
let allowedFirstWords: readonly string[];
|
|
1102
|
+
if (job.displayPriority === "final") {
|
|
1103
|
+
tenseInstruction = "Start with a past-tense verb describing what was completed.";
|
|
1104
|
+
goodExamples = [
|
|
1105
|
+
"- Updated footer summary behavior",
|
|
1106
|
+
"- Investigated live TLDR regressions",
|
|
1107
|
+
"- Refined sanitizer for stray prefixes",
|
|
1108
|
+
"- Wrapped up extension release",
|
|
1109
|
+
].join("\n");
|
|
1110
|
+
allowedFirstWords = ALLOWED_FIRST_WORDS_PAST;
|
|
1111
|
+
} else if (job.displayPriority === "immediate") {
|
|
1112
|
+
tenseInstruction =
|
|
1113
|
+
"Rephrase the user's new request as a concise present-progressive task clause. If the request is opaque (e.g. \"continue\", \"go\", \"ok\"), name the carry-over task with a noun (e.g. \"Resuming refactor work\"), not generic filler.";
|
|
1114
|
+
goodExamples = [
|
|
1115
|
+
"- Reviewing footer summary behavior",
|
|
1116
|
+
"- Investigating live TLDR regressions",
|
|
1117
|
+
"- Refining sanitizer for stray prefixes",
|
|
1118
|
+
"- Preparing extension release",
|
|
1119
|
+
"- Resuming refactor work",
|
|
1120
|
+
].join("\n");
|
|
1121
|
+
allowedFirstWords = ALLOWED_FIRST_WORDS_PROGRESSIVE;
|
|
1122
|
+
} else {
|
|
1123
|
+
tenseInstruction = "Start with a present-tense -ing verb describing current work.";
|
|
1124
|
+
goodExamples = [
|
|
1125
|
+
"- Reviewing footer summary behavior",
|
|
1126
|
+
"- Investigating live TLDR regressions",
|
|
1127
|
+
"- Refining sanitizer for stray prefixes",
|
|
1128
|
+
"- Wrapping up extension release",
|
|
1129
|
+
].join("\n");
|
|
1130
|
+
allowedFirstWords = ALLOWED_FIRST_WORDS_PROGRESSIVE;
|
|
1131
|
+
}
|
|
1132
|
+
|
|
1016
1133
|
// Frame the model as a human developer describing visible work, not an agent
|
|
1017
1134
|
// summarizing its own mechanics. The banned phrases and few-shot examples
|
|
1018
1135
|
// fix concrete regressions observed in backtests: tool-name verbs, success
|
|
1019
|
-
// suffixes, file path leaks, and markdown formatting.
|
|
1136
|
+
// suffixes, file path leaks, and markdown formatting. The HARD CONSTRAINTS
|
|
1137
|
+
// block sits at the tail of the prompt because instruction-following models
|
|
1138
|
+
// give the most weight to the most-recent directives (recency bias).
|
|
1020
1139
|
return `Write one plain-English TLDR for a Pi coding agent.
|
|
1021
1140
|
Describe the work progress as if a human developer were doing it.
|
|
1022
1141
|
Focus on the task activity and current outcome, not agent mechanics.
|
|
1023
1142
|
Do not mention tools, tool calls, prompts, messages, model output, or implementation details.
|
|
1024
|
-
|
|
1025
|
-
Use human-developer verbs instead: Reviewing, Investigating, Exploring, Updating, Refining, Fixing, Implementing, Wrapping up.
|
|
1143
|
+
Use human-developer verbs instead of tool-narration verbs.
|
|
1026
1144
|
Do not use file paths, file extensions, code identifiers, package names, or version strings.
|
|
1027
1145
|
Do not use backticks, asterisks, underscores, quotes, or any markdown formatting.
|
|
1028
1146
|
Do not append filler suffixes such as "with success", "successfully", or "completed successfully".
|
|
@@ -1039,18 +1157,20 @@ Output only the status fragment itself. No prefixes, labels, bullets, or quotes.
|
|
|
1039
1157
|
Plain text only; no markdown, JSON, code, file paths, or tool names.
|
|
1040
1158
|
|
|
1041
1159
|
Good examples:
|
|
1042
|
-
|
|
1043
|
-
- Investigating live TLDR regressions
|
|
1044
|
-
- Refining sanitizer for stray prefixes
|
|
1045
|
-
- Wrapping up extension release
|
|
1160
|
+
${goodExamples}
|
|
1046
1161
|
|
|
1047
1162
|
Bad examples:
|
|
1048
1163
|
- Editing extensions/status-footer.ts with success.
|
|
1049
1164
|
- Reading status-footer file completed successfully.
|
|
1050
1165
|
- Publishing \`pi-bar@0.3.3\` to npm.
|
|
1051
1166
|
- Grepping for sanitizeTldrText callers.
|
|
1167
|
+
- Verifying repository status after commit.
|
|
1168
|
+
- Investigating user input responses.
|
|
1052
1169
|
|
|
1053
|
-
|
|
1170
|
+
HARD CONSTRAINTS (apply last; override anything above that conflicts):
|
|
1171
|
+
- First word MUST be one of: ${allowedFirstWords.join(", ")}.
|
|
1172
|
+
- First word MUST NOT be: ${BANNED_FIRST_WORDS.join(", ")}.
|
|
1173
|
+
- ${tenseInstruction}`;
|
|
1054
1174
|
}
|
|
1055
1175
|
|
|
1056
1176
|
function extractStopReason(message: unknown): string | undefined {
|
|
@@ -1236,6 +1356,95 @@ function stripIdentifierLeaks(text: string): string {
|
|
|
1236
1356
|
.trim();
|
|
1237
1357
|
}
|
|
1238
1358
|
|
|
1359
|
+
// After identifier removal the sentence often has a dangling preposition like
|
|
1360
|
+
// "Bumping version to for publishing" (was "to 0.3.2 for publishing") or a
|
|
1361
|
+
// trailing "to"/"version". Run in a fix-point loop so chained leftovers (e.g.
|
|
1362
|
+
// "to version") collapse cleanly.
|
|
1363
|
+
const DANGLING_TRAILING_PREP_PATTERN =
|
|
1364
|
+
/\s+(?:to|at|as|of|by|for|in|on|with|from|version|v)\s*[.!?,;:]?\s*$/i;
|
|
1365
|
+
const DANGLING_PREP_CHAIN_PATTERN =
|
|
1366
|
+
/\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;
|
|
1367
|
+
// File-path / identifier strip can leave a verb directly followed by a
|
|
1368
|
+
// preposition: "Reviewing README for image update" -> after path strip ->
|
|
1369
|
+
// "Reviewing for image update". Collapse the bare verb+prep into just the verb.
|
|
1370
|
+
const VERB_BARE_PREP_PATTERN =
|
|
1371
|
+
/\b(Reviewing|Investigating|Updating|Refining|Exploring|Fixing|Implementing|Bumping|Releasing|Preparing|Drafting|Resuming|Pulling|Surveying|Recording)\s+(?:for|in|on|with|after|before|to|at|as|of|by|from)\s+/gi;
|
|
1372
|
+
|
|
1373
|
+
function stripDanglingPrepositions(text: string): string {
|
|
1374
|
+
let cleaned = text;
|
|
1375
|
+
let previous: string;
|
|
1376
|
+
do {
|
|
1377
|
+
previous = cleaned;
|
|
1378
|
+
cleaned = cleaned.replace(VERB_BARE_PREP_PATTERN, "$1 ");
|
|
1379
|
+
cleaned = cleaned.replace(DANGLING_PREP_CHAIN_PATTERN, "$2");
|
|
1380
|
+
cleaned = cleaned.replace(DANGLING_TRAILING_PREP_PATTERN, "").trim();
|
|
1381
|
+
} while (cleaned !== previous && cleaned.length > 0);
|
|
1382
|
+
return cleaned;
|
|
1383
|
+
}
|
|
1384
|
+
|
|
1385
|
+
// Last-resort fallback when the model ignored the allow-list and started the
|
|
1386
|
+
// TLDR with a banned tool-narration verb. The map preserves tense (present
|
|
1387
|
+
// vs past). Case of the first letter follows the original word.
|
|
1388
|
+
const BANNED_FIRST_WORD_REWRITES: Record<string, string> = {
|
|
1389
|
+
Reading: "Reviewing",
|
|
1390
|
+
Read: "Reviewed",
|
|
1391
|
+
Grepping: "Investigating",
|
|
1392
|
+
Grep: "Investigated",
|
|
1393
|
+
Listing: "Reviewing",
|
|
1394
|
+
List: "Reviewed",
|
|
1395
|
+
Counting: "Surveying",
|
|
1396
|
+
Counted: "Surveyed",
|
|
1397
|
+
Extracting: "Pulling",
|
|
1398
|
+
Extracted: "Pulled",
|
|
1399
|
+
Displaying: "Reviewing",
|
|
1400
|
+
Displayed: "Reviewed",
|
|
1401
|
+
Editing: "Updating",
|
|
1402
|
+
Edited: "Updated",
|
|
1403
|
+
Writing: "Drafting",
|
|
1404
|
+
Wrote: "Drafted",
|
|
1405
|
+
Running: "Working on",
|
|
1406
|
+
Ran: "Worked on",
|
|
1407
|
+
Publishing: "Releasing",
|
|
1408
|
+
Published: "Released",
|
|
1409
|
+
Capturing: "Recording",
|
|
1410
|
+
Captured: "Recorded",
|
|
1411
|
+
Verifying: "Reviewing",
|
|
1412
|
+
Verified: "Reviewed",
|
|
1413
|
+
Verify: "Review",
|
|
1414
|
+
Validating: "Reviewing",
|
|
1415
|
+
Validated: "Reviewed",
|
|
1416
|
+
Validate: "Review",
|
|
1417
|
+
Checking: "Reviewing",
|
|
1418
|
+
Checked: "Reviewed",
|
|
1419
|
+
Check: "Review",
|
|
1420
|
+
Confirming: "Reviewing",
|
|
1421
|
+
Confirmed: "Reviewed",
|
|
1422
|
+
Confirm: "Review",
|
|
1423
|
+
Searching: "Investigating",
|
|
1424
|
+
Searched: "Investigated",
|
|
1425
|
+
Search: "Investigate",
|
|
1426
|
+
Finding: "Investigating",
|
|
1427
|
+
Found: "Investigated",
|
|
1428
|
+
Find: "Investigate",
|
|
1429
|
+
};
|
|
1430
|
+
const BANNED_FIRST_WORD_PATTERN = new RegExp(
|
|
1431
|
+
`^(${Object.keys(BANNED_FIRST_WORD_REWRITES).join("|")})\\b`,
|
|
1432
|
+
);
|
|
1433
|
+
|
|
1434
|
+
function rewriteBannedFirstWord(text: string): string {
|
|
1435
|
+
const match = BANNED_FIRST_WORD_PATTERN.exec(text);
|
|
1436
|
+
if (!match) return text;
|
|
1437
|
+
const original = match[1];
|
|
1438
|
+
const replacement = BANNED_FIRST_WORD_REWRITES[original];
|
|
1439
|
+
if (!replacement) return text;
|
|
1440
|
+
// Preserve lowercase-start TLDRs (rare but possible) by matching original case.
|
|
1441
|
+
const cased =
|
|
1442
|
+
original[0] === original[0].toLowerCase()
|
|
1443
|
+
? replacement[0].toLowerCase() + replacement.slice(1)
|
|
1444
|
+
: replacement;
|
|
1445
|
+
return cased + text.slice(original.length);
|
|
1446
|
+
}
|
|
1447
|
+
|
|
1239
1448
|
// Trailing filler suffixes observed in backtests:
|
|
1240
1449
|
// "... with success.", "... completed successfully.", "... successfully."
|
|
1241
1450
|
const SUCCESS_SUFFIX_PATTERN =
|
|
@@ -1265,8 +1474,10 @@ function sanitizeTldrText(
|
|
|
1265
1474
|
const withoutMarkdown = stripMarkdownFormatting(stripped);
|
|
1266
1475
|
const withoutScaffolding = stripLeakedScaffolding(withoutMarkdown) || withoutMarkdown;
|
|
1267
1476
|
const withoutLeaks = stripIdentifierLeaks(withoutScaffolding) || withoutScaffolding;
|
|
1268
|
-
const
|
|
1269
|
-
|
|
1477
|
+
const withoutDangling = stripDanglingPrepositions(withoutLeaks) || withoutLeaks;
|
|
1478
|
+
const withoutSuccess = stripSuccessSuffix(withoutDangling) || withoutDangling;
|
|
1479
|
+
const rewritten = rewriteBannedFirstWord(withoutSuccess);
|
|
1480
|
+
return truncateText(rewritten, maxChars);
|
|
1270
1481
|
}
|
|
1271
1482
|
|
|
1272
1483
|
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.29",
|
|
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.29/assets/screenshot-yellow.png"
|
|
55
55
|
}
|
|
56
56
|
}
|