openhands / src /utils /file-priority.ts
SaylorTwift's picture
SaylorTwift HF Staff
Add files using upload-large-folder tool
a070805 verified
Raw
History Blame Contribute Delete
3.09 kB
/**
* Utilities for ranking workspace files by "importance" so that the file-tab
* top-row surfaces the entry points (`index.html`, `README.md`, `package.json`,
* etc.) ahead of nested utility modules.
*/
/**
* File basenames (lowercased) that are almost always the entrypoint for a
* project. Lower index = higher priority.
*/
const HIGH_PRIORITY_BASENAMES: string[] = [
"index.html",
"index.htm",
"readme.md",
"readme",
"main.html",
"app.html",
"index.js",
"index.ts",
"index.tsx",
"index.jsx",
"main.py",
"app.py",
"main.go",
"main.rs",
"main.java",
"main.c",
"main.cpp",
"package.json",
"pyproject.toml",
"cargo.toml",
"go.mod",
"pom.xml",
"dockerfile",
"makefile",
];
/**
* Filenames that are useful but typically of secondary interest compared to
* the entrypoints above.
*/
const SECONDARY_BASENAMES: string[] = [
"license",
"license.md",
"license.txt",
"changelog.md",
"agents.md",
"tsconfig.json",
".env.sample",
".env.example",
];
function getBasename(path: string): string {
const idx = path.lastIndexOf("/");
return (idx === -1 ? path : path.slice(idx + 1)).toLowerCase();
}
function pathDepth(path: string): number {
// Filter empty segments so leading/trailing/double slashes don't inflate
// depth (e.g. `/src/index.html`, `src//index.html`, `src/` should all
// count the same as `src/index.html`). Matches the convention already
// used by `buildFileTree` in `file-tree.ts`.
return path.split("/").filter(Boolean).length - 1;
}
/**
* Rank a path *within its own depth bucket*: high-priority entrypoints
* first (in the order listed), then secondary supporting files, then
* everything else. Depth is the primary sort axis applied by
* {@link sortFilesByPriority}; this score is the tie-breaker for paths at
* the same depth.
*/
export function filePriorityScore(path: string): number {
const base = getBasename(path);
const highIdx = HIGH_PRIORITY_BASENAMES.indexOf(base);
if (highIdx !== -1) return highIdx;
const secondaryIdx = SECONDARY_BASENAMES.indexOf(base);
if (secondaryIdx !== -1) return 1000 + secondaryIdx;
return 10000;
}
/**
* Returns a copy of `paths` sorted so the most likely "landing files" come
* first.
*
* Sort order:
* 1. Shallower paths beat deeper ones, unconditionally. A top-level
* `README.md` outranks `foo/bar/index.html` even though `index.html`
* is a more "important" basename — the user almost always cares more
* about top-level files when they first open a project.
* 2. Within the same depth, basenames are ordered by importance
* (`index.html` before `README.md` before random utility modules).
* 3. Final tie-breaker is alphabetical.
*/
export function sortFilesByPriority(paths: string[]): string[] {
return [...paths].sort((a, b) => {
const depthDiff = pathDepth(a) - pathDepth(b);
if (depthDiff !== 0) return depthDiff;
const scoreDiff = filePriorityScore(a) - filePriorityScore(b);
if (scoreDiff !== 0) return scoreDiff;
return a.localeCompare(b);
});
}