fix(terminal): keep label prose out of labelled spaced paths

A path label says a path follows, not that the words before it belong
to the path. Restore the guard that defers a relative candidate whose
only slash word is its last, so `Wrote: 120 lines to src/foo.ts` links
src/foo.ts as on master. `Saved to: My Docs/plan.md` and
`Saved to: 03 Attachments/plan.pptx` split again as a documented
trade-off; `Files: src/components and utils/x.ts` is pinned as the
residual the guard does not catch.

Refuse command lines whose executable sits under a bin/ or .bin/
directory, or that carry a flag word, so they keep master's links.

Update the JSDoc example, the CLAUDE.md pointer and the
architecture-invariants paragraph.
This commit is contained in:
Aamer Akhter committed 2026-10-24 22:28:57 -04:00
1 parent 405c9928d9
commit 5e1daaf7c7
5 files changed
+82 -5

No files matched your search

+8
View File
@@ -109,6 +109,14 @@ describe('response viewer file-path linkifier', () => {
expect(root.querySelector('strong')?.textContent).toBe(original.querySelector('strong')?.textContent);
});
it.each([
['**Wrote:** 120 lines to src/foo.ts', ['src/foo.ts']],
['- **Output:** stored in dist/app.js', ['dist/app.js']],
['**Created:** new file src/components/Button.tsx', ['src/components/Button.tsx']],
])('keeps prose after a bold path label out of the link: %s', (markdown, expected) => {
expect(paths(linkify(renderChat(markdown))).map((a) => a.getAttribute('data-path'))).toEqual(expected);
});
it('does not inherit a bold label from earlier prose or another block', () => {
for (const markdown of [
'See **Saved to:** 03 Attachments/Build SIPOC Design/Build-SIPOC.pptx',
+59 -1
View File
@@ -121,7 +121,10 @@ describe('spaced file paths', () => {
it('requires a path label for relative candidates but retains absolute ambiguity', () => {
expect(paths('- Fixed bug in src/My Docs/a.md')).toEqual(['Docs/a.md']);
expect(paths('Saved to: 03 Attachments/plan.pptx')).toEqual(['03 Attachments/plan.pptx']);
// Documented trade-off: a labelled relative path whose only space is in its top
// directory reads like prose after the label, so it splits as on master.
expect(paths('Saved to: 03 Attachments/plan.pptx')).toEqual(['Attachments/plan.pptx']);
expect(paths('Saved to: My Docs/plan.md')).toEqual(['Docs/plan.md']);
expect(paths('/home/me/proj contains a.md')).toEqual(['/home/me/proj contains a.md']);
expect(paths('Saved to: ~/My Docs/plan.md')).toEqual(['~/My Docs/plan.md']);
});
@@ -211,6 +214,61 @@ describe('spaced file paths: shapes that keep master links', () => {
expect(findFilePathLinks(line)).toEqual(masterLinks(line, [file]));
});
// A path label vouches that a path follows, not for the prose before it.
it.each([
['Wrote: 120 lines to src/foo.ts', 'src/foo.ts'],
['Created: new file src/components/Button.tsx', 'src/components/Button.tsx'],
['Output: written to logs/run.log', 'logs/run.log'],
['Files: 3 changed in src/foo.ts', 'src/foo.ts'],
['Location: line 42 in src/foo.ts', 'src/foo.ts'],
['Path: see src/foo.ts', 'src/foo.ts'],
['- Output: stored in dist/app.js', 'dist/app.js'],
['1. File: changes in src/a.ts', 'src/a.ts'],
['Created: 2026-10-24 build/x.json', 'build/x.json'],
])('keeps label prose out of the path: %s', (line, file) => {
expect(findFilePathLinks(line)).toEqual(masterLinks(line, [file]));
});
it('keeps labelled spaced paths whole when a `/` word comes before the last', () => {
for (const p of [
'03 Attachments/Build SIPOC Design/Build-SIPOC.pptx',
'~/My Docs/plan.md',
'./My Docs/plan.md',
'notes/a b/c.md',
'/mnt/c/My Docs/a.docx',
]) {
expect(paths(`Saved to: ${p}`)).toEqual([p]);
}
});
it('documented trade-off: prose between two `/` words after a path label stays in the link', () => {
// The first `/` word is not the last, so the prose guard does not fire; master
// linked only `utils/x.ts` here.
expect(paths('Files: src/components and utils/x.ts')).toEqual(['src/components and utils/x.ts']);
});
// A command whose executable lives under a known root, or one carrying a flag.
it.each([
['/opt/homebrew/bin/node server.js', '/opt/homebrew/bin/node', 'server.js'],
['/home/me/.venv/bin/python run.py', '/home/me/.venv/bin/python', 'run.py'],
[
'/home/me/proj/node_modules/.bin/vitest run test/foo.test.ts',
'/home/me/proj/node_modules/.bin/vitest',
'test/foo.test.ts',
],
['/home/me/proj/node_modules/.bin/tsc -p tsconfig.json', '/home/me/proj/node_modules/.bin/tsc', 'tsconfig.json'],
])('does not reserve a command under a known root: %s', (line, exe, file) => {
expect(findFilePathLinks(line)).toEqual(masterLinks(line, [exe, true], [file]));
});
it.each([
'/Users/me/Desktop/Screenshot 2026-10-24 at 10.15.32 AM.png',
'/mnt/c/Users/me/Docs - Copy/a.docx',
'- /mnt/c/Users/me/Docs - Copy/a.docx',
])('keeps a spaced path whole when a word only looks like a flag or a version: %s', (line) => {
expect(paths(line)).toEqual([line.replace(/^- /, '')]);
});
it('still refuses an unlinkable spaced path that reads as one path to its end', () => {
expect(paths('/etc/secret files/passwords.txt')).toEqual([]);
});