2026-03-13 16:26:07 -07:00
import { promises as fs } from "node:fs" ;
import path from "node:path" ;
import { fileURLToPath } from "node:url" ;
const __dirname = path . dirname ( fileURLToPath ( import . meta . url ));
const projectRoot = path . resolve ( __dirname , ".." );
const rules = [
{
root : "src-tauri/src" ,
extensions : new Set ([ ".rs" ]),
maxLines : 500 ,
},
{
root : "src/app" ,
extensions : new Set ([ ".ts" , ".tsx" ]),
maxLines : 500 ,
},
{
root : "src/features" ,
extensions : new Set ([ ".ts" , ".tsx" ]),
maxLines : 500 ,
},
{
root : "src/shared/api" ,
extensions : new Set ([ ".ts" , ".tsx" ]),
maxLines : 500 ,
},
];
// Exceptions should stay rare and temporary. Prefer splitting files instead.
const overrides = new Map ([
2026-04-11 13:32:14 -04:00
[ "src-tauri/src/managed_agents/personas.rs" , 830 ], // built-in persona system prompts + persona pack import/uninstall/list + uninstall safety check
[ "src-tauri/src/managed_agents/persona_card.rs" , 970 ], // PNG/ZIP/MD persona card codec + pack-zip detection + nested root finder + provider/model/namePool fields + 27 unit tests
2026-04-03 12:17:51 -07:00
[ "src/app/AppShell.tsx" , 860 ], // message edit state + handlers + ChannelPane edit prop threading + scrollback pagination + workflows view + memory-leak safeguards
2026-03-22 09:00:45 -07:00
[ "src/features/channels/hooks.ts" , 550 ], // canvas query + mutation hooks + DM hide mutation
2026-03-13 16:26:07 -07:00
[ "src/features/channels/ui/ChannelManagementSheet.tsx" , 800 ],
2026-04-02 15:16:40 -07:00
[ "src/features/messages/hooks.ts" , 500 ], // message query/mutation hooks + optimistic updates
2026-03-25 14:04:57 -07:00
[ "src/features/messages/ui/MessageComposer.tsx" , 700 ], // media upload handlers (paste, drop, dialog) + channelId reset effect + edit mode (pre-fill, save, cancel, escape)
2026-03-13 16:26:07 -07:00
[ "src/features/settings/ui/SettingsView.tsx" , 600 ],
2026-04-10 17:09:38 -07:00
[ "src/features/sidebar/ui/AppSidebar.tsx" , 860 ], // channels + forums creation forms + Pulse nav
2026-03-13 16:26:07 -07:00
[ "src/features/tokens/ui/TokenSettingsCard.tsx" , 800 ],
2026-04-13 07:46:44 -04:00
[ "src/shared/api/relayClientSession.ts" , 805 ], // durable websocket session manager with reconnect/replay/recovery state + sendTypingIndicator (optional e tag) + fetchChannelHistoryBefore
2026-03-20 20:32:55 -04:00
[ "src/shared/api/tauri.ts" , 1100 ], // remote agent provider API bindings + canvas API functions
2026-04-11 13:32:14 -04:00
[ "src-tauri/src/lib.rs" , 570 ], // sprout-media:// proxy + Range headers + Sprout nest init (ensure_nest) in setup() + persona pack commands
2026-04-10 12:23:29 -04:00
[ "src-tauri/src/commands/media.rs" , 720 ], // ffmpeg video transcode + poster frame extraction + run_ffmpeg_with_timeout (find_ffmpeg, is_video_file, transcode_to_mp4, extract_poster_frame, transcode_and_extract_poster) + spawn_blocking wrappers + tests
2026-04-11 13:32:14 -04:00
[ "src-tauri/src/commands/agents.rs" , 880 ], // remote agent lifecycle routing (local + provider branches) + scope enforcement + persona pack metadata wiring + mcp_toolsets field
[ "src-tauri/src/managed_agents/runtime.rs" , 690 ], // KNOWN_AGENT_BINARIES const + process_belongs_to_us FFI (macOS proc_name + Linux /proc/comm) + terminate_process + start/stop/sync lifecycle + pack persona live-read
2026-03-20 20:32:55 -04:00
[ "src-tauri/src/managed_agents/backend.rs" , 530 ], // provider IPC, validation, discovery, binary resolution + tests
2026-04-08 21:04:09 -07:00
[ "src/features/agents/hooks.ts" , 520 ], // agent query/mutation surface now includes built-in persona library activation
2026-04-10 07:17:19 -10:00
[ "src/features/agents/ui/AgentsView.tsx" , 880 ], // remote agent lifecycle controls + persona/team management + persona import-update dialog wiring + built-in catalog/library state orchestration
[ "src/features/agents/ui/TeamDialog.tsx" , 530 ], // team create/edit dialog with persona multi-select, import button, window drag detection, removal confirmation
[ "src/features/agents/ui/TeamImportUpdateDialog.tsx" , 660 ], // team import diff preview with member matching/updating/adding/removing sections, LCS line counts, removal confirmation
[ "src/features/agents/ui/useTeamActions.ts" , 510 ], // team CRUD + export + import + import-update orchestration with query invalidation
2026-03-20 20:32:55 -04:00
[ "src/features/agents/ui/CreateAgentDialog.tsx" , 685 ], // provider selector + config form + schema-typed config coercion + required field validation + locked scopes
2026-03-24 13:02:20 -07:00
[ "src/features/channels/ui/AddChannelBotDialog.tsx" , 640 ], // provider mode: Run on selector, trust warning, probe effect, single-agent enforcement, provider warnings display
2026-04-13 07:46:44 -04:00
[ "src/features/forum/ui/ForumThreadPanel.tsx" , 520 ], // two-column thread + branch panel + scoped typing hook wiring
2026-04-11 13:32:14 -04:00
[ "src/shared/api/types.ts" , 540 ], // persona provider/model fields + forum types + workflow type re-exports + ephemeral channel TTL fields + mcpToolsets + sourcePack
2026-03-13 16:26:07 -07:00
]);
async function walkFiles ( directory ) {
const entries = await fs . readdir ( directory , { withFileTypes : true });
const files = await Promise . all (
entries . map ( async ( entry ) => {
const fullPath = path . join ( directory , entry . name );
if ( entry . isDirectory ()) {
return walkFiles ( fullPath );
}
return [ fullPath ];
}),
);
return files . flat ();
}
function findRule ( relativePath ) {
return rules . find (( rule ) => {
const normalizedRoot = ` ${ rule . root }${ path . sep } ` ;
return relativePath . startsWith ( normalizedRoot );
});
}
function countLines ( content ) {
if ( content . length === 0 ) {
return 0 ;
}
return content . split ( /\r?\n/ ). length ;
}
const candidateFiles = (
await Promise . all (
rules . map (( rule ) => walkFiles ( path . join ( projectRoot , rule . root ))),
)
). flat ();
const violations = [];
for ( const filePath of candidateFiles ) {
const relativePath = path . relative ( projectRoot , filePath );
const rule = findRule ( relativePath );
if ( ! rule ) {
continue ;
}
const extension = path . extname ( relativePath );
if ( ! rule . extensions . has ( extension )) {
continue ;
}
const limit = overrides . get ( relativePath ) ?? rule . maxLines ;
const content = await fs . readFile ( filePath , "utf8" );
const lineCount = countLines ( content );
if ( lineCount > limit ) {
violations . push ({
limit ,
lineCount ,
relativePath ,
});
}
}
if ( violations . length > 0 ) {
console . error ( "Desktop file size check failed:" );
for ( const violation of violations ) {
console . error (
`- ${ violation . relativePath } : ${ violation . lineCount } lines (limit ${ violation . limit } )` ,
);
}
console . error (
"Split the file or add a narrowly scoped exception in `desktop/scripts/check-file-sizes.mjs`." ,
);
process . exit ( 1 );
}