4343#include <sqlite3.h>
4444#include "cypher/cypher.h"
4545#include "pipeline/pipeline.h"
46+ #include "pipeline/pass_cross_repo.h"
4647#include "cli/cli.h"
4748#include "watcher/watcher.h"
4849#include "foundation/mem.h"
@@ -257,13 +258,21 @@ typedef struct {
257258} tool_def_t ;
258259
259260static const tool_def_t TOOLS [] = {
260- {"index_repository" , "Index a repository into the knowledge graph" ,
261+ {"index_repository" ,
262+ "Index a repository into the knowledge graph. "
263+ "Special mode 'cross-repo-intelligence': skip extraction, only match Routes/Channels "
264+ "across projects to create CROSS_HTTP_CALLS/CROSS_ASYNC_CALLS/CROSS_CHANNEL edges. "
265+ "Requires target_projects param. Ensure target projects have fresh indexes first." ,
261266 "{\"type\":\"object\",\"properties\":{\"repo_path\":{\"type\":\"string\",\"description\":"
262- "\"Path to the "
263- "repository\"},\"mode\":{\"type\":\"string\",\"enum\":[\"full\",\"moderate\",\"fast\"],"
264- "\"default\":\"full\",\"description\":\"full: all passes including semantic edges. "
265- "moderate: fast discovery + SIMILAR_TO + SEMANTICALLY_RELATED. fast: structure only."
266- "\"}},\"required\":[\"repo_path\"]}" },
267+ "\"Path to the repository\"},"
268+ "\"mode\":{\"type\":\"string\","
269+ "\"enum\":[\"full\",\"moderate\",\"fast\",\"cross-repo-intelligence\"],"
270+ "\"default\":\"full\",\"description\":\"full: all passes. moderate: fast + semantic. "
271+ "fast: structure only. cross-repo-intelligence: match Routes/Channels across projects.\"},"
272+ "\"target_projects\":{\"type\":\"array\",\"items\":{\"type\":\"string\"},"
273+ "\"description\":\"Projects to search for cross-repo links (cross-repo-intelligence mode). "
274+ "Use [\\\"*\\\"] for all indexed projects. Run list_projects to see available projects.\"}"
275+ "},\"required\":[\"repo_path\"]}" },
267276
268277 {"search_graph" ,
269278 "Search the code knowledge graph for functions, classes, routes, and variables. Use INSTEAD "
@@ -2042,6 +2051,62 @@ static char *get_project_root(cbm_mcp_server_t *srv, const char *project) {
20422051
20432052/* ── index_repository ─────────────────────────────────────────── */
20442053
2054+ /* Handle mode="cross-repo-intelligence" — extract to reduce complexity. */
2055+ static char * handle_cross_repo_mode (const char * repo_path , const char * args ) {
2056+ char * project = heap_strdup (cbm_project_name_from_path (repo_path ));
2057+ if (!project ) {
2058+ return cbm_mcp_text_result ("cannot derive project name" , true);
2059+ }
2060+
2061+ yyjson_doc * jdoc = yyjson_read (args , strlen (args ), 0 );
2062+ yyjson_val * jroot = jdoc ? yyjson_doc_get_root (jdoc ) : NULL ;
2063+ yyjson_val * tp_arr = jroot ? yyjson_obj_get (jroot , "target_projects" ) : NULL ;
2064+
2065+ if (!tp_arr || !yyjson_is_arr (tp_arr ) || yyjson_arr_size (tp_arr ) == 0 ) {
2066+ yyjson_doc_free (jdoc );
2067+ free (project );
2068+ return cbm_mcp_text_result (
2069+ "{\"error\":\"target_projects is required for cross-repo-intelligence mode. "
2070+ "Use [\\\"*\\\"] for all projects. Run list_projects to see available.\"}" ,
2071+ true);
2072+ }
2073+
2074+ int tp_count = (int )yyjson_arr_size (tp_arr );
2075+ const char * * targets = malloc ((size_t )tp_count * sizeof (char * ));
2076+ size_t idx ;
2077+ size_t max ;
2078+ yyjson_val * val ;
2079+ int ti = 0 ;
2080+ yyjson_arr_foreach (tp_arr , idx , max , val ) {
2081+ targets [ti ++ ] = yyjson_get_str (val );
2082+ }
2083+
2084+ cbm_cross_repo_result_t result = cbm_cross_repo_match (project , targets , tp_count );
2085+ free (targets );
2086+ yyjson_doc_free (jdoc );
2087+
2088+ int total = result .http_edges + result .async_edges + result .channel_edges ;
2089+ yyjson_mut_doc * doc = yyjson_mut_doc_new (NULL );
2090+ yyjson_mut_val * root = yyjson_mut_obj (doc );
2091+ yyjson_mut_doc_set_root (doc , root );
2092+ yyjson_mut_obj_add_str (doc , root , "status" , "success" );
2093+ yyjson_mut_obj_add_str (doc , root , "mode" , "cross-repo-intelligence" );
2094+ yyjson_mut_obj_add_strcpy (doc , root , "project" , project );
2095+ yyjson_mut_obj_add_int (doc , root , "projects_scanned" , result .projects_scanned );
2096+ yyjson_mut_obj_add_int (doc , root , "cross_http_calls" , result .http_edges );
2097+ yyjson_mut_obj_add_int (doc , root , "cross_async_calls" , result .async_edges );
2098+ yyjson_mut_obj_add_int (doc , root , "cross_channel" , result .channel_edges );
2099+ yyjson_mut_obj_add_int (doc , root , "total_cross_edges" , total );
2100+ yyjson_mut_obj_add_real (doc , root , "elapsed_ms" , result .elapsed_ms );
2101+
2102+ char * json = yy_doc_to_str (doc );
2103+ yyjson_mut_doc_free (doc );
2104+ free (project );
2105+ char * out = cbm_mcp_text_result (json , false);
2106+ free (json );
2107+ return out ;
2108+ }
2109+
20452110static char * handle_index_repository (cbm_mcp_server_t * srv , const char * args ) {
20462111 char * repo_path = cbm_mcp_get_string_arg (args , "repo_path" );
20472112 char * mode_str = cbm_mcp_get_string_arg (args , "mode" );
@@ -2052,6 +2117,13 @@ static char *handle_index_repository(cbm_mcp_server_t *srv, const char *args) {
20522117 return cbm_mcp_text_result ("repo_path is required" , true);
20532118 }
20542119
2120+ if (mode_str && strcmp (mode_str , "cross-repo-intelligence" ) == 0 ) {
2121+ free (mode_str );
2122+ char * result = handle_cross_repo_mode (repo_path , args );
2123+ free (repo_path );
2124+ return result ;
2125+ }
2126+
20552127 cbm_index_mode_t mode = CBM_MODE_FULL ;
20562128 if (mode_str && strcmp (mode_str , "fast" ) == 0 ) {
20572129 mode = CBM_MODE_FAST ;
0 commit comments