11package io .github .randomcodespace .iq .cli ;
22
3- import io .github .randomcodespace .iq .analyzer .AnalysisResult ;
4- import io .github .randomcodespace .iq .analyzer .Analyzer ;
5- import io .github .randomcodespace .iq .cache .AnalysisCache ;
63import io .github .randomcodespace .iq .config .CodeIqConfig ;
74import io .github .randomcodespace .iq .graph .GraphStore ;
85import org .slf4j .Logger ;
96import org .slf4j .LoggerFactory ;
107import org .springframework .beans .factory .annotation .Autowired ;
11- import org .springframework .cache .CacheManager ;
128import org .springframework .stereotype .Component ;
139import picocli .CommandLine .Command ;
1410import picocli .CommandLine .Option ;
1713import java .nio .file .Path ;
1814import java .text .NumberFormat ;
1915import java .util .Locale ;
20- import java .util .Optional ;
2116import java .util .concurrent .Callable ;
2217
2318/**
2419 * Start the web UI + REST API + MCP server.
25- *
26- * This command signals to the application that it should keep running
27- * (the web server is already started by Spring Boot when the "serving" profile
28- * is active). The serve command simply prints server info and blocks until
29- * the Spring context shuts down.
30- *
31- * On startup, if the Neo4j graph is empty but an H2 analysis cache exists,
32- * the command auto-loads data from H2 into Neo4j (like a mini enrich).
20+ * <p>
21+ * This command expects Neo4j to already be populated by {@code enrich}.
22+ * It simply starts the Spring Boot web server and blocks until shutdown.
23+ * <p>
24+ * Pipeline: {@code index} → {@code enrich} → {@code serve}
3325 */
3426@ Component
3527@ Command (name = "serve" , mixinStandardHelpOptions = true ,
@@ -38,7 +30,6 @@ public class ServeCommand implements Callable<Integer> {
3830
3931 private static final Logger log = LoggerFactory .getLogger (ServeCommand .class );
4032
41- /** Marker flag — checked by CodeIqApplication to activate serving profile. */
4233 public static final String COMMAND_NAME = "serve" ;
4334
4435 @ Parameters (index = "0" , defaultValue = "." , description = "Path to analyzed codebase" )
@@ -56,40 +47,42 @@ public class ServeCommand implements Callable<Integer> {
5647 @ Autowired
5748 private CodeIqConfig config ;
5849
59- @ Autowired
60- private Analyzer analyzer ;
61-
6250 @ Autowired (required = false )
6351 private GraphStore graphStore ;
6452
65- @ Autowired (required = false )
66- private CacheManager cacheManager ;
67-
6853 @ Override
6954 public Integer call () {
7055 Path root = path .toAbsolutePath ().normalize ();
71-
72- // Update config with the root path so controllers can find it
7356 config .setRootPath (root .toString ());
57+ NumberFormat nf = NumberFormat .getIntegerInstance (Locale .US );
7458
75- // Auto-enrich: if Neo4j is empty, run full analysis to populate it
76- autoEnrich (root );
59+ // Report Neo4j graph status
60+ if (graphStore != null ) {
61+ try {
62+ long nodeCount = graphStore .count ();
63+ long edgeCount = graphStore .countEdges ();
64+ if (nodeCount > 0 ) {
65+ CliOutput .success ("Neo4j graph: " + nf .format (nodeCount ) + " nodes, "
66+ + nf .format (edgeCount ) + " edges" );
67+ } else {
68+ CliOutput .warn ("Neo4j graph is empty. Run 'code-iq enrich " + root + "' to populate." );
69+ }
70+ } catch (Exception e ) {
71+ log .debug ("Could not check Neo4j state" , e );
72+ CliOutput .warn ("Could not read Neo4j graph. Run 'code-iq enrich' first." );
73+ }
74+ }
7775
7876 CliOutput .step ("\uD83D \uDE80 " , "@|bold,green Server started|@" );
7977 System .out .println ();
8078 CliOutput .info (" URL: http://" + host + ":" + port );
8179 CliOutput .info (" REST API: http://" + host + ":" + port + "/api" );
8280 CliOutput .info (" MCP: http://" + host + ":" + port + "/mcp" );
8381 CliOutput .info (" Health: http://" + host + ":" + port + "/actuator/health" );
84- CliOutput .info (" API Docs: http://" + host + ":" + port + "/docs" );
8582 CliOutput .info (" Codebase: " + root );
8683 System .out .println ();
8784 CliOutput .info ("Press Ctrl+C to stop." );
8885
89- // The Spring Boot web server is already running. We block here
90- // to prevent the CommandLineRunner from returning (which would
91- // trigger application shutdown). The JVM shutdown hook will
92- // handle cleanup.
9386 try {
9487 Thread .currentThread ().join ();
9588 } catch (InterruptedException e ) {
@@ -99,79 +92,8 @@ public Integer call() {
9992 return 0 ;
10093 }
10194
102- /**
103- * Auto-enrich Neo4j on startup: if the graph is empty, run the full analysis
104- * pipeline to populate Neo4j from the codebase. This ensures the API/MCP
105- * endpoints return data immediately without requiring a manual POST /api/analyze.
106- */
107- private void autoEnrich (Path root ) {
108- // Check if Neo4j already has data
109- boolean neo4jEmpty = true ;
110- if (graphStore != null ) {
111- try {
112- neo4jEmpty = graphStore .count () == 0 ;
113- } catch (Exception e ) {
114- log .debug ("Could not check Neo4j state" , e );
115- }
116- }
117-
118- if (!neo4jEmpty ) {
119- long nodeCount = graphStore .count ();
120- long edgeCount = graphStore .countEdges ();
121- CliOutput .success ("Neo4j graph loaded: " + nodeCount + " nodes, " + edgeCount + " edges" );
122- return ;
123- }
124-
125- // Neo4j is empty — run analysis to populate it
126- NumberFormat nf = NumberFormat .getIntegerInstance (Locale .US );
127- CliOutput .step ("\u26A1 " , "Auto-enriching Neo4j graph..." );
128-
129- try {
130- AnalysisResult result = analyzer .run (root , null , true , msg -> {
131- if (msg .startsWith ("Discovering" ) || msg .startsWith ("Found" ) || msg .startsWith ("Analyzing" )
132- || msg .startsWith ("Building" ) || msg .startsWith ("Linking" ) || msg .startsWith ("Classifying" )
133- || msg .startsWith ("Analysis complete" )) {
134- CliOutput .info (" " + msg );
135- }
136- });
137-
138- // Persist to Neo4j
139- if (graphStore != null && result .nodes () != null && !result .nodes ().isEmpty ()) {
140- CliOutput .step ("\uD83D \uDCBE " , "Persisting " + nf .format (result .nodes ().size ()) + " nodes to Neo4j..." );
141- graphStore .bulkSave (result .nodes ());
142- }
143-
144- // Evict Spring caches
145- if (cacheManager != null ) {
146- cacheManager .getCacheNames ().forEach (name -> {
147- var cache = cacheManager .getCache (name );
148- if (cache != null ) cache .clear ();
149- });
150- }
151-
152- CliOutput .success ("Graph ready: " + nf .format (result .nodeCount ()) + " nodes, "
153- + nf .format (result .edgeCount ()) + " edges from "
154- + nf .format (result .filesAnalyzed ()) + " files" );
155- } catch (Exception e ) {
156- log .error ("Auto-enrich failed" , e );
157- CliOutput .error ("Auto-enrich failed: " + e .getMessage ());
158- CliOutput .info (" You can manually trigger analysis via POST /api/analyze" );
159- }
160- }
161-
162- public Path getPath () {
163- return path ;
164- }
165-
166- public int getPort () {
167- return port ;
168- }
169-
170- public String getHost () {
171- return host ;
172- }
173-
174- public Path getGraphPath () {
175- return graphPath ;
176- }
95+ public Path getPath () { return path ; }
96+ public int getPort () { return port ; }
97+ public String getHost () { return host ; }
98+ public Path getGraphPath () { return graphPath ; }
17799}
0 commit comments