@@ -13,6 +13,7 @@ import {
1313 runGitDiff ,
1414 runVcsDiff ,
1515 stageFile ,
16+ startPlanReviewServer ,
1617 startReviewServer ,
1718 unstageFile ,
1819} from "./server" ;
@@ -32,6 +33,28 @@ function makeTempDir(prefix: string): string {
3233 return dir ;
3334}
3435
36+ function writeTempFile ( root : string , relativePath : string , content = "x" ) : string {
37+ const full = join ( root , relativePath ) ;
38+ mkdirSync ( join ( full , ".." ) , { recursive : true } ) ;
39+ writeFileSync ( full , content , "utf-8" ) ;
40+ return full ;
41+ }
42+
43+ interface PiTreeNode {
44+ path : string ;
45+ type : "file" | "folder" ;
46+ children ?: PiTreeNode [ ] ;
47+ }
48+
49+ function flattenTree ( nodes : PiTreeNode [ ] ) : string [ ] {
50+ const paths : string [ ] = [ ] ;
51+ for ( const node of nodes ) {
52+ if ( node . type === "file" ) paths . push ( node . path ) ;
53+ else paths . push ( ...flattenTree ( node . children ?? [ ] ) ) ;
54+ }
55+ return paths ;
56+ }
57+
3558function childEnv ( ) : NodeJS . ProcessEnv {
3659 return { ...process . env } ;
3760}
@@ -964,3 +987,48 @@ describe("pi review server", () => {
964987 }
965988 } , 20_000 ) ;
966989} ) ;
990+
991+ describe ( "pi plan server file browser" , ( ) => {
992+ test ( "filters excluded folders from tree and workspace status" , async ( ) => {
993+ const repo = makeTempDir ( "plannotator-pi-files-" ) ;
994+ const dataDir = makeTempDir ( "plannotator-pi-files-data-" ) ;
995+ process . env . PLANNOTATOR_DATA_DIR = dataDir ;
996+ process . env . PLANNOTATOR_PORT = String ( await reservePort ( ) ) ;
997+ process . chdir ( repo ) ;
998+
999+ git ( repo , [ "init" ] ) ;
1000+ git ( repo , [ "branch" , "-M" , "main" ] ) ;
1001+ git ( repo , [ "config" , "user.email" , "pi-files@example.com" ] ) ;
1002+ git ( repo , [ "config" , "user.name" , "Pi Files" ] ) ;
1003+ writeTempFile ( repo , "docs/visible.md" , "visible\n" ) ;
1004+ writeTempFile ( repo , "dist/generated.md" , "before\n" ) ;
1005+ git ( repo , [ "add" , "-A" ] ) ;
1006+ git ( repo , [ "commit" , "-m" , "initial" ] ) ;
1007+
1008+ writeTempFile ( repo , "dist/generated.md" , "after\n" ) ;
1009+ writeTempFile ( repo , "packages/app/node_modules/pkg/readme.md" , "hidden\n" ) ;
1010+
1011+ const server = await startPlanReviewServer ( {
1012+ plan : "# Plan" ,
1013+ origin : "pi" ,
1014+ htmlContent : "<!doctype html><html><body>plan</body></html>" ,
1015+ } ) ;
1016+
1017+ try {
1018+ const url = new URL ( `${ server . url } /api/reference/files` ) ;
1019+ url . searchParams . set ( "dirPath" , repo ) ;
1020+ const response = await fetch ( url ) ;
1021+ const payload = await response . json ( ) as {
1022+ tree : PiTreeNode [ ] ;
1023+ workspaceStatus : { totals : { files : number } ; files : Record < string , unknown > } ;
1024+ } ;
1025+
1026+ expect ( response . status ) . toBe ( 200 ) ;
1027+ expect ( flattenTree ( payload . tree ) ) . toEqual ( [ "docs/visible.md" ] ) ;
1028+ expect ( payload . workspaceStatus . totals . files ) . toBe ( 0 ) ;
1029+ expect ( payload . workspaceStatus . files ) . toEqual ( { } ) ;
1030+ } finally {
1031+ server . stop ( ) ;
1032+ }
1033+ } ) ;
1034+ } ) ;
0 commit comments