Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 22 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,28 @@ This log starts at v1.6.5 (the reliability-pillar repositioning). Earlier histor

## [Unreleased]

_Nothing landed since [1.6.15]._
_Nothing landed since [1.6.16]._

---

## [1.6.16] — 2026-06-22

F-CACHE-DEFER + F-NATIVE-GREP — the two P0 fixes from the 2026-06-17 dogfood backlog. Tests 1,317 → 1,363.

### Fixed
- **F-CACHE-DEFER: `sipcode init` no longer invalidates the prompt cache of an active Claude Code session.** v1.6.15's Verified Warm-Fill fixed Sipcode's own dedup cache, but a separate problem remained: writing `~/.claude/settings.json` mid-session forces Anthropic's prompt cache to reset, which can cost more in extra input tokens than Sipcode saves on tool output. Anuj's 2026-06-17 session: `sipcode drift` flagged "Cache reuse down 83 points" and the regression dwarfed the proxy savings on that day. v1.6.16 detects active Claude Code sessions before writing settings.json and defers the write to a pending marker if one exists; the next quiet `sipcode` command auto-applies. The hook script file itself is still written immediately (safe — does not invalidate the cache). Pass `--force` to install anyway when you want to bypass the check.
- **F-NATIVE-GREP: raised the `native-grep` cap from 50 to 100 matches.** The same dogfood session showed `native-grep` was 30% of all proxy work but had the lowest signal-kept ratio (65%). Symbol lookups in larger codebases routinely returned 50–100 matches Claude needed for follow-up reads; the 50-cap was too aggressive. Doubling the cap restores most of that signal while still bounding pathological greps. Integrity declaration moves from 0.65 to 0.78.

