@ctxo/cli 0.10.0 → 0.11.0
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-NFAGLJ46.js} +389 -209
- package/dist/cli-router-NFAGLJ46.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";
|
|
@@ -575,7 +577,53 @@ function resolveBinary(command) {
|
|
|
575
577
|
return `${command}.cmd`;
|
|
576
578
|
}
|
|
577
579
|
|
|
580
|
+
// src/core/detection/detect-java-runtime.ts
|
|
581
|
+
import { spawnSync } from "child_process";
|
|
582
|
+
import { existsSync as existsSync5 } from "fs";
|
|
583
|
+
import { join as join4 } from "path";
|
|
584
|
+
function parseJavaMajor(output) {
|
|
585
|
+
const m = output.match(/version "([^"]+)"/);
|
|
586
|
+
if (!m) return void 0;
|
|
587
|
+
const parts = m[1].split(".");
|
|
588
|
+
const major = parts[0] === "1" && parts[1] ? parseInt(parts[1], 10) : parseInt(parts[0], 10);
|
|
589
|
+
return Number.isNaN(major) ? void 0 : major;
|
|
590
|
+
}
|
|
591
|
+
var _javaMajorCache = void 0;
|
|
592
|
+
function detectJavaMajor() {
|
|
593
|
+
if (_javaMajorCache !== void 0) {
|
|
594
|
+
return _javaMajorCache === null ? void 0 : _javaMajorCache;
|
|
595
|
+
}
|
|
596
|
+
const home = process.env["CTXO_JAVA_HOME"] || process.env["JAVA_HOME"];
|
|
597
|
+
const bin = home ? join4(home, "bin", process.platform === "win32" ? "java.exe" : "java") : "java";
|
|
598
|
+
if (home && !existsSync5(bin)) {
|
|
599
|
+
_javaMajorCache = null;
|
|
600
|
+
return void 0;
|
|
601
|
+
}
|
|
602
|
+
const r = spawnSync(bin, ["-version"], {
|
|
603
|
+
encoding: "utf-8",
|
|
604
|
+
timeout: 1e4,
|
|
605
|
+
windowsHide: true
|
|
606
|
+
});
|
|
607
|
+
if (r.error || r.status === null) {
|
|
608
|
+
_javaMajorCache = null;
|
|
609
|
+
return void 0;
|
|
610
|
+
}
|
|
611
|
+
const major = parseJavaMajor(`${r.stderr ?? ""}${r.stdout ?? ""}`);
|
|
612
|
+
_javaMajorCache = major ?? null;
|
|
613
|
+
return major;
|
|
614
|
+
}
|
|
615
|
+
function javaRuntimeAvailable() {
|
|
616
|
+
return (detectJavaMajor() ?? 0) >= 17;
|
|
617
|
+
}
|
|
618
|
+
|
|
578
619
|
// src/cli/install-command.ts
|
|
620
|
+
function resolveJavaPackages(opts) {
|
|
621
|
+
if (opts.fullTier && opts.syntaxOnly) throw new Error("Cannot combine --full-tier with --syntax-only.");
|
|
622
|
+
const base = ["@ctxo/lang-java"];
|
|
623
|
+
if (opts.syntaxOnly) return base;
|
|
624
|
+
if (opts.fullTier || opts.jreAvailable) return [...base, "@ctxo/lang-java-analyzer"];
|
|
625
|
+
return base;
|
|
626
|
+
}
|
|
579
627
|
var InstallCommand = class {
|
|
580
628
|
constructor(projectRoot) {
|
|
581
629
|
this.projectRoot = projectRoot;
|
|
@@ -599,7 +647,7 @@ var InstallCommand = class {
|
|
|
599
647
|
process.exitCode = 1;
|
|
600
648
|
return;
|
|
601
649
|
}
|
|
602
|
-
if (!options.global && !
|
|
650
|
+
if (!options.global && !existsSync6(join5(this.projectRoot, "package.json"))) {
|
|
603
651
|
console.error("[ctxo] No package.json in the current project. Re-run with --global or create a package.json first.");
|
|
604
652
|
process.exitCode = 1;
|
|
605
653
|
return;
|
|
@@ -609,9 +657,28 @@ var InstallCommand = class {
|
|
|
609
657
|
process.exitCode = 1;
|
|
610
658
|
return;
|
|
611
659
|
}
|
|
612
|
-
const specifiers =
|
|
613
|
-
|
|
614
|
-
|
|
660
|
+
const specifiers = [];
|
|
661
|
+
for (const lang of resolvedLangs) {
|
|
662
|
+
if (lang === "java") {
|
|
663
|
+
const jreAvailable = javaRuntimeAvailable();
|
|
664
|
+
if (!jreAvailable && !options.fullTier && !options.syntaxOnly) {
|
|
665
|
+
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.');
|
|
666
|
+
}
|
|
667
|
+
let javaPkgs;
|
|
668
|
+
try {
|
|
669
|
+
javaPkgs = resolveJavaPackages({ jreAvailable, fullTier: options.fullTier, syntaxOnly: options.syntaxOnly });
|
|
670
|
+
} catch (err) {
|
|
671
|
+
console.error(`[ctxo] ${err.message}`);
|
|
672
|
+
process.exitCode = 1;
|
|
673
|
+
return;
|
|
674
|
+
}
|
|
675
|
+
for (const pkg of javaPkgs) {
|
|
676
|
+
specifiers.push(options.version ? `${pkg}@${options.version}` : pkg);
|
|
677
|
+
}
|
|
678
|
+
} else {
|
|
679
|
+
specifiers.push(options.version ? `${officialPluginFor(lang)}@${options.version}` : officialPluginFor(lang));
|
|
680
|
+
}
|
|
681
|
+
}
|
|
615
682
|
const invocation = buildInstallCommand(resolution.manager, specifiers, {
|
|
616
683
|
global: options.global,
|
|
617
684
|
workspaceRoot: !options.global && isWorkspaceRoot(this.projectRoot)
|
|
@@ -703,12 +770,12 @@ function isLockedCI(env) {
|
|
|
703
770
|
}
|
|
704
771
|
|
|
705
772
|
// src/adapters/storage/json-index-writer.ts
|
|
706
|
-
import { mkdirSync, unlinkSync, existsSync as
|
|
707
|
-
import { dirname as dirname4, join as
|
|
773
|
+
import { mkdirSync, unlinkSync, existsSync as existsSync7 } from "fs";
|
|
774
|
+
import { dirname as dirname4, join as join6, resolve as resolve2, sep } from "path";
|
|
708
775
|
var JsonIndexWriter = class {
|
|
709
776
|
indexDir;
|
|
710
777
|
constructor(ctxoRoot) {
|
|
711
|
-
this.indexDir =
|
|
778
|
+
this.indexDir = join6(ctxoRoot, "index");
|
|
712
779
|
}
|
|
713
780
|
write(fileIndex) {
|
|
714
781
|
if (!fileIndex.file) {
|
|
@@ -722,7 +789,7 @@ var JsonIndexWriter = class {
|
|
|
722
789
|
}
|
|
723
790
|
writeCoChanges(matrix) {
|
|
724
791
|
mkdirSync(this.indexDir, { recursive: true });
|
|
725
|
-
const targetPath =
|
|
792
|
+
const targetPath = join6(this.indexDir, "co-changes.json");
|
|
726
793
|
this.atomicWrite(targetPath, JSON.stringify(matrix, null, 2));
|
|
727
794
|
}
|
|
728
795
|
atomicWrite(targetPath, content) {
|
|
@@ -730,7 +797,7 @@ var JsonIndexWriter = class {
|
|
|
730
797
|
}
|
|
731
798
|
delete(relativePath) {
|
|
732
799
|
const targetPath = this.resolveIndexPath(relativePath);
|
|
733
|
-
if (
|
|
800
|
+
if (existsSync7(targetPath)) {
|
|
734
801
|
unlinkSync(targetPath);
|
|
735
802
|
}
|
|
736
803
|
}
|
|
@@ -758,19 +825,19 @@ var JsonIndexWriter = class {
|
|
|
758
825
|
};
|
|
759
826
|
|
|
760
827
|
// src/adapters/storage/schema-manager.ts
|
|
761
|
-
import { readFileSync as readFileSync4, writeFileSync, existsSync as
|
|
762
|
-
import { join as
|
|
828
|
+
import { readFileSync as readFileSync4, writeFileSync, existsSync as existsSync8, mkdirSync as mkdirSync2 } from "fs";
|
|
829
|
+
import { join as join7, dirname as dirname5 } from "path";
|
|
763
830
|
var CURRENT_SCHEMA_VERSION = "1.0.0";
|
|
764
831
|
var SchemaManager = class {
|
|
765
832
|
versionFilePath;
|
|
766
833
|
constructor(ctxoRoot) {
|
|
767
|
-
this.versionFilePath =
|
|
834
|
+
this.versionFilePath = join7(ctxoRoot, "index", "schema-version");
|
|
768
835
|
}
|
|
769
836
|
currentVersion() {
|
|
770
837
|
return CURRENT_SCHEMA_VERSION;
|
|
771
838
|
}
|
|
772
839
|
readStoredVersion() {
|
|
773
|
-
if (!
|
|
840
|
+
if (!existsSync8(this.versionFilePath)) {
|
|
774
841
|
return void 0;
|
|
775
842
|
}
|
|
776
843
|
return readFileSync4(this.versionFilePath, "utf-8").trim();
|
|
@@ -787,8 +854,8 @@ var SchemaManager = class {
|
|
|
787
854
|
};
|
|
788
855
|
|
|
789
856
|
// src/adapters/storage/architecture-artifacts-writer.ts
|
|
790
|
-
import { existsSync as
|
|
791
|
-
import { join as
|
|
857
|
+
import { existsSync as existsSync9, mkdirSync as mkdirSync3, readFileSync as readFileSync5 } from "fs";
|
|
858
|
+
import { join as join8, resolve as resolve3 } from "path";
|
|
792
859
|
import { tmpdir } from "os";
|
|
793
860
|
var log3 = createLogger("ctxo:architecture-artifacts");
|
|
794
861
|
var DRIFT_FILE_NAME = "drift-events.json";
|
|
@@ -796,20 +863,20 @@ var VIOLATIONS_FILE_NAME = "boundary-violations.json";
|
|
|
796
863
|
var ArchitectureArtifactsWriter = class {
|
|
797
864
|
indexDir;
|
|
798
865
|
constructor(ctxoRoot, options = {}) {
|
|
799
|
-
this.indexDir =
|
|
866
|
+
this.indexDir = join8(ctxoRoot, "index");
|
|
800
867
|
assertSafeWritePath(ctxoRoot, options.allowProductionPath ?? false);
|
|
801
868
|
}
|
|
802
869
|
writeDriftEvents(artifact) {
|
|
803
870
|
mkdirSync3(this.indexDir, { recursive: true });
|
|
804
|
-
atomicWrite(
|
|
871
|
+
atomicWrite(join8(this.indexDir, DRIFT_FILE_NAME), JSON.stringify(artifact, null, 2));
|
|
805
872
|
}
|
|
806
873
|
writeBoundaryViolations(artifact) {
|
|
807
874
|
mkdirSync3(this.indexDir, { recursive: true });
|
|
808
|
-
atomicWrite(
|
|
875
|
+
atomicWrite(join8(this.indexDir, VIOLATIONS_FILE_NAME), JSON.stringify(artifact, null, 2));
|
|
809
876
|
}
|
|
810
877
|
readDriftEvents() {
|
|
811
|
-
const path =
|
|
812
|
-
if (!
|
|
878
|
+
const path = join8(this.indexDir, DRIFT_FILE_NAME);
|
|
879
|
+
if (!existsSync9(path)) return void 0;
|
|
813
880
|
try {
|
|
814
881
|
return JSON.parse(readFileSync5(path, "utf-8"));
|
|
815
882
|
} catch (err) {
|
|
@@ -818,8 +885,8 @@ var ArchitectureArtifactsWriter = class {
|
|
|
818
885
|
}
|
|
819
886
|
}
|
|
820
887
|
readBoundaryViolations() {
|
|
821
|
-
const path =
|
|
822
|
-
if (!
|
|
888
|
+
const path = join8(this.indexDir, VIOLATIONS_FILE_NAME);
|
|
889
|
+
if (!existsSync9(path)) return void 0;
|
|
823
890
|
try {
|
|
824
891
|
return JSON.parse(readFileSync5(path, "utf-8"));
|
|
825
892
|
} catch (err) {
|
|
@@ -1128,7 +1195,7 @@ var IndexCommand = class {
|
|
|
1128
1195
|
supportedExtensions;
|
|
1129
1196
|
constructor(projectRoot, ctxoRoot) {
|
|
1130
1197
|
this.projectRoot = projectRoot;
|
|
1131
|
-
this.ctxoRoot = ctxoRoot ??
|
|
1198
|
+
this.ctxoRoot = ctxoRoot ?? join9(projectRoot, ".ctxo");
|
|
1132
1199
|
this.supportedExtensions = /* @__PURE__ */ new Set([".ts", ".tsx", ".js", ".jsx", ".go", ".cs"]);
|
|
1133
1200
|
}
|
|
1134
1201
|
loadIgnoreConfig() {
|
|
@@ -1156,7 +1223,7 @@ var IndexCommand = class {
|
|
|
1156
1223
|
}
|
|
1157
1224
|
const ignore = this.loadIgnoreConfig();
|
|
1158
1225
|
const registry = new LanguageAdapterRegistry();
|
|
1159
|
-
const { tsMorphLike, roslynAdapter, csharpTier, goTier } = await this.registerDiscoveredPlugins(registry, ignore.ignoreProjectPatterns);
|
|
1226
|
+
const { tsMorphLike, roslynAdapter, csharpTier, goTier, javaTier } = await this.registerDiscoveredPlugins(registry, ignore.ignoreProjectPatterns);
|
|
1160
1227
|
this.supportedExtensions = registry.getSupportedExtensions();
|
|
1161
1228
|
this.printLanguagePreview();
|
|
1162
1229
|
const writer = new JsonIndexWriter(this.ctxoRoot);
|
|
@@ -1166,7 +1233,7 @@ var IndexCommand = class {
|
|
|
1166
1233
|
const revertDetector = new RevertDetector();
|
|
1167
1234
|
let files;
|
|
1168
1235
|
if (options.file) {
|
|
1169
|
-
const fullPath =
|
|
1236
|
+
const fullPath = join9(this.projectRoot, options.file);
|
|
1170
1237
|
files = [fullPath];
|
|
1171
1238
|
console.error(`[ctxo] Incremental re-index: ${options.file}`);
|
|
1172
1239
|
} else {
|
|
@@ -1179,8 +1246,8 @@ var IndexCommand = class {
|
|
|
1179
1246
|
console.error(`[ctxo] Building codebase index... Found ${files.length} source files`);
|
|
1180
1247
|
}
|
|
1181
1248
|
try {
|
|
1182
|
-
const { sweepStaleTmpFiles } = await import("./atomic-write-
|
|
1183
|
-
const swept = sweepStaleTmpFiles(
|
|
1249
|
+
const { sweepStaleTmpFiles } = await import("./atomic-write-BAFH464Q.js");
|
|
1250
|
+
const swept = sweepStaleTmpFiles(join9(this.ctxoRoot, "index"));
|
|
1184
1251
|
if (swept > 0) {
|
|
1185
1252
|
console.error(`[ctxo] Cleaned ${swept} orphan .tmp file(s) from previous interrupted run`);
|
|
1186
1253
|
}
|
|
@@ -1267,7 +1334,7 @@ var IndexCommand = class {
|
|
|
1267
1334
|
}
|
|
1268
1335
|
let communitySnapshot;
|
|
1269
1336
|
if (options.skipCommunity && pendingIndices.length > 0) {
|
|
1270
|
-
if (
|
|
1337
|
+
if (existsSync10(join9(this.ctxoRoot, "index", "communities.json"))) {
|
|
1271
1338
|
console.error(
|
|
1272
1339
|
`[ctxo] WARN stale communities.json present \u2014 --skip-community preserved existing snapshot. Run \`ctxo index\` (full) to refresh.`
|
|
1273
1340
|
);
|
|
@@ -1280,6 +1347,7 @@ var IndexCommand = class {
|
|
|
1280
1347
|
{
|
|
1281
1348
|
csharpTier,
|
|
1282
1349
|
goTier,
|
|
1350
|
+
javaTier,
|
|
1283
1351
|
tsCount: graphIndices.filter((idx) => /\.(ts|tsx|js|jsx)$/.test(idx.file)).length
|
|
1284
1352
|
},
|
|
1285
1353
|
gitAdapter
|
|
@@ -1317,10 +1385,12 @@ var IndexCommand = class {
|
|
|
1317
1385
|
const csCount = pendingIndices.filter((e) => e.relativePath.endsWith(".cs")).length;
|
|
1318
1386
|
const tsCount = pendingIndices.filter((e) => /\.(ts|tsx|js|jsx)$/.test(e.relativePath)).length;
|
|
1319
1387
|
const goCount = pendingIndices.filter((e) => e.relativePath.endsWith(".go")).length;
|
|
1388
|
+
const javaCount = pendingIndices.filter((e) => e.relativePath.endsWith(".java")).length;
|
|
1320
1389
|
console.error(`[ctxo] Index complete: ${processed} files indexed`);
|
|
1321
1390
|
if (tsCount > 0) console.error(`[ctxo] TypeScript/JS: ${tsCount} files (full tier)`);
|
|
1322
1391
|
if (csCount > 0) console.error(`[ctxo] C#: ${csCount} files (${csharpTier} tier${csharpTier === "syntax" ? " - .NET SDK 8+ for full analysis" : ""})`);
|
|
1323
1392
|
if (goCount > 0) console.error(`[ctxo] Go: ${goCount} files (${goTier} tier${goTier === "syntax" ? " - Go 1.22+ for full analysis" : ""})`);
|
|
1393
|
+
if (javaCount > 0) console.error(`[ctxo] Java: ${javaCount} files (${javaTier} tier${javaTier === "syntax" ? ' - JRE 17+ & "ctxo install java --full-tier" for full analysis' : ""})`);
|
|
1324
1394
|
if (roslynAdapter) await roslynAdapter.dispose();
|
|
1325
1395
|
}
|
|
1326
1396
|
async autoInstallMissingPlugins() {
|
|
@@ -1356,6 +1426,7 @@ var IndexCommand = class {
|
|
|
1356
1426
|
let roslynAdapter = null;
|
|
1357
1427
|
let csharpTier = "unavailable";
|
|
1358
1428
|
let goTier = "unavailable";
|
|
1429
|
+
let javaTier = "unavailable";
|
|
1359
1430
|
for (const { plugin, adapter } of loaded) {
|
|
1360
1431
|
if (typeof adapter.initialize === "function") {
|
|
1361
1432
|
try {
|
|
@@ -1379,11 +1450,15 @@ var IndexCommand = class {
|
|
|
1379
1450
|
if (plugin.id === "go" && typeof goCandidate.getTier === "function") {
|
|
1380
1451
|
goTier = goCandidate.getTier();
|
|
1381
1452
|
}
|
|
1382
|
-
const
|
|
1453
|
+
const javaCandidate = adapter;
|
|
1454
|
+
if (plugin.id === "java" && typeof javaCandidate.getTier === "function") {
|
|
1455
|
+
javaTier = javaCandidate.getTier();
|
|
1456
|
+
}
|
|
1457
|
+
const activeTier = plugin.id === "csharp" ? csharpTier : plugin.id === "go" ? goTier : plugin.id === "java" ? javaTier : plugin.tier;
|
|
1383
1458
|
const tierSuffix = activeTier !== plugin.tier ? ` (active: ${activeTier})` : "";
|
|
1384
1459
|
console.error(`[ctxo] Plugin ${plugin.id}@${plugin.version} (${plugin.tier} tier${tierSuffix}) \u2014 ${plugin.extensions.join(", ")}`);
|
|
1385
1460
|
}
|
|
1386
|
-
return { tsMorphLike, roslynAdapter, csharpTier, goTier };
|
|
1461
|
+
return { tsMorphLike, roslynAdapter, csharpTier, goTier, javaTier };
|
|
1387
1462
|
}
|
|
1388
1463
|
discoverFilesIn(root, ignoreFile = () => false) {
|
|
1389
1464
|
try {
|
|
@@ -1392,18 +1467,18 @@ var IndexCommand = class {
|
|
|
1392
1467
|
encoding: "utf-8",
|
|
1393
1468
|
maxBuffer: 10 * 1024 * 1024
|
|
1394
1469
|
});
|
|
1395
|
-
return output.split("\n").map((line) => line.trim()).filter((line) => line.length > 0).filter((line) => this.isSupportedExtension(line)).map((line) =>
|
|
1470
|
+
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
1471
|
const rel = relative(this.projectRoot, abs).replace(/\\/g, "/");
|
|
1397
1472
|
return !ignoreFile(rel);
|
|
1398
|
-
}).filter((abs) =>
|
|
1473
|
+
}).filter((abs) => existsSync10(abs));
|
|
1399
1474
|
} catch {
|
|
1400
1475
|
console.error(`[ctxo] git ls-files failed for ${root}`);
|
|
1401
1476
|
return [];
|
|
1402
1477
|
}
|
|
1403
1478
|
}
|
|
1404
1479
|
discoverWorkspaces(ignoreProject = () => false) {
|
|
1405
|
-
const pkgPath =
|
|
1406
|
-
if (!
|
|
1480
|
+
const pkgPath = join9(this.projectRoot, "package.json");
|
|
1481
|
+
if (!existsSync10(pkgPath)) return [this.projectRoot];
|
|
1407
1482
|
try {
|
|
1408
1483
|
const pkg = JSON.parse(readFileSync6(pkgPath, "utf-8"));
|
|
1409
1484
|
const workspaces = Array.isArray(pkg.workspaces) ? pkg.workspaces : pkg.workspaces?.packages;
|
|
@@ -1411,17 +1486,17 @@ var IndexCommand = class {
|
|
|
1411
1486
|
const resolved = [];
|
|
1412
1487
|
for (const ws of workspaces) {
|
|
1413
1488
|
if (ws.endsWith("/*") || ws.endsWith("\\*")) {
|
|
1414
|
-
const parentDir =
|
|
1415
|
-
if (
|
|
1489
|
+
const parentDir = join9(this.projectRoot, ws.slice(0, -2));
|
|
1490
|
+
if (existsSync10(parentDir)) {
|
|
1416
1491
|
for (const entry of readdirSync2(parentDir, { withFileTypes: true })) {
|
|
1417
1492
|
if (entry.isDirectory()) {
|
|
1418
|
-
resolved.push(
|
|
1493
|
+
resolved.push(join9(parentDir, entry.name));
|
|
1419
1494
|
}
|
|
1420
1495
|
}
|
|
1421
1496
|
}
|
|
1422
1497
|
} else {
|
|
1423
|
-
const wsPath =
|
|
1424
|
-
if (
|
|
1498
|
+
const wsPath = join9(this.projectRoot, ws);
|
|
1499
|
+
if (existsSync10(wsPath)) {
|
|
1425
1500
|
resolved.push(wsPath);
|
|
1426
1501
|
}
|
|
1427
1502
|
}
|
|
@@ -1454,7 +1529,7 @@ var IndexCommand = class {
|
|
|
1454
1529
|
if (_roslyn) await _roslyn.dispose();
|
|
1455
1530
|
const hasher = new ContentHasher();
|
|
1456
1531
|
const files = this.discoverFilesIn(this.projectRoot);
|
|
1457
|
-
const reader = new (await import("./json-index-reader-
|
|
1532
|
+
const reader = new (await import("./json-index-reader-UYS74USB.js")).JsonIndexReader(this.ctxoRoot);
|
|
1458
1533
|
const indices = reader.readAll();
|
|
1459
1534
|
const indexedMap = new Map(indices.map((i) => [i.file, i]));
|
|
1460
1535
|
let staleCount = 0;
|
|
@@ -1467,7 +1542,7 @@ var IndexCommand = class {
|
|
|
1467
1542
|
staleCount++;
|
|
1468
1543
|
continue;
|
|
1469
1544
|
}
|
|
1470
|
-
if (!
|
|
1545
|
+
if (!existsSync10(filePath)) {
|
|
1471
1546
|
console.error(`[ctxo] DELETED: ${relativePath}`);
|
|
1472
1547
|
staleCount++;
|
|
1473
1548
|
continue;
|
|
@@ -1493,13 +1568,13 @@ var IndexCommand = class {
|
|
|
1493
1568
|
console.error("[ctxo] Index is up to date");
|
|
1494
1569
|
}
|
|
1495
1570
|
ensureGitignore() {
|
|
1496
|
-
const gitignorePath =
|
|
1571
|
+
const gitignorePath = join9(this.projectRoot, ".gitignore");
|
|
1497
1572
|
const cachePattern = ".ctxo/.cache/";
|
|
1498
1573
|
const suffix = `
|
|
1499
1574
|
# Ctxo local cache (never committed)
|
|
1500
1575
|
${cachePattern}
|
|
1501
1576
|
`;
|
|
1502
|
-
const existing =
|
|
1577
|
+
const existing = existsSync10(gitignorePath) ? readFileSync6(gitignorePath, "utf-8") : "";
|
|
1503
1578
|
if (existing.includes(cachePattern)) return;
|
|
1504
1579
|
writeFileSync2(gitignorePath, existing + suffix, "utf-8");
|
|
1505
1580
|
console.error("[ctxo] Added .ctxo/.cache/ to .gitignore");
|
|
@@ -1618,8 +1693,8 @@ ${cachePattern}
|
|
|
1618
1693
|
}
|
|
1619
1694
|
};
|
|
1620
1695
|
function pickEdgeQuality(tiers) {
|
|
1621
|
-
const hasSyntaxOnly = tiers.csharpTier === "syntax" || tiers.goTier === "syntax";
|
|
1622
|
-
const hasFull = tiers.tsCount > 0 || tiers.csharpTier === "full" || tiers.goTier === "full";
|
|
1696
|
+
const hasSyntaxOnly = tiers.csharpTier === "syntax" || tiers.goTier === "syntax" || tiers.javaTier === "syntax";
|
|
1697
|
+
const hasFull = tiers.tsCount > 0 || tiers.csharpTier === "full" || tiers.goTier === "full" || tiers.javaTier === "full";
|
|
1623
1698
|
if (hasSyntaxOnly && hasFull) return "mixed";
|
|
1624
1699
|
if (hasSyntaxOnly) return "syntax-only";
|
|
1625
1700
|
return "full";
|
|
@@ -1644,11 +1719,11 @@ function countCommunities(snapshot) {
|
|
|
1644
1719
|
}
|
|
1645
1720
|
|
|
1646
1721
|
// src/cli/sync-command.ts
|
|
1647
|
-
import { join as
|
|
1722
|
+
import { join as join10 } from "path";
|
|
1648
1723
|
var SyncCommand = class {
|
|
1649
1724
|
ctxoRoot;
|
|
1650
1725
|
constructor(projectRoot) {
|
|
1651
|
-
this.ctxoRoot =
|
|
1726
|
+
this.ctxoRoot = join10(projectRoot, ".ctxo");
|
|
1652
1727
|
}
|
|
1653
1728
|
async run() {
|
|
1654
1729
|
console.error("[ctxo] Rebuilding SQLite cache from committed JSON index...");
|
|
@@ -1663,19 +1738,19 @@ var SyncCommand = class {
|
|
|
1663
1738
|
};
|
|
1664
1739
|
|
|
1665
1740
|
// src/cli/status-command.ts
|
|
1666
|
-
import { join as
|
|
1667
|
-
import { existsSync as
|
|
1741
|
+
import { join as join11 } from "path";
|
|
1742
|
+
import { existsSync as existsSync11 } from "fs";
|
|
1668
1743
|
import { execFileSync as execFileSync3 } from "child_process";
|
|
1669
1744
|
var StatusCommand = class {
|
|
1670
1745
|
projectRoot;
|
|
1671
1746
|
ctxoRoot;
|
|
1672
1747
|
constructor(projectRoot) {
|
|
1673
1748
|
this.projectRoot = projectRoot;
|
|
1674
|
-
this.ctxoRoot =
|
|
1749
|
+
this.ctxoRoot = join11(projectRoot, ".ctxo");
|
|
1675
1750
|
}
|
|
1676
1751
|
run() {
|
|
1677
|
-
const indexDir =
|
|
1678
|
-
if (!
|
|
1752
|
+
const indexDir = join11(this.ctxoRoot, "index");
|
|
1753
|
+
if (!existsSync11(indexDir)) {
|
|
1679
1754
|
console.error('[ctxo] No index found. Run "ctxo index" first.');
|
|
1680
1755
|
return;
|
|
1681
1756
|
}
|
|
@@ -1690,7 +1765,7 @@ var StatusCommand = class {
|
|
|
1690
1765
|
console.error(` Indexed files: ${indices.length}`);
|
|
1691
1766
|
console.error(` Total symbols: ${totalSymbols}`);
|
|
1692
1767
|
console.error(` Total edges: ${totalEdges}`);
|
|
1693
|
-
const cacheExists =
|
|
1768
|
+
const cacheExists = existsSync11(join11(this.ctxoRoot, ".cache", "symbols.db"));
|
|
1694
1769
|
console.error(` SQLite cache: ${cacheExists ? "present" : "missing (run ctxo sync)"}`);
|
|
1695
1770
|
if (indices.length > 0) {
|
|
1696
1771
|
console.error("");
|
|
@@ -1719,7 +1794,7 @@ var StatusCommand = class {
|
|
|
1719
1794
|
|
|
1720
1795
|
// src/cli/verify-command.ts
|
|
1721
1796
|
import { mkdtempSync, rmSync } from "fs";
|
|
1722
|
-
import { join as
|
|
1797
|
+
import { join as join12 } from "path";
|
|
1723
1798
|
import { tmpdir as tmpdir2 } from "os";
|
|
1724
1799
|
var VerifyCommand = class {
|
|
1725
1800
|
projectRoot;
|
|
@@ -1728,12 +1803,12 @@ var VerifyCommand = class {
|
|
|
1728
1803
|
}
|
|
1729
1804
|
async run() {
|
|
1730
1805
|
console.error("[ctxo] Verifying index freshness...");
|
|
1731
|
-
const tempDir = mkdtempSync(
|
|
1806
|
+
const tempDir = mkdtempSync(join12(tmpdir2(), "ctxo-verify-"));
|
|
1732
1807
|
try {
|
|
1733
|
-
const tempCtxo =
|
|
1808
|
+
const tempCtxo = join12(tempDir, ".ctxo");
|
|
1734
1809
|
const indexCmd = new IndexCommand(this.projectRoot, tempCtxo);
|
|
1735
1810
|
await indexCmd.run({ skipSideEffects: true });
|
|
1736
|
-
const committedReader = new JsonIndexReader(
|
|
1811
|
+
const committedReader = new JsonIndexReader(join12(this.projectRoot, ".ctxo"));
|
|
1737
1812
|
const freshReader = new JsonIndexReader(tempCtxo);
|
|
1738
1813
|
const committedIndices = committedReader.readAll();
|
|
1739
1814
|
const freshIndices = freshReader.readAll();
|
|
@@ -1766,20 +1841,20 @@ var VerifyCommand = class {
|
|
|
1766
1841
|
};
|
|
1767
1842
|
|
|
1768
1843
|
// src/cli/init-command.ts
|
|
1769
|
-
import { join as
|
|
1770
|
-
import { existsSync as
|
|
1844
|
+
import { join as join14 } from "path";
|
|
1845
|
+
import { existsSync as existsSync13, readFileSync as readFileSync8, writeFileSync as writeFileSync4, mkdirSync as mkdirSync5, chmodSync } from "fs";
|
|
1771
1846
|
import { createRequire as createRequire4 } from "module";
|
|
1772
1847
|
|
|
1773
1848
|
// src/cli/ai-rules.ts
|
|
1774
|
-
import { existsSync as
|
|
1775
|
-
import { join as
|
|
1849
|
+
import { existsSync as existsSync12, readFileSync as readFileSync7, writeFileSync as writeFileSync3, mkdirSync as mkdirSync4 } from "fs";
|
|
1850
|
+
import { join as join13, dirname as dirname6 } from "path";
|
|
1776
1851
|
var CTXO_HOOK_COMMAND = "ctxo gate-hook";
|
|
1777
1852
|
function ensureClaudeHook(projectRoot) {
|
|
1778
|
-
const filePath =
|
|
1853
|
+
const filePath = join13(projectRoot, ".claude", "settings.json");
|
|
1779
1854
|
const dir = dirname6(filePath);
|
|
1780
|
-
if (!
|
|
1855
|
+
if (!existsSync12(dir)) mkdirSync4(dir, { recursive: true });
|
|
1781
1856
|
const ctxoEntry = { matcher: "Edit", hooks: [{ type: "command", command: CTXO_HOOK_COMMAND }] };
|
|
1782
|
-
if (!
|
|
1857
|
+
if (!existsSync12(filePath)) {
|
|
1783
1858
|
writeFileSync3(filePath, JSON.stringify({ hooks: { PreToolUse: [ctxoEntry] } }, null, 2) + "\n", "utf-8");
|
|
1784
1859
|
return { file: ".claude/settings.json", action: "created" };
|
|
1785
1860
|
}
|
|
@@ -1815,7 +1890,7 @@ var PLATFORMS = [
|
|
|
1815
1890
|
function detectPlatforms(projectRoot) {
|
|
1816
1891
|
const detected = /* @__PURE__ */ new Set();
|
|
1817
1892
|
for (const p of PLATFORMS) {
|
|
1818
|
-
if (p.detectPaths.some((d) =>
|
|
1893
|
+
if (p.detectPaths.some((d) => existsSync12(join13(projectRoot, d)))) {
|
|
1819
1894
|
detected.add(p.id);
|
|
1820
1895
|
}
|
|
1821
1896
|
}
|
|
@@ -1877,15 +1952,15 @@ function generateRules(platformId) {
|
|
|
1877
1952
|
function installRules(projectRoot, platformId) {
|
|
1878
1953
|
const platform = PLATFORMS.find((p) => p.id === platformId);
|
|
1879
1954
|
if (!platform) throw new Error(`Unknown platform: ${platformId}`);
|
|
1880
|
-
const filePath =
|
|
1955
|
+
const filePath = join13(projectRoot, platform.file);
|
|
1881
1956
|
const content = generateRules(platformId);
|
|
1882
1957
|
const dir = dirname6(filePath);
|
|
1883
|
-
if (!
|
|
1958
|
+
if (!existsSync12(dir)) mkdirSync4(dir, { recursive: true });
|
|
1884
1959
|
if (platform.mode === "create") {
|
|
1885
1960
|
writeFileSync3(filePath, content, "utf-8");
|
|
1886
|
-
return { file: platform.file, action:
|
|
1961
|
+
return { file: platform.file, action: existsSync12(filePath) ? "updated" : "created" };
|
|
1887
1962
|
}
|
|
1888
|
-
if (!
|
|
1963
|
+
if (!existsSync12(filePath)) {
|
|
1889
1964
|
writeFileSync3(filePath, content, "utf-8");
|
|
1890
1965
|
return { file: platform.file, action: "created" };
|
|
1891
1966
|
}
|
|
@@ -1909,15 +1984,15 @@ function escapeRegExp(s) {
|
|
|
1909
1984
|
}
|
|
1910
1985
|
var SKILLS = ["ctxo-understand", "ctxo-safe-edit", "ctxo-review-pr"];
|
|
1911
1986
|
var SKILL_TARGET_DIR = {
|
|
1912
|
-
"claude-code": (root, skill) =>
|
|
1913
|
-
cursor: (root, skill) =>
|
|
1987
|
+
"claude-code": (root, skill) => join13(root, ".claude", "skills", skill, "SKILL.md"),
|
|
1988
|
+
cursor: (root, skill) => join13(root, ".cursor", "rules", `${skill}.mdc`)
|
|
1914
1989
|
};
|
|
1915
1990
|
function skillsSourceDir() {
|
|
1916
1991
|
let dir = import.meta.dirname;
|
|
1917
1992
|
for (let i = 0; i < 10; i++) {
|
|
1918
|
-
const candidate =
|
|
1919
|
-
if (
|
|
1920
|
-
const parent =
|
|
1993
|
+
const candidate = join13(dir, "skills");
|
|
1994
|
+
if (existsSync12(join13(candidate, "ctxo-safe-edit", "SKILL.md"))) return candidate;
|
|
1995
|
+
const parent = join13(dir, "..");
|
|
1921
1996
|
if (parent === dir) break;
|
|
1922
1997
|
dir = parent;
|
|
1923
1998
|
}
|
|
@@ -1929,10 +2004,10 @@ function installSkills(projectRoot, platformId) {
|
|
|
1929
2004
|
const src = skillsSourceDir();
|
|
1930
2005
|
const out = [];
|
|
1931
2006
|
for (const skill of SKILLS) {
|
|
1932
|
-
const from =
|
|
2007
|
+
const from = join13(src, skill, "SKILL.md");
|
|
1933
2008
|
const to = target(projectRoot, skill);
|
|
1934
2009
|
mkdirSync4(dirname6(to), { recursive: true });
|
|
1935
|
-
const existed =
|
|
2010
|
+
const existed = existsSync12(to);
|
|
1936
2011
|
writeFileSync3(to, readFileSync7(from, "utf-8"), "utf-8");
|
|
1937
2012
|
out.push({ file: to, action: existed ? "updated" : "created" });
|
|
1938
2013
|
}
|
|
@@ -1940,8 +2015,8 @@ function installSkills(projectRoot, platformId) {
|
|
|
1940
2015
|
}
|
|
1941
2016
|
var GITIGNORE_ENTRY = ".ctxo/.cache/";
|
|
1942
2017
|
function ensureGitignore(projectRoot) {
|
|
1943
|
-
const filePath =
|
|
1944
|
-
if (!
|
|
2018
|
+
const filePath = join13(projectRoot, ".gitignore");
|
|
2019
|
+
if (!existsSync12(filePath)) {
|
|
1945
2020
|
writeFileSync3(filePath, `# Ctxo local cache (auto-rebuilt, never committed)
|
|
1946
2021
|
${GITIGNORE_ENTRY}
|
|
1947
2022
|
`, "utf-8");
|
|
@@ -1982,10 +2057,10 @@ gate:
|
|
|
1982
2057
|
sensitivity: balanced # strict | balanced | lenient
|
|
1983
2058
|
`;
|
|
1984
2059
|
function ensureConfig(projectRoot) {
|
|
1985
|
-
const filePath =
|
|
2060
|
+
const filePath = join13(projectRoot, ".ctxo", "config.yaml");
|
|
1986
2061
|
const dir = dirname6(filePath);
|
|
1987
|
-
if (!
|
|
1988
|
-
if (
|
|
2062
|
+
if (!existsSync12(dir)) mkdirSync4(dir, { recursive: true });
|
|
2063
|
+
if (existsSync12(filePath)) {
|
|
1989
2064
|
return { file: ".ctxo/config.yaml", action: "skipped" };
|
|
1990
2065
|
}
|
|
1991
2066
|
writeFileSync3(filePath, DEFAULT_CONFIG, "utf-8");
|
|
@@ -2020,11 +2095,11 @@ function getMcpConfigTargets(selectedPlatformIds) {
|
|
|
2020
2095
|
return [...targets.values()];
|
|
2021
2096
|
}
|
|
2022
2097
|
function ensureMcpConfig(projectRoot, target) {
|
|
2023
|
-
const filePath =
|
|
2098
|
+
const filePath = join13(projectRoot, target.file);
|
|
2024
2099
|
const dir = dirname6(filePath);
|
|
2025
|
-
if (!
|
|
2100
|
+
if (!existsSync12(dir)) mkdirSync4(dir, { recursive: true });
|
|
2026
2101
|
const entry = target.extraFields ? { ...target.extraFields, ...CTXO_MCP_ENTRY } : { ...CTXO_MCP_ENTRY };
|
|
2027
|
-
if (!
|
|
2102
|
+
if (!existsSync12(filePath)) {
|
|
2028
2103
|
const config = { [target.serverKey]: { ctxo: entry } };
|
|
2029
2104
|
writeFileSync3(filePath, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
2030
2105
|
return { file: target.file, action: "created" };
|
|
@@ -2140,13 +2215,13 @@ var InitCommand = class {
|
|
|
2140
2215
|
this.projectRoot = projectRoot;
|
|
2141
2216
|
}
|
|
2142
2217
|
installHooks() {
|
|
2143
|
-
const hooksDir =
|
|
2218
|
+
const hooksDir = join14(this.projectRoot, ".git", "hooks");
|
|
2144
2219
|
mkdirSync5(hooksDir, { recursive: true });
|
|
2145
2220
|
this.installHook(hooksDir, "post-commit", POST_COMMIT_CONTENT);
|
|
2146
2221
|
this.installHook(hooksDir, "post-merge", POST_MERGE_CONTENT);
|
|
2147
2222
|
}
|
|
2148
2223
|
async run(options = {}) {
|
|
2149
|
-
if (!
|
|
2224
|
+
if (!existsSync13(join14(this.projectRoot, ".git"))) {
|
|
2150
2225
|
console.error('[ctxo] Not a git repository. Run "git init" first.');
|
|
2151
2226
|
process.exit(1);
|
|
2152
2227
|
}
|
|
@@ -2183,8 +2258,8 @@ var InitCommand = class {
|
|
|
2183
2258
|
clack.cancel("Setup cancelled.");
|
|
2184
2259
|
process.exit(0);
|
|
2185
2260
|
}
|
|
2186
|
-
const fullPath =
|
|
2187
|
-
if (!
|
|
2261
|
+
const fullPath = join14(this.projectRoot, indexDir);
|
|
2262
|
+
if (!existsSync13(fullPath)) mkdirSync5(fullPath, { recursive: true });
|
|
2188
2263
|
console.error("");
|
|
2189
2264
|
}
|
|
2190
2265
|
stepNum++;
|
|
@@ -2360,8 +2435,8 @@ var InitCommand = class {
|
|
|
2360
2435
|
const gitignoreResult = ensureGitignore(this.projectRoot);
|
|
2361
2436
|
const configResult = ensureConfig(this.projectRoot);
|
|
2362
2437
|
if (!options.rulesOnly) {
|
|
2363
|
-
const indexDir =
|
|
2364
|
-
if (!
|
|
2438
|
+
const indexDir = join14(this.projectRoot, ".ctxo", "index");
|
|
2439
|
+
if (!existsSync13(indexDir)) mkdirSync5(indexDir, { recursive: true });
|
|
2365
2440
|
console.error("[ctxo] \u2713 .ctxo/index/ ready");
|
|
2366
2441
|
}
|
|
2367
2442
|
if (configResult.action !== "skipped") {
|
|
@@ -2420,7 +2495,7 @@ var InitCommand = class {
|
|
|
2420
2495
|
for (const id of toolIds) {
|
|
2421
2496
|
const platform = PLATFORMS.find((p) => p.id === id);
|
|
2422
2497
|
if (platform) {
|
|
2423
|
-
const exists =
|
|
2498
|
+
const exists = existsSync13(join14(this.projectRoot, platform.file));
|
|
2424
2499
|
const action = platform.mode === "append" && exists ? "append section" : "create";
|
|
2425
2500
|
console.error(` ${platform.file.padEnd(42)} (${action})`);
|
|
2426
2501
|
}
|
|
@@ -2439,21 +2514,21 @@ var InitCommand = class {
|
|
|
2439
2514
|
/* ---------------------------------------------------------------- */
|
|
2440
2515
|
async getVersion() {
|
|
2441
2516
|
try {
|
|
2442
|
-
const pkgPath =
|
|
2443
|
-
if (
|
|
2517
|
+
const pkgPath = join14(this.projectRoot, "node_modules", "@ctxo", "cli", "package.json");
|
|
2518
|
+
if (existsSync13(pkgPath)) {
|
|
2444
2519
|
return JSON.parse(readFileSync8(pkgPath, "utf-8")).version;
|
|
2445
2520
|
}
|
|
2446
|
-
const { createRequire:
|
|
2447
|
-
const
|
|
2448
|
-
return
|
|
2521
|
+
const { createRequire: createRequire9 } = await import("module");
|
|
2522
|
+
const require5 = createRequire9(import.meta.url);
|
|
2523
|
+
return require5("../../package.json").version;
|
|
2449
2524
|
} catch {
|
|
2450
2525
|
return void 0;
|
|
2451
2526
|
}
|
|
2452
2527
|
}
|
|
2453
2528
|
installHook(hooksDir, hookName, hookContent) {
|
|
2454
|
-
const hookPath =
|
|
2529
|
+
const hookPath = join14(hooksDir, hookName);
|
|
2455
2530
|
let existing = "";
|
|
2456
|
-
if (
|
|
2531
|
+
if (existsSync13(hookPath)) {
|
|
2457
2532
|
existing = readFileSync8(hookPath, "utf-8");
|
|
2458
2533
|
if (existing.includes(CTXO_START)) return;
|
|
2459
2534
|
} else {
|
|
@@ -2466,7 +2541,7 @@ var InitCommand = class {
|
|
|
2466
2541
|
};
|
|
2467
2542
|
|
|
2468
2543
|
// src/cli/watch-command.ts
|
|
2469
|
-
import { join as
|
|
2544
|
+
import { join as join15, extname as extname4, relative as relative2 } from "path";
|
|
2470
2545
|
import { readFileSync as readFileSync9 } from "fs";
|
|
2471
2546
|
|
|
2472
2547
|
// src/adapters/watcher/chokidar-watcher-adapter.ts
|
|
@@ -2516,12 +2591,12 @@ var WatchCommand = class {
|
|
|
2516
2591
|
ctxoRoot;
|
|
2517
2592
|
constructor(projectRoot) {
|
|
2518
2593
|
this.projectRoot = projectRoot;
|
|
2519
|
-
this.ctxoRoot =
|
|
2594
|
+
this.ctxoRoot = join15(projectRoot, ".ctxo");
|
|
2520
2595
|
}
|
|
2521
2596
|
async run() {
|
|
2522
2597
|
console.error("[ctxo] Starting file watcher...");
|
|
2523
2598
|
const registry = new LanguageAdapterRegistry();
|
|
2524
|
-
|
|
2599
|
+
const keepAliveByExt = /* @__PURE__ */ new Map();
|
|
2525
2600
|
for (const { plugin, adapter } of await loadPlugins(this.projectRoot)) {
|
|
2526
2601
|
if (typeof adapter.initialize === "function") {
|
|
2527
2602
|
try {
|
|
@@ -2532,15 +2607,12 @@ var WatchCommand = class {
|
|
|
2532
2607
|
}
|
|
2533
2608
|
}
|
|
2534
2609
|
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
|
-
}
|
|
2610
|
+
const capable = adapter;
|
|
2611
|
+
if (typeof capable.getIncrementalReindex === "function") {
|
|
2612
|
+
const cap = capable.getIncrementalReindex();
|
|
2613
|
+
if (cap?.isReady() && await cap.startKeepAlive()) {
|
|
2614
|
+
for (const ext of plugin.extensions) keepAliveByExt.set(ext.toLowerCase(), cap);
|
|
2615
|
+
console.error(`[ctxo] ${plugin.id} watch: keep-alive active (full tier, fast incremental re-index)`);
|
|
2544
2616
|
}
|
|
2545
2617
|
}
|
|
2546
2618
|
console.error(`[ctxo] Plugin ${plugin.id}@${plugin.version} (${plugin.tier} tier)`);
|
|
@@ -2561,8 +2633,9 @@ var WatchCommand = class {
|
|
|
2561
2633
|
if (!adapter) return;
|
|
2562
2634
|
const relativePath = relative2(this.projectRoot, filePath).replace(/\\/g, "/");
|
|
2563
2635
|
try {
|
|
2564
|
-
|
|
2565
|
-
|
|
2636
|
+
const cap = keepAliveByExt.get(extname4(filePath).toLowerCase());
|
|
2637
|
+
if (cap?.isReady()) {
|
|
2638
|
+
const result = await cap.reindexFile(relativePath);
|
|
2566
2639
|
if (result) {
|
|
2567
2640
|
const commits2 = await gitAdapter.getCommitHistory(relativePath);
|
|
2568
2641
|
const intent2 = commits2.map((c) => ({
|
|
@@ -2598,7 +2671,7 @@ var WatchCommand = class {
|
|
|
2598
2671
|
};
|
|
2599
2672
|
writer.write(fileIndex2);
|
|
2600
2673
|
storage.writeSymbolFile(fileIndex2);
|
|
2601
|
-
console.error(`[ctxo] Re-indexed (
|
|
2674
|
+
console.error(`[ctxo] Re-indexed (keep-alive): ${relativePath}`);
|
|
2602
2675
|
return;
|
|
2603
2676
|
}
|
|
2604
2677
|
}
|
|
@@ -2686,8 +2759,9 @@ var WatchCommand = class {
|
|
|
2686
2759
|
clearTimeout(timeout);
|
|
2687
2760
|
}
|
|
2688
2761
|
if (communitySnapshotTimer) clearTimeout(communitySnapshotTimer);
|
|
2689
|
-
const
|
|
2690
|
-
|
|
2762
|
+
const uniques = new Set(keepAliveByExt.values());
|
|
2763
|
+
const shutdowns = Promise.all([...uniques].map((c) => c.dispose()));
|
|
2764
|
+
shutdowns.then(() => {
|
|
2691
2765
|
watcher.stop().then(() => {
|
|
2692
2766
|
storage.close();
|
|
2693
2767
|
console.error("[ctxo] Watcher stopped");
|
|
@@ -2731,21 +2805,21 @@ function readHeadSha() {
|
|
|
2731
2805
|
}
|
|
2732
2806
|
|
|
2733
2807
|
// src/cli/stats-command.ts
|
|
2734
|
-
import { join as
|
|
2735
|
-
import { existsSync as
|
|
2808
|
+
import { join as join16 } from "path";
|
|
2809
|
+
import { existsSync as existsSync14, readFileSync as readFileSync10 } from "fs";
|
|
2736
2810
|
import initSqlJs from "sql.js";
|
|
2737
2811
|
var StatsCommand = class {
|
|
2738
2812
|
ctxoRoot;
|
|
2739
2813
|
constructor(projectRoot) {
|
|
2740
|
-
this.ctxoRoot =
|
|
2814
|
+
this.ctxoRoot = join16(projectRoot, ".ctxo");
|
|
2741
2815
|
}
|
|
2742
2816
|
async run(options) {
|
|
2743
2817
|
if (!this.isStatsEnabled()) {
|
|
2744
2818
|
console.error("[ctxo] Stats collection is disabled in .ctxo/config.yaml");
|
|
2745
2819
|
return;
|
|
2746
2820
|
}
|
|
2747
|
-
const dbPath =
|
|
2748
|
-
if (!
|
|
2821
|
+
const dbPath = join16(this.ctxoRoot, ".cache", "symbols.db");
|
|
2822
|
+
if (!existsSync14(dbPath)) {
|
|
2749
2823
|
if (options.json) {
|
|
2750
2824
|
process.stdout.write(JSON.stringify(this.emptyReport(options.days)) + "\n");
|
|
2751
2825
|
} else {
|
|
@@ -2792,8 +2866,8 @@ var StatsCommand = class {
|
|
|
2792
2866
|
}
|
|
2793
2867
|
}
|
|
2794
2868
|
isStatsEnabled() {
|
|
2795
|
-
const configPath =
|
|
2796
|
-
if (!
|
|
2869
|
+
const configPath = join16(this.ctxoRoot, "config.yaml");
|
|
2870
|
+
if (!existsSync14(configPath)) return true;
|
|
2797
2871
|
try {
|
|
2798
2872
|
const raw = readFileSync10(configPath, "utf-8");
|
|
2799
2873
|
const match = raw.match(/stats:\s*\n\s*enabled:\s*(true|false)/);
|
|
@@ -2884,8 +2958,8 @@ var StatsCommand = class {
|
|
|
2884
2958
|
};
|
|
2885
2959
|
|
|
2886
2960
|
// src/cli/doctor-command.ts
|
|
2887
|
-
import { join as
|
|
2888
|
-
import { existsSync as
|
|
2961
|
+
import { join as join24 } from "path";
|
|
2962
|
+
import { existsSync as existsSync22 } from "fs";
|
|
2889
2963
|
|
|
2890
2964
|
// src/adapters/diagnostics/health-checker.ts
|
|
2891
2965
|
var log4 = createLogger("ctxo:doctor");
|
|
@@ -3220,10 +3294,111 @@ function pluginResolves(packageName) {
|
|
|
3220
3294
|
}
|
|
3221
3295
|
}
|
|
3222
3296
|
|
|
3297
|
+
// src/adapters/diagnostics/checks/java-tier-check.ts
|
|
3298
|
+
import { createRequire as createRequire7 } from "module";
|
|
3299
|
+
import { existsSync as existsSync15, readdirSync as readdirSync3 } from "fs";
|
|
3300
|
+
import { join as join17, dirname as dirname7 } from "path";
|
|
3301
|
+
var require4 = createRequire7(import.meta.url);
|
|
3302
|
+
function evaluateJavaTier(i) {
|
|
3303
|
+
const id = "java_tier";
|
|
3304
|
+
const title = "Java analysis tier";
|
|
3305
|
+
if (!i.hasJava) {
|
|
3306
|
+
return { id, title, status: "pass", message: "no Java sources detected" };
|
|
3307
|
+
}
|
|
3308
|
+
const jreOk = (i.jreMajor ?? 0) >= 17;
|
|
3309
|
+
if (jreOk && i.analyzerInstalled) {
|
|
3310
|
+
return { id, title, status: "pass", message: `full tier (JRE ${i.jreMajor}, analyzer installed)` };
|
|
3311
|
+
}
|
|
3312
|
+
if (jreOk && !i.analyzerInstalled) {
|
|
3313
|
+
return {
|
|
3314
|
+
id,
|
|
3315
|
+
title,
|
|
3316
|
+
status: "warn",
|
|
3317
|
+
message: `syntax tier (JRE ${i.jreMajor} present, analyzer missing)`,
|
|
3318
|
+
fix: 'Run "ctxo install java --full-tier" for resolved call/use edges'
|
|
3319
|
+
};
|
|
3320
|
+
}
|
|
3321
|
+
if (!jreOk && i.analyzerInstalled) {
|
|
3322
|
+
return {
|
|
3323
|
+
id,
|
|
3324
|
+
title,
|
|
3325
|
+
status: "warn",
|
|
3326
|
+
message: "syntax tier (analyzer installed but no JRE 17+)",
|
|
3327
|
+
fix: "Install a JRE 17+ and ensure it is on PATH (or set JAVA_HOME)"
|
|
3328
|
+
};
|
|
3329
|
+
}
|
|
3330
|
+
return {
|
|
3331
|
+
id,
|
|
3332
|
+
title,
|
|
3333
|
+
status: "pass",
|
|
3334
|
+
message: 'syntax tier (install JRE 17+ and run "ctxo install java --full-tier" for full tier)'
|
|
3335
|
+
};
|
|
3336
|
+
}
|
|
3337
|
+
function analyzerJarPresent() {
|
|
3338
|
+
try {
|
|
3339
|
+
const pkgJson = require4.resolve("@ctxo/lang-java-analyzer/package.json");
|
|
3340
|
+
const jarPath = join17(dirname7(pkgJson), "jar", "ctxo-jdt-analyzer.jar");
|
|
3341
|
+
return existsSync15(jarPath);
|
|
3342
|
+
} catch {
|
|
3343
|
+
return false;
|
|
3344
|
+
}
|
|
3345
|
+
}
|
|
3346
|
+
var SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
3347
|
+
"node_modules",
|
|
3348
|
+
"target",
|
|
3349
|
+
"build",
|
|
3350
|
+
"out",
|
|
3351
|
+
"bin",
|
|
3352
|
+
".git",
|
|
3353
|
+
".ctxo",
|
|
3354
|
+
"dist",
|
|
3355
|
+
"coverage"
|
|
3356
|
+
]);
|
|
3357
|
+
function findJavaFile(root, maxDepth) {
|
|
3358
|
+
function walk(dir, depth) {
|
|
3359
|
+
let entries;
|
|
3360
|
+
try {
|
|
3361
|
+
entries = readdirSync3(dir, { withFileTypes: true });
|
|
3362
|
+
} catch {
|
|
3363
|
+
return false;
|
|
3364
|
+
}
|
|
3365
|
+
for (const entry of entries) {
|
|
3366
|
+
if (entry.isSymbolicLink()) continue;
|
|
3367
|
+
if (entry.isFile() && entry.name.endsWith(".java")) return true;
|
|
3368
|
+
}
|
|
3369
|
+
if (depth >= maxDepth) return false;
|
|
3370
|
+
for (const entry of entries) {
|
|
3371
|
+
if (entry.isSymbolicLink()) continue;
|
|
3372
|
+
if (entry.isDirectory() && !SKIP_DIRS.has(entry.name)) {
|
|
3373
|
+
if (walk(join17(dir, entry.name), depth + 1)) return true;
|
|
3374
|
+
}
|
|
3375
|
+
}
|
|
3376
|
+
return false;
|
|
3377
|
+
}
|
|
3378
|
+
return walk(root, 0);
|
|
3379
|
+
}
|
|
3380
|
+
function projectHasJava(root) {
|
|
3381
|
+
for (const f of ["pom.xml", "build.gradle", "build.gradle.kts"]) {
|
|
3382
|
+
if (existsSync15(join17(root, f))) return true;
|
|
3383
|
+
}
|
|
3384
|
+
return findJavaFile(root, 4);
|
|
3385
|
+
}
|
|
3386
|
+
var JavaTierCheck = class {
|
|
3387
|
+
id = "java_tier";
|
|
3388
|
+
title = "Java analysis tier";
|
|
3389
|
+
async run(ctx) {
|
|
3390
|
+
return evaluateJavaTier({
|
|
3391
|
+
hasJava: projectHasJava(ctx.projectRoot),
|
|
3392
|
+
jreMajor: detectJavaMajor(),
|
|
3393
|
+
analyzerInstalled: analyzerJarPresent()
|
|
3394
|
+
});
|
|
3395
|
+
}
|
|
3396
|
+
};
|
|
3397
|
+
|
|
3223
3398
|
// src/adapters/diagnostics/checks/git-check.ts
|
|
3224
3399
|
import { execFileSync as execFileSync5 } from "child_process";
|
|
3225
|
-
import { existsSync as
|
|
3226
|
-
import { join as
|
|
3400
|
+
import { existsSync as existsSync16 } from "fs";
|
|
3401
|
+
import { join as join18 } from "path";
|
|
3227
3402
|
var GitBinaryCheck = class {
|
|
3228
3403
|
id = "git_binary";
|
|
3229
3404
|
title = "Git available";
|
|
@@ -3240,7 +3415,7 @@ var GitRepoCheck = class {
|
|
|
3240
3415
|
id = "git_repo";
|
|
3241
3416
|
title = "Git repository";
|
|
3242
3417
|
async run(ctx) {
|
|
3243
|
-
if (
|
|
3418
|
+
if (existsSync16(join18(ctx.projectRoot, ".git"))) {
|
|
3244
3419
|
return { id: this.id, title: this.title, status: "pass", message: ctx.projectRoot, value: ctx.projectRoot };
|
|
3245
3420
|
}
|
|
3246
3421
|
return { id: this.id, title: this.title, status: "fail", message: "Not a git repository", fix: 'Run "git init" or clone a repository' };
|
|
@@ -3248,9 +3423,9 @@ var GitRepoCheck = class {
|
|
|
3248
3423
|
};
|
|
3249
3424
|
|
|
3250
3425
|
// src/adapters/diagnostics/checks/index-check.ts
|
|
3251
|
-
import { existsSync as
|
|
3426
|
+
import { existsSync as existsSync17 } from "fs";
|
|
3252
3427
|
import { execFileSync as execFileSync6 } from "child_process";
|
|
3253
|
-
import { join as
|
|
3428
|
+
import { join as join19 } from "path";
|
|
3254
3429
|
function pass2(id, title, message, value) {
|
|
3255
3430
|
return { id, title, status: "pass", message, value };
|
|
3256
3431
|
}
|
|
@@ -3267,8 +3442,8 @@ var IndexDirectoryCheck = class {
|
|
|
3267
3442
|
id = "index_directory";
|
|
3268
3443
|
title = "Index directory";
|
|
3269
3444
|
async run(ctx) {
|
|
3270
|
-
const indexDir =
|
|
3271
|
-
if (!
|
|
3445
|
+
const indexDir = join19(ctx.ctxoRoot, "index");
|
|
3446
|
+
if (!existsSync17(indexDir)) {
|
|
3272
3447
|
return fail(this.id, this.title, "No index directory", 'Run "ctxo index"');
|
|
3273
3448
|
}
|
|
3274
3449
|
const indices = getIndices(ctx);
|
|
@@ -3362,8 +3537,8 @@ var CoChangesCacheCheck = class {
|
|
|
3362
3537
|
id = "co_changes_cache";
|
|
3363
3538
|
title = "Co-changes cache";
|
|
3364
3539
|
async run(ctx) {
|
|
3365
|
-
const coChangesPath =
|
|
3366
|
-
if (
|
|
3540
|
+
const coChangesPath = join19(ctx.ctxoRoot, "index", "co-changes.json");
|
|
3541
|
+
if (existsSync17(coChangesPath)) {
|
|
3367
3542
|
return pass2(this.id, this.title, "present");
|
|
3368
3543
|
}
|
|
3369
3544
|
return warn2(this.id, this.title, "missing (co-change analysis disabled)", 'Run "ctxo index" with git history');
|
|
@@ -3387,14 +3562,14 @@ var SchemaVersionCheck = class {
|
|
|
3387
3562
|
};
|
|
3388
3563
|
|
|
3389
3564
|
// src/adapters/diagnostics/checks/storage-check.ts
|
|
3390
|
-
import { existsSync as
|
|
3391
|
-
import { join as
|
|
3565
|
+
import { existsSync as existsSync18, readFileSync as readFileSync11 } from "fs";
|
|
3566
|
+
import { join as join20 } from "path";
|
|
3392
3567
|
var SqliteCacheCheck = class {
|
|
3393
3568
|
id = "sqlite_cache";
|
|
3394
3569
|
title = "SQLite cache";
|
|
3395
3570
|
async run(ctx) {
|
|
3396
|
-
const dbPath =
|
|
3397
|
-
if (!
|
|
3571
|
+
const dbPath = join20(ctx.ctxoRoot, ".cache", "symbols.db");
|
|
3572
|
+
if (!existsSync18(dbPath)) {
|
|
3398
3573
|
return { id: this.id, title: this.title, status: "warn", message: "SQLite cache missing", fix: 'Run "ctxo sync" to rebuild' };
|
|
3399
3574
|
}
|
|
3400
3575
|
try {
|
|
@@ -3416,14 +3591,14 @@ var SqliteCacheCheck = class {
|
|
|
3416
3591
|
};
|
|
3417
3592
|
|
|
3418
3593
|
// src/adapters/diagnostics/checks/config-check.ts
|
|
3419
|
-
import { existsSync as
|
|
3420
|
-
import { join as
|
|
3594
|
+
import { existsSync as existsSync19, readFileSync as readFileSync12 } from "fs";
|
|
3595
|
+
import { join as join21 } from "path";
|
|
3421
3596
|
var ConfigFileCheck = class {
|
|
3422
3597
|
id = "config_file";
|
|
3423
3598
|
title = "Config file";
|
|
3424
3599
|
async run(ctx) {
|
|
3425
|
-
const configPath =
|
|
3426
|
-
if (!
|
|
3600
|
+
const configPath = join21(ctx.ctxoRoot, "config.yaml");
|
|
3601
|
+
if (!existsSync19(configPath)) {
|
|
3427
3602
|
return { id: this.id, title: this.title, status: "warn", message: "No config.yaml (using defaults)" };
|
|
3428
3603
|
}
|
|
3429
3604
|
try {
|
|
@@ -3461,8 +3636,8 @@ var ConfigFileCheck = class {
|
|
|
3461
3636
|
};
|
|
3462
3637
|
|
|
3463
3638
|
// src/adapters/diagnostics/checks/disk-check.ts
|
|
3464
|
-
import { existsSync as
|
|
3465
|
-
import { join as
|
|
3639
|
+
import { existsSync as existsSync20, readdirSync as readdirSync4, statSync as statSync2 } from "fs";
|
|
3640
|
+
import { join as join22 } from "path";
|
|
3466
3641
|
var DEFAULT_THRESHOLDS = { warnMb: 100, failMb: 500 };
|
|
3467
3642
|
var DiskUsageCheck = class {
|
|
3468
3643
|
id = "disk_usage";
|
|
@@ -3472,7 +3647,7 @@ var DiskUsageCheck = class {
|
|
|
3472
3647
|
this.thresholds = thresholds;
|
|
3473
3648
|
}
|
|
3474
3649
|
async run(ctx) {
|
|
3475
|
-
if (!
|
|
3650
|
+
if (!existsSync20(ctx.ctxoRoot)) {
|
|
3476
3651
|
return { id: this.id, title: this.title, status: "pass", message: "no .ctxo/ directory", value: "0" };
|
|
3477
3652
|
}
|
|
3478
3653
|
const bytes = this.getDirSize(ctx.ctxoRoot);
|
|
@@ -3489,12 +3664,12 @@ var DiskUsageCheck = class {
|
|
|
3489
3664
|
getDirSize(dirPath) {
|
|
3490
3665
|
let total = 0;
|
|
3491
3666
|
try {
|
|
3492
|
-
const entries =
|
|
3667
|
+
const entries = readdirSync4(dirPath, { withFileTypes: true, recursive: true });
|
|
3493
3668
|
for (const entry of entries) {
|
|
3494
3669
|
if (entry.isFile()) {
|
|
3495
3670
|
try {
|
|
3496
3671
|
const parent = entry.parentPath ?? entry.path ?? dirPath;
|
|
3497
|
-
total += statSync2(
|
|
3672
|
+
total += statSync2(join22(parent, entry.name)).size;
|
|
3498
3673
|
} catch {
|
|
3499
3674
|
}
|
|
3500
3675
|
}
|
|
@@ -3507,9 +3682,9 @@ var DiskUsageCheck = class {
|
|
|
3507
3682
|
|
|
3508
3683
|
// src/cli/doctor-fix.ts
|
|
3509
3684
|
import { execFileSync as execFileSync7 } from "child_process";
|
|
3510
|
-
import { appendFileSync, existsSync as
|
|
3511
|
-
import { createRequire as
|
|
3512
|
-
import { dirname as
|
|
3685
|
+
import { appendFileSync, existsSync as existsSync21, mkdirSync as mkdirSync6, rmSync as rmSync2, unlinkSync as unlinkSync2 } from "fs";
|
|
3686
|
+
import { createRequire as createRequire8 } from "module";
|
|
3687
|
+
import { dirname as dirname8, join as join23 } from "path";
|
|
3513
3688
|
async function applyFixes(report, options) {
|
|
3514
3689
|
if (isCI(process.env) && !options.yes && !options.dryRun) {
|
|
3515
3690
|
return {
|
|
@@ -3644,8 +3819,8 @@ async function fixIndex(projectRoot, dryRun) {
|
|
|
3644
3819
|
}
|
|
3645
3820
|
async function pruneOrphans(projectRoot, ctxoRoot, dryRun) {
|
|
3646
3821
|
if (dryRun) return;
|
|
3647
|
-
const indexDir =
|
|
3648
|
-
if (!
|
|
3822
|
+
const indexDir = join23(ctxoRoot, "index");
|
|
3823
|
+
if (!existsSync21(indexDir)) return;
|
|
3649
3824
|
const sourceFiles = listGitFiles2(projectRoot);
|
|
3650
3825
|
if (sourceFiles.size === 0) return;
|
|
3651
3826
|
const reader = new JsonIndexReader(ctxoRoot);
|
|
@@ -3653,9 +3828,9 @@ async function pruneOrphans(projectRoot, ctxoRoot, dryRun) {
|
|
|
3653
3828
|
let removed = 0;
|
|
3654
3829
|
for (const idx of indices) {
|
|
3655
3830
|
if (sourceFiles.has(idx.file)) continue;
|
|
3656
|
-
const jsonPath =
|
|
3831
|
+
const jsonPath = join23(indexDir, `${idx.file}.json`);
|
|
3657
3832
|
try {
|
|
3658
|
-
if (
|
|
3833
|
+
if (existsSync21(jsonPath)) {
|
|
3659
3834
|
unlinkSync2(jsonPath);
|
|
3660
3835
|
removed += 1;
|
|
3661
3836
|
pruneEmptyParents(jsonPath, indexDir);
|
|
@@ -3666,14 +3841,14 @@ async function pruneOrphans(projectRoot, ctxoRoot, dryRun) {
|
|
|
3666
3841
|
if (removed === 0) return;
|
|
3667
3842
|
}
|
|
3668
3843
|
function pruneEmptyParents(filePath, stopAt) {
|
|
3669
|
-
let dir =
|
|
3844
|
+
let dir = dirname8(filePath);
|
|
3670
3845
|
while (dir !== stopAt && dir.startsWith(stopAt)) {
|
|
3671
3846
|
try {
|
|
3672
3847
|
rmSync2(dir, { recursive: false });
|
|
3673
3848
|
} catch {
|
|
3674
3849
|
return;
|
|
3675
3850
|
}
|
|
3676
|
-
dir =
|
|
3851
|
+
dir = dirname8(dir);
|
|
3677
3852
|
}
|
|
3678
3853
|
}
|
|
3679
3854
|
function listGitFiles2(projectRoot) {
|
|
@@ -3689,12 +3864,12 @@ function listGitFiles2(projectRoot) {
|
|
|
3689
3864
|
}
|
|
3690
3865
|
}
|
|
3691
3866
|
async function fixSqliteCache(projectRoot, ctxoRoot, dryRun) {
|
|
3692
|
-
const cacheDir =
|
|
3867
|
+
const cacheDir = join23(ctxoRoot, ".cache");
|
|
3693
3868
|
if (dryRun) return;
|
|
3694
|
-
if (
|
|
3869
|
+
if (existsSync21(cacheDir)) rmSync2(cacheDir, { recursive: true, force: true });
|
|
3695
3870
|
await new SyncCommand(projectRoot).run();
|
|
3696
3871
|
}
|
|
3697
|
-
var doctorRequire =
|
|
3872
|
+
var doctorRequire = createRequire8(import.meta.url);
|
|
3698
3873
|
function canResolvePlugin(lang) {
|
|
3699
3874
|
try {
|
|
3700
3875
|
doctorRequire.resolve(`${officialPluginFor(lang)}/package.json`);
|
|
@@ -3708,8 +3883,8 @@ function isCI(env) {
|
|
|
3708
3883
|
}
|
|
3709
3884
|
function writeLog(ctxoRoot, attempts, halted, reason) {
|
|
3710
3885
|
try {
|
|
3711
|
-
if (!
|
|
3712
|
-
const path =
|
|
3886
|
+
if (!existsSync21(ctxoRoot)) mkdirSync6(ctxoRoot, { recursive: true });
|
|
3887
|
+
const path = join23(ctxoRoot, "doctor-fix.log");
|
|
3713
3888
|
const ts = (/* @__PURE__ */ new Date()).toISOString();
|
|
3714
3889
|
const lines = [`# ${ts} doctor --fix${halted ? " (halted: " + reason + ")" : ""}`];
|
|
3715
3890
|
for (const a of attempts) {
|
|
@@ -3742,10 +3917,10 @@ var DoctorCommand = class {
|
|
|
3742
3917
|
ctxoRoot;
|
|
3743
3918
|
constructor(projectRoot) {
|
|
3744
3919
|
this.projectRoot = projectRoot;
|
|
3745
|
-
this.ctxoRoot =
|
|
3920
|
+
this.ctxoRoot = join24(projectRoot, ".ctxo");
|
|
3746
3921
|
}
|
|
3747
3922
|
async run(options = {}) {
|
|
3748
|
-
const { getVersion: getVersion2 } = await import("./cli-router-
|
|
3923
|
+
const { getVersion: getVersion2 } = await import("./cli-router-NFAGLJ46.js");
|
|
3749
3924
|
console.error(`ctxo v${getVersion2()} \u2014 health check
|
|
3750
3925
|
`);
|
|
3751
3926
|
const checks = [
|
|
@@ -3760,6 +3935,7 @@ var DoctorCommand = class {
|
|
|
3760
3935
|
new TsMorphCheck(),
|
|
3761
3936
|
new TreeSitterCheck(),
|
|
3762
3937
|
new LanguageCoverageCheck(),
|
|
3938
|
+
new JavaTierCheck(),
|
|
3763
3939
|
new DiskUsageCheck(),
|
|
3764
3940
|
new SymbolCountCheck(),
|
|
3765
3941
|
new EdgeCountCheck(),
|
|
@@ -3767,8 +3943,8 @@ var DoctorCommand = class {
|
|
|
3767
3943
|
new CoChangesCacheCheck(),
|
|
3768
3944
|
new SchemaVersionCheck()
|
|
3769
3945
|
];
|
|
3770
|
-
const indexDir =
|
|
3771
|
-
const indices =
|
|
3946
|
+
const indexDir = join24(this.ctxoRoot, "index");
|
|
3947
|
+
const indices = existsSync22(indexDir) ? new JsonIndexReader(this.ctxoRoot).readAll() : [];
|
|
3772
3948
|
const checker = new HealthChecker(checks, { quiet: Boolean(options.quiet) });
|
|
3773
3949
|
const report = await checker.runAll({
|
|
3774
3950
|
projectRoot: this.projectRoot,
|
|
@@ -3809,8 +3985,8 @@ var DoctorCommand = class {
|
|
|
3809
3985
|
};
|
|
3810
3986
|
|
|
3811
3987
|
// src/cli/visualize-command.ts
|
|
3812
|
-
import { readFileSync as readFileSync13, writeFileSync as writeFileSync5, existsSync as
|
|
3813
|
-
import { join as
|
|
3988
|
+
import { readFileSync as readFileSync13, writeFileSync as writeFileSync5, existsSync as existsSync23 } from "fs";
|
|
3989
|
+
import { join as join25, basename } from "path";
|
|
3814
3990
|
import { execFile } from "child_process";
|
|
3815
3991
|
|
|
3816
3992
|
// src/cli/graph-loader.ts
|
|
@@ -3826,7 +4002,7 @@ function loadGraph(ctxoRoot) {
|
|
|
3826
4002
|
var VisualizeCommand = class {
|
|
3827
4003
|
constructor(projectRoot) {
|
|
3828
4004
|
this.projectRoot = projectRoot;
|
|
3829
|
-
this.ctxoRoot =
|
|
4005
|
+
this.ctxoRoot = join25(projectRoot, ".ctxo");
|
|
3830
4006
|
}
|
|
3831
4007
|
projectRoot;
|
|
3832
4008
|
ctxoRoot;
|
|
@@ -3969,7 +4145,7 @@ var VisualizeCommand = class {
|
|
|
3969
4145
|
const templatePath = this.getTemplatePath();
|
|
3970
4146
|
const template = readFileSync13(templatePath, "utf-8");
|
|
3971
4147
|
const html = template.replace("/*__CTXO_DATA__*/null", JSON.stringify(payload));
|
|
3972
|
-
const outputPath = options.output ??
|
|
4148
|
+
const outputPath = options.output ?? join25(this.ctxoRoot, "visualize.html");
|
|
3973
4149
|
writeFileSync5(outputPath, html, "utf-8");
|
|
3974
4150
|
console.error(`[ctxo] Wrote visualization to ${outputPath}`);
|
|
3975
4151
|
console.error(`[ctxo] ${nodes.length} symbols, ${edges.length} edges, ${deadResult.deadSymbols.length} dead`);
|
|
@@ -3980,9 +4156,9 @@ var VisualizeCommand = class {
|
|
|
3980
4156
|
getTemplatePath() {
|
|
3981
4157
|
let dir = import.meta.dirname;
|
|
3982
4158
|
for (let i = 0; i < 10; i++) {
|
|
3983
|
-
const candidate =
|
|
3984
|
-
if (
|
|
3985
|
-
const parent =
|
|
4159
|
+
const candidate = join25(dir, "pages", "visualize-template.html");
|
|
4160
|
+
if (existsSync23(candidate)) return candidate;
|
|
4161
|
+
const parent = join25(dir, "..");
|
|
3986
4162
|
if (parent === dir) break;
|
|
3987
4163
|
dir = parent;
|
|
3988
4164
|
}
|
|
@@ -4006,8 +4182,8 @@ var VisualizeCommand = class {
|
|
|
4006
4182
|
};
|
|
4007
4183
|
|
|
4008
4184
|
// src/cli/update-command.ts
|
|
4009
|
-
import { existsSync as
|
|
4010
|
-
import { join as
|
|
4185
|
+
import { existsSync as existsSync24, readFileSync as readFileSync14 } from "fs";
|
|
4186
|
+
import { join as join26 } from "path";
|
|
4011
4187
|
|
|
4012
4188
|
// src/core/update/registry-client.ts
|
|
4013
4189
|
import { request } from "https";
|
|
@@ -4325,8 +4501,8 @@ var UpdateCommand = class {
|
|
|
4325
4501
|
return this.projectHasCtxoDep() ? "execute" : "print";
|
|
4326
4502
|
}
|
|
4327
4503
|
projectHasCtxoDep() {
|
|
4328
|
-
const pkg =
|
|
4329
|
-
if (!
|
|
4504
|
+
const pkg = join26(this.projectRoot, "package.json");
|
|
4505
|
+
if (!existsSync24(pkg)) return false;
|
|
4330
4506
|
try {
|
|
4331
4507
|
const json = JSON.parse(readFileSync14(pkg, "utf-8"));
|
|
4332
4508
|
const merged = { ...json.dependencies ?? {}, ...json.devDependencies ?? {} };
|
|
@@ -4363,11 +4539,11 @@ function isLockedCI2(env) {
|
|
|
4363
4539
|
}
|
|
4364
4540
|
|
|
4365
4541
|
// src/cli/blast-radius-command.ts
|
|
4366
|
-
import { join as
|
|
4542
|
+
import { join as join27 } from "path";
|
|
4367
4543
|
var BlastRadiusCommand = class {
|
|
4368
4544
|
ctxoRoot;
|
|
4369
4545
|
constructor(projectRoot) {
|
|
4370
|
-
this.ctxoRoot =
|
|
4546
|
+
this.ctxoRoot = join27(projectRoot, ".ctxo");
|
|
4371
4547
|
}
|
|
4372
4548
|
run(options) {
|
|
4373
4549
|
const { graph } = loadGraph(this.ctxoRoot);
|
|
@@ -4395,8 +4571,8 @@ var BlastRadiusCommand = class {
|
|
|
4395
4571
|
};
|
|
4396
4572
|
|
|
4397
4573
|
// src/cli/gate-hook-command.ts
|
|
4398
|
-
import { join as
|
|
4399
|
-
import { existsSync as
|
|
4574
|
+
import { join as join29, relative as relative3, sep as sep2 } from "path";
|
|
4575
|
+
import { existsSync as existsSync26, readFileSync as readFileSync16 } from "fs";
|
|
4400
4576
|
|
|
4401
4577
|
// src/core/gate/gate-config.ts
|
|
4402
4578
|
var DIAL = {
|
|
@@ -4463,8 +4639,8 @@ function transcriptHasBlastCheck(lines, symbolId) {
|
|
|
4463
4639
|
}
|
|
4464
4640
|
|
|
4465
4641
|
// src/adapters/gate/gate-session-store.ts
|
|
4466
|
-
import { existsSync as
|
|
4467
|
-
import { join as
|
|
4642
|
+
import { existsSync as existsSync25, mkdirSync as mkdirSync7, readFileSync as readFileSync15, writeFileSync as writeFileSync6 } from "fs";
|
|
4643
|
+
import { join as join28 } from "path";
|
|
4468
4644
|
var log7 = createLogger("ctxo:gate-store");
|
|
4469
4645
|
var GateSessionStore = class {
|
|
4470
4646
|
constructor(cacheDir) {
|
|
@@ -4473,11 +4649,11 @@ var GateSessionStore = class {
|
|
|
4473
4649
|
cacheDir;
|
|
4474
4650
|
file(sessionId) {
|
|
4475
4651
|
const safe = sessionId.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
4476
|
-
return
|
|
4652
|
+
return join28(this.cacheDir, "gate", `${safe}.json`);
|
|
4477
4653
|
}
|
|
4478
4654
|
read(sessionId) {
|
|
4479
4655
|
const f = this.file(sessionId);
|
|
4480
|
-
if (!
|
|
4656
|
+
if (!existsSync25(f)) return /* @__PURE__ */ new Set();
|
|
4481
4657
|
try {
|
|
4482
4658
|
return new Set(JSON.parse(readFileSync15(f, "utf-8")));
|
|
4483
4659
|
} catch (err) {
|
|
@@ -4493,7 +4669,7 @@ var GateSessionStore = class {
|
|
|
4493
4669
|
const set = this.read(sessionId);
|
|
4494
4670
|
set.add(symbolId);
|
|
4495
4671
|
const f = this.file(sessionId);
|
|
4496
|
-
mkdirSync7(
|
|
4672
|
+
mkdirSync7(join28(this.cacheDir, "gate"), { recursive: true });
|
|
4497
4673
|
writeFileSync6(f, JSON.stringify([...set]), "utf-8");
|
|
4498
4674
|
} catch (err) {
|
|
4499
4675
|
log7.error(`${err.message}`);
|
|
@@ -4532,8 +4708,8 @@ var RANK_ALL = 1e5;
|
|
|
4532
4708
|
var GateHookCommand = class {
|
|
4533
4709
|
constructor(projectRoot) {
|
|
4534
4710
|
this.projectRoot = projectRoot;
|
|
4535
|
-
this.ctxoRoot =
|
|
4536
|
-
this.cacheDir =
|
|
4711
|
+
this.ctxoRoot = join29(projectRoot, ".ctxo");
|
|
4712
|
+
this.cacheDir = join29(this.ctxoRoot, ".cache");
|
|
4537
4713
|
}
|
|
4538
4714
|
projectRoot;
|
|
4539
4715
|
ctxoRoot;
|
|
@@ -4549,11 +4725,11 @@ var GateHookCommand = class {
|
|
|
4549
4725
|
const thresholds = resolveGateThresholds(loaded.config.gate);
|
|
4550
4726
|
if (!thresholds.enabled) return { block: false };
|
|
4551
4727
|
const relPath = relative3(this.projectRoot, filePath).split(sep2).join("/");
|
|
4552
|
-
const indexFile =
|
|
4553
|
-
if (!
|
|
4728
|
+
const indexFile = join29(this.ctxoRoot, "index", `${relPath}.json`);
|
|
4729
|
+
if (!existsSync26(indexFile)) return { block: false };
|
|
4554
4730
|
const fileIndex = new JsonIndexReader(this.ctxoRoot).readSingle(indexFile);
|
|
4555
4731
|
if (!fileIndex) return { block: false };
|
|
4556
|
-
if (!
|
|
4732
|
+
if (!existsSync26(filePath)) return { block: false };
|
|
4557
4733
|
const source = readFileSync16(filePath, "utf-8");
|
|
4558
4734
|
const line = lineOfSnippet(source, oldString);
|
|
4559
4735
|
if (line === null) return { block: false };
|
|
@@ -4561,7 +4737,7 @@ var GateHookCommand = class {
|
|
|
4561
4737
|
if (!symbolId) return { block: false };
|
|
4562
4738
|
const store = new GateSessionStore(this.cacheDir);
|
|
4563
4739
|
if (store.hasWarned(payload.session_id, symbolId)) return { block: false };
|
|
4564
|
-
if (payload.transcript_path &&
|
|
4740
|
+
if (payload.transcript_path && existsSync26(payload.transcript_path)) {
|
|
4565
4741
|
const lines = readFileSync16(payload.transcript_path, "utf-8").split("\n").filter(Boolean);
|
|
4566
4742
|
if (transcriptHasBlastCheck(lines, symbolId)) return { block: false };
|
|
4567
4743
|
}
|
|
@@ -4627,12 +4803,12 @@ var GateHookCommand = class {
|
|
|
4627
4803
|
};
|
|
4628
4804
|
|
|
4629
4805
|
// src/cli/gate-command.ts
|
|
4630
|
-
import { join as
|
|
4806
|
+
import { join as join30 } from "path";
|
|
4631
4807
|
var RANK_ALL2 = 1e5;
|
|
4632
4808
|
var GateCommand = class {
|
|
4633
4809
|
ctxoRoot;
|
|
4634
4810
|
constructor(projectRoot) {
|
|
4635
|
-
this.ctxoRoot =
|
|
4811
|
+
this.ctxoRoot = join30(projectRoot, ".ctxo");
|
|
4636
4812
|
}
|
|
4637
4813
|
preview(options = {}) {
|
|
4638
4814
|
const gate = loadConfig(this.ctxoRoot).config.gate;
|
|
@@ -4672,8 +4848,8 @@ var GateCommand = class {
|
|
|
4672
4848
|
function getVersion() {
|
|
4673
4849
|
let dir = import.meta.dirname;
|
|
4674
4850
|
for (let i = 0; i < 10; i++) {
|
|
4675
|
-
const pkg =
|
|
4676
|
-
if (
|
|
4851
|
+
const pkg = join31(dir, "package.json");
|
|
4852
|
+
if (existsSync27(pkg)) {
|
|
4677
4853
|
try {
|
|
4678
4854
|
const json = JSON.parse(readFileSync17(pkg, "utf-8"));
|
|
4679
4855
|
return json.version ?? "unknown";
|
|
@@ -4681,7 +4857,7 @@ function getVersion() {
|
|
|
4681
4857
|
break;
|
|
4682
4858
|
}
|
|
4683
4859
|
}
|
|
4684
|
-
const parent =
|
|
4860
|
+
const parent = join31(dir, "..");
|
|
4685
4861
|
if (parent === dir) break;
|
|
4686
4862
|
dir = parent;
|
|
4687
4863
|
}
|
|
@@ -4690,8 +4866,8 @@ function getVersion() {
|
|
|
4690
4866
|
function locateGitRoot(startDir) {
|
|
4691
4867
|
let dir = startDir;
|
|
4692
4868
|
for (let i = 0; i < 12; i++) {
|
|
4693
|
-
if (
|
|
4694
|
-
const parent =
|
|
4869
|
+
if (existsSync27(join31(dir, ".git"))) return dir;
|
|
4870
|
+
const parent = dirname9(dir);
|
|
4695
4871
|
if (parent === dir) break;
|
|
4696
4872
|
dir = parent;
|
|
4697
4873
|
}
|
|
@@ -4808,6 +4984,8 @@ var CliRouter = class {
|
|
|
4808
4984
|
else if (a === "--global" || a === "-g") flagValues["global"] = true;
|
|
4809
4985
|
else if (a === "--dry-run") flagValues["dryRun"] = true;
|
|
4810
4986
|
else if (a === "--force") flagValues["force"] = true;
|
|
4987
|
+
else if (a === "--full-tier") flagValues["fullTier"] = true;
|
|
4988
|
+
else if (a === "--syntax-only") flagValues["syntaxOnly"] = true;
|
|
4811
4989
|
else if (a === "--pm") {
|
|
4812
4990
|
const next = args[++i];
|
|
4813
4991
|
if (!next) {
|
|
@@ -4838,6 +5016,8 @@ var CliRouter = class {
|
|
|
4838
5016
|
global: flagValues["global"] === true,
|
|
4839
5017
|
dryRun: flagValues["dryRun"] === true,
|
|
4840
5018
|
force: flagValues["force"] === true,
|
|
5019
|
+
fullTier: flagValues["fullTier"] === true,
|
|
5020
|
+
syntaxOnly: flagValues["syntaxOnly"] === true,
|
|
4841
5021
|
pm: typeof flagValues["pm"] === "string" ? flagValues["pm"] : void 0,
|
|
4842
5022
|
version: typeof flagValues["version"] === "string" ? flagValues["version"] : void 0
|
|
4843
5023
|
});
|
|
@@ -4972,4 +5152,4 @@ export {
|
|
|
4972
5152
|
getVersion,
|
|
4973
5153
|
locateGitRoot
|
|
4974
5154
|
};
|
|
4975
|
-
//# sourceMappingURL=cli-router-
|
|
5155
|
+
//# sourceMappingURL=cli-router-NFAGLJ46.js.map
|