getprismo 0.1.13 → 0.1.15
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 +71 -1
- package/docs/live-demo.md +115 -0
- package/docs/mcp.md +76 -0
- package/lib/prismo-dev/mcp.js +264 -0
- package/lib/prismo-dev/usage-watch.js +71 -2
- package/lib/prismo-dev-scan.js +56 -2
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -22,18 +22,21 @@ prismodev catches it before, during, and after.
|
|
|
22
22
|
|
|
23
23
|
## the loop
|
|
24
24
|
|
|
25
|
-
prismodev
|
|
25
|
+
prismodev covers the full AI coding session:
|
|
26
26
|
|
|
27
27
|
```
|
|
28
28
|
before you code npx getprismo doctor
|
|
29
29
|
while you code npx getprismo watch
|
|
30
|
+
noisy commands npx getprismo shield -- npm test
|
|
30
31
|
after you code npx getprismo cc timeline
|
|
32
|
+
agent-native npx getprismo mcp
|
|
31
33
|
```
|
|
32
34
|
|
|
33
35
|
**doctor** diagnoses the repo, applies safe fixes, and shows the before/after score.
|
|
34
36
|
**watch** monitors context pressure live and warns when things go wrong.
|
|
35
37
|
**cc timeline** reconstructs what happened in the session so you learn from it.
|
|
36
38
|
**shield** runs noisy commands without dumping full output back into the agent context.
|
|
39
|
+
**mcp** exposes PrismoDev as local tools so compatible agents can scan, search shield output, and request scoped context directly.
|
|
37
40
|
|
|
38
41
|
---
|
|
39
42
|
|
|
@@ -179,6 +182,15 @@ npx getprismo shield search "auth expected 200"
|
|
|
179
182
|
npx getprismo shield search "AUTH_FAILURE" --json
|
|
180
183
|
```
|
|
181
184
|
|
|
185
|
+
when `watch` detects tool-output floods or repeated command loops, it now recommends this flow directly:
|
|
186
|
+
|
|
187
|
+
```text
|
|
188
|
+
Shield Plan
|
|
189
|
+
Run: npx getprismo shield -- <noisy command>
|
|
190
|
+
Then: npx getprismo shield search "<error text>"
|
|
191
|
+
MCP: prismo_shield_run -> prismo_shield_search
|
|
192
|
+
```
|
|
193
|
+
|
|
182
194
|
this is intentionally not magic interception yet. it is a safe local-first primitive you can tell agents to use for noisy commands.
|
|
183
195
|
|
|
184
196
|
---
|
|
@@ -532,6 +544,7 @@ no install needed. npx runs it directly.
|
|
|
532
544
|
| `optimize` | generate `.prismo/` context packs |
|
|
533
545
|
| `context` | print paste-ready prompt for agents |
|
|
534
546
|
| `shield` | run noisy commands while keeping full output out of chat |
|
|
547
|
+
| `mcp` | expose PrismoDev tools over local MCP stdio |
|
|
535
548
|
| `setup` | detect tools, logs, proxy readiness |
|
|
536
549
|
| `usage` | show raw session token usage |
|
|
537
550
|
| `init` | add npm scripts and .prismo/README.md |
|
|
@@ -583,6 +596,53 @@ npx getprismo shield last
|
|
|
583
596
|
npx getprismo shield search "auth failure"
|
|
584
597
|
```
|
|
585
598
|
|
|
599
|
+
### mcp mode
|
|
600
|
+
|
|
601
|
+
```bash
|
|
602
|
+
npx getprismo mcp
|
|
603
|
+
npx getprismo mcp /path/to/repo
|
|
604
|
+
```
|
|
605
|
+
|
|
606
|
+
`mcp` starts a local stdio MCP server for agent clients. It exposes:
|
|
607
|
+
|
|
608
|
+
- `prismo_scan`
|
|
609
|
+
- `prismo_doctor_dry_run`
|
|
610
|
+
- `prismo_watch_snapshot`
|
|
611
|
+
- `prismo_shield_run`
|
|
612
|
+
- `prismo_shield_search`
|
|
613
|
+
- `prismo_shield_last`
|
|
614
|
+
- `prismo_context_pack`
|
|
615
|
+
- `prismo_firewall`
|
|
616
|
+
- `prismo_cc_timeline`
|
|
617
|
+
|
|
618
|
+
This lets an MCP-compatible agent search prior shielded test/build output, request scoped context packs, or inspect token-waste signals without pasting giant logs into the conversation.
|
|
619
|
+
|
|
620
|
+
Generic MCP client config:
|
|
621
|
+
|
|
622
|
+
```json
|
|
623
|
+
{
|
|
624
|
+
"mcpServers": {
|
|
625
|
+
"prismodev": {
|
|
626
|
+
"command": "npx",
|
|
627
|
+
"args": ["-y", "getprismo", "mcp", "/path/to/your/repo"]
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
```
|
|
632
|
+
|
|
633
|
+
For local development from this repo:
|
|
634
|
+
|
|
635
|
+
```json
|
|
636
|
+
{
|
|
637
|
+
"mcpServers": {
|
|
638
|
+
"prismodev": {
|
|
639
|
+
"command": "node",
|
|
640
|
+
"args": ["/path/to/prismodev/bin/prismo.js", "mcp", "/path/to/your/repo"]
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
```
|
|
645
|
+
|
|
586
646
|
---
|
|
587
647
|
|
|
588
648
|
## cc modes
|
|
@@ -728,8 +788,10 @@ lib/prismo-dev/constants.js shared defaults, pricing, patterns
|
|
|
728
788
|
lib/prismo-dev/context-optimize.js context packs, scoped prompts
|
|
729
789
|
lib/prismo-dev/doctor.js doctor/dev/init orchestration
|
|
730
790
|
lib/prismo-dev/fixes.js safe ignore/template generation
|
|
791
|
+
lib/prismo-dev/mcp.js local MCP server and Prismo tool bindings
|
|
731
792
|
lib/prismo-dev/report.js terminal, markdown, ci reports
|
|
732
793
|
lib/prismo-dev/scan.js repo scanning, scoring, readiness
|
|
794
|
+
lib/prismo-dev/shield.js local command shield and searchable output index
|
|
733
795
|
lib/prismo-dev/usage-watch.js local logs, watch, cost, timeline
|
|
734
796
|
```
|
|
735
797
|
|
|
@@ -739,8 +801,16 @@ lib/prismo-dev/usage-watch.js local logs, watch, cost, timeline
|
|
|
739
801
|
|
|
740
802
|
```bash
|
|
741
803
|
npx getprismo --help
|
|
804
|
+
npx getprismo --version
|
|
742
805
|
npx getprismo doctor --help
|
|
743
806
|
npx getprismo watch --help
|
|
807
|
+
npx getprismo shield --help
|
|
808
|
+
npx getprismo mcp --help
|
|
744
809
|
npx getprismo cc --help
|
|
745
810
|
npx getprismo scan --help
|
|
746
811
|
```
|
|
812
|
+
|
|
813
|
+
More docs:
|
|
814
|
+
|
|
815
|
+
- [MCP setup and tools](docs/mcp.md)
|
|
816
|
+
- [Live demo flow](docs/live-demo.md)
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# PrismoDev Live Demo
|
|
2
|
+
|
|
3
|
+
Use this flow to show the full product loop on a real repo.
|
|
4
|
+
|
|
5
|
+
## 1. Before Session: Doctor
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npx getprismo doctor
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Shows:
|
|
12
|
+
|
|
13
|
+
- before/after repo score
|
|
14
|
+
- missing `.claudeignore` / `.cursorignore`
|
|
15
|
+
- generated artifacts exposed to AI context
|
|
16
|
+
- compact `.prismo/` context packs
|
|
17
|
+
- recommended next starting context
|
|
18
|
+
|
|
19
|
+
## 2. During Session: Watch
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
npx getprismo watch --once
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Shows:
|
|
26
|
+
|
|
27
|
+
- context pressure
|
|
28
|
+
- session size
|
|
29
|
+
- recent context growth
|
|
30
|
+
- repeated file reads
|
|
31
|
+
- generated artifact leaks
|
|
32
|
+
- possible loops
|
|
33
|
+
- shield recommendation when command output is flooding context
|
|
34
|
+
|
|
35
|
+
If watch sees noisy output, the important part is:
|
|
36
|
+
|
|
37
|
+
```text
|
|
38
|
+
Shield Plan
|
|
39
|
+
Run: npx getprismo shield -- <noisy command>
|
|
40
|
+
Then: npx getprismo shield search "<error text>"
|
|
41
|
+
MCP: prismo_shield_run -> prismo_shield_search
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## 3. Noisy Commands: Shield
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
npx getprismo shield -- npm test
|
|
48
|
+
npx getprismo shield search "AUTH_FAILURE"
|
|
49
|
+
npx getprismo shield last
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Shows:
|
|
53
|
+
|
|
54
|
+
- compact command summary
|
|
55
|
+
- useful error lines
|
|
56
|
+
- full output stored locally
|
|
57
|
+
- searchable SQLite FTS index when `sqlite3` is available
|
|
58
|
+
|
|
59
|
+
Full output stays in:
|
|
60
|
+
|
|
61
|
+
```text
|
|
62
|
+
.prismo/shield/runs/
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## 4. Agent-Native Mode: MCP
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
npx getprismo mcp /path/to/repo
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Demo prompt:
|
|
72
|
+
|
|
73
|
+
```text
|
|
74
|
+
Use Prismo MCP to run the failing test through shield. Search the stored output for the root error. Do not paste the full test log into the chat.
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Expected MCP tool flow:
|
|
78
|
+
|
|
79
|
+
1. `prismo_shield_run`
|
|
80
|
+
2. `prismo_shield_search`
|
|
81
|
+
3. `prismo_context_pack` if the agent needs scoped repo context
|
|
82
|
+
|
|
83
|
+
## 5. After Session: Timeline
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
npx getprismo cc timeline
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Shows:
|
|
90
|
+
|
|
91
|
+
- generated artifacts that entered context
|
|
92
|
+
- repeated file/path mentions
|
|
93
|
+
- repeated command/tool patterns
|
|
94
|
+
- suggested cleanup action
|
|
95
|
+
|
|
96
|
+
## Screenshot Commands
|
|
97
|
+
|
|
98
|
+
For a quick terminal screenshot:
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
npx getprismo doctor
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
For the proactive story:
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
npx getprismo watch --once
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
For the MCP/shield story:
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
npx getprismo shield -- npm test
|
|
114
|
+
npx getprismo shield search "error"
|
|
115
|
+
```
|
package/docs/mcp.md
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# PrismoDev MCP
|
|
2
|
+
|
|
3
|
+
PrismoDev can run as a local MCP server so compatible coding agents can inspect token waste, run noisy commands through shield, search stored output, and request scoped context without pasting huge logs into chat.
|
|
4
|
+
|
|
5
|
+
## Start
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npx getprismo mcp /path/to/your/repo
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Generic MCP Config
|
|
12
|
+
|
|
13
|
+
```json
|
|
14
|
+
{
|
|
15
|
+
"mcpServers": {
|
|
16
|
+
"prismodev": {
|
|
17
|
+
"command": "npx",
|
|
18
|
+
"args": ["-y", "getprismo", "mcp", "/path/to/your/repo"]
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
For local development from this repo:
|
|
25
|
+
|
|
26
|
+
```json
|
|
27
|
+
{
|
|
28
|
+
"mcpServers": {
|
|
29
|
+
"prismodev": {
|
|
30
|
+
"command": "node",
|
|
31
|
+
"args": ["/path/to/prismodev/bin/prismo.js", "mcp", "/path/to/your/repo"]
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Tools
|
|
38
|
+
|
|
39
|
+
- `prismo_scan`: scan repo context/token waste
|
|
40
|
+
- `prismo_doctor_dry_run`: preview doctor payoff without writing files
|
|
41
|
+
- `prismo_watch_snapshot`: inspect live context pressure
|
|
42
|
+
- `prismo_shield_run`: run a noisy command and store full output locally
|
|
43
|
+
- `prismo_shield_search`: search stored shield output
|
|
44
|
+
- `prismo_shield_last`: list recent shielded command runs
|
|
45
|
+
- `prismo_context_pack`: generate or preview scoped context packs
|
|
46
|
+
- `prismo_firewall`: create a scoped context policy for a task
|
|
47
|
+
- `prismo_cc_timeline`: inspect Claude Code session postmortems
|
|
48
|
+
|
|
49
|
+
## Best Workflow
|
|
50
|
+
|
|
51
|
+
Ask your agent:
|
|
52
|
+
|
|
53
|
+
```text
|
|
54
|
+
Run the failing test command through Prismo shield, then search the stored output for the real error. Do not paste the full log into chat.
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
The agent should call:
|
|
58
|
+
|
|
59
|
+
1. `prismo_shield_run`
|
|
60
|
+
2. `prismo_shield_search`
|
|
61
|
+
|
|
62
|
+
This keeps full stdout/stderr in `.prismo/shield/runs/` and only brings the useful failure snippet back into the model context.
|
|
63
|
+
|
|
64
|
+
## Watch Integration
|
|
65
|
+
|
|
66
|
+
When `npx getprismo watch` detects a tool-output flood or repeated command loop, it now prints a Shield Plan:
|
|
67
|
+
|
|
68
|
+
```text
|
|
69
|
+
Shield Plan
|
|
70
|
+
Tool output is flooding context; shield the command so full logs stay local.
|
|
71
|
+
Run: npx getprismo shield -- <noisy command>
|
|
72
|
+
Then: npx getprismo shield search "<error text>"
|
|
73
|
+
MCP: prismo_shield_run -> prismo_shield_search
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
That is the intended live-session recovery path.
|
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
function createTextResult(payload) {
|
|
2
|
+
const text = typeof payload === "string" ? payload : JSON.stringify(payload, null, 2);
|
|
3
|
+
return {
|
|
4
|
+
content: [{ type: "text", text }],
|
|
5
|
+
};
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
function normalizeArgs(args) {
|
|
9
|
+
return args && typeof args === "object" ? args : {};
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function makeTool(name, description, properties = {}, required = []) {
|
|
13
|
+
return {
|
|
14
|
+
name,
|
|
15
|
+
description,
|
|
16
|
+
inputSchema: {
|
|
17
|
+
type: "object",
|
|
18
|
+
properties,
|
|
19
|
+
required,
|
|
20
|
+
additionalProperties: false,
|
|
21
|
+
},
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function createMcpTools(deps) {
|
|
26
|
+
const {
|
|
27
|
+
rootDir,
|
|
28
|
+
scanRepo,
|
|
29
|
+
toJsonPayload,
|
|
30
|
+
runDoctor,
|
|
31
|
+
toDoctorJsonPayload,
|
|
32
|
+
getUsageSummary,
|
|
33
|
+
getClaudeCodeCostSummary,
|
|
34
|
+
runOptimize,
|
|
35
|
+
createOptimizeContext,
|
|
36
|
+
renderStarterPrompt,
|
|
37
|
+
runFirewall,
|
|
38
|
+
runShield,
|
|
39
|
+
runShieldLast,
|
|
40
|
+
runShieldSearch,
|
|
41
|
+
} = deps;
|
|
42
|
+
|
|
43
|
+
const pathProperty = {
|
|
44
|
+
type: "string",
|
|
45
|
+
description: "Repository path. Defaults to the repo used when the MCP server started.",
|
|
46
|
+
};
|
|
47
|
+
const limitProperty = {
|
|
48
|
+
type: "number",
|
|
49
|
+
description: "Maximum number of recent sessions or runs to inspect.",
|
|
50
|
+
};
|
|
51
|
+
const scopeProperty = {
|
|
52
|
+
type: "string",
|
|
53
|
+
description: "Optional scope such as frontend, backend, auth, tests, billing, or routing.",
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
const tools = [
|
|
57
|
+
makeTool("prismo_scan", "Scan a repo for AI coding token/context waste.", {
|
|
58
|
+
path: pathProperty,
|
|
59
|
+
includeUsage: { type: "boolean", description: "Include local Claude/Codex usage logs when available." },
|
|
60
|
+
limit: limitProperty,
|
|
61
|
+
}),
|
|
62
|
+
makeTool("prismo_doctor_dry_run", "Preview PrismoDev doctor fixes and before/after payoff without writing files.", {
|
|
63
|
+
path: pathProperty,
|
|
64
|
+
scope: scopeProperty,
|
|
65
|
+
limit: limitProperty,
|
|
66
|
+
}),
|
|
67
|
+
makeTool("prismo_watch_snapshot", "Return a one-shot local session/context pressure snapshot.", {
|
|
68
|
+
path: pathProperty,
|
|
69
|
+
tool: { type: "string", enum: ["all", "codex", "claude"], description: "Which local session logs to inspect." },
|
|
70
|
+
limit: limitProperty,
|
|
71
|
+
}),
|
|
72
|
+
makeTool("prismo_shield_run", "Run a noisy command through Prismo shield and store full output locally.", {
|
|
73
|
+
path: pathProperty,
|
|
74
|
+
command: {
|
|
75
|
+
type: "array",
|
|
76
|
+
items: { type: "string" },
|
|
77
|
+
description: "Command argv array, for example [\"npm\", \"test\"].",
|
|
78
|
+
},
|
|
79
|
+
}, ["command"]),
|
|
80
|
+
makeTool("prismo_shield_search", "Search stored shield stdout/stderr without loading full logs into context.", {
|
|
81
|
+
path: pathProperty,
|
|
82
|
+
query: { type: "string", description: "Text to search for in stored shield output." },
|
|
83
|
+
limit: limitProperty,
|
|
84
|
+
}, ["query"]),
|
|
85
|
+
makeTool("prismo_shield_last", "List recent shielded command runs.", {
|
|
86
|
+
path: pathProperty,
|
|
87
|
+
limit: limitProperty,
|
|
88
|
+
}),
|
|
89
|
+
makeTool("prismo_context_pack", "Generate or preview a scoped Prismo context pack/starter prompt.", {
|
|
90
|
+
path: pathProperty,
|
|
91
|
+
scope: scopeProperty,
|
|
92
|
+
dryRun: { type: "boolean", description: "When true, do not write .prismo files." },
|
|
93
|
+
}),
|
|
94
|
+
makeTool("prismo_firewall", "Generate a scoped context firewall policy for a task.", {
|
|
95
|
+
path: pathProperty,
|
|
96
|
+
task: { type: "string", description: "Task description such as auth-bug or frontend-test-failure." },
|
|
97
|
+
scope: scopeProperty,
|
|
98
|
+
dryRun: { type: "boolean", description: "When true, preview allowed/blocked context without writing files." },
|
|
99
|
+
}),
|
|
100
|
+
makeTool("prismo_cc_timeline", "Return the latest Claude Code session timeline/postmortem data.", {
|
|
101
|
+
path: pathProperty,
|
|
102
|
+
limit: limitProperty,
|
|
103
|
+
}),
|
|
104
|
+
];
|
|
105
|
+
|
|
106
|
+
function resolveRoot(args) {
|
|
107
|
+
return args.path || rootDir || process.cwd();
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
async function callTool(name, rawArgs) {
|
|
111
|
+
const args = normalizeArgs(rawArgs);
|
|
112
|
+
const target = resolveRoot(args);
|
|
113
|
+
|
|
114
|
+
if (name === "prismo_scan") {
|
|
115
|
+
return createTextResult(toJsonPayload(scanRepo(target, {
|
|
116
|
+
includeUsage: Boolean(args.includeUsage),
|
|
117
|
+
usageLimit: Number(args.limit) || 5,
|
|
118
|
+
})));
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (name === "prismo_doctor_dry_run") {
|
|
122
|
+
const result = runDoctor(target, {
|
|
123
|
+
dryRun: true,
|
|
124
|
+
scope: args.scope || null,
|
|
125
|
+
limit: Number(args.limit) || 3,
|
|
126
|
+
});
|
|
127
|
+
return createTextResult(toDoctorJsonPayload(result));
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
if (name === "prismo_watch_snapshot") {
|
|
131
|
+
const summary = getUsageSummary({
|
|
132
|
+
cwd: target,
|
|
133
|
+
limit: Number(args.limit) || 3,
|
|
134
|
+
usageTool: args.tool || "all",
|
|
135
|
+
});
|
|
136
|
+
return createTextResult(summary);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (name === "prismo_shield_run") {
|
|
140
|
+
return createTextResult(runShield(target, args.command));
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (name === "prismo_shield_search") {
|
|
144
|
+
return createTextResult(runShieldSearch(target, args.query, { limit: Number(args.limit) || 5 }));
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
if (name === "prismo_shield_last") {
|
|
148
|
+
return createTextResult(runShieldLast(target, { limit: Number(args.limit) || 5 }));
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
if (name === "prismo_context_pack") {
|
|
152
|
+
const scope = args.scope || null;
|
|
153
|
+
const result = runOptimize(target, { scope, dryRun: args.dryRun !== false });
|
|
154
|
+
const context = createOptimizeContext(target, scope);
|
|
155
|
+
return createTextResult({
|
|
156
|
+
...result,
|
|
157
|
+
starterPrompt: renderStarterPrompt(context, scope),
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
if (name === "prismo_firewall") {
|
|
162
|
+
return createTextResult(runFirewall(target, {
|
|
163
|
+
task: args.task || args.scope || "general",
|
|
164
|
+
scope: args.scope || null,
|
|
165
|
+
dryRun: args.dryRun !== false,
|
|
166
|
+
}));
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
if (name === "prismo_cc_timeline") {
|
|
170
|
+
return createTextResult(getClaudeCodeCostSummary({
|
|
171
|
+
cwd: target,
|
|
172
|
+
limit: Number(args.limit) || 1,
|
|
173
|
+
mode: "timeline",
|
|
174
|
+
}));
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
throw new Error(`Unknown MCP tool: ${name}`);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
return { tools, callTool };
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
function runMcpServer(deps) {
|
|
184
|
+
const readline = require("readline");
|
|
185
|
+
const { packageVersion = "0.0.0" } = deps;
|
|
186
|
+
const { tools, callTool } = createMcpTools(deps);
|
|
187
|
+
const rl = readline.createInterface({
|
|
188
|
+
input: process.stdin,
|
|
189
|
+
output: process.stdout,
|
|
190
|
+
terminal: false,
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
function send(message) {
|
|
194
|
+
process.stdout.write(`${JSON.stringify(message)}\n`);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
async function handle(message) {
|
|
198
|
+
if (!message || !message.method) return;
|
|
199
|
+
if (!Object.prototype.hasOwnProperty.call(message, "id")) return;
|
|
200
|
+
|
|
201
|
+
try {
|
|
202
|
+
if (message.method === "initialize") {
|
|
203
|
+
send({
|
|
204
|
+
jsonrpc: "2.0",
|
|
205
|
+
id: message.id,
|
|
206
|
+
result: {
|
|
207
|
+
protocolVersion: "2024-11-05",
|
|
208
|
+
capabilities: { tools: {} },
|
|
209
|
+
serverInfo: { name: "prismodev", version: packageVersion },
|
|
210
|
+
},
|
|
211
|
+
});
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
if (message.method === "tools/list") {
|
|
216
|
+
send({ jsonrpc: "2.0", id: message.id, result: { tools } });
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
if (message.method === "tools/call") {
|
|
221
|
+
const params = normalizeArgs(message.params);
|
|
222
|
+
const result = await callTool(params.name, params.arguments);
|
|
223
|
+
send({ jsonrpc: "2.0", id: message.id, result });
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
send({
|
|
228
|
+
jsonrpc: "2.0",
|
|
229
|
+
id: message.id,
|
|
230
|
+
error: { code: -32601, message: `Method not found: ${message.method}` },
|
|
231
|
+
});
|
|
232
|
+
} catch (error) {
|
|
233
|
+
send({
|
|
234
|
+
jsonrpc: "2.0",
|
|
235
|
+
id: message.id,
|
|
236
|
+
error: {
|
|
237
|
+
code: -32000,
|
|
238
|
+
message: error && error.message ? error.message : String(error),
|
|
239
|
+
},
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
rl.on("line", (line) => {
|
|
245
|
+
if (!line.trim()) return;
|
|
246
|
+
try {
|
|
247
|
+
handle(JSON.parse(line));
|
|
248
|
+
} catch (error) {
|
|
249
|
+
send({
|
|
250
|
+
jsonrpc: "2.0",
|
|
251
|
+
id: null,
|
|
252
|
+
error: {
|
|
253
|
+
code: -32700,
|
|
254
|
+
message: error && error.message ? error.message : String(error),
|
|
255
|
+
},
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
module.exports = {
|
|
262
|
+
createMcpTools,
|
|
263
|
+
runMcpServer,
|
|
264
|
+
};
|
|
@@ -947,7 +947,7 @@ function getTokenBudgetStatus(activeSession, tokenBudget) {
|
|
|
947
947
|
|
|
948
948
|
function getRecommendedWatchAction(activeSession, warnings) {
|
|
949
949
|
if (!activeSession) return `${NPX_COMMAND} setup`;
|
|
950
|
-
if (activeSession.
|
|
950
|
+
if (activeSession.estimatedToolTokens >= 150000 || activeSession.loopSuspicion) return `${NPX_COMMAND} shield -- <noisy command>`;
|
|
951
951
|
if ((activeSession.generatedArtifacts || []).length || getActionableRepeatedPaths(activeSession, 1).length) return `${NPX_COMMAND} doctor`;
|
|
952
952
|
if (activeSession.recentContextGrowth >= 250000) return "Start a fresh coding-agent session with a scoped Prismo context prompt.";
|
|
953
953
|
if (warnings.some((warning) => warning.includes("Tool/output"))) return `${NPX_COMMAND} context`;
|
|
@@ -956,6 +956,41 @@ function getRecommendedWatchAction(activeSession, warnings) {
|
|
|
956
956
|
return `${NPX_COMMAND} scan --usage`;
|
|
957
957
|
}
|
|
958
958
|
|
|
959
|
+
function buildShieldPlan(activeSession, liveAction) {
|
|
960
|
+
if (!activeSession || !liveAction) return null;
|
|
961
|
+
const shieldCauses = new Set(["tool-output-flood", "possible-loop"]);
|
|
962
|
+
const hasRepeatedCommand = Boolean(activeSession.repeatedCommands?.[0]);
|
|
963
|
+
const highToolOutput = activeSession.estimatedToolTokens >= 150000;
|
|
964
|
+
if (!shieldCauses.has(liveAction.cause) && !hasRepeatedCommand && !highToolOutput) return null;
|
|
965
|
+
|
|
966
|
+
const repeatedCommand = activeSession.repeatedCommands?.[0]?.value || null;
|
|
967
|
+
const cliCommand = repeatedCommand
|
|
968
|
+
? `${NPX_COMMAND} shield -- ${repeatedCommand}`
|
|
969
|
+
: `${NPX_COMMAND} shield -- <noisy command>`;
|
|
970
|
+
const searchHint = liveAction.cause === "possible-loop"
|
|
971
|
+
? "Search the stored output for the stable error text before rerunning the command."
|
|
972
|
+
: "Search the stored output for the failure token, filename, or exception instead of loading the full log.";
|
|
973
|
+
|
|
974
|
+
return {
|
|
975
|
+
reason: liveAction.cause === "possible-loop"
|
|
976
|
+
? "Repeated command/output loop detected; shield the command and search stored output before reruns."
|
|
977
|
+
: "Tool output is flooding context; shield the command so full logs stay local.",
|
|
978
|
+
command: cliCommand,
|
|
979
|
+
commandTemplate: `${NPX_COMMAND} shield -- <command>`,
|
|
980
|
+
searchCommand: `${NPX_COMMAND} shield search "<error text>"`,
|
|
981
|
+
mcp: {
|
|
982
|
+
runTool: "prismo_shield_run",
|
|
983
|
+
searchTool: "prismo_shield_search",
|
|
984
|
+
workflow: "Call prismo_shield_run with the noisy command, then prismo_shield_search for the relevant error text.",
|
|
985
|
+
},
|
|
986
|
+
next: [
|
|
987
|
+
repeatedCommand ? `Run the command through shield: ${cliCommand}` : `Run the noisy command through shield: ${cliCommand}`,
|
|
988
|
+
searchHint,
|
|
989
|
+
"Give the agent the compact shield summary first; inspect full stdout/stderr only if needed.",
|
|
990
|
+
],
|
|
991
|
+
};
|
|
992
|
+
}
|
|
993
|
+
|
|
959
994
|
function buildLiveAction(activeSession, warnings, budgetStatus = null) {
|
|
960
995
|
if (!activeSession) {
|
|
961
996
|
return {
|
|
@@ -1103,6 +1138,7 @@ function buildLiveSessionView(summary) {
|
|
|
1103
1138
|
const actionableRepeatedPaths = activeSession ? getActionableRepeatedPaths(activeSession, 5) : [];
|
|
1104
1139
|
const generatedArtifactGroups = activeSession ? summarizeGeneratedArtifacts(activeSession.generatedArtifacts || [], 5) : [];
|
|
1105
1140
|
const liveAction = buildLiveAction(activeSession, warnings, budgetStatus);
|
|
1141
|
+
const shieldPlan = buildShieldPlan(activeSession, liveAction);
|
|
1106
1142
|
return {
|
|
1107
1143
|
activeSession: activeSession
|
|
1108
1144
|
? {
|
|
@@ -1137,7 +1173,10 @@ function buildLiveSessionView(summary) {
|
|
|
1137
1173
|
highestRisk: summary.sessions.reduce((risk, session) => (getRiskRank(session.contextRisk) > getRiskRank(risk) ? session.contextRisk : risk), "Low"),
|
|
1138
1174
|
budget: budgetStatus,
|
|
1139
1175
|
warnings,
|
|
1140
|
-
liveAction
|
|
1176
|
+
liveAction: {
|
|
1177
|
+
...liveAction,
|
|
1178
|
+
shieldPlan,
|
|
1179
|
+
},
|
|
1141
1180
|
recommendedAction: getRecommendedWatchAction(activeSession, warnings),
|
|
1142
1181
|
nextCommands: Array.from(new Set([
|
|
1143
1182
|
`${NPX_COMMAND} doctor`,
|
|
@@ -1316,6 +1355,14 @@ function renderWatchTerminal(summary) {
|
|
|
1316
1355
|
lines.push(live.liveAction.summary);
|
|
1317
1356
|
live.liveAction.now.slice(0, 4).forEach((step, index) => lines.push(`${index + 1}. ${step}`));
|
|
1318
1357
|
if (live.liveAction.rescueAvailable) lines.push(`Rescue: ${live.liveAction.rescueCommand}`);
|
|
1358
|
+
if (live.liveAction.shieldPlan) {
|
|
1359
|
+
lines.push("");
|
|
1360
|
+
lines.push("Shield Plan");
|
|
1361
|
+
lines.push(live.liveAction.shieldPlan.reason);
|
|
1362
|
+
lines.push(`Run: ${live.liveAction.shieldPlan.command}`);
|
|
1363
|
+
lines.push(`Then: ${live.liveAction.shieldPlan.searchCommand}`);
|
|
1364
|
+
lines.push(`MCP: ${live.liveAction.shieldPlan.mcp.runTool} -> ${live.liveAction.shieldPlan.mcp.searchTool}`);
|
|
1365
|
+
}
|
|
1319
1366
|
lines.push("");
|
|
1320
1367
|
if (active.actionableRepeatedPaths?.length || active.generatedArtifactGroups?.length || active.repeatedCommands.length) {
|
|
1321
1368
|
lines.push("Signals");
|
|
@@ -1366,6 +1413,13 @@ function renderRescuePrompt(summary) {
|
|
|
1366
1413
|
lines.push("");
|
|
1367
1414
|
lines.push("Do this now:");
|
|
1368
1415
|
live.liveAction.now.forEach((step, index) => lines.push(`${index + 1}. ${step}`));
|
|
1416
|
+
if (live.liveAction.shieldPlan) {
|
|
1417
|
+
lines.push("");
|
|
1418
|
+
lines.push("For noisy commands, use Prismo shield instead of loading full output into this chat:");
|
|
1419
|
+
lines.push(`- CLI: ${live.liveAction.shieldPlan.command}`);
|
|
1420
|
+
lines.push(`- Search stored output: ${live.liveAction.shieldPlan.searchCommand}`);
|
|
1421
|
+
lines.push(`- MCP workflow: ${live.liveAction.shieldPlan.mcp.workflow}`);
|
|
1422
|
+
}
|
|
1369
1423
|
lines.push("");
|
|
1370
1424
|
lines.push("Before reading or editing anything else, summarize:");
|
|
1371
1425
|
lines.push("- files changed so far");
|
|
@@ -1405,6 +1459,11 @@ function renderLiveGuardrails(summary) {
|
|
|
1405
1459
|
lines.push("## Effective Immediately");
|
|
1406
1460
|
lines.push("");
|
|
1407
1461
|
live.liveAction.now.forEach((step) => lines.push(`- ${step}`));
|
|
1462
|
+
if (live.liveAction.shieldPlan) {
|
|
1463
|
+
lines.push(`- Run noisy commands through Prismo shield: \`${live.liveAction.shieldPlan.command}\`.`);
|
|
1464
|
+
lines.push(`- Search stored command output with: \`${live.liveAction.shieldPlan.searchCommand}\`.`);
|
|
1465
|
+
lines.push(`- If MCP tools are available, use \`${live.liveAction.shieldPlan.mcp.runTool}\` then \`${live.liveAction.shieldPlan.mcp.searchTool}\`.`);
|
|
1466
|
+
}
|
|
1408
1467
|
lines.push("- Keep command output short; prefer filtered errors, small ranges, and summaries over full logs.");
|
|
1409
1468
|
lines.push("- Do not read generated artifacts, lockfiles, caches, build output, coverage, or logs unless explicitly required.");
|
|
1410
1469
|
lines.push("- Before broad exploration, summarize the current task, changed files, current failure, and next smallest useful file/test.");
|
|
@@ -1477,6 +1536,10 @@ function renderContextThrottle(summary) {
|
|
|
1477
1536
|
live.liveAction.now.slice(0, 4).forEach((step) => lines.push(`- ${step}`));
|
|
1478
1537
|
lines.push("- Keep the next tool call narrow enough to summarize in one screen.");
|
|
1479
1538
|
}
|
|
1539
|
+
if (live.liveAction.shieldPlan) {
|
|
1540
|
+
lines.push(`- Run noisy commands through shield: \`${live.liveAction.shieldPlan.command}\`.`);
|
|
1541
|
+
lines.push(`- Search shield output instead of reloading full logs: \`${live.liveAction.shieldPlan.searchCommand}\`.`);
|
|
1542
|
+
}
|
|
1480
1543
|
if (active?.actionableRepeatedPaths?.length) {
|
|
1481
1544
|
lines.push("");
|
|
1482
1545
|
lines.push("## Blocked Re-Reads");
|
|
@@ -1547,6 +1610,7 @@ function buildWatchEvent(summary) {
|
|
|
1547
1610
|
toolOutputTokens: active.estimatedToolTokens || 0,
|
|
1548
1611
|
budget,
|
|
1549
1612
|
warnings: live.warnings || [],
|
|
1613
|
+
shieldPlan: live.liveAction.shieldPlan || null,
|
|
1550
1614
|
repeatedFiles: repeated.slice(0, 5),
|
|
1551
1615
|
artifactGroups: artifacts.slice(0, 5),
|
|
1552
1616
|
signature: signatureParts.join("::"),
|
|
@@ -1621,6 +1685,11 @@ function renderWatchReport(summary) {
|
|
|
1621
1685
|
lines.push(`- Cause: ${live.liveAction.cause} (${live.liveAction.confidence} confidence)`);
|
|
1622
1686
|
lines.push(`- ${live.liveAction.summary}`);
|
|
1623
1687
|
live.liveAction.now.forEach((step) => lines.push(`- ${step}`));
|
|
1688
|
+
if (live.liveAction.shieldPlan) {
|
|
1689
|
+
lines.push(`- Shield noisy command output: \`${live.liveAction.shieldPlan.command}\``);
|
|
1690
|
+
lines.push(`- Search shielded output: \`${live.liveAction.shieldPlan.searchCommand}\``);
|
|
1691
|
+
lines.push(`- MCP workflow: \`${live.liveAction.shieldPlan.mcp.runTool}\` -> \`${live.liveAction.shieldPlan.mcp.searchTool}\``);
|
|
1692
|
+
}
|
|
1624
1693
|
if (summary.guardrailsPath) {
|
|
1625
1694
|
lines.push(`- Guardrails: ${summary.guardrailsPath}`);
|
|
1626
1695
|
lines.push(`- Rescue prompt: ${summary.rescuePath}`);
|
package/lib/prismo-dev-scan.js
CHANGED
|
@@ -3,6 +3,7 @@ const http = require("http");
|
|
|
3
3
|
const https = require("https");
|
|
4
4
|
const os = require("os");
|
|
5
5
|
const path = require("path");
|
|
6
|
+
const { version: PACKAGE_VERSION } = require("../package.json");
|
|
6
7
|
|
|
7
8
|
const {
|
|
8
9
|
HIGH_RISK_DIRS,
|
|
@@ -282,10 +283,13 @@ const {
|
|
|
282
283
|
color,
|
|
283
284
|
});
|
|
284
285
|
|
|
286
|
+
const { runMcpServer } = require("./prismo-dev/mcp");
|
|
287
|
+
|
|
285
288
|
function printHelp() {
|
|
286
289
|
console.log(`Prismo CLI
|
|
287
290
|
|
|
288
291
|
Usage:
|
|
292
|
+
prismo --version
|
|
289
293
|
prismo dev [path]
|
|
290
294
|
prismo init [--json] [--dry-run] [path]
|
|
291
295
|
prismo doctor [--json] [--dry-run] [--apply-ignores-only] [--no-context-packs] [--limit N] [path]
|
|
@@ -293,6 +297,7 @@ Usage:
|
|
|
293
297
|
prismo shield [--json] [path] -- <command ...>
|
|
294
298
|
prismo shield last [--json] [--limit N] [path]
|
|
295
299
|
prismo shield search <query> [--json] [--limit N] [path]
|
|
300
|
+
prismo mcp [path]
|
|
296
301
|
prismo setup [--json] [--proxy-url URL] [path]
|
|
297
302
|
prismo scan [--fix] [--ci] [--json] [--usage] [--simple] [--no-report] [path]
|
|
298
303
|
prismo optimize [scope] [--json] [path]
|
|
@@ -308,6 +313,7 @@ Commands:
|
|
|
308
313
|
doctor Diagnose, safely optimize, re-scan, and show before/after payoff.
|
|
309
314
|
firewall Generate allowed/blocked context policy files for an AI coding task.
|
|
310
315
|
shield Run a noisy command, store full output locally, and return a compact summary.
|
|
316
|
+
mcp Start a local MCP server exposing Prismo tools over stdio.
|
|
311
317
|
scan Run PrismoDev for Claude Code, Codex, Cursor, and AI coding workflows.
|
|
312
318
|
optimize Generate lightweight AI-readable project context files in .prismo/.
|
|
313
319
|
context Print a copy-pasteable compact context prompt for AI coding tools.
|
|
@@ -511,6 +517,28 @@ Examples:
|
|
|
511
517
|
Output:
|
|
512
518
|
Runs the command locally, stores full stdout/stderr under .prismo/shield/runs/, indexes output in SQLite FTS5 when available, and prints only a compact summary plus useful error lines.
|
|
513
519
|
Search and last retrieve prior shield runs without re-feeding full output into agent context.`,
|
|
520
|
+
mcp: `Prismo MCP Server
|
|
521
|
+
|
|
522
|
+
Usage:
|
|
523
|
+
prismo mcp [path]
|
|
524
|
+
|
|
525
|
+
Examples:
|
|
526
|
+
prismo mcp
|
|
527
|
+
prismo mcp /path/to/repo
|
|
528
|
+
|
|
529
|
+
Tools exposed:
|
|
530
|
+
prismo_scan
|
|
531
|
+
prismo_doctor_dry_run
|
|
532
|
+
prismo_watch_snapshot
|
|
533
|
+
prismo_shield_run
|
|
534
|
+
prismo_shield_search
|
|
535
|
+
prismo_shield_last
|
|
536
|
+
prismo_context_pack
|
|
537
|
+
prismo_firewall
|
|
538
|
+
prismo_cc_timeline
|
|
539
|
+
|
|
540
|
+
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.`,
|
|
514
542
|
ci: `Prismo CI
|
|
515
543
|
|
|
516
544
|
Usage:
|
|
@@ -549,6 +577,10 @@ Good first command for people who want to see the value before scanning a repo.`
|
|
|
549
577
|
|
|
550
578
|
async function runCli(argv) {
|
|
551
579
|
const [command, ...rest] = argv;
|
|
580
|
+
if (command === "--version" || command === "-v" || command === "version") {
|
|
581
|
+
console.log(PACKAGE_VERSION);
|
|
582
|
+
return;
|
|
583
|
+
}
|
|
552
584
|
if (!command || command === "--help" || command === "-h") {
|
|
553
585
|
printHelp();
|
|
554
586
|
return;
|
|
@@ -557,8 +589,8 @@ async function runCli(argv) {
|
|
|
557
589
|
printCommandHelp(command);
|
|
558
590
|
return;
|
|
559
591
|
}
|
|
560
|
-
if (!["dev", "init", "doctor", "firewall", "shield", "setup", "scan", "optimize", "context", "cc", "usage", "watch", "demo"].includes(command)) {
|
|
561
|
-
throw new Error(`Unknown command: ${command}. Try: prismo doctor, prismo watch, prismo shield, prismo firewall, prismo init, prismo scan, prismo optimize, prismo context, prismo cc, or prismo usage`);
|
|
592
|
+
if (!["dev", "init", "doctor", "firewall", "shield", "mcp", "setup", "scan", "optimize", "context", "cc", "usage", "watch", "demo"].includes(command)) {
|
|
593
|
+
throw new Error(`Unknown command: ${command}. Try: prismo doctor, prismo watch, prismo shield, prismo mcp, prismo firewall, prismo init, prismo scan, prismo optimize, prismo context, prismo cc, or prismo usage`);
|
|
562
594
|
}
|
|
563
595
|
|
|
564
596
|
if (command === "demo") {
|
|
@@ -674,6 +706,28 @@ async function runCli(argv) {
|
|
|
674
706
|
return;
|
|
675
707
|
}
|
|
676
708
|
|
|
709
|
+
if (command === "mcp") {
|
|
710
|
+
const target = getPositionals(rest)[0] || process.cwd();
|
|
711
|
+
runMcpServer({
|
|
712
|
+
rootDir: path.resolve(target),
|
|
713
|
+
packageVersion: PACKAGE_VERSION,
|
|
714
|
+
scanRepo,
|
|
715
|
+
toJsonPayload,
|
|
716
|
+
runDoctor,
|
|
717
|
+
toDoctorJsonPayload,
|
|
718
|
+
getUsageSummary,
|
|
719
|
+
getClaudeCodeCostSummary,
|
|
720
|
+
runOptimize,
|
|
721
|
+
createOptimizeContext,
|
|
722
|
+
renderStarterPrompt,
|
|
723
|
+
runFirewall,
|
|
724
|
+
runShield,
|
|
725
|
+
runShieldLast,
|
|
726
|
+
runShieldSearch,
|
|
727
|
+
});
|
|
728
|
+
return;
|
|
729
|
+
}
|
|
730
|
+
|
|
677
731
|
if (command === "setup") {
|
|
678
732
|
const json = rest.includes("--json");
|
|
679
733
|
const limitIndex = rest.indexOf("--limit");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "getprismo",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.15",
|
|
4
4
|
"description": "Local AI coding workflow scanner for Codex, Claude Code, Cursor, and token-waste diagnostics.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"homepage": "https://github.com/shanirsh/prismodev#readme",
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
"files": [
|
|
30
30
|
"bin/",
|
|
31
31
|
"lib/",
|
|
32
|
-
"docs/
|
|
32
|
+
"docs/",
|
|
33
33
|
"README.md",
|
|
34
34
|
"LICENSE",
|
|
35
35
|
"NOTICE"
|