Add caveman compressed-output extension and skill

This commit is contained in:
2026-05-06 20:48:10 +10:00
parent 0c23ef1b45
commit 6224e9a0db
3 changed files with 321 additions and 0 deletions

226
extensions/caveman/index.ts Normal file
View File

@@ -0,0 +1,226 @@
/**
* pi-caveman — Compressed-output mode for Pi coding agent.
*
* Registers a `/caveman` slash command that toggles caveman-speak
* compression levels and persists state across session restarts.
*
* Usage:
* /caveman — toggle between off and full
* /caveman lite|full|ultra|wenyan-lite|wenyan-full|wenyan-ultra|off|status
*/
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
import * as fs from "node:fs";
import * as path from "node:path";
// ── Types ────────────────────────────────────────────────────────────────
type CavemanLevel = "lite" | "full" | "ultra" | "wenyan-lite" | "wenyan-full" | "wenyan-ultra";
interface CavemanState {
active: boolean;
level: CavemanLevel;
}
// ── Constants ────────────────────────────────────────────────────────────
const DEFAULT_LEVEL: CavemanLevel = "full";
const STATE_DIR = path.join(
process.env.XDG_STATE_HOME ?? path.join(process.env.HOME!, ".local", "state"),
"pi-caveman",
);
const STATE_FILE = path.join(STATE_DIR, "caveman-state.json");
const CAVEMAN_SKILL_PATH = path.join(
process.env.HOME!,
"ai-assets",
"skills",
"caveman",
"SKILL.md",
);
// ── State helpers ────────────────────────────────────────────────────────
function readState(): CavemanState {
try {
const raw = fs.readFileSync(STATE_FILE, "utf-8");
const parsed = JSON.parse(raw);
return {
active: typeof parsed.active === "boolean" ? parsed.active : false,
level: isValidLevel(parsed.level) ? parsed.level : DEFAULT_LEVEL,
};
} catch {
return { active: false, level: DEFAULT_LEVEL };
}
}
function writeState(state: CavemanState): void {
fs.mkdirSync(STATE_DIR, { recursive: true });
fs.writeFileSync(STATE_FILE, JSON.stringify(state, null, 2), "utf-8");
}
function isValidLevel(v: unknown): v is CavemanLevel {
return (
typeof v === "string" &&
["lite", "full", "ultra", "wenyan-lite", "wenyan-full", "wenyan-ultra"].includes(v)
);
}
// ── Skill content cache ──────────────────────────────────────────────────
let cachedSkillContent: string | null = null;
function getSkillContent(): string {
if (cachedSkillContent !== null) return cachedSkillContent;
try {
cachedSkillContent = fs.readFileSync(CAVEMAN_SKILL_PATH, "utf-8");
return cachedSkillContent;
} catch {
return "";
}
}
// ── Extension ────────────────────────────────────────────────────────────
export default function (pi: ExtensionAPI): void {
// ── State ──
let state = readState();
function applyMode(): void {
writeState(state);
if (state.active) {
pi.setStatus("caveman", `🦴 caveman:${state.level}`);
// Inject caveman instructions into the system prompt each turn
pi.on("before_agent_start", async (_event) => {
if (!state.active) return;
const skill = getSkillContent();
if (!skill) return;
return {
systemPrompt: `${_event.systemPrompt}\n\n---\nCRITICAL OUTPUT MODE: You are in CAVEMAN MODE. Follow these rules:\n\n${skill}`,
};
});
} else {
// Clear any previous status
// (constructor-style state means we can't easily unregister)
}
}
// ── Footer badge (always shown) ──
function updateFooter(): void {
if (state.active) {
pi.setStatus("caveman", `🦴 caveman:${state.level}`);
} else {
pi.setStatus("caveman", "");
}
}
// ── /caveman command ────────────────────────────────────────────────
pi.registerCommand("caveman", {
description: "Toggle caveman compressed-output mode (lite|full|ultra|wenyan-lite|wenyan-full|wenyan-ultra|off|status)",
handler: async (args, ctx) => {
const arg = args?.trim().toLowerCase() ?? "";
switch (arg) {
case "":
// Toggle: off → full, anything → off
if (state.active) {
state.active = false;
ctx.ui.notify("🦴 Caveman: off", "info");
} else {
state.active = true;
state.level = DEFAULT_LEVEL;
ctx.ui.notify(`🦴 Caveman: ${state.level}`, "info");
}
break;
case "off":
case "disable":
state.active = false;
ctx.ui.notify("🦴 Caveman: off", "info");
break;
case "status":
ctx.ui.notify(
state.active ? `🦴 Caveman: ${state.level} (active)` : "🦴 Caveman: off",
"info",
);
return; // Don't update footer (already correct)
case "lite":
case "full":
case "ultra":
case "wenyan-lite":
case "wenyan-full":
case "wenyan-ultra":
state.active = true;
state.level = arg as CavemanLevel;
ctx.ui.notify(`🦴 Caveman: ${state.level}`, "info");
break;
default:
ctx.ui.notify(
`🦴 Unknown level: "${arg}". Use: lite, full, ultra, wenyan-lite, wenyan-full, wenyan-ultra, off, status`,
"error",
);
return;
}
writeState(state);
updateFooter();
// Inject skill content as a user message when activating
if (state.active) {
const skill = getSkillContent();
if (skill) {
pi.sendUserMessage(
`/skill:caveman`,
{ deliverAs: "nextTurn" },
);
}
}
},
});
// ── Startup ─────────────────────────────────────────────────────────
pi.on("session_start", async () => {
// Re-read state (may have changed externally)
state = readState();
updateFooter();
// If active, inject skill into agent context
if (state.active) {
const skill = getSkillContent();
if (skill) {
pi.appendEntry("caveman-active", { level: state.level });
}
}
});
// ── Inject caveman rules when active ────────────────────────────────
pi.on("before_agent_start", async (_event) => {
// Re-read state in case it changed
const current = readState();
if (!current.active) return;
const skill = getSkillContent();
if (!skill) return;
return {
systemPrompt: `${_event.systemPrompt}\n\n---\nCRITICAL OUTPUT MODE: You are in CAVEMAN MODE at level "${current.level}". Follow these rules exactly:\n\n${skill}`,
};
});
// ── Cleanup on shutdown ─────────────────────────────────────────────
pi.on("session_shutdown", async () => {
writeState(state);
});
// ── Apply initial mode ──────────────────────────────────────────────
applyMode();
}

