85c7f97239
Signed-off-by: The-Best-Codes <106822363+The-Best-Codes@users.noreply.github.com>
120 lines
3.2 KiB
TypeScript
120 lines
3.2 KiB
TypeScript
import fs from "fs";
|
|
import path from "path";
|
|
|
|
function getDocsDir(): string {
|
|
return process.env.DOCS_PATH || path.join(process.cwd(), "docs");
|
|
}
|
|
|
|
function generateWebUrl(filePath: string): string {
|
|
const baseUrl = "https://block.github.io/goose/docs";
|
|
// Remove file extension for the URL path
|
|
const urlPath = filePath.replace(/\.[^/.]+$/, "");
|
|
return `${baseUrl}/${urlPath}`;
|
|
}
|
|
|
|
function findDocFile(partialPath: string): string | null {
|
|
const docsDir = getDocsDir();
|
|
|
|
if (!fs.existsSync(docsDir)) {
|
|
return null;
|
|
}
|
|
|
|
const searchTerm = partialPath.toLowerCase().replace(/\.md$/, "");
|
|
let foundPath: string | null = null;
|
|
|
|
function walkDir(dir: string) {
|
|
if (foundPath) return;
|
|
|
|
const files = fs.readdirSync(dir);
|
|
|
|
for (const file of files) {
|
|
const filePath = path.join(dir, file);
|
|
const stat = fs.statSync(filePath);
|
|
|
|
if (stat.isDirectory()) {
|
|
if (file === "assets" || file === "docker") {
|
|
continue;
|
|
}
|
|
walkDir(filePath);
|
|
} else {
|
|
const relativePath = path.relative(docsDir, filePath);
|
|
if (relativePath.toLowerCase().includes(searchTerm)) {
|
|
foundPath = relativePath;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
walkDir(docsDir);
|
|
return foundPath;
|
|
}
|
|
|
|
function getDocChunk(
|
|
filePath: string,
|
|
startLine: number = 0,
|
|
lineCount: number = 1500,
|
|
): { fileName: string; content: string; webUrl: string } {
|
|
const docsDir = path.resolve(getDocsDir());
|
|
const fullPath = path.join(docsDir, filePath);
|
|
|
|
const normalizedPath = path.resolve(fullPath);
|
|
if (!normalizedPath.startsWith(docsDir + "/")) {
|
|
throw new Error("Invalid file path - directory traversal not allowed");
|
|
}
|
|
|
|
if (!fs.existsSync(fullPath)) {
|
|
throw new Error(`Documentation file not found: ${filePath}`);
|
|
}
|
|
|
|
try {
|
|
const content = fs.readFileSync(fullPath, "utf-8");
|
|
const lines = content.split("\n");
|
|
|
|
const actualStartLine = Math.max(0, Math.min(startLine, lines.length - 1));
|
|
const actualEndLine = Math.min(actualStartLine + lineCount, lines.length);
|
|
const chunkLines = lines.slice(actualStartLine, actualEndLine);
|
|
const chunkContent = chunkLines.join("\n");
|
|
|
|
const fileName = path.basename(fullPath);
|
|
|
|
return {
|
|
content: chunkContent,
|
|
fileName,
|
|
webUrl: generateWebUrl(filePath),
|
|
};
|
|
} catch (error) {
|
|
if (error instanceof Error && error.message.includes("ENOENT")) {
|
|
throw new Error(`Documentation file not found: ${filePath}`);
|
|
}
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
export function viewDocs(
|
|
filePaths: string | string[],
|
|
startLine: number = 0,
|
|
lineCount: number = 1500,
|
|
): string {
|
|
const paths = Array.isArray(filePaths) ? filePaths : [filePaths];
|
|
|
|
const docs = paths.map((filePath) => {
|
|
let resolvedPath = filePath;
|
|
// Check if file has an extension; if not, search for it
|
|
if (!path.extname(filePath)) {
|
|
const found = findDocFile(filePath);
|
|
if (found) {
|
|
resolvedPath = found;
|
|
}
|
|
}
|
|
return getDocChunk(resolvedPath, startLine, lineCount);
|
|
});
|
|
|
|
return docs
|
|
.map(
|
|
(doc) =>
|
|
`**${doc.fileName}**\nWeb URL: <${doc.webUrl}>\n\`\`\`\n${doc.content}\n\`\`\``,
|
|
)
|
|
.join("\n\n---\n\n");
|
|
}
|