hataraku
Version:
An autonomous coding agent for building AI-powered development tools. The name "Hataraku" (働く) means "to work" in Japanese.
157 lines • 6.6 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.extractTextFromFile = extractTextFromFile;
exports.addLineNumbers = addLineNumbers;
exports.everyLineHasLineNumbers = everyLineHasLineNumbers;
exports.stripLineNumbers = stripLineNumbers;
exports.truncateOutput = truncateOutput;
const path = __importStar(require("path"));
// @ts-ignore-next-line
const pdf_parse_1 = __importDefault(require("pdf-parse/lib/pdf-parse"));
const mammoth_1 = __importDefault(require("mammoth"));
const promises_1 = __importDefault(require("fs/promises"));
const isbinaryfile_1 = require("isbinaryfile");
async function extractTextFromFile(filePath) {
try {
await promises_1.default.access(filePath);
}
catch (error) {
throw new Error(`File not found: ${filePath}`);
}
const fileExtension = path.extname(filePath).toLowerCase();
switch (fileExtension) {
case ".pdf":
return extractTextFromPDF(filePath);
case ".docx":
return extractTextFromDOCX(filePath);
case ".ipynb":
return extractTextFromIPYNB(filePath);
default:
const isBinary = await (0, isbinaryfile_1.isBinaryFile)(filePath).catch(() => false);
if (!isBinary) {
return addLineNumbers(await promises_1.default.readFile(filePath, "utf8"));
}
else {
throw new Error(`Cannot read text for file type: ${fileExtension}`);
}
}
}
async function extractTextFromPDF(filePath) {
const dataBuffer = await promises_1.default.readFile(filePath);
const data = await (0, pdf_parse_1.default)(dataBuffer);
return addLineNumbers(data.text);
}
async function extractTextFromDOCX(filePath) {
const result = await mammoth_1.default.extractRawText({ path: filePath });
return addLineNumbers(result.value);
}
async function extractTextFromIPYNB(filePath) {
const data = await promises_1.default.readFile(filePath, "utf8");
const notebook = JSON.parse(data);
let extractedText = "";
for (const cell of notebook.cells) {
if ((cell.cell_type === "markdown" || cell.cell_type === "code") && cell.source) {
extractedText += cell.source.join("\n") + "\n";
}
}
return addLineNumbers(extractedText);
}
function addLineNumbers(content, startLine = 1) {
const lines = content.split('\n');
const maxLineNumberWidth = String(startLine + lines.length - 1).length;
return lines
.map((line, index) => {
const lineNumber = String(startLine + index).padStart(maxLineNumberWidth, ' ');
return `${lineNumber} | ${line}`;
}).join('\n');
}
// Checks if every line in the content has line numbers prefixed (e.g., "1 | content" or "123 | content")
// Line numbers must be followed by a single pipe character (not double pipes)
function everyLineHasLineNumbers(content) {
const lines = content.split(/\r?\n/);
return lines.length > 0 && lines.every(line => /^\s*\d+\s+\|(?!\|)/.test(line));
}
// Strips line numbers from content while preserving the actual content
// Handles formats like "1 | content", " 12 | content", "123 | content"
// Preserves content that naturally starts with pipe characters
function stripLineNumbers(content) {
// Split into lines to handle each line individually
const lines = content.split(/\r?\n/);
// Process each line
const processedLines = lines.map(line => {
// Match line number pattern and capture everything after the pipe
const match = line.match(/^\s*\d+\s+\|(?!\|)\s?(.*)$/);
return match ? match[1] : line;
});
// Join back with original line endings
const lineEnding = content.includes('\r\n') ? '\r\n' : '\n';
return processedLines.join(lineEnding);
}
/**
* Truncates multi-line output while preserving context from both the beginning and end.
* When truncation is needed, it keeps 20% of the lines from the start and 80% from the end,
* with a clear indicator of how many lines were omitted in between.
*
* @param content The multi-line string to truncate
* @param lineLimit Optional maximum number of lines to keep. If not provided or 0, returns the original content
* @returns The truncated string with an indicator of omitted lines, or the original content if no truncation needed
*
* @example
* // With 10 line limit on 25 lines of content:
* // - Keeps first 2 lines (20% of 10)
* // - Keeps last 8 lines (80% of 10)
* // - Adds "[...15 lines omitted...]" in between
*/
function truncateOutput(content, lineLimit) {
if (!lineLimit) {
return content;
}
const lines = content.split('\n');
if (lines.length <= lineLimit) {
return content;
}
const beforeLimit = Math.floor(lineLimit * 0.2); // 20% of lines before
const afterLimit = lineLimit - beforeLimit; // remaining 80% after
return [
...lines.slice(0, beforeLimit),
`\n[...${lines.length - lineLimit} lines omitted...]\n`,
...lines.slice(-afterLimit)
].join('\n');
}
//# sourceMappingURL=extract-text.js.map