getprismo 0.1.25 → 0.1.26
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 +14 -1
- package/docs/mcp.md +1 -0
- package/lib/prismo-dev/doctor.js +12 -0
- package/lib/prismo-dev/mcp.js +24 -2
- package/lib/prismo-dev/report.js +24 -0
- package/lib/prismo-dev/usage-watch.js +23 -20
- package/lib/prismo-dev/watch-live.js +103 -0
- package/lib/prismo-dev/watch-render.js +65 -0
- package/lib/prismo-dev-scan.js +6 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -552,6 +552,16 @@ watch reads local session logs from codex and claude code. it detects:
|
|
|
552
552
|
|
|
553
553
|
watch tells you the single most useful action to take right now. usually: start a fresh session, or switch to a scoped context pack.
|
|
554
554
|
|
|
555
|
+
if you run multiple agents in the same repo, use:
|
|
556
|
+
|
|
557
|
+
```bash
|
|
558
|
+
npx getprismo watch --agents
|
|
559
|
+
```
|
|
560
|
+
|
|
561
|
+
multi-agent watch shows every visible local Codex/Claude Code session for the repo, ranks each agent by context pressure, and flags coordination risks like two agents repeatedly loading the same file, shared artifact leaks, multiple high-pressure sessions, or agents that should move noisy commands into `shield`.
|
|
562
|
+
|
|
563
|
+
the same multi-agent coordination signal is included in `usage --json`, `scan --usage --json`, doctor output, and the generated markdown report whenever multiple local sessions are visible for the repo.
|
|
564
|
+
|
|
555
565
|
`watch --rescue` prints a paste-ready prompt for the active coding session. use it when the agent is looping, reading too many files, or flooding context with logs:
|
|
556
566
|
|
|
557
567
|
```bash
|
|
@@ -644,6 +654,8 @@ npx getprismo doctor --json # machine-readable output
|
|
|
644
654
|
```bash
|
|
645
655
|
npx getprismo watch # live refresh
|
|
646
656
|
npx getprismo watch --once # single snapshot
|
|
657
|
+
npx getprismo watch --agents # multi-agent coordination view
|
|
658
|
+
npx getprismo watch --agents --json # machine-readable multi-agent state
|
|
647
659
|
npx getprismo watch --once --report # write .prismo/watch-report.md
|
|
648
660
|
npx getprismo watch --once --json # machine-readable
|
|
649
661
|
npx getprismo watch --auto # guardrails + throttle + 600k budget
|
|
@@ -681,6 +693,7 @@ npx getprismo mcp /path/to/repo
|
|
|
681
693
|
- `prismo_scan`
|
|
682
694
|
- `prismo_doctor_dry_run`
|
|
683
695
|
- `prismo_watch_snapshot`
|
|
696
|
+
- `prismo_multi_agent_watch`
|
|
684
697
|
- `prismo_shield_run`
|
|
685
698
|
- `prismo_shield_search`
|
|
686
699
|
- `prismo_shield_last`
|
|
@@ -688,7 +701,7 @@ npx getprismo mcp /path/to/repo
|
|
|
688
701
|
- `prismo_firewall`
|
|
689
702
|
- `prismo_cc_timeline`
|
|
690
703
|
|
|
691
|
-
This lets an MCP-compatible agent search prior shielded test/build output, request scoped context packs,
|
|
704
|
+
This lets an MCP-compatible agent search prior shielded test/build output, request scoped context packs, inspect token-waste signals, or coordinate multiple local agents without pasting giant logs into the conversation.
|
|
692
705
|
|
|
693
706
|
Generic MCP client config:
|
|
694
707
|
|
package/docs/mcp.md
CHANGED
|
@@ -49,6 +49,7 @@ For local development from this repo:
|
|
|
49
49
|
- `prismo_scan`: scan repo context/token waste
|
|
50
50
|
- `prismo_doctor_dry_run`: preview doctor payoff without writing files
|
|
51
51
|
- `prismo_watch_snapshot`: inspect live context pressure
|
|
52
|
+
- `prismo_multi_agent_watch`: inspect coordination risks across parallel local agents
|
|
52
53
|
- `prismo_shield_run`: run a noisy command and store full output locally
|
|
53
54
|
- `prismo_shield_search`: search stored shield output
|
|
54
55
|
- `prismo_shield_last`: list recent shielded command runs
|
package/lib/prismo-dev/doctor.js
CHANGED
|
@@ -324,6 +324,7 @@ function toDoctorJsonPayload(result) {
|
|
|
324
324
|
confidence: usage.confidence,
|
|
325
325
|
totals: usage.totals,
|
|
326
326
|
sources: usage.sources,
|
|
327
|
+
multiAgent: usage.multiAgent || null,
|
|
327
328
|
}
|
|
328
329
|
: null;
|
|
329
330
|
return {
|
|
@@ -381,6 +382,13 @@ function renderDoctorTerminal(result) {
|
|
|
381
382
|
if (result.before.realUsage && result.before.realUsage.sessions.length) {
|
|
382
383
|
lines.push(`Local usage: ${formatTokenCount(result.before.realUsage.totals.displayTokens)} tokens across ${result.before.realUsage.sessions.length} recent session(s)`);
|
|
383
384
|
}
|
|
385
|
+
const multiAgent = result.before.realUsage && result.before.realUsage.multiAgent;
|
|
386
|
+
if (multiAgent && multiAgent.agentCount > 1) {
|
|
387
|
+
lines.push(`Multi-agent: ${multiAgent.agentCount} agents visible; highest pressure ${multiAgent.highestPressure}`);
|
|
388
|
+
if (multiAgent.coordinationWarnings && multiAgent.coordinationWarnings[0]) {
|
|
389
|
+
lines.push(`Coordination warning: ${multiAgent.coordinationWarnings[0]}`);
|
|
390
|
+
}
|
|
391
|
+
}
|
|
384
392
|
if (!(result.dryRun && result.applySuggestions)) lines.push(`Estimated exposed context reduction: ${result.exposedTokenReductionPercent}%`);
|
|
385
393
|
if (!result.dryRun) {
|
|
386
394
|
lines.push(`Payoff: ${scoreDelta > 0 ? `repo is ${scoreDelta} points cleaner for AI coding sessions` : "safe fixes applied; remaining risk needs manual cleanup"}`);
|
|
@@ -457,6 +465,10 @@ function renderDevTerminal(result) {
|
|
|
457
465
|
} else if (result.scan.realUsage) {
|
|
458
466
|
lines.push("Real local usage: no matching local sessions found for this repo");
|
|
459
467
|
}
|
|
468
|
+
const multiAgent = result.scan.realUsage && result.scan.realUsage.multiAgent;
|
|
469
|
+
if (multiAgent && multiAgent.agentCount > 1) {
|
|
470
|
+
lines.push(`Multi-agent: ${multiAgent.agentCount} agents visible; highest pressure ${multiAgent.highestPressure}`);
|
|
471
|
+
}
|
|
460
472
|
lines.push("");
|
|
461
473
|
lines.push("Generated:");
|
|
462
474
|
result.optimize.generatedFiles.slice(0, 8).forEach((file) => lines.push(`- ${file}`));
|
package/lib/prismo-dev/mcp.js
CHANGED
|
@@ -69,6 +69,11 @@ function createMcpTools(deps) {
|
|
|
69
69
|
tool: { type: "string", enum: ["all", "codex", "claude"], description: "Which local session logs to inspect." },
|
|
70
70
|
limit: limitProperty,
|
|
71
71
|
}),
|
|
72
|
+
makeTool("prismo_multi_agent_watch", "Return multi-agent coordination risks across visible local Codex/Claude sessions.", {
|
|
73
|
+
path: pathProperty,
|
|
74
|
+
tool: { type: "string", enum: ["all", "codex", "claude"], description: "Which local session logs to inspect." },
|
|
75
|
+
limit: limitProperty,
|
|
76
|
+
}),
|
|
72
77
|
makeTool("prismo_shield_run", "Run a noisy command through Prismo shield and store full output locally.", {
|
|
73
78
|
path: pathProperty,
|
|
74
79
|
command: {
|
|
@@ -131,11 +136,27 @@ function createMcpTools(deps) {
|
|
|
131
136
|
const summary = getUsageSummary({
|
|
132
137
|
cwd: target,
|
|
133
138
|
limit: Number(args.limit) || 3,
|
|
134
|
-
|
|
139
|
+
tool: args.tool || "all",
|
|
135
140
|
});
|
|
136
141
|
return createTextResult(summary);
|
|
137
142
|
}
|
|
138
143
|
|
|
144
|
+
if (name === "prismo_multi_agent_watch") {
|
|
145
|
+
const summary = getUsageSummary({
|
|
146
|
+
cwd: target,
|
|
147
|
+
limit: Number(args.limit) || 8,
|
|
148
|
+
tool: args.tool || "all",
|
|
149
|
+
});
|
|
150
|
+
return createTextResult({
|
|
151
|
+
schemaVersion: 1,
|
|
152
|
+
generatedAt: summary.generatedAt,
|
|
153
|
+
scannedPath: summary.scannedPath,
|
|
154
|
+
tool: summary.tool,
|
|
155
|
+
totals: summary.totals,
|
|
156
|
+
multiAgent: summary.multiAgent,
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
|
|
139
160
|
if (name === "prismo_shield_run") {
|
|
140
161
|
return createTextResult(runShield(target, args.command));
|
|
141
162
|
}
|
|
@@ -265,6 +286,7 @@ async function runMcpDoctor(deps) {
|
|
|
265
286
|
"prismo_scan",
|
|
266
287
|
"prismo_doctor_dry_run",
|
|
267
288
|
"prismo_watch_snapshot",
|
|
289
|
+
"prismo_multi_agent_watch",
|
|
268
290
|
"prismo_shield_run",
|
|
269
291
|
"prismo_shield_search",
|
|
270
292
|
"prismo_shield_last",
|
|
@@ -315,7 +337,7 @@ async function runMcpDoctor(deps) {
|
|
|
315
337
|
next: [
|
|
316
338
|
"Add the config snippet to your MCP-compatible client.",
|
|
317
339
|
"Restart the client and confirm prismodev appears in the MCP tool list.",
|
|
318
|
-
"Ask the agent to call prismo_scan or prismo_shield_run.",
|
|
340
|
+
"Ask the agent to call prismo_scan, prismo_multi_agent_watch, or prismo_shield_run.",
|
|
319
341
|
],
|
|
320
342
|
};
|
|
321
343
|
}
|
package/lib/prismo-dev/report.js
CHANGED
|
@@ -45,6 +45,10 @@ function renderTerminalReport(result, options = {}) {
|
|
|
45
45
|
if (result.realUsage && result.realUsage.sessions.length) {
|
|
46
46
|
lines.push(`- Real local usage: ${formatTokenCount(result.realUsage.totals.displayTokens)} tokens across ${result.realUsage.sessions.length} session(s)`);
|
|
47
47
|
lines.push(`- Usage confidence: ${result.realUsage.confidence}`);
|
|
48
|
+
if (result.realUsage.multiAgent && result.realUsage.multiAgent.agentCount > 1) {
|
|
49
|
+
lines.push(`- Multi-agent: ${result.realUsage.multiAgent.agentCount} agents visible; highest pressure ${result.realUsage.multiAgent.highestPressure}`);
|
|
50
|
+
if (result.realUsage.multiAgent.coordinationWarnings[0]) lines.push(`- Coordination warning: ${result.realUsage.multiAgent.coordinationWarnings[0]}`);
|
|
51
|
+
}
|
|
48
52
|
} else if (result.realUsage) {
|
|
49
53
|
lines.push("- Real local usage: no matching local Codex/Claude Code sessions found for this repo");
|
|
50
54
|
}
|
|
@@ -116,6 +120,9 @@ function renderOptimizerFitTerminal(result, options = {}) {
|
|
|
116
120
|
lines.push(`Primary bottleneck: ${color(fit.summary, tone, useColor)}`);
|
|
117
121
|
if (result.realUsage && result.realUsage.sessions.length) {
|
|
118
122
|
lines.push(`Local usage: ${formatTokenCount(result.realUsage.totals.displayTokens)} tokens across ${result.realUsage.sessions.length} session(s)`);
|
|
123
|
+
if (result.realUsage.multiAgent && result.realUsage.multiAgent.agentCount > 1) {
|
|
124
|
+
lines.push(`Multi-agent: ${result.realUsage.multiAgent.agentCount} agents visible; highest pressure ${result.realUsage.multiAgent.highestPressure}`);
|
|
125
|
+
}
|
|
119
126
|
} else if (result.realUsage) {
|
|
120
127
|
lines.push("Local usage: no matching local Claude/Codex sessions found");
|
|
121
128
|
}
|
|
@@ -261,6 +268,9 @@ function renderMarkdownReport(result) {
|
|
|
261
268
|
if (result.realUsage) {
|
|
262
269
|
lines.push(`- **Real Local Usage:** ${result.realUsage.totals.displayTokens.toLocaleString()} tokens across ${result.realUsage.sessions.length} session(s)`);
|
|
263
270
|
lines.push(`- **Usage Confidence:** ${result.realUsage.confidence}`);
|
|
271
|
+
if (result.realUsage.multiAgent && result.realUsage.multiAgent.agentCount > 1) {
|
|
272
|
+
lines.push(`- **Multi-Agent:** ${result.realUsage.multiAgent.agentCount} agents visible; highest pressure ${result.realUsage.multiAgent.highestPressure}`);
|
|
273
|
+
}
|
|
264
274
|
}
|
|
265
275
|
lines.push("");
|
|
266
276
|
lines.push("Estimates are based on local file-size and configuration heuristics. They are not provider billing data and are not guaranteed savings.");
|
|
@@ -346,6 +356,20 @@ function renderMarkdownReport(result) {
|
|
|
346
356
|
lines.push(`- Estimated tool/output tokens: ${result.realUsage.totals.toolTokens.toLocaleString()}`);
|
|
347
357
|
lines.push(`- Confidence: ${result.realUsage.confidence}`);
|
|
348
358
|
lines.push("");
|
|
359
|
+
if (result.realUsage.multiAgent && result.realUsage.multiAgent.agentCount > 1) {
|
|
360
|
+
lines.push("### Multi-Agent Coordination");
|
|
361
|
+
lines.push("");
|
|
362
|
+
lines.push(`- Agents visible: ${result.realUsage.multiAgent.agentCount}`);
|
|
363
|
+
lines.push(`- Highest pressure: ${result.realUsage.multiAgent.highestPressure}`);
|
|
364
|
+
result.realUsage.multiAgent.coordinationWarnings.slice(0, 5).forEach((warning) => lines.push(`- ${warning}`));
|
|
365
|
+
if (result.realUsage.multiAgent.sharedFiles.length) {
|
|
366
|
+
lines.push(`- Shared repeated files: ${result.realUsage.multiAgent.sharedFiles.slice(0, 5).map((item) => `\`${item.path}\` (${item.agents} agents)`).join(", ")}`);
|
|
367
|
+
}
|
|
368
|
+
if (result.realUsage.multiAgent.sharedArtifacts.length) {
|
|
369
|
+
lines.push(`- Shared artifact leaks: ${result.realUsage.multiAgent.sharedArtifacts.slice(0, 5).map((item) => `${item.type} (${item.agents} agents)`).join(", ")}`);
|
|
370
|
+
}
|
|
371
|
+
lines.push("");
|
|
372
|
+
}
|
|
349
373
|
result.realUsage.sessions.slice(0, 5).forEach((session, index) => {
|
|
350
374
|
lines.push(`${index + 1}. ${session.tool} - ${session.title || session.sessionId}`);
|
|
351
375
|
lines.push(` - Tokens: ${session.displayTokens.toLocaleString()} (${session.confidence})`);
|
|
@@ -53,6 +53,7 @@ const {
|
|
|
53
53
|
|
|
54
54
|
const {
|
|
55
55
|
buildLiveSessionView,
|
|
56
|
+
buildMultiAgentView,
|
|
56
57
|
} = require("./watch-live")({
|
|
57
58
|
NPX_COMMAND,
|
|
58
59
|
formatTokenCount,
|
|
@@ -65,7 +66,7 @@ const {
|
|
|
65
66
|
getAllClaudeSessionFiles,
|
|
66
67
|
getClaudeSessionFiles,
|
|
67
68
|
getCodexSessionFiles,
|
|
68
|
-
getUsageSummary,
|
|
69
|
+
getUsageSummary: getBaseUsageSummary,
|
|
69
70
|
} = require("./usage-sessions")({
|
|
70
71
|
fs,
|
|
71
72
|
os,
|
|
@@ -76,15 +77,25 @@ const {
|
|
|
76
77
|
readIfText,
|
|
77
78
|
});
|
|
78
79
|
|
|
80
|
+
function getUsageSummary(options = {}) {
|
|
81
|
+
const summary = getBaseUsageSummary(options);
|
|
82
|
+
if ((summary.sessions || []).length > 1) {
|
|
83
|
+
summary.multiAgent = buildMultiAgentView(summary);
|
|
84
|
+
}
|
|
85
|
+
return summary;
|
|
86
|
+
}
|
|
87
|
+
|
|
79
88
|
const {
|
|
80
89
|
renderContextThrottle,
|
|
81
90
|
renderLiveGuardrails,
|
|
91
|
+
renderMultiAgentWatchTerminal,
|
|
82
92
|
renderRescuePrompt,
|
|
83
93
|
renderUsageTerminal,
|
|
84
94
|
renderWatchTerminal,
|
|
85
95
|
} = require("./watch-render")({
|
|
86
96
|
NPX_COMMAND,
|
|
87
97
|
buildLiveSessionView,
|
|
98
|
+
buildMultiAgentView,
|
|
88
99
|
color,
|
|
89
100
|
formatTokenCount,
|
|
90
101
|
getActionableRepeatedPaths,
|
|
@@ -199,24 +210,6 @@ function parseScopeAndTarget(args, valueFlags = new Set()) {
|
|
|
199
210
|
return { scope: null, target: positional[0] || process.cwd() };
|
|
200
211
|
}
|
|
201
212
|
|
|
202
|
-
function formatTokenCount(value) {
|
|
203
|
-
const n = Number(value || 0);
|
|
204
|
-
if (n >= 1000000) return `${(n / 1000000).toFixed(2)}M`;
|
|
205
|
-
if (n >= 1000) return `${Math.round(n / 1000)}k`;
|
|
206
|
-
return String(Math.round(n));
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
function formatMoney(value) {
|
|
210
|
-
const n = Number(value || 0);
|
|
211
|
-
if (n >= 1) return `$${n.toFixed(2)}`;
|
|
212
|
-
return `$${n.toFixed(4)}`;
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
213
|
function writeLiveFile(root, relPath, contents) {
|
|
221
214
|
const fullPath = path.join(root || process.cwd(), relPath);
|
|
222
215
|
fs.mkdirSync(path.dirname(fullPath), { recursive: true });
|
|
@@ -432,6 +425,7 @@ function compactUsageSummary(summary) {
|
|
|
432
425
|
totals: summary.totals,
|
|
433
426
|
sources: summary.sources,
|
|
434
427
|
sessions: (summary.sessions || []).map(compactWatchSession),
|
|
428
|
+
multiAgent: summary.multiAgent || ((summary.sessions || []).length > 1 ? buildMultiAgentView(summary) : null),
|
|
435
429
|
};
|
|
436
430
|
}
|
|
437
431
|
|
|
@@ -484,6 +478,7 @@ function toWatchJsonPayload(summary) {
|
|
|
484
478
|
sources: summary.sources,
|
|
485
479
|
sessions: summary.sessions.map(compactWatchSession),
|
|
486
480
|
live: summary.live || buildLiveSessionView(summary),
|
|
481
|
+
multiAgent: summary.multiAgent || (summary.agents ? buildMultiAgentView(summary) : null),
|
|
487
482
|
auto: Boolean(summary.auto),
|
|
488
483
|
rescuePrompt: summary.includeRescuePrompt ? renderRescuePrompt(summary) : null,
|
|
489
484
|
guardrailsPath: summary.guardrailsPath || null,
|
|
@@ -502,7 +497,11 @@ async function watchUsage(options = {}) {
|
|
|
502
497
|
for (let i = 0; i < iterations; i += 1) {
|
|
503
498
|
const summary = getUsageSummary(options);
|
|
504
499
|
summary.auto = Boolean(options.auto);
|
|
500
|
+
summary.agents = Boolean(options.agents);
|
|
505
501
|
summary.live = buildLiveSessionView(summary);
|
|
502
|
+
if (options.agents) {
|
|
503
|
+
summary.multiAgent = buildMultiAgentView(summary);
|
|
504
|
+
}
|
|
506
505
|
if (options.guardrails) {
|
|
507
506
|
const written = writeLiveGuardrails(summary);
|
|
508
507
|
summary.guardrailsPath = written.guardrailsPath;
|
|
@@ -524,7 +523,11 @@ async function watchUsage(options = {}) {
|
|
|
524
523
|
}
|
|
525
524
|
summary.redactPaths = Boolean(options.redactPaths);
|
|
526
525
|
summary.includeRescuePrompt = Boolean(options.rescue);
|
|
527
|
-
if (options.
|
|
526
|
+
if (options.agents && !options.json) {
|
|
527
|
+
console.clear();
|
|
528
|
+
console.log(renderMultiAgentWatchTerminal(summary));
|
|
529
|
+
if (!options.once) console.log(`\nRefreshing every ${Math.round(intervalMs / 1000)}s. Press Ctrl+C to stop.`);
|
|
530
|
+
} else if (options.rescue && !options.json) {
|
|
528
531
|
console.log(renderRescuePrompt(summary));
|
|
529
532
|
} else if (options.json) {
|
|
530
533
|
console.log(JSON.stringify(toWatchJsonPayload(summary), null, 2));
|
|
@@ -345,8 +345,111 @@ module.exports = function createWatchLive(deps) {
|
|
|
345
345
|
};
|
|
346
346
|
}
|
|
347
347
|
|
|
348
|
+
function agentLabel(session, index) {
|
|
349
|
+
const tool = session.tool === "claude-code" ? "claude" : session.tool || "agent";
|
|
350
|
+
const id = String(session.sessionId || index + 1).slice(0, 8);
|
|
351
|
+
return `${tool}-${id}`;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
function buildMultiAgentView(summary) {
|
|
355
|
+
const agents = (summary.sessions || []).map((session, index) => {
|
|
356
|
+
const singleSummary = {
|
|
357
|
+
...summary,
|
|
358
|
+
sessions: [session],
|
|
359
|
+
totals: {
|
|
360
|
+
sessions: 1,
|
|
361
|
+
displayTokens: session.displayTokens || 0,
|
|
362
|
+
contextTokens: session.contextTokens || 0,
|
|
363
|
+
estimatedTokens: session.estimatedTotalTokens || 0,
|
|
364
|
+
exactTokens: session.exactAvailable ? session.exactTotalTokens || 0 : 0,
|
|
365
|
+
toolTokens: session.estimatedToolTokens || 0,
|
|
366
|
+
},
|
|
367
|
+
};
|
|
368
|
+
const live = buildLiveSessionView(singleSummary);
|
|
369
|
+
return {
|
|
370
|
+
label: agentLabel(session, index),
|
|
371
|
+
tool: session.tool,
|
|
372
|
+
sessionId: session.sessionId,
|
|
373
|
+
title: session.title,
|
|
374
|
+
model: session.model,
|
|
375
|
+
updatedAt: session.updatedAt,
|
|
376
|
+
contextPressure: live.contextPressure,
|
|
377
|
+
tokens: session.displayTokens || 0,
|
|
378
|
+
contextTokens: session.contextTokens || 0,
|
|
379
|
+
toolOutputTokens: session.estimatedToolTokens || 0,
|
|
380
|
+
turns: session.turns || 0,
|
|
381
|
+
toolCalls: session.toolCalls || 0,
|
|
382
|
+
liveAction: live.liveAction,
|
|
383
|
+
warnings: live.warnings,
|
|
384
|
+
repeatedFiles: live.activeSession?.actionableRepeatedPaths || [],
|
|
385
|
+
artifactGroups: live.activeSession?.generatedArtifactGroups || [],
|
|
386
|
+
repeatedCommands: session.repeatedCommands || [],
|
|
387
|
+
};
|
|
388
|
+
});
|
|
389
|
+
|
|
390
|
+
const warnings = [];
|
|
391
|
+
const highPressureAgents = agents.filter((agent) => agent.contextPressure === "High");
|
|
392
|
+
if (highPressureAgents.length >= 2) {
|
|
393
|
+
warnings.push(`${highPressureAgents.length} agents are under high context pressure; pause broad exploration and split work by task boundary.`);
|
|
394
|
+
}
|
|
395
|
+
const noisyAgents = agents.filter((agent) => ["tool-output-flood", "possible-loop"].includes(agent.liveAction?.cause));
|
|
396
|
+
if (noisyAgents.length) {
|
|
397
|
+
warnings.push(`${noisyAgents.length} agent${noisyAgents.length === 1 ? "" : "s"} should route noisy commands through shield before rerunning.`);
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
const fileOwners = new Map();
|
|
401
|
+
for (const agent of agents) {
|
|
402
|
+
for (const item of agent.repeatedFiles || []) {
|
|
403
|
+
const owners = fileOwners.get(item.value) || [];
|
|
404
|
+
owners.push({ agent: agent.label, count: item.count });
|
|
405
|
+
fileOwners.set(item.value, owners);
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
const sharedFiles = Array.from(fileOwners.entries())
|
|
409
|
+
.filter(([, owners]) => owners.length >= 2)
|
|
410
|
+
.map(([path, owners]) => ({ path, owners }))
|
|
411
|
+
.slice(0, 5);
|
|
412
|
+
for (const item of sharedFiles.slice(0, 3)) {
|
|
413
|
+
warnings.push(`${item.path} is repeatedly entering context across ${item.owners.length} agents.`);
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
const artifactOwners = new Map();
|
|
417
|
+
for (const agent of agents) {
|
|
418
|
+
for (const group of agent.artifactGroups || []) {
|
|
419
|
+
const owners = artifactOwners.get(group.type) || [];
|
|
420
|
+
owners.push({ agent: agent.label, count: group.count, example: group.examples?.[0] || null });
|
|
421
|
+
artifactOwners.set(group.type, owners);
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
const sharedArtifacts = Array.from(artifactOwners.entries())
|
|
425
|
+
.filter(([, owners]) => owners.length >= 2)
|
|
426
|
+
.map(([type, owners]) => ({ type, owners }))
|
|
427
|
+
.slice(0, 5);
|
|
428
|
+
for (const item of sharedArtifacts.slice(0, 2)) {
|
|
429
|
+
warnings.push(`${item.type} artifacts appear across ${item.owners.length} agents; add/verify ignore coverage before continuing.`);
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
const recommendedActions = [];
|
|
433
|
+
if (noisyAgents.length) recommendedActions.push(`${NPX_COMMAND} shield -- <noisy command>`);
|
|
434
|
+
if (sharedFiles.length || sharedArtifacts.length) recommendedActions.push(`${NPX_COMMAND} doctor --apply-suggestions --dry-run`);
|
|
435
|
+
if (highPressureAgents.length) recommendedActions.push("Ask each high-pressure agent for a compact handoff summary, then restart scoped sessions.");
|
|
436
|
+
if (!recommendedActions.length) recommendedActions.push("Keep agents scoped to separate files/tasks and continue watching.");
|
|
437
|
+
|
|
438
|
+
return {
|
|
439
|
+
enabled: true,
|
|
440
|
+
agentCount: agents.length,
|
|
441
|
+
agents,
|
|
442
|
+
highestPressure: agents.reduce((pressure, agent) => (getRiskRank(agent.contextPressure) > getRiskRank(pressure) ? agent.contextPressure : pressure), "Low"),
|
|
443
|
+
coordinationWarnings: Array.from(new Set(warnings)).slice(0, 8),
|
|
444
|
+
sharedFiles,
|
|
445
|
+
sharedArtifacts,
|
|
446
|
+
recommendedActions: Array.from(new Set(recommendedActions)).slice(0, 5),
|
|
447
|
+
};
|
|
448
|
+
}
|
|
449
|
+
|
|
348
450
|
return {
|
|
349
451
|
buildLiveSessionView,
|
|
452
|
+
buildMultiAgentView,
|
|
350
453
|
getRiskRank,
|
|
351
454
|
getTokenBudgetStatus,
|
|
352
455
|
};
|
|
@@ -2,6 +2,7 @@ module.exports = function createWatchRender(deps) {
|
|
|
2
2
|
const {
|
|
3
3
|
NPX_COMMAND,
|
|
4
4
|
buildLiveSessionView,
|
|
5
|
+
buildMultiAgentView,
|
|
5
6
|
color,
|
|
6
7
|
formatTokenCount,
|
|
7
8
|
getActionableRepeatedPaths,
|
|
@@ -33,6 +34,15 @@ function renderUsageTerminal(summary, title = "Prismo Usage") {
|
|
|
33
34
|
}
|
|
34
35
|
lines.push("");
|
|
35
36
|
lines.push("Notes: exact means the local tool log exposed token fields. Estimated means Prismo used local text size heuristics only.");
|
|
37
|
+
const multi = summary.multiAgent || ((summary.sessions || []).length > 1 && buildMultiAgentView ? buildMultiAgentView(summary) : null);
|
|
38
|
+
if (multi && multi.agentCount > 1) {
|
|
39
|
+
lines.push("");
|
|
40
|
+
lines.push("Multi-Agent Coordination:");
|
|
41
|
+
lines.push(`- Agents visible: ${multi.agentCount}`);
|
|
42
|
+
lines.push(`- Highest pressure: ${multi.highestPressure}`);
|
|
43
|
+
if (multi.coordinationWarnings.length) lines.push(`- Top warning: ${multi.coordinationWarnings[0]}`);
|
|
44
|
+
lines.push(`- Live view: ${NPX_COMMAND} watch --agents`);
|
|
45
|
+
}
|
|
36
46
|
return lines.join("\n");
|
|
37
47
|
}
|
|
38
48
|
function renderWatchTerminal(summary) {
|
|
@@ -107,6 +117,60 @@ function renderWatchTerminal(summary) {
|
|
|
107
117
|
lines.push("Expected project instruction files are muted unless they combine with stronger context-pressure signals.");
|
|
108
118
|
return lines.join("\n");
|
|
109
119
|
}
|
|
120
|
+
function renderMultiAgentWatchTerminal(summary) {
|
|
121
|
+
const multi = summary.multiAgent;
|
|
122
|
+
const lines = [];
|
|
123
|
+
lines.push("");
|
|
124
|
+
lines.push(color("Prismo Multi-Agent Watch", "bold"));
|
|
125
|
+
lines.push("");
|
|
126
|
+
if (!multi || !multi.agentCount) {
|
|
127
|
+
lines.push("Agents: 0");
|
|
128
|
+
lines.push("- No local Codex/Claude Code sessions detected for this repo yet.");
|
|
129
|
+
lines.push("");
|
|
130
|
+
lines.push("Suggested Action");
|
|
131
|
+
lines.push(`Run: ${NPX_COMMAND} setup`);
|
|
132
|
+
return lines.join("\n");
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const pressureTone = multi.highestPressure === "High" ? "red" : multi.highestPressure === "Medium" ? "yellow" : "green";
|
|
136
|
+
lines.push(`Agents: ${multi.agentCount}`);
|
|
137
|
+
lines.push(`Highest Pressure: ${color(String(multi.highestPressure).toUpperCase(), pressureTone)}`);
|
|
138
|
+
lines.push("");
|
|
139
|
+
lines.push("Active Agents");
|
|
140
|
+
multi.agents.slice(0, 8).forEach((agent) => {
|
|
141
|
+
const tone = agent.contextPressure === "High" ? "red" : agent.contextPressure === "Medium" ? "yellow" : "green";
|
|
142
|
+
lines.push(`- ${agent.label} ${agent.tool} ${color(agent.contextPressure.toUpperCase(), tone)} ${formatTokenCount(agent.tokens)} tokens ${agent.liveAction.cause}`);
|
|
143
|
+
if (agent.updatedAt) lines.push(` updated: ${agent.updatedAt}`);
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
lines.push("");
|
|
147
|
+
lines.push("Coordination Warnings");
|
|
148
|
+
if (multi.coordinationWarnings.length) multi.coordinationWarnings.forEach((warning) => lines.push(`- ${warning}`));
|
|
149
|
+
else lines.push("- No cross-agent coordination risks detected.");
|
|
150
|
+
|
|
151
|
+
if (multi.sharedFiles.length) {
|
|
152
|
+
lines.push("");
|
|
153
|
+
lines.push("Shared Repeated Files");
|
|
154
|
+
multi.sharedFiles.slice(0, 5).forEach((item) => {
|
|
155
|
+
lines.push(`- ${item.path} (${item.owners.map((owner) => `${owner.agent}:${owner.count}x`).join(", ")})`);
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
if (multi.sharedArtifacts.length) {
|
|
160
|
+
lines.push("");
|
|
161
|
+
lines.push("Shared Artifact Leaks");
|
|
162
|
+
multi.sharedArtifacts.slice(0, 5).forEach((item) => {
|
|
163
|
+
lines.push(`- ${item.type} (${item.owners.map((owner) => `${owner.agent}:${owner.count}x`).join(", ")})`);
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
lines.push("");
|
|
168
|
+
lines.push("Do This Now");
|
|
169
|
+
multi.recommendedActions.forEach((action, index) => lines.push(`${index + 1}. ${action}`));
|
|
170
|
+
lines.push("");
|
|
171
|
+
lines.push("Signals are local estimates from available coding-agent logs. Use this view to coordinate parallel agents before they duplicate work or flood context.");
|
|
172
|
+
return lines.join("\n");
|
|
173
|
+
}
|
|
110
174
|
function renderRescuePrompt(summary) {
|
|
111
175
|
const live = summary.live || buildLiveSessionView(summary);
|
|
112
176
|
const active = live.activeSession;
|
|
@@ -283,6 +347,7 @@ function renderContextThrottle(summary) {
|
|
|
283
347
|
return {
|
|
284
348
|
renderContextThrottle,
|
|
285
349
|
renderLiveGuardrails,
|
|
350
|
+
renderMultiAgentWatchTerminal,
|
|
286
351
|
renderRescuePrompt,
|
|
287
352
|
renderUsageTerminal,
|
|
288
353
|
renderWatchTerminal,
|
package/lib/prismo-dev-scan.js
CHANGED
|
@@ -265,7 +265,7 @@ Usage:
|
|
|
265
265
|
prismo context [scope] [--json] [path]
|
|
266
266
|
prismo cc [list|last N|all] [--json] [--limit N] [path]
|
|
267
267
|
prismo usage [codex|claude|all] [--json] [--limit N] [path]
|
|
268
|
-
prismo watch [codex|claude|all] [--json] [--once] [--report] [--rescue] [--guardrails] [--throttle] [--events] [--no-events] [--auto] [--budget N] [--redact-paths] [--interval N] [path]
|
|
268
|
+
prismo watch [codex|claude|all] [--json] [--once] [--agents] [--report] [--rescue] [--guardrails] [--throttle] [--events] [--no-events] [--auto] [--budget N] [--redact-paths] [--interval N] [path]
|
|
269
269
|
prismo demo
|
|
270
270
|
|
|
271
271
|
Commands:
|
|
@@ -301,6 +301,7 @@ Options:
|
|
|
301
301
|
--apply-suggestions Append missing recommended ignore rules with backups.
|
|
302
302
|
--no-context-packs Skip .prismo context-pack generation in doctor mode.
|
|
303
303
|
--report Write .prismo/watch-report.md in watch mode.
|
|
304
|
+
--agents Show multi-agent coordination view for parallel local sessions.
|
|
304
305
|
--rescue Print a paste-ready live-session rescue prompt in watch mode.
|
|
305
306
|
--guardrails Write/update .prismo/live-guardrails.md and .prismo/live-rescue-prompt.md.
|
|
306
307
|
--throttle Write/update .prismo/live-context-throttle.md in watch mode.
|
|
@@ -405,11 +406,12 @@ Examples:
|
|
|
405
406
|
watch: `Prismo Watch
|
|
406
407
|
|
|
407
408
|
Usage:
|
|
408
|
-
prismo watch [codex|claude|all] [--json] [--once] [--report] [--rescue] [--guardrails] [--throttle] [--events] [--no-events] [--auto] [--budget N] [--redact-paths] [--interval N] [path]
|
|
409
|
+
prismo watch [codex|claude|all] [--json] [--once] [--agents] [--report] [--rescue] [--guardrails] [--throttle] [--events] [--no-events] [--auto] [--budget N] [--redact-paths] [--interval N] [path]
|
|
409
410
|
|
|
410
411
|
Examples:
|
|
411
412
|
prismo watch codex
|
|
412
413
|
prismo watch claude --once --json
|
|
414
|
+
prismo watch --agents --once
|
|
413
415
|
prismo watch --once --report
|
|
414
416
|
prismo watch --once --redact-paths
|
|
415
417
|
prismo watch --rescue
|
|
@@ -421,6 +423,7 @@ Examples:
|
|
|
421
423
|
|
|
422
424
|
Output:
|
|
423
425
|
Shows context pressure, recent growth, likely context leaks, repeated reads/commands, loop suspicion, one suggested action, and live intervention steps.
|
|
426
|
+
--agents shows all visible local sessions for this repo and coordination warnings.
|
|
424
427
|
--rescue prints a paste-ready prompt to recover a noisy or looping active session.
|
|
425
428
|
--guardrails continuously updates .prismo/live-guardrails.md and .prismo/live-rescue-prompt.md for the active session.
|
|
426
429
|
--throttle writes a stricter live context budget file for the current agent session.
|
|
@@ -825,6 +828,7 @@ async function runCli(argv) {
|
|
|
825
828
|
limit,
|
|
826
829
|
tokenBudget,
|
|
827
830
|
auto,
|
|
831
|
+
agents: rest.includes("--agents"),
|
|
828
832
|
json,
|
|
829
833
|
once: rest.includes("--once"),
|
|
830
834
|
report: rest.includes("--report"),
|
package/package.json
CHANGED