### Added
- **`src/modules/init/sessionDetection.ts`** — pure module that scans `~/.claude/projects/<proj>/sessions/<sid>.jsonl` for files modified within the threshold (default 5 min = Anthropic's prompt-cache TTL). 13 tests cover empty layouts, single recent/stale detection, multi-project counts, permission failures, race-condition stat-returns-null, non-jsonl filtering, and custom thresholds.
- **`src/modules/init/pendingInstall.ts`** — marker module at `~/.sipcode/install-pending.json` (schema `sipcode-install-pending/1`). Strict version validation rejects unknown future schemas rather than mis-applying. `applyPendingInstall` regenerates the proxy hook script at apply time (so users always get the latest) and applies `installProxyHook` against the CURRENT settings.json (preserving any user-managed hook entries). Idempotent: a second apply finds no marker and no-ops.
- **`maybeApplyPendingInstall`** — CLI startup wrapper. Wired into `cli.ts` via a Commander `preAction` hook for every command except `init`. Fast no-op when no marker; skips when an active session is detected (cache safety); applies when safe and logs a single line. Hook failures never block the user's actual command.
- **`--force` flag on `sipcode init`.** Bypasses F-CACHE-DEFER active-session detection and installs settings.json directly, invalidating the active session's prompt cache. Use when you want the install now and accept the cost (e.g. on a fresh machine where the "active session" is the one you just opened to install Sipcode).
- **New `StepStatus` variant `{ kind: "deferred"; reason: string }`** for the v1.6.15 SETUP card. Renders with the ⏸ glyph and a specific footer ("auto-applies on your next sipcode command outside an active session, or pass --force") so users understand what happened and what to do next.

### Engineering
- Test count: 1,317 → 1,363 (46 new tests). Branch `v1.6.16-fixes` ships as one PR for review surface; full suite green every commit.
- Branch and commit trail: each step (detection module / pending-install module / runSystemSetup integration / CLI auto-apply / nativeGrep tune) ships as a separate commit with the related tests included, so any single step can be reverted in isolation.

---

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
<p align="center">
<a href="https://www.npmjs.com/package/sipcode"><img src="https://img.shields.io/npm/v/sipcode?color=5B4FCF&label=npm" alt="npm" /></a>
<a href="https://github.com/Anuj7411/sipcode/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-MIT-5B4FCF" alt="MIT licensed" /></a>
<img src="https://img.shields.io/badge/tests-1%2C317%20passing-28C840" alt="1317 tests passing" />
<img src="https://img.shields.io/badge/tests-1%2C363%20passing-28C840" alt="1363 tests passing" />
<img src="https://img.shields.io/badge/network%20calls-0-2D3142" alt="zero network calls" />
</p>

Expand Down
4 changes: 2 additions & 2 deletions docs/site/public/llms-full.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Sipcode is a context-hygiene proxy for [Claude Code](https://www.anthropic.com/c

On a locked, public 20-task benchmark corpus, Sipcode delivers 62.6% median tool-output savings (range 37.4% to 80.6%), totalling 3,567,170 tokens saved and $67.43 at current Claude Sonnet pricing. Anyone can reproduce these numbers with `sipcode benchmark`. Anthropic's published research finds cleaner context gives a 29% quality lift and 40% fewer agent errors, which is the mechanism Sipcode targets.

Sipcode is solo-maintained, MIT licensed, has 1,317 passing tests, and makes zero network calls during normal use. A privacy test in the repo fails the build if any `node:http`, `node:https`, `node:net`, or `node:dns` import is added to `src/`. Your code and transcripts never leave your laptop.
Sipcode is solo-maintained, MIT licensed, has 1,363 passing tests, and makes zero network calls during normal use. A privacy test in the repo fails the build if any `node:http`, `node:https`, `node:net`, or `node:dns` import is added to `src/`. Your code and transcripts never leave your laptop.

## How Sipcode differs from neighboring tools

Expand Down Expand Up @@ -140,7 +140,7 @@ Verify: `node --version` should show v18.0.0 or higher.
npm i -g sipcode
```

Verify: `sipcode --version` should show 1.6.15 or higher.
Verify: `sipcode --version` should show 1.6.16 or higher.

### Step 3. Run `sipcode init`

Expand Down
6 changes: 3 additions & 3 deletions docs/site/public/llms.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Sipcode is a context-hygiene proxy for [Claude Code](https://www.anthropic.com/c

On a locked, public 20-task benchmark corpus, Sipcode delivers 62.6% median tool-output savings (range 37.4% to 80.6%), totalling 3,567,170 tokens saved and $67.43 at current Claude Sonnet pricing. Anyone can reproduce these numbers with `sipcode benchmark`. Anthropic's published research finds cleaner context gives a 29% quality lift and 40% fewer agent errors, which is the mechanism Sipcode targets.

Sipcode is solo-maintained, MIT licensed, has 1,317 passing tests, and makes zero network calls during normal use. A privacy test in the repo fails the build if any `node:http`, `node:https`, `node:net`, or `node:dns` import is added to `src/`. Your code and transcripts never leave your laptop.
Sipcode is solo-maintained, MIT licensed, has 1,363 passing tests, and makes zero network calls during normal use. A privacy test in the repo fails the build if any `node:http`, `node:https`, `node:net`, or `node:dns` import is added to `src/`. Your code and transcripts never leave your laptop.

## How Sipcode differs from neighboring tools

Expand Down Expand Up @@ -53,8 +53,8 @@ Sipcode ships an MCP server exposing 15 tools so Claude Code can introspect its

## Source code

- [GitHub repository](https://github.com/Anuj7411/sipcode): MIT licensed source, 1,317 tests
- [npm package](https://www.npmjs.com/package/sipcode): `sipcode` on npm, current version 1.6.15
- [GitHub repository](https://github.com/Anuj7411/sipcode): MIT licensed source, 1,363 tests
- [npm package](https://www.npmjs.com/package/sipcode): `sipcode` on npm, current version 1.6.16
- [Privacy test](https://github.com/Anuj7411/sipcode/blob/main/src): build-blocking check that no network modules are imported in `src/`

## License
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "sipcode",
"version": "1.6.15",
"version": "1.6.16",
"description": "Sip your tokens, don't gulp them. Keep Claude Code's context clean: drift detection, re-read dedup, integrity scoring, AST-aware reads, and 15 MCP tools for Claude Desktop.",
"keywords": [
"claude-code",
Expand Down
94 changes: 94 additions & 0 deletions src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,100 @@ program
if (r?.exitCode) process.exit(r.exitCode);
});

// v1.6.16 F-CACHE-DEFER: before any command (except `init`, which manages
// its own pending state), check for a deferred install and auto-apply it
// when there is no active Claude Code session. Silent unless the apply
// actually changes something on disk.
program.hook("preAction", async (_thisCommand, actionCommand) => {
if (actionCommand.name() === "init") return; // init has its own gate

try {
const { homedir } = await import("node:os");
const { promises: fsp } = await import("node:fs");
const { detectActiveClaudeSessions } = await import(
"./modules/init/sessionDetection.js"
);
const { maybeApplyPendingInstall } = await import(
"./modules/init/pendingInstall.js"
);
const { generateProxyHookScript } = await import(
"./modules/proxy/proxyHookScript.js"
);
const {
runRewriterModuleUrl,
hookReadDedupModuleUrl,
hookAstReadModuleUrl,
} = await import("./modules/proxy/install.js");

const home = homedir();

await maybeApplyPendingInstall({
homeDir: home,
async detectActiveSessions(homeDir) {
return detectActiveClaudeSessions({
homeDir,
io: {
async listDir(p) {
return fsp.readdir(p);
},
async stat(p) {
try {
const s = await fsp.stat(p);
return { mtimeMs: s.mtimeMs, isDirectory: s.isDirectory() };
} catch {
return null;
}
},
now() {
return new Date();
},
},
});
},
pendingIO: {
async readFile(p) {
try {
return await fsp.readFile(p, "utf-8");
} catch {
return null;
}
},
async writeFile(p, content) {
await fsp.mkdir(
(await import("node:path")).dirname(p),
{ recursive: true },
);
await fsp.writeFile(p, content, "utf-8");
},
async deleteFile(p) {
try {
await fsp.unlink(p);
} catch {
// missing is fine
}
},
now() {
return new Date();
},
},
generateScript() {
return generateProxyHookScript(
runRewriterModuleUrl(),
hookReadDedupModuleUrl(),
hookAstReadModuleUrl(),
);
},
log(message) {
process.stderr.write(message + "\n");
},
});
} catch {
// The preAction hook is a pure ergonomic nicety. Any failure here must
// NEVER stop the user's actual command. Swallow and let the real action
// proceed.
}
});

program.parseAsync(process.argv).catch((err) => {
console.error(err);
process.exit(1);
Expand Down
Loading
Loading