getprismo 0.1.52 → 0.1.54
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/docs/announcement.md +39 -26
- package/lib/prismo-dev/firewall.js +8 -1
- package/lib/prismo-dev/help.js +4 -0
- package/lib/prismo-dev/mcp.js +123 -0
- package/package.json +1 -1
package/docs/announcement.md
CHANGED
|
@@ -1,56 +1,69 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Launch drafts
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Built on real numbers from a week of dogfooding (getprismo 0.1.52). Verified figures are measured against actual later sessions, never estimated — keep that distinction in every claim, it's the whole point.
|
|
4
|
+
|
|
5
|
+
Snapshot used below (one developer, ~1 week, single machine):
|
|
6
|
+
- **Verified saved: $22.81 (6,338,568 tokens)** — repairs whose savings were confirmed by a lower waste rate in later sessions.
|
|
7
|
+
- Observed: 141 sessions, 11.69M tokens, ~5.3M flagged avoidable.
|
|
8
|
+
- Per-developer annualized ≈ **$1,100/yr**; a 10-dev team ≈ **$11k/yr** — verified, not projected from a model.
|
|
4
9
|
|
|
5
10
|
---
|
|
6
11
|
|
|
7
12
|
## Show HN
|
|
8
13
|
|
|
9
|
-
**Title:** Show HN: Prismo
|
|
14
|
+
**Title:** Show HN: Prismo – an AI-coding cost agent that verifies its own savings ($22/wk/dev)
|
|
10
15
|
|
|
11
16
|
**Body:**
|
|
12
17
|
|
|
13
|
-
AI coding agents (Claude Code, Codex, Cursor) waste a
|
|
18
|
+
AI coding agents (Claude Code, Codex, Cursor) quietly waste a big share of their tokens: re-reading the same file hundreds of times, dumping full test output into context, loading lockfiles and build artifacts, retrying a failing command over and over. Most tools here show you a dashboard of the damage and stop. I wanted one that *fixes* it and then *proves* the fix worked.
|
|
14
19
|
|
|
15
|
-
Prismo
|
|
20
|
+
Prismo runs locally — `npx getprismo doctor`, no login, nothing leaves your machine — reads your agents' own session logs, and attributes waste to one of five causes. Connect it and it closes the loop:
|
|
16
21
|
|
|
17
|
-
-
|
|
18
|
-
-
|
|
19
|
-
-
|
|
20
|
-
-
|
|
21
|
-
-
|
|
22
|
-
- and the planner learns from the fleet: anonymized repair verdicts (counts only) aggregate into priors, so your first repair starts at the tier that's known to work
|
|
22
|
+
- A local planner repairs the top cause automatically. Each cause gets a dedicated fix (ignore rules + context packs, shielded commands, a context firewall, scoped session restarts) — not one generic "optimize."
|
|
23
|
+
- After each repair it measures that cause's waste rate in your *later* sessions and stores a verdict: improved / no-change / regressed (14-day baseline, before/after rates, 1% epsilon — the math is in the repo).
|
|
24
|
+
- A repair that doesn't help escalates to a stronger tier; one that fails both tiers is held for human review instead of being retried forever.
|
|
25
|
+
- For Claude Code it goes past advice: a PreToolUse hook actually *denies* reads into blocked context and the 4th retry of an identical failing command (fail-open, removed with one command).
|
|
26
|
+
- Savings are reported in dollars, **verified against real usage, not estimated**, with a weekly digest you can paste into Slack.
|
|
23
27
|
|
|
24
|
-
|
|
28
|
+
I dogfooded it on my own machine for a week. It verified **$22.81 / 6.3M tokens saved** (≈ $1,100/yr/dev annualized) — and, more honestly, the week of dogfooding caught **six real bugs in Prismo itself**, including one where it silently dropped *all* Claude Code telemetry for any repo whose path contains a space (e.g. `~/Code Projects/...`), and a verification bug where mixing in Cursor (which can't report the signal) poisoned the baseline so nothing ever verified. Both are fixed; finding them this way is kind of the best argument for the verify-everything design.
|
|
25
29
|
|
|
26
|
-
|
|
30
|
+
MIT-licensed, the verification and enforcement logic is all in the open: https://github.com/shanirsh/prismodev — would love feedback on the enforcement model and the verdict thresholds.
|
|
27
31
|
|
|
28
32
|
---
|
|
29
33
|
|
|
30
34
|
## X / Twitter thread
|
|
31
35
|
|
|
32
|
-
1/ Every AI-coding-cost tool shows you a dashboard of waste.
|
|
36
|
+
1/ Every AI-coding-cost tool shows you a dashboard of waste. I built the one that fixes it — then proves the fix worked, in dollars, measured against your real later sessions.
|
|
33
37
|
|
|
34
|
-
2/
|
|
38
|
+
2/ One week dogfooding on my own machine: **$22.81 / 6.3M tokens verified saved** (~$1,100/yr/dev). "Verified" = a repair ran, then later sessions showed a measurably lower waste rate. Not an estimate.
|
|
35
39
|
|
|
36
|
-
3/
|
|
40
|
+
3/ How: it reads your Claude Code / Codex / Cursor logs locally, attributes waste to 5 causes, and repairs the top one — each cause gets its own fix, not a generic "optimize."
|
|
37
41
|
|
|
38
|
-
4/
|
|
42
|
+
4/ The part nobody else does: after a repair it re-measures that cause in your NEXT sessions. Improved → stand down. No change → escalate to a stronger repair. Failed twice → stop and ask a human. A feedback controller, not a script.
|
|
39
43
|
|
|
40
|
-
5/
|
|
44
|
+
5/ For Claude Code it's not advisory — a PreToolUse hook *denies* reads into node_modules/logs/build output and blocks the 4th retry of an identical failing command. Fail-open, one command to remove.
|
|
41
45
|
|
|
42
|
-
6/
|
|
46
|
+
6/ Most honest part: a week of dogfooding caught 6 real bugs in Prismo itself — including one that silently hid ALL Claude Code data for any repo path with a space in it. Verifying everything is how you find that.
|
|
43
47
|
|
|
44
|
-
7/
|
|
48
|
+
7/ `npx getprismo doctor` — 10 seconds, no login, local-only. MIT: github.com/shanirsh/prismodev
|
|
45
49
|
|
|
46
50
|
---
|
|
47
51
|
|
|
48
|
-
## Release notes (0.1.40 → 0.1.
|
|
52
|
+
## Release notes (0.1.40 → 0.1.52)
|
|
49
53
|
|
|
50
|
-
- **Cause-specific repair executors** —
|
|
51
|
-
- **Autonomous repair planner** — `agent --watch` self-repairs
|
|
52
|
-
- **Runtime enforcement** — `prismo enforce install` adds
|
|
53
|
-
- **Verified savings in dollars** — `prismo digest
|
|
54
|
+
- **Cause-specific repair executors** — targeted repairs per waste cause instead of generic doctor; `prismo repair <cause|auto>`.
|
|
55
|
+
- **Autonomous repair planner** — `agent --watch` self-repairs with thresholds, per-cause cooldowns, local before/after verdicts, and mild→aggressive escalation.
|
|
56
|
+
- **Runtime enforcement** — `prismo enforce install` adds Claude Code Pre/PostToolUse hooks that block blocked-context reads and failing-command loops; fail-open; outcome-aware.
|
|
57
|
+
- **Verified savings in dollars** — `prismo digest`; the dashboard leads with verified dollars, with live prevention and early signals labeled separately.
|
|
54
58
|
- **Fleet priors** — first repairs start at the tier the fleet's verified outcomes recommend (anonymized counts only; local verdicts always win).
|
|
55
|
-
- **
|
|
59
|
+
- **Multi-repo capture** — one connector observes every repo on the machine, each session attributed to its own repo.
|
|
60
|
+
- **One-command protect** — `prismo protect` runs the full stack (safe fixes + context packs + enforcement + connector).
|
|
56
61
|
- **CI releases** — tag push runs tests and publishes.
|
|
62
|
+
|
|
63
|
+
### Bugs the dogfood week caught (and fixed)
|
|
64
|
+
1. Digest counted background auto-detect scans as interventions (186 fake vs 3 real).
|
|
65
|
+
2. Verification baseline poisoned by Cursor's blind zero-signals — now compares like-for-like across tools.
|
|
66
|
+
3. Claude Code telemetry silently dropped for repo paths containing a space or dot (folder-name encoding mismatch).
|
|
67
|
+
4. Connector was single-repo — now captures every repo.
|
|
68
|
+
5. `prismo protect` silently repointed a running connector (checked a field that never existed).
|
|
69
|
+
6. Dashboard polling (every 8s, loading a large unused column) blew past the DB egress quota — deferred the column, slowed/gated polling, windowed scans.
|
|
@@ -201,6 +201,13 @@ function renderFirewallPrompt(result) {
|
|
|
201
201
|
].join("\n");
|
|
202
202
|
}
|
|
203
203
|
|
|
204
|
+
function writeFirewallGeneratedFile(root, relPath, contents) {
|
|
205
|
+
const fullPath = path.join(root, relPath);
|
|
206
|
+
fs.mkdirSync(path.dirname(fullPath), { recursive: true });
|
|
207
|
+
fs.writeFileSync(fullPath, contents, "utf8");
|
|
208
|
+
return { path: relPath, backupPath: null };
|
|
209
|
+
}
|
|
210
|
+
|
|
204
211
|
function runFirewall(rootDir = process.cwd(), options = {}) {
|
|
205
212
|
const ctx = createOptimizeContext(rootDir, options.scope || null);
|
|
206
213
|
const task = options.task || options.scope || "general";
|
|
@@ -224,7 +231,7 @@ function runFirewall(rootDir = process.cwd(), options = {}) {
|
|
|
224
231
|
|
|
225
232
|
if (!options.dryRun) {
|
|
226
233
|
const write = (relPath, contents) => {
|
|
227
|
-
if (!options.live) return
|
|
234
|
+
if (!options.live) return writeFirewallGeneratedFile(ctx.root, relPath, contents);
|
|
228
235
|
const fullPath = path.join(ctx.root, relPath);
|
|
229
236
|
fs.mkdirSync(path.dirname(fullPath), { recursive: true });
|
|
230
237
|
fs.writeFileSync(fullPath, contents, "utf8");
|
package/lib/prismo-dev/help.js
CHANGED
|
@@ -475,9 +475,13 @@ Tools exposed:
|
|
|
475
475
|
prismo_firewall
|
|
476
476
|
prismo_cc_timeline
|
|
477
477
|
prismo_cursor_sessions
|
|
478
|
+
prismo_should_shield (agent-native: should I shield this command?)
|
|
479
|
+
prismo_loop_check (agent-native: am I looping?)
|
|
480
|
+
prismo_context_guard (agent-native: what repo am I in / what to avoid rereading?)
|
|
478
481
|
|
|
479
482
|
Output:
|
|
480
483
|
Starts a local JSON-RPC MCP server over stdio. Use it from MCP-compatible clients so agents can scan context waste, search shielded command output, and request scoped context without loading huge logs into chat.
|
|
484
|
+
The agent-native tools answer single mid-session questions directly so Claude Code, Codex, and Cursor can call Prismo while they work, not just after.
|
|
481
485
|
|
|
482
486
|
prismo mcp doctor validates the local MCP tool surface and prints a ready-to-use client config snippet.`,
|
|
483
487
|
connect: `PrismoDev Connect
|
package/lib/prismo-dev/mcp.js
CHANGED
|
@@ -1,3 +1,40 @@
|
|
|
1
|
+
const path = require("path");
|
|
2
|
+
|
|
3
|
+
// Commands whose output reliably floods agent context and should run through
|
|
4
|
+
// shield (full output to disk, compact summary to the model).
|
|
5
|
+
const NOISY_COMMAND_PATTERNS = [
|
|
6
|
+
{ re: /(^|\s)(jest|vitest|mocha|pytest|rspec|phpunit|ava|tap)(\s|$)|(\b(npm|yarn|pnpm|bun)\s+(run\s+)?test\b)|\b(go|cargo)\s+test\b/, why: "test runs emit large pass/fail output" },
|
|
7
|
+
{ re: /(^|\s)(webpack|rollup|esbuild|vite|tsc)(\s|$)|\b(next|nuxt|astro)\s+build\b|\b(npm|yarn|pnpm|bun)\s+(run\s+)?build\b|\b(go|cargo)\s+build\b|(^|\s)make(\s|$)/, why: "builds emit long compiler/bundler output" },
|
|
8
|
+
{ re: /\b(npm|pnpm|bun)\s+(install|ci|i)\b|\byarn(\s+install)?\b|\bpip\s+install\b|\bbundle\s+install\b/, why: "installs print large dependency trees" },
|
|
9
|
+
{ re: /(^|\s)(eslint|ruff|flake8|mypy|pylint)(\s|$)|\b(npm|yarn|pnpm)\s+(run\s+)?lint\b|tsc\s+--noEmit/, why: "linters/type-checkers emit many diagnostics" },
|
|
10
|
+
{ re: /\b(playwright|cypress|e2e)\b/, why: "e2e runs dump verbose logs" },
|
|
11
|
+
{ re: /\bdocker\s+build\b|\bterraform\b|\bkubectl\s+logs\b|\bcoverage\b|--verbose\b/, why: "infra/coverage/verbose commands flood output" },
|
|
12
|
+
];
|
|
13
|
+
const QUICK_COMMAND_PATTERNS = [
|
|
14
|
+
/(^|\s)(git\s+(status|log|diff|branch|show)|ls|pwd|whoami|which|echo|cat|head|tail|wc|env|date|node\s+-v|--version)(\s|$)/,
|
|
15
|
+
];
|
|
16
|
+
|
|
17
|
+
function classifyCommandForShield(command, repeatedCommands = []) {
|
|
18
|
+
const cmd = String(command || "").trim();
|
|
19
|
+
if (!cmd) return { shouldShield: false, confidence: "low", reason: "No command provided." };
|
|
20
|
+
const normalized = cmd.replace(/\s+/g, " ");
|
|
21
|
+
const repeated = (repeatedCommands || []).some((item) => {
|
|
22
|
+
const value = String(item.value || "").replace(/\s+/g, " ");
|
|
23
|
+
return value && (normalized.includes(value) || value.includes(normalized)) && Number(item.count || 0) >= 3;
|
|
24
|
+
});
|
|
25
|
+
if (repeated) {
|
|
26
|
+
return { shouldShield: true, confidence: "high", reason: "This command has already repeated several times in recent sessions; shield it so each retry costs a summary, not full output." };
|
|
27
|
+
}
|
|
28
|
+
const noisy = NOISY_COMMAND_PATTERNS.find((entry) => entry.re.test(normalized));
|
|
29
|
+
if (noisy) {
|
|
30
|
+
return { shouldShield: true, confidence: "high", reason: `Shield recommended: ${noisy.why}.` };
|
|
31
|
+
}
|
|
32
|
+
if (QUICK_COMMAND_PATTERNS.some((re) => re.test(normalized))) {
|
|
33
|
+
return { shouldShield: false, confidence: "high", reason: "Quick, low-output command; shielding is unnecessary." };
|
|
34
|
+
}
|
|
35
|
+
return { shouldShield: false, confidence: "low", reason: "No known flooding pattern; shield only if you expect large output." };
|
|
36
|
+
}
|
|
37
|
+
|
|
1
38
|
function createTextResult(payload) {
|
|
2
39
|
const text = typeof payload === "string" ? payload : JSON.stringify(payload, null, 2);
|
|
3
40
|
return {
|
|
@@ -147,6 +184,20 @@ function createMcpTools(deps) {
|
|
|
147
184
|
tool: { type: "string", enum: ["all", "codex", "claude", "cursor"], description: "Which local session logs to inspect." },
|
|
148
185
|
limit: limitProperty,
|
|
149
186
|
}),
|
|
187
|
+
// Agent-native, mid-loop decision tools: tight yes/no answers an agent can
|
|
188
|
+
// act on during a session, not analysis dumps.
|
|
189
|
+
makeTool("prismo_should_shield", "Decide whether a shell command should be run through Prismo shield (full output to disk, compact summary to context) before you run it. Call this before any command that might print a lot.", {
|
|
190
|
+
command: { type: "string", description: "The exact shell command you are about to run." },
|
|
191
|
+
path: pathProperty,
|
|
192
|
+
}, ["command"]),
|
|
193
|
+
makeTool("prismo_loop_check", "Check whether the current coding session is stuck in a loop (repeating the same command or failing repeatedly). Call this when you have retried something more than once.", {
|
|
194
|
+
path: pathProperty,
|
|
195
|
+
tool: { type: "string", enum: ["all", "codex", "claude", "cursor"], description: "Which local session logs to inspect." },
|
|
196
|
+
}),
|
|
197
|
+
makeTool("prismo_context_guard", "Get what repo you are in, which paths to avoid rereading, files you have already read repeatedly, and the compact context pack to start from. Call this at the start of a session or before broad exploration.", {
|
|
198
|
+
path: pathProperty,
|
|
199
|
+
scope: scopeProperty,
|
|
200
|
+
}),
|
|
150
201
|
];
|
|
151
202
|
|
|
152
203
|
function resolveRoot(args) {
|
|
@@ -292,6 +343,75 @@ function createMcpTools(deps) {
|
|
|
292
343
|
}));
|
|
293
344
|
}
|
|
294
345
|
|
|
346
|
+
if (name === "prismo_should_shield") {
|
|
347
|
+
let repeatedCommands = [];
|
|
348
|
+
try {
|
|
349
|
+
const summary = getUsageSummary({ cwd: target, limit: 3, tool: "all" });
|
|
350
|
+
const latest = (summary.sessions || [])[0];
|
|
351
|
+
repeatedCommands = (latest && latest.repeatedCommands) || [];
|
|
352
|
+
} catch { /* command classification still works without session context */ }
|
|
353
|
+
const decision = classifyCommandForShield(args.command, repeatedCommands);
|
|
354
|
+
return createTextResult({
|
|
355
|
+
schemaVersion: 1,
|
|
356
|
+
command: args.command,
|
|
357
|
+
shouldShield: decision.shouldShield,
|
|
358
|
+
confidence: decision.confidence,
|
|
359
|
+
reason: decision.reason,
|
|
360
|
+
recommended: decision.shouldShield
|
|
361
|
+
? `npx -y getprismo@latest shield -- ${String(args.command || "").trim()}`
|
|
362
|
+
: null,
|
|
363
|
+
note: "Shield stores full stdout/stderr locally and returns a compact summary, so the output never floods context.",
|
|
364
|
+
});
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
if (name === "prismo_loop_check") {
|
|
368
|
+
const summary = getUsageSummary({ cwd: target, limit: 3, tool: args.tool || "all" });
|
|
369
|
+
const latest = (summary.sessions || [])[0] || null;
|
|
370
|
+
const repeatedCommands = (latest && latest.repeatedCommands) || [];
|
|
371
|
+
const looping = Boolean(latest && latest.loopSuspicion) || repeatedCommands.some((c) => Number(c.count || 0) >= 4);
|
|
372
|
+
const topRepeat = repeatedCommands[0] || null;
|
|
373
|
+
return createTextResult({
|
|
374
|
+
schemaVersion: 1,
|
|
375
|
+
looping,
|
|
376
|
+
confidence: latest ? (latest.loopConfidence || (looping ? "medium" : "low")) : "low",
|
|
377
|
+
signals: {
|
|
378
|
+
repeatedCommands: repeatedCommands.slice(0, 5),
|
|
379
|
+
failureMentions: latest ? Number(latest.failureMentions || 0) : 0,
|
|
380
|
+
turns: latest ? Number(latest.turns || 0) : 0,
|
|
381
|
+
},
|
|
382
|
+
advice: looping
|
|
383
|
+
? `You appear to be looping${topRepeat ? ` on \`${topRepeat.value}\` (${topRepeat.count}x)` : ""}. Stop retrying, change the approach, and capture the command once with \`npx -y getprismo@latest shield -- <command>\` instead of re-running it.`
|
|
384
|
+
: "No loop detected. Keep going.",
|
|
385
|
+
});
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
if (name === "prismo_context_guard") {
|
|
389
|
+
const scope = args.scope || null;
|
|
390
|
+
const scan = scanRepo(target, { includeUsage: true, usageLimit: 3 });
|
|
391
|
+
const ctx = createOptimizeContext(target, scope);
|
|
392
|
+
const usage = scan.realUsage && scan.realUsage.sessions ? scan.realUsage.sessions : [];
|
|
393
|
+
const repeatedlyRead = [];
|
|
394
|
+
for (const session of usage) {
|
|
395
|
+
for (const item of session.repeatedPathMentions || []) {
|
|
396
|
+
if (Number(item.count || 0) >= 4) repeatedlyRead.push({ path: item.value, reads: item.count });
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
const blocked = (scan.recommendedClaudeIgnore || []).slice(0, 24);
|
|
400
|
+
return createTextResult({
|
|
401
|
+
schemaVersion: 1,
|
|
402
|
+
repo: path.basename(path.resolve(target)),
|
|
403
|
+
avoidRereading: {
|
|
404
|
+
blockedContext: blocked,
|
|
405
|
+
alreadyReadRepeatedly: repeatedlyRead.slice(0, 12),
|
|
406
|
+
},
|
|
407
|
+
startFrom: {
|
|
408
|
+
contextFile: scope ? `.prismo/${scope}-summary.md` : ".prismo/architecture-summary.md",
|
|
409
|
+
starterPrompt: renderStarterPrompt(ctx, scope),
|
|
410
|
+
},
|
|
411
|
+
advice: "Read the start-from context pack first. Do not reread blocked paths or files you have already read several times; quote what you already saw instead.",
|
|
412
|
+
});
|
|
413
|
+
}
|
|
414
|
+
|
|
295
415
|
throw new Error(`Unknown MCP tool: ${name}`);
|
|
296
416
|
}
|
|
297
417
|
|
|
@@ -397,6 +517,9 @@ async function runMcpDoctor(deps) {
|
|
|
397
517
|
"prismo_timeline",
|
|
398
518
|
"prismo_replay",
|
|
399
519
|
"prismo_boundaries",
|
|
520
|
+
"prismo_should_shield",
|
|
521
|
+
"prismo_loop_check",
|
|
522
|
+
"prismo_context_guard",
|
|
400
523
|
];
|
|
401
524
|
const toolNames = tools.map((tool) => tool.name);
|
|
402
525
|
const missingTools = requiredTools.filter((name) => !toolNames.includes(name));
|
package/package.json
CHANGED