getprismo 0.1.20 → 0.1.21
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 +37 -0
- package/lib/prismo-dev/report.js +42 -0
- package/lib/prismo-dev/scan.js +233 -1
- package/lib/prismo-dev-scan.js +26 -7
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -146,6 +146,42 @@ watch caught lockfiles entering context, a file being read 286 times, and tool o
|
|
|
146
146
|
|
|
147
147
|
---
|
|
148
148
|
|
|
149
|
+
## new: optimizer fit
|
|
150
|
+
|
|
151
|
+
not every token optimizer solves the same bottleneck. before stacking compression proxies, repo packers, code indexes, and MCP tools, run:
|
|
152
|
+
|
|
153
|
+
```bash
|
|
154
|
+
npx getprismo scan --optimizer-fit
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
PrismoDev scores your actual repo/session signals and recommends the right path:
|
|
158
|
+
|
|
159
|
+
```text
|
|
160
|
+
Prismo Optimizer Fit
|
|
161
|
+
|
|
162
|
+
Primary bottleneck: Generated artifacts / ignore cleanup: HIGH
|
|
163
|
+
|
|
164
|
+
Bottlenecks
|
|
165
|
+
- Generated artifacts / ignore cleanup: High
|
|
166
|
+
.claudeignore is missing
|
|
167
|
+
- Oversized command/tool output: Medium
|
|
168
|
+
237k tool/output tokens found in local sessions
|
|
169
|
+
- Repeated source exploration: Low
|
|
170
|
+
Repo/source exploration does not look like the main bottleneck
|
|
171
|
+
|
|
172
|
+
Recommended Stack
|
|
173
|
+
1. Apply safe ignore/context fixes first.
|
|
174
|
+
Run: npx getprismo doctor --apply-suggestions --dry-run
|
|
175
|
+
Category: ignore cleanup (.claudeignore, .cursorignore)
|
|
176
|
+
2. Sandbox noisy command output before adding more code-indexing tools.
|
|
177
|
+
Run: npx getprismo shield -- <noisy command>
|
|
178
|
+
Category: output sandboxing (Prismo shield, context-mode, RTK, tokf, distill)
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
This makes PrismoDev the measure-first layer: it tells you whether you need ignore cleanup, output sandboxing, code indexing, repo packing, instruction trimming, session splitting, or MCP/tool hygiene.
|
|
182
|
+
|
|
183
|
+
---
|
|
184
|
+
|
|
149
185
|
## new: context shield
|
|
150
186
|
|
|
151
187
|
if you know a command may dump huge output, run it through prismo:
|
|
@@ -543,6 +579,7 @@ no install needed. npx runs it directly.
|
|
|
543
579
|
| `cc` | claude code cost breakdown |
|
|
544
580
|
| `cc timeline` | session reconstruction with events |
|
|
545
581
|
| `scan --usage` | full repo scan with local usage data |
|
|
582
|
+
| `scan --optimizer-fit` | recommend which token-optimization path fits your repo/session |
|
|
546
583
|
| `scan --simple` | plain-english summary |
|
|
547
584
|
| `scan --fix` | create safe fix files |
|
|
548
585
|
| `scan --ci` | fail CI when token-risk gates fail |
|
package/lib/prismo-dev/report.js
CHANGED
|
@@ -105,6 +105,47 @@ function renderTerminalReport(result, options = {}) {
|
|
|
105
105
|
return lines.join("\n");
|
|
106
106
|
}
|
|
107
107
|
|
|
108
|
+
function renderOptimizerFitTerminal(result, options = {}) {
|
|
109
|
+
const useColor = options.color !== false;
|
|
110
|
+
const fit = result.optimizerFit;
|
|
111
|
+
const tone = fit.summary.includes("High") ? "red" : fit.summary.includes("Medium") ? "yellow" : "green";
|
|
112
|
+
const lines = [];
|
|
113
|
+
lines.push("");
|
|
114
|
+
lines.push(color("Prismo Optimizer Fit", "bold", useColor));
|
|
115
|
+
lines.push("");
|
|
116
|
+
lines.push(`Primary bottleneck: ${color(fit.summary, tone, useColor)}`);
|
|
117
|
+
if (result.realUsage && result.realUsage.sessions.length) {
|
|
118
|
+
lines.push(`Local usage: ${formatTokenCount(result.realUsage.totals.displayTokens)} tokens across ${result.realUsage.sessions.length} session(s)`);
|
|
119
|
+
} else if (result.realUsage) {
|
|
120
|
+
lines.push("Local usage: no matching local Claude/Codex sessions found");
|
|
121
|
+
}
|
|
122
|
+
lines.push("");
|
|
123
|
+
lines.push(color("Bottlenecks", "bold", useColor));
|
|
124
|
+
fit.bottlenecks.forEach((item) => {
|
|
125
|
+
lines.push(`- ${item.label}: ${item.level}`);
|
|
126
|
+
item.evidence.slice(0, 2).forEach((evidence) => lines.push(` ${evidence}`));
|
|
127
|
+
});
|
|
128
|
+
lines.push("");
|
|
129
|
+
lines.push(color("Recommended Stack", "bold", useColor));
|
|
130
|
+
fit.recommendedStack.forEach((item) => {
|
|
131
|
+
lines.push(`${item.rank}. ${item.action}`);
|
|
132
|
+
lines.push(` Run: ${item.command}`);
|
|
133
|
+
lines.push(` Why: ${item.why}`);
|
|
134
|
+
lines.push(` Category: ${item.category} (${item.examples.join(", ")})`);
|
|
135
|
+
});
|
|
136
|
+
lines.push("");
|
|
137
|
+
lines.push(color("Tool Fit", "bold", useColor));
|
|
138
|
+
fit.toolFit.forEach((item) => {
|
|
139
|
+
lines.push(`- ${item.category}: ${item.fit}`);
|
|
140
|
+
lines.push(` Examples: ${item.examples.join(", ")}`);
|
|
141
|
+
lines.push(` ${item.reason}`);
|
|
142
|
+
});
|
|
143
|
+
lines.push("");
|
|
144
|
+
lines.push("Notes:");
|
|
145
|
+
fit.caveats.forEach((caveat) => lines.push(`- ${caveat}`));
|
|
146
|
+
return lines.join("\n");
|
|
147
|
+
}
|
|
148
|
+
|
|
108
149
|
function evaluateCi(result, options = {}) {
|
|
109
150
|
const minScore = Number(options.minScore || 80);
|
|
110
151
|
const failures = [];
|
|
@@ -392,6 +433,7 @@ function writeReport(result) {
|
|
|
392
433
|
evaluateCi,
|
|
393
434
|
renderCiReport,
|
|
394
435
|
renderMarkdownReport,
|
|
436
|
+
renderOptimizerFitTerminal,
|
|
395
437
|
renderSimpleScanReport,
|
|
396
438
|
renderTerminalReport,
|
|
397
439
|
writeReport,
|
package/lib/prismo-dev/scan.js
CHANGED
|
@@ -786,6 +786,235 @@ function buildRecommendations({ hasClaudeIgnore, gitignorePatterns, exposedHighR
|
|
|
786
786
|
return Array.from(new Set(recs));
|
|
787
787
|
}
|
|
788
788
|
|
|
789
|
+
function levelFromScore(score) {
|
|
790
|
+
if (score >= 70) return "High";
|
|
791
|
+
if (score >= 35) return "Medium";
|
|
792
|
+
return "Low";
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
function addEvidence(evidence, text) {
|
|
796
|
+
if (text && !evidence.includes(text)) evidence.push(text);
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
function countRepeatedSourceReads(realUsage) {
|
|
800
|
+
if (!realUsage || !Array.isArray(realUsage.sessions)) return 0;
|
|
801
|
+
const generatedPattern = /(^|\/)(node_modules|dist|build|coverage|\.next|__pycache__|logs|test-results|playwright-report)\//;
|
|
802
|
+
return realUsage.sessions.reduce((sum, session) => {
|
|
803
|
+
return sum + (session.repeatedPathMentions || []).filter((item) => {
|
|
804
|
+
const value = String(item.value || "");
|
|
805
|
+
if (!value || generatedPattern.test(value)) return false;
|
|
806
|
+
return /\.(js|jsx|ts|tsx|py|go|rs|java|kt|swift|rb|php|cs|svelte|vue|astro|md|json|toml|yaml|yml)$/i.test(value);
|
|
807
|
+
}).reduce((inner, item) => inner + Number(item.count || 0), 0);
|
|
808
|
+
}, 0);
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
function buildOptimizerFit(result) {
|
|
812
|
+
const bottlenecks = [];
|
|
813
|
+
const realUsage = result.realUsage;
|
|
814
|
+
const toolTokens = realUsage ? Number(realUsage.totals.toolTokens || 0) : 0;
|
|
815
|
+
const displayTokens = realUsage ? Number(realUsage.totals.displayTokens || 0) : 0;
|
|
816
|
+
const highRiskSessions = realUsage ? realUsage.sessions.filter((session) => session.contextRisk === "High").length : 0;
|
|
817
|
+
const repeatedSourceReads = countRepeatedSourceReads(realUsage);
|
|
818
|
+
|
|
819
|
+
const ignoreEvidence = [];
|
|
820
|
+
let ignoreScore = 0;
|
|
821
|
+
if (!result.hasClaudeIgnore) {
|
|
822
|
+
ignoreScore += 35;
|
|
823
|
+
addEvidence(ignoreEvidence, ".claudeignore is missing");
|
|
824
|
+
}
|
|
825
|
+
if (!result.hasCursorIgnore) {
|
|
826
|
+
ignoreScore += 20;
|
|
827
|
+
addEvidence(ignoreEvidence, ".cursorignore is missing");
|
|
828
|
+
}
|
|
829
|
+
if (result.exposedHighRiskDirs.length) {
|
|
830
|
+
ignoreScore += Math.min(35, result.exposedHighRiskDirs.length * 8);
|
|
831
|
+
addEvidence(ignoreEvidence, `${result.exposedHighRiskDirs.length} generated/cache directories are exposed`);
|
|
832
|
+
}
|
|
833
|
+
if ((result.sessionIgnoreSuggestions || []).length) {
|
|
834
|
+
ignoreScore += 25;
|
|
835
|
+
addEvidence(ignoreEvidence, `${result.sessionIgnoreSuggestions.length} ignore rules came from actual session leaks`);
|
|
836
|
+
}
|
|
837
|
+
bottlenecks.push({
|
|
838
|
+
id: "ignore-cleanup",
|
|
839
|
+
label: "Generated artifacts / ignore cleanup",
|
|
840
|
+
score: Math.min(100, ignoreScore),
|
|
841
|
+
level: levelFromScore(ignoreScore),
|
|
842
|
+
evidence: ignoreEvidence.length ? ignoreEvidence : ["No major ignore-file leak detected"],
|
|
843
|
+
});
|
|
844
|
+
|
|
845
|
+
const outputEvidence = [];
|
|
846
|
+
let outputScore = result.toolOutputRisk.level === "High" ? 70 : result.toolOutputRisk.level === "Medium" ? 45 : 10;
|
|
847
|
+
if (toolTokens >= 150000) outputScore += 25;
|
|
848
|
+
else if (toolTokens >= 50000) outputScore += 15;
|
|
849
|
+
if (result.toolOutputRisk.exposedNoisyFiles.length) addEvidence(outputEvidence, `${result.toolOutputRisk.exposedNoisyFiles.length} noisy files are exposed`);
|
|
850
|
+
if (result.toolOutputRisk.exposedNoisyDirectories.length) addEvidence(outputEvidence, `${result.toolOutputRisk.exposedNoisyDirectories.length} noisy directories are exposed`);
|
|
851
|
+
if (toolTokens) addEvidence(outputEvidence, `${formatTokenCount(toolTokens)} tool/output tokens found in local sessions`);
|
|
852
|
+
bottlenecks.push({
|
|
853
|
+
id: "output-sandboxing",
|
|
854
|
+
label: "Oversized command/tool output",
|
|
855
|
+
score: Math.min(100, outputScore),
|
|
856
|
+
level: levelFromScore(outputScore),
|
|
857
|
+
evidence: outputEvidence.length ? outputEvidence : ["No dominant command-output flood detected"],
|
|
858
|
+
});
|
|
859
|
+
|
|
860
|
+
const indexEvidence = [];
|
|
861
|
+
let indexScore = 0;
|
|
862
|
+
if (result.stats.sourceFiles >= 1000) indexScore += 35;
|
|
863
|
+
else if (result.stats.sourceFiles >= 250) indexScore += 20;
|
|
864
|
+
if (result.stats.totalFiles >= 5000) indexScore += 25;
|
|
865
|
+
else if (result.stats.totalFiles >= 1000) indexScore += 15;
|
|
866
|
+
if (repeatedSourceReads >= 50) indexScore += 35;
|
|
867
|
+
else if (repeatedSourceReads >= 12) indexScore += 20;
|
|
868
|
+
if (result.stats.sourceFiles >= 250) addEvidence(indexEvidence, `${result.stats.sourceFiles.toLocaleString()} source files`);
|
|
869
|
+
if (repeatedSourceReads) addEvidence(indexEvidence, `${repeatedSourceReads} repeated source-file mentions in local sessions`);
|
|
870
|
+
bottlenecks.push({
|
|
871
|
+
id: "code-indexing",
|
|
872
|
+
label: "Repeated source exploration",
|
|
873
|
+
score: Math.min(100, indexScore),
|
|
874
|
+
level: levelFromScore(indexScore),
|
|
875
|
+
evidence: indexEvidence.length ? indexEvidence : ["Repo/source exploration does not look like the main bottleneck"],
|
|
876
|
+
});
|
|
877
|
+
|
|
878
|
+
const instructionEvidence = [];
|
|
879
|
+
const instructionTokens = result.instructionFiles.reduce((sum, file) => sum + Math.max(0, (file.tokens || 0) - 500), 0);
|
|
880
|
+
let instructionScore = instructionTokens >= 3000 ? 80 : instructionTokens >= 1000 ? 55 : instructionTokens > 0 ? 30 : 0;
|
|
881
|
+
result.instructionFiles
|
|
882
|
+
.filter((file) => file.tokens > 500)
|
|
883
|
+
.slice(0, 3)
|
|
884
|
+
.forEach((file) => addEvidence(instructionEvidence, `${file.path} is ~${(file.tokens || 0).toLocaleString()} tokens`));
|
|
885
|
+
bottlenecks.push({
|
|
886
|
+
id: "instruction-trim",
|
|
887
|
+
label: "Persistent instruction bloat",
|
|
888
|
+
score: Math.min(100, instructionScore),
|
|
889
|
+
level: levelFromScore(instructionScore),
|
|
890
|
+
evidence: instructionEvidence.length ? instructionEvidence : ["Persistent instruction files look manageable"],
|
|
891
|
+
});
|
|
892
|
+
|
|
893
|
+
const sessionEvidence = [];
|
|
894
|
+
let sessionScore = 0;
|
|
895
|
+
if (displayTokens >= 2000000) sessionScore += 60;
|
|
896
|
+
else if (displayTokens >= 500000) sessionScore += 35;
|
|
897
|
+
if (highRiskSessions) sessionScore += Math.min(35, highRiskSessions * 18);
|
|
898
|
+
if (displayTokens) addEvidence(sessionEvidence, `${formatTokenCount(displayTokens)} tokens across recent local sessions`);
|
|
899
|
+
if (highRiskSessions) addEvidence(sessionEvidence, `${highRiskSessions} high-context-risk session${highRiskSessions === 1 ? "" : "s"}`);
|
|
900
|
+
bottlenecks.push({
|
|
901
|
+
id: "session-splitting",
|
|
902
|
+
label: "Long-session context buildup",
|
|
903
|
+
score: Math.min(100, sessionScore),
|
|
904
|
+
level: levelFromScore(sessionScore),
|
|
905
|
+
evidence: sessionEvidence.length ? sessionEvidence : ["No matching high-growth local sessions found"],
|
|
906
|
+
});
|
|
907
|
+
|
|
908
|
+
const mcpEvidence = [];
|
|
909
|
+
let mcpScore = result.optimizationStack.mcpServerTotal >= 10 ? 70 : result.optimizationStack.mcpServerTotal >= 5 ? 45 : 10;
|
|
910
|
+
if (result.optimizationStack.mcpServerTotal) addEvidence(mcpEvidence, `${result.optimizationStack.mcpServerTotal} MCP/tool servers detected`);
|
|
911
|
+
bottlenecks.push({
|
|
912
|
+
id: "tool-surface",
|
|
913
|
+
label: "Tool/MCP surface overhead",
|
|
914
|
+
score: Math.min(100, mcpScore),
|
|
915
|
+
level: levelFromScore(mcpScore),
|
|
916
|
+
evidence: mcpEvidence.length ? mcpEvidence : ["Tool surface does not look unusually large"],
|
|
917
|
+
});
|
|
918
|
+
|
|
919
|
+
const ranked = bottlenecks.sort((a, b) => b.score - a.score || a.label.localeCompare(b.label));
|
|
920
|
+
const primary = ranked[0];
|
|
921
|
+
const actionById = {
|
|
922
|
+
"ignore-cleanup": {
|
|
923
|
+
action: "Apply safe ignore/context fixes first.",
|
|
924
|
+
command: `${NPX_COMMAND} doctor --apply-suggestions --dry-run`,
|
|
925
|
+
category: "ignore cleanup",
|
|
926
|
+
examples: ["Prismo doctor", ".claudeignore", ".cursorignore"],
|
|
927
|
+
},
|
|
928
|
+
"output-sandboxing": {
|
|
929
|
+
action: "Sandbox noisy command output before adding more code-indexing tools.",
|
|
930
|
+
command: `${NPX_COMMAND} shield -- <noisy command>`,
|
|
931
|
+
category: "output sandboxing",
|
|
932
|
+
examples: ["Prismo shield", "context-mode", "RTK", "tokf", "distill"],
|
|
933
|
+
},
|
|
934
|
+
"code-indexing": {
|
|
935
|
+
action: "Use a code indexer if repeated source exploration keeps happening.",
|
|
936
|
+
command: `${NPX_COMMAND} context`,
|
|
937
|
+
category: "code indexing",
|
|
938
|
+
examples: ["codegraph", "jcodemunch", "codebase-memory-mcp", "sigmap"],
|
|
939
|
+
},
|
|
940
|
+
"instruction-trim": {
|
|
941
|
+
action: "Trim persistent instructions before adding runtime compression.",
|
|
942
|
+
command: `${NPX_COMMAND} doctor`,
|
|
943
|
+
category: "instruction quality",
|
|
944
|
+
examples: ["CLAUDE.md cleanup", "AGENTS.md cleanup", "caveman-style concise responses"],
|
|
945
|
+
},
|
|
946
|
+
"session-splitting": {
|
|
947
|
+
action: "Split long sessions and recover from context pressure while working.",
|
|
948
|
+
command: `${NPX_COMMAND} watch --auto`,
|
|
949
|
+
category: "session control",
|
|
950
|
+
examples: ["Prismo watch", "Prismo rescue", "fresh task sessions"],
|
|
951
|
+
},
|
|
952
|
+
"tool-surface": {
|
|
953
|
+
action: "Reduce unused MCP/tool surface for the current task.",
|
|
954
|
+
command: `${NPX_COMMAND} mcp doctor`,
|
|
955
|
+
category: "tool hygiene",
|
|
956
|
+
examples: ["disable unused MCP servers", "strict task-scoped tool config"],
|
|
957
|
+
},
|
|
958
|
+
};
|
|
959
|
+
|
|
960
|
+
const recommendedStack = ranked
|
|
961
|
+
.filter((item) => item.level !== "Low")
|
|
962
|
+
.slice(0, 4)
|
|
963
|
+
.map((item, index) => ({ rank: index + 1, bottleneck: item.id, ...actionById[item.id], why: item.evidence[0] }));
|
|
964
|
+
|
|
965
|
+
if (!recommendedStack.length) {
|
|
966
|
+
recommendedStack.push({
|
|
967
|
+
rank: 1,
|
|
968
|
+
bottleneck: "baseline",
|
|
969
|
+
action: "Keep the stack simple; no major optimizer fit signal was detected.",
|
|
970
|
+
command: `${NPX_COMMAND} watch --once`,
|
|
971
|
+
category: "baseline monitoring",
|
|
972
|
+
examples: ["Prismo watch", "Prismo cc timeline"],
|
|
973
|
+
why: "Repo scan did not find a dominant token-waste source.",
|
|
974
|
+
});
|
|
975
|
+
}
|
|
976
|
+
|
|
977
|
+
return {
|
|
978
|
+
schemaVersion: 1,
|
|
979
|
+
primaryBottleneck: primary.id,
|
|
980
|
+
summary: `${primary.label}: ${primary.level}`,
|
|
981
|
+
bottlenecks: ranked,
|
|
982
|
+
recommendedStack,
|
|
983
|
+
toolFit: [
|
|
984
|
+
{
|
|
985
|
+
category: "PrismoDev workflow",
|
|
986
|
+
fit: "High",
|
|
987
|
+
examples: ["doctor", "watch", "shield", "cc timeline"],
|
|
988
|
+
reason: "Use this first to diagnose repo/session waste and verify before stacking optimizers.",
|
|
989
|
+
},
|
|
990
|
+
{
|
|
991
|
+
category: "Output compression/sandboxing",
|
|
992
|
+
fit: ranked.find((item) => item.id === "output-sandboxing").level,
|
|
993
|
+
examples: ["Prismo shield", "context-mode", "RTK", "tokf", "distill", "headroom"],
|
|
994
|
+
reason: "Best when shell/test/log output is the dominant waste source.",
|
|
995
|
+
},
|
|
996
|
+
{
|
|
997
|
+
category: "Code indexing / AST graph",
|
|
998
|
+
fit: ranked.find((item) => item.id === "code-indexing").level,
|
|
999
|
+
examples: ["codegraph", "jcodemunch", "codebase-memory-mcp", "sigmap"],
|
|
1000
|
+
reason: "Best when the agent repeatedly greps/reads source files to orient itself.",
|
|
1001
|
+
},
|
|
1002
|
+
{
|
|
1003
|
+
category: "Repo packing",
|
|
1004
|
+
fit: result.stats.sourceFiles && result.stats.sourceFiles <= 250 && result.toolOutputRisk.level === "Low" ? "Medium" : "Low",
|
|
1005
|
+
examples: ["repomix", "Prismo context packs"],
|
|
1006
|
+
reason: "Best for one-shot repo handoff, less ideal for long live coding sessions.",
|
|
1007
|
+
},
|
|
1008
|
+
],
|
|
1009
|
+
caveats: [
|
|
1010
|
+
"Do not stack optimizers blindly; measure one real workflow before and after.",
|
|
1011
|
+
"Payload reduction is not the same as workflow savings; repeated tool calls can erase compression wins.",
|
|
1012
|
+
"Token savings are only useful if the agent still finds the right files and produces accepted changes.",
|
|
1013
|
+
],
|
|
1014
|
+
nextCommands: recommendedStack.map((item) => item.command),
|
|
1015
|
+
};
|
|
1016
|
+
}
|
|
1017
|
+
|
|
789
1018
|
function scoreScan(issues, stats, context = {}) {
|
|
790
1019
|
const issuePenalty = issues.reduce((sum, issue) => sum + severityWeight(issue.severity), 0);
|
|
791
1020
|
const repoPenalty =
|
|
@@ -897,6 +1126,7 @@ function toJsonPayload(result) {
|
|
|
897
1126
|
optimizationStack: result.optimizationStack,
|
|
898
1127
|
toolOutputRisk: result.toolOutputRisk,
|
|
899
1128
|
operationalNoise: result.operationalNoise,
|
|
1129
|
+
optimizerFit: result.optimizerFit,
|
|
900
1130
|
sessionIgnoreSuggestions: result.sessionIgnoreSuggestions || [],
|
|
901
1131
|
proxyTrackingReadiness: result.proxyTrackingReadiness,
|
|
902
1132
|
suggestedClaudeIgnore: result.recommendedClaudeIgnore,
|
|
@@ -1308,7 +1538,7 @@ function scanRepo(rootDir = process.cwd(), options = {}) {
|
|
|
1308
1538
|
});
|
|
1309
1539
|
buildRealUsageRecommendations(realUsage).forEach((rec) => recommendations.push(rec));
|
|
1310
1540
|
|
|
1311
|
-
|
|
1541
|
+
const scanResult = {
|
|
1312
1542
|
root,
|
|
1313
1543
|
score: score.score,
|
|
1314
1544
|
risk: score.risk,
|
|
@@ -1344,6 +1574,8 @@ function scanRepo(rootDir = process.cwd(), options = {}) {
|
|
|
1344
1574
|
topTokenLeaks: getTopTokenLeaks(issues),
|
|
1345
1575
|
generatedAt: new Date().toISOString(),
|
|
1346
1576
|
};
|
|
1577
|
+
scanResult.optimizerFit = buildOptimizerFit(scanResult);
|
|
1578
|
+
return scanResult;
|
|
1347
1579
|
}
|
|
1348
1580
|
|
|
1349
1581
|
return {
|
package/lib/prismo-dev-scan.js
CHANGED
|
@@ -193,6 +193,7 @@ const {
|
|
|
193
193
|
evaluateCi,
|
|
194
194
|
renderCiReport,
|
|
195
195
|
renderMarkdownReport,
|
|
196
|
+
renderOptimizerFitTerminal,
|
|
196
197
|
renderSimpleScanReport,
|
|
197
198
|
renderTerminalReport,
|
|
198
199
|
writeReport,
|
|
@@ -305,7 +306,7 @@ Usage:
|
|
|
305
306
|
prismo mcp [path]
|
|
306
307
|
prismo mcp doctor [--json] [path]
|
|
307
308
|
prismo setup [--json] [--proxy-url URL] [path]
|
|
308
|
-
prismo scan [--fix] [--ci] [--json] [--usage] [--simple] [--no-report] [path]
|
|
309
|
+
prismo scan [--fix] [--ci] [--json] [--usage] [--optimizer-fit] [--simple] [--no-report] [path]
|
|
309
310
|
prismo optimize [scope] [--json] [path]
|
|
310
311
|
prismo context [scope] [--json] [path]
|
|
311
312
|
prismo cc [list|last N|all] [--json] [--limit N] [path]
|
|
@@ -334,6 +335,7 @@ Options:
|
|
|
334
335
|
--ci Fail with exit code 1 when token-risk gates fail.
|
|
335
336
|
--json Output valid JSON only for CI or future dashboard ingestion.
|
|
336
337
|
--usage Include real local Codex/Claude Code session usage in scan diagnostics.
|
|
338
|
+
--optimizer-fit Recommend the right optimization path for this repo/session.
|
|
337
339
|
--simple Print a plain-English scan summary for first-time or non-technical users.
|
|
338
340
|
--no-report Do not write .prismo/prismo-dev-report.md.
|
|
339
341
|
--limit N Number of recent local sessions to show.
|
|
@@ -373,18 +375,21 @@ function printCommandHelp(command) {
|
|
|
373
375
|
scan: `PrismoDev
|
|
374
376
|
|
|
375
377
|
Usage:
|
|
376
|
-
prismo scan [--fix] [--ci] [--json] [--usage] [--simple] [--no-report] [--limit N] [path]
|
|
378
|
+
prismo scan [--fix] [--ci] [--json] [--usage] [--optimizer-fit] [--simple] [--no-report] [--limit N] [path]
|
|
377
379
|
|
|
378
380
|
Examples:
|
|
379
381
|
prismo scan
|
|
380
382
|
prismo scan --usage
|
|
383
|
+
prismo scan --optimizer-fit
|
|
381
384
|
prismo scan --simple
|
|
382
385
|
prismo scan --fix
|
|
383
386
|
prismo scan --ci
|
|
384
387
|
prismo scan --usage --json --no-report
|
|
388
|
+
prismo scan --optimizer-fit --json
|
|
385
389
|
|
|
386
390
|
Notes:
|
|
387
391
|
--usage reads local Codex/Claude Code logs when present.
|
|
392
|
+
--optimizer-fit explains whether ignore cleanup, output sandboxing, code indexing, repo packing, instruction trimming, or session splitting fits this repo best.
|
|
388
393
|
--simple keeps the output short and does not write a report unless combined with --fix.
|
|
389
394
|
--fix creates safe recommendation files and never overwrites CLAUDE.md or AGENTS.md.`,
|
|
390
395
|
optimize: `Prismo Optimize
|
|
@@ -920,12 +925,13 @@ async function runCli(argv) {
|
|
|
920
925
|
const noReport = rest.includes("--no-report");
|
|
921
926
|
const json = rest.includes("--json");
|
|
922
927
|
const simple = rest.includes("--simple");
|
|
928
|
+
const optimizerFit = rest.includes("--optimizer-fit");
|
|
923
929
|
const ciMode = rest.includes("--ci");
|
|
924
|
-
const includeUsage = rest.includes("--usage");
|
|
930
|
+
const includeUsage = rest.includes("--usage") || optimizerFit;
|
|
925
931
|
const limitIndex = rest.indexOf("--limit");
|
|
926
932
|
const usageToolIndex = rest.indexOf("--usage-tool");
|
|
927
933
|
const target = getPositionals(rest, new Set(["--limit", "--usage-tool"]))[0] || process.cwd();
|
|
928
|
-
const scanDone = printStep(includeUsage ? "Scanning repo and local usage" : "Scanning repo", json || simple);
|
|
934
|
+
const scanDone = printStep(includeUsage ? "Scanning repo and local usage" : "Scanning repo", json || simple || optimizerFit);
|
|
929
935
|
const result = scanRepo(target, {
|
|
930
936
|
includeUsage,
|
|
931
937
|
usageLimit: parsePositiveInt(limitIndex >= 0 ? rest[limitIndex + 1] : null, 5),
|
|
@@ -938,7 +944,7 @@ async function runCli(argv) {
|
|
|
938
944
|
let report = null;
|
|
939
945
|
if (fix) {
|
|
940
946
|
fixActions = applyFixes(result);
|
|
941
|
-
} else if (!noReport) {
|
|
947
|
+
} else if (!noReport && !optimizerFit) {
|
|
942
948
|
report = writeReport(result);
|
|
943
949
|
}
|
|
944
950
|
const payload = toJsonPayload(result);
|
|
@@ -946,13 +952,26 @@ async function runCli(argv) {
|
|
|
946
952
|
payload.ci = evaluateCi(result);
|
|
947
953
|
if (!payload.ci.passed) process.exitCode = 1;
|
|
948
954
|
}
|
|
955
|
+
if (optimizerFit) {
|
|
956
|
+
console.log(JSON.stringify({
|
|
957
|
+
schemaVersion: 1,
|
|
958
|
+
scannedPath: result.root,
|
|
959
|
+
score: result.score,
|
|
960
|
+
riskLevel: result.risk,
|
|
961
|
+
optimizerFit: result.optimizerFit,
|
|
962
|
+
generatedAt: result.generatedAt,
|
|
963
|
+
}, null, 2));
|
|
964
|
+
return;
|
|
965
|
+
}
|
|
949
966
|
if (fixActions.length) payload.fixActions = fixActions;
|
|
950
967
|
if (report) payload.reportPath = report.reportPath;
|
|
951
968
|
console.log(JSON.stringify(payload, null, 2));
|
|
952
969
|
return;
|
|
953
970
|
}
|
|
954
971
|
|
|
955
|
-
if (
|
|
972
|
+
if (optimizerFit) {
|
|
973
|
+
console.log(renderOptimizerFitTerminal(result));
|
|
974
|
+
} else if (simple) {
|
|
956
975
|
console.log(renderSimpleScanReport(result));
|
|
957
976
|
} else if (ciMode) {
|
|
958
977
|
const ci = evaluateCi(result);
|
|
@@ -966,7 +985,7 @@ async function runCli(argv) {
|
|
|
966
985
|
const actions = applyFixes(result);
|
|
967
986
|
console.log("\nFix Mode:");
|
|
968
987
|
actions.forEach((action) => console.log(`- ${action}`));
|
|
969
|
-
} else if (!noReport && !simple) {
|
|
988
|
+
} else if (!noReport && !simple && !optimizerFit) {
|
|
970
989
|
const report = writeReport(result);
|
|
971
990
|
if (report.backupPath) {
|
|
972
991
|
console.log(`\nExisting report backed up to ${path.basename(report.backupPath)}.`);
|
package/package.json
CHANGED