@ctxo/cli 0.10.0 → 0.11.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.
- package/README.md +18 -0
- package/dist/{atomic-write-AQNYJFAG.js → atomic-write-BAFH464Q.js} +3 -2
- package/dist/chunk-5DSKZJNN.js +38 -0
- package/dist/chunk-5DSKZJNN.js.map +1 -0
- package/dist/{chunk-2TVHKCCU.js → chunk-CDGQGCVN.js} +55 -5
- package/dist/chunk-CDGQGCVN.js.map +1 -0
- package/dist/{chunk-I6HLEK4G.js → chunk-HZ4UGA6J.js} +43 -9
- package/dist/chunk-HZ4UGA6J.js.map +1 -0
- package/dist/{chunk-6EKERTJ7.js → chunk-RFEXMBTG.js} +4 -36
- package/dist/chunk-RFEXMBTG.js.map +1 -0
- package/dist/{cli-router-45W4IRYE.js → cli-router-2JVPBW4S.js} +389 -215
- package/dist/cli-router-2JVPBW4S.js.map +1 -0
- package/dist/index.js +7 -6
- package/dist/index.js.map +1 -1
- package/dist/json-index-reader-UYS74USB.js +9 -0
- package/llms-full.txt +2 -1
- package/llms.txt +1 -0
- package/package.json +4 -4
- package/dist/chunk-2TVHKCCU.js.map +0 -1
- package/dist/chunk-6EKERTJ7.js.map +0 -1
- package/dist/chunk-I6HLEK4G.js.map +0 -1
- package/dist/cli-router-45W4IRYE.js.map +0 -1
- package/dist/json-index-reader-XOEE6VMD.js +0 -8
- /package/dist/{atomic-write-AQNYJFAG.js.map → atomic-write-BAFH464Q.js.map} +0 -0
- /package/dist/{json-index-reader-XOEE6VMD.js.map → json-index-reader-UYS74USB.js.map} +0 -0
|
@@ -19,26 +19,28 @@ import {
|
|
|
19
19
|
loadConfig,
|
|
20
20
|
makeGlobMatcher,
|
|
21
21
|
watchSnapshotMinFileChanges
|
|
22
|
-
} from "./chunk-
|
|
22
|
+
} from "./chunk-HZ4UGA6J.js";
|
|
23
23
|
import {
|
|
24
|
-
atomicWrite
|
|
25
|
-
|
|
26
|
-
} from "./chunk-6EKERTJ7.js";
|
|
24
|
+
atomicWrite
|
|
25
|
+
} from "./chunk-RFEXMBTG.js";
|
|
27
26
|
import {
|
|
28
27
|
JsonIndexReader
|
|
29
|
-
} from "./chunk-
|
|
28
|
+
} from "./chunk-CDGQGCVN.js";
|
|
29
|
+
import {
|
|
30
|
+
createLogger
|
|
31
|
+
} from "./chunk-5DSKZJNN.js";
|
|
30
32
|
import {
|
|
31
33
|
StalenessDetector
|
|
32
34
|
} from "./chunk-PSKDDLSF.js";
|
|
33
35
|
|
|
34
36
|
// src/cli/cli-router.ts
|
|
35
|
-
import { readFileSync as readFileSync17, existsSync as
|
|
36
|
-
import { dirname as
|
|
37
|
+
import { readFileSync as readFileSync17, existsSync as existsSync27 } from "fs";
|
|
38
|
+
import { dirname as dirname9, join as join31 } from "path";
|
|
37
39
|
|
|
38
40
|
// src/cli/index-command.ts
|
|
39
41
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
40
|
-
import { readFileSync as readFileSync6, writeFileSync as writeFileSync2, existsSync as
|
|
41
|
-
import { join as
|
|
42
|
+
import { readFileSync as readFileSync6, writeFileSync as writeFileSync2, existsSync as existsSync10, statSync, readdirSync as readdirSync2 } from "fs";
|
|
43
|
+
import { join as join9, relative, extname as extname3 } from "path";
|
|
42
44
|
import { createRequire as createRequire3 } from "module";
|
|
43
45
|
|
|
44
46
|
// src/core/staleness/content-hasher.ts
|
|
@@ -264,9 +266,9 @@ async function loadPlugins(projectRoot, options = {}) {
|
|
|
264
266
|
}
|
|
265
267
|
|
|
266
268
|
// src/cli/install-command.ts
|
|
267
|
-
import { existsSync as
|
|
269
|
+
import { existsSync as existsSync6 } from "fs";
|
|
268
270
|
import { createRequire as createRequire2 } from "module";
|
|
269
|
-
import { join as
|
|
271
|
+
import { join as join5 } from "path";
|
|
270
272
|
|
|
271
273
|
// src/core/detection/detect-languages.ts
|
|
272
274
|
import { execFileSync } from "child_process";
|
|
@@ -562,20 +564,60 @@ function buildInstallCommand(manager, packages, options = {}) {
|
|
|
562
564
|
// src/core/install/run-package-manager.ts
|
|
563
565
|
import { spawn } from "child_process";
|
|
564
566
|
function runPackageManager(command, args, cwd) {
|
|
565
|
-
const resolved = resolveBinary(command);
|
|
566
567
|
return new Promise((resolve4, reject) => {
|
|
567
|
-
const child = spawn(
|
|
568
|
+
const child = process.platform === "win32" ? spawn([command, ...args].join(" "), { cwd, stdio: "inherit", shell: true }) : spawn(command, [...args], { cwd, stdio: "inherit", shell: false });
|
|
568
569
|
child.on("exit", (code) => resolve4(code ?? 0));
|
|
569
570
|
child.on("error", (err) => reject(err));
|
|
570
571
|
});
|
|
571
572
|
}
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
573
|
+
|
|
574
|
+
// src/core/detection/detect-java-runtime.ts
|
|
575
|
+
import { spawnSync } from "child_process";
|
|
576
|
+
import { existsSync as existsSync5 } from "fs";
|
|
577
|
+
import { join as join4 } from "path";
|
|
578
|
+
function parseJavaMajor(output) {
|
|
579
|
+
const m = output.match(/version "([^"]+)"/);
|
|
580
|
+
if (!m) return void 0;
|
|
581
|
+
const parts = m[1].split(".");
|
|
582
|
+
const major = parts[0] === "1" && parts[1] ? parseInt(parts[1], 10) : parseInt(parts[0], 10);
|
|
583
|
+
return Number.isNaN(major) ? void 0 : major;
|
|
584
|
+
}
|
|
585
|
+
var _javaMajorCache = void 0;
|
|
586
|
+
function detectJavaMajor() {
|
|
587
|
+
if (_javaMajorCache !== void 0) {
|
|
588
|
+
return _javaMajorCache === null ? void 0 : _javaMajorCache;
|
|
589
|
+
}
|
|
590
|
+
const home = process.env["CTXO_JAVA_HOME"] || process.env["JAVA_HOME"];
|
|
591
|
+
const bin = home ? join4(home, "bin", process.platform === "win32" ? "java.exe" : "java") : "java";
|
|
592
|
+
if (home && !existsSync5(bin)) {
|
|
593
|
+
_javaMajorCache = null;
|
|
594
|
+
return void 0;
|
|
595
|
+
}
|
|
596
|
+
const r = spawnSync(bin, ["-version"], {
|
|
597
|
+
encoding: "utf-8",
|
|
598
|
+
timeout: 1e4,
|
|
599
|
+
windowsHide: true
|
|
600
|
+
});
|
|
601
|
+
if (r.error || r.status === null) {
|
|
602
|
+
_javaMajorCache = null;
|
|
603
|
+
return void 0;
|
|
604
|
+
}
|
|
605
|
+
const major = parseJavaMajor(`${r.stderr ?? ""}${r.stdout ?? ""}`);
|
|
606
|
+
_javaMajorCache = major ?? null;
|
|
607
|
+
return major;
|
|
608
|
+
}
|
|
609
|
+
function javaRuntimeAvailable() {
|
|
610
|
+
return (detectJavaMajor() ?? 0) >= 17;
|
|
576
611
|
}
|
|
577
612
|
|
|
578
613
|
// src/cli/install-command.ts
|
|
614
|
+
function resolveJavaPackages(opts) {
|
|
615
|
+
if (opts.fullTier && opts.syntaxOnly) throw new Error("Cannot combine --full-tier with --syntax-only.");
|
|
616
|
+
const base = ["@ctxo/lang-java"];
|
|
617
|
+
if (opts.syntaxOnly) return base;
|
|
618
|
+
if (opts.fullTier || opts.jreAvailable) return [...base, "@ctxo/lang-java-analyzer"];
|
|
619
|
+
return base;
|
|
620
|
+
}
|
|
579
621
|
var InstallCommand = class {
|
|
580
622
|
constructor(projectRoot) {
|
|
581
623
|
this.projectRoot = projectRoot;
|
|
@@ -599,7 +641,7 @@ var InstallCommand = class {
|
|
|
599
641
|
process.exitCode = 1;
|
|
600
642
|
return;
|
|
601
643
|
}
|
|
602
|
-
if (!options.global && !
|
|
644
|
+
if (!options.global && !existsSync6(join5(this.projectRoot, "package.json"))) {
|
|
603
645
|
console.error("[ctxo] No package.json in the current project. Re-run with --global or create a package.json first.");
|
|
604
646
|
process.exitCode = 1;
|
|
605
647
|
return;
|
|
@@ -609,9 +651,28 @@ var InstallCommand = class {
|
|
|
609
651
|
process.exitCode = 1;
|
|
610
652
|
return;
|
|
611
653
|
}
|
|
612
|
-
const specifiers =
|
|
613
|
-
|
|
614
|
-
|
|
654
|
+
const specifiers = [];
|
|
655
|
+
for (const lang of resolvedLangs) {
|
|
656
|
+
if (lang === "java") {
|
|
657
|
+
const jreAvailable = javaRuntimeAvailable();
|
|
658
|
+
if (!jreAvailable && !options.fullTier && !options.syntaxOnly) {
|
|
659
|
+
console.error('[ctxo] JRE 17+ not found; installing Java syntax tier. Install a JRE then run "ctxo install java --full-tier" for resolved call/use edges.');
|
|
660
|
+
}
|
|
661
|
+
let javaPkgs;
|
|
662
|
+
try {
|
|
663
|
+
javaPkgs = resolveJavaPackages({ jreAvailable, fullTier: options.fullTier, syntaxOnly: options.syntaxOnly });
|
|
664
|
+
} catch (err) {
|
|
665
|
+
console.error(`[ctxo] ${err.message}`);
|
|
666
|
+
process.exitCode = 1;
|
|
667
|
+
return;
|
|
668
|
+
}
|
|
669
|
+
for (const pkg of javaPkgs) {
|
|
670
|
+
specifiers.push(options.version ? `${pkg}@${options.version}` : pkg);
|
|
671
|
+
}
|
|
672
|
+
} else {
|
|
673
|
+
specifiers.push(options.version ? `${officialPluginFor(lang)}@${options.version}` : officialPluginFor(lang));
|
|
674
|
+
}
|
|
675
|
+
}
|
|
615
676
|
const invocation = buildInstallCommand(resolution.manager, specifiers, {
|
|
616
677
|
global: options.global,
|
|
617
678
|
workspaceRoot: !options.global && isWorkspaceRoot(this.projectRoot)
|
|
@@ -703,12 +764,12 @@ function isLockedCI(env) {
|
|
|
703
764
|
}
|
|
704
765
|
|
|
705
766
|
// src/adapters/storage/json-index-writer.ts
|
|
706
|
-
import { mkdirSync, unlinkSync, existsSync as
|
|
707
|
-
import { dirname as dirname4, join as
|
|
767
|
+
import { mkdirSync, unlinkSync, existsSync as existsSync7 } from "fs";
|
|
768
|
+
import { dirname as dirname4, join as join6, resolve as resolve2, sep } from "path";
|
|
708
769
|
var JsonIndexWriter = class {
|
|
709
770
|
indexDir;
|
|
710
771
|
constructor(ctxoRoot) {
|
|
711
|
-
this.indexDir =
|
|
772
|
+
this.indexDir = join6(ctxoRoot, "index");
|
|
712
773
|
}
|
|
713
774
|
write(fileIndex) {
|
|
714
775
|
if (!fileIndex.file) {
|
|
@@ -722,7 +783,7 @@ var JsonIndexWriter = class {
|
|
|
722
783
|
}
|
|
723
784
|
writeCoChanges(matrix) {
|
|
724
785
|
mkdirSync(this.indexDir, { recursive: true });
|
|
725
|
-
const targetPath =
|
|
786
|
+
const targetPath = join6(this.indexDir, "co-changes.json");
|
|
726
787
|
this.atomicWrite(targetPath, JSON.stringify(matrix, null, 2));
|
|
727
788
|
}
|
|
728
789
|
atomicWrite(targetPath, content) {
|
|
@@ -730,7 +791,7 @@ var JsonIndexWriter = class {
|
|
|
730
791
|
}
|
|
731
792
|
delete(relativePath) {
|
|
732
793
|
const targetPath = this.resolveIndexPath(relativePath);
|
|
733
|
-
if (
|
|
794
|
+
if (existsSync7(targetPath)) {
|
|
734
795
|
unlinkSync(targetPath);
|
|
735
796
|
}
|
|
736
797
|
}
|
|
@@ -758,19 +819,19 @@ var JsonIndexWriter = class {
|
|
|
758
819
|
};
|
|
759
820
|
|
|
760
821
|
// src/adapters/storage/schema-manager.ts
|
|
761
|
-
import { readFileSync as readFileSync4, writeFileSync, existsSync as
|
|
762
|
-
import { join as
|
|
822
|
+
import { readFileSync as readFileSync4, writeFileSync, existsSync as existsSync8, mkdirSync as mkdirSync2 } from "fs";
|
|
823
|
+
import { join as join7, dirname as dirname5 } from "path";
|
|
763
824
|
var CURRENT_SCHEMA_VERSION = "1.0.0";
|
|
764
825
|
var SchemaManager = class {
|
|
765
826
|
versionFilePath;
|
|
766
827
|
constructor(ctxoRoot) {
|
|
767
|
-
this.versionFilePath =
|
|
828
|
+
this.versionFilePath = join7(ctxoRoot, "index", "schema-version");
|
|
768
829
|
}
|
|
769
830
|
currentVersion() {
|
|
770
831
|
return CURRENT_SCHEMA_VERSION;
|
|
771
832
|
}
|
|
772
833
|
readStoredVersion() {
|
|
773
|
-
if (!
|
|
834
|
+
if (!existsSync8(this.versionFilePath)) {
|
|
774
835
|
return void 0;
|
|
775
836
|
}
|
|
776
837
|
return readFileSync4(this.versionFilePath, "utf-8").trim();
|
|
@@ -787,8 +848,8 @@ var SchemaManager = class {
|
|
|
787
848
|
};
|
|
788
849
|
|
|
789
850
|
// src/adapters/storage/architecture-artifacts-writer.ts
|
|
790
|
-
import { existsSync as
|
|
791
|
-
import { join as
|
|
851
|
+
import { existsSync as existsSync9, mkdirSync as mkdirSync3, readFileSync as readFileSync5 } from "fs";
|
|
852
|
+
import { join as join8, resolve as resolve3 } from "path";
|
|
792
853
|
import { tmpdir } from "os";
|
|
793
854
|
var log3 = createLogger("ctxo:architecture-artifacts");
|
|
794
855
|
var DRIFT_FILE_NAME = "drift-events.json";
|
|
@@ -796,20 +857,20 @@ var VIOLATIONS_FILE_NAME = "boundary-violations.json";
|
|
|
796
857
|
var ArchitectureArtifactsWriter = class {
|
|
797
858
|
indexDir;
|
|
798
859
|
constructor(ctxoRoot, options = {}) {
|
|
799
|
-
this.indexDir =
|
|
860
|
+
this.indexDir = join8(ctxoRoot, "index");
|
|
800
861
|
assertSafeWritePath(ctxoRoot, options.allowProductionPath ?? false);
|
|
801
862
|
}
|
|
802
863
|
writeDriftEvents(artifact) {
|
|
803
864
|
mkdirSync3(this.indexDir, { recursive: true });
|
|
804
|
-
atomicWrite(
|
|
865
|
+
atomicWrite(join8(this.indexDir, DRIFT_FILE_NAME), JSON.stringify(artifact, null, 2));
|
|
805
866
|
}
|
|
806
867
|
writeBoundaryViolations(artifact) {
|
|
807
868
|
mkdirSync3(this.indexDir, { recursive: true });
|
|
808
|
-
atomicWrite(
|
|
869
|
+
atomicWrite(join8(this.indexDir, VIOLATIONS_FILE_NAME), JSON.stringify(artifact, null, 2));
|
|
809
870
|
}
|
|
810
871
|
readDriftEvents() {
|
|
811
|
-
const path =
|
|
812
|
-
if (!
|
|
872
|
+
const path = join8(this.indexDir, DRIFT_FILE_NAME);
|
|
873
|
+
if (!existsSync9(path)) return void 0;
|
|
813
874
|
try {
|
|
814
875
|
return JSON.parse(readFileSync5(path, "utf-8"));
|
|
815
876
|
} catch (err) {
|
|
@@ -818,8 +879,8 @@ var ArchitectureArtifactsWriter = class {
|
|
|
818
879
|
}
|
|
819
880
|
}
|
|
820
881
|
readBoundaryViolations() {
|
|
821
|
-
const path =
|
|
822
|
-
if (!
|
|
882
|
+
const path = join8(this.indexDir, VIOLATIONS_FILE_NAME);
|
|
883
|
+
if (!existsSync9(path)) return void 0;
|
|
823
884
|
try {
|
|
824
885
|
return JSON.parse(readFileSync5(path, "utf-8"));
|
|
825
886
|
} catch (err) {
|
|
@@ -1128,7 +1189,7 @@ var IndexCommand = class {
|
|
|
1128
1189
|
supportedExtensions;
|
|
1129
1190
|
constructor(projectRoot, ctxoRoot) {
|
|
1130
1191
|
this.projectRoot = projectRoot;
|
|
1131
|
-
this.ctxoRoot = ctxoRoot ??
|
|
1192
|
+
this.ctxoRoot = ctxoRoot ?? join9(projectRoot, ".ctxo");
|
|
1132
1193
|
this.supportedExtensions = /* @__PURE__ */ new Set([".ts", ".tsx", ".js", ".jsx", ".go", ".cs"]);
|
|
1133
1194
|
}
|
|
1134
1195
|
loadIgnoreConfig() {
|
|
@@ -1156,7 +1217,7 @@ var IndexCommand = class {
|
|
|
1156
1217
|
}
|
|
1157
1218
|
const ignore = this.loadIgnoreConfig();
|
|
1158
1219
|
const registry = new LanguageAdapterRegistry();
|
|
1159
|
-
const { tsMorphLike, roslynAdapter, csharpTier, goTier } = await this.registerDiscoveredPlugins(registry, ignore.ignoreProjectPatterns);
|
|
1220
|
+
const { tsMorphLike, roslynAdapter, csharpTier, goTier, javaTier } = await this.registerDiscoveredPlugins(registry, ignore.ignoreProjectPatterns);
|
|
1160
1221
|
this.supportedExtensions = registry.getSupportedExtensions();
|
|
1161
1222
|
this.printLanguagePreview();
|
|
1162
1223
|
const writer = new JsonIndexWriter(this.ctxoRoot);
|
|
@@ -1166,7 +1227,7 @@ var IndexCommand = class {
|
|
|
1166
1227
|
const revertDetector = new RevertDetector();
|
|
1167
1228
|
let files;
|
|
1168
1229
|
if (options.file) {
|
|
1169
|
-
const fullPath =
|
|
1230
|
+
const fullPath = join9(this.projectRoot, options.file);
|
|
1170
1231
|
files = [fullPath];
|
|
1171
1232
|
console.error(`[ctxo] Incremental re-index: ${options.file}`);
|
|
1172
1233
|
} else {
|
|
@@ -1179,8 +1240,8 @@ var IndexCommand = class {
|
|
|
1179
1240
|
console.error(`[ctxo] Building codebase index... Found ${files.length} source files`);
|
|
1180
1241
|
}
|
|
1181
1242
|
try {
|
|
1182
|
-
const { sweepStaleTmpFiles } = await import("./atomic-write-
|
|
1183
|
-
const swept = sweepStaleTmpFiles(
|
|
1243
|
+
const { sweepStaleTmpFiles } = await import("./atomic-write-BAFH464Q.js");
|
|
1244
|
+
const swept = sweepStaleTmpFiles(join9(this.ctxoRoot, "index"));
|
|
1184
1245
|
if (swept > 0) {
|
|
1185
1246
|
console.error(`[ctxo] Cleaned ${swept} orphan .tmp file(s) from previous interrupted run`);
|
|
1186
1247
|
}
|
|
@@ -1267,7 +1328,7 @@ var IndexCommand = class {
|
|
|
1267
1328
|
}
|
|
1268
1329
|
let communitySnapshot;
|
|
1269
1330
|
if (options.skipCommunity && pendingIndices.length > 0) {
|
|
1270
|
-
if (
|
|
1331
|
+
if (existsSync10(join9(this.ctxoRoot, "index", "communities.json"))) {
|
|
1271
1332
|
console.error(
|
|
1272
1333
|
`[ctxo] WARN stale communities.json present \u2014 --skip-community preserved existing snapshot. Run \`ctxo index\` (full) to refresh.`
|
|
1273
1334
|
);
|
|
@@ -1280,6 +1341,7 @@ var IndexCommand = class {
|
|
|
1280
1341
|
{
|
|
1281
1342
|
csharpTier,
|
|
1282
1343
|
goTier,
|
|
1344
|
+
javaTier,
|
|
1283
1345
|
tsCount: graphIndices.filter((idx) => /\.(ts|tsx|js|jsx)$/.test(idx.file)).length
|
|
1284
1346
|
},
|
|
1285
1347
|
gitAdapter
|
|
@@ -1317,10 +1379,12 @@ var IndexCommand = class {
|
|
|
1317
1379
|
const csCount = pendingIndices.filter((e) => e.relativePath.endsWith(".cs")).length;
|
|
1318
1380
|
const tsCount = pendingIndices.filter((e) => /\.(ts|tsx|js|jsx)$/.test(e.relativePath)).length;
|
|
1319
1381
|
const goCount = pendingIndices.filter((e) => e.relativePath.endsWith(".go")).length;
|
|
1382
|
+
const javaCount = pendingIndices.filter((e) => e.relativePath.endsWith(".java")).length;
|
|
1320
1383
|
console.error(`[ctxo] Index complete: ${processed} files indexed`);
|
|
1321
1384
|
if (tsCount > 0) console.error(`[ctxo] TypeScript/JS: ${tsCount} files (full tier)`);
|
|
1322
1385
|
if (csCount > 0) console.error(`[ctxo] C#: ${csCount} files (${csharpTier} tier${csharpTier === "syntax" ? " - .NET SDK 8+ for full analysis" : ""})`);
|
|
1323
1386
|
if (goCount > 0) console.error(`[ctxo] Go: ${goCount} files (${goTier} tier${goTier === "syntax" ? " - Go 1.22+ for full analysis" : ""})`);
|
|
1387
|
+
if (javaCount > 0) console.error(`[ctxo] Java: ${javaCount} files (${javaTier} tier${javaTier === "syntax" ? ' - JRE 17+ & "ctxo install java --full-tier" for full analysis' : ""})`);
|
|
1324
1388
|
if (roslynAdapter) await roslynAdapter.dispose();
|
|
1325
1389
|
}
|
|
1326
1390
|
async autoInstallMissingPlugins() {
|
|
@@ -1356,6 +1420,7 @@ var IndexCommand = class {
|
|
|
1356
1420
|
let roslynAdapter = null;
|
|
1357
1421
|
let csharpTier = "unavailable";
|
|
1358
1422
|
let goTier = "unavailable";
|
|
1423
|
+
let javaTier = "unavailable";
|
|
1359
1424
|
for (const { plugin, adapter } of loaded) {
|
|
1360
1425
|
if (typeof adapter.initialize === "function") {
|
|
1361
1426
|
try {
|
|
@@ -1379,11 +1444,15 @@ var IndexCommand = class {
|
|
|
1379
1444
|
if (plugin.id === "go" && typeof goCandidate.getTier === "function") {
|
|
1380
1445
|
goTier = goCandidate.getTier();
|
|
1381
1446
|
}
|
|
1382
|
-
const
|
|
1447
|
+
const javaCandidate = adapter;
|
|
1448
|
+
if (plugin.id === "java" && typeof javaCandidate.getTier === "function") {
|
|
1449
|
+
javaTier = javaCandidate.getTier();
|
|
1450
|
+
}
|
|
1451
|
+
const activeTier = plugin.id === "csharp" ? csharpTier : plugin.id === "go" ? goTier : plugin.id === "java" ? javaTier : plugin.tier;
|
|
1383
1452
|
const tierSuffix = activeTier !== plugin.tier ? ` (active: ${activeTier})` : "";
|
|
1384
1453
|
console.error(`[ctxo] Plugin ${plugin.id}@${plugin.version} (${plugin.tier} tier${tierSuffix}) \u2014 ${plugin.extensions.join(", ")}`);
|
|
1385
1454
|
}
|
|
1386
|
-
return { tsMorphLike, roslynAdapter, csharpTier, goTier };
|
|
1455
|
+
return { tsMorphLike, roslynAdapter, csharpTier, goTier, javaTier };
|
|
1387
1456
|
}
|
|
1388
1457
|
discoverFilesIn(root, ignoreFile = () => false) {
|
|
1389
1458
|
try {
|
|
@@ -1392,18 +1461,18 @@ var IndexCommand = class {
|
|
|
1392
1461
|
encoding: "utf-8",
|
|
1393
1462
|
maxBuffer: 10 * 1024 * 1024
|
|
1394
1463
|
});
|
|
1395
|
-
return output.split("\n").map((line) => line.trim()).filter((line) => line.length > 0).filter((line) => this.isSupportedExtension(line)).map((line) =>
|
|
1464
|
+
return output.split("\n").map((line) => line.trim()).filter((line) => line.length > 0).filter((line) => this.isSupportedExtension(line)).map((line) => join9(root, line)).filter((abs) => {
|
|
1396
1465
|
const rel = relative(this.projectRoot, abs).replace(/\\/g, "/");
|
|
1397
1466
|
return !ignoreFile(rel);
|
|
1398
|
-
}).filter((abs) =>
|
|
1467
|
+
}).filter((abs) => existsSync10(abs));
|
|
1399
1468
|
} catch {
|
|
1400
1469
|
console.error(`[ctxo] git ls-files failed for ${root}`);
|
|
1401
1470
|
return [];
|
|
1402
1471
|
}
|
|
1403
1472
|
}
|
|
1404
1473
|
discoverWorkspaces(ignoreProject = () => false) {
|
|
1405
|
-
const pkgPath =
|
|
1406
|
-
if (!
|
|
1474
|
+
const pkgPath = join9(this.projectRoot, "package.json");
|
|
1475
|
+
if (!existsSync10(pkgPath)) return [this.projectRoot];
|
|
1407
1476
|
try {
|
|
1408
1477
|
const pkg = JSON.parse(readFileSync6(pkgPath, "utf-8"));
|
|
1409
1478
|
const workspaces = Array.isArray(pkg.workspaces) ? pkg.workspaces : pkg.workspaces?.packages;
|
|
@@ -1411,17 +1480,17 @@ var IndexCommand = class {
|
|
|
1411
1480
|
const resolved = [];
|
|
1412
1481
|
for (const ws of workspaces) {
|
|
1413
1482
|
if (ws.endsWith("/*") || ws.endsWith("\\*")) {
|
|
1414
|
-
const parentDir =
|
|
1415
|
-
if (
|
|
1483
|
+
const parentDir = join9(this.projectRoot, ws.slice(0, -2));
|
|
1484
|
+
if (existsSync10(parentDir)) {
|
|
1416
1485
|
for (const entry of readdirSync2(parentDir, { withFileTypes: true })) {
|
|
1417
1486
|
if (entry.isDirectory()) {
|
|
1418
|
-
resolved.push(
|
|
1487
|
+
resolved.push(join9(parentDir, entry.name));
|
|
1419
1488
|
}
|
|
1420
1489
|
}
|
|
1421
1490
|
}
|
|
1422
1491
|
} else {
|
|
1423
|
-
const wsPath =
|
|
1424
|
-
if (
|
|
1492
|
+
const wsPath = join9(this.projectRoot, ws);
|
|
1493
|
+
if (existsSync10(wsPath)) {
|
|
1425
1494
|
resolved.push(wsPath);
|
|
1426
1495
|
}
|
|
1427
1496
|
}
|
|
@@ -1454,7 +1523,7 @@ var IndexCommand = class {
|
|
|
1454
1523
|
if (_roslyn) await _roslyn.dispose();
|
|
1455
1524
|
const hasher = new ContentHasher();
|
|
1456
1525
|
const files = this.discoverFilesIn(this.projectRoot);
|
|
1457
|
-
const reader = new (await import("./json-index-reader-
|
|
1526
|
+
const reader = new (await import("./json-index-reader-UYS74USB.js")).JsonIndexReader(this.ctxoRoot);
|
|
1458
1527
|
const indices = reader.readAll();
|
|
1459
1528
|
const indexedMap = new Map(indices.map((i) => [i.file, i]));
|
|
1460
1529
|
let staleCount = 0;
|
|
@@ -1467,7 +1536,7 @@ var IndexCommand = class {
|
|
|
1467
1536
|
staleCount++;
|
|
1468
1537
|
continue;
|
|
1469
1538
|
}
|
|
1470
|
-
if (!
|
|
1539
|
+
if (!existsSync10(filePath)) {
|
|
1471
1540
|
console.error(`[ctxo] DELETED: ${relativePath}`);
|
|
1472
1541
|
staleCount++;
|
|
1473
1542
|
continue;
|
|
@@ -1493,13 +1562,13 @@ var IndexCommand = class {
|
|
|
1493
1562
|
console.error("[ctxo] Index is up to date");
|
|
1494
1563
|
}
|
|
1495
1564
|
ensureGitignore() {
|
|
1496
|
-
const gitignorePath =
|
|
1565
|
+
const gitignorePath = join9(this.projectRoot, ".gitignore");
|
|
1497
1566
|
const cachePattern = ".ctxo/.cache/";
|
|
1498
1567
|
const suffix = `
|
|
1499
1568
|
# Ctxo local cache (never committed)
|
|
1500
1569
|
${cachePattern}
|
|
1501
1570
|
`;
|
|
1502
|
-
const existing =
|
|
1571
|
+
const existing = existsSync10(gitignorePath) ? readFileSync6(gitignorePath, "utf-8") : "";
|
|
1503
1572
|
if (existing.includes(cachePattern)) return;
|
|
1504
1573
|
writeFileSync2(gitignorePath, existing + suffix, "utf-8");
|
|
1505
1574
|
console.error("[ctxo] Added .ctxo/.cache/ to .gitignore");
|
|
@@ -1618,8 +1687,8 @@ ${cachePattern}
|
|
|
1618
1687
|
}
|
|
1619
1688
|
};
|
|
1620
1689
|
function pickEdgeQuality(tiers) {
|
|
1621
|
-
const hasSyntaxOnly = tiers.csharpTier === "syntax" || tiers.goTier === "syntax";
|
|
1622
|
-
const hasFull = tiers.tsCount > 0 || tiers.csharpTier === "full" || tiers.goTier === "full";
|
|
1690
|
+
const hasSyntaxOnly = tiers.csharpTier === "syntax" || tiers.goTier === "syntax" || tiers.javaTier === "syntax";
|
|
1691
|
+
const hasFull = tiers.tsCount > 0 || tiers.csharpTier === "full" || tiers.goTier === "full" || tiers.javaTier === "full";
|
|
1623
1692
|
if (hasSyntaxOnly && hasFull) return "mixed";
|
|
1624
1693
|
if (hasSyntaxOnly) return "syntax-only";
|
|
1625
1694
|
return "full";
|
|
@@ -1644,11 +1713,11 @@ function countCommunities(snapshot) {
|
|
|
1644
1713
|
}
|
|
1645
1714
|
|
|
1646
1715
|
// src/cli/sync-command.ts
|
|
1647
|
-
import { join as
|
|
1716
|
+
import { join as join10 } from "path";
|
|
1648
1717
|
var SyncCommand = class {
|
|
1649
1718
|
ctxoRoot;
|
|
1650
1719
|
constructor(projectRoot) {
|
|
1651
|
-
this.ctxoRoot =
|
|
1720
|
+
this.ctxoRoot = join10(projectRoot, ".ctxo");
|
|
1652
1721
|
}
|
|
1653
1722
|
async run() {
|
|
1654
1723
|
console.error("[ctxo] Rebuilding SQLite cache from committed JSON index...");
|
|
@@ -1663,19 +1732,19 @@ var SyncCommand = class {
|
|
|
1663
1732
|
};
|
|
1664
1733
|
|
|
1665
1734
|
// src/cli/status-command.ts
|
|
1666
|
-
import { join as
|
|
1667
|
-
import { existsSync as
|
|
1735
|
+
import { join as join11 } from "path";
|
|
1736
|
+
import { existsSync as existsSync11 } from "fs";
|
|
1668
1737
|
import { execFileSync as execFileSync3 } from "child_process";
|
|
1669
1738
|
var StatusCommand = class {
|
|
1670
1739
|
projectRoot;
|
|
1671
1740
|
ctxoRoot;
|
|
1672
1741
|
constructor(projectRoot) {
|
|
1673
1742
|
this.projectRoot = projectRoot;
|
|
1674
|
-
this.ctxoRoot =
|
|
1743
|
+
this.ctxoRoot = join11(projectRoot, ".ctxo");
|
|
1675
1744
|
}
|
|
1676
1745
|
run() {
|
|
1677
|
-
const indexDir =
|
|
1678
|
-
if (!
|
|
1746
|
+
const indexDir = join11(this.ctxoRoot, "index");
|
|
1747
|
+
if (!existsSync11(indexDir)) {
|
|
1679
1748
|
console.error('[ctxo] No index found. Run "ctxo index" first.');
|
|
1680
1749
|
return;
|
|
1681
1750
|
}
|
|
@@ -1690,7 +1759,7 @@ var StatusCommand = class {
|
|
|
1690
1759
|
console.error(` Indexed files: ${indices.length}`);
|
|
1691
1760
|
console.error(` Total symbols: ${totalSymbols}`);
|
|
1692
1761
|
console.error(` Total edges: ${totalEdges}`);
|
|
1693
|
-
const cacheExists =
|
|
1762
|
+
const cacheExists = existsSync11(join11(this.ctxoRoot, ".cache", "symbols.db"));
|
|
1694
1763
|
console.error(` SQLite cache: ${cacheExists ? "present" : "missing (run ctxo sync)"}`);
|
|
1695
1764
|
if (indices.length > 0) {
|
|
1696
1765
|
console.error("");
|
|
@@ -1719,7 +1788,7 @@ var StatusCommand = class {
|
|
|
1719
1788
|
|
|
1720
1789
|
// src/cli/verify-command.ts
|
|
1721
1790
|
import { mkdtempSync, rmSync } from "fs";
|
|
1722
|
-
import { join as
|
|
1791
|
+
import { join as join12 } from "path";
|
|
1723
1792
|
import { tmpdir as tmpdir2 } from "os";
|
|
1724
1793
|
var VerifyCommand = class {
|
|
1725
1794
|
projectRoot;
|
|
@@ -1728,12 +1797,12 @@ var VerifyCommand = class {
|
|
|
1728
1797
|
}
|
|
1729
1798
|
async run() {
|
|
1730
1799
|
console.error("[ctxo] Verifying index freshness...");
|
|
1731
|
-
const tempDir = mkdtempSync(
|
|
1800
|
+
const tempDir = mkdtempSync(join12(tmpdir2(), "ctxo-verify-"));
|
|
1732
1801
|
try {
|
|
1733
|
-
const tempCtxo =
|
|
1802
|
+
const tempCtxo = join12(tempDir, ".ctxo");
|
|
1734
1803
|
const indexCmd = new IndexCommand(this.projectRoot, tempCtxo);
|
|
1735
1804
|
await indexCmd.run({ skipSideEffects: true });
|
|
1736
|
-
const committedReader = new JsonIndexReader(
|
|
1805
|
+
const committedReader = new JsonIndexReader(join12(this.projectRoot, ".ctxo"));
|
|
1737
1806
|
const freshReader = new JsonIndexReader(tempCtxo);
|
|
1738
1807
|
const committedIndices = committedReader.readAll();
|
|
1739
1808
|
const freshIndices = freshReader.readAll();
|
|
@@ -1766,20 +1835,20 @@ var VerifyCommand = class {
|
|
|
1766
1835
|
};
|
|
1767
1836
|
|
|
1768
1837
|
// src/cli/init-command.ts
|
|
1769
|
-
import { join as
|
|
1770
|
-
import { existsSync as
|
|
1838
|
+
import { join as join14 } from "path";
|
|
1839
|
+
import { existsSync as existsSync13, readFileSync as readFileSync8, writeFileSync as writeFileSync4, mkdirSync as mkdirSync5, chmodSync } from "fs";
|
|
1771
1840
|
import { createRequire as createRequire4 } from "module";
|
|
1772
1841
|
|
|
1773
1842
|
// src/cli/ai-rules.ts
|
|
1774
|
-
import { existsSync as
|
|
1775
|
-
import { join as
|
|
1843
|
+
import { existsSync as existsSync12, readFileSync as readFileSync7, writeFileSync as writeFileSync3, mkdirSync as mkdirSync4 } from "fs";
|
|
1844
|
+
import { join as join13, dirname as dirname6 } from "path";
|
|
1776
1845
|
var CTXO_HOOK_COMMAND = "ctxo gate-hook";
|
|
1777
1846
|
function ensureClaudeHook(projectRoot) {
|
|
1778
|
-
const filePath =
|
|
1847
|
+
const filePath = join13(projectRoot, ".claude", "settings.json");
|
|
1779
1848
|
const dir = dirname6(filePath);
|
|
1780
|
-
if (!
|
|
1849
|
+
if (!existsSync12(dir)) mkdirSync4(dir, { recursive: true });
|
|
1781
1850
|
const ctxoEntry = { matcher: "Edit", hooks: [{ type: "command", command: CTXO_HOOK_COMMAND }] };
|
|
1782
|
-
if (!
|
|
1851
|
+
if (!existsSync12(filePath)) {
|
|
1783
1852
|
writeFileSync3(filePath, JSON.stringify({ hooks: { PreToolUse: [ctxoEntry] } }, null, 2) + "\n", "utf-8");
|
|
1784
1853
|
return { file: ".claude/settings.json", action: "created" };
|
|
1785
1854
|
}
|
|
@@ -1815,7 +1884,7 @@ var PLATFORMS = [
|
|
|
1815
1884
|
function detectPlatforms(projectRoot) {
|
|
1816
1885
|
const detected = /* @__PURE__ */ new Set();
|
|
1817
1886
|
for (const p of PLATFORMS) {
|
|
1818
|
-
if (p.detectPaths.some((d) =>
|
|
1887
|
+
if (p.detectPaths.some((d) => existsSync12(join13(projectRoot, d)))) {
|
|
1819
1888
|
detected.add(p.id);
|
|
1820
1889
|
}
|
|
1821
1890
|
}
|
|
@@ -1877,15 +1946,15 @@ function generateRules(platformId) {
|
|
|
1877
1946
|
function installRules(projectRoot, platformId) {
|
|
1878
1947
|
const platform = PLATFORMS.find((p) => p.id === platformId);
|
|
1879
1948
|
if (!platform) throw new Error(`Unknown platform: ${platformId}`);
|
|
1880
|
-
const filePath =
|
|
1949
|
+
const filePath = join13(projectRoot, platform.file);
|
|
1881
1950
|
const content = generateRules(platformId);
|
|
1882
1951
|
const dir = dirname6(filePath);
|
|
1883
|
-
if (!
|
|
1952
|
+
if (!existsSync12(dir)) mkdirSync4(dir, { recursive: true });
|
|
1884
1953
|
if (platform.mode === "create") {
|
|
1885
1954
|
writeFileSync3(filePath, content, "utf-8");
|
|
1886
|
-
return { file: platform.file, action:
|
|
1955
|
+
return { file: platform.file, action: existsSync12(filePath) ? "updated" : "created" };
|
|
1887
1956
|
}
|
|
1888
|
-
if (!
|
|
1957
|
+
if (!existsSync12(filePath)) {
|
|
1889
1958
|
writeFileSync3(filePath, content, "utf-8");
|
|
1890
1959
|
return { file: platform.file, action: "created" };
|
|
1891
1960
|
}
|
|
@@ -1909,15 +1978,15 @@ function escapeRegExp(s) {
|
|
|
1909
1978
|
}
|
|
1910
1979
|
var SKILLS = ["ctxo-understand", "ctxo-safe-edit", "ctxo-review-pr"];
|
|
1911
1980
|
var SKILL_TARGET_DIR = {
|
|
1912
|
-
"claude-code": (root, skill) =>
|
|
1913
|
-
cursor: (root, skill) =>
|
|
1981
|
+
"claude-code": (root, skill) => join13(root, ".claude", "skills", skill, "SKILL.md"),
|
|
1982
|
+
cursor: (root, skill) => join13(root, ".cursor", "rules", `${skill}.mdc`)
|
|
1914
1983
|
};
|
|
1915
1984
|
function skillsSourceDir() {
|
|
1916
1985
|
let dir = import.meta.dirname;
|
|
1917
1986
|
for (let i = 0; i < 10; i++) {
|
|
1918
|
-
const candidate =
|
|
1919
|
-
if (
|
|
1920
|
-
const parent =
|
|
1987
|
+
const candidate = join13(dir, "skills");
|
|
1988
|
+
if (existsSync12(join13(candidate, "ctxo-safe-edit", "SKILL.md"))) return candidate;
|
|
1989
|
+
const parent = join13(dir, "..");
|
|
1921
1990
|
if (parent === dir) break;
|
|
1922
1991
|
dir = parent;
|
|
1923
1992
|
}
|
|
@@ -1929,10 +1998,10 @@ function installSkills(projectRoot, platformId) {
|
|
|
1929
1998
|
const src = skillsSourceDir();
|
|
1930
1999
|
const out = [];
|
|
1931
2000
|
for (const skill of SKILLS) {
|
|
1932
|
-
const from =
|
|
2001
|
+
const from = join13(src, skill, "SKILL.md");
|
|
1933
2002
|
const to = target(projectRoot, skill);
|
|
1934
2003
|
mkdirSync4(dirname6(to), { recursive: true });
|
|
1935
|
-
const existed =
|
|
2004
|
+
const existed = existsSync12(to);
|
|
1936
2005
|
writeFileSync3(to, readFileSync7(from, "utf-8"), "utf-8");
|
|
1937
2006
|
out.push({ file: to, action: existed ? "updated" : "created" });
|
|
1938
2007
|
}
|
|
@@ -1940,8 +2009,8 @@ function installSkills(projectRoot, platformId) {
|
|
|
1940
2009
|
}
|
|
1941
2010
|
var GITIGNORE_ENTRY = ".ctxo/.cache/";
|
|
1942
2011
|
function ensureGitignore(projectRoot) {
|
|
1943
|
-
const filePath =
|
|
1944
|
-
if (!
|
|
2012
|
+
const filePath = join13(projectRoot, ".gitignore");
|
|
2013
|
+
if (!existsSync12(filePath)) {
|
|
1945
2014
|
writeFileSync3(filePath, `# Ctxo local cache (auto-rebuilt, never committed)
|
|
1946
2015
|
${GITIGNORE_ENTRY}
|
|
1947
2016
|
`, "utf-8");
|
|
@@ -1982,10 +2051,10 @@ gate:
|
|
|
1982
2051
|
sensitivity: balanced # strict | balanced | lenient
|
|
1983
2052
|
`;
|
|
1984
2053
|
function ensureConfig(projectRoot) {
|
|
1985
|
-
const filePath =
|
|
2054
|
+
const filePath = join13(projectRoot, ".ctxo", "config.yaml");
|
|
1986
2055
|
const dir = dirname6(filePath);
|
|
1987
|
-
if (!
|
|
1988
|
-
if (
|
|
2056
|
+
if (!existsSync12(dir)) mkdirSync4(dir, { recursive: true });
|
|
2057
|
+
if (existsSync12(filePath)) {
|
|
1989
2058
|
return { file: ".ctxo/config.yaml", action: "skipped" };
|
|
1990
2059
|
}
|
|
1991
2060
|
writeFileSync3(filePath, DEFAULT_CONFIG, "utf-8");
|
|
@@ -2020,11 +2089,11 @@ function getMcpConfigTargets(selectedPlatformIds) {
|
|
|
2020
2089
|
return [...targets.values()];
|
|
2021
2090
|
}
|
|
2022
2091
|
function ensureMcpConfig(projectRoot, target) {
|
|
2023
|
-
const filePath =
|
|
2092
|
+
const filePath = join13(projectRoot, target.file);
|
|
2024
2093
|
const dir = dirname6(filePath);
|
|
2025
|
-
if (!
|
|
2094
|
+
if (!existsSync12(dir)) mkdirSync4(dir, { recursive: true });
|
|
2026
2095
|
const entry = target.extraFields ? { ...target.extraFields, ...CTXO_MCP_ENTRY } : { ...CTXO_MCP_ENTRY };
|
|
2027
|
-
if (!
|
|
2096
|
+
if (!existsSync12(filePath)) {
|
|
2028
2097
|
const config = { [target.serverKey]: { ctxo: entry } };
|
|
2029
2098
|
writeFileSync3(filePath, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
2030
2099
|
return { file: target.file, action: "created" };
|
|
@@ -2140,13 +2209,13 @@ var InitCommand = class {
|
|
|
2140
2209
|
this.projectRoot = projectRoot;
|
|
2141
2210
|
}
|
|
2142
2211
|
installHooks() {
|
|
2143
|
-
const hooksDir =
|
|
2212
|
+
const hooksDir = join14(this.projectRoot, ".git", "hooks");
|
|
2144
2213
|
mkdirSync5(hooksDir, { recursive: true });
|
|
2145
2214
|
this.installHook(hooksDir, "post-commit", POST_COMMIT_CONTENT);
|
|
2146
2215
|
this.installHook(hooksDir, "post-merge", POST_MERGE_CONTENT);
|
|
2147
2216
|
}
|
|
2148
2217
|
async run(options = {}) {
|
|
2149
|
-
if (!
|
|
2218
|
+
if (!existsSync13(join14(this.projectRoot, ".git"))) {
|
|
2150
2219
|
console.error('[ctxo] Not a git repository. Run "git init" first.');
|
|
2151
2220
|
process.exit(1);
|
|
2152
2221
|
}
|
|
@@ -2183,8 +2252,8 @@ var InitCommand = class {
|
|
|
2183
2252
|
clack.cancel("Setup cancelled.");
|
|
2184
2253
|
process.exit(0);
|
|
2185
2254
|
}
|
|
2186
|
-
const fullPath =
|
|
2187
|
-
if (!
|
|
2255
|
+
const fullPath = join14(this.projectRoot, indexDir);
|
|
2256
|
+
if (!existsSync13(fullPath)) mkdirSync5(fullPath, { recursive: true });
|
|
2188
2257
|
console.error("");
|
|
2189
2258
|
}
|
|
2190
2259
|
stepNum++;
|
|
@@ -2360,8 +2429,8 @@ var InitCommand = class {
|
|
|
2360
2429
|
const gitignoreResult = ensureGitignore(this.projectRoot);
|
|
2361
2430
|
const configResult = ensureConfig(this.projectRoot);
|
|
2362
2431
|
if (!options.rulesOnly) {
|
|
2363
|
-
const indexDir =
|
|
2364
|
-
if (!
|
|
2432
|
+
const indexDir = join14(this.projectRoot, ".ctxo", "index");
|
|
2433
|
+
if (!existsSync13(indexDir)) mkdirSync5(indexDir, { recursive: true });
|
|
2365
2434
|
console.error("[ctxo] \u2713 .ctxo/index/ ready");
|
|
2366
2435
|
}
|
|
2367
2436
|
if (configResult.action !== "skipped") {
|
|
@@ -2420,7 +2489,7 @@ var InitCommand = class {
|
|
|
2420
2489
|
for (const id of toolIds) {
|
|
2421
2490
|
const platform = PLATFORMS.find((p) => p.id === id);
|
|
2422
2491
|
if (platform) {
|
|
2423
|
-
const exists =
|
|
2492
|
+
const exists = existsSync13(join14(this.projectRoot, platform.file));
|
|
2424
2493
|
const action = platform.mode === "append" && exists ? "append section" : "create";
|
|
2425
2494
|
console.error(` ${platform.file.padEnd(42)} (${action})`);
|
|
2426
2495
|
}
|
|
@@ -2439,21 +2508,21 @@ var InitCommand = class {
|
|
|
2439
2508
|
/* ---------------------------------------------------------------- */
|
|
2440
2509
|
async getVersion() {
|
|
2441
2510
|
try {
|
|
2442
|
-
const pkgPath =
|
|
2443
|
-
if (
|
|
2511
|
+
const pkgPath = join14(this.projectRoot, "node_modules", "@ctxo", "cli", "package.json");
|
|
2512
|
+
if (existsSync13(pkgPath)) {
|
|
2444
2513
|
return JSON.parse(readFileSync8(pkgPath, "utf-8")).version;
|
|
2445
2514
|
}
|
|
2446
|
-
const { createRequire:
|
|
2447
|
-
const
|
|
2448
|
-
return
|
|
2515
|
+
const { createRequire: createRequire9 } = await import("module");
|
|
2516
|
+
const require5 = createRequire9(import.meta.url);
|
|
2517
|
+
return require5("../../package.json").version;
|
|
2449
2518
|
} catch {
|
|
2450
2519
|
return void 0;
|
|
2451
2520
|
}
|
|
2452
2521
|
}
|
|
2453
2522
|
installHook(hooksDir, hookName, hookContent) {
|
|
2454
|
-
const hookPath =
|
|
2523
|
+
const hookPath = join14(hooksDir, hookName);
|
|
2455
2524
|
let existing = "";
|
|
2456
|
-
if (
|
|
2525
|
+
if (existsSync13(hookPath)) {
|
|
2457
2526
|
existing = readFileSync8(hookPath, "utf-8");
|
|
2458
2527
|
if (existing.includes(CTXO_START)) return;
|
|
2459
2528
|
} else {
|
|
@@ -2466,7 +2535,7 @@ var InitCommand = class {
|
|
|
2466
2535
|
};
|
|
2467
2536
|
|
|
2468
2537
|
// src/cli/watch-command.ts
|
|
2469
|
-
import { join as
|
|
2538
|
+
import { join as join15, extname as extname4, relative as relative2 } from "path";
|
|
2470
2539
|
import { readFileSync as readFileSync9 } from "fs";
|
|
2471
2540
|
|
|
2472
2541
|
// src/adapters/watcher/chokidar-watcher-adapter.ts
|
|
@@ -2516,12 +2585,12 @@ var WatchCommand = class {
|
|
|
2516
2585
|
ctxoRoot;
|
|
2517
2586
|
constructor(projectRoot) {
|
|
2518
2587
|
this.projectRoot = projectRoot;
|
|
2519
|
-
this.ctxoRoot =
|
|
2588
|
+
this.ctxoRoot = join15(projectRoot, ".ctxo");
|
|
2520
2589
|
}
|
|
2521
2590
|
async run() {
|
|
2522
2591
|
console.error("[ctxo] Starting file watcher...");
|
|
2523
2592
|
const registry = new LanguageAdapterRegistry();
|
|
2524
|
-
|
|
2593
|
+
const keepAliveByExt = /* @__PURE__ */ new Map();
|
|
2525
2594
|
for (const { plugin, adapter } of await loadPlugins(this.projectRoot)) {
|
|
2526
2595
|
if (typeof adapter.initialize === "function") {
|
|
2527
2596
|
try {
|
|
@@ -2532,15 +2601,12 @@ var WatchCommand = class {
|
|
|
2532
2601
|
}
|
|
2533
2602
|
}
|
|
2534
2603
|
registry.register(plugin.extensions, adapter);
|
|
2535
|
-
const
|
|
2536
|
-
if (typeof
|
|
2537
|
-
const
|
|
2538
|
-
if (
|
|
2539
|
-
const
|
|
2540
|
-
|
|
2541
|
-
roslynAdapter = roslyn;
|
|
2542
|
-
console.error("[ctxo] C# watch: Roslyn keep-alive active (full tier, <100ms per change)");
|
|
2543
|
-
}
|
|
2604
|
+
const capable = adapter;
|
|
2605
|
+
if (typeof capable.getIncrementalReindex === "function") {
|
|
2606
|
+
const cap = capable.getIncrementalReindex();
|
|
2607
|
+
if (cap?.isReady() && await cap.startKeepAlive()) {
|
|
2608
|
+
for (const ext of plugin.extensions) keepAliveByExt.set(ext.toLowerCase(), cap);
|
|
2609
|
+
console.error(`[ctxo] ${plugin.id} watch: keep-alive active (full tier, fast incremental re-index)`);
|
|
2544
2610
|
}
|
|
2545
2611
|
}
|
|
2546
2612
|
console.error(`[ctxo] Plugin ${plugin.id}@${plugin.version} (${plugin.tier} tier)`);
|
|
@@ -2561,8 +2627,9 @@ var WatchCommand = class {
|
|
|
2561
2627
|
if (!adapter) return;
|
|
2562
2628
|
const relativePath = relative2(this.projectRoot, filePath).replace(/\\/g, "/");
|
|
2563
2629
|
try {
|
|
2564
|
-
|
|
2565
|
-
|
|
2630
|
+
const cap = keepAliveByExt.get(extname4(filePath).toLowerCase());
|
|
2631
|
+
if (cap?.isReady()) {
|
|
2632
|
+
const result = await cap.reindexFile(relativePath);
|
|
2566
2633
|
if (result) {
|
|
2567
2634
|
const commits2 = await gitAdapter.getCommitHistory(relativePath);
|
|
2568
2635
|
const intent2 = commits2.map((c) => ({
|
|
@@ -2598,7 +2665,7 @@ var WatchCommand = class {
|
|
|
2598
2665
|
};
|
|
2599
2666
|
writer.write(fileIndex2);
|
|
2600
2667
|
storage.writeSymbolFile(fileIndex2);
|
|
2601
|
-
console.error(`[ctxo] Re-indexed (
|
|
2668
|
+
console.error(`[ctxo] Re-indexed (keep-alive): ${relativePath}`);
|
|
2602
2669
|
return;
|
|
2603
2670
|
}
|
|
2604
2671
|
}
|
|
@@ -2686,8 +2753,9 @@ var WatchCommand = class {
|
|
|
2686
2753
|
clearTimeout(timeout);
|
|
2687
2754
|
}
|
|
2688
2755
|
if (communitySnapshotTimer) clearTimeout(communitySnapshotTimer);
|
|
2689
|
-
const
|
|
2690
|
-
|
|
2756
|
+
const uniques = new Set(keepAliveByExt.values());
|
|
2757
|
+
const shutdowns = Promise.all([...uniques].map((c) => c.dispose()));
|
|
2758
|
+
shutdowns.then(() => {
|
|
2691
2759
|
watcher.stop().then(() => {
|
|
2692
2760
|
storage.close();
|
|
2693
2761
|
console.error("[ctxo] Watcher stopped");
|
|
@@ -2731,21 +2799,21 @@ function readHeadSha() {
|
|
|
2731
2799
|
}
|
|
2732
2800
|
|
|
2733
2801
|
// src/cli/stats-command.ts
|
|
2734
|
-
import { join as
|
|
2735
|
-
import { existsSync as
|
|
2802
|
+
import { join as join16 } from "path";
|
|
2803
|
+
import { existsSync as existsSync14, readFileSync as readFileSync10 } from "fs";
|
|
2736
2804
|
import initSqlJs from "sql.js";
|
|
2737
2805
|
var StatsCommand = class {
|
|
2738
2806
|
ctxoRoot;
|
|
2739
2807
|
constructor(projectRoot) {
|
|
2740
|
-
this.ctxoRoot =
|
|
2808
|
+
this.ctxoRoot = join16(projectRoot, ".ctxo");
|
|
2741
2809
|
}
|
|
2742
2810
|
async run(options) {
|
|
2743
2811
|
if (!this.isStatsEnabled()) {
|
|
2744
2812
|
console.error("[ctxo] Stats collection is disabled in .ctxo/config.yaml");
|
|
2745
2813
|
return;
|
|
2746
2814
|
}
|
|
2747
|
-
const dbPath =
|
|
2748
|
-
if (!
|
|
2815
|
+
const dbPath = join16(this.ctxoRoot, ".cache", "symbols.db");
|
|
2816
|
+
if (!existsSync14(dbPath)) {
|
|
2749
2817
|
if (options.json) {
|
|
2750
2818
|
process.stdout.write(JSON.stringify(this.emptyReport(options.days)) + "\n");
|
|
2751
2819
|
} else {
|
|
@@ -2792,8 +2860,8 @@ var StatsCommand = class {
|
|
|
2792
2860
|
}
|
|
2793
2861
|
}
|
|
2794
2862
|
isStatsEnabled() {
|
|
2795
|
-
const configPath =
|
|
2796
|
-
if (!
|
|
2863
|
+
const configPath = join16(this.ctxoRoot, "config.yaml");
|
|
2864
|
+
if (!existsSync14(configPath)) return true;
|
|
2797
2865
|
try {
|
|
2798
2866
|
const raw = readFileSync10(configPath, "utf-8");
|
|
2799
2867
|
const match = raw.match(/stats:\s*\n\s*enabled:\s*(true|false)/);
|
|
@@ -2884,8 +2952,8 @@ var StatsCommand = class {
|
|
|
2884
2952
|
};
|
|
2885
2953
|
|
|
2886
2954
|
// src/cli/doctor-command.ts
|
|
2887
|
-
import { join as
|
|
2888
|
-
import { existsSync as
|
|
2955
|
+
import { join as join24 } from "path";
|
|
2956
|
+
import { existsSync as existsSync22 } from "fs";
|
|
2889
2957
|
|
|
2890
2958
|
// src/adapters/diagnostics/health-checker.ts
|
|
2891
2959
|
var log4 = createLogger("ctxo:doctor");
|
|
@@ -3220,10 +3288,111 @@ function pluginResolves(packageName) {
|
|
|
3220
3288
|
}
|
|
3221
3289
|
}
|
|
3222
3290
|
|
|
3291
|
+
// src/adapters/diagnostics/checks/java-tier-check.ts
|
|
3292
|
+
import { createRequire as createRequire7 } from "module";
|
|
3293
|
+
import { existsSync as existsSync15, readdirSync as readdirSync3 } from "fs";
|
|
3294
|
+
import { join as join17, dirname as dirname7 } from "path";
|
|
3295
|
+
var require4 = createRequire7(import.meta.url);
|
|
3296
|
+
function evaluateJavaTier(i) {
|
|
3297
|
+
const id = "java_tier";
|
|
3298
|
+
const title = "Java analysis tier";
|
|
3299
|
+
if (!i.hasJava) {
|
|
3300
|
+
return { id, title, status: "pass", message: "no Java sources detected" };
|
|
3301
|
+
}
|
|
3302
|
+
const jreOk = (i.jreMajor ?? 0) >= 17;
|
|
3303
|
+
if (jreOk && i.analyzerInstalled) {
|
|
3304
|
+
return { id, title, status: "pass", message: `full tier (JRE ${i.jreMajor}, analyzer installed)` };
|
|
3305
|
+
}
|
|
3306
|
+
if (jreOk && !i.analyzerInstalled) {
|
|
3307
|
+
return {
|
|
3308
|
+
id,
|
|
3309
|
+
title,
|
|
3310
|
+
status: "warn",
|
|
3311
|
+
message: `syntax tier (JRE ${i.jreMajor} present, analyzer missing)`,
|
|
3312
|
+
fix: 'Run "ctxo install java --full-tier" for resolved call/use edges'
|
|
3313
|
+
};
|
|
3314
|
+
}
|
|
3315
|
+
if (!jreOk && i.analyzerInstalled) {
|
|
3316
|
+
return {
|
|
3317
|
+
id,
|
|
3318
|
+
title,
|
|
3319
|
+
status: "warn",
|
|
3320
|
+
message: "syntax tier (analyzer installed but no JRE 17+)",
|
|
3321
|
+
fix: "Install a JRE 17+ and ensure it is on PATH (or set JAVA_HOME)"
|
|
3322
|
+
};
|
|
3323
|
+
}
|
|
3324
|
+
return {
|
|
3325
|
+
id,
|
|
3326
|
+
title,
|
|
3327
|
+
status: "pass",
|
|
3328
|
+
message: 'syntax tier (install JRE 17+ and run "ctxo install java --full-tier" for full tier)'
|
|
3329
|
+
};
|
|
3330
|
+
}
|
|
3331
|
+
function analyzerJarPresent() {
|
|
3332
|
+
try {
|
|
3333
|
+
const pkgJson = require4.resolve("@ctxo/lang-java-analyzer/package.json");
|
|
3334
|
+
const jarPath = join17(dirname7(pkgJson), "jar", "ctxo-jdt-analyzer.jar");
|
|
3335
|
+
return existsSync15(jarPath);
|
|
3336
|
+
} catch {
|
|
3337
|
+
return false;
|
|
3338
|
+
}
|
|
3339
|
+
}
|
|
3340
|
+
var SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
3341
|
+
"node_modules",
|
|
3342
|
+
"target",
|
|
3343
|
+
"build",
|
|
3344
|
+
"out",
|
|
3345
|
+
"bin",
|
|
3346
|
+
".git",
|
|
3347
|
+
".ctxo",
|
|
3348
|
+
"dist",
|
|
3349
|
+
"coverage"
|
|
3350
|
+
]);
|
|
3351
|
+
function findJavaFile(root, maxDepth) {
|
|
3352
|
+
function walk(dir, depth) {
|
|
3353
|
+
let entries;
|
|
3354
|
+
try {
|
|
3355
|
+
entries = readdirSync3(dir, { withFileTypes: true });
|
|
3356
|
+
} catch {
|
|
3357
|
+
return false;
|
|
3358
|
+
}
|
|
3359
|
+
for (const entry of entries) {
|
|
3360
|
+
if (entry.isSymbolicLink()) continue;
|
|
3361
|
+
if (entry.isFile() && entry.name.endsWith(".java")) return true;
|
|
3362
|
+
}
|
|
3363
|
+
if (depth >= maxDepth) return false;
|
|
3364
|
+
for (const entry of entries) {
|
|
3365
|
+
if (entry.isSymbolicLink()) continue;
|
|
3366
|
+
if (entry.isDirectory() && !SKIP_DIRS.has(entry.name)) {
|
|
3367
|
+
if (walk(join17(dir, entry.name), depth + 1)) return true;
|
|
3368
|
+
}
|
|
3369
|
+
}
|
|
3370
|
+
return false;
|
|
3371
|
+
}
|
|
3372
|
+
return walk(root, 0);
|
|
3373
|
+
}
|
|
3374
|
+
function projectHasJava(root) {
|
|
3375
|
+
for (const f of ["pom.xml", "build.gradle", "build.gradle.kts"]) {
|
|
3376
|
+
if (existsSync15(join17(root, f))) return true;
|
|
3377
|
+
}
|
|
3378
|
+
return findJavaFile(root, 4);
|
|
3379
|
+
}
|
|
3380
|
+
var JavaTierCheck = class {
|
|
3381
|
+
id = "java_tier";
|
|
3382
|
+
title = "Java analysis tier";
|
|
3383
|
+
async run(ctx) {
|
|
3384
|
+
return evaluateJavaTier({
|
|
3385
|
+
hasJava: projectHasJava(ctx.projectRoot),
|
|
3386
|
+
jreMajor: detectJavaMajor(),
|
|
3387
|
+
analyzerInstalled: analyzerJarPresent()
|
|
3388
|
+
});
|
|
3389
|
+
}
|
|
3390
|
+
};
|
|
3391
|
+
|
|
3223
3392
|
// src/adapters/diagnostics/checks/git-check.ts
|
|
3224
3393
|
import { execFileSync as execFileSync5 } from "child_process";
|
|
3225
|
-
import { existsSync as
|
|
3226
|
-
import { join as
|
|
3394
|
+
import { existsSync as existsSync16 } from "fs";
|
|
3395
|
+
import { join as join18 } from "path";
|
|
3227
3396
|
var GitBinaryCheck = class {
|
|
3228
3397
|
id = "git_binary";
|
|
3229
3398
|
title = "Git available";
|
|
@@ -3240,7 +3409,7 @@ var GitRepoCheck = class {
|
|
|
3240
3409
|
id = "git_repo";
|
|
3241
3410
|
title = "Git repository";
|
|
3242
3411
|
async run(ctx) {
|
|
3243
|
-
if (
|
|
3412
|
+
if (existsSync16(join18(ctx.projectRoot, ".git"))) {
|
|
3244
3413
|
return { id: this.id, title: this.title, status: "pass", message: ctx.projectRoot, value: ctx.projectRoot };
|
|
3245
3414
|
}
|
|
3246
3415
|
return { id: this.id, title: this.title, status: "fail", message: "Not a git repository", fix: 'Run "git init" or clone a repository' };
|
|
@@ -3248,9 +3417,9 @@ var GitRepoCheck = class {
|
|
|
3248
3417
|
};
|
|
3249
3418
|
|
|
3250
3419
|
// src/adapters/diagnostics/checks/index-check.ts
|
|
3251
|
-
import { existsSync as
|
|
3420
|
+
import { existsSync as existsSync17 } from "fs";
|
|
3252
3421
|
import { execFileSync as execFileSync6 } from "child_process";
|
|
3253
|
-
import { join as
|
|
3422
|
+
import { join as join19 } from "path";
|
|
3254
3423
|
function pass2(id, title, message, value) {
|
|
3255
3424
|
return { id, title, status: "pass", message, value };
|
|
3256
3425
|
}
|
|
@@ -3267,8 +3436,8 @@ var IndexDirectoryCheck = class {
|
|
|
3267
3436
|
id = "index_directory";
|
|
3268
3437
|
title = "Index directory";
|
|
3269
3438
|
async run(ctx) {
|
|
3270
|
-
const indexDir =
|
|
3271
|
-
if (!
|
|
3439
|
+
const indexDir = join19(ctx.ctxoRoot, "index");
|
|
3440
|
+
if (!existsSync17(indexDir)) {
|
|
3272
3441
|
return fail(this.id, this.title, "No index directory", 'Run "ctxo index"');
|
|
3273
3442
|
}
|
|
3274
3443
|
const indices = getIndices(ctx);
|
|
@@ -3362,8 +3531,8 @@ var CoChangesCacheCheck = class {
|
|
|
3362
3531
|
id = "co_changes_cache";
|
|
3363
3532
|
title = "Co-changes cache";
|
|
3364
3533
|
async run(ctx) {
|
|
3365
|
-
const coChangesPath =
|
|
3366
|
-
if (
|
|
3534
|
+
const coChangesPath = join19(ctx.ctxoRoot, "index", "co-changes.json");
|
|
3535
|
+
if (existsSync17(coChangesPath)) {
|
|
3367
3536
|
return pass2(this.id, this.title, "present");
|
|
3368
3537
|
}
|
|
3369
3538
|
return warn2(this.id, this.title, "missing (co-change analysis disabled)", 'Run "ctxo index" with git history');
|
|
@@ -3387,14 +3556,14 @@ var SchemaVersionCheck = class {
|
|
|
3387
3556
|
};
|
|
3388
3557
|
|
|
3389
3558
|
// src/adapters/diagnostics/checks/storage-check.ts
|
|
3390
|
-
import { existsSync as
|
|
3391
|
-
import { join as
|
|
3559
|
+
import { existsSync as existsSync18, readFileSync as readFileSync11 } from "fs";
|
|
3560
|
+
import { join as join20 } from "path";
|
|
3392
3561
|
var SqliteCacheCheck = class {
|
|
3393
3562
|
id = "sqlite_cache";
|
|
3394
3563
|
title = "SQLite cache";
|
|
3395
3564
|
async run(ctx) {
|
|
3396
|
-
const dbPath =
|
|
3397
|
-
if (!
|
|
3565
|
+
const dbPath = join20(ctx.ctxoRoot, ".cache", "symbols.db");
|
|
3566
|
+
if (!existsSync18(dbPath)) {
|
|
3398
3567
|
return { id: this.id, title: this.title, status: "warn", message: "SQLite cache missing", fix: 'Run "ctxo sync" to rebuild' };
|
|
3399
3568
|
}
|
|
3400
3569
|
try {
|
|
@@ -3416,14 +3585,14 @@ var SqliteCacheCheck = class {
|
|
|
3416
3585
|
};
|
|
3417
3586
|
|
|
3418
3587
|
// src/adapters/diagnostics/checks/config-check.ts
|
|
3419
|
-
import { existsSync as
|
|
3420
|
-
import { join as
|
|
3588
|
+
import { existsSync as existsSync19, readFileSync as readFileSync12 } from "fs";
|
|
3589
|
+
import { join as join21 } from "path";
|
|
3421
3590
|
var ConfigFileCheck = class {
|
|
3422
3591
|
id = "config_file";
|
|
3423
3592
|
title = "Config file";
|
|
3424
3593
|
async run(ctx) {
|
|
3425
|
-
const configPath =
|
|
3426
|
-
if (!
|
|
3594
|
+
const configPath = join21(ctx.ctxoRoot, "config.yaml");
|
|
3595
|
+
if (!existsSync19(configPath)) {
|
|
3427
3596
|
return { id: this.id, title: this.title, status: "warn", message: "No config.yaml (using defaults)" };
|
|
3428
3597
|
}
|
|
3429
3598
|
try {
|
|
@@ -3461,8 +3630,8 @@ var ConfigFileCheck = class {
|
|
|
3461
3630
|
};
|
|
3462
3631
|
|
|
3463
3632
|
// src/adapters/diagnostics/checks/disk-check.ts
|
|
3464
|
-
import { existsSync as
|
|
3465
|
-
import { join as
|
|
3633
|
+
import { existsSync as existsSync20, readdirSync as readdirSync4, statSync as statSync2 } from "fs";
|
|
3634
|
+
import { join as join22 } from "path";
|
|
3466
3635
|
var DEFAULT_THRESHOLDS = { warnMb: 100, failMb: 500 };
|
|
3467
3636
|
var DiskUsageCheck = class {
|
|
3468
3637
|
id = "disk_usage";
|
|
@@ -3472,7 +3641,7 @@ var DiskUsageCheck = class {
|
|
|
3472
3641
|
this.thresholds = thresholds;
|
|
3473
3642
|
}
|
|
3474
3643
|
async run(ctx) {
|
|
3475
|
-
if (!
|
|
3644
|
+
if (!existsSync20(ctx.ctxoRoot)) {
|
|
3476
3645
|
return { id: this.id, title: this.title, status: "pass", message: "no .ctxo/ directory", value: "0" };
|
|
3477
3646
|
}
|
|
3478
3647
|
const bytes = this.getDirSize(ctx.ctxoRoot);
|
|
@@ -3489,12 +3658,12 @@ var DiskUsageCheck = class {
|
|
|
3489
3658
|
getDirSize(dirPath) {
|
|
3490
3659
|
let total = 0;
|
|
3491
3660
|
try {
|
|
3492
|
-
const entries =
|
|
3661
|
+
const entries = readdirSync4(dirPath, { withFileTypes: true, recursive: true });
|
|
3493
3662
|
for (const entry of entries) {
|
|
3494
3663
|
if (entry.isFile()) {
|
|
3495
3664
|
try {
|
|
3496
3665
|
const parent = entry.parentPath ?? entry.path ?? dirPath;
|
|
3497
|
-
total += statSync2(
|
|
3666
|
+
total += statSync2(join22(parent, entry.name)).size;
|
|
3498
3667
|
} catch {
|
|
3499
3668
|
}
|
|
3500
3669
|
}
|
|
@@ -3507,9 +3676,9 @@ var DiskUsageCheck = class {
|
|
|
3507
3676
|
|
|
3508
3677
|
// src/cli/doctor-fix.ts
|
|
3509
3678
|
import { execFileSync as execFileSync7 } from "child_process";
|
|
3510
|
-
import { appendFileSync, existsSync as
|
|
3511
|
-
import { createRequire as
|
|
3512
|
-
import { dirname as
|
|
3679
|
+
import { appendFileSync, existsSync as existsSync21, mkdirSync as mkdirSync6, rmSync as rmSync2, unlinkSync as unlinkSync2 } from "fs";
|
|
3680
|
+
import { createRequire as createRequire8 } from "module";
|
|
3681
|
+
import { dirname as dirname8, join as join23 } from "path";
|
|
3513
3682
|
async function applyFixes(report, options) {
|
|
3514
3683
|
if (isCI(process.env) && !options.yes && !options.dryRun) {
|
|
3515
3684
|
return {
|
|
@@ -3644,8 +3813,8 @@ async function fixIndex(projectRoot, dryRun) {
|
|
|
3644
3813
|
}
|
|
3645
3814
|
async function pruneOrphans(projectRoot, ctxoRoot, dryRun) {
|
|
3646
3815
|
if (dryRun) return;
|
|
3647
|
-
const indexDir =
|
|
3648
|
-
if (!
|
|
3816
|
+
const indexDir = join23(ctxoRoot, "index");
|
|
3817
|
+
if (!existsSync21(indexDir)) return;
|
|
3649
3818
|
const sourceFiles = listGitFiles2(projectRoot);
|
|
3650
3819
|
if (sourceFiles.size === 0) return;
|
|
3651
3820
|
const reader = new JsonIndexReader(ctxoRoot);
|
|
@@ -3653,9 +3822,9 @@ async function pruneOrphans(projectRoot, ctxoRoot, dryRun) {
|
|
|
3653
3822
|
let removed = 0;
|
|
3654
3823
|
for (const idx of indices) {
|
|
3655
3824
|
if (sourceFiles.has(idx.file)) continue;
|
|
3656
|
-
const jsonPath =
|
|
3825
|
+
const jsonPath = join23(indexDir, `${idx.file}.json`);
|
|
3657
3826
|
try {
|
|
3658
|
-
if (
|
|
3827
|
+
if (existsSync21(jsonPath)) {
|
|
3659
3828
|
unlinkSync2(jsonPath);
|
|
3660
3829
|
removed += 1;
|
|
3661
3830
|
pruneEmptyParents(jsonPath, indexDir);
|
|
@@ -3666,14 +3835,14 @@ async function pruneOrphans(projectRoot, ctxoRoot, dryRun) {
|
|
|
3666
3835
|
if (removed === 0) return;
|
|
3667
3836
|
}
|
|
3668
3837
|
function pruneEmptyParents(filePath, stopAt) {
|
|
3669
|
-
let dir =
|
|
3838
|
+
let dir = dirname8(filePath);
|
|
3670
3839
|
while (dir !== stopAt && dir.startsWith(stopAt)) {
|
|
3671
3840
|
try {
|
|
3672
3841
|
rmSync2(dir, { recursive: false });
|
|
3673
3842
|
} catch {
|
|
3674
3843
|
return;
|
|
3675
3844
|
}
|
|
3676
|
-
dir =
|
|
3845
|
+
dir = dirname8(dir);
|
|
3677
3846
|
}
|
|
3678
3847
|
}
|
|
3679
3848
|
function listGitFiles2(projectRoot) {
|
|
@@ -3689,12 +3858,12 @@ function listGitFiles2(projectRoot) {
|
|
|
3689
3858
|
}
|
|
3690
3859
|
}
|
|
3691
3860
|
async function fixSqliteCache(projectRoot, ctxoRoot, dryRun) {
|
|
3692
|
-
const cacheDir =
|
|
3861
|
+
const cacheDir = join23(ctxoRoot, ".cache");
|
|
3693
3862
|
if (dryRun) return;
|
|
3694
|
-
if (
|
|
3863
|
+
if (existsSync21(cacheDir)) rmSync2(cacheDir, { recursive: true, force: true });
|
|
3695
3864
|
await new SyncCommand(projectRoot).run();
|
|
3696
3865
|
}
|
|
3697
|
-
var doctorRequire =
|
|
3866
|
+
var doctorRequire = createRequire8(import.meta.url);
|
|
3698
3867
|
function canResolvePlugin(lang) {
|
|
3699
3868
|
try {
|
|
3700
3869
|
doctorRequire.resolve(`${officialPluginFor(lang)}/package.json`);
|
|
@@ -3708,8 +3877,8 @@ function isCI(env) {
|
|
|
3708
3877
|
}
|
|
3709
3878
|
function writeLog(ctxoRoot, attempts, halted, reason) {
|
|
3710
3879
|
try {
|
|
3711
|
-
if (!
|
|
3712
|
-
const path =
|
|
3880
|
+
if (!existsSync21(ctxoRoot)) mkdirSync6(ctxoRoot, { recursive: true });
|
|
3881
|
+
const path = join23(ctxoRoot, "doctor-fix.log");
|
|
3713
3882
|
const ts = (/* @__PURE__ */ new Date()).toISOString();
|
|
3714
3883
|
const lines = [`# ${ts} doctor --fix${halted ? " (halted: " + reason + ")" : ""}`];
|
|
3715
3884
|
for (const a of attempts) {
|
|
@@ -3742,10 +3911,10 @@ var DoctorCommand = class {
|
|
|
3742
3911
|
ctxoRoot;
|
|
3743
3912
|
constructor(projectRoot) {
|
|
3744
3913
|
this.projectRoot = projectRoot;
|
|
3745
|
-
this.ctxoRoot =
|
|
3914
|
+
this.ctxoRoot = join24(projectRoot, ".ctxo");
|
|
3746
3915
|
}
|
|
3747
3916
|
async run(options = {}) {
|
|
3748
|
-
const { getVersion: getVersion2 } = await import("./cli-router-
|
|
3917
|
+
const { getVersion: getVersion2 } = await import("./cli-router-2JVPBW4S.js");
|
|
3749
3918
|
console.error(`ctxo v${getVersion2()} \u2014 health check
|
|
3750
3919
|
`);
|
|
3751
3920
|
const checks = [
|
|
@@ -3760,6 +3929,7 @@ var DoctorCommand = class {
|
|
|
3760
3929
|
new TsMorphCheck(),
|
|
3761
3930
|
new TreeSitterCheck(),
|
|
3762
3931
|
new LanguageCoverageCheck(),
|
|
3932
|
+
new JavaTierCheck(),
|
|
3763
3933
|
new DiskUsageCheck(),
|
|
3764
3934
|
new SymbolCountCheck(),
|
|
3765
3935
|
new EdgeCountCheck(),
|
|
@@ -3767,8 +3937,8 @@ var DoctorCommand = class {
|
|
|
3767
3937
|
new CoChangesCacheCheck(),
|
|
3768
3938
|
new SchemaVersionCheck()
|
|
3769
3939
|
];
|
|
3770
|
-
const indexDir =
|
|
3771
|
-
const indices =
|
|
3940
|
+
const indexDir = join24(this.ctxoRoot, "index");
|
|
3941
|
+
const indices = existsSync22(indexDir) ? new JsonIndexReader(this.ctxoRoot).readAll() : [];
|
|
3772
3942
|
const checker = new HealthChecker(checks, { quiet: Boolean(options.quiet) });
|
|
3773
3943
|
const report = await checker.runAll({
|
|
3774
3944
|
projectRoot: this.projectRoot,
|
|
@@ -3809,8 +3979,8 @@ var DoctorCommand = class {
|
|
|
3809
3979
|
};
|
|
3810
3980
|
|
|
3811
3981
|
// src/cli/visualize-command.ts
|
|
3812
|
-
import { readFileSync as readFileSync13, writeFileSync as writeFileSync5, existsSync as
|
|
3813
|
-
import { join as
|
|
3982
|
+
import { readFileSync as readFileSync13, writeFileSync as writeFileSync5, existsSync as existsSync23 } from "fs";
|
|
3983
|
+
import { join as join25, basename } from "path";
|
|
3814
3984
|
import { execFile } from "child_process";
|
|
3815
3985
|
|
|
3816
3986
|
// src/cli/graph-loader.ts
|
|
@@ -3826,7 +3996,7 @@ function loadGraph(ctxoRoot) {
|
|
|
3826
3996
|
var VisualizeCommand = class {
|
|
3827
3997
|
constructor(projectRoot) {
|
|
3828
3998
|
this.projectRoot = projectRoot;
|
|
3829
|
-
this.ctxoRoot =
|
|
3999
|
+
this.ctxoRoot = join25(projectRoot, ".ctxo");
|
|
3830
4000
|
}
|
|
3831
4001
|
projectRoot;
|
|
3832
4002
|
ctxoRoot;
|
|
@@ -3969,7 +4139,7 @@ var VisualizeCommand = class {
|
|
|
3969
4139
|
const templatePath = this.getTemplatePath();
|
|
3970
4140
|
const template = readFileSync13(templatePath, "utf-8");
|
|
3971
4141
|
const html = template.replace("/*__CTXO_DATA__*/null", JSON.stringify(payload));
|
|
3972
|
-
const outputPath = options.output ??
|
|
4142
|
+
const outputPath = options.output ?? join25(this.ctxoRoot, "visualize.html");
|
|
3973
4143
|
writeFileSync5(outputPath, html, "utf-8");
|
|
3974
4144
|
console.error(`[ctxo] Wrote visualization to ${outputPath}`);
|
|
3975
4145
|
console.error(`[ctxo] ${nodes.length} symbols, ${edges.length} edges, ${deadResult.deadSymbols.length} dead`);
|
|
@@ -3980,9 +4150,9 @@ var VisualizeCommand = class {
|
|
|
3980
4150
|
getTemplatePath() {
|
|
3981
4151
|
let dir = import.meta.dirname;
|
|
3982
4152
|
for (let i = 0; i < 10; i++) {
|
|
3983
|
-
const candidate =
|
|
3984
|
-
if (
|
|
3985
|
-
const parent =
|
|
4153
|
+
const candidate = join25(dir, "pages", "visualize-template.html");
|
|
4154
|
+
if (existsSync23(candidate)) return candidate;
|
|
4155
|
+
const parent = join25(dir, "..");
|
|
3986
4156
|
if (parent === dir) break;
|
|
3987
4157
|
dir = parent;
|
|
3988
4158
|
}
|
|
@@ -4006,8 +4176,8 @@ var VisualizeCommand = class {
|
|
|
4006
4176
|
};
|
|
4007
4177
|
|
|
4008
4178
|
// src/cli/update-command.ts
|
|
4009
|
-
import { existsSync as
|
|
4010
|
-
import { join as
|
|
4179
|
+
import { existsSync as existsSync24, readFileSync as readFileSync14 } from "fs";
|
|
4180
|
+
import { join as join26 } from "path";
|
|
4011
4181
|
|
|
4012
4182
|
// src/core/update/registry-client.ts
|
|
4013
4183
|
import { request } from "https";
|
|
@@ -4325,8 +4495,8 @@ var UpdateCommand = class {
|
|
|
4325
4495
|
return this.projectHasCtxoDep() ? "execute" : "print";
|
|
4326
4496
|
}
|
|
4327
4497
|
projectHasCtxoDep() {
|
|
4328
|
-
const pkg =
|
|
4329
|
-
if (!
|
|
4498
|
+
const pkg = join26(this.projectRoot, "package.json");
|
|
4499
|
+
if (!existsSync24(pkg)) return false;
|
|
4330
4500
|
try {
|
|
4331
4501
|
const json = JSON.parse(readFileSync14(pkg, "utf-8"));
|
|
4332
4502
|
const merged = { ...json.dependencies ?? {}, ...json.devDependencies ?? {} };
|
|
@@ -4363,11 +4533,11 @@ function isLockedCI2(env) {
|
|
|
4363
4533
|
}
|
|
4364
4534
|
|
|
4365
4535
|
// src/cli/blast-radius-command.ts
|
|
4366
|
-
import { join as
|
|
4536
|
+
import { join as join27 } from "path";
|
|
4367
4537
|
var BlastRadiusCommand = class {
|
|
4368
4538
|
ctxoRoot;
|
|
4369
4539
|
constructor(projectRoot) {
|
|
4370
|
-
this.ctxoRoot =
|
|
4540
|
+
this.ctxoRoot = join27(projectRoot, ".ctxo");
|
|
4371
4541
|
}
|
|
4372
4542
|
run(options) {
|
|
4373
4543
|
const { graph } = loadGraph(this.ctxoRoot);
|
|
@@ -4395,8 +4565,8 @@ var BlastRadiusCommand = class {
|
|
|
4395
4565
|
};
|
|
4396
4566
|
|
|
4397
4567
|
// src/cli/gate-hook-command.ts
|
|
4398
|
-
import { join as
|
|
4399
|
-
import { existsSync as
|
|
4568
|
+
import { join as join29, relative as relative3, sep as sep2 } from "path";
|
|
4569
|
+
import { existsSync as existsSync26, readFileSync as readFileSync16 } from "fs";
|
|
4400
4570
|
|
|
4401
4571
|
// src/core/gate/gate-config.ts
|
|
4402
4572
|
var DIAL = {
|
|
@@ -4463,8 +4633,8 @@ function transcriptHasBlastCheck(lines, symbolId) {
|
|
|
4463
4633
|
}
|
|
4464
4634
|
|
|
4465
4635
|
// src/adapters/gate/gate-session-store.ts
|
|
4466
|
-
import { existsSync as
|
|
4467
|
-
import { join as
|
|
4636
|
+
import { existsSync as existsSync25, mkdirSync as mkdirSync7, readFileSync as readFileSync15, writeFileSync as writeFileSync6 } from "fs";
|
|
4637
|
+
import { join as join28 } from "path";
|
|
4468
4638
|
var log7 = createLogger("ctxo:gate-store");
|
|
4469
4639
|
var GateSessionStore = class {
|
|
4470
4640
|
constructor(cacheDir) {
|
|
@@ -4473,11 +4643,11 @@ var GateSessionStore = class {
|
|
|
4473
4643
|
cacheDir;
|
|
4474
4644
|
file(sessionId) {
|
|
4475
4645
|
const safe = sessionId.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
4476
|
-
return
|
|
4646
|
+
return join28(this.cacheDir, "gate", `${safe}.json`);
|
|
4477
4647
|
}
|
|
4478
4648
|
read(sessionId) {
|
|
4479
4649
|
const f = this.file(sessionId);
|
|
4480
|
-
if (!
|
|
4650
|
+
if (!existsSync25(f)) return /* @__PURE__ */ new Set();
|
|
4481
4651
|
try {
|
|
4482
4652
|
return new Set(JSON.parse(readFileSync15(f, "utf-8")));
|
|
4483
4653
|
} catch (err) {
|
|
@@ -4493,7 +4663,7 @@ var GateSessionStore = class {
|
|
|
4493
4663
|
const set = this.read(sessionId);
|
|
4494
4664
|
set.add(symbolId);
|
|
4495
4665
|
const f = this.file(sessionId);
|
|
4496
|
-
mkdirSync7(
|
|
4666
|
+
mkdirSync7(join28(this.cacheDir, "gate"), { recursive: true });
|
|
4497
4667
|
writeFileSync6(f, JSON.stringify([...set]), "utf-8");
|
|
4498
4668
|
} catch (err) {
|
|
4499
4669
|
log7.error(`${err.message}`);
|
|
@@ -4532,8 +4702,8 @@ var RANK_ALL = 1e5;
|
|
|
4532
4702
|
var GateHookCommand = class {
|
|
4533
4703
|
constructor(projectRoot) {
|
|
4534
4704
|
this.projectRoot = projectRoot;
|
|
4535
|
-
this.ctxoRoot =
|
|
4536
|
-
this.cacheDir =
|
|
4705
|
+
this.ctxoRoot = join29(projectRoot, ".ctxo");
|
|
4706
|
+
this.cacheDir = join29(this.ctxoRoot, ".cache");
|
|
4537
4707
|
}
|
|
4538
4708
|
projectRoot;
|
|
4539
4709
|
ctxoRoot;
|
|
@@ -4549,11 +4719,11 @@ var GateHookCommand = class {
|
|
|
4549
4719
|
const thresholds = resolveGateThresholds(loaded.config.gate);
|
|
4550
4720
|
if (!thresholds.enabled) return { block: false };
|
|
4551
4721
|
const relPath = relative3(this.projectRoot, filePath).split(sep2).join("/");
|
|
4552
|
-
const indexFile =
|
|
4553
|
-
if (!
|
|
4722
|
+
const indexFile = join29(this.ctxoRoot, "index", `${relPath}.json`);
|
|
4723
|
+
if (!existsSync26(indexFile)) return { block: false };
|
|
4554
4724
|
const fileIndex = new JsonIndexReader(this.ctxoRoot).readSingle(indexFile);
|
|
4555
4725
|
if (!fileIndex) return { block: false };
|
|
4556
|
-
if (!
|
|
4726
|
+
if (!existsSync26(filePath)) return { block: false };
|
|
4557
4727
|
const source = readFileSync16(filePath, "utf-8");
|
|
4558
4728
|
const line = lineOfSnippet(source, oldString);
|
|
4559
4729
|
if (line === null) return { block: false };
|
|
@@ -4561,7 +4731,7 @@ var GateHookCommand = class {
|
|
|
4561
4731
|
if (!symbolId) return { block: false };
|
|
4562
4732
|
const store = new GateSessionStore(this.cacheDir);
|
|
4563
4733
|
if (store.hasWarned(payload.session_id, symbolId)) return { block: false };
|
|
4564
|
-
if (payload.transcript_path &&
|
|
4734
|
+
if (payload.transcript_path && existsSync26(payload.transcript_path)) {
|
|
4565
4735
|
const lines = readFileSync16(payload.transcript_path, "utf-8").split("\n").filter(Boolean);
|
|
4566
4736
|
if (transcriptHasBlastCheck(lines, symbolId)) return { block: false };
|
|
4567
4737
|
}
|
|
@@ -4627,12 +4797,12 @@ var GateHookCommand = class {
|
|
|
4627
4797
|
};
|
|
4628
4798
|
|
|
4629
4799
|
// src/cli/gate-command.ts
|
|
4630
|
-
import { join as
|
|
4800
|
+
import { join as join30 } from "path";
|
|
4631
4801
|
var RANK_ALL2 = 1e5;
|
|
4632
4802
|
var GateCommand = class {
|
|
4633
4803
|
ctxoRoot;
|
|
4634
4804
|
constructor(projectRoot) {
|
|
4635
|
-
this.ctxoRoot =
|
|
4805
|
+
this.ctxoRoot = join30(projectRoot, ".ctxo");
|
|
4636
4806
|
}
|
|
4637
4807
|
preview(options = {}) {
|
|
4638
4808
|
const gate = loadConfig(this.ctxoRoot).config.gate;
|
|
@@ -4672,8 +4842,8 @@ var GateCommand = class {
|
|
|
4672
4842
|
function getVersion() {
|
|
4673
4843
|
let dir = import.meta.dirname;
|
|
4674
4844
|
for (let i = 0; i < 10; i++) {
|
|
4675
|
-
const pkg =
|
|
4676
|
-
if (
|
|
4845
|
+
const pkg = join31(dir, "package.json");
|
|
4846
|
+
if (existsSync27(pkg)) {
|
|
4677
4847
|
try {
|
|
4678
4848
|
const json = JSON.parse(readFileSync17(pkg, "utf-8"));
|
|
4679
4849
|
return json.version ?? "unknown";
|
|
@@ -4681,7 +4851,7 @@ function getVersion() {
|
|
|
4681
4851
|
break;
|
|
4682
4852
|
}
|
|
4683
4853
|
}
|
|
4684
|
-
const parent =
|
|
4854
|
+
const parent = join31(dir, "..");
|
|
4685
4855
|
if (parent === dir) break;
|
|
4686
4856
|
dir = parent;
|
|
4687
4857
|
}
|
|
@@ -4690,8 +4860,8 @@ function getVersion() {
|
|
|
4690
4860
|
function locateGitRoot(startDir) {
|
|
4691
4861
|
let dir = startDir;
|
|
4692
4862
|
for (let i = 0; i < 12; i++) {
|
|
4693
|
-
if (
|
|
4694
|
-
const parent =
|
|
4863
|
+
if (existsSync27(join31(dir, ".git"))) return dir;
|
|
4864
|
+
const parent = dirname9(dir);
|
|
4695
4865
|
if (parent === dir) break;
|
|
4696
4866
|
dir = parent;
|
|
4697
4867
|
}
|
|
@@ -4808,6 +4978,8 @@ var CliRouter = class {
|
|
|
4808
4978
|
else if (a === "--global" || a === "-g") flagValues["global"] = true;
|
|
4809
4979
|
else if (a === "--dry-run") flagValues["dryRun"] = true;
|
|
4810
4980
|
else if (a === "--force") flagValues["force"] = true;
|
|
4981
|
+
else if (a === "--full-tier") flagValues["fullTier"] = true;
|
|
4982
|
+
else if (a === "--syntax-only") flagValues["syntaxOnly"] = true;
|
|
4811
4983
|
else if (a === "--pm") {
|
|
4812
4984
|
const next = args[++i];
|
|
4813
4985
|
if (!next) {
|
|
@@ -4838,6 +5010,8 @@ var CliRouter = class {
|
|
|
4838
5010
|
global: flagValues["global"] === true,
|
|
4839
5011
|
dryRun: flagValues["dryRun"] === true,
|
|
4840
5012
|
force: flagValues["force"] === true,
|
|
5013
|
+
fullTier: flagValues["fullTier"] === true,
|
|
5014
|
+
syntaxOnly: flagValues["syntaxOnly"] === true,
|
|
4841
5015
|
pm: typeof flagValues["pm"] === "string" ? flagValues["pm"] : void 0,
|
|
4842
5016
|
version: typeof flagValues["version"] === "string" ? flagValues["version"] : void 0
|
|
4843
5017
|
});
|
|
@@ -4972,4 +5146,4 @@ export {
|
|
|
4972
5146
|
getVersion,
|
|
4973
5147
|
locateGitRoot
|
|
4974
5148
|
};
|
|
4975
|
-
//# sourceMappingURL=cli-router-
|
|
5149
|
+
//# sourceMappingURL=cli-router-2JVPBW4S.js.map
|