getprismo 0.1.7 → 0.1.8

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 CHANGED
@@ -392,6 +392,10 @@ what doctor creates:
392
392
  prismo-dev-report.md full diagnostic report
393
393
  ```
394
394
 
395
+ if an existing `.claudeignore` or `.cursorignore` already covers prismo's recommendations, doctor skips the suggested ignore file instead of creating redundant noise. the default recommendations include common project state, local db, export, credential, and token patterns such as `*_state.json`, `*_tokens.json`, `*_export.json`, `*.sqlite`, `models/`, and `state-backups/`.
396
+
397
+ backend and frontend summaries include load-bearing candidates ranked by lightweight reference signals plus file size, not just directory listings.
398
+
395
399
  what doctor never touches:
396
400
 
397
401
  - your real `CLAUDE.md`
@@ -122,6 +122,19 @@ const DEFAULT_CLAUDEIGNORE = [
122
122
  "pnpm-lock.yaml",
123
123
  "test-results/",
124
124
  "playwright-report/",
125
+ "models/",
126
+ "state-backups/",
127
+ "backups/",
128
+ "*.sqlite",
129
+ "*.sqlite3",
130
+ "*.db",
131
+ "*_state.json",
132
+ "*_tokens.json",
133
+ "*_export.json",
134
+ "*secret*.json",
135
+ "*credential*.json",
136
+ ".env",
137
+ ".env.*",
125
138
  ];
126
139
 
127
140
  const NPX_COMMAND = "npx getprismo";
@@ -217,12 +217,20 @@ function mdList(items, empty = "None detected.") {
217
217
  return items.map((item) => `- \`${item}\``).join("\n");
218
218
  }
219
219
 
