Skip to content

feat(desktop): make file paths in chat messages clickable - #9677

Closed
bdeenyy wants to merge 53 commits into
aaif-goose:mainfrom
bdeenyy:feature/clickable-paths
Closed

feat(desktop): make file paths in chat messages clickable#9677
bdeenyy wants to merge 53 commits into
aaif-goose:mainfrom
bdeenyy:feature/clickable-paths

Conversation

@bdeenyy

@bdeenyy bdeenyy commented Jun 8, 2026

Copy link
Copy Markdown
Contributor

Summary

Make file paths in chat messages clickable in the desktop app. A remark plugin detects Unix (/home/user/file.rs), tilde (~/.config/app/settings.json), and Windows (C:\Users\dev\project\index.ts) paths in markdown text and inline code, and renders them as open-file:// links. Clicking a path reveals the file in Finder/Explorer via shell.showItemInFolder().

Rebased onto latest main; duplicate scrollbar commit from #9601 removed.

Testing

  • pnpm test -- src/utils/linkifyPaths.test.ts — 14 unit tests (path detection, edge cases, trailing punctuation)
  • Full desktop test suite passes locally (396 tests)
  • Manual: click Unix, tilde, and Windows paths in chat messages

Related Issues

N/A — happy to open a discussion/issue if you'd prefer to track this separately.

Screenshots/Demos (for UX changes)

Before: file paths in chat are plain text, not clickable.

After: paths render as links; clicking opens the file location in the system file manager.

Screenshots/Demos (for UX changes)

Before:
image
After:
image

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

Copy link
Copy Markdown

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: b4f4f91aea

ℹ️ About Codex in GitHub

Your team has set up Codex to 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 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread ui/desktop/src/main.ts Outdated

