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 @@ GitHub stars npm downloads per month MIT licensed - 1388 tests passing + 1389 tests passing zero network calls

@@ -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"