@ctxo/cli 0.7.0-alpha.0 → 0.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -10,17 +10,18 @@ import {
10
10
  SymbolGraph,
11
11
  aggregateCoChanges,
12
12
  createLogger,
13
- detectWorkspace
14
- } from "./chunk-O3L56NY6.js";
13
+ detectWorkspace,
14
+ indexIgnorePatterns,
15
+ indexIgnoreProjectPatterns,
16
+ loadConfig,
17
+ makeGlobMatcher
18
+ } from "./chunk-4M4V5V36.js";
15
19
  import {
16
20
  JsonIndexReader
17
- } from "./chunk-I4TUA7CD.js";
21
+ } from "./chunk-WXA2JJLH.js";
18
22
  import {
19
23
  StalenessDetector
20
24
  } from "./chunk-PSKDDLSF.js";
21
- import {
22
- __require
23
- } from "./chunk-7OCVIDC7.js";
24
25
 
25
26
  // src/cli/cli-router.ts
26
27
  import { readFileSync as readFileSync13, existsSync as existsSync21 } from "fs";
@@ -30,7 +31,7 @@ import { dirname as dirname8, join as join23 } from "path";
30
31
  import { execFileSync as execFileSync2 } from "child_process";
31
32
  import { readFileSync as readFileSync5, writeFileSync as writeFileSync3, existsSync as existsSync8, statSync, readdirSync as readdirSync2 } from "fs";
32
33
  import { join as join7, relative, extname as extname3 } from "path";
33
- import { createRequire } from "module";
34
+ import { createRequire as createRequire2 } from "module";
34
35
 
35
36
  // src/core/staleness/content-hasher.ts
36
37
  import { createHash } from "crypto";
