1- import { cp , mkdir , mkdtemp , rm , symlink , writeFile } from 'node:fs/promises' ;
1+ import { cp , mkdir , mkdtemp , readFile , rm , symlink , writeFile } from 'node:fs/promises' ;
22import { spawn , type ChildProcess } from 'node:child_process' ;
33import { createServer , type Server , type Socket } from 'node:net' ;
44import { tmpdir } from 'node:os' ;
@@ -1496,6 +1496,15 @@ it('proves plugin-scoped Cursor hook registration and flags stale, missing, and
14961496 expect ( hostReport ( await doctor ( ) , 'cursor' ) . inventory . findings [ 0 ] ?. hooks ) . toMatchObject ( { state : 'stale' } ) ;
14971497 await writeHookedCursorPlugin ( pluginRoot ) ;
14981498
1499+ // A sibling plugin whose path shares this plugin's path as a prefix must not count as duplicate delivery.
1500+ await writeJson ( join ( fixture . home , '.cursor' , 'hooks.json' ) , {
1501+ hooks : { preToolUse : [ { command : `node ${ pluginRoot } -tools/hooks/before-tool.mjs` } ] } ,
1502+ version : 1 ,
1503+ } ) ;
1504+ const sibling = await doctor ( ) ;
1505+ expect ( hostReport ( sibling , 'cursor' ) . inventory . findings [ 0 ] ?. hooks ) . toMatchObject ( { duplicates : [ ] , state : 'registered' } ) ;
1506+ expect ( hookDiagnostics ( sibling ) . map ( ( entry ) => entry . code ) ) . toEqual ( [ 'AB7321' ] ) ;
1507+
14991508 await writeJson ( join ( fixture . home , '.cursor' , 'hooks.json' ) , {
15001509 hooks : { preToolUse : [ { command : `node ${ join ( pluginRoot , 'hooks/before-tool.mjs' ) } ` } ] } ,
15011510 version : 1 ,
@@ -1596,11 +1605,20 @@ it('tracks staged Cursor marketplaces from staged to imported', async () => {
15961605 expect ( hostReport ( foreign , 'cursor' ) . inventory . findings ) . toEqual ( [ { ...stagedFinding , state : 'unregistered' } ] ) ;
15971606
15981607 // A cache directory without Cursor's `.cache-complete` receipt is a half-written copy, not an import.
1599- const cached = join ( fixture . home , '.cursor' , 'plugins' , 'cache' , 'doctor-fixture-marketplace' , 'doctor-fixture' , commit ) ;
1608+ const cacheRoot = join ( fixture . home , '.cursor' , 'plugins' , 'cache' , 'doctor-fixture-marketplace' , 'doctor-fixture' ) ;
1609+ const cached = join ( cacheRoot , commit ) ;
16001610 await writeJson ( join ( cached , '.cursor-plugin' , 'plugin.json' ) , { name : 'doctor-fixture' , version : '1.2.3' } ) ;
16011611 const incomplete = await doctor ( ) ;
16021612 expect ( hostReport ( incomplete , 'cursor' ) . inventory . findings ) . toEqual ( [ { ...stagedFinding , state : 'unregistered' } ] ) ;
16031613
1614+ // A receipted copy from an earlier staging commit, or a malformed cache entry, does not prove this commit was imported.
1615+ const previous = join ( cacheRoot , 'b' . repeat ( 40 ) ) ;
1616+ await writeJson ( join ( previous , '.cursor-plugin' , 'plugin.json' ) , { name : 'doctor-fixture' , version : '1.2.3' } ) ;
1617+ await writeFile ( join ( previous , '.cache-complete' ) , '' ) ;
1618+ await writeFile ( join ( cacheRoot , 'not-a-directory' ) , 'stray' ) ;
1619+ const staleReceipt = await doctor ( ) ;
1620+ expect ( hostReport ( staleReceipt , 'cursor' ) . inventory . findings ) . toEqual ( [ { ...stagedFinding , state : 'unregistered' } ] ) ;
1621+
16041622 await writeFile ( join ( cached , '.cache-complete' ) , '' ) ;
16051623 const imported = await doctor ( ) ;
16061624 expect ( hostReport ( imported , 'cursor' ) . inventory . findings ) . toEqual ( [ { ...stagedFinding , state : 'registered' } ] ) ;
@@ -1627,6 +1645,19 @@ it('tracks staged Cursor marketplaces from staged to imported', async () => {
16271645 const corruptBundle = await doctor ( ) ;
16281646 expect ( hostReport ( corruptBundle , 'cursor' ) . bundle ?. state ) . toBe ( 'corrupt' ) ;
16291647
1648+ // A plugin manifest naming a different plugin than the entry is corrupt even with a valid marketplace entry.
1649+ await writeJson ( join ( repo , '.cursor-plugin/marketplace.json' ) , {
1650+ name : 'doctor-fixture-marketplace' ,
1651+ owner : { name : 'doctor-fixture' } ,
1652+ plugins : [ { name : 'doctor-fixture' , source : 'plugins/doctor-fixture' } ] ,
1653+ } ) ;
1654+ const stagedManifest = await readFile ( join ( repo , 'plugins' , 'doctor-fixture' , '.cursor-plugin' , 'plugin.json' ) , 'utf8' ) ;
1655+ await writeJson ( join ( repo , 'plugins' , 'doctor-fixture' , '.cursor-plugin' , 'plugin.json' ) , { name : 'someone-else' , version : '1.2.3' } ) ;
1656+ const misnamed = await doctor ( ) ;
1657+ expect ( hostReport ( misnamed , 'cursor' ) . inventory . findings ) . toEqual ( [ expect . objectContaining ( { entry : 'doctor-fixture' , state : 'corrupt' } ) ] ) ;
1658+ expect ( misnamed . diagnostics . filter ( ( entry ) => entry . code === 'AB7323' ) [ 0 ] ?. message ) . toContain ( 'not named doctor-fixture' ) ;
1659+ await writeFile ( join ( repo , 'plugins' , 'doctor-fixture' , '.cursor-plugin' , 'plugin.json' ) , stagedManifest ) ;
1660+
16301661 await rm ( join ( repo , '.git' ) , { recursive : true } ) ;
16311662 const corrupt = await doctor ( ) ;
16321663 expect ( hostReport ( corrupt , 'cursor' ) . inventory . findings ) . toEqual ( [ expect . objectContaining ( { entry : 'doctor-fixture' , state : 'corrupt' } ) ] ) ;
0 commit comments