Skip to content

fix(memory): fall back to qmd get for slugified paths#44390

Closed
velisiska wants to merge 2 commits intoopenclaw:mainfrom
velisiska:fix/qmd-memory-get-slugified-paths
Closed

fix(memory): fall back to qmd get for slugified paths#44390
velisiska wants to merge 2 commits intoopenclaw:mainfrom
velisiska:fix/qmd-memory-get-slugified-paths

Conversation

@velisiska
Copy link
Copy Markdown

Summary

  • When qmd indexes files it slugifies filenames (e.g. My Recipe Notes.mdmy-recipe-notes.md), so filesystem reads via readFile() fail with ENOENT
  • Added getQmdDoc() fallback that retrieves document content via qmd get using qmd:// URIs when the filesystem path doesn't exist
  • This allows memory_get tool calls to return full document content for qmd search results

AI-assisted

  • Mark as AI-assisted in the PR title or description
  • Lightly tested — verified in Docker container with qmd
  • Author understands what the code does
  • pnpm build && pnpm check && pnpm test all passing (906 test files, 7495 tests)

Test plan

  • Verified qmd get resolves slugified paths in running container
  • Confirmed readFile() fallback returns full document content
  • pnpm build passes
  • pnpm check passes
  • pnpm test passes (7495 tests, 0 failures)

🤖 Generated with Claude Code

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Mar 12, 2026

Greptile Summary

This PR adds a fallback mechanism in QmdMemoryManager.readFile() so that when a filesystem path is missing (due to qmd's slugification of filenames like My Recipe Notes.mdmy-recipe-notes.md), it retries document retrieval via qmd get using a qmd:// URI. The new private getQmdDoc helper correctly decomposes the relPath into collection and sub-path components, mirroring the logic already in resolveReadPath, and wraps the CLI call in a try/catch so failures degrade gracefully to the existing empty-string response.

Key observations:

  • Logic bug: The early return inside the fallback block (line 900) ignores params.from and params.lines. The file-exists path at lines 905–923 slices the content via readPartialText or manual line arithmetic, but the new branch skips this entirely. Any memory_get call that specifies a line window on a slugified qmd path will receive the full document instead of the requested slice.
  • The path-traversal protections provided by resolveReadPath (collection allow-list, isWithinRoot check) are still exercised before the fallback is reached, so the URI passed to qmd get is already bounded.
  • No automated tests are added for the new fallback behavior.

Confidence Score: 3/5

  • The core fix works for the full-document case but contains a pagination bug that causes incorrect output when from/lines are supplied.
  • The primary use case (returning full document content for qmd search results) is correct. However, the early-return in the fallback path bypasses the existing from/lines pagination logic, which is a clear logical regression that will silently return wrong data when callers specify a line window.
  • src/memory/qmd-manager.ts — specifically the new early-return block inside readFile and the getQmdDoc helper.
Prompt To Fix All With AI
This is a comment left during a code review.
Path: src/memory/qmd-manager.ts
Line: 896-902

Comment:
**Pagination ignored on `getQmdDoc` fallback**

When `getQmdDoc` successfully retrieves content and `params.from` / `params.lines` are specified, the full document is returned without slicing. The normal (file-exists) code path at lines 905–923 carefully applies pagination via `readPartialText` or manual slicing, but this new early-return branch bypasses all of that logic.

The `memory_get` tool in `src/agents/tools/memory-tool.ts` passes `from` and `lines` directly to `readFile`, so a caller that requests a specific window (e.g. `from: 10, lines: 5`) on a slugified qmd path would silently receive the entire document instead of the expected slice.

```suggestion
      // Fall back to `qmd get` which can resolve slugified paths.
      if (relPath.startsWith("qmd/")) {
        const qmdText = await this.getQmdDoc(relPath);
        if (qmdText !== null) {
          if (params.from !== undefined || params.lines !== undefined) {
            const allLines = qmdText.split("\n");
            const start = Math.max(1, params.from ?? 1);
            const count = Math.max(1, params.lines ?? allLines.length);
            return { text: allLines.slice(start - 1, start - 1 + count).join("\n"), path: relPath };
          }
          return { text: qmdText, path: relPath };
        }
      }
```

How can I resolve this? If you propose a fix, please make it concise.

Last reviewed commit: 4ebcb5a

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4ebcb5ab09

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant