@jgiox/goodvibes 1.0.0

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/dist/index.js ADDED
@@ -0,0 +1,295 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/index.ts
4
+ import { Command } from "commander";
5
+
6
+ // src/commands/init.ts
7
+ import { intro, outro, note, tasks } from "@clack/prompts";
8
+
9
+ // src/steps/copy-templates.ts
10
+ import { copy } from "fs-extra";
11
+ import { readFile as readFile2 } from "fs/promises";
12
+ import { readdir } from "fs/promises";
13
+ import { existsSync } from "fs";
14
+ import { join, relative } from "path";
15
+ import { fileURLToPath } from "url";
16
+
17
+ // src/utils/sentinel-merge.ts
18
+ import { outputFile, pathExists } from "fs-extra";
19
+ import { readFile, writeFile } from "fs/promises";
20
+ var SENTINEL_START = "<!-- goodvibes:start -->";
21
+ var SENTINEL_END = "<!-- goodvibes:end -->";
22
+ function extractVersion(block) {
23
+ const match = block.match(/# goodvibes: v([\d.]+)/);
24
+ return match ? match[1] : null;
25
+ }
26
+ function versionGte(a, b) {
27
+ const pa = a.split(".").map((s) => parseInt(s, 10));
28
+ const pb = b.split(".").map((s) => parseInt(s, 10));
29
+ for (let i = 0; i < Math.max(pa.length, pb.length); i++) {
30
+ const va = pa[i] ?? 0;
31
+ const vb = pb[i] ?? 0;
32
+ if (va > vb) return true;
33
+ if (va < vb) return false;
34
+ }
35
+ return true;
36
+ }
37
+ function extractSentinelBlock(content) {
38
+ const start = content.indexOf(SENTINEL_START);
39
+ const end = content.indexOf(SENTINEL_END);
40
+ if (start === -1 || end === -1) return "";
41
+ return content.slice(start, end + SENTINEL_END.length);
42
+ }
43
+ async function mergeClaude(destPath, templateContent) {
44
+ const templateBlock = extractSentinelBlock(templateContent);
45
+ if (!await pathExists(destPath)) {
46
+ await outputFile(destPath, templateContent);
47
+ return;
48
+ }
49
+ const existing = await readFile(destPath, "utf-8");
50
+ const startIdx = existing.indexOf(SENTINEL_START);
51
+ if (startIdx === -1) {
52
+ await writeFile(destPath, existing.trimEnd() + "\n\n" + templateBlock + "\n");
53
+ return;
54
+ }
55
+ const endIdx = existing.indexOf(SENTINEL_END);
56
+ const existingBlock = existing.slice(startIdx, endIdx + SENTINEL_END.length);
57
+ const existingVersion = extractVersion(existingBlock);
58
+ const templateVersion = extractVersion(templateBlock);
59
+ if (existingVersion && templateVersion && versionGte(existingVersion, templateVersion)) {
60
+ return;
61
+ }
62
+ const before = existing.slice(0, startIdx);
63
+ const after = existing.slice(endIdx + SENTINEL_END.length);
64
+ await writeFile(destPath, before + templateBlock + after);
65
+ }
66
+
67
+ // src/steps/copy-templates.ts
68
+ function resolveTemplatesDir() {
69
+ const distRelative = fileURLToPath(new URL("../templates", import.meta.url));
70
+ if (existsSync(distRelative)) return distRelative;
71
+ return fileURLToPath(new URL("../../../../templates", import.meta.url));
72
+ }
73
+ async function listTemplateFiles(templateDir) {
74
+ const results = [];
75
+ async function walk(dir) {
76
+ const entries = await readdir(dir, { withFileTypes: true });
77
+ for (const entry of entries) {
78
+ const fullPath = join(dir, entry.name);
79
+ if (entry.isDirectory()) {
80
+ await walk(fullPath);
81
+ } else {
82
+ results.push(relative(templateDir, fullPath));
83
+ }
84
+ }
85
+ }
86
+ await walk(templateDir);
87
+ return results.sort();
88
+ }
89
+ async function copyTemplates(templateDir, destDir, dryRun, minimal) {
90
+ if (dryRun) {
91
+ return listTemplateFiles(templateDir);
92
+ }
93
+ await copy(templateDir, destDir, {
94
+ overwrite: false,
95
+ errorOnExist: false,
96
+ filter: (src) => {
97
+ if (src.endsWith("CLAUDE.md")) return false;
98
+ if (minimal && src.includes(".github/workflows")) return false;
99
+ if (relative(templateDir, src).includes("..")) return false;
100
+ return true;
101
+ }
102
+ });
103
+ const claudeSrc = join(templateDir, "CLAUDE.md");
104
+ const claudeDest = join(destDir, "CLAUDE.md");
105
+ const templateContent = await readFile2(claudeSrc, "utf-8");
106
+ await mergeClaude(claudeDest, templateContent);
107
+ return listTemplateFiles(templateDir);
108
+ }
109
+
110
+ // src/steps/install-headroom.ts
111
+ import { execa as execa2 } from "execa";
112
+
113
+ // src/utils/detect-python.ts
114
+ import { execa } from "execa";
115
+ async function detectPython() {
116
+ for (const cmd of ["python3", "python", "py"]) {
117
+ try {
118
+ const { stdout, stderr } = await execa(cmd, ["--version"]);
119
+ const output = stdout || stderr;
120
+ const match = output.match(/Python (\d+)\.(\d+)/);
121
+ if (!match) {
122
+ continue;
123
+ }
124
+ const major2 = parseInt(match[1], 10);
125
+ const minor = parseInt(match[2], 10);
126
+ if (major2 > 3) {
127
+ return cmd;
128
+ }
129
+ if (major2 === 3 && minor >= 10) {
130
+ return cmd;
131
+ }
132
+ } catch (e) {
133
+ if (e.code === "ENOENT") {
134
+ continue;
135
+ }
136
+ continue;
137
+ }
138
+ }
139
+ return null;
140
+ }
141
+
142
+ // src/steps/install-headroom.ts
143
+ async function installHeadroom(log) {
144
+ const pythonCmd = await detectPython();
145
+ if (pythonCmd === null) {
146
+ log("Python 3.10+ not found \u2014 skipping headroom install. Install Python 3.10+ and run `goodvibes init` again.");
147
+ return;
148
+ }
149
+ log("Note: headroom will download its compression model on first use \u2014 this may take 1\u20133 minutes on a slow connection.");
150
+ const installers = [
151
+ { cmd: "uv", args: ["tool", "install", "headroom-ai[all]"] },
152
+ { cmd: "pipx", args: ["install", "headroom-ai[all]"] },
153
+ { cmd: pythonCmd, args: ["-m", "pip", "install", "--user", "headroom-ai[all]"] }
154
+ ];
155
+ for (const installer of installers) {
156
+ try {
157
+ await execa2(installer.cmd, installer.args);
158
+ return;
159
+ } catch (e) {
160
+ if (e.code === "ENOENT") {
161
+ continue;
162
+ }
163
+ const summary = e.message?.split("\n")[0] ?? "unknown error";
164
+ log(`headroom install failed: ${summary}`);
165
+ log('You can install headroom manually later: uv tool install "headroom-ai[all]"');
166
+ return;
167
+ }
168
+ }
169
+ log("No package installer found (uv, pipx, pip). Install Python 3.10+ with uv or pipx and run `goodvibes init` again.");
170
+ }
171
+
172
+ // src/steps/configure-mcp.ts
173
+ import { execa as execa3 } from "execa";
174
+ async function configureMcp(log) {
175
+ try {
176
+ await execa3("headroom", ["mcp", "status"]);
177
+ log("headroom MCP already configured \u2014 skipping");
178
+ return;
179
+ } catch {
180
+ }
181
+ try {
182
+ const list = await execa3("claude", ["mcp", "list"]);
183
+ if (list.stdout.includes("headroom")) {
184
+ log("headroom already registered in claude MCP \u2014 skipping");
185
+ return;
186
+ }
187
+ const whichCmd = process.platform === "win32" ? "where" : "which";
188
+ let absolutePath;
189
+ try {
190
+ const pathResult = await execa3(whichCmd, ["headroom"]);
191
+ absolutePath = pathResult.stdout.trim();
192
+ } catch {
193
+ log('headroom binary not found on PATH \u2014 MCP registration skipped. Run `uv tool install "headroom-ai[all]"` then re-run `goodvibes init`.');
194
+ return;
195
+ }
196
+ await execa3("claude", ["mcp", "add", "-s", "user", "headroom", absolutePath]);
197
+ log("headroom registered as global MCP server");
198
+ return;
199
+ } catch (e) {
200
+ if (e.code === "ENOENT") {
201
+ log("claude CLI not found \u2014 falling back to headroom mcp install");
202
+ log("Warning: if you use CLAUDE_CONFIG_DIR, you may need to run `headroom mcp install` manually");
203
+ try {
204
+ await execa3("headroom", ["mcp", "install"]);
205
+ return;
206
+ } catch (fallbackErr) {
207
+ if (fallbackErr.code === "ENOENT") {
208
+ log("headroom binary not found \u2014 MCP registration skipped. Install headroom and run `headroom mcp install` manually.");
209
+ return;
210
+ }
211
+ throw fallbackErr;
212
+ }
213
+ }
214
+ throw e;
215
+ }
216
+ }
217
+
218
+ // src/commands/init.ts
219
+ function registerInitCommand(program2) {
220
+ program2.command("init").description("Bootstrap a project with goodvibes configuration").option("--dry-run", "Preview files without writing to disk").option("--minimal", "Skip headroom install and CI workflows").action(async (options) => {
221
+ const dryRun = options.dryRun ?? false;
222
+ const minimal = options.minimal ?? false;
223
+ const cwd = process.cwd();
224
+ const templateDir = resolveTemplatesDir();
225
+ intro("goodvibes init");
226
+ if (dryRun) {
227
+ const files = await listTemplateFiles(templateDir);
228
+ note(files.map((f) => ` Would write: ${f}`).join("\n"), "Dry run \u2014 no files written");
229
+ note(
230
+ [
231
+ "1. Open this project in Claude Code",
232
+ "2. Run /plugin marketplace add DietrichGebert/ponytail",
233
+ "3. Start coding \u2014 CLAUDE.md rules are already active"
234
+ ].join("\n"),
235
+ "Next steps"
236
+ );
237
+ outro("Run without --dry-run to apply these changes.");
238
+ return;
239
+ }
240
+ const createdFiles = [];
241
+ const taskList = [
242
+ {
243
+ title: "Copying template files",
244
+ task: async (message) => {
245
+ const files = await copyTemplates(templateDir, cwd, false, minimal);
246
+ createdFiles.push(...files);
247
+ return `Copied ${files.length} files`;
248
+ }
249
+ }
250
+ ];
251
+ if (!minimal) {
252
+ taskList.push(
253
+ {
254
+ title: "Installing headroom",
255
+ task: async (message) => {
256
+ await installHeadroom((msg) => message(msg));
257
+ return "headroom ready";
258
+ }
259
+ },
260
+ {
261
+ title: "Configuring headroom MCP",
262
+ task: async (message) => {
263
+ await configureMcp((msg) => message(msg));
264
+ return "MCP server registered";
265
+ }
266
+ }
267
+ );
268
+ }
269
+ await tasks(taskList);
270
+ note(createdFiles.join("\n"), "Files created");
271
+ const nextSteps = [
272
+ "1. Open this project in Claude Code",
273
+ "2. Run /plugin marketplace add DietrichGebert/ponytail",
274
+ "3. Start coding \u2014 CLAUDE.md rules are already active"
275
+ ].join("\n");
276
+ note(nextSteps, "Next steps");
277
+ outro("You're all set!");
278
+ });
279
+ }
280
+
281
+ // src/index.ts
282
+ var [major] = process.version.replace("v", "").split(".").map(Number);
283
+ if (major < 20) {
284
+ process.stderr.write(
285
+ `goodvibes requires Node.js 20 or higher.
286
+ You are running Node.js ${process.version}.
287
+ Install the latest LTS from https://nodejs.org
288
+ `
289
+ );
290
+ process.exit(1);
291
+ }
292
+ var program = new Command();
293
+ program.name("goodvibes").version("1.0.0").description("One-command bootstrap for vibe coding projects");
294
+ registerInitCommand(program);
295
+ await program.parseAsync(process.argv);
package/package.json ADDED
@@ -0,0 +1,51 @@
1
+ {
2
+ "name": "@jgiox/goodvibes",
3
+ "version": "1.0.0",
4
+ "description": "One-command bootstrap for vibe coding projects",
5
+ "type": "module",
6
+ "bin": {
7
+ "goodvibes": "./dist/index.js"
8
+ },
9
+ "main": "./dist/index.js",
10
+ "engines": {
11
+ "node": ">=20.0.0"
12
+ },
13
+ "files": [
14
+ "dist",
15
+ "templates"
16
+ ],
17
+ "repository": {
18
+ "type": "git",
19
+ "url": "https://github.com/jgiox/goodvibes"
20
+ },
21
+ "keywords": [
22
+ "scaffold",
23
+ "vibe-coding",
24
+ "claude",
25
+ "llm",
26
+ "starter-kit",
27
+ "cli"
28
+ ],
29
+ "publishConfig": {
30
+ "access": "public"
31
+ },
32
+ "license": "Apache-2.0",
33
+ "scripts": {
34
+ "prebuild": "node -e \"const{cpSync}=require('fs');cpSync('../../templates','./templates',{recursive:true,force:true})\"",
35
+ "build": "tsup",
36
+ "test": "vitest run",
37
+ "test:watch": "vitest"
38
+ },
39
+ "dependencies": {
40
+ "commander": "^13",
41
+ "@clack/prompts": "^0.9",
42
+ "fs-extra": "^11",
43
+ "execa": "^9"
44
+ },
45
+ "devDependencies": {
46
+ "tsup": "^8",
47
+ "typescript": "^5.5",
48
+ "@types/node": "^20",
49
+ "vitest": "^2"
50
+ }
51
+ }
@@ -0,0 +1,41 @@
1
+ # cavecrew
2
+
3
+ Decision guide. When to delegate to caveman subagents instead of doing the work inline.
4
+
5
+ ## What it does
6
+
7
+ Tells the main thread when to spawn a caveman-style subagent versus the vanilla equivalent. The win: subagent tool-results inject back into main context verbatim, and caveman output is roughly 1/3 the size of vanilla prose. Across 20 delegations in one session, that is the difference between context exhaustion and finishing the task.
8
+
9
+ Three subagents:
10
+
11
+ | Subagent | Job | Use when |
12
+ |----------|-----|----------|
13
+ | `cavecrew-investigator` | Locate code (read-only) | "Where is X defined / what calls Y / list uses of Z" |
14
+ | `cavecrew-builder` | Surgical edit, 1-2 files | Scope is obvious, ≤2 files. Refuses 3+ file scope. |
15
+ | `cavecrew-reviewer` | Diff/file review | One-line findings with severity emoji |
16
+
17
+ Use vanilla `Explore` or `Code Reviewer` when you want prose, architecture commentary, or rationale. Use main thread directly for one-line answers and 3+ file refactors.
18
+
19
+ This skill is a decision guide, not a slash command. It activates when the conversation mentions delegation.
20
+
21
+ ## How to invoke
22
+
23
+ Triggers on phrases like "delegate to subagent", "use cavecrew", "spawn investigator", "save context", "compressed agent output".
24
+
25
+ ## Example chaining
26
+
27
+ Locate → fix → verify (most common):
28
+
29
+ 1. `cavecrew-investigator` returns site list (`path:line — symbol — note`)
30
+ 2. Main thread picks 1-2 sites, hands paths to `cavecrew-builder`
31
+ 3. `cavecrew-reviewer` audits the resulting diff
32
+
33
+ Parallel scout: spawn 2-3 `cavecrew-investigator` calls in one message with different angles (defs, callers, tests). Aggregate in main.
34
+
35
+ ## See also
36
+
37
+ - [`SKILL.md`](./SKILL.md) — full decision matrix and output contracts
38
+ - [`agents/cavecrew-investigator.md`](../../agents/cavecrew-investigator.md)
39
+ - [`agents/cavecrew-builder.md`](../../agents/cavecrew-builder.md)
40
+ - [`agents/cavecrew-reviewer.md`](../../agents/cavecrew-reviewer.md)
41
+ - [Caveman README](../../README.md) — repo overview
@@ -0,0 +1,82 @@
1
+ ---
2
+ name: cavecrew
3
+ description: >
4
+ Decision guide for delegating to caveman-style subagents. Tells the main
5
+ thread WHEN to spawn `cavecrew-investigator` (locate code), `cavecrew-builder`
6
+ (1-2 file edit), or `cavecrew-reviewer` (diff review) instead of doing the
7
+ work inline or using vanilla `Explore`. Subagent output is caveman-compressed
8
+ so the tool-result injected back into main context is ~60% smaller — main
9
+ context lasts longer across long sessions.
10
+ Trigger: "delegate to subagent", "use cavecrew", "spawn investigator/builder/reviewer",
11
+ "save context", "compressed agent output".
12
+ ---
13
+
14
+ Cavecrew = three subagent presets that emit caveman output. Same job as Anthropic defaults (`Explore`, edit-style agents, reviewer); difference is the tool-result they return is compressed, so main context shrinks per delegation.
15
+
16
+ ## When to use cavecrew vs alternatives
17
+
18
+ | Task | Use |
19
+ |---|---|
20
+ | "Where is X defined / what calls Y / list uses of Z" | `cavecrew-investigator` |
21
+ | Same but you also want suggestions/architecture commentary | `Explore` (vanilla) |
22
+ | Surgical edit, ≤2 files, scope obvious | `cavecrew-builder` |
23
+ | New feature / 3+ files / cross-cutting refactor | Main thread or `feature-dev:code-architect` |
24
+ | Review diff, branch, or file for bugs | `cavecrew-reviewer` |
25
+ | Deep code review with rationale + alternatives | `Code Reviewer` (vanilla) |
26
+ | One-line answer you already know | Main thread, no subagent |
27
+
28
+ Rule of thumb: **if you'd want the subagent's output in 1/3 the tokens, pick cavecrew. If you'd want prose, pick vanilla.**
29
+
30
+ ## Why this exists (the real win)
31
+
32
+ Subagent tool results get injected into main context verbatim. A vanilla `Explore` that returns 2k tokens of prose costs 2k tokens of main-context budget every time. The same finding from `cavecrew-investigator` returns ~700 tokens. Across 20 delegations in one session that's the difference between context exhaustion and finishing the task.
33
+
34
+ ## Output contracts
35
+
36
+ What main thread can rely on per agent:
37
+
38
+ **`cavecrew-investigator`**
39
+ ```
40
+ <Header>:
41
+ - path:line — `symbol` — short note
42
+ totals: <counts>.
43
+ ```
44
+ Or `No match.` Always file-path-first, line-number-attached, backticked symbols. Safe to grep with `path:\d+`.
45
+
46
+ **`cavecrew-builder`**
47
+ ```
48
+ <path:line-range> — <change ≤10 words>.
49
+ verified: <re-read OK | mismatch @ path:line>.
50
+ ```
51
+ Or one of: `too-big.` / `needs-confirm.` / `ambiguous.` / `regressed.` (terminal first token).
52
+
53
+ **`cavecrew-reviewer`**
54
+ ```
55
+ path:line: <emoji> <severity>: <problem>. <fix>.
56
+ totals: N🔴 N🟡 N🔵 N❓
57
+ ```
58
+ Or `No issues.` Findings sorted file → line ascending.
59
+
60
+ ## Chaining patterns
61
+
62
+ **Locate → fix → verify** (most common):
63
+ 1. `cavecrew-investigator` returns site list.
64
+ 2. Main thread picks 1-2 sites, hands paths to `cavecrew-builder`.
65
+ 3. `cavecrew-reviewer` audits the diff.
66
+
67
+ **Parallel scout** (when investigation is broad):
68
+ Spawn 2-3 `cavecrew-investigator` calls in one message (different angles: defs vs callers vs tests). Aggregate in main thread.
69
+
70
+ **Single-shot edit** (when site is already known):
71
+ Skip investigator. Hand exact path:line to `cavecrew-builder` directly.
72
+
73
+ ## What NOT to do
74
+
75
+ - Don't use `cavecrew-builder` when you don't already know the file. Spawn investigator first or main thread will eat tokens passing context.
76
+ - Don't chain `cavecrew-investigator → cavecrew-builder` for a 5-file refactor. Builder will return `too-big.` and you'll have wasted a turn.
77
+ - Don't ask `cavecrew-reviewer` for "general feedback" — it returns findings only, no architecture opinions. Use `Code Reviewer` for that.
78
+ - Don't expect prose. Cavecrew output is structured, sometimes terse to the point of cryptic. If a human will read it directly, paraphrase.
79
+
80
+ ## Auto-clarity (inherited)
81
+
82
+ Subagents drop caveman → normal English for security warnings, irreversible-action confirmations, and any output where fragment ambiguity could be misread. Resume caveman after.
@@ -0,0 +1,48 @@
1
+ # caveman
2
+
3
+ Talk like smart caveman. Same brain, fewer tokens.
4
+
5
+ ## What it does
6
+
7
+ Compress every model response to caveman-style prose. Drops articles, filler, pleasantries, and hedging. Keeps every technical detail, code block, error string, and symbol exact. Cuts ~65-75% of output tokens with full accuracy preserved. Mode persists for the whole session until changed or stopped.
8
+
9
+ Six intensity levels:
10
+
11
+ | Level | What change |
12
+ |-------|-------------|
13
+ | `lite` | Drop filler/hedging. Sentences stay full. Professional but tight. |
14
+ | `full` | Default. Drop articles, fragments OK, short synonyms. |
15
+ | `ultra` | Bare fragments. Abbreviations (DB, auth, fn). Arrows for causality. |
16
+ | `wenyan-lite` | Classical Chinese register, light compression. |
17
+ | `wenyan-full` | Maximum 文言文. 80-90% character reduction. |
18
+ | `wenyan-ultra` | Extreme classical compression. |
19
+
20
+ Auto-clarity rule: caveman drops to normal prose for security warnings, irreversible-action confirmations, multi-step sequences where fragment ambiguity risks misread, and when user repeats a question. Resumes after the clear part.
21
+
22
+ ## How to invoke
23
+
24
+ ```
25
+ /caveman # full mode (default)
26
+ /caveman lite # lighter compression
27
+ /caveman ultra # extreme compression
28
+ /caveman wenyan # classical Chinese
29
+ stop caveman # back to normal prose
30
+ ```
31
+
32
+ ## Example output
33
+
34
+ Question: "Why does my React component re-render?"
35
+
36
+ Normal prose:
37
+ > Your component re-renders because you create a new object reference each render. Wrapping it in `useMemo` will fix the issue.
38
+
39
+ Caveman (full):
40
+ > New object ref each render. Inline object prop = new ref = re-render. Wrap in `useMemo`.
41
+
42
+ Caveman (ultra):
43
+ > Inline obj prop → new ref → re-render. `useMemo`.
44
+
45
+ ## See also
46
+
47
+ - [`SKILL.md`](./SKILL.md) — full LLM-facing instructions
48
+ - [Caveman README](../../README.md) — repo overview, install, benchmarks
@@ -0,0 +1,78 @@
1
+ ---
2
+ name: caveman
3
+ description: >
4
+ Ultra-compressed communication mode. Cuts token usage ~75% by speaking like caveman
5
+ while keeping full technical accuracy. Supports intensity levels: lite, full (default), ultra,
6
+ wenyan-lite, wenyan-full, wenyan-ultra.
7
+ Use when user says "caveman mode", "talk like caveman", "use caveman", "less tokens",
8
+ "be brief", or invokes /caveman. Also auto-triggers when token efficiency is requested.
9
+ ---
10
+
11
+ Respond terse like smart caveman. All technical substance stay. Only fluff die.
12
+
13
+ ## Persistence
14
+
15
+ ACTIVE EVERY RESPONSE. No revert after many turns. No filler drift. Still active if unsure. Off only: "stop caveman" / "normal mode".
16
+
17
+ Default: **full**. Switch: `/caveman lite|full|ultra`.
18
+
19
+ ## Rules
20
+
21
+ 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"). No tool-call narration, no decorative tables/emoji, no dumping long raw error logs unless asked — quote shortest decisive line. Standard well-known tech acronyms OK (DB/API/HTTP); never invent new abbreviations reader can't decode. Technical terms exact. Code blocks unchanged. Errors quoted exact.
22
+
23
+ Preserve user's dominant language. User write Portuguese → reply Portuguese caveman. User write Spanish → reply Spanish caveman. Compress the style, not the language. No forced English openings or status phrases. ALWAYS keep technical terms, code, API names, CLI commands, commit-type keywords (feat/fix/...), and exact error strings verbatim — unless user explicitly ask for translation.
24
+
25
+ No self-reference. Never name or announce the style. No "caveman mode on", "me caveman think", no third-person caveman tags. Output caveman-only — never normal answer plus "Caveman:" recap. Exception: user explicitly ask what the mode is.
26
+
27
+ Pattern: `[thing] [action] [reason]. [next step].`
28
+
29
+ Not: "Sure! I'd be happy to help you with that. The issue you're experiencing is likely caused by..."
30
+ Yes: "Bug in auth middleware. Token expiry check use `<` not `<=`. Fix:"
31
+
32
+ ## Intensity
33
+
34
+ | Level | What change |
35
+ |-------|------------|
36
+ | **lite** | No filler/hedging. Keep articles + full sentences. Professional but tight |
37
+ | **full** | Drop articles, fragments OK, short synonyms. Classic caveman. No tool-call narration, no decorative tables/emoji, no long raw error-log dumps unless asked. Standard acronyms OK; no invented abbreviations |
38
+ | **ultra** | Abbreviate prose words (DB/auth/config/req/res/fn/impl) — prose words only, never real code symbols/function names. Strip conjunctions, arrows for causality (X → Y), one word when one word enough. Code symbols, function names, API names, error strings: never abbreviate |
39
+ | **wenyan-lite** | Semi-classical. Drop filler/hedging but keep grammar structure, classical register |
40
+ | **wenyan-full** | Maximum classical terseness. Fully 文言文. 80-90% character reduction. Classical sentence patterns, verbs precede objects, subjects often omitted, classical particles (之/乃/為/其) |
41
+ | **wenyan-ultra** | Extreme abbreviation while keeping classical Chinese feel. Maximum compression, ultra terse |
42
+
43
+ Example — "Why React component re-render?"
44
+ - lite: "Your component re-renders because you create a new object reference each render. Wrap it in `useMemo`."
45
+ - full: "New object ref each render. Inline object prop = new ref = re-render. Wrap in `useMemo`."
46
+ - ultra: "Inline obj prop → new ref → re-render. `useMemo`."
47
+ - wenyan-lite: "組件頻重繪,以每繪新生對象參照故。以 useMemo 包之。"
48
+ - wenyan-full: "每繪新生對象參照,故重繪;以 useMemo 包之則免。"
49
+ - wenyan-ultra: "新參照→重繪。useMemo Wrap。"
50
+
51
+ Example — "Explain database connection pooling."
52
+ - lite: "Connection pooling reuses open connections instead of creating new ones per request. Avoids repeated handshake overhead."
53
+ - full: "Pool reuse open DB connections. No new connection per request. Skip handshake overhead."
54
+ - ultra: "Pool = reuse DB conn. Skip handshake → fast under load."
55
+ - wenyan-full: "池reuse open connection。不每req新開。skip handshake overhead。"
56
+ - wenyan-ultra: "池reuse conn。skip handshake → fast。"
57
+
58
+ ## Auto-Clarity
59
+
60
+ Drop caveman when:
61
+ - Security warnings
62
+ - Irreversible action confirmations
63
+ - Multi-step sequences where fragment order or omitted conjunctions risk misread
64
+ - Compression itself creates technical ambiguity (e.g., `"migrate table drop column backup first"` — order unclear without articles/conjunctions)
65
+ - User asks to clarify or repeats question
66
+
67
+ Resume caveman after clear part done.
68
+
69
+ Example — destructive op:
70
+ > **Warning:** This will permanently delete all rows in the `users` table and cannot be undone.
71
+ > ```sql
72
+ > DROP TABLE users;
73
+ > ```
74
+ > Caveman resume. Verify backup exist first.
75
+
76
+ ## Boundaries
77
+
78
+ Code/commits/PRs: write normal. "stop caveman" or "normal mode": revert. Level persist until changed or session end.
@@ -0,0 +1,65 @@
1
+ ---
2
+ name: caveman-commit
3
+ description: >
4
+ Ultra-compressed commit message generator. Cuts noise from commit messages while preserving
5
+ intent and reasoning. Conventional Commits format. Subject ≤50 chars, body only when "why"
6
+ isn't obvious. Use when user says "write a commit", "commit message", "generate commit",
7
+ "/commit", or invokes /caveman-commit. Auto-triggers when staging changes.
8
+ ---
9
+
10
+ Write commit messages terse and exact. Conventional Commits format. No fluff. Why over what.
11
+
12
+ ## Rules
13
+
14
+ **Subject line:**
15
+ - `<type>(<scope>): <imperative summary>` — `<scope>` optional
16
+ - Types: `feat`, `fix`, `refactor`, `perf`, `docs`, `test`, `chore`, `build`, `ci`, `style`, `revert`
17
+ - Imperative mood: "add", "fix", "remove" — not "added", "adds", "adding"
18
+ - ≤50 chars when possible, hard cap 72
19
+ - No trailing period
20
+ - Match project convention for capitalization after the colon
21
+
22
+ **Body (only if needed):**
23
+ - Skip entirely when subject is self-explanatory
24
+ - Add body only for: non-obvious *why*, breaking changes, migration notes, linked issues
25
+ - Wrap at 72 chars
26
+ - Bullets `-` not `*`
27
+ - Reference issues/PRs at end: `Closes #42`, `Refs #17`
28
+
29
+ **What NEVER goes in:**
30
+ - "This commit does X", "I", "we", "now", "currently" — the diff says what
31
+ - "As requested by..." — use Co-authored-by trailer
32
+ - "Generated with Claude Code" or any AI attribution — unless the user's own rule requires an `Assisted-by`/AI-attribution trailer, then add it as a trailer
33
+ - Emoji (unless project convention requires)
34
+ - Restating the file name when scope already says it
35
+
36
+ ## Examples
37
+
38
+ Diff: new endpoint for user profile with body explaining the why
39
+ - ❌ "feat: add a new endpoint to get user profile information from the database"
40
+ - ✅
41
+ ```
42
+ feat(api): add GET /users/:id/profile
43
+
44
+ Mobile client needs profile data without the full user payload
45
+ to reduce LTE bandwidth on cold-launch screens.
46
+
47
+ Closes #128
48
+ ```
49
+
50
+ Diff: breaking API change
51
+ - ✅
52
+ ```
53
+ feat(api)!: rename /v1/orders to /v1/checkout
54
+
55
+ BREAKING CHANGE: clients on /v1/orders must migrate to /v1/checkout
56
+ before 2026-06-01. Old route returns 410 after that date.
57
+ ```
58
+
59
+ ## Auto-Clarity
60
+
61
+ Always include body for: breaking changes, security fixes, data migrations, anything reverting a prior commit. Never compress these into subject-only — future debuggers need the context.
62
+
63
+ ## Boundaries
64
+
65
+ Only generates the commit message. Does not run `git commit`, does not stage files, does not amend. Output the message as a code block ready to paste. "stop caveman-commit" or "normal mode": revert to verbose commit style.