pi-bar 0.3.28 → 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 +168 -7
- 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,6 +1008,88 @@ 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
1094
|
// Tense + exemplars vary by priority. Final TLDRs describe completed turns;
|
|
1013
1095
|
// using -ing examples for final caused all final TLDRs to slip back into
|
|
@@ -1016,6 +1098,7 @@ function checkpointSystemPrompt(job: TldrCheckpointJob): string {
|
|
|
1016
1098
|
// directive instead of generic filler like "Continuing with task".
|
|
1017
1099
|
let tenseInstruction: string;
|
|
1018
1100
|
let goodExamples: string;
|
|
1101
|
+
let allowedFirstWords: readonly string[];
|
|
1019
1102
|
if (job.displayPriority === "final") {
|
|
1020
1103
|
tenseInstruction = "Start with a past-tense verb describing what was completed.";
|
|
1021
1104
|
goodExamples = [
|
|
@@ -1024,15 +1107,18 @@ function checkpointSystemPrompt(job: TldrCheckpointJob): string {
|
|
|
1024
1107
|
"- Refined sanitizer for stray prefixes",
|
|
1025
1108
|
"- Wrapped up extension release",
|
|
1026
1109
|
].join("\n");
|
|
1110
|
+
allowedFirstWords = ALLOWED_FIRST_WORDS_PAST;
|
|
1027
1111
|
} else if (job.displayPriority === "immediate") {
|
|
1028
1112
|
tenseInstruction =
|
|
1029
|
-
"Rephrase the user's new request as a concise present-progressive task clause.
|
|
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.";
|
|
1030
1114
|
goodExamples = [
|
|
1031
1115
|
"- Reviewing footer summary behavior",
|
|
1032
1116
|
"- Investigating live TLDR regressions",
|
|
1033
1117
|
"- Refining sanitizer for stray prefixes",
|
|
1034
1118
|
"- Preparing extension release",
|
|
1119
|
+
"- Resuming refactor work",
|
|
1035
1120
|
].join("\n");
|
|
1121
|
+
allowedFirstWords = ALLOWED_FIRST_WORDS_PROGRESSIVE;
|
|
1036
1122
|
} else {
|
|
1037
1123
|
tenseInstruction = "Start with a present-tense -ing verb describing current work.";
|
|
1038
1124
|
goodExamples = [
|
|
@@ -1041,18 +1127,20 @@ function checkpointSystemPrompt(job: TldrCheckpointJob): string {
|
|
|
1041
1127
|
"- Refining sanitizer for stray prefixes",
|
|
1042
1128
|
"- Wrapping up extension release",
|
|
1043
1129
|
].join("\n");
|
|
1130
|
+
allowedFirstWords = ALLOWED_FIRST_WORDS_PROGRESSIVE;
|
|
1044
1131
|
}
|
|
1045
1132
|
|
|
1046
1133
|
// Frame the model as a human developer describing visible work, not an agent
|
|
1047
1134
|
// summarizing its own mechanics. The banned phrases and few-shot examples
|
|
1048
1135
|
// fix concrete regressions observed in backtests: tool-name verbs, success
|
|
1049
|
-
// 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).
|
|
1050
1139
|
return `Write one plain-English TLDR for a Pi coding agent.
|
|
1051
1140
|
Describe the work progress as if a human developer were doing it.
|
|
1052
1141
|
Focus on the task activity and current outcome, not agent mechanics.
|
|
1053
1142
|
Do not mention tools, tool calls, prompts, messages, model output, or implementation details.
|
|
1054
|
-
|
|
1055
|
-
Use human-developer verbs instead: Reviewing, Investigating, Exploring, Updating, Refining, Fixing, Implementing, Wrapping up, Bumping, Releasing.
|
|
1143
|
+
Use human-developer verbs instead of tool-narration verbs.
|
|
1056
1144
|
Do not use file paths, file extensions, code identifiers, package names, or version strings.
|
|
1057
1145
|
Do not use backticks, asterisks, underscores, quotes, or any markdown formatting.
|
|
1058
1146
|
Do not append filler suffixes such as "with success", "successfully", or "completed successfully".
|
|
@@ -1076,10 +1164,13 @@ Bad examples:
|
|
|
1076
1164
|
- Reading status-footer file completed successfully.
|
|
1077
1165
|
- Publishing \`pi-bar@0.3.3\` to npm.
|
|
1078
1166
|
- Grepping for sanitizeTldrText callers.
|
|
1079
|
-
-
|
|
1167
|
+
- Verifying repository status after commit.
|
|
1080
1168
|
- Investigating user input responses.
|
|
1081
1169
|
|
|
1082
|
-
|
|
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}`;
|
|
1083
1174
|
}
|
|
1084
1175
|
|
|
1085
1176
|
function extractStopReason(message: unknown): string | undefined {
|
|
@@ -1273,18 +1364,87 @@ const DANGLING_TRAILING_PREP_PATTERN =
|
|
|
1273
1364
|
/\s+(?:to|at|as|of|by|for|in|on|with|from|version|v)\s*[.!?,;:]?\s*$/i;
|
|
1274
1365
|
const DANGLING_PREP_CHAIN_PATTERN =
|
|
1275
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;
|
|
1276
1372
|
|
|
1277
1373
|
function stripDanglingPrepositions(text: string): string {
|
|
1278
1374
|
let cleaned = text;
|
|
1279
1375
|
let previous: string;
|
|
1280
1376
|
do {
|
|
1281
1377
|
previous = cleaned;
|
|
1378
|
+
cleaned = cleaned.replace(VERB_BARE_PREP_PATTERN, "$1 ");
|
|
1282
1379
|
cleaned = cleaned.replace(DANGLING_PREP_CHAIN_PATTERN, "$2");
|
|
1283
1380
|
cleaned = cleaned.replace(DANGLING_TRAILING_PREP_PATTERN, "").trim();
|
|
1284
1381
|
} while (cleaned !== previous && cleaned.length > 0);
|
|
1285
1382
|
return cleaned;
|
|
1286
1383
|
}
|
|
1287
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
|
+
|
|
1288
1448
|
// Trailing filler suffixes observed in backtests:
|
|
1289
1449
|
// "... with success.", "... completed successfully.", "... successfully."
|
|
1290
1450
|
const SUCCESS_SUFFIX_PATTERN =
|
|
@@ -1316,7 +1476,8 @@ function sanitizeTldrText(
|
|
|
1316
1476
|
const withoutLeaks = stripIdentifierLeaks(withoutScaffolding) || withoutScaffolding;
|
|
1317
1477
|
const withoutDangling = stripDanglingPrepositions(withoutLeaks) || withoutLeaks;
|
|
1318
1478
|
const withoutSuccess = stripSuccessSuffix(withoutDangling) || withoutDangling;
|
|
1319
|
-
|
|
1479
|
+
const rewritten = rewriteBannedFirstWord(withoutSuccess);
|
|
1480
|
+
return truncateText(rewritten, maxChars);
|
|
1320
1481
|
}
|
|
1321
1482
|
|
|
1322
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
|
}
|