getprismo 0.1.53 → 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/lib/prismo-dev/help.js +4 -0
- package/lib/prismo-dev/mcp.js +123 -0
- package/package.json +1 -1
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