From bc55a625adbefd31d4fef8aad9d81bb1b2ed8e18 Mon Sep 17 00:00:00 2001
From: Anuj7411
Date: Sat, 4 Jul 2026 16:55:57 +0530
Subject: [PATCH 1/2] feat(scope): --here now works on
today/forecast/trend/impact too
Previously --here was only on why/stats/receipt; running it on
today/forecast/trend/impact errored with "unknown option", which is exactly
what a user expects to work. Wire the shared cwdToProjectHash() project filter
(same code stats uses) into all four. Now --here scopes consistently across
every analytics/report command.
Added a stats --here scoping integration test (2 projects -> 1) that exercises
the shared filter all five commands share. README scope tip updated. drift
already scopes to the latest session's project internally, so it stays as-is.
Co-Authored-By: Claude Opus 4.8
---
README.md | 2 +-
src/cli.ts | 4 +++
src/commands/forecast.ts | 12 ++++++++-
src/commands/impact.ts | 14 ++++++++--
src/commands/today.ts | 12 ++++++++-
src/commands/trend.ts | 10 ++++++-
tests/integration/stats.integration.test.ts | 30 +++++++++++++++++++++
7 files changed, 78 insertions(+), 6 deletions(-)
diff --git a/README.md b/README.md
index fc32542..2cddc0e 100644
--- a/README.md
+++ b/README.md
@@ -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 `. (`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 `.
---
diff --git a/src/cli.ts b/src/cli.ts
index e8b2a6a..6e3508d 100644
--- a/src/cli.ts
+++ b/src/cli.ts
@@ -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 ", "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);
@@ -163,6 +164,7 @@ program
.description("Projected month-end spend with confidence band + last-month comparison.")
.option("--json", "machine-readable output")
.option("--agent ", "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);
@@ -176,6 +178,7 @@ program
.option("--since ", "time window: NNd | NNw | NNm (e.g. 30d, 4w, 3m)", "30d")
.option("--json", "machine-readable output")
.option("--agent ", "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);
@@ -273,6 +276,7 @@ program
.option("--since ", "override the install date (defaults to .sipcode/install-state.json)")
.option("--json", "machine-readable output")
.option("--agent ", "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);
diff --git a/src/commands/forecast.ts b/src/commands/forecast.ts
index ed94ade..458eaea 100644
--- a/src/commands/forecast.ts
+++ b/src/commands/forecast.ts
@@ -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";
@@ -19,6 +20,7 @@ export interface ForecastOptions {
json?: boolean;
agent?: string;
cwd?: string;
+ here?: boolean;
}
export interface ForecastDeps {
@@ -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);
diff --git a/src/commands/impact.ts b/src/commands/impact.ts
index 1997f25..51db88f 100644
--- a/src/commands/impact.ts
+++ b/src/commands/impact.ts
@@ -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,
@@ -37,6 +40,7 @@ export interface ImpactOptions {
json?: boolean;
agent?: string;
cwd?: string;
+ here?: boolean;
}
export interface ImpactDeps {
@@ -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;
diff --git a/src/commands/today.ts b/src/commands/today.ts
index d7ea977..2b5ec91 100644
--- a/src/commands/today.ts
+++ b/src/commands/today.ts
@@ -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";
@@ -22,6 +23,7 @@ export interface TodayOptions {
json?: boolean;
agent?: string;
cwd?: string;
+ here?: boolean;
}
export interface TodayDeps {
@@ -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);
diff --git a/src/commands/trend.ts b/src/commands/trend.ts
index 465f0a3..8b141d2 100644
--- a/src/commands/trend.ts
+++ b/src/commands/trend.ts
@@ -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";
@@ -33,6 +34,7 @@ export interface TrendOptions {
json?: boolean;
agent?: string;
cwd?: string;
+ here?: boolean;
}
export interface TrendDeps {
@@ -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[] = [];
diff --git a/tests/integration/stats.integration.test.ts b/tests/integration/stats.integration.test.ts
index 6154c6f..f67538b 100644
--- a/tests/integration/stats.integration.test.ts
+++ b/tests/integration/stats.integration.test.ts
@@ -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[] = [];
From 7e690f0b405f55f3582b69b4016d5577f7ff9ab4 Mon Sep 17 00:00:00 2001
From: Anuj7411
Date: Sat, 4 Jul 2026 22:21:59 +0530
Subject: [PATCH 2/2] =?UTF-8?q?release:=20v1.6.20=20=E2=80=94=20--here=20o?=
=?UTF-8?q?n=20today/forecast/trend/impact?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Bump 1.6.19 -> 1.6.20. CHANGELOG 1.6.20 entry + link ref. README badge,
llms.txt / llms-full.txt, landing page, server.json all -> 1.6.20 / 1,389.
Tests: 1,388 -> 1,389, full suite green.
Co-Authored-By: Claude Opus 4.8
---
CHANGELOG.md | 13 +++++++++++--
README.md | 4 ++--
docs/site/public/llms-full.txt | 4 ++--
docs/site/public/llms.txt | 6 +++---
docs/site/src/components/Footer.astro | 4 ++--
docs/site/src/components/Hero.astro | 6 +++---
package-lock.json | 4 ++--
package.json | 2 +-
server.json | 4 ++--
9 files changed, 28 insertions(+), 19 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index bc6099a..421d389 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -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
@@ -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
diff --git a/README.md b/README.md
index 2cddc0e..90510b0 100644
--- a/README.md
+++ b/README.md
@@ -15,7 +15,7 @@
-
+
@@ -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
diff --git a/docs/site/public/llms-full.txt b/docs/site/public/llms-full.txt
index e06f6b2..b138dca 100644
--- a/docs/site/public/llms-full.txt
+++ b/docs/site/public/llms-full.txt
@@ -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
@@ -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`
diff --git a/docs/site/public/llms.txt b/docs/site/public/llms.txt
index b21d2e8..93beade 100644
--- a/docs/site/public/llms.txt
+++ b/docs/site/public/llms.txt
@@ -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
@@ -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
diff --git a/docs/site/src/components/Footer.astro b/docs/site/src/components/Footer.astro
index f28f86b..146b4cf 100644
--- a/docs/site/src/components/Footer.astro
+++ b/docs/site/src/components/Footer.astro
@@ -25,11 +25,11 @@
- v1.6.19
+ v1.6.20
- 1,388 tests passing
+ 1,389 tests passing
@@ -19,7 +19,7 @@ const TEST_COUNT = 1388;
- v1.6.19 · MIT licensed
+ v1.6.20 · MIT licensed
{TEST_COUNT.toLocaleString()} tests passing
diff --git a/package-lock.json b/package-lock.json
index c99d67a..9a19091 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "sipcode",
- "version": "1.6.19",
+ "version": "1.6.20",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "sipcode",
- "version": "1.6.19",
+ "version": "1.6.20",
"license": "MIT",
"dependencies": {
"@modelcontextprotocol/sdk": "^1.29.0",
diff --git a/package.json b/package.json
index 5f2fa5b..71e8be1 100644
--- a/package.json
+++ b/package.json
@@ -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": [
diff --git a/server.json b/server.json
index aabc0c7..4694244 100644
--- a/server.json
+++ b/server.json
@@ -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"