claude-yaad 0.0.0-stage → 1.0.1
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/LICENSE +21 -0
- package/README.md +116 -3
- package/bin/install.js +108 -0
- package/claude-ai/kharcha.zip +0 -0
- package/claude-ai/save.zip +0 -0
- package/claude-ai/yaad.zip +0 -0
- package/kit/scripts/ck_state.js +126 -0
- package/kit/skills/kharcha/SKILL.md +23 -0
- package/kit/skills/save/SKILL.md +46 -0
- package/kit/skills/yaad/SKILL.md +27 -0
- package/package.json +33 -5
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 rahmangamer12
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,116 @@
|
|
|
1
|
-
#
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
# claude-yaad
|
|
2
|
+
|
|
3
|
+
**Session memory and usage savers for Claude Code. One command, zero tokens.**
|
|
4
|
+
|
|
5
|
+
*Yaad* means "memory" in Urdu. Claude Code forgets everything on `/clear`, and long sessions burn your usage limit because every message re-sends the whole conversation. `claude-yaad` fixes both:
|
|
6
|
+
|
|
7
|
+
- **Each project remembers where it was.** A fresh session (after `/clear`, a compact, a crash or a new day) starts by reading that project's own small state file.
|
|
8
|
+
- **Long sessions get flagged.** You get a one-line warning when the context passes ~200k tokens, so you can `/save` and `/clear` before every message gets expensive.
|
|
9
|
+
- **Sessions never grow huge.** Auto-compact runs at a 150k-token window, so a long chat can't creep to 200k+ tokens re-read on every message (this was the single biggest usage drain we measured: one chat re-read 216 million tokens in a night). Your state file keeps the continuity after each compact.
|
|
10
|
+
- **Subagents default to Sonnet** instead of the main model. Pass a model explicitly when one piece really needs the big one.
|
|
11
|
+
- **Three skills:** `/save`, `/yaad` and `/kharcha` (find what is eating your usage).
|
|
12
|
+
|
|
13
|
+
The hooks are plain Node scripts. They read only the tail of the transcript, finish in under a second, and print nothing unless there is something to say. They cost **zero model tokens** except the short state they print at session start.
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## Install
|
|
18
|
+
|
|
19
|
+
You need **Node.js 18+** and **Claude Code**.
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
npx claude-yaad
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Not on npm yet? Install straight from GitHub; it works the same:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
npx github:rahmangamer12/claude-yaad
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Then **restart Claude Code**. That's it.
|
|
32
|
+
|
|
33
|
+
What the installer does:
|
|
34
|
+
|
|
35
|
+
| | Where |
|
|
36
|
+
|---|---|
|
|
37
|
+
| Hook script `ck_state.js` | `~/.claude/scripts/` |
|
|
38
|
+
| Skills `save`, `yaad`, `kharcha` | `~/.claude/skills/` (an older skill with the same name is moved to `~/.claude/skill-backups/`) |
|
|
39
|
+
| Hooks: SessionStart, PreCompact, SessionEnd, UserPromptSubmit | `~/.claude/settings.json` (backed up first as `settings.json.bak-claude-kit-<time>`) |
|
|
40
|
+
| `CLAUDE_CODE_AUTO_COMPACT_WINDOW=150k` | `env` in `~/.claude/settings.json`; skipped if you already set a window, or with `--keep-compact-window` |
|
|
41
|
+
| `CLAUDE_CODE_SUBAGENT_MODEL=sonnet` | `env` in `~/.claude/settings.json`; skipped if you already set one, or with `--keep-subagent-model` |
|
|
42
|
+
|
|
43
|
+
Your other hooks, settings and skills are left alone. Running it again updates the kit in place.
|
|
44
|
+
|
|
45
|
+
### Options
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
npx claude-yaad --keep-subagent-model # do not touch the subagent model
|
|
49
|
+
npx claude-yaad --keep-compact-window # do not touch the auto-compact window
|
|
50
|
+
npx claude-yaad --zips # put the skill zips for claude.ai in the current folder
|
|
51
|
+
npx claude-yaad --uninstall # remove the hooks and the script (keeps skills and state)
|
|
52
|
+
npx claude-yaad --uninstall --purge # also remove the three skills
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
## Daily use
|
|
58
|
+
|
|
59
|
+
| You want to… | Do this |
|
|
60
|
+
|---|---|
|
|
61
|
+
| Stop for now, or `/clear` | `/save`, then `/clear`. Nothing is lost. |
|
|
62
|
+
| Pick up where you left off | Just start. The state shows at the top of the session. Ask `/yaad` for a short summary. |
|
|
63
|
+
| The hook says the context is heavy | Finish the current step, `/save`, `/clear`. |
|
|
64
|
+
| Usage limit going too fast | `/kharcha`: it checks the usual culprits and fixes the safe ones. |
|
|
65
|
+
|
|
66
|
+
### How the memory works
|
|
67
|
+
|
|
68
|
+
One file per project: `~/.claude/state/projects/<project-slug>.md`. The slug is the working directory with every character that is not a letter or digit turned into `-`, e.g. `F:\Work\Shop Site` → `F--Work-Shop-Site.md`.
|
|
69
|
+
|
|
70
|
+
The file has two parts:
|
|
71
|
+
|
|
72
|
+
1. **Your part, written by `/save`:** what we are doing, key paths, done, next, the commands to run, and gotchas. 60 lines max.
|
|
73
|
+
2. **The auto part, written by the hook** on every compact and session end: your last few requests, the files edited, and the last reply. It needs no model call, so it is free.
|
|
74
|
+
|
|
75
|
+
Because every project has its own file, two Claude windows on two projects never overwrite each other's memory.
|
|
76
|
+
|
|
77
|
+
### Settings you can change
|
|
78
|
+
|
|
79
|
+
Set these as environment variables, or in `env` in `settings.json`:
|
|
80
|
+
|
|
81
|
+
| Variable | Default | Meaning |
|
|
82
|
+
|---|---|---|
|
|
83
|
+
| `CK_WARN_TOKENS` | `200000` | Warn when the context passes this many tokens |
|
|
84
|
+
| `CK_SHOW_CHARS` | `3500` | How much of the state file to show at session start |
|
|
85
|
+
|
|
86
|
+
---
|
|
87
|
+
|
|
88
|
+
## claude.ai (web) and the Claude desktop app
|
|
89
|
+
|
|
90
|
+
Chat on claude.ai and in the desktop app has no hooks and no lasting disk, so only the skills apply there:
|
|
91
|
+
|
|
92
|
+
1. `npx claude-yaad --zips` puts `save.zip`, `yaad.zip` and `kharcha.zip` in your folder. They are also in the [`claude-ai/`](claude-ai) folder of this repo.
|
|
93
|
+
2. claude.ai → **Settings → Capabilities → Skills → Upload skill**, one zip at a time.
|
|
94
|
+
3. In chat, `/save` prints a state block. Paste it into your **Project instructions** (or the first message of the next chat), and `/yaad` reads it from there.
|
|
95
|
+
|
|
96
|
+
The **Claude Code tab** of the desktop app and the IDE extensions use the same `~/.claude` folder as the terminal, so the full kit works there after the normal install.
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
## Why this saves usage
|
|
101
|
+
|
|
102
|
+
- The biggest cost is **context length × number of messages**. A 400k-token session costs about 10× a 40k one *per message*. Cheap memory makes `/clear` painless, so you clear more.
|
|
103
|
+
- **Subagents are full extra sessions.** Running them on Sonnet by default cuts their cost a lot. Your main session keeps the model you chose.
|
|
104
|
+
- Older autosave hooks parsed the whole transcript, which can be hundreds of MB, and wrote one global file that every window overwrote. This reads the last 3 MB and keeps one file per project.
|
|
105
|
+
|
|
106
|
+
## Uninstall
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
npx claude-yaad --uninstall --purge
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Your state files in `~/.claude/state/projects/` are kept. Delete them by hand if you want.
|
|
113
|
+
|
|
114
|
+
## License
|
|
115
|
+
|
|
116
|
+
MIT
|
package/bin/install.js
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// claude-yaad installer: npx claude-yaad -> install / update
|
|
3
|
+
// npx claude-yaad --uninstall -> remove hooks + kit files (skills kept unless --purge)
|
|
4
|
+
// npx claude-yaad --zips -> copy claude.ai skill zips to the current folder
|
|
5
|
+
'use strict';
|
|
6
|
+
const fs = require('fs');
|
|
7
|
+
const os = require('os');
|
|
8
|
+
const path = require('path');
|
|
9
|
+
|
|
10
|
+
const ROOT = path.join(__dirname, '..');
|
|
11
|
+
const CLAUDE = path.join(os.homedir(), '.claude');
|
|
12
|
+
const SETTINGS = path.join(CLAUDE, 'settings.json');
|
|
13
|
+
const SCRIPT = path.join(CLAUDE, 'scripts', 'ck_state.js');
|
|
14
|
+
const SKILLS = ['save', 'yaad', 'kharcha'];
|
|
15
|
+
const args = process.argv.slice(2);
|
|
16
|
+
const has = (f) => args.includes(f);
|
|
17
|
+
const log = (s) => console.log(' ' + s);
|
|
18
|
+
|
|
19
|
+
function copyDir(src, dst) {
|
|
20
|
+
fs.mkdirSync(dst, { recursive: true });
|
|
21
|
+
for (const e of fs.readdirSync(src, { withFileTypes: true })) {
|
|
22
|
+
const s = path.join(src, e.name), d = path.join(dst, e.name);
|
|
23
|
+
e.isDirectory() ? copyDir(s, d) : fs.copyFileSync(s, d);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
function readSettings() {
|
|
27
|
+
try { return JSON.parse(fs.readFileSync(SETTINGS, 'utf8')); } catch (e) {
|
|
28
|
+
if (fs.existsSync(SETTINGS)) { console.error('settings.json is not valid JSON; fix it first: ' + SETTINGS); process.exit(1); }
|
|
29
|
+
return {};
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
function backup() {
|
|
33
|
+
if (!fs.existsSync(SETTINGS)) return;
|
|
34
|
+
const b = SETTINGS + '.bak-claude-kit-' + Date.now();
|
|
35
|
+
fs.copyFileSync(SETTINGS, b);
|
|
36
|
+
log('backup: ' + b);
|
|
37
|
+
}
|
|
38
|
+
const cmd = () => `node "${SCRIPT.replace(/\\/g, '/')}"`;
|
|
39
|
+
// Old hooks this kit replaces (the earlier python autosave and the global CURRENT.md loader).
|
|
40
|
+
const isOurs = (h) => /ck_state\.js|autosave\.py|state\/CURRENT\.md|state\\CURRENT\.md/.test((h && h.command) || '');
|
|
41
|
+
|
|
42
|
+
function stripOurs(s) {
|
|
43
|
+
for (const ev of Object.keys(s.hooks || {})) {
|
|
44
|
+
s.hooks[ev] = s.hooks[ev]
|
|
45
|
+
.map((g) => ({ ...g, hooks: (g.hooks || []).filter((h) => !isOurs(h)) }))
|
|
46
|
+
.filter((g) => g.hooks.length);
|
|
47
|
+
if (!s.hooks[ev].length) delete s.hooks[ev];
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
function addHook(s, ev, matcher, timeout) {
|
|
51
|
+
s.hooks = s.hooks || {};
|
|
52
|
+
s.hooks[ev] = s.hooks[ev] || [];
|
|
53
|
+
const g = { hooks: [{ type: 'command', command: cmd(), timeout }] };
|
|
54
|
+
if (matcher) g.matcher = matcher;
|
|
55
|
+
s.hooks[ev].unshift(g);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (has('--zips')) {
|
|
59
|
+
const src = path.join(ROOT, 'claude-ai');
|
|
60
|
+
for (const f of fs.readdirSync(src)) fs.copyFileSync(path.join(src, f), path.join(process.cwd(), f));
|
|
61
|
+
console.log('Skill zips copied here. claude.ai → Settings → Capabilities → Skills → Upload skill (one zip at a time).');
|
|
62
|
+
process.exit(0);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (has('--uninstall')) {
|
|
66
|
+
const s = readSettings(); backup(); stripOurs(s);
|
|
67
|
+
if (s.env && s.env.CLAUDE_CODE_SUBAGENT_MODEL === 'sonnet') delete s.env.CLAUDE_CODE_SUBAGENT_MODEL;
|
|
68
|
+
if (s.env && s.env.CLAUDE_CODE_AUTO_COMPACT_WINDOW === '150k') delete s.env.CLAUDE_CODE_AUTO_COMPACT_WINDOW;
|
|
69
|
+
fs.writeFileSync(SETTINGS, JSON.stringify(s, null, 2));
|
|
70
|
+
try { fs.unlinkSync(SCRIPT); } catch {}
|
|
71
|
+
if (has('--purge')) for (const k of SKILLS) fs.rmSync(path.join(CLAUDE, 'skills', k), { recursive: true, force: true });
|
|
72
|
+
console.log('claude-kit removed. Your state files in ~/.claude/state/projects are kept.');
|
|
73
|
+
process.exit(0);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
console.log('claude-kit: installing into ' + CLAUDE);
|
|
77
|
+
copyDir(path.join(ROOT, 'kit', 'scripts'), path.join(CLAUDE, 'scripts'));
|
|
78
|
+
log('hook script: ' + SCRIPT);
|
|
79
|
+
for (const k of SKILLS) {
|
|
80
|
+
const dst = path.join(CLAUDE, 'skills', k);
|
|
81
|
+
if (fs.existsSync(dst) && !has('--force')) {
|
|
82
|
+
const old = path.join(CLAUDE, 'skill-backups', k + '-' + Date.now());
|
|
83
|
+
fs.mkdirSync(path.dirname(old), { recursive: true });
|
|
84
|
+
fs.renameSync(dst, old);
|
|
85
|
+
log(`skill ${k}: previous version kept at ${old}`);
|
|
86
|
+
}
|
|
87
|
+
copyDir(path.join(ROOT, 'kit', 'skills', k), dst);
|
|
88
|
+
log('skill: /' + k);
|
|
89
|
+
}
|
|
90
|
+
const s = readSettings(); backup(); stripOurs(s);
|
|
91
|
+
addHook(s, 'SessionStart', 'startup|clear|compact|resume', 10);
|
|
92
|
+
addHook(s, 'PreCompact', 'auto|manual', 20);
|
|
93
|
+
addHook(s, 'SessionEnd', null, 20);
|
|
94
|
+
addHook(s, 'UserPromptSubmit', null, 10);
|
|
95
|
+
s.env = s.env || {};
|
|
96
|
+
if (!has('--keep-subagent-model') && !s.env.CLAUDE_CODE_SUBAGENT_MODEL) {
|
|
97
|
+
s.env.CLAUDE_CODE_SUBAGENT_MODEL = 'sonnet';
|
|
98
|
+
log('subagents default to Sonnet (pass a model explicitly for hard work)');
|
|
99
|
+
}
|
|
100
|
+
if (!has('--keep-compact-window') && !s.env.CLAUDE_CODE_AUTO_COMPACT_WINDOW && !s.autoCompactWindow) {
|
|
101
|
+
s.env.CLAUDE_CODE_AUTO_COMPACT_WINDOW = '150k';
|
|
102
|
+
log('auto-compact window 150k: sessions never grow huge, so every message stays cheap');
|
|
103
|
+
}
|
|
104
|
+
fs.mkdirSync(CLAUDE, { recursive: true });
|
|
105
|
+
fs.writeFileSync(SETTINGS, JSON.stringify(s, null, 2));
|
|
106
|
+
log('hooks: SessionStart, PreCompact, SessionEnd, UserPromptSubmit');
|
|
107
|
+
console.log('\nDone. Restart Claude Code. Use /save before /clear, /yaad to pick up, /kharcha to find waste.');
|
|
108
|
+
console.log('claude.ai web / Claude desktop chat: run npx claude-yaad --zips and upload the zips there.');
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// claude-kit: zero-token session memory for Claude Code.
|
|
3
|
+
// One state file per project: ~/.claude/state/projects/<project-slug>.md
|
|
4
|
+
// SessionStart -> prints that file (capped) so a fresh session knows where we were
|
|
5
|
+
// PreCompact/SessionEnd -> rewrites only the AUTO part (last asks, files touched, last reply)
|
|
6
|
+
// UserPromptSubmit -> one-line warning when the context is getting expensive
|
|
7
|
+
// It reads only the tail of the transcript, so it stays fast on 200 MB transcripts.
|
|
8
|
+
'use strict';
|
|
9
|
+
const fs = require('fs');
|
|
10
|
+
const os = require('os');
|
|
11
|
+
const path = require('path');
|
|
12
|
+
|
|
13
|
+
const HOME = os.homedir();
|
|
14
|
+
const DIR = path.join(HOME, '.claude', 'state', 'projects');
|
|
15
|
+
const MARK = '<!-- AUTO: written by claude-kit hook, everything below is replaced -->';
|
|
16
|
+
const WARN = parseInt(process.env.CK_WARN_TOKENS || '200000', 10);
|
|
17
|
+
const SHOW = parseInt(process.env.CK_SHOW_CHARS || '3500', 10);
|
|
18
|
+
const TAIL = 3 * 1024 * 1024;
|
|
19
|
+
|
|
20
|
+
function readStdin() {
|
|
21
|
+
try { return JSON.parse(fs.readFileSync(0, 'utf8') || '{}'); } catch { return {}; }
|
|
22
|
+
}
|
|
23
|
+
const slug = (p) => String(p || process.cwd()).replace(/[^A-Za-z0-9]/g, '-');
|
|
24
|
+
const stamp = () => {
|
|
25
|
+
const d = new Date(), z = (n) => String(n).padStart(2, '0');
|
|
26
|
+
return `${d.getFullYear()}-${z(d.getMonth() + 1)}-${z(d.getDate())} ${z(d.getHours())}:${z(d.getMinutes())}`;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
function tailLines(file) {
|
|
30
|
+
try {
|
|
31
|
+
const size = fs.statSync(file).size;
|
|
32
|
+
const len = Math.min(size, TAIL);
|
|
33
|
+
const fd = fs.openSync(file, 'r');
|
|
34
|
+
const buf = Buffer.alloc(len);
|
|
35
|
+
fs.readSync(fd, buf, 0, len, size - len);
|
|
36
|
+
fs.closeSync(fd);
|
|
37
|
+
const lines = buf.toString('utf8').split('\n');
|
|
38
|
+
if (size > len) lines.shift(); // first line is probably cut
|
|
39
|
+
return lines;
|
|
40
|
+
} catch { return []; }
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function parse(lines) {
|
|
44
|
+
const asks = [], replies = [], files = [];
|
|
45
|
+
let tokens = 0;
|
|
46
|
+
for (const raw of lines) {
|
|
47
|
+
let m; try { m = JSON.parse(raw); } catch { continue; }
|
|
48
|
+
const msg = m.message || {};
|
|
49
|
+
if (msg.usage) {
|
|
50
|
+
const u = msg.usage;
|
|
51
|
+
tokens = (u.input_tokens || 0) + (u.cache_read_input_tokens || 0) + (u.cache_creation_input_tokens || 0);
|
|
52
|
+
}
|
|
53
|
+
const c = msg.content;
|
|
54
|
+
if (m.type === 'user' && !m.isMeta) {
|
|
55
|
+
let t = typeof c === 'string' ? c
|
|
56
|
+
: Array.isArray(c) && !c.some((x) => x && x.type === 'tool_result')
|
|
57
|
+
? c.filter((x) => x && x.type === 'text').map((x) => x.text).join(' ') : '';
|
|
58
|
+
t = (t || '').trim();
|
|
59
|
+
if (t && !t.startsWith('<') && !t.startsWith('[Request interrupted') && !t.startsWith('Caveat:'))
|
|
60
|
+
asks.push(t.replace(/\s+/g, ' ').slice(0, 280));
|
|
61
|
+
} else if (m.type === 'assistant' && Array.isArray(c)) {
|
|
62
|
+
for (const x of c) {
|
|
63
|
+
if (x && x.type === 'text' && x.text && x.text.trim()) replies.push(x.text.trim());
|
|
64
|
+
if (x && x.type === 'tool_use' && /^(Write|Edit|MultiEdit|NotebookEdit)$/.test(x.name) && x.input) {
|
|
65
|
+
const f = x.input.file_path || x.input.notebook_path;
|
|
66
|
+
if (f) { const i = files.indexOf(f); if (i >= 0) files.splice(i, 1); files.push(f); }
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return { asks, replies, files, tokens };
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function stateFile(cwd) {
|
|
75
|
+
fs.mkdirSync(DIR, { recursive: true });
|
|
76
|
+
return path.join(DIR, slug(cwd) + '.md');
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const d = readStdin();
|
|
80
|
+
const ev = d.hook_event_name || process.argv[2] || '';
|
|
81
|
+
const cwd = d.cwd || process.cwd();
|
|
82
|
+
const file = stateFile(cwd);
|
|
83
|
+
|
|
84
|
+
if (ev === 'SessionStart') {
|
|
85
|
+
let txt = '';
|
|
86
|
+
try { txt = fs.readFileSync(file, 'utf8'); } catch {}
|
|
87
|
+
console.log(`[claude-kit] Is project ki state file: ${file}`);
|
|
88
|
+
console.log('/save isi file ka upar wala hissa likhta hai; /yaad isi ko padhta hai.');
|
|
89
|
+
if (txt.trim()) {
|
|
90
|
+
console.log('=== Pichli dafa kahan the ===');
|
|
91
|
+
console.log(txt.length > SHOW ? txt.slice(0, SHOW) + '\n…(baqi file mein)' : txt);
|
|
92
|
+
}
|
|
93
|
+
process.exit(0);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const lines = d.transcript_path ? tailLines(d.transcript_path) : [];
|
|
97
|
+
|
|
98
|
+
if (ev === 'UserPromptSubmit') {
|
|
99
|
+
const { tokens } = parse(lines.slice(-400));
|
|
100
|
+
if (tokens > WARN)
|
|
101
|
+
console.log(`[claude-kit] Context ~${Math.round(tokens / 1000)}k tokens: har message mehnga ho raha hai. Ye kaam khatam kar ke /save phir /clear karo (state yaad rahegi).`);
|
|
102
|
+
process.exit(0);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (ev === 'PreCompact' || ev === 'SessionEnd' || ev === 'Stop') {
|
|
106
|
+
const { asks, replies, files } = parse(lines);
|
|
107
|
+
if (!asks.length && !replies.length) process.exit(0);
|
|
108
|
+
let hand = '';
|
|
109
|
+
try { hand = fs.readFileSync(file, 'utf8').split(MARK)[0].trimEnd(); } catch {}
|
|
110
|
+
const last = (replies[replies.length - 1] || '').slice(0, 1200);
|
|
111
|
+
const auto = [
|
|
112
|
+
MARK,
|
|
113
|
+
`## Auto — ${stamp()} (${ev})`,
|
|
114
|
+
'Last asks:',
|
|
115
|
+
...asks.slice(-4).map((a) => '- ' + a),
|
|
116
|
+
files.length ? 'Files touched:' : '',
|
|
117
|
+
...files.slice(-12).map((f) => '- ' + f),
|
|
118
|
+
'Last reply:',
|
|
119
|
+
last,
|
|
120
|
+
].filter((l) => l !== '').join('\n');
|
|
121
|
+
fs.writeFileSync(file, (hand ? hand + '\n\n' : '') + auto + '\n');
|
|
122
|
+
try {
|
|
123
|
+
fs.appendFileSync(path.join(HOME, '.claude', 'state', 'LOG.md'), `- ${stamp()}: auto-save ${ev} → ${path.basename(file)}\n`);
|
|
124
|
+
} catch {}
|
|
125
|
+
}
|
|
126
|
+
process.exit(0);
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: kharcha
|
|
3
|
+
description: Tokens ka kharcha kahan ja raha hai — find and fix what is eating usage and the context window. Use when the user says usage zyada hai, weekly limit khatam ho rahi hai, tokens bach jayein, kharcha zyada hai, context bhar gaya, kaunsa MCP band karun, or hits a compact.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# /kharcha — cut the fixed cost, not the useful work
|
|
7
|
+
|
|
8
|
+
Measure before prescribing. A fix that saves nothing still costs the user something.
|
|
9
|
+
|
|
10
|
+
## 1. The big four (check these first — they are usually 90% of the bill)
|
|
11
|
+
1. **Long sessions.** Every message re-sends the whole context. A 400k-token session costs ~10x a 40k one *per message*. Fix: `/save` then `/clear` between unrelated jobs; one session per project. The claude-kit hook warns when the context passes ~200k.
|
|
12
|
+
2. **Parallel workers and subagents.** Each background session or Agent is a full extra context. Fix: stop finished workers; no Agent/Explore for anything a `grep` answers; run subagents on a smaller model (`CLAUDE_CODE_SUBAGENT_MODEL=sonnet` in settings `env`, already set by claude-kit) and pass a bigger model only for the hard piece.
|
|
13
|
+
3. **Model and effort.** Opus/Fable at high effort for routine edits is the most expensive habit. Use Sonnet for normal coding, Haiku for mechanical work, `/model` to switch; keep effort medium for routine work.
|
|
14
|
+
4. **Screenshots and big tool output.** An image costs more than a page of text; a 2,000-line log dump stays in context forever. Cap output with `head`/`tail`; take a screenshot only when you will act on it.
|
|
15
|
+
|
|
16
|
+
## 2. MCP servers — check whether they are deferred
|
|
17
|
+
Run `/context`. If MCP tools show as **deferred**, only their names are loaded: disabling servers saves almost nothing, so say so and do not push a cleanup. If they are **loaded** (tens of thousands of tokens): delete dead servers (`claude mcp remove <name>`), scope servers to the project that needs them, prefer a CLI (`gh`, `curl`, `ffmpeg`) over an MCP server. Always name a server and ask before removing it.
|
|
18
|
+
|
|
19
|
+
## 3. Things that reload every turn
|
|
20
|
+
CLAUDE.md, MEMORY.md, and any hook that prints text on every prompt. Keep them short; a hook should print only when it has something to say.
|
|
21
|
+
|
|
22
|
+
## Reporting
|
|
23
|
+
A table: item · what it costs · fix · worth doing (yes/no). Then do the safe fixes, and ask before removing any server, skill or worker.
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: save
|
|
3
|
+
description: Kaam ka state save karo taake /clear, compact ya band karne par kuch na khoye. Use when the user says save karo, clear karne laga hoon, band kar raha hoon, yaad rakho kahan the, handoff, bas aaj ke liye — and proactively when a piece of work reaches a natural stopping point, before a long or risky operation, or when a hook says the context is getting heavy.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# /save — freeze this project's state in one small file
|
|
7
|
+
|
|
8
|
+
## Where to write
|
|
9
|
+
|
|
10
|
+
**Claude Code (terminal / desktop / IDE):** the project's own state file. The SessionStart hook prints its path at the top of every session ("Is project ki state file: …"). If you cannot see it, build it yourself:
|
|
11
|
+
`~/.claude/state/projects/<slug>.md`, where `<slug>` is the working directory with every non-letter/digit replaced by `-`
|
|
12
|
+
(e.g. `F:\Work\Shop Site` → `F--Work-Shop-Site.md`).
|
|
13
|
+
|
|
14
|
+
The file has two parts:
|
|
15
|
+
- **Top part (yours):** everything above the line `<!-- AUTO: written by claude-kit hook ... -->`. Replace it completely each time.
|
|
16
|
+
- **Auto part (the hook's):** that marker line and everything below it. Never edit it, and keep it in place: the hook rewrites it on its own.
|
|
17
|
+
If the file does not exist, just write the top part.
|
|
18
|
+
|
|
19
|
+
**claude.ai web / Claude desktop chat (no file system that survives):** do not write a file. Print the same block in a code box and tell the user: "Ye block Project ke instructions/knowledge mein paste kar do, ya naye chat ke pehle message mein." That is the only memory that survives there.
|
|
20
|
+
|
|
21
|
+
## What goes in (60 lines maximum, hard cap)
|
|
22
|
+
|
|
23
|
+
Use these headings. Skip an empty heading entirely.
|
|
24
|
+
|
|
25
|
+
```markdown
|
|
26
|
+
# State — <YYYY-MM-DD HH:MM>
|
|
27
|
+
## Doing
|
|
28
|
+
<one line: the task, for whom>
|
|
29
|
+
## Where
|
|
30
|
+
<absolute paths that matter, one per line, max 5>
|
|
31
|
+
## Done
|
|
32
|
+
<finished things, one line each, newest last>
|
|
33
|
+
## Next
|
|
34
|
+
<the next action, written so it can start without asking anything>
|
|
35
|
+
## Run
|
|
36
|
+
<exact commands to build / test / render>
|
|
37
|
+
## Gotchas
|
|
38
|
+
<only things that already bit us: a flag, a path, a thing not to touch>
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Rules
|
|
42
|
+
- Facts, not story. Absolute paths. Next must be doable without a question.
|
|
43
|
+
- Long-lived facts (a client's rules, how a tool works) belong in memory, not here.
|
|
44
|
+
- Trim: drop finished items nobody will need again; cut the oldest Done lines first.
|
|
45
|
+
|
|
46
|
+
After writing, tell the user in one line that it is saved and `/clear` is safe. Do not print the file back.
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: yaad
|
|
3
|
+
description: Yaad karo hum kahan the — restore working context cheaply after a /clear, a compact, a crash or a new day. Answers kahan the hum, aaj kya kiya tha, continue karo, where were we, what was I doing. Reads one small file and stops. Use instead of searching the disk to rebuild context.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# /yaad — get oriented for the fewest tokens possible
|
|
7
|
+
|
|
8
|
+
The point of this skill is to **stop early**.
|
|
9
|
+
|
|
10
|
+
## Step 1 — the state is probably already in context
|
|
11
|
+
In Claude Code the SessionStart hook already printed this project's state ("=== Pichli dafa kahan the ==="). If you can see it, answer from it: 3-6 lines on what we were doing, what is done, what is next. Then ask whether to continue. Open nothing.
|
|
12
|
+
|
|
13
|
+
## Step 2 — read the file once
|
|
14
|
+
If it is not in context, read `~/.claude/state/projects/<slug>.md` (`<slug>` = working directory with every non-letter/digit replaced by `-`). Top part = the last /save; the AUTO part = the hook's note of the last asks, files touched and last reply. Answer from it and stop.
|
|
15
|
+
|
|
16
|
+
## Step 3 — only if the file is missing or stale (older than ~2 days)
|
|
17
|
+
Stop at the first one that answers:
|
|
18
|
+
1. The last lines of `~/.claude/state/LOG.md`.
|
|
19
|
+
2. The 15 newest files in the working directory (`ls -lt | head -15`).
|
|
20
|
+
|
|
21
|
+
**Never** run a repo-wide grep, a recursive find, or an Agent/Explore search to answer "where were we". That costs more than the conversation you lost.
|
|
22
|
+
|
|
23
|
+
## claude.ai web / Claude desktop chat
|
|
24
|
+
There is no state file. Look for a "# State —" block in the Project instructions/knowledge or the first message, and answer from it. If there is none, say so in one line and ask the user to paste their last /save block.
|
|
25
|
+
|
|
26
|
+
## Then
|
|
27
|
+
If the state was missing or wrong, write a fresh one with /save before starting work.
|
package/package.json
CHANGED
|
@@ -1,6 +1,34 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "claude-yaad",
|
|
3
|
-
"version": "
|
|
4
|
-
"
|
|
5
|
-
"
|
|
1
|
+
{
|
|
2
|
+
"name": "claude-yaad",
|
|
3
|
+
"version": "1.0.1",
|
|
4
|
+
"description": "Zero-token session memory and usage savers for Claude Code: per-project state that survives /clear and compaction, /save /yaad /kharcha skills, context-size warning, Sonnet subagents.",
|
|
5
|
+
"bin": {
|
|
6
|
+
"claude-yaad": "bin/install.js"
|
|
7
|
+
},
|
|
8
|
+
"files": [
|
|
9
|
+
"bin",
|
|
10
|
+
"kit",
|
|
11
|
+
"claude-ai",
|
|
12
|
+
"README.md",
|
|
13
|
+
"LICENSE"
|
|
14
|
+
],
|
|
15
|
+
"engines": {
|
|
16
|
+
"node": ">=18"
|
|
17
|
+
},
|
|
18
|
+
"keywords": [
|
|
19
|
+
"claude",
|
|
20
|
+
"claude-code",
|
|
21
|
+
"skills",
|
|
22
|
+
"hooks",
|
|
23
|
+
"tokens"
|
|
24
|
+
],
|
|
25
|
+
"license": "MIT",
|
|
26
|
+
"repository": {
|
|
27
|
+
"type": "git",
|
|
28
|
+
"url": "git+https://github.com/rahmangamer12/claude-yaad.git"
|
|
29
|
+
},
|
|
30
|
+
"homepage": "https://github.com/rahmangamer12/claude-yaad#readme",
|
|
31
|
+
"bugs": {
|
|
32
|
+
"url": "https://github.com/rahmangamer12/claude-yaad/issues"
|
|
33
|
+
}
|
|
6
34
|
}
|