From 4a9c2e709175a0d3129c59074b1db9ed07486cb9 Mon Sep 17 00:00:00 2001 From: jlin53882 Date: Sun, 12 Apr 2026 23:46:18 +0800 Subject: [PATCH 1/2] =?UTF-8?q?fix(import-markdown):=20CI=E6=B8=AC?= =?UTF-8?q?=E8=A9=A6=E7=99=BB=E8=A8=98=20+=20.md=E7=9B=AE=E9=8C=84skip?= =?UTF-8?q?=E4=BF=9D=E8=AD=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - cli.ts: 讀檔前先stat確認isFile(),非檔案skip並log warn - ci-test-manifest.mjs: 加入import-markdown.test.mjs到cli-smoke群組 - import-markdown.test.mjs: 新增測試『skip .md directory』 Ref: PR #482 issuecomment-4229349438 (app3apps) --- cli.ts | 16 ++++++++- scripts/ci-test-manifest.mjs | 1 + test/import-markdown/import-markdown.test.mjs | 33 +++++++++++++++++++ 3 files changed, 49 insertions(+), 1 deletion(-) diff --git a/cli.ts b/cli.ts index c969b755..c59cf212 100644 --- a/cli.ts +++ b/cli.ts @@ -659,7 +659,21 @@ export async function runImportMarkdown( // Parse each file for memory entries (lines starting with "- ") for (const { filePath, scope: discoveredScope } of mdFiles) { foundFiles++; - let content = await fsPromises.readFile(filePath, "utf-8"); + let content: string; + try { + const stats = await fsPromises.stat(filePath); + if (!stats.isFile()) { + // Skip non-file entries (e.g. a directory named "YYYY-MM-DD.md") + console.warn(` [skip] not a file: ${filePath}`); + skipped++; + continue; + } + content = await fsPromises.readFile(filePath, "utf-8"); + } catch (err) { + console.warn(` [skip] unreadable: ${filePath} — ${err}`); + skipped++; + continue; + } // Strip UTF-8 BOM (e.g. from Windows Notepad-saved files) content = content.replace(/^\uFEFF/, ""); // Normalize line endings: handle both CRLF (\r\n) and LF (\n) diff --git a/scripts/ci-test-manifest.mjs b/scripts/ci-test-manifest.mjs index 77bc1d98..d412fe27 100644 --- a/scripts/ci-test-manifest.mjs +++ b/scripts/ci-test-manifest.mjs @@ -20,6 +20,7 @@ export const CI_TEST_MANIFEST = [ { group: "core-regression", runner: "node", file: "test/strip-envelope-metadata.test.mjs", args: ["--test"] }, { group: "cli-smoke", runner: "node", file: "test/cli-smoke.mjs" }, { group: "cli-smoke", runner: "node", file: "test/functional-e2e.mjs" }, + { group: "cli-smoke", runner: "node", file: "test/import-markdown/import-markdown.test.mjs", args: ["--test"] }, { group: "core-regression", runner: "node", file: "test/retriever-rerank-regression.mjs" }, { group: "core-regression", runner: "node", file: "test/smart-memory-lifecycle.mjs" }, { group: "core-regression", runner: "node", file: "test/smart-extractor-branches.mjs" }, diff --git a/test/import-markdown/import-markdown.test.mjs b/test/import-markdown/import-markdown.test.mjs index e2deb247..b2995449 100644 --- a/test/import-markdown/import-markdown.test.mjs +++ b/test/import-markdown/import-markdown.test.mjs @@ -416,6 +416,39 @@ describe("import-markdown CLI", () => { ); }); }); + + describe("skip non-file .md entries", () => { + it("skips a directory named YYYY-MM-DD.md without aborting import", async () => { + const wsDir = await setupWorkspace("nonfile-test"); + // Create memory/ subdirectory first + await mkdir(join(wsDir, "memory"), { recursive: true }); + // Create a real .md file + await writeFile( + join(wsDir, "memory", "2026-04-11.md"), + "- Real file entry\n", + "utf-8", + ); + // Create a directory that looks like a .md file (the bug scenario) + const fakeDir = join(wsDir, "memory", "2026-04-12.md"); + await mkdir(fakeDir, { recursive: true }); + + const ctx = { embedder: mockEmbedder, store: mockStore }; + let threw = false; + try { + const { imported, skipped } = await runImportMarkdown(ctx, { + openclawHome: testWorkspaceDir, + workspaceGlob: "nonfile-test", + }); + // Should have imported the real file (1 entry from "- Real file entry") + assert.strictEqual(imported, 1, "should import the real .md file"); + assert.ok(skipped >= 0); + } catch (err) { + threw = true; + throw new Error(`Import aborted on .md directory: ${err}`); + } + assert.ok(!threw, "import should not abort when encountering .md directory"); + }); + }); }); // ────────────────────────────────────────────────────────────────────────────── Test runner helper ────────────────────────────────────────────────────────────────────────────── From 1a83d72d28f47ff0ce5cbe5cfb77f103d0105000 Mon Sep 17 00:00:00 2001 From: jlin53882 Date: Sun, 12 Apr 2026 23:46:18 +0800 Subject: [PATCH 2/2] =?UTF-8?q?fix(import-markdown):=20CI=E6=B8=AC?= =?UTF-8?q?=E8=A9=A6=E7=99=BB=E8=A8=98=20+=20.md=E7=9B=AE=E9=8C=84skip?= =?UTF-8?q?=E4=BF=9D=E8=AD=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - cli.ts: 讀檔前先stat確認isFile(),非檔案skip並log warn - ci-test-manifest.mjs: 加入import-markdown.test.mjs到cli-smoke群組 - import-markdown.test.mjs: 新增測試『skip .md directory』 Ref: PR #482 issuecomment-4229349438 (app3apps) --- scripts/verify-ci-test-manifest.mjs | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/verify-ci-test-manifest.mjs b/scripts/verify-ci-test-manifest.mjs index 1a7d652a..c15fce4e 100644 --- a/scripts/verify-ci-test-manifest.mjs +++ b/scripts/verify-ci-test-manifest.mjs @@ -21,6 +21,7 @@ const EXPECTED_BASELINE = [ { group: "core-regression", runner: "node", file: "test/strip-envelope-metadata.test.mjs", args: ["--test"] }, { group: "cli-smoke", runner: "node", file: "test/cli-smoke.mjs" }, { group: "cli-smoke", runner: "node", file: "test/functional-e2e.mjs" }, + { group: "cli-smoke", runner: "node", file: "test/import-markdown/import-markdown.test.mjs", args: ["--test"] }, { group: "core-regression", runner: "node", file: "test/retriever-rerank-regression.mjs" }, { group: "core-regression", runner: "node", file: "test/smart-memory-lifecycle.mjs" }, { group: "core-regression", runner: "node", file: "test/smart-extractor-branches.mjs" },