pi-caveman 1.0.10 → 1.0.12

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 CHANGED
@@ -82,7 +82,7 @@ When active, a status bar displays caveman level and an animated campfire flicke
82
82
 
83
83
  ## How It Works
84
84
 
85
- The extension hooks `before_agent_start` to append caveman communication rules to the system prompt at the selected intensity. Within a session, the active level is stored as a custom session entry and restored on resume. Across sessions, persistent config (`~/.pi/agent/caveman.json` by default, or `$XDG_CONFIG_HOME/pi/agent/caveman.json` when `XDG_CONFIG_HOME` is set; `PI_CODING_AGENT_DIR` overrides both) provides the default level and status bar preference. Auto-clarity rules tell the model to drop caveman mode for security warnings or irreversible actions.
85
+ The extension hooks `before_agent_start` to add caveman communication rules at the selected intensity. On Pi hosts with `systemPromptOptions.sections`, it writes a `caveman` section so later extensions can still add their own prompt sections, and removes that section when disabled. Older hosts use whole-prompt replacement, preserving ordered prompt blocks on OMP. Within a session, the active level is stored as a custom session entry and restored on resume. Across sessions, persistent config (`~/.pi/agent/caveman.json` by default, or `$XDG_CONFIG_HOME/pi/agent/caveman.json` when `XDG_CONFIG_HOME` is set; `PI_CODING_AGENT_DIR` overrides both) provides the default level and status bar preference. Auto-clarity rules tell the model to drop caveman mode for security warnings or irreversible actions.
86
86
 
87
87
  ## Warning
88
88
 
@@ -212,6 +212,7 @@ export default function caveman(pi: ExtensionAPI) {
212
212
  let config: CavemanConfig = { ...DEFAULT_CONFIG };
213
213
  let timer: ReturnType<typeof setInterval> | null = null;
214
214
  let frameIndex = 0;
215
+ let animationGeneration = 0;
215
216
  let isActive = false;
216
217
  let configLoadPromise: Promise<void> | null = null;
217
218
 
@@ -230,6 +231,7 @@ export default function caveman(pi: ExtensionAPI) {
230
231
  // -- Animation helpers --
231
232
 
232
233
  function stopAnimation() {
234
+ animationGeneration++;
233
235
  if (timer) {
234
236
  clearInterval(timer);
235
237
  timer = null;
@@ -240,32 +242,34 @@ export default function caveman(pi: ExtensionAPI) {
240
242
  function syncStatus(ctx: Pick<ExtensionContext, "ui">) {
241
243
  stopAnimation();
242
244
 
243
- if (level === "off" || !config.showStatus) {
244
- ctx.ui.setStatus("caveman", "");
245
- return;
246
- }
247
-
248
- // Hosts like pi-web-ui expose setStatus without a TUI theme — bail instead of throwing on theme.fg.
249
- if (!ctx.ui.theme?.fg) return;
250
- const theme = ctx.ui.theme;
245
+ const generation = animationGeneration;
246
+ const anim = level === "off" ? null : ANIMATIONS[level];
247
+ const renderFrame = () => {
248
+ if (generation !== animationGeneration) return false;
249
+ try {
250
+ if (!anim || !config.showStatus) {
251
+ ctx.ui.setStatus("caveman", "");
252
+ return false;
253
+ }
251
254
 
252
- const anim = ANIMATIONS[level];
253
- const setFrame = (frame: string) => {
254
- ctx.ui.setStatus("caveman", frame + " " + theme.fg("muted", "caveman level: ") + theme.fg("text", anim.label));
255
+ // Hosts like pi-web-ui may expose setStatus without a TUI theme.
256
+ const theme = ctx.ui.theme;
257
+ if (!theme?.fg) return false;
258
+ const frame = anim.frames[frameIndex % anim.frames.length]!;
259
+ ctx.ui.setStatus("caveman", frame + " " + theme.fg("muted", "caveman level: ") + theme.fg("text", anim.label));
260
+ frameIndex++;
261
+ return true;
262
+ } catch (error) {
263
+ // Timer callbacks run outside Pi's event-handler error boundary.
264
+ stopAnimation();
265
+ if (!(error instanceof Error) || !error.message.includes("stale after session replacement")) throw error;
266
+ return false;
267
+ }
255
268
  };
256
269
 
257
- if (!isActive) {
258
- setFrame(anim.frames[0]!);
259
- return;
270
+ if (renderFrame() && isActive && anim) {
271
+ timer = setInterval(renderFrame, anim.interval);
260
272
  }
261
-
262
- const renderFrame = () => {
263
- setFrame(anim.frames[frameIndex % anim.frames.length]!);
264
- frameIndex++;
265
- };
266
-
267
- renderFrame();
268
- timer = setInterval(renderFrame, anim.interval);
269
273
  }
270
274
 
271
275
  // -- Restore state on session load --
@@ -439,13 +443,25 @@ export default function caveman(pi: ExtensionAPI) {
439
443
 
440
444
  pi.on("before_agent_start", async (event) => {
441
445
  await ensureConfigLoaded();
442
- if (level === "off") return;
446
+ // Use a structural check so older Pi/OMP hosts can still use the legacy path.
447
+ const sections = (event as { systemPromptOptions?: { sections?: Record<string, string> } })
448
+ .systemPromptOptions?.sections;
449
+ const hasSections = sections !== null && typeof sections === "object" && !Array.isArray(sections);
450
+ if (level === "off") {
451
+ if (hasSections) delete sections.caveman;
452
+ return;
453
+ }
443
454
  const addition =
444
455
  level === "micro"
445
456
  ? MICRO_PROMPT
446
457
  : level === "lite"
447
458
  ? `${INTENSITY.lite}\n\n${SAFETY}`
448
459
  : `${BASE}\n\n${INTENSITY[level]}\n\n${SAFETY}`;
460
+ if (hasSections) {
461
+ // Returning a whole prompt freezes it before later handlers add their sections.
462
+ sections.caveman = addition;
463
+ return;
464
+ }
449
465
  return {
450
466
  systemPrompt: appendSystemPrompt(
451
467
  event.systemPrompt as string | string[] | null | undefined,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-caveman",
3
- "version": "1.0.10",
3
+ "version": "1.0.12",
4
4
  "description": "Why use many token when few do trick. Caveman mode for pi — cuts ~75% output tokens while keeping full technical accuracy.",
5
5
  "keywords": [
6
6
  "pi-package",