diff --git a/packages/commands/src/commands/knowledge/chunk-add.ts b/packages/commands/src/commands/knowledge/chunk-add.ts index 512981a..0efb374 100644 --- a/packages/commands/src/commands/knowledge/chunk-add.ts +++ b/packages/commands/src/commands/knowledge/chunk-add.ts @@ -121,7 +121,9 @@ export default defineCommand({ field = parseFieldEntries(flags.field); } else { const content = - flags.contentFile !== undefined ? readUtf8TextFile(flags.contentFile) : flags.content; + flags.contentFile !== undefined + ? readUtf8TextFile(flags.contentFile, "--content") + : flags.content; if (typeof content === "string" && content.length > 6000) { throw new BailianError("Chunk content must be at most 6000 characters", ExitCode.USAGE); } diff --git a/packages/commands/src/commands/knowledge/chunk-update.ts b/packages/commands/src/commands/knowledge/chunk-update.ts index 772b020..c14e87c 100644 --- a/packages/commands/src/commands/knowledge/chunk-update.ts +++ b/packages/commands/src/commands/knowledge/chunk-update.ts @@ -122,7 +122,9 @@ export default defineCommand({ // dry-run also reads the file and validates (rehearsal semantics); the read-back // request is only made outside dry-run and when no new content is given let content = - flags.contentFile !== undefined ? readUtf8TextFile(flags.contentFile) : flags.content; + flags.contentFile !== undefined + ? readUtf8TextFile(flags.contentFile, "--content") + : flags.content; if (content !== undefined && (content.length < 10 || content.length > 6000)) { throw new BailianError("Chunk content must be 10-6000 characters", ExitCode.USAGE); } diff --git a/packages/commands/src/commands/knowledge/upload-support.ts b/packages/commands/src/commands/knowledge/upload-support.ts index 54df030..97a519c 100644 --- a/packages/commands/src/commands/knowledge/upload-support.ts +++ b/packages/commands/src/commands/knowledge/upload-support.ts @@ -192,22 +192,33 @@ const DOCUMENT_EXTENSIONS = new Set([ ]); /** - * Read a UTF-8 plain-text file (--content-file for chunk add/update). + * Read a UTF-8 plain-text file. * Support is defined by content, not extension: any UTF-8 text is valid. * Strict decode failure → USAGE, with a hint pointing to the document upload * flow when the extension is a document format; file I/O failure → GENERAL + errno. + * + * @param inlineAlternativeFlag When provided, an ENOENT on a value that looks + * like inline text (no path separator / no extension) appends a hint pointing + * the user to this flag instead. Pass the inline-text flag name (e.g. + * `"--content"`) only from commands that have a file-vs-inline choice. */ -export function readUtf8TextFile(filePath: string): string { +export function readUtf8TextFile(filePath: string, inlineAlternativeFlag?: string): string { let fileBuffer: Buffer; try { fileBuffer = readFileSync(filePath); } catch (error) { const errno = (error as { code?: string }).code ?? "unknown"; - throw new BailianError( - `Cannot read file: ${filePath}`, - ExitCode.GENERAL, - `File system error (${errno}) — check the path and permissions.`, - ); + let hint = `File system error (${errno}) — check the path and permissions.`; + if ( + inlineAlternativeFlag && + errno === "ENOENT" && + !extname(filePath) && + !filePath.includes("/") && + !filePath.includes("\\") + ) { + hint += ` If you meant to pass text content directly, use ${inlineAlternativeFlag} instead of --content-file.`; + } + throw new BailianError(`Cannot read file: ${filePath}`, ExitCode.GENERAL, hint); } try { return new TextDecoder("utf-8", { fatal: true }).decode(fileBuffer);