Skip to content

Commit 65aac67

Browse files
aksOpsclaude
andauthored
chore(config): retire legacy ProjectConfigLoader static API (#52) (#52)
* feat(config): extend unified tree with detectors.categories, detectors.include, indexing.parsers Prep for Phase-2 retirement of the legacy ProjectConfigLoader static API. The Analyzer pipeline currently reads language/detector/parser/parallelism filters from the legacy ProjectConfig POJO via ProjectConfigLoader.loadProjectConfig; moving those call sites onto the injected CodeIqUnifiedConfig bean requires the unified tree to carry the same fields. This commit adds: - DetectorsConfig: categories, include (List<String>). empty() updated. - IndexingConfig: parsers (List<String>); parallelism changed from String to Integer with null = auto-detect. empty() updated. - UnifiedConfigLoader: reads detectors.categories/include (snake_case) with camelCase aliases detectorCategories/detectorInclude (deprecated, per-load WARN via existing pick() helper); reads indexing.parsers; parses parallelism as Integer via requireIntOrNull. - EnvVarOverlay: CODEIQ_DETECTORS_CATEGORIES, CODEIQ_DETECTORS_INCLUDE, CODEIQ_INDEXING_PARSERS (CSV); CODEIQ_INDEXING_PARALLELISM now parses to Integer and throws ConfigLoadException on malformed input. - ConfigMerger: merges the three new leaves with full provenance tracking. - ConfigValidator: indexing.parallelism must be > 0 if set (null = auto). - ConfigDefaults.builtIn(): parallelism = null (auto), parsers = [], detectors.categories = [], detectors.include = []. No behavior change on the default path. - ProjectConfigLoader.translateLegacyToUnified: maps the nested legacy shape (detectors.categories, detectors.include, parsers map, pipeline.parallelism) plus the flat top-level aliases detector_categories / detector_include into the new unified fields. parsers map is flattened to its values (Analyzer never consumed the per-language map at runtime). - docs/codeiq.yml.example: documents parsers, detectors.categories, and detectors.include; notes parallelism: null = auto. Test coverage (+16 cases): - UnifiedConfigLoaderTest: snake_case load, camelCase alias WARN, parallelism as Integer, malformed parallelism throws, missing detectors section = empty lists. - EnvVarOverlayTest: CSV parsing for the three new vars, Integer parallelism, malformed parallelism throws. - ConfigMergerTest: layer-replacement for categories/include/parsers, parallelism merge, provenance attribution. - ConfigValidatorTest: parallelism > 0 rejection + null-is-valid (auto). - ConfigDefaultsTest: baseline assertions for new defaults. Full suite: 3275 -> 3291 tests, 0 failures, 31 skipped. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * refactor(analyzer): drop ProjectConfigLoader reload, read filters from CodeIqUnifiedConfig Before: runWithCache/runBatchedWithCache/runAnalyzeBatched each called ProjectConfigLoader.loadProjectConfig(root) and re-parsed .osscodeiq.yml from disk to pull detector category/include filters, language filter, exclude patterns, and pipeline.parallelism. The unified config bean has already resolved these at startup (UnifiedConfigBeans.codeIqUnifiedConfig) so the per-call file I/O is pure waste. After: inject CodeIqUnifiedConfig into Analyzer and project the relevant leaves through a small private PipelineFilters record (categories, include, languages, exclude, parallelism). All three call sites now read filters from the injected tree; no file I/O, no legacy POJO, no static loader. Back-compat constructor (6-arg, used by 5 existing tests) preserved — defaults the unified overlay to CodeIqUnifiedConfig.empty(), matching the pre-Phase-B "no .osscodeiq.yml present" path (no filters, auto parallelism). SmartIndexTest.setUp updated to the new 9-arg primary constructor with CodeIqUnifiedConfig.empty() (no filter semantics — the test doesn't exercise filters). Full suite: 3291 tests, 0 failures, 31 skipped. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * refactor(cli): drop redundant ProjectConfigLoader reload in CliOutput CliOutput.configureFromOptions called ProjectConfigLoader.loadIfPresent to re-apply cache_dir / max_depth / max_radius from .osscodeiq.yml onto the injected CodeIqConfig bean. Those fields are already resolved at Spring startup by UnifiedConfigBeans.codeIqConfig (via ConfigResolver + UnifiedConfigAdapter), so the re-read was pure duplication. Removed the reload call. The `root` parameter became unused and was dropped from the method signature; two callers (AnalyzeCommand, IndexCommand) updated accordingly. Full suite: 3291 tests, 0 failures, 31 skipped. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * refactor(test): port ProjectConfigLoaderApplyOverridesTest to unified config pipeline The legacy static API (ProjectConfigLoader.loadIfPresent / loadProjectConfig / parseProjectConfig) is being removed in the next commit. This test file previously exercised those methods directly; all cases are rewritten to drive the same behaviour through the Phase-B canonical shim ProjectConfigLoader.loadFrom(Path), asserting on the returned CodeIqUnifiedConfig (and the projected CodeIqConfig via UnifiedConfigAdapter for legacy-POJO-shaped assertions). Ported cases (11): - legacy cache_dir/max_depth/max_radius flow through to CodeIqConfig. - legacy languages/detectors.categories/detectors.include/exclude/parsers/ pipeline sections populate CodeIqUnifiedConfig. - missing .osscodeiq.yml returns CodeIqUnifiedConfig.empty() with no deprecation warning emitted. - SafeConstructor rejects unsafe YAML tags without executing arbitrary code (either returns a safe representation or throws ConfigLoadException; no code execution is the invariant). Dropped cases (3): - nestedAnalysisSectionDoesNotCrash / nestedOutputSectionDoesNotCrash: exercised dead branches inside the legacy applyOverrides that are being deleted entirely. - invalidMaxDepthFallsBackToDefault: the legacy applyOverrides silently coerced non-numeric max_depth back to the default via its toInt helper. The unified loader is deliberately strict — malformed scalars throw ConfigLoadException with the field path. That behaviour change is already covered by UnifiedConfigLoaderTest; preserving the old lenient semantics would mask real misconfiguration. Full suite: 3291 -> 3288 tests (-3 net), 0 failures, 31 skipped. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * refactor(test): delete ConfigDrivenPipelineTest (superseded by unified-pipeline tests) ConfigDrivenPipelineTest exclusively exercised ProjectConfigLoader.parseProjectConfig(Map) and .loadProjectConfig(Path) — the deprecated static legacy-POJO parsers being deleted in the next commit. Every unique assertion has equivalent coverage on the unified pipeline: - languages/detectors.categories/detectors.include/exclude/parsers/pipeline map parsing: covered by UnifiedConfigLoaderTest (loadsDetectorsCategoriesAndInclude, loadsIndexingParsersList, loadsIndexingParallelismAsInteger, fullFileRoundTripsEveryField). - legacy .osscodeiq.yml end-to-end with flat keys: covered by ProjectConfigLoaderApplyOverridesTest (ported in the prior commit). - empty/missing-file semantics: covered by UnifiedConfigLoaderTest.missingFileProducesEmptyOverlay and ProjectConfigLoaderApplyOverridesTest.missingConfigFileReturnsEmptyOverlay. - ProjectConfig.empty() "no filters" assertion: equivalent to CodeIqUnifiedConfig.empty() behavior pinned in ConfigDefaultsTest. No unique coverage lost. Removing the file (not porting) keeps the test tree clean — parseProjectConfig is a private implementation detail of the legacy shim, not an API worth separate test coverage after migration. Full suite: 3288 -> 3277 tests (-11), 0 failures, 31 skipped. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * refactor(config): delete legacy static ProjectConfigLoader API + orphaned ProjectConfig POJO With Analyzer and CliOutput migrated off the legacy static API (prior two commits) and the legacy-only tests rewritten against loadFrom(Path) (prior two commits), the deprecated surface has no remaining callers. Removed from ProjectConfigLoader: - public static boolean loadIfPresent(Path, CodeIqConfig) - public static ProjectConfig loadProjectConfig(Path) - package-private static ProjectConfig parseProjectConfig(Map) - private static applyOverrides / toInt helpers (only used by loadIfPresent) - LEGACY_CONFIG_FILE_NAMES array (only used by the deleted statics; the new surface only honors codeiq.yml and .osscodeiq.yml). Inlined parseProjectConfig's logic directly into translateLegacyToUnified so the legacy ProjectConfig POJO has no remaining reference. The translator now reads languages/detectors/exclude/parsers/pipeline.* in place from the raw YAML map, without the intermediate POJO. Deleted: - src/main/java/io/github/randomcodespace/iq/config/ProjectConfig.java (orphaned after inlining) Kept: - ProjectConfigLoader#loadFrom(Path) — canonical Phase-B surface. - ProjectConfigLoader.LoadResult — returned record. - translateLegacyToUnified + readAndTranslateLegacy + countLegacyKeys — internal helpers for the .osscodeiq.yml deprecation shim. ProjectConfigLoaderTest trimmed: removed 5 tests that exclusively exercised the deleted legacy-file-name (.code-iq.yml/.yaml) paths plus the removed return-false-and-mutate-CodeIqConfig semantics. Ported 2 meaningful tests (empty-file handling, invalid-YAML resilience) onto loadFrom(Path); those pin safety behaviour of the deprecation shim. Full suite: 3277 -> 3272 tests (-5), 0 failures, 31 skipped. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 2292051 commit 65aac67

25 files changed

Lines changed: 612 additions & 824 deletions

docs/codeiq.yml.example

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,12 +39,13 @@ indexing:
3939
- '**/generated/**'
4040
incremental: true # reuse H2 cache when file hashes match
4141
cache_dir: .code-iq/cache # H2 analysis cache directory
42-
parallelism: auto # "auto" or a positive integer
42+
parallelism: null # null = auto-detect (Runtime.availableProcessors()); positive int to pin
4343
batch_size: 500 # files per H2 flush batch (default: 500)
4444
max_depth: 10 # max impact-trace depth
4545
max_radius: 10 # max ego-graph radius
4646
max_files: null # null = no cap; positive int to bound discovery
4747
max_snippet_lines: null # null = use CodeIqConfig default
48+
parsers: [] # parser-preference names (e.g. ["javaparser","antlr"]); empty = defaults
4849

4950
# ---------------------------------------------------------------------------
5051
# serving
@@ -92,6 +93,8 @@ observability:
9293
# ---------------------------------------------------------------------------
9394
detectors:
9495
profiles: [default] # named detector bundles to activate
96+
categories: [] # allow-list of detector categories (e.g. ["endpoints","entities"]); empty = all
97+
include: [] # allow-list of detector names (by Detector#getName()); empty = no name filter
9598
overrides: # per-detector feature flags, keyed by SimpleClassName
9699
SpringRestDetector: { enabled: true }
97100
QuarkusRestDetector: { enabled: true }

src/main/java/io/github/randomcodespace/iq/analyzer/Analyzer.java

Lines changed: 89 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,7 @@
66
import io.github.randomcodespace.iq.cache.FileHasher;
77
import io.github.randomcodespace.iq.cli.VersionCommand;
88
import io.github.randomcodespace.iq.config.CodeIqConfig;
9-
import io.github.randomcodespace.iq.config.ProjectConfig;
10-
import io.github.randomcodespace.iq.config.ProjectConfigLoader;
9+
import io.github.randomcodespace.iq.config.unified.CodeIqUnifiedConfig;
1110
import io.github.randomcodespace.iq.detector.AbstractAntlrDetector;
1211
import io.github.randomcodespace.iq.detector.Detector;
1312
import io.github.randomcodespace.iq.detector.DetectorContext;
@@ -88,9 +87,36 @@ public class Analyzer {
8887
private final LayerClassifier layerClassifier;
8988
private final List<Linker> linkers;
9089
private final CodeIqConfig config;
90+
private final CodeIqUnifiedConfig unifiedConfig;
9191
private final ConfigScanner configScanner;
9292
private final ArchitectureKeywordFilter keywordFilter;
9393

94+
/**
95+
* Projection of the injected {@link CodeIqUnifiedConfig} tree into the flat
96+
* shape the pipeline consumes: detector category/include filters, language +
97+
* exclude filters, and a parallelism override ({@code null} = auto-detect).
98+
*
99+
* <p>Lists are always non-null; an empty list means "no filter" (same
100+
* semantics as the pre-Phase-B legacy {@code ProjectConfig.empty()} path).
101+
*/
102+
private record PipelineFilters(
103+
List<String> categories,
104+
List<String> include,
105+
List<String> languages,
106+
List<String> exclude,
107+
Integer parallelism) {}
108+
109+
private PipelineFilters pipelineFilters() {
110+
var indexing = unifiedConfig.indexing();
111+
var detectors = unifiedConfig.detectors();
112+
return new PipelineFilters(
113+
detectors.categories() == null ? List.of() : detectors.categories(),
114+
detectors.include() == null ? List.of() : detectors.include(),
115+
indexing.languages() == null ? List.of() : indexing.languages(),
116+
indexing.exclude() == null ? List.of() : indexing.exclude(),
117+
indexing.parallelism());
118+
}
119+
94120
/** Primary constructor — used by Spring Boot dependency injection. */
95121
@Autowired
96122
public Analyzer(
@@ -100,6 +126,7 @@ public Analyzer(
100126
LayerClassifier layerClassifier,
101127
List<Linker> linkers,
102128
CodeIqConfig config,
129+
CodeIqUnifiedConfig unifiedConfig,
103130
ConfigScanner configScanner,
104131
ArchitectureKeywordFilter keywordFilter
105132
) {
@@ -109,11 +136,20 @@ public Analyzer(
109136
this.layerClassifier = layerClassifier;
110137
this.linkers = linkers;
111138
this.config = config;
139+
this.unifiedConfig = unifiedConfig;
112140
this.configScanner = configScanner;
113141
this.keywordFilter = keywordFilter;
114142
}
115143

116-
/** Backward-compatible constructor for tests that don't need smart indexing. */
144+
/**
145+
* Backward-compatible constructor for tests that don't need smart indexing.
146+
*
147+
* <p>Defaults the unified-config overlay to {@link CodeIqUnifiedConfig#empty()} —
148+
* equivalent to the pre-Phase-B "no {@code .osscodeiq.yml} present" path
149+
* (no detector filters, no language filter, auto parallelism). Tests that
150+
* need to exercise filters should use the primary constructor with a
151+
* hand-rolled {@link CodeIqUnifiedConfig}.
152+
*/
117153
public Analyzer(
118154
DetectorRegistry registry,
119155
StructuredParser parser,
@@ -123,6 +159,7 @@ public Analyzer(
123159
CodeIqConfig config
124160
) {
125161
this(registry, parser, fileDiscovery, layerClassifier, linkers, config,
162+
CodeIqUnifiedConfig.empty(),
126163
new ConfigScanner(), new ArchitectureKeywordFilter());
127164
}
128165

@@ -188,51 +225,49 @@ public AnalysisResult run(Path repoPath, Integer parallelism, boolean incrementa
188225

189226
private AnalysisResult runWithCache(Path root, Integer parallelism, AnalysisCache cache,
190227
Consumer<String> report, Instant start) {
191-
// 0. Load project config for pipeline filtering
192-
ProjectConfig projectConfig = ProjectConfigLoader.loadProjectConfig(root);
228+
// 0. Read pipeline filters from the injected unified config (single source of truth
229+
// resolved at startup by UnifiedConfigBeans — no per-call file I/O).
230+
PipelineFilters filters = pipelineFilters();
193231
DetectorRegistry effectiveRegistry = registry;
194232

195-
// Apply detector category filter from project config
196-
if (projectConfig.hasDetectorCategoryFilter()) {
197-
effectiveRegistry = effectiveRegistry.filterByCategories(
198-
projectConfig.getDetectorCategories());
199-
report.accept("Detector categories: " + projectConfig.getDetectorCategories());
233+
// Apply detector category filter from unified config
234+
if (!filters.categories().isEmpty()) {
235+
effectiveRegistry = effectiveRegistry.filterByCategories(filters.categories());
236+
report.accept("Detector categories: " + filters.categories());
200237
}
201238

202-
// Apply detector include filter from project config
203-
if (projectConfig.hasDetectorIncludeFilter()) {
204-
effectiveRegistry = effectiveRegistry.filterByNames(
205-
projectConfig.getDetectorInclude());
206-
report.accept("Detector include: " + projectConfig.getDetectorInclude());
239+
// Apply detector include filter from unified config
240+
if (!filters.include().isEmpty()) {
241+
effectiveRegistry = effectiveRegistry.filterByNames(filters.include());
242+
report.accept("Detector include: " + filters.include());
207243
}
208244

209-
// Apply parallelism override from project config
210-
if (parallelism == null && projectConfig.getPipelineParallelism() != null) {
211-
parallelism = projectConfig.getPipelineParallelism();
245+
// Apply parallelism override from unified config (null = auto-detect)
246+
if (parallelism == null && filters.parallelism() != null) {
247+
parallelism = filters.parallelism();
212248
report.accept("Pipeline parallelism: " + parallelism + " (from config)");
213249
}
214250

215251
// 1. Discover files
216252
report.accept("Discovering files...");
217253
List<DiscoveredFile> files = fileDiscovery.discover(root);
218254

219-
// Apply language filter from project config
220-
if (projectConfig.hasLanguageFilter()) {
221-
Set<String> allowedLanguages = new HashSet<>(projectConfig.getLanguages());
255+
// Apply language filter from unified config
256+
if (!filters.languages().isEmpty()) {
257+
Set<String> allowedLanguages = new HashSet<>(filters.languages());
222258
files = files.stream()
223259
.filter(f -> allowedLanguages.contains(f.language()))
224260
.toList();
225-
report.accept("Language filter active: " + projectConfig.getLanguages());
261+
report.accept("Language filter active: " + filters.languages());
226262
}
227263

228-
// Apply exclude patterns from project config
229-
if (projectConfig.hasExcludePatterns()) {
230-
List<String> excludes = projectConfig.getExclude();
231-
List<java.util.regex.Pattern> compiledExcludes = compileExcludePatterns(excludes);
264+
// Apply exclude patterns from unified config
265+
if (!filters.exclude().isEmpty()) {
266+
List<java.util.regex.Pattern> compiledExcludes = compileExcludePatterns(filters.exclude());
232267
files = files.stream()
233268
.filter(f -> !matchesAnyCompiledExclude(f.path().toString(), compiledExcludes))
234269
.toList();
235-
report.accept("Exclude patterns: " + excludes);
270+
report.accept("Exclude patterns: " + filters.exclude());
236271
}
237272

238273
int totalFiles = files.size();
@@ -486,43 +521,40 @@ public AnalysisResult runBatchedIndex(Path repoPath, Integer parallelism, int ba
486521
private AnalysisResult runBatchedWithCache(Path root, Integer parallelism, int batchSize,
487522
boolean incremental, AnalysisCache cache,
488523
Consumer<String> report, Instant start) {
489-
// 0. Load project config for pipeline filtering
490-
ProjectConfig projectConfig = ProjectConfigLoader.loadProjectConfig(root);
524+
// 0. Read pipeline filters from the injected unified config.
525+
PipelineFilters filters = pipelineFilters();
491526
DetectorRegistry effectiveRegistry = registry;
492527

493-
if (projectConfig.hasDetectorCategoryFilter()) {
494-
effectiveRegistry = effectiveRegistry.filterByCategories(
495-
projectConfig.getDetectorCategories());
496-
report.accept("Detector categories: " + projectConfig.getDetectorCategories());
528+
if (!filters.categories().isEmpty()) {
529+
effectiveRegistry = effectiveRegistry.filterByCategories(filters.categories());
530+
report.accept("Detector categories: " + filters.categories());
497531
}
498-
if (projectConfig.hasDetectorIncludeFilter()) {
499-
effectiveRegistry = effectiveRegistry.filterByNames(
500-
projectConfig.getDetectorInclude());
501-
report.accept("Detector include: " + projectConfig.getDetectorInclude());
532+
if (!filters.include().isEmpty()) {
533+
effectiveRegistry = effectiveRegistry.filterByNames(filters.include());
534+
report.accept("Detector include: " + filters.include());
502535
}
503-
if (parallelism == null && projectConfig.getPipelineParallelism() != null) {
504-
parallelism = projectConfig.getPipelineParallelism();
536+
if (parallelism == null && filters.parallelism() != null) {
537+
parallelism = filters.parallelism();
505538
report.accept("Pipeline parallelism: " + parallelism + " (from config)");
506539
}
507540

508541
// 1. Discover files
509542
report.accept("Discovering files...");
510543
List<DiscoveredFile> files = fileDiscovery.discover(root);
511544

512-
if (projectConfig.hasLanguageFilter()) {
513-
Set<String> allowedLanguages = new HashSet<>(projectConfig.getLanguages());
545+
if (!filters.languages().isEmpty()) {
546+
Set<String> allowedLanguages = new HashSet<>(filters.languages());
514547
files = files.stream()
515548
.filter(f -> allowedLanguages.contains(f.language()))
516549
.toList();
517-
report.accept("Language filter active: " + projectConfig.getLanguages());
550+
report.accept("Language filter active: " + filters.languages());
518551
}
519-
if (projectConfig.hasExcludePatterns()) {
520-
List<String> excludes = projectConfig.getExclude();
521-
List<java.util.regex.Pattern> compiledExcludes = compileExcludePatterns(excludes);
552+
if (!filters.exclude().isEmpty()) {
553+
List<java.util.regex.Pattern> compiledExcludes = compileExcludePatterns(filters.exclude());
522554
files = files.stream()
523555
.filter(f -> !matchesAnyCompiledExclude(f.path().toString(), compiledExcludes))
524556
.toList();
525-
report.accept("Exclude patterns: " + excludes);
557+
report.accept("Exclude patterns: " + filters.exclude());
526558
}
527559

528560
int totalFiles = files.size();
@@ -790,30 +822,28 @@ private AnalysisResult runSmartWithCache(Path root, Integer parallelism, int bat
790822
Instant phase2Start = Instant.now();
791823
report.accept("Phase 2: Discovering files...");
792824

793-
ProjectConfig projectConfig = ProjectConfigLoader.loadProjectConfig(root);
825+
PipelineFilters filters = pipelineFilters();
794826
DetectorRegistry effectiveRegistry = registry;
795827

796-
if (projectConfig.hasDetectorCategoryFilter()) {
797-
effectiveRegistry = effectiveRegistry.filterByCategories(
798-
projectConfig.getDetectorCategories());
828+
if (!filters.categories().isEmpty()) {
829+
effectiveRegistry = effectiveRegistry.filterByCategories(filters.categories());
799830
}
800-
if (projectConfig.hasDetectorIncludeFilter()) {
801-
effectiveRegistry = effectiveRegistry.filterByNames(
802-
projectConfig.getDetectorInclude());
831+
if (!filters.include().isEmpty()) {
832+
effectiveRegistry = effectiveRegistry.filterByNames(filters.include());
803833
}
804-
if (parallelism == null && projectConfig.getPipelineParallelism() != null) {
805-
parallelism = projectConfig.getPipelineParallelism();
834+
if (parallelism == null && filters.parallelism() != null) {
835+
parallelism = filters.parallelism();
806836
}
807837

808838
List<DiscoveredFile> allFiles = fileDiscovery.discover(root);
809839

810-
if (projectConfig.hasLanguageFilter()) {
811-
Set<String> allowed = new HashSet<>(projectConfig.getLanguages());
840+
if (!filters.languages().isEmpty()) {
841+
Set<String> allowed = new HashSet<>(filters.languages());
812842
allFiles = allFiles.stream().filter(f -> allowed.contains(f.language())).toList();
813843
}
814-
if (projectConfig.hasExcludePatterns()) {
844+
if (!filters.exclude().isEmpty()) {
815845
List<java.util.regex.Pattern> compiledExcludes =
816-
compileExcludePatterns(projectConfig.getExclude());
846+
compileExcludePatterns(filters.exclude());
817847
allFiles = allFiles.stream()
818848
.filter(f -> !matchesAnyCompiledExclude(f.path().toString(), compiledExcludes))
819849
.toList();

src/main/java/io/github/randomcodespace/iq/cli/AnalyzeCommand.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ public AnalyzeCommand(Analyzer analyzer, CodeIqConfig config) {
5858
public Integer call() {
5959
Path root = path.toAbsolutePath().normalize();
6060

61-
CliOutput.configureFromOptions(config, graphDir, serviceName, root);
61+
CliOutput.configureFromOptions(config, graphDir, serviceName);
6262

6363
NumberFormat nf = NumberFormat.getIntegerInstance(Locale.US);
6464
int cores = parallelism != null ? parallelism : Runtime.getRuntime().availableProcessors();

src/main/java/io/github/randomcodespace/iq/cli/CliOutput.java

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -80,12 +80,19 @@ static String format(String ansiFormatted) {
8080
}
8181

8282
/**
83-
* Configure shared CLI options: graph directory, service name, and project-level overrides.
83+
* Configure shared CLI options: graph directory + service name.
8484
* Used by both {@code analyze} and {@code index} commands.
85+
*
86+
* <p>Project-level overrides ({@code cache_dir}, {@code max_depth},
87+
* {@code max_radius} from {@code codeiq.yml} / legacy {@code .osscodeiq.yml})
88+
* are already resolved at Spring startup by
89+
* {@link io.github.randomcodespace.iq.config.UnifiedConfigBeans#codeIqConfig}
90+
* via {@code ConfigResolver} + {@code UnifiedConfigAdapter}. The {@code config}
91+
* bean passed in already carries those values, so re-reading the file here
92+
* would be pure redundancy.
8593
*/
8694
static void configureFromOptions(io.github.randomcodespace.iq.config.CodeIqConfig config,
87-
java.nio.file.Path graphDir, String serviceName,
88-
java.nio.file.Path root) {
95+
java.nio.file.Path graphDir, String serviceName) {
8996
if (graphDir != null) {
9097
java.nio.file.Path sharedDir = graphDir.toAbsolutePath().normalize();
9198
config.setCacheDir(sharedDir.toString());
@@ -95,7 +102,6 @@ static void configureFromOptions(io.github.randomcodespace.iq.config.CodeIqConfi
95102
config.setServiceName(serviceName);
96103
info(" Service name: " + serviceName);
97104
}
98-
io.github.randomcodespace.iq.config.ProjectConfigLoader.loadIfPresent(root, config);
99105
}
100106

101107
/**

src/main/java/io/github/randomcodespace/iq/cli/IndexCommand.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ public Integer call() {
7070

7171
Path root = path.toAbsolutePath().normalize();
7272

73-
CliOutput.configureFromOptions(config, graphDir, serviceName, root);
73+
CliOutput.configureFromOptions(config, graphDir, serviceName);
7474

7575
// Use configured batch size if not overridden on command line
7676
int effectiveBatchSize = batchSize > 0 ? batchSize : config.getBatchSize();

0 commit comments

Comments
 (0)