@@ -1486,12 +1486,54 @@ static char *handle_get_impact(cbm_mcp_server_t *srv, const char *args) {
14861486static char * handle_get_channels (cbm_mcp_server_t * srv , const char * args ) {
14871487 char * project = cbm_mcp_get_string_arg (args , "project" );
14881488 char * channel = cbm_mcp_get_string_arg (args , "channel" );
1489- cbm_store_t * store = resolve_store (srv , project );
1490- REQUIRE_STORE (store , project );
14911489
1490+ /* Cross-repo channel query: when project is NULL, iterate all indexed projects */
14921491 cbm_channel_info_t * channels = NULL ;
14931492 int count = 0 ;
1494- cbm_store_find_channels (store , project , channel , & channels , & count );
1493+
1494+ if (!project || strlen (project ) == 0 ) {
1495+ char dir_path [1024 ];
1496+ cache_dir (dir_path , sizeof (dir_path ));
1497+ cbm_dir_t * d = cbm_opendir (dir_path );
1498+ if (d ) {
1499+ cbm_dirent_t * entry ;
1500+ while ((entry = cbm_readdir (d )) != NULL ) {
1501+ const char * n = entry -> name ;
1502+ size_t len = strlen (n );
1503+ if (len < 4 || strcmp (n + len - 3 , ".db" ) != 0 ) continue ;
1504+ if (strncmp (n , "tmp-" , 4 ) == 0 || strncmp (n , "_" , 1 ) == 0 ) continue ;
1505+
1506+ /* Extract project name (filename without .db) */
1507+ char proj_name [512 ];
1508+ snprintf (proj_name , sizeof (proj_name ), "%.*s" , (int )(len - 3 ), n );
1509+
1510+ /* Open this project's store and query channels */
1511+ char db_path [2048 ];
1512+ snprintf (db_path , sizeof (db_path ), "%s/%s" , dir_path , n );
1513+ cbm_store_t * ps = cbm_store_open_path_query (db_path );
1514+ if (!ps ) continue ;
1515+
1516+ cbm_channel_info_t * proj_ch = NULL ;
1517+ int proj_count = 0 ;
1518+ cbm_store_find_channels (ps , proj_name , channel , & proj_ch , & proj_count );
1519+
1520+ if (proj_count > 0 ) {
1521+ /* Merge into main results */
1522+ channels = safe_realloc (channels ,
1523+ (count + proj_count ) * sizeof (cbm_channel_info_t ));
1524+ memcpy (channels + count , proj_ch , proj_count * sizeof (cbm_channel_info_t ));
1525+ count += proj_count ;
1526+ free (proj_ch ); /* shallow free — info fields now owned by channels[] */
1527+ }
1528+ cbm_store_close (ps );
1529+ }
1530+ cbm_closedir (d );
1531+ }
1532+ } else {
1533+ cbm_store_t * store = resolve_store (srv , project );
1534+ REQUIRE_STORE (store , project );
1535+ cbm_store_find_channels (store , project , channel , & channels , & count );
1536+ }
14951537
14961538 yyjson_mut_doc * doc = yyjson_mut_doc_new (NULL );
14971539 yyjson_mut_val * root = yyjson_mut_obj (doc );
@@ -2165,6 +2207,29 @@ static char *handle_trace_call_path(cbm_mcp_server_t *srv, const char *args) {
21652207 yyjson_mut_obj_add_val (doc , outgoing , "has_method" , methods_arr );
21662208 }
21672209
2210+ /* Outgoing HAS_PROPERTY (for Classes — class properties). */
2211+ {
2212+ int saved_tr = tr_count ;
2213+ if (is_class_like && tr_count < MAX_TR ) {
2214+ const char * hp_types [] = {"HAS_PROPERTY" };
2215+ cbm_store_bfs (store , nodes [best_idx ].id , "outbound" , hp_types , 1 , 1 , 30 ,
2216+ & all_tr [tr_count ]);
2217+ tr_count ++ ;
2218+ }
2219+ yyjson_mut_val * props_arr = yyjson_mut_arr (doc );
2220+ for (int t = saved_tr ; t < tr_count ; t ++ ) {
2221+ for (int i = 0 ; i < all_tr [t ].visited_count ; i ++ ) {
2222+ cbm_node_t * vn = & all_tr [t ].visited [i ].node ;
2223+ yyjson_mut_val * item = yyjson_mut_obj (doc );
2224+ yyjson_mut_obj_add_str (doc , item , "name" , vn -> name ? vn -> name : "" );
2225+ yyjson_mut_obj_add_str (doc , item , "file_path" , vn -> file_path ? vn -> file_path : "" );
2226+ yyjson_mut_obj_add_int (doc , item , "line" , vn -> start_line );
2227+ yyjson_mut_arr_add_val (props_arr , item );
2228+ }
2229+ }
2230+ yyjson_mut_obj_add_val (doc , outgoing , "has_property" , props_arr );
2231+ }
2232+
21682233 /* Outgoing INHERITS (what this extends) */
21692234 {
21702235 int saved_tr = tr_count ;
0 commit comments