Skip to content

Commit f143ef5

Browse files
aksOpsclaude
andcommitted
refactor: serve is read-only — remove auto-enrich, use CLI pipeline
Pipeline is now strictly: code-iq index → H2 (memory-efficient batched scanning) code-iq enrich → Neo4j (linkers, layers, services, bulk-load) code-iq serve → read-only server (API, MCP, UI) serve no longer runs analysis or loads data into Neo4j. It expects enrich to have already populated Neo4j. Reports graph status on startup and warns if Neo4j is empty. This matches the deployment model: index+enrich on developer machine, serve on remote server from pre-built graph. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 4ec4134 commit f143ef5

1 file changed

Lines changed: 26 additions & 104 deletions

File tree

Lines changed: 26 additions & 104 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,10 @@
11
package 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;
63
import io.github.randomcodespace.iq.config.CodeIqConfig;
74
import io.github.randomcodespace.iq.graph.GraphStore;
85
import org.slf4j.Logger;
96
import org.slf4j.LoggerFactory;
107
import org.springframework.beans.factory.annotation.Autowired;
11-
import org.springframework.cache.CacheManager;
128
import org.springframework.stereotype.Component;
139
import picocli.CommandLine.Command;
1410
import picocli.CommandLine.Option;
@@ -17,19 +13,15 @@
1713
import java.nio.file.Path;
1814
import java.text.NumberFormat;
1915
import java.util.Locale;
20-
import java.util.Optional;
2116
import 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

Comments
 (0)