ipcMain.handle('open-path-in-explorer', async (_event, path: string) => {
try {
shell.showItemInFolder(path);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Expand tilde paths before revealing files

When the clicked link is a ~/... path, which the new linkifier explicitly recognizes in ui/desktop/src/utils/linkifyPaths.ts, this passes the literal tilde to Electron. Electron/Node file APIs do not perform shell expansion, and this app already uses expandTilde before other filesystem operations, so links like ~/.config/app/settings.json will fail to reveal the actual file. Please expand the path before calling showItemInFolder.

Useful? React with 👍 / 👎.

Comment thread ui/desktop/src/utils/linkifyPaths.ts Outdated
Comment on lines +7 to +8
const UNIX_PATH_RE = /(?:^|[\s('"`\[(,;]|\/\*.*?\*\/)()(\/(?:[a-zA-Z0-9._+-]+\/){1,}[a-zA-Z0-9._+-]+)/g;
const TILDE_PATH_RE = /(?:^|[\s('"`\[(,;]|\/\*.*?\*\/)()(~\/(?:[a-zA-Z0-9._+-]+\/)*[a-zA-Z0-9._+-]+)/g;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Avoid including sentence periods in path links

For paths followed by sentence punctuation, e.g. Created /tmp/result.txt., the final period is included in the generated link because . is allowed as the last path character. That makes the click try to reveal /tmp/result.txt. instead of /tmp/result.txt, so common assistant prose produces broken file links. Please strip terminal sentence punctuation or require a non-punctuation final character when matching these prose paths.

Useful? React with 👍 / 👎.

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

Copy link
Copy Markdown

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: cecfaa628c

ℹ️ About Codex in GitHub

Your team has set up Codex to 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 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +211 to +213
ExtensionConfig::Frontend { .. }
| ExtensionConfig::InlinePython { .. }
| ExtensionConfig::Sse { .. } => return Ok(None),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Preserve legacy extension entries in the config list

When a user already has a configured sse, inline_python, or frontend extension, this branch returns None and on_get_config_extensions drops it with flatten(). The desktop now populates configured extensions through this ACP endpoint (ui/desktop/src/acp/extensions.ts), so those existing config entries disappear from Settings and bulk operations even though they remain in the config, leaving users unable to see, disable, or remove legacy SSE/custom entries. Please keep returning a list entry for unsupported-but-configured extensions instead of filtering them out.

Useful? React with 👍 / 👎.

Comment on lines +199 to +202
let bytes = response
.bytes()
.await
.map_err(|error| format!("failed to read image response: {error}"))?;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Cap HTTP image downloads before buffering

For read_image URLs where the server omits or understates Content-Length, this buffers the entire response with response.bytes() before the 20 MB limit is enforced back in load_image. A large or unbounded response can therefore consume memory despite the advertised MAX_IMAGE_BYTES guard. Please stream the body and stop once the cap is exceeded, or otherwise apply a hard download-size limit before collecting the bytes.

Useful? React with 👍 / 👎.

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

Copy link
Copy Markdown

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: e0601f6dee

ℹ️ About Codex in GitHub

Your team has set up Codex to 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 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread ui/desktop/src/utils/linkifyPaths.ts Outdated
const localRe = new RegExp(re.source, 'g');
while ((m = localRe.exec(text)) !== null) {
const prefixLen = m[1].length;
const path = m[2];

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Capture the path from the existing regex group

When any message contains a recognized path, the regexes above only define one capture group (the path itself), but this reads m[2] as the path. That makes path undefined for normal inputs like /home/user/file.txt, so the later path.length access throws while Markdown is rendering instead of producing a clickable link. Either add a separate prefix capture group or read the path from m[1] and compute the prefix length differently.

Useful? React with 👍 / 👎.

@bdeenyy
bdeenyy force-pushed the feature/clickable-paths branch from e0601f6 to d6ddeaa Compare June 8, 2026 15:53

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

Copy link
Copy Markdown

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: d6ddeaa65b

ℹ️ About Codex in GitHub

Your team has set up Codex to 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 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +175 to +180
env_keys,
timeout,
bundled,
..
} => GooseExtension::Mcp {
server: McpServer::Stdio(McpServerStdio::new(name, cmd).args(args.clone())),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Preserve legacy env values when listing extensions

When a configured stdio extension still uses the legacy envs map, this arm discards it (..) and only returns env_keys; the analogous streamable_http arm does the same. The desktop now builds its Settings list from this ACP response and round-trips those entries through addExtension for toggles/edits, so a user with literal legacy env values can lose the actual environment values from config instead of having them migrated or preserved. Please either include/migrate the legacy env values or avoid round-tripping this lossy representation for config updates.

Useful? React with 👍 / 👎.

@bdeenyy
bdeenyy force-pushed the feature/clickable-paths branch from 28d68ad to e2a7475 Compare June 8, 2026 17:07

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

Copy link
Copy Markdown

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: e2a74753ee

ℹ️ About Codex in GitHub

Your team has set up Codex to 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 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread ui/desktop/src/utils/linkifyPaths.ts Outdated
Comment on lines +74 to +76
visit(tree, 'text', (node: Text, index: number | undefined, parent: Parent | undefined) => {
if (index === undefined || !parent) return;
linkifyNode(node, index, parent);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Skip generated link contents while linkifying paths

When a message contains a plain path such as See /etc/hosts, this visitor replaces the text node with a file-link node, but traversal then continues into the newly inserted link's text child and linkifies the same path again. That produces nested file links/anchors, which React/HTML cannot represent reliably and can break the click target; skip traversal inside link nodes or advance the visitor past the inserted nodes after splicing.

Useful? React with 👍 / 👎.

@DOsinga

DOsinga commented Jun 17, 2026

Copy link
Copy Markdown
Collaborator

Thanks for the contribution, and congrats on landing #9601! 🎉

We'd like to take this one but there are a few things to sort out first:

  • Codex P1 (the feature currently crashes): In findPaths, the path regexes use a non-capturing prefix group (?:...)? plus a single capturing group for the path. That means m[1] is the path and m[2] is undefined. The code reads path = m[2] and prefixLen = m[1].length, so at runtime path.length throws Cannot read properties of undefined — markdown rendering will break on any message that contains a file path. Please fix the group indexing (path is m[1], prefix length should come from m[0].length - m[1].length).
  • Tests don't test the real code: linkifyPaths.test.ts re-declares the regexes and a copy of findPaths inline and asserts against that copy, rather than importing findPaths from the module. Please import and test the actual implementation — with the real (currently broken) indexing, these tests would catch the bug above.
  • A couple of other open Codex comments are worth addressing too: trailing punctuation getting pulled into links (e.g. /tmp/result.txt.), and ~/ paths not being tilde-expanded before shell.showItemInFolder, so they won't resolve.

Also note that, per our contribution guide, for a new feature like this it's best to open an issue/discussion first so we can align on direction.

Could you address the Codex comments (fix or a one-line reason per AI Code Reviews)? We'll revisit in a few days. Thanks!

Denis and others added 3 commits June 18, 2026 08:45
Add remark plugin (linkifyPaths) that detects Unix, tilde, and Windows
file paths in markdown text and converts them to open-file:// links.
Clicking a path calls shell.showItemInFolder() to reveal the file in
Finder/Explorer.

Signed-off-by: Denis <i@deenyy.ru>
Detect Unix, tilde, and Windows file paths in chat text and inline
code nodes, and render them as open-file:// links so users can click
through to the referenced file.

Signed-off-by: Denis <i@deenyy.ru>
Fix regex capture group indexing in findPaths, strip trailing sentence
punctuation, skip URL-like matches, and avoid re-linkifying text inside
links. Test the exported findPaths implementation and expand tilde paths
before revealing files in the system file manager.

Signed-off-by: Denis <i@deenyy.ru>
Co-authored-by: Cursor <cursoragent@cursor.com>
@bdeenyy
bdeenyy force-pushed the feature/clickable-paths branch from e2a7475 to e310220 Compare June 18, 2026 05:50

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

Copy link
Copy Markdown

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: e310220557

ℹ️ About Codex in GitHub

Your team has set up Codex to 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 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread ui/desktop/src/utils/linkifyPaths.ts Outdated
Comment on lines +7 to +9
const UNIX_PATH_RE = /(?:^|[\s('"`[(,;]|\/\*.*?\*\/)?(\/(?:[a-zA-Z0-9._+-]+\/){1,}[a-zA-Z0-9._+-]+)/g;
const TILDE_PATH_RE = /(?:^|[\s('"`[(,;]|\/\*.*?\*\/)?(~\/(?:[a-zA-Z0-9._+-]+\/)*[a-zA-Z0-9._+-]+)/g;
const WIN_PATH_RE = /(?:^|[\s('"`[(,;]|\/\*.*?\*\/)?([A-Za-z]:[\\/](?:[a-zA-Z0-9._+-]+[\\/])+[a-zA-Z0-9._+-]+)/g;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Preserve paths that contain spaces

When a valid local path contains a space, these regexes stop the match at the first space and still create a link for the truncated prefix; for example Saved /Users/me/My Project/result.txt becomes a clickable /Users/me/My, which reveals the wrong or nonexistent item instead of the actual file. This affects common folder/file names with spaces, so the matcher should either include spaces with a reliable terminator or avoid linkifying partial segment matches.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Thanks for the review and for merging #9601!

I've addressed the feedback in e310220:

P1 — regex group indexing: findPaths now reads the path from m[1] and computes prefixLen as m[0].length - m[1].length.

Tests: linkifyPaths.test.ts imports and tests the exported findPaths from the module instead of duplicating the logic inline.

Trailing punctuation: sentence-ending punctuation (e.g. /tmp/result.txt.) is stripped before creating the link.

Tilde paths: open-path-in-explorer now calls expandTilde() before shell.showItemInFolder().

Other Codex comments:

Nested links (P2): text/inlineCode nodes inside existing link parents are skipped during traversal.
URL false positives: paths immediately after a URL scheme (https://...) are ignored.
Also rebased onto latest main and dropped the duplicate scrollbar commit (already landed in #9601).

Re: opening an issue/discussion first — understood for future features. Happy to open one retroactively if that helps track this.

Let me know if anything else needs adjusting!

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Addressed the P2 "paths with spaces" feedback in 8ed41e8:

Replaced greedy regex with a segment parser that allows spaces inside folder/file names but stops before trailing prose.
open-file:// hrefs now use encodeURI() so spaces survive markdown URL handling.

@bdeenyy bdeenyy changed the title Feature/clickable paths feat(desktop): make file paths in chat messages clickable Jun 18, 2026
Replace greedy regex matching with a path parser that allows spaces
inside segment names while stopping before trailing prose. Encode
spaces in open-file:// URLs so markdown hrefs remain valid.

Signed-off-by: Denis <i@deenyy.ru>
Co-authored-by: Cursor <cursoragent@cursor.com>

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

Copy link
Copy Markdown

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: 8ed41e8fec

ℹ️ About Codex in GitHub

Your team has set up Codex to 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 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread ui/desktop/src/utils/linkifyPaths.ts Outdated

const after = text[j];
if (after === separator) return j;
if (after === undefined || /[.,;:!?)'\]"]/.test(after)) return j;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Stop treating following prose as part of a path

When a normal path is followed by a word and sentence punctuation, this branch treats that word as a spaced path continuation, so text like Created /tmp/result successfully. is linkified as /tmp/result successfully instead of /tmp/result. That makes common assistant prose reveal the wrong or nonexistent file; only include the spaced continuation when there is stronger evidence it is part of the path.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed in 692f479: spaced continuations before punctuation now require path-like evidence (capitalized/dotted segment name, or short preceding token for multi-word folder names like my documents). Trailing sentence punctuation is stripped while scanning continuations so . is not absorbed via isPathChar.

Do not treat lowercase words after a complete path segment as part of
the path when they precede sentence punctuation. Also strip trailing
punctuation while scanning spaced continuations so dots are not
absorbed into the word via isPathChar.

Signed-off-by: Denis <i@deenyy.ru>
Co-authored-by: Cursor <cursoragent@cursor.com>

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

Copy link
Copy Markdown

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: 692f479c4e

ℹ️ About Codex in GitHub

Your team has set up Codex to 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 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread ui/desktop/src/utils/linkifyPaths.ts Outdated
}

function isUrlPath(text: string, index: number): boolean {
const before = text.slice(0, index);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Avoid O(n²) path scans on long text nodes

When a message contains a long plain-text node with no paths, findPaths calls isValidPathStart for every character, and this prefix slice plus the later prefix slice/comment regex scan reprocess all preceding text each time. Since remarkLinkifyPaths now runs on every rendered chat message, a large assistant paragraph or pasted log outside a fenced code block can stall the desktop UI even though there is nothing to linkify; keep the scan linear by checking the immediate context/protocol state without slicing the whole prefix at each index.

Useful? React with 👍 / 👎.

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

Copy link
Copy Markdown

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: 692f479c4e

ℹ️ About Codex in GitHub

Your team has set up Codex to 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 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread ui/desktop/src/utils/linkifyPaths.ts Outdated
}

function isUrlPath(text: string, index: number): boolean {
const before = text.slice(0, index);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Avoid O(n²) path scans on long text nodes

When a message contains a long plain-text node with no paths, findPaths calls isValidPathStart for every character, and this prefix slice plus the later prefix slice/comment regex scan reprocess all preceding text each time. Since remarkLinkifyPaths now runs on every rendered chat message, a large assistant paragraph or pasted log outside a fenced code block can stall the desktop UI even though there is nothing to linkify; keep the scan linear by checking the immediate context/protocol state without slicing the whole prefix at each index.

Useful? React with 👍 / 👎.

Scan text in a single pass with O(1) URL and block-comment checks
instead of re-slicing prefixes at every index. Skip visiting children
of generated open-file links to avoid re-linkifying path text.

Signed-off-by: Denis <i@deenyy.ru>
Co-authored-by: Cursor <cursoragent@cursor.com>

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

Copy link
Copy Markdown

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: 18923abb4e

ℹ️ About Codex in GitHub

Your team has set up Codex to 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 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread ui/desktop/src/utils/linkifyPaths.ts Outdated
function isPathLikeSpacedWord(word: string, prevToken: string): boolean {
if (/[A-Z0-9._+-]/.test(word)) return true;
return (
/^[a-z][a-z0-9]*$/.test(word) && prevToken.length <= 3 && !prevToken.includes('.')

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Stop absorbing prose after short basenames

The fresh case not covered by the earlier prose fix is when the matched path ends with a short basename: with Created /tmp/a successfully. this condition treats successfully as a spaced path segment because prevToken.length <= 3, so the link becomes /tmp/a successfully and the click opens a nonexistent item rather than /tmp/a. The prose guard should require stronger evidence than a short previous token at the end of the match.

Useful? React with 👍 / 👎.

Comment thread ui/desktop/src/utils/urlSecurity.ts Outdated
'firefox:',
'safari:',
'goose:',
'open-file:',

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Keep file links out of globally safe protocols

I checked the other isProtocolSafe call sites in MCPUIResourceRenderer and McpAppRenderer: safe protocols are opened directly via window.electron.openExternal without the confirmation path. Since open-file:// is only handled specially by MarkdownContent, marking it globally safe lets an MCP link action with this app-internal scheme bypass confirmation and get forwarded by main's open-external handler to the OS as an external protocol. Keep this scheme local to generated markdown file links instead of adding it to the shared safe list.

Useful? React with 👍 / 👎.

a: (props) => {
const href = props.href;
if (href && href.startsWith(OPEN_FILE_PROTOCOL)) {
const filePath = decodeURIComponent(href.slice(OPEN_FILE_PROTOCOL.length));

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Guard decoding of authored file links

Generated file links are encoded, but arbitrary markdown can also include an open-file:// href now; if it contains malformed percent escapes such as [x](open-file:///%E0%A4%A), this decodeURIComponent runs during render and throws URIError, breaking the whole MarkdownContent render. Please catch decode failures or ignore invalid file-link hrefs before rendering the anchor.

Useful? React with 👍 / 👎.

Require multi-char basenames before spaced continuations, remove
open-file from global safe protocols, guard decodeURIComponent for
authored links, and return explicitly from visit callback branches.

Signed-off-by: Denis <i@deenyy.ru>
Co-authored-by: Cursor <cursoragent@cursor.com>

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

Copy link
Copy Markdown

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: dd6b368ca5

ℹ️ About Codex in GitHub

Your team has set up Codex to 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 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread ui/desktop/src/utils/linkifyPaths.ts Outdated
Comment on lines +12 to +13
function isPathChar(char: string): boolean {
return /[a-zA-Z0-9._+-]/.test(char);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Handle non-ASCII path characters

When a valid path contains a Unicode directory or file name after enough ASCII segments, for example /Users/me/デスクトップ/out.txt, isPathChar stops at the first non-ASCII character while parsePathAt has already counted a valid prefix, so the generated link points to /Users/me/ instead of the actual file. This misdirects clicks for localized filenames; either allow Unicode filename characters or avoid linkifying partial prefixes when parsing stops on an unsupported path character.

Useful? React with 👍 / 👎.

return (
<a
{...props}
href={undefined}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Preserve keyboard access for file links

Because the generated file link renders an <a> without an href, it is no longer focusable or activatable via normal keyboard link semantics, so users tabbing through chat cannot open these file paths even though mouse clicks work. Keep a safe href or render a button-like control with the appropriate focus and key handling.

Useful? React with 👍 / 👎.

Allow Unicode letters and numbers in path segments, reject partial
matches when a later segment contains unsupported characters, and
restore href on file-path links so they remain keyboard accessible.

Signed-off-by: Denis <i@deenyy.ru>
Co-authored-by: Cursor <cursoragent@cursor.com>

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

Copy link
Copy Markdown

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: fc5c4d0ed0

ℹ️ About Codex in GitHub

Your team has set up Codex to 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 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread ui/desktop/src/utils/linkifyPaths.ts Outdated
Comment on lines +61 to +65
if (!/^[a-z0-9]+$/.test(word)) return true;
return (
prevToken.length >= 2 &&
prevToken.length <= 3 &&
!prevToken.includes('.')

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Keep numeric suffixes in spaced path segments

Fresh evidence beyond the earlier spaces fix: when a valid path ends with a numeric/lowercase suffix after a longer token, e.g. Saved /Users/me/Project 2026., this predicate rejects 2026 because Project is longer than 3 chars, so the generated link points at /Users/me/Project instead of the actual path. That makes common year/version-suffixed folders reveal the wrong item; include these continuations with a stronger terminator or avoid emitting a partial link.

Useful? React with 👍 / 👎.

Comment thread ui/desktop/src/utils/linkifyPaths.ts Outdated
if (node.type !== 'text' && node.type !== 'inlineCode') {
return undefined;
}
if (index === undefined || !parent || parent.type === 'link') {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Skip reference links before linkifying children

When authored Markdown uses a reference-style link whose visible text contains a path, such as [log /tmp/out][log], the text node's parent is linkReference, not link, so this guard still replaces that child with an open-file:// link. The result is nested/broken anchors and the original reference link no longer behaves as authored; skip all link-like parents before calling linkifyNode.

Useful? React with 👍 / 👎.

Allow digit-only spaced continuations for folders like "Project 2026"
and skip linkification inside linkReference parents to preserve
reference-style markdown.

Signed-off-by: Denis <i@deenyy.ru>
Co-authored-by: Cursor <cursoragent@cursor.com>

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

Copy link
Copy Markdown

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: 435732b872

ℹ️ About Codex in GitHub

Your team has set up Codex to 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 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

if (node.type !== 'text' && node.type !== 'inlineCode') {
return undefined;
}
if (index === undefined || !parent || isLinkLikeParent(parent)) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Skip link ancestors, not just direct parents

Fresh evidence beyond the direct-child reference-link case: when an authored link label wraps the path in formatting, e.g. [**/tmp/out**](https://example.com) or [**/tmp/out**][log], the text node's parent is strong, so this immediate-parent check misses the link ancestor and linkifyNode inserts an open-file:// link inside the original link. That produces nested/broken anchors and changes the user's authored target; skip traversal for the whole link/linkReference subtree instead of only checking the immediate parent.

Useful? React with 👍 / 👎.

Comment on lines +118 to +120
while (i < text.length && text[i] === ' ') {
const continuationEnd = readSpacedContinuation(text, i, start, separator);
if (continuationEnd === i) break;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Avoid partial links before parenthesized suffixes

Fresh evidence beyond the earlier spaces fix: for common duplicate/download names like /Users/me/Downloads/report (1).pdf, readSpacedContinuation stops at the space before (, so the path emitted is only /Users/me/Downloads/report and the click reveals the wrong item while (1).pdf remains plain text. Either support these filename characters in spaced continuations or suppress the partial link when the continuation looks path-like.

Useful? React with 👍 / 👎.

Skip the entire link and linkReference subtrees during traversal so
formatted link labels are not linkified, and include download-style
suffixes like "report (1).pdf" in matched paths.

Signed-off-by: Denis <i@deenyy.ru>
Co-authored-by: Cursor <cursoragent@cursor.com>

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

Copy link
Copy Markdown

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: f7ccad017e

ℹ️ About Codex in GitHub

Your team has set up Codex to 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 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread ui/desktop/src/utils/linkifyPaths.ts Outdated
if (!segment) {
if (segmentCount >= minSegments && i < text.length) {
const next = text[i];
if (next !== separator && next !== ' ' && !/[.,;:!?)'\]"]/.test(next)) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Avoid partial links for punctuated filenames

When a valid filename contains punctuation that is also treated as prose punctuation, e.g. Saved /tmp/report,final.txt, readSegment stops at the comma and this condition accepts it as a path terminator. The emitted link then targets only /tmp/report while ,final.txt remains plain text, so clicking opens the wrong or nonexistent item; please include such punctuation when more path characters follow, or reject the partial match.

Useful? React with 👍 / 👎.

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

Copy link
Copy Markdown

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: f31ed2a41a

ℹ️ About Codex in GitHub

Your team has set up Codex to 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 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread ui/desktop/src/utils/linkifyPaths.ts Outdated
if (i >= text.length) return spaceIndex;

const content = text.slice(contentStart, i);
if (!/^[a-zA-Z0-9]+$/.test(content)) return spaceIndex;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Preserve parenthesized filename suffixes with spaces

When a filename has a parenthesized suffix containing a space or separator, such as /Users/me/Downloads/report (final copy).pdf or report (final-draft).pdf, this alphanumeric-only guard returns before the following .pdf can prove the parenthetical is part of the filename. readSegment then stops at the space and linkifies only /Users/me/Downloads/report, so clicking reveals the wrong prefix while the actual filename remains plain text.

Useful? React with 👍 / 👎.

Allow spaced and hyphenated parenthetical filename suffixes when an
extension follows, while still rejecting short prose parentheticals like
(temp) after paths.

Signed-off-by: Denis <i@deenyy.ru>
Co-authored-by: Cursor <cursoragent@cursor.com>

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

Copy link
Copy Markdown

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: b3a3484e6f

ℹ️ About Codex in GitHub

Your team has set up Codex to 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 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread ui/desktop/src/utils/linkifyPaths.ts Outdated
Comment on lines +98 to +100
if (/\.[A-Za-z0-9]+$/.test(word) && /^[a-z]/.test(prevToken)) {
return true;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Accept lowercase extension words after titlecase basenames

When a spaced filename starts with a capitalized token and the next token is lowercase but carries the extension, such as Saved /tmp/Project notes.txt or Saved /tmp/API response.json, this condition rejects the continuation because prevToken is not lowercase and the fallback only allows very short previous tokens. parsePathAt then treats the space as a boundary and emits /tmp/Project or /tmp/API, so clicking opens the wrong prefix instead of the actual file; an extension-bearing continuation should count as path evidence regardless of the previous token's case.

Useful? React with 👍 / 👎.

Comment thread ui/desktop/src/utils/linkifyPaths.ts Outdated
Comment on lines +84 to +85
if (/^\d+$/.test(word)) {
return word.length >= 4;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Stop absorbing long numeric prose after paths

When a complete path is followed by a 4+ digit count or year in prose, for example Created /tmp/out 2026 files, this branch accepts 2026 as a spaced filename segment solely because of its length. The generated link targets /tmp/out 2026 instead of /tmp/out, so common status/count text after paths opens the wrong or nonexistent item; require stronger path evidence before appending bare numeric continuations.

Useful? React with 👍 / 👎.

Accept extension-bearing spaced filename segments regardless of basename
case, and only allow bare 4+ digit continuations after capitalized basenames
when the path ends or hits sentence punctuation.

Signed-off-by: Denis <i@deenyy.ru>
Co-authored-by: Cursor <cursoragent@cursor.com>

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

Copy link
Copy Markdown

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: f71168a9e8

ℹ️ About Codex in GitHub

Your team has set up Codex to 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 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

}

function isFilenamePunctuation(char: string): boolean {
return char === ',' || char === "'" || char === ':' || char === '?';

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Handle balanced parentheses inside filenames

When a real filename uses parentheses without a preceding space, such as /tmp/report(1).pdf or /tmp/foo(bar).txt, readSegment stops at ( because this punctuation whitelist does not hand it off to any balanced-parentheses reader. Since ( is not a path boundary, parsePathAt rejects the whole match, leaving these common file paths unclickable even though the spaced parenthetical form is supported.

Useful? React with 👍 / 👎.

Support filenames like report(1).pdf and foo(bar).txt in path detection
while rejecting inline prose parentheticals without extensions.

Signed-off-by: Denis <i@deenyy.ru>
Co-authored-by: Cursor <cursoragent@cursor.com>

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

Copy link
Copy Markdown

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: d69f581ef1

ℹ️ About Codex in GitHub

Your team has set up Codex to 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 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread ui/desktop/src/utils/linkifyPaths.ts Outdated

function isParenthesizedFilenameContent(content: string): boolean {
if (/^\d+$/.test(content)) return true;
return /^[a-zA-Z0-9]+(?:[ -][a-zA-Z0-9]+)*$/.test(content);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Preserve versioned parenthetical suffixes

When a filename has a versioned parenthetical suffix such as /tmp/report (v1.2).pdf or /tmp/report (final_v2).pdf, this content check rejects the parenthetical before the following .pdf can prove it belongs to the filename. readSegment then stops at the space and the generated link targets only /tmp/report, leaving the real filename as plain text; allow filename characters like . and _ here when the closing parenthesis is followed by extension/path characters.

Useful? React with 👍 / 👎.

…ffixes

Versioned download names like (v1.2) or (final_v2) were rejected by
isParenthesizedFilenameContent, so spaced paths stopped at the basename.

Signed-off-by: Denis <i@deenyy.ru>
Co-authored-by: Cursor <cursoragent@cursor.com>

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

Copy link
Copy Markdown

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: 2c7ac42887

ℹ️ About Codex in GitHub

Your team has set up Codex to 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 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread ui/desktop/src/utils/linkifyPaths.ts Outdated
}

function stripLineColumnSuffix(path: string): string {
const match = path.match(/\.[A-Za-z0-9]+(:\d+(?::\d+)?)$/);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Strip line suffixes from extensionless diagnostics

Fresh evidence beyond the extension-bearing cases is common diagnostics for extensionless files such as /workspace/Dockerfile:12 or /workspace/Makefile:8:1: readSegment consumes the numeric suffix via colon punctuation, but this regex only strips :line[:column] when the basename has a dot extension. The generated link therefore targets a nonexistent Dockerfile:12-style path instead of the real file, so line suffix handling needs to cover extensionless diagnostic filenames too.

Useful? React with 👍 / 👎.

Comment thread ui/desktop/src/utils/linkifyPaths.ts Outdated
Comment on lines +184 to +185
if (i > afterParen) {
return i;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Require real suffix evidence after parentheticals

Fresh evidence in the current code is that a sentence period after a word parenthetical counts as isPathChar, so Created /tmp/out (temporary). is accepted as /tmp/out (temporary) and the final period is stripped later. That makes ordinary explanatory parentheticals at sentence boundaries open the wrong/nonexistent path; only treat the parenthetical as part of the filename when the following characters prove an actual extension or filename continuation.

Useful? React with 👍 / 👎.

Strip line:column suffixes from extensionless diagnostic filenames like
Dockerfile and Makefile, and require real extension evidence after
word parentheticals so prose like "(temporary)." is not absorbed.

Signed-off-by: Denis <i@deenyy.ru>
Co-authored-by: Cursor <cursoragent@cursor.com>

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

Copy link
Copy Markdown

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: 032955f478

ℹ️ About Codex in GitHub

Your team has set up Codex to 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 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread ui/desktop/src/utils/linkifyPaths.ts Outdated
while (i >= 0 && /[a-zA-Z0-9_.$-]/.test(text[i])) {
i--;
}
return !(i >= 0 && (text[i] === '?' || text[i] === '&'));

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Block fragment URL values after equals

When a local-looking path appears inside inline code or other non-autolinked URL text such as https://host/page#file=/tmp/out or https://host/download;file=/tmp/out, this guard accepts the slash after = because it only rejects ? and &. The path linkifier then inserts an open-file link into the URL text, corrupting the URL and making the embedded value reveal /tmp/out; treat fragment/path-parameter delimiters like query delimiters before allowing assignment-style starts.

Useful? React with 👍 / 👎.

Comment thread ui/desktop/src/utils/linkifyPaths.ts Outdated

const lastSep = Math.max(path.lastIndexOf('/'), path.lastIndexOf('\\'));
const basename = path.slice(lastSep + 1);
const lineColMatch = basename.match(/^([A-Z][A-Za-z0-9_-]*)(:\d+(?::\d+)?)$/);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Strip lowercase extensionless line suffixes

Fresh evidence in this revision is that the extensionless fallback only matches basenames starting with an uppercase letter. Diagnostics for lowercase extensionless files, for example this repo's crates/goose-sdk/justfile:12, keep :12 in the matched path after readSegment consumes the colon digits, so the generated link targets a nonexistent justfile:12 instead of the actual file.

Useful? React with 👍 / 👎.

…ic suffixes

Treat # and ; like query delimiters before assignment-style path starts so
paths in URL fragments and path parameters are not linkified. Strip :line
suffixes from known lowercase extensionless diagnostic filenames such as
justfile and dockerfile without affecting colon-in-filename cases like snapshot:1.

Signed-off-by: Denis <i@deenyy.ru>
Co-authored-by: Cursor <cursoragent@cursor.com>

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

Copy link
Copy Markdown

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: a321ea3042

ℹ️ About Codex in GitHub

Your team has set up Codex to 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 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread ui/desktop/src/utils/linkifyPaths.ts Outdated
Comment on lines +281 to +286
if (after !== undefined && /\s/.test(after)) {
if (after === ' ') {
const rest = text.slice(j).trimStart();
if (rest.startsWith(separator)) return spaceIndex;
}
return isSpacedFilenameContinuation(word, prevToken, text, j) ? j : spaceIndex;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Handle parenthesized spaced directories before separators

For common paths such as C:\Program Files (x86)\Goose, the continuation after Files reaches this whitespace branch with rest starting (x86)\Goose, not the separator, so it falls back to the capitalization heuristics and rejects Files because the previous token Program is longer than three characters. The match is then emitted as C:\Program, so clicking the generated link reveals the wrong prefix; treat a following parenthesized directory suffix before the next separator as evidence to continue the segment.

Useful? React with 👍 / 👎.

Accept spaced path continuations when a parenthesized directory suffix
like (x86) or (Beta) appears before the next separator, so Windows and
Unix paths such as C:\Program Files (x86)\Goose linkify fully.

Signed-off-by: Denis <i@deenyy.ru>
Co-authored-by: Cursor <cursoragent@cursor.com>

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

Copy link
Copy Markdown

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: bc1dd15100

ℹ️ About Codex in GitHub

Your team has set up Codex to 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 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread ui/desktop/src/utils/linkifyPaths.ts Outdated
Comment on lines +83 to +86
while (i >= 0 && /[a-zA-Z0-9_.$-]/.test(text[i])) {
i--;
}
return !(i >= 0 && URL_PARAM_DELIMITERS.has(text[i]));

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Reject bracketed URL query keys before paths

Fresh evidence in the current delimiter check is that URL query keys containing common bracket/encoding characters are still treated as assignments. In inline code like https://host/download?file[]=/tmp/out or https://host/download?file%5B%5D=/tmp/out, this scan stops on ] or % instead of reaching the preceding ?, so isAssignmentEqualsStart returns true and the linkifier inserts an open-file:// link into the URL value. This is the same URL-query context the tests are trying to suppress; keep scanning valid parameter-key characters or otherwise detect URL context before allowing the = case.

Useful? React with 👍 / 👎.

Comment thread ui/desktop/src/utils/linkifyPaths.ts Outdated
}
if (j === i) return false;
const word = text.slice(i, j).replace(TRAILING_PUNCTUATION_RE, '');
if (hasFileExtension(word) && /^[a-z]/.test(word)) return true;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Accept uppercase extension words during lookahead

When a multi-word filename has an intermediate lowercase segment and the extension-bearing segment starts uppercase, e.g. Saved /tmp/project notes Draft.txt, this condition rejects the lookahead solely because Draft.txt is not lowercase. readSpacedContinuation then stops at notes and parsePathAt emits only /tmp/project, so clicks reveal the wrong prefix even though the .txt extension proves the remaining words are part of the filename.

Useful? React with 👍 / 👎.

…ahead

Extend query-param key scanning in isAssignmentEqualsStart to include
brackets and percent-encoding so paths in values like file[]=/tmp/out
are not linkified. Accept extension-bearing spaced words regardless of
case in readExtensionWordAhead to match isPathLikeSpacedWord behavior.

Signed-off-by: Denis <i@deenyy.ru>
Co-authored-by: Cursor <cursoragent@cursor.com>

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

Copy link
Copy Markdown

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: c9823ef9ec

ℹ️ About Codex in GitHub

Your team has set up Codex to 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 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread ui/desktop/src/utils/linkifyPaths.ts Outdated
Comment on lines +184 to +186
/^[a-z][a-z0-9]*$/.test(word) &&
/^[a-z]/.test(prevToken) &&
readExtensionWordAhead(text, endIndex)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Allow titlecase filenames to reach later extensions

When a spaced filename starts with a capitalized token and the extension appears on a later word, e.g. Saved /tmp/Project notes draft.txt or Saved /tmp/API response data.json, this lookahead is the only branch that can use the later .txt/.json as evidence, but it requires the previous token to be lowercase. The first continuation word is rejected, so parsePathAt emits only /tmp/Project or /tmp/API; clicking the generated link opens the wrong prefix even though the later extension proves the full filename.

Useful? React with 👍 / 👎.

Let lowercase spaced filename words continue after titlecase or acronym
basenames when readExtensionWordAhead proves a later extension, matching
two-word cases already handled via hasFileExtension.

Signed-off-by: Denis <i@deenyy.ru>
Co-authored-by: Cursor <cursoragent@cursor.com>

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

Copy link
Copy Markdown

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: ad9db5af90

ℹ️ About Codex in GitHub

Your team has set up Codex to 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 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread ui/desktop/src/utils/linkifyPaths.ts Outdated
}

function isQueryParamKeyChar(char: string): boolean {
return /[a-zA-Z0-9_.$\[\]%-]/.test(char);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Keep hyphenated URL query values out of file links

Fresh evidence in the current URL guard is that hyphens are not treated as query-key characters, so a URL such as https://host/download?file-name=/tmp/out stops scanning at - and isAssignmentEqualsStart allows the /tmp/out value to become an open-file:// link. Hyphenated parameter names are common, and this corrupts inline URL text in the same query-value context the surrounding checks are trying to suppress; include - in the backwards scan or otherwise detect the URL delimiter before accepting the assignment case.

Useful? React with 👍 / 👎.

Move hyphen to the start of the isQueryParamKeyChar character class so
it cannot be misread as a range operator, and add tests for hyphenated
query keys like file-name= and content-type=.

Signed-off-by: Denis <i@deenyy.ru>
Co-authored-by: Cursor <cursoragent@cursor.com>

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

Copy link
Copy Markdown

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: d5619ded04

ℹ️ About Codex in GitHub

Your team has set up Codex to 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 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread ui/desktop/src/utils/linkifyPaths.ts Outdated

function isParenthesizedFilenameContent(content: string): boolean {
if (/^\d+$/.test(content)) return true;
return /^[a-zA-Z0-9._]+(?:[ -][a-zA-Z0-9._]+)*$/.test(content);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Allow supported filename characters inside parentheticals

When a parenthesized filename suffix contains characters the parser already supports elsewhere, such as an apostrophe or non-ASCII text, this ASCII-only guard rejects the suffix before the following extension can prove it is part of the filename. For example, Saved /tmp/report (John's copy).pdf or Saved /tmp/report (最終).pdf gets linkified only as /tmp/report, so clicking opens the wrong prefix while the actual file path remains plain text.

Useful? React with 👍 / 👎.

Denis and others added 2 commits June 19, 2026 10:44
Expand isParenthesizedFilenameContent to accept characters already valid
in path segments via isPathChar, plus spaces, hyphens, and apostrophes.
Extension evidence in readParenthesizedContent still blocks prose like
(temporary) without a real filename suffix.

Signed-off-by: Denis <i@deenyy.ru>
Co-authored-by: Cursor <cursoragent@cursor.com>
Remove unnecessary \[ escape in isQueryParamKeyChar regex and extend
parenthesized filename detection for apostrophes and Unicode content.

Signed-off-by: Denis <i@deenyy.ru>
Co-authored-by: Cursor <cursoragent@cursor.com>

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

Copy link
Copy Markdown

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: 7aad701d84

ℹ️ About Codex in GitHub

Your team has set up Codex to 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 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +145 to +146
prevToken.length >= 2 &&
prevToken.length <= 2 &&

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Stop appending prose after two-character basenames

Fresh evidence in this revision is that this fallback still accepts any 4+ letter lowercase word when the previous path token is exactly two characters. In messages like Created /tmp/ui successfully. or Check /tmp/go output, the generated link targets /tmp/ui successfully or /tmp/go output instead of the completed short path, so common status prose after short directories opens the wrong item; require stronger filename evidence such as an extension or following separator before taking this branch.

Useful? React with 👍 / 👎.

Require extension, separator, or extension-ahead evidence before the
two-char basename spaced-word fallback, and keep determiner-led folder
names like "my documents" working via an explicit rule.

Signed-off-by: Denis <i@deenyy.ru>
Co-authored-by: Cursor <cursoragent@cursor.com>

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

Copy link
Copy Markdown

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: 535be0762a

ℹ️ About Codex in GitHub

Your team has set up Codex to 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 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

while (i >= 0 && isQueryParamKeyChar(text[i])) {
i--;
}
return !(i >= 0 && URL_PARAM_DELIMITERS.has(text[i]));

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Reject URL path assignment values

When a non-autolinked URL has an equals-valued path segment, such as inline code https://host/download/file=/tmp/out or bare text example.com/download/file=/tmp/out, this scan stops at the preceding / and returns true because / is not treated as URL context. The path linkifier then inserts an open-file:// link into the URL text, corrupting the displayed URL and making the value look like a local file path; detect slash/domain URL context before accepting assignment-style starts.

Useful? React with 👍 / 👎.

Comment thread ui/desktop/src/utils/linkifyPaths.ts Outdated
return true;
}
return (
/^[a-z][a-z0-9]*$/.test(word) &&

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Allow supported chars in spaced filename words

When a spaced filename has a supported filename character in an intermediate word and the extension appears later, e.g. Saved /tmp/project release-notes draft.txt or Saved /tmp/project release_notes draft.txt, this alphanumeric-only check rejects the first continuation before the later .txt evidence can be used. findPaths then emits only /tmp/project, so the generated link opens the wrong prefix for valid filenames that otherwise use characters this parser already accepts.

Useful? React with 👍 / 👎.

Reject linkifying paths after = when the slash belongs to a URL path
segment, and allow hyphens/underscores in intermediate spaced filename
words when extension lookahead succeeds.

Signed-off-by: Denis <i@deenyy.ru>
Co-authored-by: Cursor <cursoragent@cursor.com>
@DOsinga

DOsinga commented Jun 19, 2026

Copy link
Copy Markdown
Collaborator

Thanks for all the work on this, and genuinely for being so responsive to the review — you fixed the P1 and the test issue quickly, and stuck with it through a lot of codex rounds.

That said, watching what codex keeps surfacing on every commit (line:col suffixes, (1).pdf, [OK] tags, apostrophes, ... for details, years vs counts, and so on), I think this is telling us the approach is the problem rather than any individual bug. The current scanner starts at a separator and reads forward into open prose, so the one reliable landmark — the file extension — arrives late or never, and the right edge of the path has to be guessed. That guessing is an unwinnable game: every heuristic fixes one case and opens another, which is exactly the churn we are seeing. So in its current form this is not the way, and I am going to close it.

If you (or anyone) wants to take another run at it, here is a direction that sidesteps almost all of this:

  • Keep a fixed set of known extensions (plus extensionless names like Dockerfile).
  • Find every .<known-ext> occurrence and scan backwards, consuming allowed path chars until you hit a / (or C:\ on Windows), a clear boundary, or a length cap.

Because the extension anchors the right edge, you never have to guess where the path ends — which is what causes 80% of the edge cases here. The only remaining judgement is how far left to go across spaces, and even there a simple default (don't cross spaces, or only cross while the previous token is path-chars-only) terminates cleanly with no per-case heuristics. That should collapse most of this ~600-line file and make it robust by construction.

Please feel free to open a fresh PR (or reopen) if you land on a more scalable solution along those lines — I'd be happy to look. Thanks again! 🙏

@DOsinga DOsinga closed this Jun 19, 2026
@bdeenyy
bdeenyy deleted the feature/clickable-paths branch June 19, 2026 18:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants