ctxloom-pro 1.7.9 → 1.7.10
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.
|
@@ -2706,7 +2706,7 @@ var CallGraphIndex = class _CallGraphIndex {
|
|
|
2706
2706
|
var TS_EXTENSIONS2 = /* @__PURE__ */ new Set([".ts", ".tsx", ".js", ".jsx", ".mjs", ".vue"]);
|
|
2707
2707
|
var PY_EXTENSIONS = /* @__PURE__ */ new Set([".py", ".ipynb"]);
|
|
2708
2708
|
var AST_EXTENSIONS = /* @__PURE__ */ new Set([".ts", ".tsx", ".js", ".jsx", ".mjs", ".py", ".go", ".rs", ".java", ".cs", ".rb", ".kt", ".kts", ".swift", ".ipynb", ".php", ".dart"]);
|
|
2709
|
-
var CTXLOOM_VERSION = "1.7.
|
|
2709
|
+
var CTXLOOM_VERSION = "1.7.10".length > 0 ? "1.7.10" : "dev";
|
|
2710
2710
|
var SNAPSHOT_SCHEMA_VERSION = 2;
|
|
2711
2711
|
function compareCtxloomVersions(snapshotVer, currentVer) {
|
|
2712
2712
|
if (snapshotVer === currentVer) return "same";
|
|
@@ -4219,8 +4219,8 @@ var CoChangeIndex = class _CoChangeIndex {
|
|
|
4219
4219
|
if (event.isBulk || event.isMerge) return;
|
|
4220
4220
|
const paths = event.files.map((f) => f.path);
|
|
4221
4221
|
if (paths.length === 0) return;
|
|
4222
|
-
for (const
|
|
4223
|
-
this.nodeCounts.set(
|
|
4222
|
+
for (const path39 of paths) {
|
|
4223
|
+
this.nodeCounts.set(path39, (this.nodeCounts.get(path39) ?? 0) + 1);
|
|
4224
4224
|
}
|
|
4225
4225
|
for (let i = 0; i < paths.length; i++) {
|
|
4226
4226
|
for (let j = i + 1; j < paths.length; j++) {
|
|
@@ -4367,8 +4367,8 @@ var ChurnIndex = class _ChurnIndex {
|
|
|
4367
4367
|
*/
|
|
4368
4368
|
snapshot() {
|
|
4369
4369
|
const nodes = {};
|
|
4370
|
-
for (const [
|
|
4371
|
-
nodes[
|
|
4370
|
+
for (const [path39, raw] of this.nodes) {
|
|
4371
|
+
nodes[path39] = {
|
|
4372
4372
|
commits: raw.commits,
|
|
4373
4373
|
churnLines: raw.churnLines,
|
|
4374
4374
|
bugCommits: raw.bugCommits,
|
|
@@ -4383,8 +4383,8 @@ var ChurnIndex = class _ChurnIndex {
|
|
|
4383
4383
|
*/
|
|
4384
4384
|
static load(s) {
|
|
4385
4385
|
const idx = new _ChurnIndex();
|
|
4386
|
-
for (const [
|
|
4387
|
-
idx.nodes.set(
|
|
4386
|
+
for (const [path39, raw] of Object.entries(s.nodes)) {
|
|
4387
|
+
idx.nodes.set(path39, {
|
|
4388
4388
|
commits: raw.commits,
|
|
4389
4389
|
churnLines: raw.churnLines,
|
|
4390
4390
|
bugCommits: raw.bugCommits,
|
|
@@ -4397,8 +4397,8 @@ var ChurnIndex = class _ChurnIndex {
|
|
|
4397
4397
|
// -------------------------------------------------------------------------
|
|
4398
4398
|
// Private helpers
|
|
4399
4399
|
// -------------------------------------------------------------------------
|
|
4400
|
-
getOrCreate(
|
|
4401
|
-
const existing = this.nodes.get(
|
|
4400
|
+
getOrCreate(path39) {
|
|
4401
|
+
const existing = this.nodes.get(path39);
|
|
4402
4402
|
if (existing !== void 0) return existing;
|
|
4403
4403
|
const fresh = {
|
|
4404
4404
|
commits: 0,
|
|
@@ -4407,7 +4407,7 @@ var ChurnIndex = class _ChurnIndex {
|
|
|
4407
4407
|
authorCounts: {},
|
|
4408
4408
|
lastTouch: 0
|
|
4409
4409
|
};
|
|
4410
|
-
this.nodes.set(
|
|
4410
|
+
this.nodes.set(path39, fresh);
|
|
4411
4411
|
return fresh;
|
|
4412
4412
|
}
|
|
4413
4413
|
};
|
|
@@ -4488,12 +4488,12 @@ var OwnershipIndex = class _OwnershipIndex {
|
|
|
4488
4488
|
*/
|
|
4489
4489
|
snapshot() {
|
|
4490
4490
|
const nodes = {};
|
|
4491
|
-
for (const [
|
|
4491
|
+
for (const [path39, raw] of this.nodes) {
|
|
4492
4492
|
const authorWeights = {};
|
|
4493
4493
|
for (const [email, entry] of Object.entries(raw.authorWeights)) {
|
|
4494
4494
|
authorWeights[email] = { ...entry };
|
|
4495
4495
|
}
|
|
4496
|
-
nodes[
|
|
4496
|
+
nodes[path39] = { authorWeights, lastTouch: raw.lastTouch };
|
|
4497
4497
|
}
|
|
4498
4498
|
return { version: 1, nodes };
|
|
4499
4499
|
}
|
|
@@ -4502,23 +4502,23 @@ var OwnershipIndex = class _OwnershipIndex {
|
|
|
4502
4502
|
*/
|
|
4503
4503
|
static load(s) {
|
|
4504
4504
|
const idx = new _OwnershipIndex();
|
|
4505
|
-
for (const [
|
|
4505
|
+
for (const [path39, raw] of Object.entries(s.nodes)) {
|
|
4506
4506
|
const authorWeights = {};
|
|
4507
4507
|
for (const [email, entry] of Object.entries(raw.authorWeights)) {
|
|
4508
4508
|
authorWeights[email] = { ...entry };
|
|
4509
4509
|
}
|
|
4510
|
-
idx.nodes.set(
|
|
4510
|
+
idx.nodes.set(path39, { authorWeights, lastTouch: raw.lastTouch });
|
|
4511
4511
|
}
|
|
4512
4512
|
return idx;
|
|
4513
4513
|
}
|
|
4514
4514
|
// -------------------------------------------------------------------------
|
|
4515
4515
|
// Private helpers
|
|
4516
4516
|
// -------------------------------------------------------------------------
|
|
4517
|
-
getOrCreate(
|
|
4518
|
-
const existing = this.nodes.get(
|
|
4517
|
+
getOrCreate(path39) {
|
|
4518
|
+
const existing = this.nodes.get(path39);
|
|
4519
4519
|
if (existing !== void 0) return existing;
|
|
4520
4520
|
const fresh = { authorWeights: {}, lastTouch: 0 };
|
|
4521
|
-
this.nodes.set(
|
|
4521
|
+
this.nodes.set(path39, fresh);
|
|
4522
4522
|
return fresh;
|
|
4523
4523
|
}
|
|
4524
4524
|
};
|
|
@@ -7094,7 +7094,8 @@ function registerBlastRadiusTool(registry, ctx) {
|
|
|
7094
7094
|
return '<blast_radius changed_files="0">\n <!-- No changed files detected -->\n</blast_radius>';
|
|
7095
7095
|
}
|
|
7096
7096
|
const result = await computeBlastRadius({ changedFiles: files, depth, projectRoot: gitRoot, graph });
|
|
7097
|
-
const
|
|
7097
|
+
const overlay = await ctx.getOverlay(project_root) ?? void 0;
|
|
7098
|
+
const report = getImpactRadius({ graph, overlay, changedFiles: files, depth });
|
|
7098
7099
|
return buildBlastRadiusXml(result, depth, detail_level, report.historicalCoupling);
|
|
7099
7100
|
}
|
|
7100
7101
|
);
|
|
@@ -8731,15 +8732,16 @@ function registerDetectChangesTool(registry, ctx) {
|
|
|
8731
8732
|
if (files.length === 0) {
|
|
8732
8733
|
return '<detect_changes count="0">\n <!-- No changed files detected -->\n</detect_changes>';
|
|
8733
8734
|
}
|
|
8735
|
+
const overlay = await ctx.getOverlay(project_root) ?? void 0;
|
|
8734
8736
|
const { changedFiles: scored, summary } = detectChanges({
|
|
8735
8737
|
graph,
|
|
8736
|
-
overlay
|
|
8738
|
+
overlay,
|
|
8737
8739
|
changedFiles: files
|
|
8738
8740
|
});
|
|
8739
8741
|
if (detail_level === "minimal") {
|
|
8740
8742
|
return `<detect_changes count="${scored.length}" critical="${summary.critical}" high="${summary.high}" medium="${summary.medium}" low="${summary.low}" detail_level="minimal" />`;
|
|
8741
8743
|
}
|
|
8742
|
-
const hasOverlay =
|
|
8744
|
+
const hasOverlay = overlay !== void 0;
|
|
8743
8745
|
const xml = [
|
|
8744
8746
|
`<detect_changes count="${scored.length}" critical="${summary.critical}" high="${summary.high}" medium="${summary.medium}" low="${summary.low}">`
|
|
8745
8747
|
];
|
|
@@ -9449,6 +9451,28 @@ function registerFindLargeFunctionsTool(registry, ctx) {
|
|
|
9449
9451
|
|
|
9450
9452
|
// packages/core/src/tools/git-coupling.ts
|
|
9451
9453
|
import { z as z31 } from "zod";
|
|
9454
|
+
|
|
9455
|
+
// packages/core/src/tools/overlayNote.ts
|
|
9456
|
+
import fs22 from "fs";
|
|
9457
|
+
import path24 from "path";
|
|
9458
|
+
async function overlayUnavailableNote(ctx, projectRoot) {
|
|
9459
|
+
let rootDir = "";
|
|
9460
|
+
try {
|
|
9461
|
+
const graph = await ctx.getGraph(projectRoot);
|
|
9462
|
+
rootDir = graph.getRootDir();
|
|
9463
|
+
} catch {
|
|
9464
|
+
}
|
|
9465
|
+
if (rootDir) {
|
|
9466
|
+
const overlayFile = path24.join(rootDir, ".ctxloom", "git-overlay.json");
|
|
9467
|
+
if (fs22.existsSync(overlayFile)) {
|
|
9468
|
+
return `Git overlay exists at ${overlayFile} but could not be loaded into the server. Restart the MCP server (or check its logs for a git/overlay error); a re-index is not required.`;
|
|
9469
|
+
}
|
|
9470
|
+
return `No git overlay for ${rootDir}. Build it by running \`ctxloom index\` in that repo (the git overlay is created automatically when git is enabled). If the server was started with --no-git, restart it without that flag.`;
|
|
9471
|
+
}
|
|
9472
|
+
return "Git overlay not available. Run `ctxloom index` with git enabled to build it.";
|
|
9473
|
+
}
|
|
9474
|
+
|
|
9475
|
+
// packages/core/src/tools/git-coupling.ts
|
|
9452
9476
|
var Schema26 = z31.object({
|
|
9453
9477
|
file: z31.string().describe("File path to look up co-changed siblings for"),
|
|
9454
9478
|
limit: z31.number().int().min(1).max(50).default(10),
|
|
@@ -9456,11 +9480,11 @@ var Schema26 = z31.object({
|
|
|
9456
9480
|
half_life_days: z31.number().int().min(1).max(3650).default(90),
|
|
9457
9481
|
project_root: ProjectRootField
|
|
9458
9482
|
});
|
|
9459
|
-
function overlayUnavailableResponse(file) {
|
|
9483
|
+
function overlayUnavailableResponse(file, note) {
|
|
9460
9484
|
return {
|
|
9461
9485
|
file,
|
|
9462
9486
|
coupledFiles: [],
|
|
9463
|
-
note
|
|
9487
|
+
note
|
|
9464
9488
|
};
|
|
9465
9489
|
}
|
|
9466
9490
|
function buildExplanation(sharedCommits, lastSharedDaysAgo) {
|
|
@@ -9485,13 +9509,18 @@ function registerGitCouplingTool(registry, ctx) {
|
|
|
9485
9509
|
}
|
|
9486
9510
|
},
|
|
9487
9511
|
async (args) => {
|
|
9488
|
-
const { file, limit, min_confidence, half_life_days } = Schema26.parse(args);
|
|
9489
|
-
|
|
9490
|
-
|
|
9512
|
+
const { file, limit, min_confidence, half_life_days, project_root } = Schema26.parse(args);
|
|
9513
|
+
const overlay = await ctx.getOverlay(project_root);
|
|
9514
|
+
if (!overlay) {
|
|
9515
|
+
return JSON.stringify(
|
|
9516
|
+
overlayUnavailableResponse(file, await overlayUnavailableNote(ctx, project_root))
|
|
9517
|
+
);
|
|
9491
9518
|
}
|
|
9492
|
-
const coChange =
|
|
9519
|
+
const coChange = overlay.coChange;
|
|
9493
9520
|
if (coChange.size().pairs === 0) {
|
|
9494
|
-
return JSON.stringify(
|
|
9521
|
+
return JSON.stringify(
|
|
9522
|
+
overlayUnavailableResponse(file, await overlayUnavailableNote(ctx, project_root))
|
|
9523
|
+
);
|
|
9495
9524
|
}
|
|
9496
9525
|
const nowSec = Math.floor(Date.now() / 1e3);
|
|
9497
9526
|
const pairs = coChange.topFor({
|
|
@@ -9556,8 +9585,9 @@ function registerRiskOverlayTool(registry, ctx) {
|
|
|
9556
9585
|
}
|
|
9557
9586
|
},
|
|
9558
9587
|
async (args) => {
|
|
9559
|
-
const { nodes } = Schema27.parse(args);
|
|
9560
|
-
|
|
9588
|
+
const { nodes, project_root } = Schema27.parse(args);
|
|
9589
|
+
const overlay = await ctx.getOverlay(project_root);
|
|
9590
|
+
if (!overlay) {
|
|
9561
9591
|
const response2 = {
|
|
9562
9592
|
nodes: nodes.map((file) => ({
|
|
9563
9593
|
file,
|
|
@@ -9571,11 +9601,11 @@ function registerRiskOverlayTool(registry, ctx) {
|
|
|
9571
9601
|
note: "no git data"
|
|
9572
9602
|
})),
|
|
9573
9603
|
overallRiskScore: 0,
|
|
9574
|
-
note:
|
|
9604
|
+
note: await overlayUnavailableNote(ctx, project_root)
|
|
9575
9605
|
};
|
|
9576
9606
|
return JSON.stringify(response2);
|
|
9577
9607
|
}
|
|
9578
|
-
const { churn, ownership, coChange } =
|
|
9608
|
+
const { churn, ownership, coChange } = overlay;
|
|
9579
9609
|
const nowSec = Math.floor(Date.now() / 1e3);
|
|
9580
9610
|
const nodeEntries = nodes.map((file) => {
|
|
9581
9611
|
const churnStats = churn.statsFor(file);
|
|
@@ -9640,8 +9670,8 @@ var RulesConfigError = class extends Error {
|
|
|
9640
9670
|
};
|
|
9641
9671
|
|
|
9642
9672
|
// packages/core/src/rules/loadConfig.ts
|
|
9643
|
-
import
|
|
9644
|
-
import
|
|
9673
|
+
import fs23 from "fs/promises";
|
|
9674
|
+
import path25 from "path";
|
|
9645
9675
|
import yaml from "js-yaml";
|
|
9646
9676
|
import { z as z33 } from "zod";
|
|
9647
9677
|
var RuleSchema = z33.object({
|
|
@@ -9656,10 +9686,10 @@ var RulesConfigSchema = z33.object({
|
|
|
9656
9686
|
rules: z33.array(RuleSchema).default([])
|
|
9657
9687
|
});
|
|
9658
9688
|
async function loadRulesConfig(rootDir) {
|
|
9659
|
-
const filePath =
|
|
9689
|
+
const filePath = path25.join(rootDir, ".ctxloom", "rules.yml");
|
|
9660
9690
|
let raw;
|
|
9661
9691
|
try {
|
|
9662
|
-
raw = await
|
|
9692
|
+
raw = await fs23.readFile(filePath, "utf-8");
|
|
9663
9693
|
} catch (err) {
|
|
9664
9694
|
if (err.code === "ENOENT") return null;
|
|
9665
9695
|
throw new RulesConfigError(`Failed to read rules config: ${String(err)}`);
|
|
@@ -10072,11 +10102,11 @@ function readRecentChanges(projectRoot) {
|
|
|
10072
10102
|
return lines.slice(0, 20).map((line) => {
|
|
10073
10103
|
const x = line[0];
|
|
10074
10104
|
const y = line[1];
|
|
10075
|
-
const
|
|
10105
|
+
const path39 = line.slice(3).trim();
|
|
10076
10106
|
let status = "?";
|
|
10077
10107
|
const xy = x === " " ? y : x;
|
|
10078
10108
|
if (xy === "M" || xy === "A" || xy === "D" || xy === "R") status = xy;
|
|
10079
|
-
return { file:
|
|
10109
|
+
return { file: path39, status };
|
|
10080
10110
|
});
|
|
10081
10111
|
} catch {
|
|
10082
10112
|
return [];
|
|
@@ -10303,8 +10333,8 @@ function createToolRegistry(ctx) {
|
|
|
10303
10333
|
}
|
|
10304
10334
|
|
|
10305
10335
|
// packages/core/src/tools/ruleManager.ts
|
|
10306
|
-
import
|
|
10307
|
-
import
|
|
10336
|
+
import fs24 from "fs";
|
|
10337
|
+
import path26 from "path";
|
|
10308
10338
|
var RULE_FILES = [
|
|
10309
10339
|
".cursorrules",
|
|
10310
10340
|
"CLAUDE.md",
|
|
@@ -10328,30 +10358,30 @@ var RuleManager = class {
|
|
|
10328
10358
|
if (this.cachedRules) return this.cachedRules;
|
|
10329
10359
|
const rules = [];
|
|
10330
10360
|
for (const ruleFile of RULE_FILES) {
|
|
10331
|
-
const fullPath =
|
|
10361
|
+
const fullPath = path26.join(this.projectRoot, ruleFile);
|
|
10332
10362
|
try {
|
|
10333
10363
|
this.pathValidator.validate(fullPath);
|
|
10334
|
-
if (
|
|
10335
|
-
const stat =
|
|
10364
|
+
if (fs24.existsSync(fullPath)) {
|
|
10365
|
+
const stat = fs24.statSync(fullPath);
|
|
10336
10366
|
if (stat.isFile()) {
|
|
10337
|
-
const content =
|
|
10367
|
+
const content = fs24.readFileSync(fullPath, "utf-8");
|
|
10338
10368
|
rules.push({
|
|
10339
10369
|
name: ruleFile,
|
|
10340
10370
|
path: ruleFile,
|
|
10341
10371
|
content
|
|
10342
10372
|
});
|
|
10343
10373
|
} else if (stat.isDirectory()) {
|
|
10344
|
-
const dirEntries =
|
|
10374
|
+
const dirEntries = fs24.readdirSync(fullPath);
|
|
10345
10375
|
for (const entry of dirEntries) {
|
|
10346
|
-
const entryPath =
|
|
10376
|
+
const entryPath = path26.join(fullPath, entry);
|
|
10347
10377
|
try {
|
|
10348
10378
|
this.pathValidator.validate(entryPath);
|
|
10349
10379
|
} catch {
|
|
10350
10380
|
continue;
|
|
10351
10381
|
}
|
|
10352
|
-
const entryStat =
|
|
10382
|
+
const entryStat = fs24.statSync(entryPath);
|
|
10353
10383
|
if (entryStat.isFile()) {
|
|
10354
|
-
const content =
|
|
10384
|
+
const content = fs24.readFileSync(entryPath, "utf-8");
|
|
10355
10385
|
rules.push({
|
|
10356
10386
|
name: `${ruleFile}/${entry}`,
|
|
10357
10387
|
path: `${ruleFile}/${entry}`,
|
|
@@ -10394,8 +10424,8 @@ var RuleManager = class {
|
|
|
10394
10424
|
};
|
|
10395
10425
|
|
|
10396
10426
|
// packages/core/src/review/AuthorResolver.ts
|
|
10397
|
-
import
|
|
10398
|
-
import
|
|
10427
|
+
import fs25 from "fs/promises";
|
|
10428
|
+
import path27 from "path";
|
|
10399
10429
|
import yaml2 from "js-yaml";
|
|
10400
10430
|
var AuthorResolver = class {
|
|
10401
10431
|
constructor(ctxloomDir) {
|
|
@@ -10420,8 +10450,8 @@ var AuthorResolver = class {
|
|
|
10420
10450
|
/** Write a new mapping to the cache file. */
|
|
10421
10451
|
async writeCache(email, handle) {
|
|
10422
10452
|
this.cache = { ...this.cache, [email]: handle };
|
|
10423
|
-
await
|
|
10424
|
-
|
|
10453
|
+
await fs25.writeFile(
|
|
10454
|
+
path27.join(this.ctxloomDir, "authors-cache.json"),
|
|
10425
10455
|
JSON.stringify(this.cache, null, 2)
|
|
10426
10456
|
);
|
|
10427
10457
|
}
|
|
@@ -10430,9 +10460,9 @@ var AuthorResolver = class {
|
|
|
10430
10460
|
return emails.filter((e) => this.resolve(e) === void 0);
|
|
10431
10461
|
}
|
|
10432
10462
|
async loadYml() {
|
|
10433
|
-
const file =
|
|
10463
|
+
const file = path27.join(this.ctxloomDir, "authors.yml");
|
|
10434
10464
|
try {
|
|
10435
|
-
const raw = await
|
|
10465
|
+
const raw = await fs25.readFile(file, "utf8");
|
|
10436
10466
|
const parsed = yaml2.load(raw);
|
|
10437
10467
|
if (!parsed) return;
|
|
10438
10468
|
this.mappings = parsed.mappings ?? {};
|
|
@@ -10441,9 +10471,9 @@ var AuthorResolver = class {
|
|
|
10441
10471
|
}
|
|
10442
10472
|
}
|
|
10443
10473
|
async loadCache() {
|
|
10444
|
-
const file =
|
|
10474
|
+
const file = path27.join(this.ctxloomDir, "authors-cache.json");
|
|
10445
10475
|
try {
|
|
10446
|
-
const raw = await
|
|
10476
|
+
const raw = await fs25.readFile(file, "utf8");
|
|
10447
10477
|
this.cache = JSON.parse(raw);
|
|
10448
10478
|
} catch {
|
|
10449
10479
|
}
|
|
@@ -10468,8 +10498,8 @@ async function resolveViaGitHubApi(email, owner, repo, token) {
|
|
|
10468
10498
|
}
|
|
10469
10499
|
|
|
10470
10500
|
// packages/core/src/review/CodeownersWriter.ts
|
|
10471
|
-
import
|
|
10472
|
-
import
|
|
10501
|
+
import fs26 from "fs/promises";
|
|
10502
|
+
import path28 from "path";
|
|
10473
10503
|
var MARKER_START = "# <ctxloom:start> \u2014 managed by ctxloom review-suggest; do not edit between markers";
|
|
10474
10504
|
var MARKER_START_DETECT = "# <ctxloom:start>";
|
|
10475
10505
|
var MARKER_END = "# <ctxloom:end>";
|
|
@@ -10501,15 +10531,15 @@ ${block}
|
|
|
10501
10531
|
async function generateCODEOWNERS(codeownersPath, rules) {
|
|
10502
10532
|
let existing = "";
|
|
10503
10533
|
try {
|
|
10504
|
-
existing = await
|
|
10534
|
+
existing = await fs26.readFile(codeownersPath, "utf8");
|
|
10505
10535
|
} catch {
|
|
10506
10536
|
}
|
|
10507
10537
|
const block = buildCodeownersBlock(rules);
|
|
10508
10538
|
return mergeIntoFile(existing, block);
|
|
10509
10539
|
}
|
|
10510
10540
|
async function writeCODEOWNERS(codeownersPath, content) {
|
|
10511
|
-
await
|
|
10512
|
-
await
|
|
10541
|
+
await fs26.mkdir(path28.dirname(codeownersPath), { recursive: true });
|
|
10542
|
+
await fs26.writeFile(codeownersPath, content, "utf8");
|
|
10513
10543
|
}
|
|
10514
10544
|
|
|
10515
10545
|
// packages/core/src/review/types.ts
|
|
@@ -10694,8 +10724,8 @@ function matchGlob(pattern, value) {
|
|
|
10694
10724
|
}
|
|
10695
10725
|
|
|
10696
10726
|
// packages/core/src/review/loadConfig.ts
|
|
10697
|
-
import
|
|
10698
|
-
import
|
|
10727
|
+
import fs27 from "fs/promises";
|
|
10728
|
+
import path29 from "path";
|
|
10699
10729
|
import yaml3 from "js-yaml";
|
|
10700
10730
|
function freshDefaults() {
|
|
10701
10731
|
return {
|
|
@@ -10706,9 +10736,9 @@ function freshDefaults() {
|
|
|
10706
10736
|
};
|
|
10707
10737
|
}
|
|
10708
10738
|
async function loadReviewConfig(root) {
|
|
10709
|
-
const file =
|
|
10739
|
+
const file = path29.join(root, ".ctxloom", "review.yml");
|
|
10710
10740
|
try {
|
|
10711
|
-
const raw = await
|
|
10741
|
+
const raw = await fs27.readFile(file, "utf8");
|
|
10712
10742
|
const parsed = yaml3.load(raw);
|
|
10713
10743
|
if (!parsed) return freshDefaults();
|
|
10714
10744
|
return {
|
|
@@ -10723,13 +10753,13 @@ async function loadReviewConfig(root) {
|
|
|
10723
10753
|
}
|
|
10724
10754
|
|
|
10725
10755
|
// packages/core/src/security/PathValidator.ts
|
|
10726
|
-
import
|
|
10727
|
-
import
|
|
10756
|
+
import path30 from "path";
|
|
10757
|
+
import fs28 from "fs";
|
|
10728
10758
|
var MAX_FILE_SIZE = 5 * 1024 * 1024;
|
|
10729
10759
|
var PathValidator = class {
|
|
10730
10760
|
canonicalRoot;
|
|
10731
10761
|
constructor(projectRoot) {
|
|
10732
|
-
this.canonicalRoot =
|
|
10762
|
+
this.canonicalRoot = fs28.realpathSync(path30.resolve(projectRoot));
|
|
10733
10763
|
}
|
|
10734
10764
|
/**
|
|
10735
10765
|
* Validates that the given input path resolves within the project root.
|
|
@@ -10739,14 +10769,14 @@ var PathValidator = class {
|
|
|
10739
10769
|
* @throws Error if the path escapes the project root
|
|
10740
10770
|
*/
|
|
10741
10771
|
validate(inputPath) {
|
|
10742
|
-
const resolved =
|
|
10772
|
+
const resolved = path30.resolve(this.canonicalRoot, inputPath);
|
|
10743
10773
|
let canonical;
|
|
10744
10774
|
try {
|
|
10745
|
-
canonical =
|
|
10775
|
+
canonical = fs28.realpathSync(resolved);
|
|
10746
10776
|
} catch {
|
|
10747
10777
|
canonical = resolved;
|
|
10748
10778
|
}
|
|
10749
|
-
if (!canonical.startsWith(this.canonicalRoot +
|
|
10779
|
+
if (!canonical.startsWith(this.canonicalRoot + path30.sep) && canonical !== this.canonicalRoot) {
|
|
10750
10780
|
throw new Error(
|
|
10751
10781
|
`Path traversal blocked: "${inputPath}" resolves outside of the project root`
|
|
10752
10782
|
);
|
|
@@ -10763,7 +10793,7 @@ var PathValidator = class {
|
|
|
10763
10793
|
* Converts an absolute path to a relative path from the project root.
|
|
10764
10794
|
*/
|
|
10765
10795
|
toRelative(absolutePath) {
|
|
10766
|
-
return
|
|
10796
|
+
return path30.relative(this.canonicalRoot, absolutePath);
|
|
10767
10797
|
}
|
|
10768
10798
|
/**
|
|
10769
10799
|
* Validates and reads a file, returning its content.
|
|
@@ -10771,11 +10801,11 @@ var PathValidator = class {
|
|
|
10771
10801
|
*/
|
|
10772
10802
|
readFile(inputPath) {
|
|
10773
10803
|
const absPath = this.validate(inputPath);
|
|
10774
|
-
const stat =
|
|
10804
|
+
const stat = fs28.statSync(absPath);
|
|
10775
10805
|
if (stat.size > MAX_FILE_SIZE) {
|
|
10776
10806
|
throw new Error(`File too large: ${inputPath} (${Math.round(stat.size / 1024)}KB, max ${MAX_FILE_SIZE / 1024 / 1024}MB)`);
|
|
10777
10807
|
}
|
|
10778
|
-
return
|
|
10808
|
+
return fs28.readFileSync(absPath, "utf-8");
|
|
10779
10809
|
}
|
|
10780
10810
|
/**
|
|
10781
10811
|
* Checks if a path exists and is within the project root.
|
|
@@ -10926,7 +10956,7 @@ var EmailAlreadyUsedError = class extends Error {
|
|
|
10926
10956
|
|
|
10927
10957
|
// packages/core/src/license/LicenseStore.ts
|
|
10928
10958
|
import { readFileSync, writeFileSync, unlinkSync, mkdirSync, chmodSync, existsSync } from "fs";
|
|
10929
|
-
import
|
|
10959
|
+
import path31 from "path";
|
|
10930
10960
|
|
|
10931
10961
|
// packages/core/src/license/types.ts
|
|
10932
10962
|
import { z as z37 } from "zod";
|
|
@@ -10947,7 +10977,7 @@ var LicenseFileSchema = z37.object({
|
|
|
10947
10977
|
|
|
10948
10978
|
// packages/core/src/license/LicenseStore.ts
|
|
10949
10979
|
function licenseFilePath(home) {
|
|
10950
|
-
return
|
|
10980
|
+
return path31.join(home, ".ctxloom", "license.json");
|
|
10951
10981
|
}
|
|
10952
10982
|
var LicenseStore = class {
|
|
10953
10983
|
filePath;
|
|
@@ -10970,7 +11000,7 @@ var LicenseStore = class {
|
|
|
10970
11000
|
}
|
|
10971
11001
|
}
|
|
10972
11002
|
async write(license) {
|
|
10973
|
-
mkdirSync(
|
|
11003
|
+
mkdirSync(path31.dirname(this.filePath), { recursive: true });
|
|
10974
11004
|
writeFileSync(this.filePath, JSON.stringify(license, null, 2), {
|
|
10975
11005
|
encoding: "utf8",
|
|
10976
11006
|
mode: 384
|
|
@@ -11101,11 +11131,11 @@ import os5 from "os";
|
|
|
11101
11131
|
|
|
11102
11132
|
// packages/core/src/license/DistinctIdStore.ts
|
|
11103
11133
|
import { readFileSync as readFileSync3, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2, existsSync as existsSync2 } from "fs";
|
|
11104
|
-
import
|
|
11134
|
+
import path32 from "path";
|
|
11105
11135
|
import os3 from "os";
|
|
11106
11136
|
var UUID_V4_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
11107
11137
|
function distinctIdPath(home) {
|
|
11108
|
-
return
|
|
11138
|
+
return path32.join(home ?? os3.homedir(), ".ctxloom", "distinct_id");
|
|
11109
11139
|
}
|
|
11110
11140
|
function isValidV4(id) {
|
|
11111
11141
|
return typeof id === "string" && UUID_V4_REGEX.test(id);
|
|
@@ -11126,7 +11156,7 @@ function getOrCreateDistinctId(home) {
|
|
|
11126
11156
|
id: crypto.randomUUID(),
|
|
11127
11157
|
alias_pending: os3.hostname()
|
|
11128
11158
|
};
|
|
11129
|
-
mkdirSync2(
|
|
11159
|
+
mkdirSync2(path32.dirname(filePath), { recursive: true });
|
|
11130
11160
|
writeFileSync2(filePath, JSON.stringify(record), { mode: 384 });
|
|
11131
11161
|
return record;
|
|
11132
11162
|
}
|
|
@@ -11155,7 +11185,7 @@ var TELEMETRY_DISABLED = TELEMETRY_LEVEL === "off";
|
|
|
11155
11185
|
function getTelemetryLevel() {
|
|
11156
11186
|
return TELEMETRY_LEVEL;
|
|
11157
11187
|
}
|
|
11158
|
-
var CTXLOOM_VERSION2 = "1.7.
|
|
11188
|
+
var CTXLOOM_VERSION2 = "1.7.10".length > 0 ? "1.7.10" : "dev";
|
|
11159
11189
|
var POSTHOG_HOST = "https://eu.i.posthog.com";
|
|
11160
11190
|
var POSTHOG_KEY = process.env["POSTHOG_API_KEY"] ?? (true ? "phc_CiDkmFLcZ2K6uCpcoSUQLmFrnnUvsyXGhSxopX5TVKE6" : "");
|
|
11161
11191
|
var SENTRY_DSN = process.env["SENTRY_DSN"] ?? (true ? "https://81c94a0f04a8e242dee493ac1e17f733@o4508531702497280.ingest.de.sentry.io/4511256875368528" : "");
|
|
@@ -11288,17 +11318,17 @@ function parseStack(stack) {
|
|
|
11288
11318
|
|
|
11289
11319
|
// packages/core/src/license/FunnelMilestones.ts
|
|
11290
11320
|
import { existsSync as existsSync3, mkdirSync as mkdirSync3, writeFileSync as writeFileSync3 } from "fs";
|
|
11291
|
-
import
|
|
11321
|
+
import path33 from "path";
|
|
11292
11322
|
import os4 from "os";
|
|
11293
11323
|
var INSTALL_MARKER = "installed_at";
|
|
11294
11324
|
var FIRST_REVIEW_MARKER = "first_review_at";
|
|
11295
11325
|
function writeMarker(filePath) {
|
|
11296
|
-
mkdirSync3(
|
|
11326
|
+
mkdirSync3(path33.dirname(filePath), { recursive: true });
|
|
11297
11327
|
writeFileSync3(filePath, (/* @__PURE__ */ new Date()).toISOString(), { mode: 384 });
|
|
11298
11328
|
}
|
|
11299
11329
|
function shouldEmitInstallCompleted(home) {
|
|
11300
11330
|
const root = home ?? os4.homedir();
|
|
11301
|
-
const marker =
|
|
11331
|
+
const marker = path33.join(root, ".ctxloom", INSTALL_MARKER);
|
|
11302
11332
|
if (existsSync3(marker)) return false;
|
|
11303
11333
|
try {
|
|
11304
11334
|
writeMarker(marker);
|
|
@@ -11307,7 +11337,7 @@ function shouldEmitInstallCompleted(home) {
|
|
|
11307
11337
|
return true;
|
|
11308
11338
|
}
|
|
11309
11339
|
function shouldEmitFirstReviewRun(projectRoot) {
|
|
11310
|
-
const marker =
|
|
11340
|
+
const marker = path33.join(projectRoot, ".ctxloom", FIRST_REVIEW_MARKER);
|
|
11311
11341
|
if (existsSync3(marker)) return false;
|
|
11312
11342
|
try {
|
|
11313
11343
|
writeMarker(marker);
|
|
@@ -11425,16 +11455,16 @@ async function startTrial(email, opts = {}) {
|
|
|
11425
11455
|
|
|
11426
11456
|
// packages/core/src/license/TelemetryNotice.ts
|
|
11427
11457
|
import { existsSync as existsSync4, mkdirSync as mkdirSync4, writeFileSync as writeFileSync4 } from "fs";
|
|
11428
|
-
import
|
|
11458
|
+
import path34 from "path";
|
|
11429
11459
|
import os6 from "os";
|
|
11430
11460
|
function noticePath(home) {
|
|
11431
|
-
return
|
|
11461
|
+
return path34.join(home ?? os6.homedir(), ".ctxloom", "telemetry_notice_shown");
|
|
11432
11462
|
}
|
|
11433
11463
|
function shouldShowTelemetryNotice(home) {
|
|
11434
11464
|
const filePath = noticePath(home);
|
|
11435
11465
|
if (existsSync4(filePath)) return false;
|
|
11436
11466
|
try {
|
|
11437
|
-
mkdirSync4(
|
|
11467
|
+
mkdirSync4(path34.dirname(filePath), { recursive: true });
|
|
11438
11468
|
writeFileSync4(filePath, (/* @__PURE__ */ new Date()).toISOString(), { mode: 384 });
|
|
11439
11469
|
} catch {
|
|
11440
11470
|
}
|
|
@@ -11442,13 +11472,13 @@ function shouldShowTelemetryNotice(home) {
|
|
|
11442
11472
|
}
|
|
11443
11473
|
|
|
11444
11474
|
// packages/core/src/server/ProjectState.ts
|
|
11445
|
-
import
|
|
11475
|
+
import path36 from "path";
|
|
11446
11476
|
|
|
11447
11477
|
// packages/core/src/server/projectId.ts
|
|
11448
11478
|
import crypto5 from "crypto";
|
|
11449
|
-
import
|
|
11479
|
+
import path35 from "path";
|
|
11450
11480
|
function hashProjectRoot(absPath) {
|
|
11451
|
-
const canonical =
|
|
11481
|
+
const canonical = path35.resolve(absPath);
|
|
11452
11482
|
return crypto5.createHash("sha256").update(canonical).digest("hex").slice(0, 16);
|
|
11453
11483
|
}
|
|
11454
11484
|
|
|
@@ -11456,7 +11486,7 @@ function hashProjectRoot(absPath) {
|
|
|
11456
11486
|
function createProjectState(projectRoot, opts = {}) {
|
|
11457
11487
|
return {
|
|
11458
11488
|
projectRoot,
|
|
11459
|
-
dbPath:
|
|
11489
|
+
dbPath: path36.join(projectRoot, ".ctxloom", "vectors.lancedb"),
|
|
11460
11490
|
pinned: opts.pinned ?? false,
|
|
11461
11491
|
lastTouchedAt: Date.now(),
|
|
11462
11492
|
vectorsInitialized: false,
|
|
@@ -11467,6 +11497,7 @@ function createProjectState(projectRoot, opts = {}) {
|
|
|
11467
11497
|
skeletonizerPromise: null,
|
|
11468
11498
|
ruleManager: null,
|
|
11469
11499
|
overlay: null,
|
|
11500
|
+
overlayPromise: null,
|
|
11470
11501
|
watcher: null,
|
|
11471
11502
|
pathValidator: null
|
|
11472
11503
|
};
|
|
@@ -11507,6 +11538,7 @@ async function disposeProjectState(state) {
|
|
|
11507
11538
|
state.skeletonizerPromise = null;
|
|
11508
11539
|
state.ruleManager = null;
|
|
11509
11540
|
state.overlay = null;
|
|
11541
|
+
state.overlayPromise = null;
|
|
11510
11542
|
state.pathValidator = null;
|
|
11511
11543
|
state.graphInitialized = false;
|
|
11512
11544
|
state.vectorsInitialized = false;
|
|
@@ -11613,8 +11645,8 @@ var ProjectStateManager = class {
|
|
|
11613
11645
|
};
|
|
11614
11646
|
|
|
11615
11647
|
// packages/core/src/server/resolveProjectRoot.ts
|
|
11616
|
-
import
|
|
11617
|
-
import
|
|
11648
|
+
import fs29 from "fs";
|
|
11649
|
+
import path37 from "path";
|
|
11618
11650
|
var PATH_SEPARATOR_PATTERN = /[/\\~]|^[A-Za-z]:/;
|
|
11619
11651
|
function looksLikePath(value) {
|
|
11620
11652
|
return PATH_SEPARATOR_PATTERN.test(value);
|
|
@@ -11639,13 +11671,13 @@ function resolvePathSafely(p, cwd) {
|
|
|
11639
11671
|
let expanded = p;
|
|
11640
11672
|
if (p === "~" || p.startsWith("~/")) {
|
|
11641
11673
|
const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
|
|
11642
|
-
expanded = p === "~" ? home :
|
|
11674
|
+
expanded = p === "~" ? home : path37.join(home, p.slice(2));
|
|
11643
11675
|
}
|
|
11644
|
-
return
|
|
11676
|
+
return path37.isAbsolute(expanded) ? path37.resolve(expanded) : path37.resolve(cwd, expanded);
|
|
11645
11677
|
}
|
|
11646
11678
|
function realpathOrSame(p) {
|
|
11647
11679
|
try {
|
|
11648
|
-
return
|
|
11680
|
+
return fs29.realpathSync(p);
|
|
11649
11681
|
} catch {
|
|
11650
11682
|
return p;
|
|
11651
11683
|
}
|
|
@@ -11670,7 +11702,7 @@ function resolveProjectRoot(input) {
|
|
|
11670
11702
|
};
|
|
11671
11703
|
}
|
|
11672
11704
|
const resolved2 = resolvePathSafely(arg, cwd);
|
|
11673
|
-
if (!
|
|
11705
|
+
if (!fs29.existsSync(resolved2)) {
|
|
11674
11706
|
return {
|
|
11675
11707
|
kind: "project_root_not_found",
|
|
11676
11708
|
attemptedPath: resolved2,
|
|
@@ -11681,7 +11713,7 @@ function resolveProjectRoot(input) {
|
|
|
11681
11713
|
}
|
|
11682
11714
|
if (env !== void 0 && env !== "") {
|
|
11683
11715
|
const resolved2 = resolvePathSafely(env, cwd);
|
|
11684
|
-
if (!
|
|
11716
|
+
if (!fs29.existsSync(resolved2)) {
|
|
11685
11717
|
return {
|
|
11686
11718
|
kind: "project_root_not_found",
|
|
11687
11719
|
attemptedPath: resolved2,
|
|
@@ -11708,12 +11740,12 @@ var FILESYSTEM_ROOTS = /* @__PURE__ */ new Set(["/", "C:\\", "D:\\", "E:\\", "F:
|
|
|
11708
11740
|
function validateDefaultRoot(candidate) {
|
|
11709
11741
|
if (FILESYSTEM_ROOTS.has(candidate)) return false;
|
|
11710
11742
|
try {
|
|
11711
|
-
const stat =
|
|
11743
|
+
const stat = fs29.statSync(candidate);
|
|
11712
11744
|
if (!stat.isDirectory()) return false;
|
|
11713
11745
|
} catch {
|
|
11714
11746
|
return false;
|
|
11715
11747
|
}
|
|
11716
|
-
return PROJECT_MARKERS.some((m) =>
|
|
11748
|
+
return PROJECT_MARKERS.some((m) => fs29.existsSync(path37.join(candidate, m)));
|
|
11717
11749
|
}
|
|
11718
11750
|
|
|
11719
11751
|
// packages/core/src/server/structuredErrors.ts
|
|
@@ -11785,8 +11817,8 @@ var EmittedOnceTracker = class {
|
|
|
11785
11817
|
};
|
|
11786
11818
|
|
|
11787
11819
|
// packages/core/src/install/installer.ts
|
|
11788
|
-
import
|
|
11789
|
-
import
|
|
11820
|
+
import fs30 from "fs";
|
|
11821
|
+
import path38 from "path";
|
|
11790
11822
|
|
|
11791
11823
|
// packages/core/src/install/templates.ts
|
|
11792
11824
|
var RULES_BLOCK_NAME = "CTXLOOM-RULES";
|
|
@@ -12491,8 +12523,8 @@ function skillFilePath(name) {
|
|
|
12491
12523
|
// packages/core/src/install/installer.ts
|
|
12492
12524
|
function installHarness(opts = {}) {
|
|
12493
12525
|
const cwd = opts.cwd ?? process.cwd();
|
|
12494
|
-
const projectRoot =
|
|
12495
|
-
const stat =
|
|
12526
|
+
const projectRoot = path38.resolve(cwd);
|
|
12527
|
+
const stat = fs30.statSync(projectRoot);
|
|
12496
12528
|
if (!stat.isDirectory()) {
|
|
12497
12529
|
throw new Error(`installHarness: ${projectRoot} is not a directory`);
|
|
12498
12530
|
}
|
|
@@ -12540,20 +12572,20 @@ function resolveExtraHosts(ids, warnings) {
|
|
|
12540
12572
|
return HOST_ADAPTERS.filter((a) => requested.has(a.id));
|
|
12541
12573
|
}
|
|
12542
12574
|
function safeJoin(root, name) {
|
|
12543
|
-
const target =
|
|
12544
|
-
const rootResolved =
|
|
12575
|
+
const target = path38.resolve(root, name);
|
|
12576
|
+
const rootResolved = path38.resolve(root);
|
|
12545
12577
|
const caseFold = process.platform === "darwin" || process.platform === "win32";
|
|
12546
12578
|
const t = caseFold ? target.toLowerCase() : target;
|
|
12547
12579
|
const r = caseFold ? rootResolved.toLowerCase() : rootResolved;
|
|
12548
|
-
if (!t.startsWith(r +
|
|
12580
|
+
if (!t.startsWith(r + path38.sep) && t !== r) {
|
|
12549
12581
|
throw new Error(`installHarness: refusing to write outside project root: ${target}`);
|
|
12550
12582
|
}
|
|
12551
12583
|
return target;
|
|
12552
12584
|
}
|
|
12553
12585
|
function writeRulesBlock(projectRoot, filename, opts) {
|
|
12554
12586
|
const filePath = safeJoin(projectRoot, filename);
|
|
12555
|
-
const existed =
|
|
12556
|
-
const existing = existed ?
|
|
12587
|
+
const existed = fs30.existsSync(filePath);
|
|
12588
|
+
const existing = existed ? fs30.readFileSync(filePath, "utf-8") : "";
|
|
12557
12589
|
if (existed) {
|
|
12558
12590
|
const block = extractBlock(existing, RULES_BLOCK_NAME);
|
|
12559
12591
|
if (block) {
|
|
@@ -12571,7 +12603,7 @@ function writeRulesBlock(projectRoot, filename, opts) {
|
|
|
12571
12603
|
}
|
|
12572
12604
|
const next = upsertBlock(existing, RULES_BLOCK_NAME, RULES_BLOCK_CONTENT);
|
|
12573
12605
|
if (!opts.dryRun) {
|
|
12574
|
-
|
|
12606
|
+
fs30.writeFileSync(filePath, next, "utf-8");
|
|
12575
12607
|
}
|
|
12576
12608
|
return {
|
|
12577
12609
|
path: filePath,
|
|
@@ -12584,15 +12616,15 @@ function writeRulesBlock(projectRoot, filename, opts) {
|
|
|
12584
12616
|
function writeHooksJson(projectRoot, opts) {
|
|
12585
12617
|
const dir = safeJoin(projectRoot, ".claude");
|
|
12586
12618
|
const filePath = safeJoin(projectRoot, ".claude/hooks.json");
|
|
12587
|
-
const existed =
|
|
12619
|
+
const existed = fs30.existsSync(filePath);
|
|
12588
12620
|
let current = {};
|
|
12589
12621
|
if (existed) {
|
|
12590
12622
|
try {
|
|
12591
|
-
const text =
|
|
12623
|
+
const text = fs30.readFileSync(filePath, "utf-8");
|
|
12592
12624
|
current = JSON.parse(text);
|
|
12593
12625
|
} catch (err) {
|
|
12594
12626
|
opts.warnings.push(
|
|
12595
|
-
`Could not parse existing ${
|
|
12627
|
+
`Could not parse existing ${path38.relative(projectRoot, filePath)}; treating as empty. (${err instanceof Error ? err.message : String(err)})`
|
|
12596
12628
|
);
|
|
12597
12629
|
current = {};
|
|
12598
12630
|
}
|
|
@@ -12609,12 +12641,12 @@ function writeHooksJson(projectRoot, opts) {
|
|
|
12609
12641
|
const nextJson = JSON.stringify(merged, null, 2) + "\n";
|
|
12610
12642
|
let alreadyCorrect = false;
|
|
12611
12643
|
if (existed) {
|
|
12612
|
-
const currentText =
|
|
12644
|
+
const currentText = fs30.readFileSync(filePath, "utf-8");
|
|
12613
12645
|
if (currentText === nextJson) alreadyCorrect = true;
|
|
12614
12646
|
}
|
|
12615
12647
|
if (!opts.dryRun && !alreadyCorrect) {
|
|
12616
|
-
|
|
12617
|
-
|
|
12648
|
+
fs30.mkdirSync(dir, { recursive: true });
|
|
12649
|
+
fs30.writeFileSync(filePath, nextJson, "utf-8");
|
|
12618
12650
|
}
|
|
12619
12651
|
return {
|
|
12620
12652
|
path: filePath,
|
|
@@ -12636,19 +12668,19 @@ function isCtxloomEntry(entry, expectedMatcher) {
|
|
|
12636
12668
|
}
|
|
12637
12669
|
function writeHostAdapter(projectRoot, adapter, opts) {
|
|
12638
12670
|
const filePath = safeJoin(projectRoot, adapter.path);
|
|
12639
|
-
const dir =
|
|
12640
|
-
const existed =
|
|
12671
|
+
const dir = path38.dirname(filePath);
|
|
12672
|
+
const existed = fs30.existsSync(filePath);
|
|
12641
12673
|
const rendered = adapter.render();
|
|
12642
12674
|
let alreadyCorrect = false;
|
|
12643
12675
|
if (existed) {
|
|
12644
|
-
const current =
|
|
12676
|
+
const current = fs30.readFileSync(filePath, "utf-8");
|
|
12645
12677
|
if (adapter.isCanonical(current)) {
|
|
12646
12678
|
alreadyCorrect = true;
|
|
12647
12679
|
}
|
|
12648
12680
|
}
|
|
12649
12681
|
if (!opts.dryRun && !alreadyCorrect) {
|
|
12650
|
-
|
|
12651
|
-
|
|
12682
|
+
fs30.mkdirSync(dir, { recursive: true });
|
|
12683
|
+
fs30.writeFileSync(filePath, rendered, "utf-8");
|
|
12652
12684
|
}
|
|
12653
12685
|
return {
|
|
12654
12686
|
path: filePath,
|
|
@@ -12661,16 +12693,16 @@ function writeHostAdapter(projectRoot, adapter, opts) {
|
|
|
12661
12693
|
function writeSkill(projectRoot, skill, opts) {
|
|
12662
12694
|
const dir = safeJoin(projectRoot, `.claude/skills/${skill.name}`);
|
|
12663
12695
|
const filePath = safeJoin(projectRoot, skillFilePath(skill.name));
|
|
12664
|
-
const existed =
|
|
12696
|
+
const existed = fs30.existsSync(filePath);
|
|
12665
12697
|
let alreadyCorrect = false;
|
|
12666
12698
|
if (existed) {
|
|
12667
|
-
if (
|
|
12699
|
+
if (fs30.readFileSync(filePath, "utf-8") === skill.content) {
|
|
12668
12700
|
alreadyCorrect = true;
|
|
12669
12701
|
}
|
|
12670
12702
|
}
|
|
12671
12703
|
if (!opts.dryRun && !alreadyCorrect) {
|
|
12672
|
-
|
|
12673
|
-
|
|
12704
|
+
fs30.mkdirSync(dir, { recursive: true });
|
|
12705
|
+
fs30.writeFileSync(filePath, skill.content, "utf-8");
|
|
12674
12706
|
}
|
|
12675
12707
|
return {
|
|
12676
12708
|
path: filePath,
|
|
@@ -12683,17 +12715,17 @@ function writeSkill(projectRoot, skill, opts) {
|
|
|
12683
12715
|
function writeSessionStartScript(projectRoot, opts) {
|
|
12684
12716
|
const dir = safeJoin(projectRoot, ".claude/hooks");
|
|
12685
12717
|
const filePath = safeJoin(projectRoot, ".claude/hooks/session-start.sh");
|
|
12686
|
-
const existed =
|
|
12718
|
+
const existed = fs30.existsSync(filePath);
|
|
12687
12719
|
let alreadyCorrect = false;
|
|
12688
12720
|
if (existed) {
|
|
12689
|
-
const current =
|
|
12721
|
+
const current = fs30.readFileSync(filePath, "utf-8");
|
|
12690
12722
|
if (current === SESSION_START_FULL) alreadyCorrect = true;
|
|
12691
12723
|
}
|
|
12692
12724
|
if (!opts.dryRun && !alreadyCorrect) {
|
|
12693
|
-
|
|
12694
|
-
|
|
12725
|
+
fs30.mkdirSync(dir, { recursive: true });
|
|
12726
|
+
fs30.writeFileSync(filePath, SESSION_START_FULL, "utf-8");
|
|
12695
12727
|
try {
|
|
12696
|
-
|
|
12728
|
+
fs30.chmodSync(filePath, 493);
|
|
12697
12729
|
} catch {
|
|
12698
12730
|
}
|
|
12699
12731
|
}
|
|
@@ -12841,4 +12873,4 @@ export {
|
|
|
12841
12873
|
skillFilePath,
|
|
12842
12874
|
installHarness
|
|
12843
12875
|
};
|
|
12844
|
-
//# sourceMappingURL=chunk-
|
|
12876
|
+
//# sourceMappingURL=chunk-J3NVYR6J.js.map
|