getprismo 0.1.22 → 0.1.24
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 +8 -1
- package/lib/prismo-dev/scan-path-utils.js +203 -0
- package/lib/prismo-dev/scan.js +7 -191
- package/lib/prismo-dev/usage-cost.js +336 -0
- package/lib/prismo-dev/usage-log-utils.js +251 -0
- package/lib/prismo-dev/usage-sessions.js +298 -0
- package/lib/prismo-dev/usage-watch.js +72 -1417
- package/lib/prismo-dev/utils.js +85 -0
- package/lib/prismo-dev/watch-live.js +353 -0
- package/lib/prismo-dev/watch-render.js +290 -0
- package/lib/prismo-dev-scan.js +9 -70
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -864,8 +864,15 @@ lib/prismo-dev/fixes.js safe ignore/template generation
|
|
|
864
864
|
lib/prismo-dev/mcp.js local MCP server and Prismo tool bindings
|
|
865
865
|
lib/prismo-dev/report.js terminal, markdown, ci reports
|
|
866
866
|
lib/prismo-dev/scan.js repo scanning, scoring, readiness
|
|
867
|
+
lib/prismo-dev/scan-path-utils.js scan ignore/path helper logic
|
|
867
868
|
lib/prismo-dev/shield.js local command shield and searchable output index
|
|
868
|
-
lib/prismo-dev/usage-
|
|
869
|
+
lib/prismo-dev/usage-cost.js Claude Code cost and timeline analysis
|
|
870
|
+
lib/prismo-dev/usage-log-utils.js local session log parsing helpers
|
|
871
|
+
lib/prismo-dev/usage-sessions.js local Codex/Claude session discovery
|
|
872
|
+
lib/prismo-dev/usage-watch.js watch orchestration, JSON payloads, live files
|
|
873
|
+
lib/prismo-dev/utils.js shared terminal/file/token helpers
|
|
874
|
+
lib/prismo-dev/watch-live.js live context-pressure decisions
|
|
875
|
+
lib/prismo-dev/watch-render.js watch terminal and guardrail renderers
|
|
869
876
|
```
|
|
870
877
|
|
|
871
878
|
---
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
module.exports = function createScanPathUtils(deps) {
|
|
2
|
+
const { fs, path } = deps;
|
|
3
|
+
|
|
4
|
+
function normalizeRel(value) {
|
|
5
|
+
return value.split(path.sep).join("/");
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
function readIgnoreFile(root, fileName) {
|
|
9
|
+
const filePath = path.join(root, fileName);
|
|
10
|
+
if (!fs.existsSync(filePath)) return [];
|
|
11
|
+
const text = fs.readFileSync(filePath, "utf8");
|
|
12
|
+
return text
|
|
13
|
+
.split(/\r?\n/)
|
|
14
|
+
.map((line) => line.trim())
|
|
15
|
+
.filter((line) => line && !line.startsWith("#"))
|
|
16
|
+
.map((line) => line.replace(/^!/, ""));
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function patternMatches(pattern, relPath, isDir = false) {
|
|
20
|
+
const normalizedPattern = pattern.replace(/\\/g, "/").replace(/^\//, "");
|
|
21
|
+
const normalizedRel = normalizeRel(relPath);
|
|
22
|
+
const dirRel = isDir && !normalizedRel.endsWith("/") ? `${normalizedRel}/` : normalizedRel;
|
|
23
|
+
|
|
24
|
+
if (!normalizedPattern) return false;
|
|
25
|
+
if (normalizedPattern.endsWith("/")) {
|
|
26
|
+
const base = normalizedPattern.slice(0, -1);
|
|
27
|
+
return (
|
|
28
|
+
normalizedRel === base ||
|
|
29
|
+
normalizedRel.startsWith(`${base}/`) ||
|
|
30
|
+
normalizedRel.endsWith(`/${base}`) ||
|
|
31
|
+
normalizedRel.includes(`/${base}/`) ||
|
|
32
|
+
dirRel.includes(`/${base}/`)
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
if (normalizedPattern.startsWith("*.")) {
|
|
36
|
+
return normalizedRel.endsWith(normalizedPattern.slice(1));
|
|
37
|
+
}
|
|
38
|
+
if (normalizedPattern.includes("*")) {
|
|
39
|
+
const escaped = normalizedPattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*");
|
|
40
|
+
return new RegExp(`(^|/)${escaped}$`).test(normalizedRel);
|
|
41
|
+
}
|
|
42
|
+
return (
|
|
43
|
+
normalizedRel === normalizedPattern ||
|
|
44
|
+
dirRel === normalizedPattern ||
|
|
45
|
+
normalizedRel.startsWith(`${normalizedPattern}/`) ||
|
|
46
|
+
normalizedRel.endsWith(`/${normalizedPattern}`)
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function isIgnored(relPath, patterns, isDir = false) {
|
|
51
|
+
return patterns.some((pattern) => patternMatches(pattern, relPath, isDir));
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function ignoreSuggestionCovered(pattern, existingPatterns) {
|
|
55
|
+
if (!pattern) return true;
|
|
56
|
+
if (existingPatterns.includes(pattern)) return true;
|
|
57
|
+
const sample = pattern
|
|
58
|
+
.replace(/^\*\//, "")
|
|
59
|
+
.replace(/^\*\*/, "sample")
|
|
60
|
+
.replace(/\*/g, "sample")
|
|
61
|
+
.replace(/\/$/, "");
|
|
62
|
+
const isDir = pattern.endsWith("/") || pattern.endsWith("/**");
|
|
63
|
+
return existingPatterns.some((existing) => {
|
|
64
|
+
if (existing === pattern) return true;
|
|
65
|
+
if (existing.endsWith("/") && pattern.startsWith(existing)) return true;
|
|
66
|
+
return patternMatches(existing, sample, isDir);
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function missingIgnoreSuggestions(recommended, existingPatterns) {
|
|
71
|
+
return recommended.filter((pattern) => !ignoreSuggestionCovered(pattern, existingPatterns));
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const SESSION_NOISE_DIRS = new Set([
|
|
75
|
+
".next",
|
|
76
|
+
".nuxt",
|
|
77
|
+
".prismo",
|
|
78
|
+
".pytest_cache",
|
|
79
|
+
".turbo",
|
|
80
|
+
"__pycache__",
|
|
81
|
+
"build",
|
|
82
|
+
"calendar-dumps",
|
|
83
|
+
"coverage",
|
|
84
|
+
"dist",
|
|
85
|
+
"event-dumps",
|
|
86
|
+
"events",
|
|
87
|
+
"exports",
|
|
88
|
+
"htmlcov",
|
|
89
|
+
"inbox-dumps",
|
|
90
|
+
"logs",
|
|
91
|
+
"models",
|
|
92
|
+
"node_modules",
|
|
93
|
+
"out",
|
|
94
|
+
"playwright-report",
|
|
95
|
+
"session-dumps",
|
|
96
|
+
"source-streams",
|
|
97
|
+
"state-backups",
|
|
98
|
+
"test-results",
|
|
99
|
+
"tmp",
|
|
100
|
+
"temp",
|
|
101
|
+
]);
|
|
102
|
+
|
|
103
|
+
const SESSION_NOISE_FILE_NAMES = new Set([
|
|
104
|
+
"package-lock.json",
|
|
105
|
+
"pnpm-lock.yaml",
|
|
106
|
+
"yarn.lock",
|
|
107
|
+
"bun.lockb",
|
|
108
|
+
"coverage-final.json",
|
|
109
|
+
"lcov.info",
|
|
110
|
+
]);
|
|
111
|
+
|
|
112
|
+
const SESSION_NOISE_EXTENSIONS = new Set([
|
|
113
|
+
".db",
|
|
114
|
+
".jsonl",
|
|
115
|
+
".lock",
|
|
116
|
+
".log",
|
|
117
|
+
".sqlite",
|
|
118
|
+
".sqlite3",
|
|
119
|
+
]);
|
|
120
|
+
|
|
121
|
+
function cleanSessionPath(value) {
|
|
122
|
+
const text = String(value || "").trim().replace(/\\/g, "/");
|
|
123
|
+
if (!text || /^https?:\/\//.test(text)) return null;
|
|
124
|
+
const withoutQuotes = text.replace(/^["'`]+|["'`.,:;)\]]+$/g, "");
|
|
125
|
+
if (!withoutQuotes || withoutQuotes.includes("\n")) return null;
|
|
126
|
+
const markerIndex = withoutQuotes.indexOf("/Users/");
|
|
127
|
+
if (markerIndex > 0) return withoutQuotes.slice(markerIndex);
|
|
128
|
+
return withoutQuotes;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function sessionIgnorePatternForPath(value, root) {
|
|
132
|
+
const cleaned = cleanSessionPath(value);
|
|
133
|
+
if (!cleaned) return null;
|
|
134
|
+
const rootNormalized = normalizeRel(root);
|
|
135
|
+
let rel = cleaned;
|
|
136
|
+
if (path.isAbsolute(cleaned)) {
|
|
137
|
+
const normalized = normalizeRel(cleaned);
|
|
138
|
+
if (!normalized.startsWith(`${rootNormalized}/`)) return null;
|
|
139
|
+
rel = normalizeRel(path.relative(root, cleaned));
|
|
140
|
+
}
|
|
141
|
+
rel = normalizeRel(rel).replace(/^\.\//, "");
|
|
142
|
+
if (!rel || rel === "." || rel.startsWith("../") || rel.includes("..")) return null;
|
|
143
|
+
|
|
144
|
+
const segments = rel.split("/").filter(Boolean);
|
|
145
|
+
if (!segments.length) return null;
|
|
146
|
+
for (let index = 0; index < segments.length; index += 1) {
|
|
147
|
+
const segment = segments[index];
|
|
148
|
+
if (SESSION_NOISE_DIRS.has(segment)) {
|
|
149
|
+
return `${segments.slice(0, index + 1).join("/")}/`;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const fileName = segments[segments.length - 1];
|
|
154
|
+
const lowerName = fileName.toLowerCase();
|
|
155
|
+
const ext = path.extname(lowerName);
|
|
156
|
+
if (SESSION_NOISE_FILE_NAMES.has(lowerName)) return fileName;
|
|
157
|
+
if (SESSION_NOISE_EXTENSIONS.has(ext)) return rel;
|
|
158
|
+
if (/_state\.json$/i.test(fileName)) return "*_state.json";
|
|
159
|
+
if (/_tokens\.json$/i.test(fileName)) return "*_tokens.json";
|
|
160
|
+
if (/_export\.json$/i.test(fileName)) return "*_export.json";
|
|
161
|
+
if (/secret|credential|token/i.test(fileName) && /\.json$/i.test(fileName)) return rel;
|
|
162
|
+
return null;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function buildSessionIgnoreSuggestions(realUsage, root) {
|
|
166
|
+
if (!realUsage || !Array.isArray(realUsage.sessions)) return [];
|
|
167
|
+
const byPattern = new Map();
|
|
168
|
+
const add = (pattern, item, source, reason) => {
|
|
169
|
+
if (!pattern) return;
|
|
170
|
+
const existing = byPattern.get(pattern) || {
|
|
171
|
+
pattern,
|
|
172
|
+
source,
|
|
173
|
+
reason,
|
|
174
|
+
count: 0,
|
|
175
|
+
examples: [],
|
|
176
|
+
};
|
|
177
|
+
existing.count += Number(item?.count || 1);
|
|
178
|
+
const example = item?.value || item?.path || pattern;
|
|
179
|
+
if (example && !existing.examples.includes(example) && existing.examples.length < 3) existing.examples.push(example);
|
|
180
|
+
byPattern.set(pattern, existing);
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
for (const session of realUsage.sessions) {
|
|
184
|
+
for (const item of session.generatedArtifacts || []) {
|
|
185
|
+
add(sessionIgnorePatternForPath(item.value, root), item, session.tool || "session", "Generated artifact entered local session context.");
|
|
186
|
+
}
|
|
187
|
+
for (const item of session.repeatedPathMentions || []) {
|
|
188
|
+
add(sessionIgnorePatternForPath(item.value, root), item, session.tool || "session", "Noisy path appeared repeatedly in local session context.");
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
return Array.from(byPattern.values())
|
|
192
|
+
.sort((a, b) => b.count - a.count || a.pattern.localeCompare(b.pattern))
|
|
193
|
+
.slice(0, 25);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
return {
|
|
197
|
+
buildSessionIgnoreSuggestions,
|
|
198
|
+
isIgnored,
|
|
199
|
+
missingIgnoreSuggestions,
|
|
200
|
+
normalizeRel,
|
|
201
|
+
readIgnoreFile,
|
|
202
|
+
};
|
|
203
|
+
};
|
package/lib/prismo-dev/scan.js
CHANGED
|
@@ -27,197 +27,13 @@ const ASSUMED_TURNS_PER_AI_SESSION = 40;
|
|
|
27
27
|
const ASSUMED_INPUT_COST_PER_1K_TOKENS = 0.003;
|
|
28
28
|
const ASSUMED_SESSIONS_PER_MONTH = 30;
|
|
29
29
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
const text = fs.readFileSync(filePath, "utf8");
|
|
38
|
-
return text
|
|
39
|
-
.split(/\r?\n/)
|
|
40
|
-
.map((line) => line.trim())
|
|
41
|
-
.filter((line) => line && !line.startsWith("#"))
|
|
42
|
-
.map((line) => line.replace(/^!/, ""));
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
function patternMatches(pattern, relPath, isDir = false) {
|
|
46
|
-
const normalizedPattern = pattern.replace(/\\/g, "/").replace(/^\//, "");
|
|
47
|
-
const normalizedRel = normalizeRel(relPath);
|
|
48
|
-
const dirRel = isDir && !normalizedRel.endsWith("/") ? `${normalizedRel}/` : normalizedRel;
|
|
49
|
-
|
|
50
|
-
if (!normalizedPattern) return false;
|
|
51
|
-
if (normalizedPattern.endsWith("/")) {
|
|
52
|
-
const base = normalizedPattern.slice(0, -1);
|
|
53
|
-
return (
|
|
54
|
-
normalizedRel === base ||
|
|
55
|
-
normalizedRel.startsWith(`${base}/`) ||
|
|
56
|
-
normalizedRel.endsWith(`/${base}`) ||
|
|
57
|
-
normalizedRel.includes(`/${base}/`) ||
|
|
58
|
-
dirRel.includes(`/${base}/`)
|
|
59
|
-
);
|
|
60
|
-
}
|
|
61
|
-
if (normalizedPattern.startsWith("*.")) {
|
|
62
|
-
return normalizedRel.endsWith(normalizedPattern.slice(1));
|
|
63
|
-
}
|
|
64
|
-
if (normalizedPattern.includes("*")) {
|
|
65
|
-
const escaped = normalizedPattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*");
|
|
66
|
-
return new RegExp(`(^|/)${escaped}$`).test(normalizedRel);
|
|
67
|
-
}
|
|
68
|
-
return (
|
|
69
|
-
normalizedRel === normalizedPattern ||
|
|
70
|
-
dirRel === normalizedPattern ||
|
|
71
|
-
normalizedRel.startsWith(`${normalizedPattern}/`) ||
|
|
72
|
-
normalizedRel.endsWith(`/${normalizedPattern}`)
|
|
73
|
-
);
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
function isIgnored(relPath, patterns, isDir = false) {
|
|
77
|
-
return patterns.some((pattern) => patternMatches(pattern, relPath, isDir));
|
|
78
|
-
}
|
|
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
|
-
|
|
100
|
-
const SESSION_NOISE_DIRS = new Set([
|
|
101
|
-
".next",
|
|
102
|
-
".nuxt",
|
|
103
|
-
".prismo",
|
|
104
|
-
".pytest_cache",
|
|
105
|
-
".turbo",
|
|
106
|
-
"__pycache__",
|
|
107
|
-
"build",
|
|
108
|
-
"calendar-dumps",
|
|
109
|
-
"coverage",
|
|
110
|
-
"dist",
|
|
111
|
-
"event-dumps",
|
|
112
|
-
"events",
|
|
113
|
-
"exports",
|
|
114
|
-
"htmlcov",
|
|
115
|
-
"inbox-dumps",
|
|
116
|
-
"logs",
|
|
117
|
-
"models",
|
|
118
|
-
"node_modules",
|
|
119
|
-
"out",
|
|
120
|
-
"playwright-report",
|
|
121
|
-
"session-dumps",
|
|
122
|
-
"source-streams",
|
|
123
|
-
"state-backups",
|
|
124
|
-
"test-results",
|
|
125
|
-
"tmp",
|
|
126
|
-
"temp",
|
|
127
|
-
]);
|
|
128
|
-
|
|
129
|
-
const SESSION_NOISE_FILE_NAMES = new Set([
|
|
130
|
-
"package-lock.json",
|
|
131
|
-
"pnpm-lock.yaml",
|
|
132
|
-
"yarn.lock",
|
|
133
|
-
"bun.lockb",
|
|
134
|
-
"coverage-final.json",
|
|
135
|
-
"lcov.info",
|
|
136
|
-
]);
|
|
137
|
-
|
|
138
|
-
const SESSION_NOISE_EXTENSIONS = new Set([
|
|
139
|
-
".db",
|
|
140
|
-
".jsonl",
|
|
141
|
-
".lock",
|
|
142
|
-
".log",
|
|
143
|
-
".sqlite",
|
|
144
|
-
".sqlite3",
|
|
145
|
-
]);
|
|
146
|
-
|
|
147
|
-
function cleanSessionPath(value) {
|
|
148
|
-
const text = String(value || "").trim().replace(/\\/g, "/");
|
|
149
|
-
if (!text || /^https?:\/\//.test(text)) return null;
|
|
150
|
-
const withoutQuotes = text.replace(/^["'`]+|["'`.,:;)\]]+$/g, "");
|
|
151
|
-
if (!withoutQuotes || withoutQuotes.includes("\n")) return null;
|
|
152
|
-
const markerIndex = withoutQuotes.indexOf("/Users/");
|
|
153
|
-
if (markerIndex > 0) return withoutQuotes.slice(markerIndex);
|
|
154
|
-
return withoutQuotes;
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
function sessionIgnorePatternForPath(value, root) {
|
|
158
|
-
const cleaned = cleanSessionPath(value);
|
|
159
|
-
if (!cleaned) return null;
|
|
160
|
-
const rootNormalized = normalizeRel(root);
|
|
161
|
-
let rel = cleaned;
|
|
162
|
-
if (path.isAbsolute(cleaned)) {
|
|
163
|
-
const normalized = normalizeRel(cleaned);
|
|
164
|
-
if (!normalized.startsWith(`${rootNormalized}/`)) return null;
|
|
165
|
-
rel = normalizeRel(path.relative(root, cleaned));
|
|
166
|
-
}
|
|
167
|
-
rel = normalizeRel(rel).replace(/^\.\//, "");
|
|
168
|
-
if (!rel || rel === "." || rel.startsWith("../") || rel.includes("..")) return null;
|
|
169
|
-
|
|
170
|
-
const segments = rel.split("/").filter(Boolean);
|
|
171
|
-
if (!segments.length) return null;
|
|
172
|
-
for (let index = 0; index < segments.length; index += 1) {
|
|
173
|
-
const segment = segments[index];
|
|
174
|
-
if (SESSION_NOISE_DIRS.has(segment)) {
|
|
175
|
-
return `${segments.slice(0, index + 1).join("/")}/`;
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
const fileName = segments[segments.length - 1];
|
|
180
|
-
const lowerName = fileName.toLowerCase();
|
|
181
|
-
const ext = path.extname(lowerName);
|
|
182
|
-
if (SESSION_NOISE_FILE_NAMES.has(lowerName)) return fileName;
|
|
183
|
-
if (SESSION_NOISE_EXTENSIONS.has(ext)) return rel;
|
|
184
|
-
if (/_state\.json$/i.test(fileName)) return "*_state.json";
|
|
185
|
-
if (/_tokens\.json$/i.test(fileName)) return "*_tokens.json";
|
|
186
|
-
if (/_export\.json$/i.test(fileName)) return "*_export.json";
|
|
187
|
-
if (/secret|credential|token/i.test(fileName) && /\.json$/i.test(fileName)) return rel;
|
|
188
|
-
return null;
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
function buildSessionIgnoreSuggestions(realUsage, root) {
|
|
192
|
-
if (!realUsage || !Array.isArray(realUsage.sessions)) return [];
|
|
193
|
-
const byPattern = new Map();
|
|
194
|
-
const add = (pattern, item, source, reason) => {
|
|
195
|
-
if (!pattern) return;
|
|
196
|
-
const existing = byPattern.get(pattern) || {
|
|
197
|
-
pattern,
|
|
198
|
-
source,
|
|
199
|
-
reason,
|
|
200
|
-
count: 0,
|
|
201
|
-
examples: [],
|
|
202
|
-
};
|
|
203
|
-
existing.count += Number(item?.count || 1);
|
|
204
|
-
const example = item?.value || item?.path || pattern;
|
|
205
|
-
if (example && !existing.examples.includes(example) && existing.examples.length < 3) existing.examples.push(example);
|
|
206
|
-
byPattern.set(pattern, existing);
|
|
207
|
-
};
|
|
208
|
-
|
|
209
|
-
for (const session of realUsage.sessions) {
|
|
210
|
-
for (const item of session.generatedArtifacts || []) {
|
|
211
|
-
add(sessionIgnorePatternForPath(item.value, root), item, session.tool || "session", "Generated artifact entered local session context.");
|
|
212
|
-
}
|
|
213
|
-
for (const item of session.repeatedPathMentions || []) {
|
|
214
|
-
add(sessionIgnorePatternForPath(item.value, root), item, session.tool || "session", "Noisy path appeared repeatedly in local session context.");
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
return Array.from(byPattern.values())
|
|
218
|
-
.sort((a, b) => b.count - a.count || a.pattern.localeCompare(b.pattern))
|
|
219
|
-
.slice(0, 25);
|
|
220
|
-
}
|
|
30
|
+
const {
|
|
31
|
+
buildSessionIgnoreSuggestions,
|
|
32
|
+
isIgnored,
|
|
33
|
+
missingIgnoreSuggestions,
|
|
34
|
+
normalizeRel,
|
|
35
|
+
readIgnoreFile,
|
|
36
|
+
} = require("./scan-path-utils")({ fs, path });
|
|
221
37
|
|
|
222
38
|
function getFileKind(filePath) {
|
|
223
39
|
const ext = path.extname(filePath).toLowerCase();
|