vibe-roast 0.98.0
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 +418 -0
- package/assests/README.md +43 -0
- package/assests/badges/.gitkeep +0 -0
- package/assests/badges/01-builder/builder-badge.png +0 -0
- package/assests/badges/01-builder/builder-badge.svg +4 -0
- package/assests/badges/02-debugger/debugger-badge.png +0 -0
- package/assests/badges/02-debugger/debugger-badge.svg +4 -0
- package/assests/badges/03-architect/architect-badge.png +0 -0
- package/assests/badges/03-architect/architect-badge.svg +4 -0
- package/assests/badges/04-codex-believer/codex-believer-badge.png +0 -0
- package/assests/badges/04-codex-believer/codex-believer-badge.svg +4 -0
- package/assests/badges/05-prompt-priest/prompt-priest-badge.png +0 -0
- package/assests/badges/05-prompt-priest/prompt-priest-badge.svg +4 -0
- package/assests/badges/06-tab-hoarder/tab-hoarder-badge.png +0 -0
- package/assests/badges/06-tab-hoarder/tab-hoarder-badge.svg +4 -0
- package/assests/badges/07-context-maxxer/context-maxxer-badge.png +0 -0
- package/assests/badges/07-context-maxxer/context-maxxer-badge.svg +4 -0
- package/assests/badges/08-yolo-shipper/yolo-shipper-badge.png +0 -0
- package/assests/badges/08-yolo-shipper/yolo-shipper-badge.svg +4 -0
- package/assests/banners/.gitkeep +0 -0
- package/assests/banners/home-hero.png +0 -0
- package/assests/banners/home-hero.svg +3 -0
- package/assests/banners/result-header.png +0 -0
- package/assests/banners/result-header.svg +3 -0
- package/assests/banners/share-strip.png +0 -0
- package/assests/banners/share-strip.svg +3 -0
- package/assests/characters/01-builder/builder-card.sbti-archive.svg +5 -0
- package/assests/characters/01-builder/builder-card.svg +3 -0
- package/assests/characters/01-builder/builder-character.sbti-archive.svg +3 -0
- package/assests/characters/01-builder/builder-character.svg +3 -0
- package/assests/characters/01-builder/builder-figure.png +0 -0
- package/assests/characters/01-builder/builder-mascot.png +0 -0
- package/assests/characters/02-debugger/debugger-card.sbti-archive.svg +5 -0
- package/assests/characters/02-debugger/debugger-card.svg +3 -0
- package/assests/characters/02-debugger/debugger-character.sbti-archive.svg +3 -0
- package/assests/characters/02-debugger/debugger-character.svg +3 -0
- package/assests/characters/02-debugger/debugger-figure.png +0 -0
- package/assests/characters/02-debugger/debugger-mascot.png +0 -0
- package/assests/characters/03-architect/architect-card.sbti-archive.svg +5 -0
- package/assests/characters/03-architect/architect-card.svg +3 -0
- package/assests/characters/03-architect/architect-character.sbti-archive.svg +3 -0
- package/assests/characters/03-architect/architect-character.svg +3 -0
- package/assests/characters/03-architect/architect-figure.png +0 -0
- package/assests/characters/03-architect/architect-mascot.png +0 -0
- package/assests/characters/04-codex-believer/codex-believer-card.sbti-archive.svg +5 -0
- package/assests/characters/04-codex-believer/codex-believer-card.svg +3 -0
- package/assests/characters/04-codex-believer/codex-believer-character.sbti-archive.svg +3 -0
- package/assests/characters/04-codex-believer/codex-believer-character.svg +3 -0
- package/assests/characters/04-codex-believer/codex-believer-figure.png +0 -0
- package/assests/characters/04-codex-believer/codex-believer-mascot.png +0 -0
- package/assests/characters/05-prompt-priest/prompt-priest-card.sbti-archive.svg +5 -0
- package/assests/characters/05-prompt-priest/prompt-priest-card.svg +3 -0
- package/assests/characters/05-prompt-priest/prompt-priest-character.sbti-archive.svg +3 -0
- package/assests/characters/05-prompt-priest/prompt-priest-character.svg +3 -0
- package/assests/characters/05-prompt-priest/prompt-priest-figure.png +0 -0
- package/assests/characters/05-prompt-priest/prompt-priest-mascot.png +0 -0
- package/assests/characters/06-tab-hoarder/tab-hoarder-card.sbti-archive.svg +5 -0
- package/assests/characters/06-tab-hoarder/tab-hoarder-card.svg +3 -0
- package/assests/characters/06-tab-hoarder/tab-hoarder-character.sbti-archive.svg +3 -0
- package/assests/characters/06-tab-hoarder/tab-hoarder-character.svg +3 -0
- package/assests/characters/06-tab-hoarder/tab-hoarder-figure.png +0 -0
- package/assests/characters/06-tab-hoarder/tab-hoarder-mascot.png +0 -0
- package/assests/characters/07-context-maxxer/context-maxxer-card.sbti-archive.svg +5 -0
- package/assests/characters/07-context-maxxer/context-maxxer-card.svg +3 -0
- package/assests/characters/07-context-maxxer/context-maxxer-character.sbti-archive.svg +3 -0
- package/assests/characters/07-context-maxxer/context-maxxer-character.svg +3 -0
- package/assests/characters/07-context-maxxer/context-maxxer-figure.png +0 -0
- package/assests/characters/07-context-maxxer/context-maxxer-mascot.png +0 -0
- package/assests/characters/08-yolo-shipper/yolo-shipper-card.sbti-archive.svg +5 -0
- package/assests/characters/08-yolo-shipper/yolo-shipper-card.svg +3 -0
- package/assests/characters/08-yolo-shipper/yolo-shipper-character.sbti-archive.svg +3 -0
- package/assests/characters/08-yolo-shipper/yolo-shipper-character.svg +3 -0
- package/assests/characters/08-yolo-shipper/yolo-shipper-figure.png +0 -0
- package/assests/characters/08-yolo-shipper/yolo-shipper-mascot.png +0 -0
- package/assests/characters-vibe-types/aosf-strategy-shipper/strategy-shipper-figure.png +0 -0
- package/assests/characters-vibe-types/aosx-agent-believer/agent-believer-figure.png +0 -0
- package/assests/characters-vibe-types/aovf-systems-architect/systems-architect-figure.png +0 -0
- package/assests/characters-vibe-types/aovx-context-cartographer/context-cartographer-figure.png +0 -0
- package/assests/characters-vibe-types/apsf-diagram-sprinter/diagram-sprinter-figure.png +0 -0
- package/assests/characters-vibe-types/apsx-infinite-planner/infinite-planner-figure.png +0 -0
- package/assests/characters-vibe-types/apvf-architect/architect-figure.png +0 -0
- package/assests/characters-vibe-types/apvx-prompt-priest/prompt-priest-figure.png +0 -0
- package/assests/characters-vibe-types/mosf-yolo-shipper/yolo-shipper-figure.png +0 -0
- package/assests/characters-vibe-types/mosx-agent-commander/agent-commander-figure.png +0 -0
- package/assests/characters-vibe-types/movf-agent-engineer/agent-engineer-figure.png +0 -0
- package/assests/characters-vibe-types/movx-systems-wrangler/systems-wrangler-figure.png +0 -0
- package/assests/characters-vibe-types/mpsf-builder/builder-figure.png +0 -0
- package/assests/characters-vibe-types/mpsx-tab-hoarder/tab-hoarder-figure.png +0 -0
- package/assests/characters-vibe-types/mpvf-debugger/debugger-figure.png +0 -0
- package/assests/characters-vibe-types/mpvx-context-maxxer/context-maxxer-figure.png +0 -0
- package/assests/live-report.html +1403 -0
- package/assests/notes/live-inspect-summary.json +3487 -0
- package/assests/scripts/compose-banners.py +148 -0
- package/assests/scripts/live-report-boot.js +2 -0
- package/assests/scripts/profiles.json +104 -0
- package/assests/scripts/qa-faces.mjs +72 -0
- package/assests/scripts/roast-extras.js +619 -0
- package/assests/scripts/score-engine.js +62 -0
- package/assests/scripts/split-badges.py +49 -0
- package/assests/scripts/split-reference.py +38 -0
- package/assests/scripts/sync-banners.mjs +45 -0
- package/assests/scripts/sync-cards.mjs +50 -0
- package/assests/scripts/sync-raster.mjs +118 -0
- package/assests/scripts/vibe-types.json +22 -0
- package/assests/source/design-system.md +50 -0
- package/assests/source/prompts/01-builder.md +19 -0
- package/assests/source/prompts/02-debugger.md +17 -0
- package/assests/source/prompts/03-architect.md +16 -0
- package/assests/source/prompts/04-codex-believer.md +13 -0
- package/assests/source/prompts/05-prompt-priest.md +13 -0
- package/assests/source/prompts/06-tab-hoarder.md +13 -0
- package/assests/source/prompts/07-context-maxxer.md +13 -0
- package/assests/source/prompts/08-yolo-shipper.md +13 -0
- package/assests/source/references/sbti-style-reference-board.png +0 -0
- package/assests/source/references/vibe-badges-v6-board.png +0 -0
- package/assests/source/references/vibe-mascots-v4-reference.png +0 -0
- package/assests/source/references/vibe-profiles-v5-board-a.png +0 -0
- package/assests/source/references/vibe-profiles-v5-board-b.png +0 -0
- package/assests/source/vibe-types-visual-brief.md +38 -0
- package/bin/hook.js +121 -0
- package/bin/vibe-roast.js +98 -0
- package/bin/vibe-wrapper.js +4 -0
- package/dashboard/dist/assets/index-DlBycTcO.css +1 -0
- package/dashboard/dist/assets/index-t8m64Hm9.js +168 -0
- package/dashboard/dist/index.html +32 -0
- package/package.json +58 -0
- package/src/extract/environment.js +160 -0
- package/src/extract/phrase-stats.js +684 -0
- package/src/extract/prompt-analysis.js +368 -0
- package/src/extract/text.js +30 -0
- package/src/hooks/install.js +184 -0
- package/src/inspect.js +261 -0
- package/src/lib/activity-metrics.js +238 -0
- package/src/lib/agent-score.js +187 -0
- package/src/lib/ai-roast.js +446 -0
- package/src/lib/dates.js +43 -0
- package/src/lib/hashtags.js +150 -0
- package/src/lib/jsonl.js +48 -0
- package/src/lib/load-env.js +59 -0
- package/src/lib/roast-evidence.js +186 -0
- package/src/lib/roast-snapshot.js +58 -0
- package/src/server.js +644 -0
- package/src/sources/aider.js +150 -0
- package/src/sources/amazonq.js +113 -0
- package/src/sources/antigravity.js +147 -0
- package/src/sources/claude.js +262 -0
- package/src/sources/cline.js +11 -0
- package/src/sources/codex.js +222 -0
- package/src/sources/common.js +133 -0
- package/src/sources/continue.js +67 -0
- package/src/sources/copilot.js +170 -0
- package/src/sources/cursor.js +238 -0
- package/src/sources/gemini.js +75 -0
- package/src/sources/index.js +61 -0
- package/src/sources/roo.js +11 -0
- package/src/sources/token-tracker.js +171 -0
- package/src/sources/vibe-tracker.js +79 -0
- package/src/sources/vscode-tasks.js +142 -0
- package/src/sources/windsurf.js +109 -0
- package/worker/README.md +96 -0
- package/worker/package-lock.json +1547 -0
- package/worker/package.json +20 -0
- package/worker/src/index.mjs +694 -0
- package/worker/wrangler.jsonc +40 -0
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
+
<title>Vibe Roaster</title>
|
|
7
|
+
<meta name="description" content="Local-first vibe coding personality analyzer" />
|
|
8
|
+
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
|
9
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
|
10
|
+
<link href="https://fonts.googleapis.com/css2?family=Great+Vibes&display=swap" rel="stylesheet" />
|
|
11
|
+
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'><text y='24' font-size='24'>✦</text></svg>" />
|
|
12
|
+
<meta name="theme-color" content="#f3f1ec" media="(prefers-color-scheme: light)" />
|
|
13
|
+
<meta name="theme-color" content="#0a0a0a" media="(prefers-color-scheme: dark)" />
|
|
14
|
+
<script>
|
|
15
|
+
(function(){
|
|
16
|
+
var t;
|
|
17
|
+
try { t = localStorage.getItem("vibe-theme"); } catch(e) {}
|
|
18
|
+
var dark = t === "dark" || (t !== "light" && matchMedia("(prefers-color-scheme:dark)").matches);
|
|
19
|
+
if (dark) document.documentElement.classList.add("dark");
|
|
20
|
+
})();
|
|
21
|
+
</script>
|
|
22
|
+
<style>
|
|
23
|
+
html { background: #f3f1ec; }
|
|
24
|
+
html.dark { background: #0a0a0a; }
|
|
25
|
+
</style>
|
|
26
|
+
<script type="module" crossorigin src="/assets/index-t8m64Hm9.js"></script>
|
|
27
|
+
<link rel="stylesheet" crossorigin href="/assets/index-DlBycTcO.css">
|
|
28
|
+
</head>
|
|
29
|
+
<body>
|
|
30
|
+
<div id="root"></div>
|
|
31
|
+
</body>
|
|
32
|
+
</html>
|
package/package.json
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "vibe-roast",
|
|
3
|
+
"version": "0.98.0",
|
|
4
|
+
"description": "Local-first AI coding personality profiler, usage dashboard, and evidence-grounded roast.",
|
|
5
|
+
"type": "commonjs",
|
|
6
|
+
"bin": {
|
|
7
|
+
"vibe-roast": "bin/vibe-roast.js",
|
|
8
|
+
"vibe-wrapper": "bin/vibe-wrapper.js"
|
|
9
|
+
},
|
|
10
|
+
"repository": {
|
|
11
|
+
"type": "git",
|
|
12
|
+
"url": "git+https://github.com/PinkR1ver/vibe-roast.git"
|
|
13
|
+
},
|
|
14
|
+
"homepage": "https://github.com/PinkR1ver/vibe-roast#readme",
|
|
15
|
+
"bugs": {
|
|
16
|
+
"url": "https://github.com/PinkR1ver/vibe-roast/issues"
|
|
17
|
+
},
|
|
18
|
+
"keywords": [
|
|
19
|
+
"ai",
|
|
20
|
+
"coding-agent",
|
|
21
|
+
"codex",
|
|
22
|
+
"claude-code",
|
|
23
|
+
"cursor",
|
|
24
|
+
"developer-tools",
|
|
25
|
+
"roast"
|
|
26
|
+
],
|
|
27
|
+
"files": [
|
|
28
|
+
"bin/",
|
|
29
|
+
"src/",
|
|
30
|
+
"worker/README.md",
|
|
31
|
+
"worker/package.json",
|
|
32
|
+
"worker/package-lock.json",
|
|
33
|
+
"worker/wrangler.jsonc",
|
|
34
|
+
"worker/src/",
|
|
35
|
+
"dashboard/dist/",
|
|
36
|
+
"assests/",
|
|
37
|
+
"README.md"
|
|
38
|
+
],
|
|
39
|
+
"scripts": {
|
|
40
|
+
"test": "node --test test/*.test.js",
|
|
41
|
+
"inspect": "node bin/vibe-roast.js inspect",
|
|
42
|
+
"serve": "node bin/vibe-roast.js serve",
|
|
43
|
+
"worker:login": "npm --prefix worker run login",
|
|
44
|
+
"worker:dev": "npm --prefix worker run dev",
|
|
45
|
+
"worker:deploy": "npm --prefix worker run deploy",
|
|
46
|
+
"worker:secret:github": "npm --prefix worker run secret:github",
|
|
47
|
+
"worker:secret:session": "npm --prefix worker run secret:session",
|
|
48
|
+
"build": "cd dashboard && npm install && npm run build",
|
|
49
|
+
"dev": "cd dashboard && npm run dev",
|
|
50
|
+
"prepack": "npm run build"
|
|
51
|
+
},
|
|
52
|
+
"engines": {
|
|
53
|
+
"node": ">=20"
|
|
54
|
+
},
|
|
55
|
+
"publishConfig": {
|
|
56
|
+
"access": "public"
|
|
57
|
+
}
|
|
58
|
+
}
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
const fs = require("node:fs/promises");
|
|
2
|
+
const path = require("node:path");
|
|
3
|
+
const os = require("node:os");
|
|
4
|
+
|
|
5
|
+
async function inspectEnvironment({ home = os.homedir(), codexHome } = {}) {
|
|
6
|
+
const resolvedCodexHome = codexHome || process.env.CODEX_HOME || path.join(home, ".codex");
|
|
7
|
+
const [skills, config, customInstructions] = await Promise.all([
|
|
8
|
+
inspectSkills(resolvedCodexHome),
|
|
9
|
+
inspectCodexConfig(path.join(resolvedCodexHome, "config.toml")),
|
|
10
|
+
inspectCustomInstructions(path.join(resolvedCodexHome, "AGENTS.md")),
|
|
11
|
+
]);
|
|
12
|
+
|
|
13
|
+
return {
|
|
14
|
+
codex: {
|
|
15
|
+
home: resolvedCodexHome,
|
|
16
|
+
skills,
|
|
17
|
+
mcp_servers: config.mcp_servers,
|
|
18
|
+
plugins: config.plugins,
|
|
19
|
+
config: config.config,
|
|
20
|
+
custom_instructions: customInstructions,
|
|
21
|
+
system_prompt: customInstructions,
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
async function inspectSkills(codexHome) {
|
|
27
|
+
const userFiles = await walkFiles(path.join(codexHome, "skills"), (filePath) =>
|
|
28
|
+
filePath.endsWith("SKILL.md"),
|
|
29
|
+
);
|
|
30
|
+
const pluginFiles = await walkFiles(path.join(codexHome, "plugins", "cache"), (filePath) =>
|
|
31
|
+
/[/\\]skills[/\\][^/\\]+[/\\]SKILL\.md$/.test(filePath),
|
|
32
|
+
);
|
|
33
|
+
const userNames = await skillNames(userFiles);
|
|
34
|
+
const pluginNames = await skillNames(pluginFiles);
|
|
35
|
+
const names = Array.from(new Set([...userNames, ...pluginNames])).sort((a, b) =>
|
|
36
|
+
a.localeCompare(b),
|
|
37
|
+
);
|
|
38
|
+
return {
|
|
39
|
+
count: names.length,
|
|
40
|
+
user_count: userNames.length,
|
|
41
|
+
plugin_count: pluginNames.length,
|
|
42
|
+
names,
|
|
43
|
+
user_names: userNames,
|
|
44
|
+
plugin_names: pluginNames,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async function skillNames(files) {
|
|
49
|
+
const names = [];
|
|
50
|
+
for (const file of files) {
|
|
51
|
+
const name = (await readSkillName(file)) || path.basename(path.dirname(file));
|
|
52
|
+
names.push(name);
|
|
53
|
+
}
|
|
54
|
+
names.sort((a, b) => a.localeCompare(b));
|
|
55
|
+
return names;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
async function inspectCodexConfig(configPath) {
|
|
59
|
+
const text = await fs.readFile(configPath, "utf8").catch(() => "");
|
|
60
|
+
return {
|
|
61
|
+
mcp_servers: {
|
|
62
|
+
count: sectionNames(text, "mcp_servers").length,
|
|
63
|
+
names: sectionNames(text, "mcp_servers"),
|
|
64
|
+
},
|
|
65
|
+
plugins: inspectPlugins(text),
|
|
66
|
+
config: {
|
|
67
|
+
present: Boolean(text),
|
|
68
|
+
model: scalarValue(text, "model"),
|
|
69
|
+
personality: scalarValue(text, "personality"),
|
|
70
|
+
sandbox_mode: scalarValue(text, "sandbox_mode"),
|
|
71
|
+
approval_policy: scalarValue(text, "approval_policy"),
|
|
72
|
+
},
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function inspectPlugins(text) {
|
|
77
|
+
const names = sectionNames(text, "plugins");
|
|
78
|
+
let enabledCount = 0;
|
|
79
|
+
for (const name of names) {
|
|
80
|
+
const block = sectionBlock(text, `plugins.${JSON.stringify(name)}`) || sectionBlock(text, `plugins."${name}"`);
|
|
81
|
+
if (/^\s*enabled\s*=\s*true\s*$/m.test(block)) enabledCount += 1;
|
|
82
|
+
}
|
|
83
|
+
return { count: names.length, enabled_count: enabledCount, names };
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
async function inspectCustomInstructions(filePath) {
|
|
87
|
+
const text = await fs.readFile(filePath, "utf8").catch(() => "");
|
|
88
|
+
return {
|
|
89
|
+
present: Boolean(text.trim()),
|
|
90
|
+
path: filePath,
|
|
91
|
+
char_count: text.length,
|
|
92
|
+
line_count: text ? text.split(/\r?\n/).length : 0,
|
|
93
|
+
preview: text.trim().slice(0, 500),
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function sectionNames(text, prefix) {
|
|
98
|
+
const re = new RegExp(`^\\s*\\[${escapeRegExp(prefix)}\\.(?:"([^"]+)"|([^\\]]+))\\]\\s*$`, "gm");
|
|
99
|
+
const names = [];
|
|
100
|
+
let match;
|
|
101
|
+
while ((match = re.exec(text))) {
|
|
102
|
+
const raw = (match[1] || match[2] || "").trim();
|
|
103
|
+
const name = raw.split(".")[0].replace(/^"|"$/g, "");
|
|
104
|
+
if (name && !names.includes(name)) names.push(name);
|
|
105
|
+
}
|
|
106
|
+
names.sort((a, b) => a.localeCompare(b));
|
|
107
|
+
return names;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function sectionBlock(text, sectionName) {
|
|
111
|
+
const lines = text.split(/\r?\n/);
|
|
112
|
+
const header = `[${sectionName}]`;
|
|
113
|
+
const start = lines.findIndex((line) => line.trim() === header);
|
|
114
|
+
if (start === -1) return "";
|
|
115
|
+
const out = [];
|
|
116
|
+
for (let i = start + 1; i < lines.length; i++) {
|
|
117
|
+
if (/^\s*\[/.test(lines[i])) break;
|
|
118
|
+
out.push(lines[i]);
|
|
119
|
+
}
|
|
120
|
+
return out.join("\n");
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function scalarValue(text, key) {
|
|
124
|
+
const re = new RegExp(`^\\s*${escapeRegExp(key)}\\s*=\\s*"(.*)"\\s*$`, "m");
|
|
125
|
+
const match = text.match(re);
|
|
126
|
+
return match ? match[1] : null;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
async function readSkillName(filePath) {
|
|
130
|
+
const text = await fs.readFile(filePath, "utf8").catch(() => "");
|
|
131
|
+
const match = text.match(/^name:\s*(.+)\s*$/m);
|
|
132
|
+
return match ? match[1].trim().replace(/^["']|["']$/g, "") : null;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
async function walkFiles(rootDir, predicate) {
|
|
136
|
+
const out = [];
|
|
137
|
+
const stack = [rootDir];
|
|
138
|
+
while (stack.length > 0) {
|
|
139
|
+
const dir = stack.pop();
|
|
140
|
+
let entries;
|
|
141
|
+
try {
|
|
142
|
+
entries = await fs.readdir(dir, { withFileTypes: true });
|
|
143
|
+
} catch {
|
|
144
|
+
continue;
|
|
145
|
+
}
|
|
146
|
+
for (const entry of entries) {
|
|
147
|
+
const filePath = path.join(dir, entry.name);
|
|
148
|
+
if (entry.isDirectory()) stack.push(filePath);
|
|
149
|
+
else if (entry.isFile() && predicate(filePath)) out.push(filePath);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
out.sort((a, b) => a.localeCompare(b));
|
|
153
|
+
return out;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function escapeRegExp(value) {
|
|
157
|
+
return String(value).replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
module.exports = { inspectEnvironment };
|