@@ -134,6 +135,10 @@ async function discoverPlugins(options) {
134
135
  const plugins = [];
135
136
  const failures = [];
136
137
  for (const spec of specifiers) {
138
+ if (options.shouldSkipSpecifier?.(spec)) {
139
+ log.info(`Skipping plugin ${spec} (matched ignoreProjects)`);
140
+ continue;
141
+ }
137
142
  const result = await tryLoad(spec, baseDir);
138
143
  if ("apiVersion" in result) {
139
144
  plugins.push({ plugin: result, specifier: spec });
@@ -188,14 +193,16 @@ function buildContext(projectRoot, workspace, pluginId) {
188
193
  config: {}
189
194
  };
190
195
  }
191
- async function loadPlugins(projectRoot) {
196
+ async function loadPlugins(projectRoot, options = {}) {
192
197
  const manifestPath = resolveManifestPath(projectRoot);
193
198
  if (!manifestPath) {
194
199
  log2.info("No package.json found for plugin discovery");
195
200
  return [];
196
201
  }
197
202
  const workspace = detectWorkspace(projectRoot);
198
- const result = await discoverPlugins({ manifestPath });
203
+ const ignoreProjectPatterns = options.ignoreProjects ?? [];
204
+ const shouldSkipSpecifier = ignoreProjectPatterns.length === 0 ? void 0 : makeGlobMatcher(ignoreProjectPatterns.slice());
205
+ const result = await discoverPlugins({ manifestPath, shouldSkipSpecifier });
199
206
  const loaded = [];
200
207
  for (const { plugin, specifier } of result.plugins) {
201
208
  try {
@@ -212,6 +219,7 @@ async function loadPlugins(projectRoot) {
212
219
  // src/cli/install-command.ts
213
220
  import { existsSync as existsSync5 } from "fs";
214
221
  import { spawn } from "child_process";
222
+ import { createRequire } from "module";
215
223
  import { join as join4 } from "path";
216
224
 
217
225
  // src/core/detection/detect-languages.ts
@@ -581,11 +589,10 @@ function normalizeLanguage(input) {
581
589
  };
582
590
  return aliases[lowered] ?? null;
583
591
  }
592
+ var installRequire = createRequire(import.meta.url);
584
593
  function isPluginInstalled(lang) {
585
594
  try {
586
- const { createRequire: createRequire5 } = __require("module");
587
- const r = createRequire5(import.meta.url);
588
- r.resolve(`${officialPluginFor(lang)}/package.json`);
595
+ installRequire.resolve(`${officialPluginFor(lang)}/package.json`);
589
596
  return true;
590
597
  } catch {
591
598
  return false;
@@ -690,7 +697,7 @@ var SchemaManager = class {
690
697
  };
691
698
 
692
699
  // src/cli/index-command.ts
693
- var requireCjs = createRequire(import.meta.url);
700
+ var requireCjs = createRequire2(import.meta.url);
694
701
  var IndexCommand = class {
695
702
  projectRoot;
696
703
  ctxoRoot;
@@ -700,6 +707,22 @@ var IndexCommand = class {
700
707
  this.ctxoRoot = ctxoRoot ?? join7(projectRoot, ".ctxo");
701
708
  this.supportedExtensions = /* @__PURE__ */ new Set([".ts", ".tsx", ".js", ".jsx", ".go", ".cs"]);
702
709
  }
710
+ loadIgnoreConfig() {
711
+ const { config } = loadConfig(this.ctxoRoot);
712
+ const filePatterns = indexIgnorePatterns(config);
713
+ const projectPatterns = indexIgnoreProjectPatterns(config);
714
+ if (filePatterns.length > 0) {
715
+ console.error(`[ctxo] index.ignore: ${filePatterns.length} pattern(s) active`);
716
+ }
717
+ if (projectPatterns.length > 0) {
718
+ console.error(`[ctxo] index.ignoreProjects: ${projectPatterns.length} pattern(s) active`);
719
+ }
720
+ return {
721
+ ignoreFile: makeGlobMatcher(filePatterns),
722
+ ignoreProject: makeGlobMatcher(projectPatterns),
723
+ ignoreProjectPatterns: projectPatterns
724
+ };
725
+ }
703
726
  async run(options = {}) {
704
727
  if (options.check) {
705
728
  return this.runCheck();
@@ -707,8 +730,9 @@ var IndexCommand = class {
707
730
  if (options.installMissing) {
708
731
  await this.autoInstallMissingPlugins();
709
732
  }
733
+ const ignore = this.loadIgnoreConfig();
710
734
  const registry = new LanguageAdapterRegistry();
711
- const { tsMorphLike, roslynAdapter, csharpTier } = await this.registerDiscoveredPlugins(registry);
735
+ const { tsMorphLike, roslynAdapter, csharpTier, goTier } = await this.registerDiscoveredPlugins(registry, ignore.ignoreProjectPatterns);
712
736
  this.supportedExtensions = registry.getSupportedExtensions();
713
737
  this.printLanguagePreview();
714
738
  const writer = new JsonIndexWriter(this.ctxoRoot);
@@ -722,10 +746,10 @@ var IndexCommand = class {
722
746
  files = [fullPath];
723
747
  console.error(`[ctxo] Incremental re-index: ${options.file}`);
724
748
  } else {
725
- const workspaces = this.discoverWorkspaces();
749
+ const workspaces = this.discoverWorkspaces(ignore.ignoreProject);
726
750
  files = [];
727
751
  for (const ws of workspaces) {
728
- const wsFiles = this.discoverFilesIn(ws);
752
+ const wsFiles = this.discoverFilesIn(ws, ignore.ignoreFile);
729
753
  files.push(...wsFiles);
730
754
  }
731
755
  console.error(`[ctxo] Building codebase index... Found ${files.length} source files`);
@@ -831,7 +855,7 @@ var IndexCommand = class {
831
855
  console.error(`[ctxo] Index complete: ${processed} files indexed`);
832
856
  if (tsCount > 0) console.error(`[ctxo] TypeScript/JS: ${tsCount} files (full tier)`);
833
857
  if (csCount > 0) console.error(`[ctxo] C#: ${csCount} files (${csharpTier} tier${csharpTier === "syntax" ? " - .NET SDK 8+ for full analysis" : ""})`);
834
- if (goCount > 0) console.error(`[ctxo] Go: ${goCount} files (syntax tier)`);
858
+ if (goCount > 0) console.error(`[ctxo] Go: ${goCount} files (${goTier} tier${goTier === "syntax" ? " - Go 1.22+ for full analysis" : ""})`);
835
859
  if (roslynAdapter) await roslynAdapter.dispose();
836
860
  }
837
861
  async autoInstallMissingPlugins() {
@@ -861,11 +885,12 @@ var IndexCommand = class {
861
885
  console.error("[ctxo] Language scan:");
862
886
  for (const line of lines) console.error(line);
863
887
  }
864
- async registerDiscoveredPlugins(registry) {
865
- const loaded = await loadPlugins(this.projectRoot);
888
+ async registerDiscoveredPlugins(registry, ignoreProjectPatterns = []) {
889
+ const loaded = await loadPlugins(this.projectRoot, { ignoreProjects: ignoreProjectPatterns });
866
890
  let tsMorphLike;
867
891
  let roslynAdapter = null;
868
892
  let csharpTier = "unavailable";
893
+ let goTier = "unavailable";
869
894
  for (const { plugin, adapter } of loaded) {
870
895
  if (typeof adapter.initialize === "function") {
871
896
  try {
@@ -881,29 +906,37 @@ var IndexCommand = class {
881
906
  tsMorphLike = tsCandidate;
882
907
  }
883
908
  const csCandidate = adapter;
884
- if (typeof csCandidate.getRoslynDelegate === "function" && typeof csCandidate.getTier === "function") {
909
+ if (plugin.id === "csharp" && typeof csCandidate.getRoslynDelegate === "function" && typeof csCandidate.getTier === "function") {
885
910
  csharpTier = csCandidate.getTier();
886
911
  roslynAdapter = csCandidate.getRoslynDelegate() ?? null;
887
912
  }
888
- const tierSuffix = plugin.id === "csharp" && csharpTier !== "full" ? ` (active: ${csharpTier})` : "";
913
+ const goCandidate = adapter;
914
+ if (plugin.id === "go" && typeof goCandidate.getTier === "function") {
915
+ goTier = goCandidate.getTier();
916
+ }
917
+ const activeTier = plugin.id === "csharp" ? csharpTier : plugin.id === "go" ? goTier : plugin.tier;
918
+ const tierSuffix = activeTier !== plugin.tier ? ` (active: ${activeTier})` : "";
889
919
  console.error(`[ctxo] Plugin ${plugin.id}@${plugin.version} (${plugin.tier} tier${tierSuffix}) \u2014 ${plugin.extensions.join(", ")}`);
890
920
  }
891
- return { tsMorphLike, roslynAdapter, csharpTier };
921
+ return { tsMorphLike, roslynAdapter, csharpTier, goTier };
892
922
  }
893
- discoverFilesIn(root) {
923
+ discoverFilesIn(root, ignoreFile = () => false) {
894
924
  try {
895
925
  const output = execFileSync2("git", ["ls-files", "--cached", "--others", "--exclude-standard"], {
896
926
  cwd: root,
897
927
  encoding: "utf-8",
898
928
  maxBuffer: 10 * 1024 * 1024
899
929
  });
900
- return output.split("\n").map((line) => line.trim()).filter((line) => line.length > 0).filter((line) => this.isSupportedExtension(line)).map((line) => join7(root, line));
930
+ return output.split("\n").map((line) => line.trim()).filter((line) => line.length > 0).filter((line) => this.isSupportedExtension(line)).map((line) => join7(root, line)).filter((abs) => {
931
+ const rel = relative(this.projectRoot, abs).replace(/\\/g, "/");
932
+ return !ignoreFile(rel);
933
+ });
901
934
  } catch {
902
935
  console.error(`[ctxo] git ls-files failed for ${root}`);
903
936
  return [];
904
937
  }
905
938
  }
906
- discoverWorkspaces() {
939
+ discoverWorkspaces(ignoreProject = () => false) {
907
940
  const pkgPath = join7(this.projectRoot, "package.json");
908
941
  if (!existsSync8(pkgPath)) return [this.projectRoot];
909
942
  try {
@@ -929,8 +962,17 @@ var IndexCommand = class {
929
962
  }
930
963
  }
931
964
  if (resolved.length === 0) return [this.projectRoot];
932
- console.error(`[ctxo] Monorepo detected: ${resolved.length} workspace(s)`);
933
- return resolved;
965
+ const filtered = resolved.filter((wsAbs) => {
966
+ const rel = relative(this.projectRoot, wsAbs).replace(/\\/g, "/");
967
+ if (ignoreProject(rel)) {
968
+ console.error(`[ctxo] index.ignoreProjects: skipping ${rel}`);
969
+ return false;
970
+ }
971
+ return true;
972
+ });
973
+ if (filtered.length === 0) return [this.projectRoot];
974
+ console.error(`[ctxo] Monorepo detected: ${filtered.length} workspace(s)`);
975
+ return filtered;
934
976
  } catch {
935
977
  return [this.projectRoot];
936
978
  }
@@ -947,7 +989,7 @@ var IndexCommand = class {
947
989
  if (_roslyn) await _roslyn.dispose();
948
990
  const hasher = new ContentHasher();
949
991
  const files = this.discoverFilesIn(this.projectRoot);
950
- const reader = new (await import("./json-index-reader-NNKOVCOG.js")).JsonIndexReader(this.ctxoRoot);
992
+ const reader = new (await import("./json-index-reader-LME4EDA5.js")).JsonIndexReader(this.ctxoRoot);
951
993
  const indices = reader.readAll();
952
994
  const indexedMap = new Map(indices.map((i) => [i.file, i]));
953
995
  let staleCount = 0;
@@ -1120,7 +1162,7 @@ var VerifyCommand = class {
1120
1162
  // src/cli/init-command.ts
1121
1163
  import { join as join12 } from "path";
1122
1164
  import { existsSync as existsSync11, readFileSync as readFileSync7, writeFileSync as writeFileSync5, mkdirSync as mkdirSync4, chmodSync } from "fs";
1123
- import { createRequire as createRequire2 } from "module";
1165
+ import { createRequire as createRequire3 } from "module";
1124
1166
 
1125
1167
  // src/cli/ai-rules.ts
1126
1168
  import { existsSync as existsSync10, readFileSync as readFileSync6, writeFileSync as writeFileSync4, mkdirSync as mkdirSync3 } from "fs";
@@ -1252,6 +1294,21 @@ ${GITIGNORE_ENTRY}
1252
1294
  var DEFAULT_CONFIG = `# ctxo project configuration
1253
1295
  # Docs: https://github.com/alperhankendi/Ctxo
1254
1296
  version: "1.0"
1297
+
1298
+ # Exclude files and workspaces from indexing.
1299
+ # Uncomment and edit to activate \u2014 both fields accept picomatch globs
1300
+ # (forward-slash paths relative to repo root).
1301
+ index:
1302
+ # Per-file filter applied after \`git ls-files\`.
1303
+ ignore: []
1304
+ # - "packages/**/fixtures/**"
1305
+ # - "tools/legacy-*/**"
1306
+
1307
+ # Workspace filter \u2014 matching workspaces are skipped entirely
1308
+ # (no plugin load, no file enumeration).
1309
+ ignoreProjects: []
1310
+ # - "packages/experimental-*"
1311
+ # - "examples/*"
1255
1312
  `;
1256
1313
  function ensureConfig(projectRoot) {
1257
1314
  const filePath = join11(projectRoot, ".ctxo", "config.yaml");
@@ -1314,7 +1371,7 @@ function ensureMcpConfig(projectRoot, target) {
1314
1371
  }
1315
1372
 
1316
1373
  // src/cli/init-command.ts
1317
- var requireCjs2 = createRequire2(import.meta.url);
1374
+ var requireCjs2 = createRequire3(import.meta.url);
1318
1375
  function resolveInstalledPlugins(needed) {
1319
1376
  const installed = [];
1320
1377
  const missing = [];
@@ -1665,8 +1722,8 @@ var InitCommand = class {
1665
1722
  if (existsSync11(pkgPath)) {
1666
1723
  return JSON.parse(readFileSync7(pkgPath, "utf-8")).version;
1667
1724
  }
1668
- const { createRequire: createRequire5 } = await import("module");
1669
- const require4 = createRequire5(import.meta.url);
1725
+ const { createRequire: createRequire7 } = await import("module");
1726
+ const require4 = createRequire7(import.meta.url);
1670
1727
  return require4("../../package.json").version;
1671
1728
  } catch {
1672
1729
  return void 0;
@@ -2136,8 +2193,8 @@ var DoctorReporter = class {
2136
2193
  };
2137
2194
 
2138
2195
  // src/adapters/diagnostics/checks/runtime-check.ts
2139
- import { createRequire as createRequire3 } from "module";
2140
- var require2 = createRequire3(import.meta.url);
2196
+ import { createRequire as createRequire4 } from "module";
2197
+ var require2 = createRequire4(import.meta.url);
2141
2198
  function pass(id, title, message) {
2142
2199
  return { id, title, status: "pass", message };
2143
2200
  }
@@ -2328,8 +2385,8 @@ function buildSummary(info) {
2328
2385
  }
2329
2386
 
2330
2387
  // src/adapters/diagnostics/checks/language-coverage-check.ts
2331
- import { createRequire as createRequire4 } from "module";
2332
- var require3 = createRequire4(import.meta.url);
2388
+ import { createRequire as createRequire5 } from "module";
2389
+ var require3 = createRequire5(import.meta.url);
2333
2390
  var LanguageCoverageCheck = class {
2334
2391
  id = "language_coverage";
2335
2392
  title = "Language coverage";
@@ -2593,6 +2650,25 @@ var ConfigFileCheck = class {
2593
2650
  if (content.includes(" ")) {
2594
2651
  return { id: this.id, title: this.title, status: "fail", message: "Invalid config.yaml: tabs are not allowed in YAML", fix: "Replace tabs with spaces in .ctxo/config.yaml" };
2595
2652
  }
2653
+ const loaded = loadConfig(ctx.ctxoRoot);
2654
+ if (loaded.errors.length > 0) {
2655
+ return {
2656
+ id: this.id,
2657
+ title: this.title,
2658
+ status: "fail",
2659
+ message: `Invalid config.yaml: ${loaded.errors[0]}${loaded.errors.length > 1 ? ` (+${loaded.errors.length - 1} more)` : ""}`,
2660
+ fix: "Fix schema violations in .ctxo/config.yaml (see docs for the full schema)"
2661
+ };
2662
+ }
2663
+ if (loaded.invalidGlobs.length > 0) {
2664
+ return {
2665
+ id: this.id,
2666
+ title: this.title,
2667
+ status: "warn",
2668
+ message: `config.yaml has ${loaded.invalidGlobs.length} invalid glob pattern(s): ${loaded.invalidGlobs.join("; ")}`,
2669
+ fix: "Fix the listed glob patterns under index.ignore / index.ignoreProjects"
2670
+ };
2671
+ }
2596
2672
  return { id: this.id, title: this.title, status: "pass", message: ".ctxo/config.yaml valid" };
2597
2673
  } catch (err) {
2598
2674
  return { id: this.id, title: this.title, status: "fail", message: `Cannot read config.yaml: ${err.message}`, fix: "Check file permissions for .ctxo/config.yaml" };
@@ -2648,6 +2724,7 @@ var DiskUsageCheck = class {
2648
2724
  // src/cli/doctor-fix.ts
2649
2725
  import { execFileSync as execFileSync6 } from "child_process";
2650
2726
  import { appendFileSync, existsSync as existsSync18, mkdirSync as mkdirSync5, rmSync as rmSync2, unlinkSync as unlinkSync2 } from "fs";
2727
+ import { createRequire as createRequire6 } from "module";
2651
2728
  import { dirname as dirname7, join as join20 } from "path";
2652
2729
  async function applyFixes(report, options) {
2653
2730
  if (isCI(process.env) && !options.yes && !options.dryRun) {
@@ -2833,11 +2910,10 @@ async function fixSqliteCache(projectRoot, ctxoRoot, dryRun) {
2833
2910
  if (existsSync18(cacheDir)) rmSync2(cacheDir, { recursive: true, force: true });
2834
2911
  await new SyncCommand(projectRoot).run();
2835
2912
  }
2913
+ var doctorRequire = createRequire6(import.meta.url);
2836
2914
  function canResolvePlugin(lang) {
2837
2915
  try {
2838
- const { createRequire: createRequire5 } = __require("module");
2839
- const r = createRequire5(import.meta.url);
2840
- r.resolve(`${officialPluginFor(lang)}/package.json`);
2916
+ doctorRequire.resolve(`${officialPluginFor(lang)}/package.json`);
2841
2917
  return true;
2842
2918
  } catch {
2843
2919
  return false;
@@ -2885,7 +2961,7 @@ var DoctorCommand = class {
2885
2961
  this.ctxoRoot = join21(projectRoot, ".ctxo");
2886
2962
  }
2887
2963
  async run(options = {}) {
2888
- const { getVersion: getVersion2 } = await import("./cli-router-RA4EBMVV.js");
2964
+ const { getVersion: getVersion2 } = await import("./cli-router-T3VZZJD6.js");
2889
2965
  console.error(`ctxo v${getVersion2()} \u2014 health check
2890
2966
  `);
2891
2967
  const checks = [
@@ -3327,4 +3403,4 @@ export {
3327
3403
  getVersion,
3328
3404
  locateGitRoot
3329
3405
  };
3330
- //# sourceMappingURL=cli-router-RA4EBMVV.js.map
3406
+ //# sourceMappingURL=cli-router-T3VZZJD6.js.map