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
13 changes: 11 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,15 @@ This log starts at v1.6.5 (the reliability-pillar repositioning). Earlier histor

## [Unreleased]

_Nothing landed since [1.6.19]._
_Nothing landed since [1.6.20]._

## [1.6.20] — 2026-07-04

### Added
- **`--here` now works on `today`, `forecast`, `trend`, and `impact`.** Previously only `why`, `stats`, and `receipt` accepted it; the others errored with "unknown option" — which is exactly what a user expects to work. All analytics/report commands now scope to the current project consistently, using the same shared project-hash filter. (`drift` already scopes to the latest session's project internally.)

### Internal
- Test count: 1,388 → 1,389 (added a `--here` scoping integration test that exercises the shared filter all five commands share). Full suite green.

## [1.6.19] — 2026-06-28

Expand Down Expand Up @@ -257,7 +265,8 @@ This release rolls v1.6.9's B3 work (bumped but never published to npm) together

---

[Unreleased]: https://github.com/Anuj7411/sipcode/compare/v1.6.19...HEAD
[Unreleased]: https://github.com/Anuj7411/sipcode/compare/v1.6.20...HEAD
[1.6.20]: https://github.com/Anuj7411/sipcode/compare/v1.6.19...v1.6.20
[1.6.19]: https://github.com/Anuj7411/sipcode/compare/v1.6.18...v1.6.19
[1.6.18]: https://github.com/Anuj7411/sipcode/compare/v1.6.17...v1.6.18
[1.6.17]: https://github.com/Anuj7411/sipcode/compare/v1.6.16...v1.6.17
Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
<a href="https://github.com/Anuj7411/sipcode/stargazers"><img src="https://img.shields.io/github/stars/Anuj7411/sipcode?color=5B4FCF&label=stars" alt="GitHub stars" /></a>
<a href="https://www.npmjs.com/package/sipcode"><img src="https://img.shields.io/npm/dm/sipcode?color=5B4FCF&label=downloads" alt="npm downloads per month" /></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%2C388%20passing-28C840" alt="1388 tests passing" />
<img src="https://img.shields.io/badge/tests-1%2C389%20passing-28C840" alt="1389 tests passing" />
<img src="https://img.shields.io/badge/network%20calls-0-2D3142" alt="zero network calls" />
</p>

Expand Down Expand Up @@ -179,7 +179,7 @@ Verify it installed:
sipcode --version
```

You should see `1.6.19` or higher.
You should see `1.6.20` or higher.

### Step 3. Run `sipcode init` to wire it into Claude Code

Expand Down Expand Up @@ -329,7 +329,7 @@ npm uninstall -g sipcode

Run any of them with `--help` for full options.

**Tip: which session do these report on?** `sipcode why` and `sipcode stats` look at your most recent session across **all** projects by default, not the folder you happen to be standing in. So if you run `sipcode why` inside project A but project B had the most recent activity, you will see project B. To scope either to the project you are currently in, add `--here` (for example, `sipcode why --here`). Use `sipcode why --list` to see every session and pick one with `--session <id>`. (`sipcode today` always summarizes all of today's sessions and has no `--here`.)
**Tip: which sessions do these report on?** By default, `why`, `stats`, `today`, `forecast`, `trend`, and `impact` look across **all** your projects, not just the folder you are standing in. So if you run `sipcode why` inside project A but project B had the most recent activity, you will see project B. To scope any of them to the project you are currently in, add `--here` (for example, `sipcode why --here` or `sipcode today --here`). Use `sipcode why --list` to see every session and pick a specific one with `--session <id>`.

---

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,388 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,389 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.19 or higher.
Verify: `sipcode --version` should show 1.6.20 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,388 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,389 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,388 tests
- [npm package](https://www.npmjs.com/package/sipcode): `sipcode` on npm, current version 1.6.19
- [GitHub repository](https://github.com/Anuj7411/sipcode): MIT licensed source, 1,389 tests
- [npm package](https://www.npmjs.com/package/sipcode): `sipcode` on npm, current version 1.6.20
- [Privacy test](https://github.com/Anuj7411/sipcode/blob/main/src): build-blocking check that no network modules are imported in `src/`

## License
Expand Down
4 changes: 2 additions & 2 deletions docs/site/src/components/Footer.astro
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,11 @@
<div class="badges">
<span class="badge">
<span class="badge-dot"></span>
v1.6.19
v1.6.20
</span>
<span class="badge">
<span class="badge-dot badge-dot--green"></span>
1,388 tests passing
1,389 tests passing
</span>
</div>
<a
Expand Down
6 changes: 3 additions & 3 deletions docs/site/src/components/Hero.astro
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ import PlotBackground from "./PlotBackground.astro";

// Tier-1 fix #2 — test count from the project root. Single source of truth.
// Update on each release alongside Hero/Footer version labels.
// Last bumped: 2026-06-28 (v1.6.19hardening: exit-code preservation, --here, agent-crash, flag fixes).
const TEST_COUNT = 1388;
// Last bumped: 2026-07-04 (v1.6.20--here now on today/forecast/trend/impact).
const TEST_COUNT = 1389;
---

<section class="hero">
Expand All @@ -19,7 +19,7 @@ const TEST_COUNT = 1388;

<div class="container">
<div class="hero-head-row">
<span class="hero-eyebrow"><span class="hero-tick"></span>v1.6.19 · MIT licensed</span>
<span class="hero-eyebrow"><span class="hero-tick"></span>v1.6.20 · MIT licensed</span>
<span class="hero-head-meta">{TEST_COUNT.toLocaleString()} tests passing</span>
</div>
</div>
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

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.19",
"version": "1.6.20",
"mcpName": "io.github.Anuj7411/sipcode",
"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": [
Expand Down
4 changes: 2 additions & 2 deletions server.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@
"url": "https://github.com/Anuj7411/sipcode",
"source": "github"
},
"version": "1.6.19",
"version": "1.6.20",
"packages": [
{
"registryType": "npm",
"identifier": "sipcode",
"version": "1.6.19",
"version": "1.6.20",
"runtimeHint": "npx",
"transport": {
"type": "stdio"
Expand Down
4 changes: 4 additions & 0 deletions src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ program
.description("Daily dashboard: spend so far + vs your N-day median (adaptive 30/14/7/3).")
.option("--json", "machine-readable output")
.option("--agent <id>", "claude-code | cursor | auto")
.option("--here", "scope to sessions for the current working directory")
.action(async (opts) => {
const { runTodayCmd } = await import("./commands/today.js");
const r = await runTodayCmd(opts);
Expand All @@ -163,6 +164,7 @@ program
.description("Projected month-end spend with confidence band + last-month comparison.")
.option("--json", "machine-readable output")
.option("--agent <id>", "claude-code | cursor | auto")
.option("--here", "scope to sessions for the current working directory")
.action(async (opts) => {
const { runForecastCmd } = await import("./commands/forecast.js");
const r = await runForecastCmd(opts);
Expand All @@ -176,6 +178,7 @@ program
.option("--since <window>", "time window: NNd | NNw | NNm (e.g. 30d, 4w, 3m)", "30d")
.option("--json", "machine-readable output")
.option("--agent <id>", "which agent to source transcripts from: claude-code | cursor | auto")
.option("--here", "scope to sessions for the current working directory")
.action(async (opts) => {
const { runTrend } = await import("./commands/trend.js");
const r = await runTrend(opts);
Expand Down Expand Up @@ -273,6 +276,7 @@ program
.option("--since <YYYY-MM-DD>", "override the install date (defaults to .sipcode/install-state.json)")
.option("--json", "machine-readable output")
.option("--agent <id>", "which agent to source transcripts from: claude-code | cursor | auto")
.option("--here", "scope to sessions for the current working directory")
.action(async (opts) => {
const { runImpactCommand } = await import("./commands/impact.js");
const r = await runImpactCommand(opts);
Expand Down
12 changes: 11 additions & 1 deletion src/commands/forecast.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { RealFileSystem, type FileSystem } from "../lib/fs.js";
import { RealClock, type Clock } from "../lib/clock.js";
import { RealProcessEnv, type ProcessEnv } from "../lib/process.js";
import { resolveAgentFromOpts } from "../modules/agents/cli.js";
import { cwdToProjectHash } from "../modules/transcript/discover.js";
import { MESSAGES } from "../lib/messages.js";
import { loadPricingForDate } from "../lib/pricing/load.js";
import { analyzeTokens, isEmptySession } from "../modules/transcript/analyzers/tokens.js";
Expand All @@ -19,6 +20,7 @@ export interface ForecastOptions {
json?: boolean;
agent?: string;
cwd?: string;
here?: boolean;
}

export interface ForecastDeps {
Expand Down Expand Up @@ -67,8 +69,16 @@ export async function runForecastCmd(
return { exitCode: 1 };
}

let metas = discovery.value;
if (opts.here) {
const cwdHash = cwdToProjectHash(opts.cwd ?? process.cwd());
metas = metas.filter(
(m) => m.projectHash === cwdHash || cwdHash.endsWith(m.projectHash),
);
}

const sessions: ForecastSession[] = [];
for (const meta of discovery.value) {
for (const meta of metas) {
let content: string;
try {
content = await fs.readFile(meta.filePath);
Expand Down
14 changes: 12 additions & 2 deletions src/commands/impact.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@ import { RealFileSystem, type FileSystem } from "../lib/fs.js";
import { RealClock, type Clock } from "../lib/clock.js";
import { RealProcessEnv, type ProcessEnv } from "../lib/process.js";
import { resolveAgentFromOpts } from "../modules/agents/cli.js";
import { resolveProjectsDir } from "../modules/transcript/discover.js";
import {
resolveProjectsDir,
cwdToProjectHash,
} from "../modules/transcript/discover.js";
import {
analyzeTokens,
isEmptySession,
Expand All @@ -37,6 +40,7 @@ export interface ImpactOptions {
json?: boolean;
agent?: string;
cwd?: string;
here?: boolean;
}

export interface ImpactDeps {
Expand Down Expand Up @@ -108,7 +112,13 @@ export async function runImpactCommand(
for (const i of discovery.error) stderr(i.message);
return { exitCode: 1 };
}
const metas = discovery.value;
let metas = discovery.value;
if (opts.here) {
const cwdHash = cwdToProjectHash(cwd);
metas = metas.filter(
(m) => m.projectHash === cwdHash || cwdHash.endsWith(m.projectHash),
);
}
const pricing = loadPricingForDate(clock.now());
for (const meta of metas) {
let content: string;
Expand Down
12 changes: 11 additions & 1 deletion src/commands/today.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { RealFileSystem, type FileSystem } from "../lib/fs.js";
import { RealClock, type Clock } from "../lib/clock.js";
import { RealProcessEnv, type ProcessEnv } from "../lib/process.js";
import { resolveAgentFromOpts } from "../modules/agents/cli.js";
import { cwdToProjectHash } from "../modules/transcript/discover.js";
import { MESSAGES } from "../lib/messages.js";
import { loadPricingForDate } from "../lib/pricing/load.js";
import { analyzeTokens, isEmptySession } from "../modules/transcript/analyzers/tokens.js";
Expand All @@ -22,6 +23,7 @@ export interface TodayOptions {
json?: boolean;
agent?: string;
cwd?: string;
here?: boolean;
}

export interface TodayDeps {
Expand Down Expand Up @@ -70,8 +72,16 @@ export async function runTodayCmd(
return { exitCode: 1 };
}

let metas = discovery.value;
if (opts.here) {
const cwdHash = cwdToProjectHash(opts.cwd ?? process.cwd());
metas = metas.filter(
(m) => m.projectHash === cwdHash || cwdHash.endsWith(m.projectHash),
);
}

const sessions: TodaySession[] = [];
for (const meta of discovery.value) {
for (const meta of metas) {
let content: string;
try {
content = await fs.readFile(meta.filePath);
Expand Down
10 changes: 9 additions & 1 deletion src/commands/trend.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { RealFileSystem, type FileSystem } from "../lib/fs.js";
import { RealClock, type Clock } from "../lib/clock.js";
import { RealProcessEnv, type ProcessEnv } from "../lib/process.js";
import { resolveAgentFromOpts } from "../modules/agents/cli.js";
import { cwdToProjectHash } from "../modules/transcript/discover.js";
import { MESSAGES } from "../lib/messages.js";
import { loadPricingForDate } from "../lib/pricing/load.js";
import { analyzeTokens, isEmptySession } from "../modules/transcript/analyzers/tokens.js";
Expand All @@ -33,6 +34,7 @@ export interface TrendOptions {
json?: boolean;
agent?: string;
cwd?: string;
here?: boolean;
}

export interface TrendDeps {
Expand Down Expand Up @@ -108,7 +110,13 @@ export async function runTrend(
stderr(metasResult.error.map((e: { message: string }) => e.message).join("\n"));
return { exitCode: 1 };
}
const metas = metasResult.value;
let metas = metasResult.value;
if (opts.here) {
const cwdHash = cwdToProjectHash(opts.cwd ?? process.cwd());
metas = metas.filter(
(m) => m.projectHash === cwdHash || cwdHash.endsWith(m.projectHash),
);
}

// Pre-filter by mtime then parse and aggregate.
const sessions: TrendSession[] = [];
Expand Down
30 changes: 30 additions & 0 deletions tests/integration/stats.integration.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,36 @@ describe("runStats integration", () => {
expect(out.join("\n")).not.toContain("transcripts exist");
});

it("--here scopes to the cwd's project (shared filter used by today/forecast/trend/impact too)", async () => {
// makeFs seeds 2 sessions under C--Projects-Sipcode + 1 under other-proj.
const all: string[] = [];
await runStats(
{ json: true, since: "all" },
{
fs: makeFs(),
env: makeEnv(),
clock: new FakeClock(NOW),
stdout: (s) => all.push(s),
stderr: () => {},
},
);
expect(JSON.parse(all.join("\n")).sessionCount).toBe(3);

// cwd "C:\Projects\Sipcode" hashes to "C--Projects-Sipcode" → only its 2.
const here: string[] = [];
await runStats(
{ json: true, since: "all", here: true, cwd: "C:\\Projects\\Sipcode" },
{
fs: makeFs(),
env: makeEnv(),
clock: new FakeClock(NOW),
stdout: (s) => here.push(s),
stderr: () => {},
},
);
expect(JSON.parse(here.join("\n")).sessionCount).toBe(2);
});

it("--html writes .sipcode/stats.html via injected writeFile", async () => {
const wrote: { path: string; content: string } = { path: "", content: "" };
const out: string[] = [];
Expand Down
Loading