220
- function topBySize(files, limit = 8) {
220
+ function topLoadBearing(root, files, allFiles, limit = 8) {
221
+ const textFiles = (allFiles || []).filter((file) => !file.ignored && file.kind !== "binary" && /\.(py|tsx?|jsx?)$/.test(file.path));
222
+ const corpus = textFiles.map((file) => `${file.path}\n${readIfText(path.join(root, file.path)) || ""}`);
221
223
  return [...(files || [])]
222
224
  .filter((file) => !file.ignored && file.kind !== "binary")
223
- .sort((a, b) => b.size - a.size)
225
+ .map((file) => {
226
+ const base = path.basename(file.path).replace(/\.[^.]+$/, "");
227
+ const importName = base.replace(/[-.]/g, "_");
228
+ const refs = corpus.reduce((sum, text) => sum + (text.includes(base) || text.includes(importName) ? 1 : 0), 0);
229
+ return { file, refs };
230
+ })
231
+ .sort((a, b) => b.refs - a.refs || b.file.size - a.file.size)
224
232
  .slice(0, limit)
225
- .map((file) => `${file.path} (${formatBytes(file.size)})`);
233
+ .map(({ file, refs }) => `${file.path} (${refs} reference signal${refs === 1 ? "" : "s"}, ${formatBytes(file.size)})`);
226
234
  }
227
235
 
228
236
  function inferGaps(ctx) {
@@ -334,7 +342,7 @@ function renderArchitectureSummary(ctx) {
334
342
  }
335
343
 
336
344
  function renderBackendSummary(ctx) {
337
- const backendCandidates = topBySize(ctx.scan.files.filter((file) => file.path.endsWith(".py") && !isNonSourcePath(file.path)), 8);
345
+ const backendCandidates = topLoadBearing(ctx.root, ctx.scan.files.filter((file) => file.path.endsWith(".py") && !isNonSourcePath(file.path)), ctx.scan.files, 8);
338
346
  return [
339
347
  "# Backend Summary",
340
348
  "",
@@ -378,7 +386,7 @@ function renderBackendSummary(ctx) {
378
386
  }
379
387
 
380
388
  function renderFrontendSummary(ctx) {
381
- const frontendCandidates = topBySize(ctx.scan.files.filter((file) => /\.(tsx?|jsx?)$/.test(file.path) && /(^|\/)(frontend|src|app|components|hooks)\//.test(file.path)), 8);
389
+ const frontendCandidates = topLoadBearing(ctx.root, ctx.scan.files.filter((file) => /\.(tsx?|jsx?)$/.test(file.path) && /(^|\/)(frontend|src|app|components|hooks)\//.test(file.path)), ctx.scan.files, 8);
382
390
  return [
383
391
  "# Frontend Summary",
384
392
  "",
@@ -60,18 +60,24 @@ function applyFixes(result, options = {}) {
60
60
  if (!result.hasClaudeIgnore) {
61
61
  if (!options.dryRun) fs.writeFileSync(claudeIgnorePath, `${result.recommendedClaudeIgnore.join("\n")}\n`, "utf8");
62
62
  actions.push(`${dryRunPrefix} .claudeignore`);
63
+ } else if (result.missingClaudeIgnoreSuggestions && result.missingClaudeIgnoreSuggestions.length === 0) {
64
+ actions.push("Skipped .claudeignore.prismo-suggested because existing .claudeignore already covers Prismo recommendations");
63
65
  } else {
64
66
  const backupPath = options.dryRun ? null : backupIfExists(suggestedClaudeIgnorePath);
65
- if (!options.dryRun) fs.writeFileSync(suggestedClaudeIgnorePath, `${result.recommendedClaudeIgnore.join("\n")}\n`, "utf8");
67
+ const suggestions = result.missingClaudeIgnoreSuggestions || result.recommendedClaudeIgnore;
68
+ if (!options.dryRun) fs.writeFileSync(suggestedClaudeIgnorePath, `${suggestions.join("\n")}\n`, "utf8");
66
69
  actions.push(`${dryRunPrefix} .claudeignore.prismo-suggested because .claudeignore already exists`);
67
70
  if (backupPath) actions.push(`Backed up existing .claudeignore.prismo-suggested to ${path.basename(backupPath)}`);
68
71
  }
69
72
  if (!result.hasCursorIgnore) {
70
73
  if (!options.dryRun) fs.writeFileSync(cursorIgnorePath, `${result.recommendedCursorIgnore.join("\n")}\n`, "utf8");
71
74
  actions.push(`${dryRunPrefix} .cursorignore`);
75
+ } else if (result.missingCursorIgnoreSuggestions && result.missingCursorIgnoreSuggestions.length === 0) {
76
+ actions.push("Skipped .cursorignore.prismo-suggested because existing .cursorignore already covers Prismo recommendations");
72
77
  } else {
73
78
  const backupPath = options.dryRun ? null : backupIfExists(suggestedCursorIgnorePath);
74
- if (!options.dryRun) fs.writeFileSync(suggestedCursorIgnorePath, `${result.recommendedCursorIgnore.join("\n")}\n`, "utf8");
79
+ const suggestions = result.missingCursorIgnoreSuggestions || result.recommendedCursorIgnore;
80
+ if (!options.dryRun) fs.writeFileSync(suggestedCursorIgnorePath, `${suggestions.join("\n")}\n`, "utf8");
75
81
  actions.push(`${dryRunPrefix} .cursorignore.prismo-suggested because .cursorignore already exists`);
76
82
  if (backupPath) actions.push(`Backed up existing .cursorignore.prismo-suggested to ${path.basename(backupPath)}`);
77
83
  }
@@ -312,14 +312,22 @@ function renderMarkdownReport(result) {
312
312
  lines.push("");
313
313
  lines.push("## Recommended .claudeignore");
314
314
  lines.push("");
315
+ if (result.hasClaudeIgnore && result.missingClaudeIgnoreSuggestions && !result.missingClaudeIgnoreSuggestions.length) {
316
+ lines.push("Existing .claudeignore already covers Prismo recommendations.");
317
+ lines.push("");
318
+ }
315
319
  lines.push("```gitignore");
316
- result.recommendedClaudeIgnore.forEach((line) => lines.push(line));
320
+ (result.hasClaudeIgnore && result.missingClaudeIgnoreSuggestions ? result.missingClaudeIgnoreSuggestions : result.recommendedClaudeIgnore).forEach((line) => lines.push(line));
317
321
  lines.push("```");
318
322
  lines.push("");
319
323
  lines.push("## Recommended .cursorignore");
320
324
  lines.push("");
325
+ if (result.hasCursorIgnore && result.missingCursorIgnoreSuggestions && !result.missingCursorIgnoreSuggestions.length) {
326
+ lines.push("Existing .cursorignore already covers Prismo recommendations.");
327
+ lines.push("");
328
+ }
321
329
  lines.push("```gitignore");
322
- result.recommendedCursorIgnore.forEach((line) => lines.push(line));
330
+ (result.hasCursorIgnore && result.missingCursorIgnoreSuggestions ? result.missingCursorIgnoreSuggestions : result.recommendedCursorIgnore).forEach((line) => lines.push(line));
323
331
  lines.push("```");
324
332
  lines.push("");
325
333
  lines.push("## Recommended Next Steps");
@@ -77,6 +77,26 @@ function isIgnored(relPath, patterns, isDir = false) {
77
77
  return patterns.some((pattern) => patternMatches(pattern, relPath, isDir));
78
78
  }
79
79
 
80
+ function ignoreSuggestionCovered(pattern, existingPatterns) {
81
+ if (!pattern) return true;
82
+ if (existingPatterns.includes(pattern)) return true;
83
+ const sample = pattern
84
+ .replace(/^\*\//, "")
85
+ .replace(/^\*\*/, "sample")
86
+ .replace(/\*/g, "sample")
87
+ .replace(/\/$/, "");
88
+ const isDir = pattern.endsWith("/") || pattern.endsWith("/**");
89
+ return existingPatterns.some((existing) => {
90
+ if (existing === pattern) return true;
91
+ if (existing.endsWith("/") && pattern.startsWith(existing)) return true;
92
+ return patternMatches(existing, sample, isDir);
93
+ });
94
+ }
95
+
96
+ function missingIgnoreSuggestions(recommended, existingPatterns) {
97
+ return recommended.filter((pattern) => !ignoreSuggestionCovered(pattern, existingPatterns));
98
+ }
99
+
80
100
  function getFileKind(filePath) {
81
101
  const ext = path.extname(filePath).toLowerCase();
82
102
  const name = path.basename(filePath).toLowerCase();
@@ -703,6 +723,8 @@ function toJsonPayload(result) {
703
723
  proxyTrackingReadiness: result.proxyTrackingReadiness,
704
724
  suggestedClaudeIgnore: result.recommendedClaudeIgnore,
705
725
  suggestedCursorIgnore: result.recommendedCursorIgnore,
726
+ missingClaudeIgnoreSuggestions: result.missingClaudeIgnoreSuggestions || [],
727
+ missingCursorIgnoreSuggestions: result.missingCursorIgnoreSuggestions || [],
706
728
  nextCommands: getNextCommands(result),
707
729
  generatedAt: result.generatedAt,
708
730
  scannedPath: result.root,
@@ -1062,6 +1084,8 @@ function scanRepo(rootDir = process.cwd(), options = {}) {
1062
1084
  ".prismo/",
1063
1085
  "prismo-optimized-CLAUDE.template.md",
1064
1086
  ]));
1087
+ const missingClaudeIgnoreSuggestions = hasClaudeIgnore ? missingIgnoreSuggestions(recommendedClaudeIgnore, claudeIgnorePatterns) : recommendedClaudeIgnore;
1088
+ const missingCursorIgnoreSuggestions = hasCursorIgnore ? missingIgnoreSuggestions(recommendedCursorIgnore, cursorIgnorePatterns) : recommendedCursorIgnore;
1065
1089
  const recommendations = buildRecommendations({
1066
1090
  hasClaudeIgnore,
1067
1091
  gitignorePatterns,
@@ -1103,6 +1127,8 @@ function scanRepo(rootDir = process.cwd(), options = {}) {
1103
1127
  repoDetected,
1104
1128
  recommendedClaudeIgnore,
1105
1129
  recommendedCursorIgnore,
1130
+ missingClaudeIgnoreSuggestions,
1131
+ missingCursorIgnoreSuggestions,
1106
1132
  topTokenLeaks: getTopTokenLeaks(issues),
1107
1133
  generatedAt: new Date().toISOString(),
1108
1134
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "getprismo",
3
- "version": "0.1.7",
3
+ "version": "0.1.8",
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",