lakonai 0.6.1 → 0.7.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/README.md +76 -72
- package/assets/logo.svg +2 -2
- package/bin/{lakon.js → lakonai.js} +22 -21
- package/package.json +5 -4
- package/src/hooks/bash-rewrite.js +2 -2
- package/src/hooks/grep-guard.js +1 -1
- package/src/hooks/read-guard.js +2 -2
- package/src/hooks/version-check.js +2 -2
- package/src/install/claude-commands.js +23 -12
- package/src/install/index.js +14 -14
- package/src/install/platforms.js +22 -11
- package/src/rules/{caveman.md → lakonai.md} +17 -17
- package/src/tracking.js +2 -2
package/README.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
<p align="center">
|
|
2
|
-
<img src="./assets/logo.svg" width="140" alt="
|
|
2
|
+
<img src="./assets/logo.svg" width="140" alt="lakonai" />
|
|
3
3
|
</p>
|
|
4
4
|
|
|
5
|
-
<h1 align="center">
|
|
5
|
+
<h1 align="center">lakonai</h1>
|
|
6
6
|
|
|
7
7
|
<p align="center">
|
|
8
8
|
<strong>Cut LLM tokens by up to 94% — without losing a single identifier.</strong>
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
</p>
|
|
14
14
|
|
|
15
15
|
<p align="center">
|
|
16
|
-
<a href="https://www.npmjs.com/package/
|
|
16
|
+
<a href="https://www.npmjs.com/package/lakonai"><img src="https://img.shields.io/npm/v/lakonai?color=0F0F0F&label=npm" alt="npm" /></a>
|
|
17
17
|
<a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-0F0F0F" alt="MIT" /></a>
|
|
18
18
|
<img src="https://img.shields.io/badge/node-%E2%89%A518-0F0F0F" alt="node ≥18" />
|
|
19
19
|
<img src="https://img.shields.io/badge/deps-0-0F0F0F" alt="zero dependencies" />
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
| `Read pnpm-lock.yaml` | ~56,000 | **blocked** | **-95%** |
|
|
41
41
|
| `Grep` (auto `head_limit`) | unbounded | 30 matches | **capped** |
|
|
42
42
|
|
|
43
|
-
Conservative numbers — peaks go higher in practice. Run `
|
|
43
|
+
Conservative numbers — peaks go higher in practice. Run `lakonai inspect <cmd>` on your own commands to measure.
|
|
44
44
|
|
|
45
45
|
---
|
|
46
46
|
|
|
@@ -58,13 +58,13 @@ That region was **Lakonía**. Its people gave the English language the word **la
|
|
|
58
58
|
|
|
59
59
|
Your AI coding agent does. It opens with *"Sure! I'd be happy to help…"*, repeats your question back, and explains what the diff already shows. It reads `git log` in full when one line per commit would do. Every wasted token is a soldier you didn't need to send.
|
|
60
60
|
|
|
61
|
-
**
|
|
61
|
+
**lakonai trims both sides.**
|
|
62
62
|
|
|
63
63
|
---
|
|
64
64
|
|
|
65
65
|
## Three fronts. One install.
|
|
66
66
|
|
|
67
|
-
| Front | Wasted tokens look like… |
|
|
67
|
+
| Front | Wasted tokens look like… | lakonai fixes it by… |
|
|
68
68
|
|--------------------------------|-------------------------------------------------------|-------------------------------------------------------------------------------------|
|
|
69
69
|
| **Output** (the model) | *"Great question! Let me explain…"* | Installing a terse-response rule. No preamble, no recap, no restating. |
|
|
70
70
|
| **Input** (your shell tools) | `git log` dumping 1.8 k tokens of author metadata | Wrapping `git`/`ls`/`grep`/`cat`/`tree`/`head`/`tail` and compressing before context. |
|
|
@@ -72,54 +72,56 @@ Your AI coding agent does. It opens with *"Sure! I'd be happy to help…"*, repe
|
|
|
72
72
|
| **Search** (Grep tool) | `Grep` returns 800 matches and you re-read every one | A `PreToolUse` hook on `Grep` auto-caps `head_limit` at 30 with a one-shot hint. |
|
|
73
73
|
| **Analysis** (the rule) | `Read` 5k of logs to count errors in your head | "Think in code" — write `node -e '…filter…count'`, consume only the answer. |
|
|
74
74
|
|
|
75
|
-
Other tools stop at one front.
|
|
75
|
+
Other tools stop at one front. lakonai does all three transparently — your agent doesn't have to remember anything.
|
|
76
76
|
|
|
77
77
|
---
|
|
78
78
|
|
|
79
79
|
## Quick start
|
|
80
80
|
|
|
81
81
|
```bash
|
|
82
|
-
npm install -g
|
|
83
|
-
|
|
82
|
+
npm install -g lakonai
|
|
83
|
+
lakonai install
|
|
84
84
|
```
|
|
85
85
|
|
|
86
|
-
That's it. `
|
|
86
|
+
That's it. `lakonai install` configures your **global** agents — Claude Code, Codex, Gemini CLI — by writing rule blocks under `~/` only. It never touches your current directory by default.
|
|
87
87
|
|
|
88
88
|
Working inside a repo and want **per-project** rules (Cursor, Windsurf, Cline)? Add `--here`:
|
|
89
89
|
|
|
90
90
|
```bash
|
|
91
91
|
cd path/to/your/repo
|
|
92
|
-
|
|
92
|
+
lakonai install --here # globals + per-project rules in this dir
|
|
93
93
|
```
|
|
94
94
|
|
|
95
95
|
From the next session forward your agent:
|
|
96
96
|
|
|
97
97
|
1. **Responds tersely** — no preamble, no restating, no recap. (rule in `CLAUDE.md` / equivalent)
|
|
98
|
-
2. **Has its `Bash` calls auto-rewritten** — `PreToolUse` hook intercepts `git`/`ls`/`cat`/`grep`/etc and prefixes them with `
|
|
98
|
+
2. **Has its `Bash` calls auto-rewritten** — `PreToolUse` hook intercepts `git`/`ls`/`cat`/`grep`/etc and prefixes them with `lakonai` transparently.
|
|
99
99
|
3. **Has its `Read` calls guarded** — a second hook denies `node_modules/`, lockfiles, and build artifacts (with a hint to `grep` instead), and auto-caps reads over 800 lines.
|
|
100
100
|
4. **Has its `Grep` calls capped** — a third hook auto-sets `head_limit` to 30 if you didn't, with a once-per-session hint to use `output_mode:"count"` for tallies.
|
|
101
101
|
5. **Is told to "think in code"** — for any count/filter/parse task, the rule pushes the agent toward a one-shot `node -e` (or `awk`) script that consumes the data so the agent consumes only the answer.
|
|
102
|
-
6. **Logs per-turn LLM token usage** — a `Stop` hook records `input_tokens` / `output_tokens` / `cache_read` after each model turn so `
|
|
103
|
-
7. **Tells you about new versions** — a `SessionStart` hook checks npm once per day and surfaces a `
|
|
102
|
+
6. **Logs per-turn LLM token usage** — a `Stop` hook records `input_tokens` / `output_tokens` / `cache_read` after each model turn so `lakonai gain` shows model-side savings alongside shell-side savings.
|
|
103
|
+
7. **Tells you about new versions** — a `SessionStart` hook checks npm once per day and surfaces a `lakonai X.Y.Z available` notice inside the session (opt-out: `LAKON_NO_UPDATE_CHECK=1`).
|
|
104
104
|
|
|
105
|
-
You'll see savings stack up immediately in `
|
|
105
|
+
You'll see savings stack up immediately in `lakonai gain`.
|
|
106
106
|
|
|
107
|
-
> Hooks are currently Claude Code-only (the only platform with documented hook APIs). For Codex/Cursor/Windsurf/Cline/Gemini, the rule asks the model to grep-before-Read and use the `
|
|
107
|
+
> Hooks are currently Claude Code-only (the only platform with documented hook APIs). For Codex/Cursor/Windsurf/Cline/Gemini, the rule asks the model to grep-before-Read and use the `lakonai` prefix itself.
|
|
108
108
|
|
|
109
|
-
> **Worried?** Every install backs up the target file first. `
|
|
109
|
+
> **Worried?** Every install backs up the target file first. `lakonai revert` puts it back byte-for-byte.
|
|
110
|
+
|
|
111
|
+
> **Upgrading from `lakon`?** The package was renamed to **lakonai** in 0.7.0. The `lakon` and `lak` commands still work as aliases, your `~/.lakon/` log + backups carry over untouched, and `lakonai install` rewrites your existing config block to the new brand. No migration needed.
|
|
110
112
|
|
|
111
113
|
---
|
|
112
114
|
|
|
113
115
|
## Use the filter directly
|
|
114
116
|
|
|
115
|
-
The CLI works as a standalone tool too. Run any shell command through `
|
|
117
|
+
The CLI works as a standalone tool too. Run any shell command through `lakonai` (or the short alias `lak`) to filter its output:
|
|
116
118
|
|
|
117
119
|
```bash
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
120
|
+
lakonai git status # compressed git status
|
|
121
|
+
lakonai git log -50 # one line per commit (hash + subject)
|
|
122
|
+
lakonai git diff # only +/- lines, no noise
|
|
123
|
+
lakonai ls -la # size + name only
|
|
124
|
+
lakonai grep -r foo src/ # truncates at 40 matches
|
|
123
125
|
```
|
|
124
126
|
|
|
125
127
|
Unsupported commands run unchanged.
|
|
@@ -129,11 +131,11 @@ Unsupported commands run unchanged.
|
|
|
129
131
|
## See your savings
|
|
130
132
|
|
|
131
133
|
```bash
|
|
132
|
-
|
|
134
|
+
lakonai gain
|
|
133
135
|
```
|
|
134
136
|
|
|
135
137
|
```
|
|
136
|
-
|
|
138
|
+
lakonai — savings this week: 36.4k tok saved across 512 shell calls (67%)
|
|
137
139
|
|
|
138
140
|
shell + read/grep guards (tokens filtered before context)
|
|
139
141
|
win calls before after saved %
|
|
@@ -160,7 +162,7 @@ The top block measures **input savings** (what filtered shell + guards prevented
|
|
|
160
162
|
### Inspect a single command
|
|
161
163
|
|
|
162
164
|
```bash
|
|
163
|
-
|
|
165
|
+
lakonai inspect git log
|
|
164
166
|
```
|
|
165
167
|
|
|
166
168
|
```
|
|
@@ -174,22 +176,22 @@ saved: 85%
|
|
|
174
176
|
|
|
175
177
|
## Commands
|
|
176
178
|
|
|
177
|
-
| Command
|
|
178
|
-
|
|
179
|
-
| `
|
|
180
|
-
| `
|
|
181
|
-
| `
|
|
182
|
-
| `
|
|
183
|
-
| `
|
|
184
|
-
| `
|
|
185
|
-
| `
|
|
186
|
-
| `
|
|
187
|
-
| `
|
|
188
|
-
| `
|
|
189
|
-
| `
|
|
190
|
-
| `
|
|
191
|
-
|
|
192
|
-
`lak` is the short alias for `lakon`
|
|
179
|
+
| Command | What it does |
|
|
180
|
+
|--------------------------------------|-----------------------------------------------------------------------|
|
|
181
|
+
| `lakonai install` | Install rule for detected GLOBAL agents only (no CWD writes) |
|
|
182
|
+
| `lakonai install --here` | Globals + per-project rules (Cursor/Windsurf/Cline) in current dir |
|
|
183
|
+
| `lakonai install --only <p>` | Install just one platform by id (any scope) |
|
|
184
|
+
| `lakonai uninstall` | Strip the lakonai block from each config (keeps your other content) |
|
|
185
|
+
| `lakonai revert [--only <p>]` | Restore each config to its pre-install state from backup |
|
|
186
|
+
| `lakonai backups` | Show backup history per platform |
|
|
187
|
+
| `lakonai list` | Show supported platforms and which are detected |
|
|
188
|
+
| `lakonai <cmd> [args]` | Run a command, filter its output, track savings |
|
|
189
|
+
| `lakonai gain` | Show savings by hour / day / week / month / all-time + session totals |
|
|
190
|
+
| `lakonai inspect <cmd>` | Run once and show raw-vs-filtered (no tracking) |
|
|
191
|
+
| `lakonai reset` | Wipe the savings log |
|
|
192
|
+
| `lakonai version` / `--version` / `-v` | Print the installed lakonai version |
|
|
193
|
+
|
|
194
|
+
`lak` is the short alias for `lakonai`. `lakon` is kept as a legacy alias so existing setups keep working.
|
|
193
195
|
|
|
194
196
|
---
|
|
195
197
|
|
|
@@ -226,48 +228,48 @@ The `Grep` hook auto-sets `head_limit` to **30** when the agent didn't pass one.
|
|
|
226
228
|
|
|
227
229
|
A `Stop` hook fires at the end of every model turn, reads the latest `usage` block from the transcript, and appends a `cmd: "session"` entry to the log with `input_tokens`, `output_tokens`, `cache_read_input_tokens`, and `cache_creation_input_tokens`.
|
|
228
230
|
|
|
229
|
-
`
|
|
231
|
+
`lakonai gain` renders these in a separate **session output** block (see example above) — so you can watch model-side verbosity drop and cache-hit ratios climb over time. Top commands list excludes session entries; they're not shell calls.
|
|
230
232
|
|
|
231
233
|
### Update notifications (Claude Code)
|
|
232
234
|
|
|
233
|
-
A `SessionStart` hook checks `registry.npmjs.org/
|
|
235
|
+
A `SessionStart` hook checks `registry.npmjs.org/lakonai/latest` at most once per 24 hours (cached at `~/.lakon/version.json`) and, if a newer version exists, emits a `hookSpecificOutput.additionalContext` that surfaces inside the Claude session:
|
|
234
236
|
|
|
235
237
|
```
|
|
236
|
-
|
|
238
|
+
lakonai 0.8.0 available (you have 0.7.0). Update: npm i -g lakonai@latest
|
|
237
239
|
```
|
|
238
240
|
|
|
239
|
-
Outside Claude, `
|
|
241
|
+
Outside Claude, `lakonai gain` and `lakonai version` print the same notice on stderr (yellow when TTY).
|
|
240
242
|
|
|
241
243
|
**Opt out:** `LAKON_NO_UPDATE_CHECK=1`.
|
|
242
244
|
**Test endpoint:** `LAKON_REGISTRY_URL=http://localhost:8080/` (overrides the npm URL for local testing).
|
|
243
245
|
|
|
244
246
|
### Multi-profile Claude Code
|
|
245
247
|
|
|
246
|
-
If you use wrapper aliases like `claude-my=CLAUDE_CONFIG_DIR=$HOME/.claude-my claude` (e.g. one profile per Anthropic account or org), set the same env var when running `
|
|
248
|
+
If you use wrapper aliases like `claude-my=CLAUDE_CONFIG_DIR=$HOME/.claude-my claude` (e.g. one profile per Anthropic account or org), set the same env var when running `lakonai install` so hooks and the rule file land in the right config dir:
|
|
247
249
|
|
|
248
250
|
```bash
|
|
249
|
-
CLAUDE_CONFIG_DIR=$HOME/.claude-my
|
|
250
|
-
CLAUDE_CONFIG_DIR=$HOME/.claude-arco
|
|
251
|
+
CLAUDE_CONFIG_DIR=$HOME/.claude-my lakonai install
|
|
252
|
+
CLAUDE_CONFIG_DIR=$HOME/.claude-arco lakonai install
|
|
251
253
|
```
|
|
252
254
|
|
|
253
|
-
Each profile gets its own independent install. `
|
|
255
|
+
Each profile gets its own independent install. `lakonai uninstall` / `lakonai revert` respect the same env var.
|
|
254
256
|
|
|
255
257
|
---
|
|
256
258
|
|
|
257
259
|
## Supported AI agents
|
|
258
260
|
|
|
259
|
-
| Agent | Scope | What `
|
|
260
|
-
|
|
261
|
-
| Claude Code¹ | global | Rule block in `~/.claude/CLAUDE.md` + **five** hooks in `~/.claude/settings.json` (`PreToolUse`: Bash rewrite + Read guard + Grep guard; `Stop`: session-usage log; `SessionStart`: update notify) + `/
|
|
262
|
-
| Codex CLI | global | Rule block in `~/.codex/AGENTS.md`
|
|
263
|
-
| Gemini CLI | global | Rule block in `~/.gemini/GEMINI.md`
|
|
264
|
-
| Cursor | project² | `.cursor/rules/
|
|
265
|
-
| Windsurf | project² | `.windsurf/rules/
|
|
266
|
-
| Cline | project² | `.clinerules/
|
|
261
|
+
| Agent | Scope | What `lakonai install` writes |
|
|
262
|
+
|-----------------|----------|--------------------------------------------------------------------------------------------------------|
|
|
263
|
+
| Claude Code¹ | global | Rule block in `~/.claude/CLAUDE.md` + **five** hooks in `~/.claude/settings.json` (`PreToolUse`: Bash rewrite + Read guard + Grep guard; `Stop`: session-usage log; `SessionStart`: update notify) + `/lakonai:gain` `/lakonai:reset` `/lakonai:inspect` slash commands |
|
|
264
|
+
| Codex CLI | global | Rule block in `~/.codex/AGENTS.md` |
|
|
265
|
+
| Gemini CLI | global | Rule block in `~/.gemini/GEMINI.md` |
|
|
266
|
+
| Cursor | project² | `.cursor/rules/lakonai.mdc` in the current dir |
|
|
267
|
+
| Windsurf | project² | `.windsurf/rules/lakonai.md` in the current dir |
|
|
268
|
+
| Cline | project² | `.clinerules/lakonai.md` in the current dir |
|
|
267
269
|
|
|
268
270
|
¹ "Claude Code" covers **every** Claude Code frontend — terminal CLI, VS Code extension, JetBrains plugin, desktop app. All read the same `~/.claude/CLAUDE.md` + `~/.claude/settings.json`, so one install lights up all of them.
|
|
269
271
|
|
|
270
|
-
² Project-scoped tools only read rules from the current directory, so `
|
|
272
|
+
² Project-scoped tools only read rules from the current directory, so `lakonai install` skips them by default to avoid scattering files across your repos. Add `--here` (or use `--project`) when you actually want them in the current dir.
|
|
271
273
|
|
|
272
274
|
Each install is **idempotent** (rerunning replaces the existing block) and **reversible** (`uninstall` strips it, `revert` restores from backup).
|
|
273
275
|
|
|
@@ -275,39 +277,41 @@ Each install is **idempotent** (rerunning replaces the existing block) and **rev
|
|
|
275
277
|
|
|
276
278
|
## Backup & revert
|
|
277
279
|
|
|
278
|
-
Before writing to your config file for the first time, `
|
|
280
|
+
Before writing to your config file for the first time, `lakonai` copies it into `~/.lakon/backups/<platform>/<filename>.<timestamp>.bak`. Every install thereafter appends another snapshot to that file's manifest.
|
|
279
281
|
|
|
280
282
|
```bash
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
283
|
+
lakonai uninstall # strips just the lakonai block; keeps your other CLAUDE.md content
|
|
284
|
+
lakonai revert # restores the file to its pre-install state, byte for byte
|
|
285
|
+
lakonai backups # shows every snapshot, per platform, with timestamps
|
|
284
286
|
```
|
|
285
287
|
|
|
286
|
-
Use `uninstall` to remove
|
|
288
|
+
Use `uninstall` to remove lakonai while keeping your other edits. Use `revert` when you want a clean rollback to exactly the file you had before.
|
|
287
289
|
|
|
288
290
|
---
|
|
289
291
|
|
|
290
292
|
## How tracking works
|
|
291
293
|
|
|
292
|
-
Every filtered command appends a JSON line to `~/.lakon/log.jsonl`. The `Stop` hook appends one line per model turn with token counts (`cmd: "session"`). `
|
|
294
|
+
Every filtered command appends a JSON line to `~/.lakon/log.jsonl`. The `Stop` hook appends one line per model turn with token counts (`cmd: "session"`). `lakonai gain` reads that log and renders both sides separately.
|
|
293
295
|
|
|
294
296
|
The log stores: timestamp, command name, first few args, raw/filtered token counts (shell entries); timestamp, session id, `input_tokens` / `output_tokens` / `cache_read` / `cache_create` (session entries). **No file contents. No full arguments. No transcript content. No data ever leaves your machine** — except the one daily HEAD request to `registry.npmjs.org` for the update check (opt-out: `LAKON_NO_UPDATE_CHECK=1`).
|
|
295
297
|
|
|
296
298
|
Override the location with `LAKON_HOME=/path`. Disable per-command logging with `LAKON_NO_TRACK=1`.
|
|
297
299
|
|
|
300
|
+
> Note: env vars and the data dir keep the historical `LAKON_*` / `~/.lakon/` names so existing installs keep their logs and backups intact. New installs land there too.
|
|
301
|
+
|
|
298
302
|
---
|
|
299
303
|
|
|
300
304
|
## Configuration
|
|
301
305
|
|
|
302
|
-
| Env var | Effect
|
|
303
|
-
|
|
306
|
+
| Env var | Effect |
|
|
307
|
+
|-------------------------|------------------------------------------------------------------------------|
|
|
304
308
|
| `LAKON_HOME` | Where to keep the log + backups + version cache (default `~/.lakon`) |
|
|
305
309
|
| `LAKON_NO_TRACK` | Set to `1` to disable per-command logging |
|
|
306
310
|
| `LAKON_NO_UPDATE_CHECK` | Set to `1` to disable the `SessionStart` npm check + terminal hint |
|
|
307
311
|
| `LAKON_REGISTRY_URL` | Override the npm registry URL used by the update check (testing) |
|
|
308
|
-
| `LAKON_COLOR` | `1` forces ANSI colors in `
|
|
312
|
+
| `LAKON_COLOR` | `1` forces ANSI colors in `lakonai gain`; `0` disables; unset = TTY auto-detect |
|
|
309
313
|
| `NO_COLOR` | Standard. Disables ANSI colors when set to any non-empty value. |
|
|
310
|
-
| `CLAUDE_CONFIG_DIR` | When set during `
|
|
314
|
+
| `CLAUDE_CONFIG_DIR` | When set during `lakonai install` / `uninstall`, hooks + rule land in that dir instead of `~/.claude/`. Used for multi-profile setups. |
|
|
311
315
|
|
|
312
316
|
---
|
|
313
317
|
|
|
@@ -317,7 +321,7 @@ Override the location with `LAKON_HOME=/path`. Disable per-command logging with
|
|
|
317
321
|
> *"Vēnī, vīdī, vīcī."* — Julius Caesar, three words to describe winning a war.
|
|
318
322
|
> *"If."* — Spartans, refusing to be intimidated by a single conditional.
|
|
319
323
|
|
|
320
|
-
Every token your agent emits or reads is paid for — in latency, in money, in context budget. The fastest way to think clearly is to speak briefly.
|
|
324
|
+
Every token your agent emits or reads is paid for — in latency, in money, in context budget. The fastest way to think clearly is to speak briefly. lakonai doesn't make your agent dumber; it makes it Spartan.
|
|
321
325
|
|
|
322
326
|
---
|
|
323
327
|
|
|
@@ -330,7 +334,7 @@ npm install # only devDeps (c8 for coverage); zero runtime
|
|
|
330
334
|
node --test tests/ # run the suite
|
|
331
335
|
npm run test:coverage # text + HTML coverage report (coverage/index.html)
|
|
332
336
|
npm run test:coverage:check # fail if any metric drops below 100%
|
|
333
|
-
node bin/
|
|
337
|
+
node bin/lakonai.js --help
|
|
334
338
|
```
|
|
335
339
|
|
|
336
340
|
Suite: **187 tests**. Coverage gate: **100% lines / 100% branches / 100% functions / 100% statements**. Zero runtime dependencies. Node ≥ 18.
|
|
@@ -344,7 +348,7 @@ Built on ideas from two excellent projects:
|
|
|
344
348
|
- [**caveman**](https://github.com/juliusbrussee/caveman) — terse-prose rule + auto-clarity carve-outs.
|
|
345
349
|
- [**rtk**](https://github.com/rtk-ai/rtk) — CLI output filtering as a force multiplier for LLM agents.
|
|
346
350
|
|
|
347
|
-
|
|
351
|
+
lakonai condenses both into one zero-dependency npm package with a single install command, automatic backups, and time-windowed savings tracking.
|
|
348
352
|
|
|
349
353
|
---
|
|
350
354
|
|
package/assets/logo.svg
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 240 240" width="240" height="240" role="img" aria-label="
|
|
2
|
-
<title>
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 240 240" width="240" height="240" role="img" aria-label="lakonai">
|
|
2
|
+
<title>lakonai</title>
|
|
3
3
|
<desc>Spartan lambda inside a circular shield — symbol of Lakonía, ancient Sparta.</desc>
|
|
4
4
|
<style>
|
|
5
5
|
.mark { stroke: #0F0F0F; fill: none; }
|
|
@@ -7,45 +7,46 @@ const { install, uninstall, revert, listPlatforms, backupsReport } = require('..
|
|
|
7
7
|
const tracking = require('../src/tracking');
|
|
8
8
|
const versionCheck = require('../src/hooks/version-check');
|
|
9
9
|
|
|
10
|
-
const HELP = `
|
|
10
|
+
const HELP = `lakonai — spartan replies for AI agents
|
|
11
11
|
|
|
12
12
|
Usage:
|
|
13
|
-
|
|
13
|
+
lakonai <cmd> [args...] Run <cmd> and filter its output (tracks savings)
|
|
14
14
|
lak <cmd> [args...] (short alias)
|
|
15
|
+
lakon <cmd> [args...] (legacy alias, kept for back-compat)
|
|
15
16
|
|
|
16
|
-
|
|
17
|
+
lakonai install Install rule + hooks for detected GLOBAL platforms
|
|
17
18
|
(Claude Code / Codex / Gemini — touches ~/ only)
|
|
18
|
-
|
|
19
|
+
lakonai install --here Same as above + per-project rules (Cursor /
|
|
19
20
|
Windsurf / Cline) written into the current dir
|
|
20
|
-
|
|
21
|
+
lakonai install --only <p> Install just one platform by id (any scope)
|
|
21
22
|
(every install backs up the target file first)
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
23
|
+
lakonai uninstall Strip the lakonai block (keeps rest of file)
|
|
24
|
+
lakonai revert [--only <p>] Restore files to pre-install state from backup
|
|
25
|
+
lakonai backups Show backup history per platform
|
|
26
|
+
lakonai list Show supported platforms
|
|
26
27
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
28
|
+
lakonai gain Show token savings (hour / day / week / month / all)
|
|
29
|
+
lakonai inspect <cmd> ... Run <cmd> once and show raw vs filtered (no tracking)
|
|
30
|
+
lakonai reset Wipe the savings log
|
|
31
|
+
lakonai version Print the installed lakonai version
|
|
32
|
+
lakonai --help This help
|
|
32
33
|
|
|
33
34
|
Supported filters: git (log/status/diff/show), ls, tree, cat, head, tail, grep, rg, ag.
|
|
34
35
|
Unsupported commands run unchanged (passthrough, still tracked as 0% savings).
|
|
35
36
|
|
|
36
37
|
Multi-profile Claude Code (e.g. claude-my / claude-arco wrappers):
|
|
37
|
-
CLAUDE_CONFIG_DIR=$HOME/.claude-my
|
|
38
|
-
CLAUDE_CONFIG_DIR=$HOME/.claude-arco
|
|
38
|
+
CLAUDE_CONFIG_DIR=$HOME/.claude-my lakonai install
|
|
39
|
+
CLAUDE_CONFIG_DIR=$HOME/.claude-arco lakonai install
|
|
39
40
|
|
|
40
41
|
Update notifications:
|
|
41
|
-
SessionStart hook + \`
|
|
42
|
+
SessionStart hook + \`lakonai gain\` / \`lakonai version\` check npm once per day.
|
|
42
43
|
Disable with LAKON_NO_UPDATE_CHECK=1.
|
|
43
44
|
`;
|
|
44
45
|
|
|
45
46
|
function runAndFilter(cmd, args) {
|
|
46
47
|
const child = spawnSync(cmd, args, { encoding: 'utf8', stdio: ['inherit', 'pipe', 'inherit'] });
|
|
47
48
|
if (child.error) {
|
|
48
|
-
process.stderr.write(`
|
|
49
|
+
process.stderr.write(`lakonai: ${child.error.message}\n`);
|
|
49
50
|
process.exit(127);
|
|
50
51
|
}
|
|
51
52
|
/* c8 ignore next */
|
|
@@ -68,7 +69,7 @@ function runAndFilter(cmd, args) {
|
|
|
68
69
|
|
|
69
70
|
function inspectCmd(rest) {
|
|
70
71
|
if (!rest.length) {
|
|
71
|
-
process.stderr.write('
|
|
72
|
+
process.stderr.write('lakonai inspect: missing command\n');
|
|
72
73
|
process.exit(2);
|
|
73
74
|
}
|
|
74
75
|
const [cmd, ...args] = rest;
|
|
@@ -163,7 +164,7 @@ async function main() {
|
|
|
163
164
|
}
|
|
164
165
|
if (first === 'reset') {
|
|
165
166
|
const ok = tracking.reset();
|
|
166
|
-
process.stdout.write(ok ? '
|
|
167
|
+
process.stdout.write(ok ? 'lakonai: log cleared\n' : 'lakonai: nothing to clear\n');
|
|
167
168
|
return;
|
|
168
169
|
}
|
|
169
170
|
|
|
@@ -172,6 +173,6 @@ async function main() {
|
|
|
172
173
|
|
|
173
174
|
/* c8 ignore next 4 */
|
|
174
175
|
main().catch((err) => {
|
|
175
|
-
process.stderr.write(`
|
|
176
|
+
process.stderr.write(`lakonai: ${err.message}\n`);
|
|
176
177
|
process.exit(1);
|
|
177
178
|
});
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lakonai",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Spartan replies for AI agents
|
|
3
|
+
"version": "0.7.0",
|
|
4
|
+
"description": "lakonai — Spartan replies for AI agents. Terse model output + filtered CLI output for Claude Code, Codex, Cursor, Windsurf, Cline, and Gemini.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"claude",
|
|
7
7
|
"claude-code",
|
|
@@ -29,8 +29,9 @@
|
|
|
29
29
|
"type": "commonjs",
|
|
30
30
|
"main": "src/filters/index.js",
|
|
31
31
|
"bin": {
|
|
32
|
-
"
|
|
33
|
-
"
|
|
32
|
+
"lakonai": "bin/lakonai.js",
|
|
33
|
+
"lakon": "bin/lakonai.js",
|
|
34
|
+
"lak": "bin/lakonai.js"
|
|
34
35
|
},
|
|
35
36
|
"files": [
|
|
36
37
|
"bin/",
|
|
@@ -9,10 +9,10 @@ function rewriteIfNeeded(command) {
|
|
|
9
9
|
if (typeof command !== 'string') return null;
|
|
10
10
|
const trimmed = command.trim();
|
|
11
11
|
if (!trimmed) return null;
|
|
12
|
-
if (/^(lakon|lak)(\s|$)/.test(trimmed)) return null;
|
|
12
|
+
if (/^(lakonai|lakon|lak)(\s|$)/.test(trimmed)) return null;
|
|
13
13
|
const firstWord = trimmed.split(/\s+/)[0];
|
|
14
14
|
if (!FILTERED_CMDS.has(firstWord)) return null;
|
|
15
|
-
return `
|
|
15
|
+
return `lakonai ${trimmed}`;
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
async function readStdin() {
|
package/src/hooks/grep-guard.js
CHANGED
|
@@ -54,7 +54,7 @@ async function main() {
|
|
|
54
54
|
|
|
55
55
|
const updatedInput = { ...input, head_limit: DEFAULT_HEAD_LIMIT };
|
|
56
56
|
const reason = shouldEmit('grep-head-cap')
|
|
57
|
-
? `
|
|
57
|
+
? `lakonai: head_limit auto-set to ${DEFAULT_HEAD_LIMIT}. Pass head_limit explicitly to override; pass output_mode:"count" for a tally instead of matches.`
|
|
58
58
|
: undefined;
|
|
59
59
|
|
|
60
60
|
trackRecord({
|
package/src/hooks/read-guard.js
CHANGED
|
@@ -137,7 +137,7 @@ async function main() {
|
|
|
137
137
|
hookSpecificOutput: {
|
|
138
138
|
hookEventName: 'PreToolUse',
|
|
139
139
|
permissionDecision: 'deny',
|
|
140
|
-
permissionDecisionReason: `
|
|
140
|
+
permissionDecisionReason: `lakonai: ${denyReason}`,
|
|
141
141
|
},
|
|
142
142
|
};
|
|
143
143
|
process.stdout.write(JSON.stringify(response));
|
|
@@ -165,7 +165,7 @@ async function main() {
|
|
|
165
165
|
offset: 1,
|
|
166
166
|
limit: AUTO_CAP_LINES,
|
|
167
167
|
},
|
|
168
|
-
permissionDecisionReason: `
|
|
168
|
+
permissionDecisionReason: `lakonai: file has ${n} lines, capped at ${AUTO_CAP_LINES}. Read again with offset=${AUTO_CAP_LINES + 1} for more, or grep -n the symbol you need.`,
|
|
169
169
|
},
|
|
170
170
|
};
|
|
171
171
|
process.stdout.write(JSON.stringify(response));
|
|
@@ -8,7 +8,7 @@ const http = require('http');
|
|
|
8
8
|
|
|
9
9
|
const CHECK_TTL_MS = 24 * 60 * 60 * 1000;
|
|
10
10
|
const FETCH_TIMEOUT_MS = 1500;
|
|
11
|
-
const REGISTRY_URL = 'https://registry.npmjs.org/
|
|
11
|
+
const REGISTRY_URL = 'https://registry.npmjs.org/lakonai/latest';
|
|
12
12
|
|
|
13
13
|
function lakonHome() {
|
|
14
14
|
/* c8 ignore next */
|
|
@@ -161,7 +161,7 @@ function getCachedUpdate() {
|
|
|
161
161
|
}
|
|
162
162
|
|
|
163
163
|
function formatNotice(info) {
|
|
164
|
-
return `
|
|
164
|
+
return `lakonai ${info.latest} available (you have ${info.current}). Update: npm i -g lakonai@latest`;
|
|
165
165
|
}
|
|
166
166
|
|
|
167
167
|
module.exports = {
|
|
@@ -8,37 +8,41 @@ const COMMANDS = [
|
|
|
8
8
|
{
|
|
9
9
|
name: 'gain',
|
|
10
10
|
body: `---
|
|
11
|
-
description: Show
|
|
12
|
-
allowed-tools: Bash(lakon gain:*), Bash(lak gain:*)
|
|
11
|
+
description: Show lakonai token savings (raw vs filtered, per window and top commands).
|
|
12
|
+
allowed-tools: Bash(lakonai gain:*), Bash(lakon gain:*), Bash(lak gain:*)
|
|
13
13
|
---
|
|
14
14
|
|
|
15
|
-
Run \`
|
|
15
|
+
Run \`lakonai gain\` and show the output verbatim. Do not summarize — the table is the answer.
|
|
16
16
|
`,
|
|
17
17
|
},
|
|
18
18
|
{
|
|
19
19
|
name: 'reset',
|
|
20
20
|
body: `---
|
|
21
|
-
description: Wipe the
|
|
22
|
-
allowed-tools: Bash(lakon reset:*)
|
|
21
|
+
description: Wipe the lakonai savings log.
|
|
22
|
+
allowed-tools: Bash(lakonai reset:*), Bash(lakon reset:*)
|
|
23
23
|
---
|
|
24
24
|
|
|
25
|
-
Run \`
|
|
25
|
+
Run \`lakonai reset\` and show the output. Confirm with the user before running if they didn't explicitly ask to clear.
|
|
26
26
|
`,
|
|
27
27
|
},
|
|
28
28
|
{
|
|
29
29
|
name: 'inspect',
|
|
30
30
|
body: `---
|
|
31
|
-
description: Run a command once through
|
|
31
|
+
description: Run a command once through lakonai and compare raw vs filtered token counts.
|
|
32
32
|
argument-hint: <command> [args...]
|
|
33
|
-
allowed-tools: Bash(lakon inspect:*)
|
|
33
|
+
allowed-tools: Bash(lakonai inspect:*), Bash(lakon inspect:*)
|
|
34
34
|
---
|
|
35
35
|
|
|
36
|
-
Run \`
|
|
36
|
+
Run \`lakonai inspect $ARGUMENTS\` and show the output verbatim.
|
|
37
37
|
`,
|
|
38
38
|
},
|
|
39
39
|
];
|
|
40
40
|
|
|
41
41
|
function commandsDir(home) {
|
|
42
|
+
return path.join(claudeConfigDir(home), 'commands', 'lakonai');
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function legacyCommandsDir(home) {
|
|
42
46
|
return path.join(claudeConfigDir(home), 'commands', 'lakon');
|
|
43
47
|
}
|
|
44
48
|
|
|
@@ -49,13 +53,12 @@ function installCommands(home) {
|
|
|
49
53
|
for (const c of COMMANDS) {
|
|
50
54
|
const p = path.join(dir, `${c.name}.md`);
|
|
51
55
|
fs.writeFileSync(p, c.body, 'utf8');
|
|
52
|
-
written.push(`/
|
|
56
|
+
written.push(`/lakonai:${c.name}`);
|
|
53
57
|
}
|
|
54
58
|
return written;
|
|
55
59
|
}
|
|
56
60
|
|
|
57
|
-
function
|
|
58
|
-
const dir = commandsDir(home);
|
|
61
|
+
function removeCommandsFromDir(dir) {
|
|
59
62
|
const removed = [];
|
|
60
63
|
for (const c of COMMANDS) {
|
|
61
64
|
const p = path.join(dir, `${c.name}.md`);
|
|
@@ -68,4 +71,12 @@ function uninstallCommands(home) {
|
|
|
68
71
|
return removed;
|
|
69
72
|
}
|
|
70
73
|
|
|
74
|
+
function uninstallCommands(home) {
|
|
75
|
+
const removed = [
|
|
76
|
+
...removeCommandsFromDir(commandsDir(home)),
|
|
77
|
+
...removeCommandsFromDir(legacyCommandsDir(home)),
|
|
78
|
+
];
|
|
79
|
+
return removed;
|
|
80
|
+
}
|
|
81
|
+
|
|
71
82
|
module.exports = { installCommands, uninstallCommands, commandsDir };
|
package/src/install/index.js
CHANGED
|
@@ -6,7 +6,7 @@ const os = require('os');
|
|
|
6
6
|
const platforms = require('./platforms');
|
|
7
7
|
const { listBackups } = require('./backup');
|
|
8
8
|
|
|
9
|
-
const RULE_PATH = path.join(__dirname, '..', 'rules', '
|
|
9
|
+
const RULE_PATH = path.join(__dirname, '..', 'rules', 'lakonai.md');
|
|
10
10
|
|
|
11
11
|
const OK = '✅';
|
|
12
12
|
const FAIL = '❌';
|
|
@@ -52,16 +52,16 @@ async function install({ only, here = false } = {}) {
|
|
|
52
52
|
|
|
53
53
|
if (!targets.length) {
|
|
54
54
|
if (only) {
|
|
55
|
-
process.stdout.write(`${FAIL} unknown platform "${only}". Run \`
|
|
55
|
+
process.stdout.write(`${FAIL} unknown platform "${only}". Run \`lakonai list\` to see options.\n`);
|
|
56
56
|
} else {
|
|
57
|
-
process.stdout.write(`${FAIL} no supported global platforms detected. Install Claude Code, Codex, or Gemini CLI first — or run \`
|
|
57
|
+
process.stdout.write(`${FAIL} no supported global platforms detected. Install Claude Code, Codex, or Gemini CLI first — or run \`lakonai install --here\` to write per-project rules (Cursor/Windsurf/Cline) in the current directory.\n`);
|
|
58
58
|
}
|
|
59
59
|
process.exitCode = 1;
|
|
60
60
|
return;
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
-
process.stdout.write('\
|
|
64
|
-
process.stdout.write('
|
|
63
|
+
process.stdout.write('\nlakonai install\n');
|
|
64
|
+
process.stdout.write('───────────────\n');
|
|
65
65
|
|
|
66
66
|
for (const p of targets) {
|
|
67
67
|
try {
|
|
@@ -76,18 +76,18 @@ async function install({ only, here = false } = {}) {
|
|
|
76
76
|
process.stdout.write('\n');
|
|
77
77
|
if (!only && !here) {
|
|
78
78
|
process.stdout.write(` ${BULLET} Per-project rules (Cursor/Windsurf/Cline) were skipped.\n`);
|
|
79
|
-
process.stdout.write(` Inside a repo? Run \`
|
|
79
|
+
process.stdout.write(` Inside a repo? Run \`lakonai install --here\` to add them in the current directory.\n`);
|
|
80
80
|
}
|
|
81
|
-
process.stdout.write(` ${BULLET} \`
|
|
82
|
-
process.stdout.write(` ${BULLET} \`
|
|
83
|
-
process.stdout.write(` ${BULLET} \`
|
|
81
|
+
process.stdout.write(` ${BULLET} \`lakonai uninstall\` removes only the lakonai block (keeps your other content).\n`);
|
|
82
|
+
process.stdout.write(` ${BULLET} \`lakonai revert\` restores files to pre-install state from backup.\n`);
|
|
83
|
+
process.stdout.write(` ${BULLET} \`lakonai gain\` shows how many tokens you've saved.\n`);
|
|
84
84
|
process.stdout.write('\nRestart your AI agent (or open a new session) for the rule to take effect.\n');
|
|
85
85
|
}
|
|
86
86
|
|
|
87
87
|
async function uninstall() {
|
|
88
88
|
const home = os.homedir();
|
|
89
|
-
process.stdout.write('\
|
|
90
|
-
process.stdout.write('
|
|
89
|
+
process.stdout.write('\nlakonai uninstall\n');
|
|
90
|
+
process.stdout.write('─────────────────\n');
|
|
91
91
|
let any = false;
|
|
92
92
|
for (const p of platforms.list()) {
|
|
93
93
|
try {
|
|
@@ -108,8 +108,8 @@ async function revert({ only } = {}) {
|
|
|
108
108
|
? platforms.list().filter((p) => p.id === only)
|
|
109
109
|
: platforms.list();
|
|
110
110
|
|
|
111
|
-
process.stdout.write('\
|
|
112
|
-
process.stdout.write('
|
|
111
|
+
process.stdout.write('\nlakonai revert\n');
|
|
112
|
+
process.stdout.write('──────────────\n');
|
|
113
113
|
|
|
114
114
|
let any = false;
|
|
115
115
|
for (const p of targets) {
|
|
@@ -129,7 +129,7 @@ async function revert({ only } = {}) {
|
|
|
129
129
|
}
|
|
130
130
|
|
|
131
131
|
function backupsReport() {
|
|
132
|
-
const lines = ['', '
|
|
132
|
+
const lines = ['', 'lakonai — backup history', '────────────────────────'];
|
|
133
133
|
let any = false;
|
|
134
134
|
for (const p of platforms.list()) {
|
|
135
135
|
const entries = listBackups(p.id);
|
package/src/install/platforms.js
CHANGED
|
@@ -7,8 +7,10 @@ const { installHook, uninstallHook } = require('./claude-hook');
|
|
|
7
7
|
const { installCommands, uninstallCommands } = require('./claude-commands');
|
|
8
8
|
const { claudeConfigDir } = require('./paths');
|
|
9
9
|
|
|
10
|
-
const MARK_BEGIN = '<!--
|
|
11
|
-
const MARK_END = '<!--
|
|
10
|
+
const MARK_BEGIN = '<!-- lakonai:begin -->';
|
|
11
|
+
const MARK_END = '<!-- lakonai:end -->';
|
|
12
|
+
const ANY_BEGIN = '<!-- lakon(?:ai)?:begin -->';
|
|
13
|
+
const ANY_END = '<!-- lakon(?:ai)?:end -->';
|
|
12
14
|
|
|
13
15
|
function wrap(rule) {
|
|
14
16
|
return `${MARK_BEGIN}\n${rule.trim()}\n${MARK_END}\n`;
|
|
@@ -30,7 +32,7 @@ function hasBlock(filePath) {
|
|
|
30
32
|
const existing = readSafe(filePath);
|
|
31
33
|
/* c8 ignore next */
|
|
32
34
|
if (!existing) return false;
|
|
33
|
-
const re = new RegExp(`${
|
|
35
|
+
const re = new RegExp(`${ANY_BEGIN}[\\s\\S]*?${ANY_END}`);
|
|
34
36
|
return re.test(existing);
|
|
35
37
|
}
|
|
36
38
|
|
|
@@ -41,7 +43,7 @@ function upsertBlock(platformId, filePath, rule) {
|
|
|
41
43
|
}
|
|
42
44
|
const existing = readSafe(filePath);
|
|
43
45
|
const block = wrap(rule);
|
|
44
|
-
const re = new RegExp(`${
|
|
46
|
+
const re = new RegExp(`${ANY_BEGIN}[\\s\\S]*?${ANY_END}\\n?`);
|
|
45
47
|
const next = re.test(existing) ? existing.replace(re, block) : (existing ? `${existing.trim()}\n\n${block}` : block);
|
|
46
48
|
fs.writeFileSync(filePath, next, 'utf8');
|
|
47
49
|
return filePath;
|
|
@@ -50,7 +52,7 @@ function upsertBlock(platformId, filePath, rule) {
|
|
|
50
52
|
function stripBlock(filePath) {
|
|
51
53
|
const existing = readSafe(filePath);
|
|
52
54
|
if (!existing) return null;
|
|
53
|
-
const re = new RegExp(`\\n*${
|
|
55
|
+
const re = new RegExp(`\\n*${ANY_BEGIN}[\\s\\S]*?${ANY_END}\\n?`);
|
|
54
56
|
if (!re.test(existing)) return null;
|
|
55
57
|
const next = existing.replace(re, '').trim();
|
|
56
58
|
if (next) fs.writeFileSync(filePath, next + '\n', 'utf8');
|
|
@@ -97,24 +99,33 @@ const PLATFORMS = [
|
|
|
97
99
|
label: 'Cursor (per-repo rule)',
|
|
98
100
|
scope: 'project',
|
|
99
101
|
detect: () => fs.existsSync(path.join(process.cwd(), '.cursor')),
|
|
100
|
-
install: ({ rule, id }) => upsertBlock(id, path.join(process.cwd(), '.cursor', 'rules', '
|
|
101
|
-
uninstall: () =>
|
|
102
|
+
install: ({ rule, id }) => upsertBlock(id, path.join(process.cwd(), '.cursor', 'rules', 'lakonai.mdc'), rule),
|
|
103
|
+
uninstall: () => {
|
|
104
|
+
stripBlock(path.join(process.cwd(), '.cursor', 'rules', 'lakon.mdc'));
|
|
105
|
+
return stripBlock(path.join(process.cwd(), '.cursor', 'rules', 'lakonai.mdc'));
|
|
106
|
+
},
|
|
102
107
|
},
|
|
103
108
|
{
|
|
104
109
|
id: 'windsurf',
|
|
105
110
|
label: 'Windsurf (per-repo rule)',
|
|
106
111
|
scope: 'project',
|
|
107
112
|
detect: () => fs.existsSync(path.join(process.cwd(), '.windsurf')),
|
|
108
|
-
install: ({ rule, id }) => upsertBlock(id, path.join(process.cwd(), '.windsurf', 'rules', '
|
|
109
|
-
uninstall: () =>
|
|
113
|
+
install: ({ rule, id }) => upsertBlock(id, path.join(process.cwd(), '.windsurf', 'rules', 'lakonai.md'), rule),
|
|
114
|
+
uninstall: () => {
|
|
115
|
+
stripBlock(path.join(process.cwd(), '.windsurf', 'rules', 'lakon.md'));
|
|
116
|
+
return stripBlock(path.join(process.cwd(), '.windsurf', 'rules', 'lakonai.md'));
|
|
117
|
+
},
|
|
110
118
|
},
|
|
111
119
|
{
|
|
112
120
|
id: 'cline',
|
|
113
121
|
label: 'Cline (per-repo rule)',
|
|
114
122
|
scope: 'project',
|
|
115
123
|
detect: () => fs.existsSync(path.join(process.cwd(), '.clinerules')),
|
|
116
|
-
install: ({ rule, id }) => upsertBlock(id, path.join(process.cwd(), '.clinerules', '
|
|
117
|
-
uninstall: () =>
|
|
124
|
+
install: ({ rule, id }) => upsertBlock(id, path.join(process.cwd(), '.clinerules', 'lakonai.md'), rule),
|
|
125
|
+
uninstall: () => {
|
|
126
|
+
stripBlock(path.join(process.cwd(), '.clinerules', 'lakon.md'));
|
|
127
|
+
return stripBlock(path.join(process.cwd(), '.clinerules', 'lakonai.md'));
|
|
128
|
+
},
|
|
118
129
|
},
|
|
119
130
|
{
|
|
120
131
|
id: 'gemini',
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
#
|
|
1
|
+
# lakonai — terse response style
|
|
2
2
|
|
|
3
|
-
You are operating in **
|
|
3
|
+
You are operating in **lakonai mode** (named for Lakonía — the region of ancient Sparta whose people gave us the word "laconic"). Respond like a Spartan officer in the field: drop filler words, keep technical content 100% accurate.
|
|
4
4
|
|
|
5
5
|
## Rules
|
|
6
6
|
|
|
@@ -40,38 +40,38 @@ Resume terse mode after.
|
|
|
40
40
|
|
|
41
41
|
---
|
|
42
42
|
|
|
43
|
-
## Shell commands — use the `
|
|
43
|
+
## Shell commands — use the `lakonai` prefix
|
|
44
44
|
|
|
45
|
-
For any shell command whose output you read into context, prefix it with `
|
|
45
|
+
For any shell command whose output you read into context, prefix it with `lakonai`. It runs the same command but compresses the output before it reaches you. Same exit code, same behavior — just less to read.
|
|
46
46
|
|
|
47
|
-
| Use this
|
|
48
|
-
|
|
49
|
-
| `
|
|
50
|
-
| `
|
|
51
|
-
| `
|
|
52
|
-
| `
|
|
53
|
-
| `
|
|
54
|
-
| `
|
|
47
|
+
| Use this | Instead of |
|
|
48
|
+
|-----------------------|-----------------|
|
|
49
|
+
| `lakonai git log` | `git log` |
|
|
50
|
+
| `lakonai git status` | `git status` |
|
|
51
|
+
| `lakonai git diff` | `git diff` |
|
|
52
|
+
| `lakonai ls -la` | `ls -la` |
|
|
53
|
+
| `lakonai cat <file>` | `cat <file>` |
|
|
54
|
+
| `lakonai grep -r …` | `grep -r …` |
|
|
55
55
|
|
|
56
|
-
The short alias `lak` works identically: `lak git log`.
|
|
56
|
+
The short alias `lak` works identically: `lak git log`. The legacy alias `lakon` is also accepted.
|
|
57
57
|
|
|
58
|
-
Unsupported commands run unchanged through `
|
|
58
|
+
Unsupported commands run unchanged through `lakonai`, so when in doubt, prefix it.
|
|
59
59
|
|
|
60
60
|
**Skip the prefix only when:**
|
|
61
61
|
- The user explicitly asks for raw, unfiltered output.
|
|
62
|
-
- You're piping into another command (`git log | head` — pipe `
|
|
62
|
+
- You're piping into another command (`git log | head` — pipe `lakonai git log | head` instead).
|
|
63
63
|
- You need a specific format the filter would strip (e.g. machine-parseable `git log --format=...`).
|
|
64
64
|
|
|
65
65
|
## File reads — grep first, then Read with offset/limit
|
|
66
66
|
|
|
67
67
|
Reading entire files is the single biggest token sink. Before using `Read` on any file:
|
|
68
68
|
|
|
69
|
-
1. **Don't Read what you don't need.** If you're looking for one symbol or section, `
|
|
69
|
+
1. **Don't Read what you don't need.** If you're looking for one symbol or section, `lakonai grep -n <pattern> <file>` first. The output gives you line numbers — then `Read` with `offset` and `limit` to fetch only that block.
|
|
70
70
|
2. **Never Read these — grep them or skip:**
|
|
71
71
|
- `node_modules/**`, `vendor/**`, `dist/**`, `build/**`, `target/**`, `.next/**`, `.turbo/**`, `coverage/**`
|
|
72
72
|
- Lockfiles: `package-lock.json`, `pnpm-lock.yaml`, `yarn.lock`, `Cargo.lock`, `go.sum`, `*.lock`
|
|
73
73
|
- Build artifacts: `*.tsbuildinfo`, `*.min.js`, `*.min.css`, source maps, log files, `*.pyc`, `*.so`, `*.class`
|
|
74
|
-
3. **For files > 300 lines:** start with `
|
|
74
|
+
3. **For files > 300 lines:** start with `lakonai grep -n` to locate, then `Read` a slice. Don't `Read` a 2000-line file to find one function.
|
|
75
75
|
4. **Use `Glob` to find files**, not `Read` on the directory listing.
|
|
76
76
|
|
|
77
77
|
These reads cost real context. A `node_modules` peek is 50k tokens of nothing.
|
package/src/tracking.js
CHANGED
|
@@ -160,14 +160,14 @@ function rpad(s, n) {
|
|
|
160
160
|
function report() {
|
|
161
161
|
const entries = readEntries();
|
|
162
162
|
if (!entries.length) {
|
|
163
|
-
return '
|
|
163
|
+
return 'lakonai: no usage recorded yet. Run a few commands through `lakonai` first.\n';
|
|
164
164
|
}
|
|
165
165
|
|
|
166
166
|
const lines = [];
|
|
167
167
|
const W = byWindow(entries, WEEK_MS);
|
|
168
168
|
const headlinePct = pct(W.saved, W.raw);
|
|
169
169
|
lines.push(
|
|
170
|
-
`${bold('
|
|
170
|
+
`${bold('lakonai')} ${dim('— savings this week:')} ` +
|
|
171
171
|
`${green(tok(W.saved))} ${dim('saved across')} ${W.calls} ${dim('shell calls')} ${green(`(${headlinePct}%)`)}`
|
|
172
172
|
);
|
|
173
173
|
lines.push('');
|