mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-11-15 02:49:33 +01:00
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:
1 parent
405c9928d9
commit
5e1daaf7c7
5 files changed
+82
-5
No files matched your search
@@ -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',
|
||||
|
||||
@@ -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([]);
|
||||
});
|
||||
|
||||
Reference in new issue
Block a user