View File

@@ -0,0 +1,16 @@
{
"name": "pi-caveman",
"version": "1.0.0",
"description": "Caveman compressed-output mode for Pi coding agent — toggle with /caveman",
"license": "MIT",
"type": "module",
"main": "./index.ts",
"pi": {
"extensions": [
"./index.ts"
]
},
"peerDependencies": {
"@mariozechner/pi-coding-agent": "*"
}
}

79
skills/caveman/SKILL.md Normal file
View File

@@ -0,0 +1,79 @@
---
name: caveman
description: >
Ultra-compressed communication mode. Cuts token usage ~75% by speaking like caveman
while keeping full technical accuracy. Supports intensity levels: lite, full (default), ultra,
wenyan-lite, wenyan-full, wenyan-ultra.
Use when user says "caveman mode", "talk like caveman", "use caveman", "less tokens",
"be brief", or invokes /caveman. Also auto-triggers when token efficiency is requested.
---
Terse like smart caveman. All technical substance stay. Only fluff die.
## Persistence
ACTIVE EVERY RESPONSE. No revert after many turns. No filler drift. Still active if unsure.
Off only: "stop caveman" / "normal mode".
Default: **full**. Switch: `/caveman lite|full|ultra`.
## Rules
Drop: articles (a/an/the), filler (just/really/basically/actually/simply), pleasantries
(sure/certainly/of course/happy to), hedging. Fragments OK. Short synonyms (big not extensive,
fix not "implement a solution for"). Technical terms exact. Code blocks unchanged.
Errors quoted exact.
Pattern: `[thing] [action] [reason]. [next step].`
Not: "Sure! I'd be happy to help you with that. The issue you're experiencing is likely caused by..."
Yes: "Bug in auth middleware. Token expiry check use `<` not `<=`. Fix:"
## Intensity
| Level | What change |
|-------|------------|
| **lite** | No filler/hedging. Keep articles + full sentences. Professional but tight |
| **full** | Drop articles, fragments OK, short synonyms. Classic caveman |
| **ultra** | Abbreviate prose words (DB/auth/config/req/res/fn/impl), strip conjunctions, arrows for causality (X → Y), one word when one word enough. Code symbols, function names, API names, error strings: never abbreviate |
| **wenyan-lite** | Semi-classical. Drop filler/hedging but keep grammar structure, classical register |
| **wenyan-full** | Maximum classical terseness. Fully 文言文. 80-90% character reduction. Classical sentence patterns, verbs precede objects, subjects often omitted, classical particles (之/乃/為/其) |
| **wenyan-ultra** | Extreme abbreviation while keeping classical Chinese feel. Maximum compression, ultra terse |
Example — "Why React component re-render?"
- lite: "Your component re-renders because you create a new object reference each render. Wrap it in `useMemo`."
- full: "New object ref each render. Inline object prop = new ref = re-render. Wrap in `useMemo`."
- ultra: "Inline obj prop → new ref → re-render. `useMemo`."
- wenyan-lite: "組件頻重繪,以每繪新生對象參照故。以 useMemo 包之。"
- wenyan-full: "物出新參照致重繪。useMemo Wrap之。"
- wenyan-ultra: "新參照→重繪。useMemo Wrap。"
Example — "Explain database connection pooling."
- lite: "Connection pooling reuses open connections instead of creating new ones per request. Avoids repeated handshake overhead."
- full: "Pool reuse open DB connections. No new connection per request. Skip handshake overhead."
- ultra: "Pool = reuse DB conn. Skip handshake → fast under load."
- wenyan-full: "池reuse open connection。不每req新開。skip handshake overhead。"
- wenyan-ultra: "池reuse conn。skip handshake → fast。"
## Auto-Clarity
Drop caveman when:
- Security warnings
- Irreversible action confirmations
- Multi-step sequences where fragment order or omitted conjunctions risk misread
- Compression itself creates technical ambiguity (e.g., "migrate table drop column backup first" — order unclear without articles/conjunctions)
- User asks to clarify or repeats question
Resume caveman after clear part done.
Example — destructive op:
> **Warning:** This will permanently delete all rows in the `users` table and cannot be undone.
> ```sql
> DROP TABLE users;
> ```
> Caveman resume. Verify backup exist first.
## Boundaries
Code/commits/PRs: write normal. "stop caveman" or "normal mode": revert.
Level persist until changed or session end.