getprismo 0.1.16 → 0.1.18
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/README.md +4 -0
- package/docs/live-demo.md +1 -0
- package/docs/mcp.md +10 -0
- package/lib/prismo-dev/mcp.js +89 -0
- package/lib/prismo-dev/report.js +10 -0
- package/lib/prismo-dev/scan.js +138 -0
- package/lib/prismo-dev/usage-watch.js +1 -1
- package/lib/prismo-dev-scan.js +25 -5
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -52,6 +52,7 @@ agent-native npx getprismo mcp
|
|
|
52
52
|
- repeated file reads (same file loaded 100+ times in one session)
|
|
53
53
|
- repeated commands (agent running the same command in a loop)
|
|
54
54
|
- high context risk sessions that should have been split at task boundaries
|
|
55
|
+
- session-derived ignore candidates from actual Claude/Codex logs (`logs/debug.log`, `dist/app.js`, `package-lock.json`, source-stream dumps)
|
|
55
56
|
|
|
56
57
|
---
|
|
57
58
|
|
|
@@ -96,6 +97,8 @@ Next:
|
|
|
96
97
|
|
|
97
98
|
doctor went from 79 to 91 in one run. the repo now has proper ignore files, compact context packs, and a clear starting point for the next coding session.
|
|
98
99
|
|
|
100
|
+
`scan --usage` and `doctor` can also turn real session leaks into concrete ignore suggestions. if local Claude/Codex logs show `logs/debug.log`, `dist/app.js`, `package-lock.json`, source-stream dumps, or other noisy files repeatedly entering context, prismodev adds conservative `.claudeignore` / `.cursorignore` candidate rules instead of only reporting the problem.
|
|
101
|
+
|
|
99
102
|
---
|
|
100
103
|
|
|
101
104
|
## real output: watch
|
|
@@ -806,6 +809,7 @@ npx getprismo doctor --help
|
|
|
806
809
|
npx getprismo watch --help
|
|
807
810
|
npx getprismo shield --help
|
|
808
811
|
npx getprismo mcp --help
|
|
812
|
+
npx getprismo mcp doctor
|
|
809
813
|
npx getprismo cc --help
|
|
810
814
|
npx getprismo scan --help
|
|
811
815
|
```
|
package/docs/live-demo.md
CHANGED
|
@@ -12,6 +12,7 @@ Shows:
|
|
|
12
12
|
|
|
13
13
|
- before/after repo score
|
|
14
14
|
- missing `.claudeignore` / `.cursorignore`
|
|
15
|
+
- ignore suggestions derived from actual local Claude/Codex session leaks
|
|
15
16
|
- generated artifacts exposed to AI context
|
|
16
17
|
- compact `.prismo/` context packs
|
|
17
18
|
- recommended next starting context
|
package/docs/mcp.md
CHANGED
|
@@ -8,6 +8,16 @@ PrismoDev can run as a local MCP server so compatible coding agents can inspect
|
|
|
8
8
|
npx getprismo mcp /path/to/your/repo
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
+
## Doctor
|
|
12
|
+
|
|
13
|
+
Before adding PrismoDev to a client, validate the local MCP surface:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npx getprismo mcp doctor /path/to/your/repo
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
This checks that the MCP server can expose all Prismo tools, runs a scan smoke test, and prints a ready-to-use client config snippet.
|
|
20
|
+
|
|
11
21
|
## Generic MCP Config
|
|
12
22
|
|
|
13
23
|
```json
|
package/lib/prismo-dev/mcp.js
CHANGED
|
@@ -258,7 +258,96 @@ function runMcpServer(deps) {
|
|
|
258
258
|
});
|
|
259
259
|
}
|
|
260
260
|
|
|
261
|
+
async function runMcpDoctor(deps) {
|
|
262
|
+
const { rootDir, packageVersion = "0.0.0" } = deps;
|
|
263
|
+
const { tools, callTool } = createMcpTools(deps);
|
|
264
|
+
const requiredTools = [
|
|
265
|
+
"prismo_scan",
|
|
266
|
+
"prismo_doctor_dry_run",
|
|
267
|
+
"prismo_watch_snapshot",
|
|
268
|
+
"prismo_shield_run",
|
|
269
|
+
"prismo_shield_search",
|
|
270
|
+
"prismo_shield_last",
|
|
271
|
+
"prismo_context_pack",
|
|
272
|
+
"prismo_firewall",
|
|
273
|
+
"prismo_cc_timeline",
|
|
274
|
+
];
|
|
275
|
+
const toolNames = tools.map((tool) => tool.name);
|
|
276
|
+
const missingTools = requiredTools.filter((name) => !toolNames.includes(name));
|
|
277
|
+
const scanResult = await callTool("prismo_scan", { path: rootDir, includeUsage: false, limit: 1 });
|
|
278
|
+
let scanPayload = {};
|
|
279
|
+
try {
|
|
280
|
+
scanPayload = JSON.parse(scanResult.content?.[0]?.text || "{}");
|
|
281
|
+
} catch {
|
|
282
|
+
scanPayload = {};
|
|
283
|
+
}
|
|
284
|
+
const config = {
|
|
285
|
+
mcpServers: {
|
|
286
|
+
prismodev: {
|
|
287
|
+
command: "npx",
|
|
288
|
+
args: ["-y", "getprismo", "mcp", rootDir],
|
|
289
|
+
},
|
|
290
|
+
},
|
|
291
|
+
};
|
|
292
|
+
return {
|
|
293
|
+
schemaVersion: 1,
|
|
294
|
+
ok: missingTools.length === 0 && Boolean(scanPayload.schemaVersion),
|
|
295
|
+
server: {
|
|
296
|
+
name: "prismodev",
|
|
297
|
+
version: packageVersion,
|
|
298
|
+
transport: "stdio",
|
|
299
|
+
root: rootDir,
|
|
300
|
+
},
|
|
301
|
+
tools: {
|
|
302
|
+
count: tools.length,
|
|
303
|
+
required: requiredTools,
|
|
304
|
+
missing: missingTools,
|
|
305
|
+
hasShield: toolNames.includes("prismo_shield_run") && toolNames.includes("prismo_shield_search"),
|
|
306
|
+
},
|
|
307
|
+
smoke: {
|
|
308
|
+
scan: {
|
|
309
|
+
ok: Boolean(scanPayload.schemaVersion),
|
|
310
|
+
score: scanPayload.score ?? null,
|
|
311
|
+
riskLevel: scanPayload.riskLevel ?? null,
|
|
312
|
+
},
|
|
313
|
+
},
|
|
314
|
+
config,
|
|
315
|
+
next: [
|
|
316
|
+
"Add the config snippet to your MCP-compatible client.",
|
|
317
|
+
"Restart the client and confirm prismodev appears in the MCP tool list.",
|
|
318
|
+
"Ask the agent to call prismo_scan or prismo_shield_run.",
|
|
319
|
+
],
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
function renderMcpDoctorTerminal(result) {
|
|
324
|
+
const lines = [];
|
|
325
|
+
lines.push("");
|
|
326
|
+
lines.push("Prismo MCP Doctor");
|
|
327
|
+
lines.push("");
|
|
328
|
+
lines.push(`Status: ${result.ok ? "ready" : "needs attention"}`);
|
|
329
|
+
lines.push(`Server: ${result.server.name}@${result.server.version}`);
|
|
330
|
+
lines.push(`Transport: ${result.server.transport}`);
|
|
331
|
+
lines.push(`Repo: ${result.server.root}`);
|
|
332
|
+
lines.push("");
|
|
333
|
+
lines.push("Checks");
|
|
334
|
+
lines.push(`- Tools exposed: ${result.tools.count}`);
|
|
335
|
+
lines.push(`- Required tools missing: ${result.tools.missing.length ? result.tools.missing.join(", ") : "none"}`);
|
|
336
|
+
lines.push(`- Shield tools: ${result.tools.hasShield ? "ready" : "missing"}`);
|
|
337
|
+
lines.push(`- Scan smoke: ${result.smoke.scan.ok ? `ok (${result.smoke.scan.score}/100, ${result.smoke.scan.riskLevel})` : "failed"}`);
|
|
338
|
+
lines.push("");
|
|
339
|
+
lines.push("MCP config");
|
|
340
|
+
lines.push("");
|
|
341
|
+
lines.push(JSON.stringify(result.config, null, 2));
|
|
342
|
+
lines.push("");
|
|
343
|
+
lines.push("Next");
|
|
344
|
+
result.next.forEach((step, index) => lines.push(`${index + 1}. ${step}`));
|
|
345
|
+
return lines.join("\n");
|
|
346
|
+
}
|
|
347
|
+
|
|
261
348
|
module.exports = {
|
|
262
349
|
createMcpTools,
|
|
350
|
+
renderMcpDoctorTerminal,
|
|
351
|
+
runMcpDoctor,
|
|
263
352
|
runMcpServer,
|
|
264
353
|
};
|
package/lib/prismo-dev/report.js
CHANGED
|
@@ -277,6 +277,16 @@ function renderMarkdownReport(result) {
|
|
|
277
277
|
});
|
|
278
278
|
lines.push("");
|
|
279
279
|
}
|
|
280
|
+
if (result.sessionIgnoreSuggestions && result.sessionIgnoreSuggestions.length) {
|
|
281
|
+
lines.push("## Session-Derived Ignore Suggestions");
|
|
282
|
+
lines.push("");
|
|
283
|
+
lines.push("These rules came from local Claude/Codex session logs where noisy paths already entered context.");
|
|
284
|
+
lines.push("");
|
|
285
|
+
result.sessionIgnoreSuggestions.slice(0, 20).forEach((item) => {
|
|
286
|
+
lines.push(`- \`${item.pattern}\` - ${item.reason} (${item.count} mention${item.count === 1 ? "" : "s"}${item.examples.length ? `; e.g. ${item.examples[0]}` : ""})`);
|
|
287
|
+
});
|
|
288
|
+
lines.push("");
|
|
289
|
+
}
|
|
280
290
|
lines.push("## Issues");
|
|
281
291
|
lines.push("");
|
|
282
292
|
if (!result.issues.length) {
|
package/lib/prismo-dev/scan.js
CHANGED
|
@@ -97,6 +97,128 @@ function missingIgnoreSuggestions(recommended, existingPatterns) {
|
|
|
97
97
|
return recommended.filter((pattern) => !ignoreSuggestionCovered(pattern, existingPatterns));
|
|
98
98
|
}
|
|
99
99
|
|
|
100
|
+
const SESSION_NOISE_DIRS = new Set([
|
|
101
|
+
".next",
|
|
102
|
+
".nuxt",
|
|
103
|
+
".prismo",
|
|
104
|
+
".pytest_cache",
|
|
105
|
+
".turbo",
|
|
106
|
+
"__pycache__",
|
|
107
|
+
"build",
|
|
108
|
+
"calendar-dumps",
|
|
109
|
+
"coverage",
|
|
110
|
+
"dist",
|
|
111
|
+
"event-dumps",
|
|
112
|
+
"events",
|
|
113
|
+
"exports",
|
|
114
|
+
"htmlcov",
|
|
115
|
+
"inbox-dumps",
|
|
116
|
+
"logs",
|
|
117
|
+
"models",
|
|
118
|
+
"node_modules",
|
|
119
|
+
"out",
|
|
120
|
+
"playwright-report",
|
|
121
|
+
"session-dumps",
|
|
122
|
+
"source-streams",
|
|
123
|
+
"state-backups",
|
|
124
|
+
"test-results",
|
|
125
|
+
"tmp",
|
|
126
|
+
"temp",
|
|
127
|
+
]);
|
|
128
|
+
|
|
129
|
+
const SESSION_NOISE_FILE_NAMES = new Set([
|
|
130
|
+
"package-lock.json",
|
|
131
|
+
"pnpm-lock.yaml",
|
|
132
|
+
"yarn.lock",
|
|
133
|
+
"bun.lockb",
|
|
134
|
+
"coverage-final.json",
|
|
135
|
+
"lcov.info",
|
|
136
|
+
]);
|
|
137
|
+
|
|
138
|
+
const SESSION_NOISE_EXTENSIONS = new Set([
|
|
139
|
+
".db",
|
|
140
|
+
".jsonl",
|
|
141
|
+
".lock",
|
|
142
|
+
".log",
|
|
143
|
+
".sqlite",
|
|
144
|
+
".sqlite3",
|
|
145
|
+
]);
|
|
146
|
+
|
|
147
|
+
function cleanSessionPath(value) {
|
|
148
|
+
const text = String(value || "").trim().replace(/\\/g, "/");
|
|
149
|
+
if (!text || /^https?:\/\//.test(text)) return null;
|
|
150
|
+
const withoutQuotes = text.replace(/^["'`]+|["'`.,:;)\]]+$/g, "");
|
|
151
|
+
if (!withoutQuotes || withoutQuotes.includes("\n")) return null;
|
|
152
|
+
const markerIndex = withoutQuotes.indexOf("/Users/");
|
|
153
|
+
if (markerIndex > 0) return withoutQuotes.slice(markerIndex);
|
|
154
|
+
return withoutQuotes;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function sessionIgnorePatternForPath(value, root) {
|
|
158
|
+
const cleaned = cleanSessionPath(value);
|
|
159
|
+
if (!cleaned) return null;
|
|
160
|
+
const rootNormalized = normalizeRel(root);
|
|
161
|
+
let rel = cleaned;
|
|
162
|
+
if (path.isAbsolute(cleaned)) {
|
|
163
|
+
const normalized = normalizeRel(cleaned);
|
|
164
|
+
if (!normalized.startsWith(`${rootNormalized}/`)) return null;
|
|
165
|
+
rel = normalizeRel(path.relative(root, cleaned));
|
|
166
|
+
}
|
|
167
|
+
rel = normalizeRel(rel).replace(/^\.\//, "");
|
|
168
|
+
if (!rel || rel === "." || rel.startsWith("../") || rel.includes("..")) return null;
|
|
169
|
+
|
|
170
|
+
const segments = rel.split("/").filter(Boolean);
|
|
171
|
+
if (!segments.length) return null;
|
|
172
|
+
for (let index = 0; index < segments.length; index += 1) {
|
|
173
|
+
const segment = segments[index];
|
|
174
|
+
if (SESSION_NOISE_DIRS.has(segment)) {
|
|
175
|
+
return `${segments.slice(0, index + 1).join("/")}/`;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
const fileName = segments[segments.length - 1];
|
|
180
|
+
const lowerName = fileName.toLowerCase();
|
|
181
|
+
const ext = path.extname(lowerName);
|
|
182
|
+
if (SESSION_NOISE_FILE_NAMES.has(lowerName)) return fileName;
|
|
183
|
+
if (SESSION_NOISE_EXTENSIONS.has(ext)) return rel;
|
|
184
|
+
if (/_state\.json$/i.test(fileName)) return "*_state.json";
|
|
185
|
+
if (/_tokens\.json$/i.test(fileName)) return "*_tokens.json";
|
|
186
|
+
if (/_export\.json$/i.test(fileName)) return "*_export.json";
|
|
187
|
+
if (/secret|credential|token/i.test(fileName) && /\.json$/i.test(fileName)) return rel;
|
|
188
|
+
return null;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
function buildSessionIgnoreSuggestions(realUsage, root) {
|
|
192
|
+
if (!realUsage || !Array.isArray(realUsage.sessions)) return [];
|
|
193
|
+
const byPattern = new Map();
|
|
194
|
+
const add = (pattern, item, source, reason) => {
|
|
195
|
+
if (!pattern) return;
|
|
196
|
+
const existing = byPattern.get(pattern) || {
|
|
197
|
+
pattern,
|
|
198
|
+
source,
|
|
199
|
+
reason,
|
|
200
|
+
count: 0,
|
|
201
|
+
examples: [],
|
|
202
|
+
};
|
|
203
|
+
existing.count += Number(item?.count || 1);
|
|
204
|
+
const example = item?.value || item?.path || pattern;
|
|
205
|
+
if (example && !existing.examples.includes(example) && existing.examples.length < 3) existing.examples.push(example);
|
|
206
|
+
byPattern.set(pattern, existing);
|
|
207
|
+
};
|
|
208
|
+
|
|
209
|
+
for (const session of realUsage.sessions) {
|
|
210
|
+
for (const item of session.generatedArtifacts || []) {
|
|
211
|
+
add(sessionIgnorePatternForPath(item.value, root), item, session.tool || "session", "Generated artifact entered local session context.");
|
|
212
|
+
}
|
|
213
|
+
for (const item of session.repeatedPathMentions || []) {
|
|
214
|
+
add(sessionIgnorePatternForPath(item.value, root), item, session.tool || "session", "Noisy path appeared repeatedly in local session context.");
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
return Array.from(byPattern.values())
|
|
218
|
+
.sort((a, b) => b.count - a.count || a.pattern.localeCompare(b.pattern))
|
|
219
|
+
.slice(0, 25);
|
|
220
|
+
}
|
|
221
|
+
|
|
100
222
|
function getFileKind(filePath) {
|
|
101
223
|
const ext = path.extname(filePath).toLowerCase();
|
|
102
224
|
const name = path.basename(filePath).toLowerCase();
|
|
@@ -775,6 +897,7 @@ function toJsonPayload(result) {
|
|
|
775
897
|
optimizationStack: result.optimizationStack,
|
|
776
898
|
toolOutputRisk: result.toolOutputRisk,
|
|
777
899
|
operationalNoise: result.operationalNoise,
|
|
900
|
+
sessionIgnoreSuggestions: result.sessionIgnoreSuggestions || [],
|
|
778
901
|
proxyTrackingReadiness: result.proxyTrackingReadiness,
|
|
779
902
|
suggestedClaudeIgnore: result.recommendedClaudeIgnore,
|
|
780
903
|
suggestedCursorIgnore: result.recommendedCursorIgnore,
|
|
@@ -1061,7 +1184,19 @@ function scanRepo(rootDir = process.cwd(), options = {}) {
|
|
|
1061
1184
|
}
|
|
1062
1185
|
|
|
1063
1186
|
const realUsage = options.includeUsage ? getUsageSummary({ tool: options.usageTool || "all", cwd: root, limit: options.usageLimit || 5 }) : null;
|
|
1187
|
+
const sessionIgnoreSuggestions = buildSessionIgnoreSuggestions(realUsage, root);
|
|
1064
1188
|
addRealUsageIssues(issues, realUsage);
|
|
1189
|
+
if (sessionIgnoreSuggestions.length) {
|
|
1190
|
+
addIssue(
|
|
1191
|
+
issues,
|
|
1192
|
+
"medium",
|
|
1193
|
+
"ignore_file",
|
|
1194
|
+
`${sessionIgnoreSuggestions.length} session-derived ignore suggestion${sessionIgnoreSuggestions.length === 1 ? "" : "s"}`,
|
|
1195
|
+
sessionIgnoreSuggestions.slice(0, 5).map((item) => `${item.pattern} (${item.count}x)`).join(", "),
|
|
1196
|
+
"Review the generated .claudeignore/.cursorignore suggestions from actual local session context.",
|
|
1197
|
+
"Likely avoidable token exposure: these paths already appeared in local coding-agent context."
|
|
1198
|
+
);
|
|
1199
|
+
}
|
|
1065
1200
|
const optimizationStack = detectOptimizationStack(root, claudeConfig, codexConfig);
|
|
1066
1201
|
const agentReadiness = detectAgentReadiness(root, claudeConfig, codexConfig, realUsage);
|
|
1067
1202
|
const toolOutputRisk = detectToolOutputRisk({ exposedLargeFiles, exposedHighRiskDirs, highRiskDirs });
|
|
@@ -1145,11 +1280,13 @@ function scanRepo(rootDir = process.cwd(), options = {}) {
|
|
|
1145
1280
|
.filter((file) => file.size >= 1024 * 1024 || ["log", "json", "minified", "lock/generated"].includes(file.kind))
|
|
1146
1281
|
.map((file) => file.path);
|
|
1147
1282
|
const operationalNoiseSuggestions = operationalNoise.files.map((file) => file.path);
|
|
1283
|
+
const sessionIgnorePatterns = sessionIgnoreSuggestions.map((item) => item.pattern);
|
|
1148
1284
|
const recommendedClaudeIgnore = Array.from(new Set([
|
|
1149
1285
|
...DEFAULT_CLAUDEIGNORE,
|
|
1150
1286
|
...gitignorePatterns.filter((line) => !line.startsWith("!")),
|
|
1151
1287
|
...largeFileSuggestions,
|
|
1152
1288
|
...operationalNoiseSuggestions,
|
|
1289
|
+
...sessionIgnorePatterns,
|
|
1153
1290
|
]));
|
|
1154
1291
|
const recommendedCursorIgnore = Array.from(new Set([
|
|
1155
1292
|
...recommendedClaudeIgnore,
|
|
@@ -1203,6 +1340,7 @@ function scanRepo(rootDir = process.cwd(), options = {}) {
|
|
|
1203
1340
|
recommendedCursorIgnore,
|
|
1204
1341
|
missingClaudeIgnoreSuggestions,
|
|
1205
1342
|
missingCursorIgnoreSuggestions,
|
|
1343
|
+
sessionIgnoreSuggestions,
|
|
1206
1344
|
topTokenLeaks: getTopTokenLeaks(issues),
|
|
1207
1345
|
generatedAt: new Date().toISOString(),
|
|
1208
1346
|
};
|
|
@@ -186,7 +186,7 @@ function looksLikeUsefulPath(relPath) {
|
|
|
186
186
|
function extractMentionedPaths(text, cwd = "") {
|
|
187
187
|
const found = new Set();
|
|
188
188
|
const source = String(text || "");
|
|
189
|
-
const pathPattern = /(?:^|[\s"'`])((?:\.{0,2}\/)?(?:[\w
|
|
189
|
+
const pathPattern = /(?:^|[\s"'`])((?:\.{0,2}\/)?(?:[\w.@-]+\/)+[\w.@+-]+\.[A-Za-z0-9]{1,12})/g;
|
|
190
190
|
const filePattern = /(?:^|[\s"'`])((?:package-lock\.json|pnpm-lock\.yaml|yarn\.lock|coverage-final\.json|tsconfig\.json|pyproject\.toml|requirements\.txt|README\.md|CLAUDE\.md|AGENTS\.md))/g;
|
|
191
191
|
for (const pattern of [pathPattern, filePattern]) {
|
|
192
192
|
let match;
|
package/lib/prismo-dev-scan.js
CHANGED
|
@@ -283,7 +283,11 @@ const {
|
|
|
283
283
|
color,
|
|
284
284
|
});
|
|
285
285
|
|
|
286
|
-
const {
|
|
286
|
+
const {
|
|
287
|
+
renderMcpDoctorTerminal,
|
|
288
|
+
runMcpDoctor,
|
|
289
|
+
runMcpServer,
|
|
290
|
+
} = require("./prismo-dev/mcp");
|
|
287
291
|
|
|
288
292
|
function printHelp() {
|
|
289
293
|
console.log(`Prismo CLI
|
|
@@ -298,6 +302,7 @@ Usage:
|
|
|
298
302
|
prismo shield last [--json] [--limit N] [path]
|
|
299
303
|
prismo shield search <query> [--json] [--limit N] [path]
|
|
300
304
|
prismo mcp [path]
|
|
305
|
+
prismo mcp doctor [--json] [path]
|
|
301
306
|
prismo setup [--json] [--proxy-url URL] [path]
|
|
302
307
|
prismo scan [--fix] [--ci] [--json] [--usage] [--simple] [--no-report] [path]
|
|
303
308
|
prismo optimize [scope] [--json] [path]
|
|
@@ -521,10 +526,13 @@ Output:
|
|
|
521
526
|
|
|
522
527
|
Usage:
|
|
523
528
|
prismo mcp [path]
|
|
529
|
+
prismo mcp doctor [--json] [path]
|
|
524
530
|
|
|
525
531
|
Examples:
|
|
526
532
|
prismo mcp
|
|
527
533
|
prismo mcp /path/to/repo
|
|
534
|
+
prismo mcp doctor
|
|
535
|
+
prismo mcp doctor --json
|
|
528
536
|
|
|
529
537
|
Tools exposed:
|
|
530
538
|
prismo_scan
|
|
@@ -538,7 +546,9 @@ Tools exposed:
|
|
|
538
546
|
prismo_cc_timeline
|
|
539
547
|
|
|
540
548
|
Output:
|
|
541
|
-
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
|
|
549
|
+
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.
|
|
550
|
+
|
|
551
|
+
prismo mcp doctor validates the local MCP tool surface and prints a ready-to-use client config snippet.`,
|
|
542
552
|
ci: `Prismo CI
|
|
543
553
|
|
|
544
554
|
Usage:
|
|
@@ -707,8 +717,11 @@ async function runCli(argv) {
|
|
|
707
717
|
}
|
|
708
718
|
|
|
709
719
|
if (command === "mcp") {
|
|
710
|
-
const
|
|
711
|
-
|
|
720
|
+
const json = rest.includes("--json");
|
|
721
|
+
const positional = getPositionals(rest);
|
|
722
|
+
const subcommand = positional[0] === "doctor" ? "doctor" : "server";
|
|
723
|
+
const target = subcommand === "doctor" ? positional[1] || process.cwd() : positional[0] || process.cwd();
|
|
724
|
+
const mcpDeps = {
|
|
712
725
|
rootDir: path.resolve(target),
|
|
713
726
|
packageVersion: PACKAGE_VERSION,
|
|
714
727
|
scanRepo,
|
|
@@ -724,7 +737,14 @@ async function runCli(argv) {
|
|
|
724
737
|
runShield,
|
|
725
738
|
runShieldLast,
|
|
726
739
|
runShieldSearch,
|
|
727
|
-
}
|
|
740
|
+
};
|
|
741
|
+
if (subcommand === "doctor") {
|
|
742
|
+
const result = await runMcpDoctor(mcpDeps);
|
|
743
|
+
if (json) console.log(JSON.stringify(result, null, 2));
|
|
744
|
+
else console.log(renderMcpDoctorTerminal(result));
|
|
745
|
+
return;
|
|
746
|
+
}
|
|
747
|
+
runMcpServer(mcpDeps);
|
|
728
748
|
return;
|
|
729
749
|
}
|
|
730
750
|
|
package/package.json
CHANGED