@yawlabs/ctxlint 0.13.1 → 0.13.2
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/.pre-commit-hooks.yaml +1 -1
- package/dist/index.js +145 -81
- package/package.json +1 -1
package/.pre-commit-hooks.yaml
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
# Version-pinned so a checkout at `rev: vX.Y.Z` runs exactly that release
|
|
5
5
|
# of ctxlint — matches the pinning done by `ctxlint init`. release.sh keeps
|
|
6
6
|
# this in sync with package.json on each bump.
|
|
7
|
-
entry: npx @yawlabs/ctxlint@0.13.
|
|
7
|
+
entry: npx @yawlabs/ctxlint@0.13.2 --strict
|
|
8
8
|
language: node
|
|
9
9
|
always_run: true
|
|
10
10
|
pass_filenames: false
|
package/dist/index.js
CHANGED
|
@@ -22404,7 +22404,7 @@ var init_protocol = __esm({
|
|
|
22404
22404
|
return;
|
|
22405
22405
|
}
|
|
22406
22406
|
const pollInterval = task2.pollInterval ?? this._options?.defaultTaskPollInterval ?? 1e3;
|
|
22407
|
-
await new Promise((
|
|
22407
|
+
await new Promise((resolve15) => setTimeout(resolve15, pollInterval));
|
|
22408
22408
|
options?.signal?.throwIfAborted();
|
|
22409
22409
|
}
|
|
22410
22410
|
} catch (error49) {
|
|
@@ -22421,7 +22421,7 @@ var init_protocol = __esm({
|
|
|
22421
22421
|
*/
|
|
22422
22422
|
request(request, resultSchema, options) {
|
|
22423
22423
|
const { relatedRequestId, resumptionToken, onresumptiontoken, task, relatedTask } = options ?? {};
|
|
22424
|
-
return new Promise((
|
|
22424
|
+
return new Promise((resolve15, reject) => {
|
|
22425
22425
|
const earlyReject = (error49) => {
|
|
22426
22426
|
reject(error49);
|
|
22427
22427
|
};
|
|
@@ -22499,7 +22499,7 @@ var init_protocol = __esm({
|
|
|
22499
22499
|
if (!parseResult.success) {
|
|
22500
22500
|
reject(parseResult.error);
|
|
22501
22501
|
} else {
|
|
22502
|
-
|
|
22502
|
+
resolve15(parseResult.data);
|
|
22503
22503
|
}
|
|
22504
22504
|
} catch (error49) {
|
|
22505
22505
|
reject(error49);
|
|
@@ -22760,12 +22760,12 @@ var init_protocol = __esm({
|
|
|
22760
22760
|
}
|
|
22761
22761
|
} catch {
|
|
22762
22762
|
}
|
|
22763
|
-
return new Promise((
|
|
22763
|
+
return new Promise((resolve15, reject) => {
|
|
22764
22764
|
if (signal.aborted) {
|
|
22765
22765
|
reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
|
|
22766
22766
|
return;
|
|
22767
22767
|
}
|
|
22768
|
-
const timeoutId = setTimeout(
|
|
22768
|
+
const timeoutId = setTimeout(resolve15, interval);
|
|
22769
22769
|
signal.addEventListener("abort", () => {
|
|
22770
22770
|
clearTimeout(timeoutId);
|
|
22771
22771
|
reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
|
|
@@ -25813,7 +25813,7 @@ var require_compile = __commonJS({
|
|
|
25813
25813
|
const schOrFunc = root.refs[ref];
|
|
25814
25814
|
if (schOrFunc)
|
|
25815
25815
|
return schOrFunc;
|
|
25816
|
-
let _sch =
|
|
25816
|
+
let _sch = resolve15.call(this, root, ref);
|
|
25817
25817
|
if (_sch === void 0) {
|
|
25818
25818
|
const schema = (_a3 = root.localRefs) === null || _a3 === void 0 ? void 0 : _a3[ref];
|
|
25819
25819
|
const { schemaId } = this.opts;
|
|
@@ -25840,7 +25840,7 @@ var require_compile = __commonJS({
|
|
|
25840
25840
|
function sameSchemaEnv(s1, s2) {
|
|
25841
25841
|
return s1.schema === s2.schema && s1.root === s2.root && s1.baseId === s2.baseId;
|
|
25842
25842
|
}
|
|
25843
|
-
function
|
|
25843
|
+
function resolve15(root, ref) {
|
|
25844
25844
|
let sch;
|
|
25845
25845
|
while (typeof (sch = this.refs[ref]) == "string")
|
|
25846
25846
|
ref = sch;
|
|
@@ -26418,7 +26418,7 @@ var require_fast_uri = __commonJS({
|
|
|
26418
26418
|
}
|
|
26419
26419
|
return uri;
|
|
26420
26420
|
}
|
|
26421
|
-
function
|
|
26421
|
+
function resolve15(baseURI, relativeURI, options) {
|
|
26422
26422
|
const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
|
|
26423
26423
|
const resolved = resolveComponent(parse4(baseURI, schemelessOptions), parse4(relativeURI, schemelessOptions), schemelessOptions, true);
|
|
26424
26424
|
schemelessOptions.skipEscape = true;
|
|
@@ -26645,7 +26645,7 @@ var require_fast_uri = __commonJS({
|
|
|
26645
26645
|
var fastUri = {
|
|
26646
26646
|
SCHEMES,
|
|
26647
26647
|
normalize: normalize3,
|
|
26648
|
-
resolve:
|
|
26648
|
+
resolve: resolve15,
|
|
26649
26649
|
resolveComponent,
|
|
26650
26650
|
equal,
|
|
26651
26651
|
serialize,
|
|
@@ -30827,7 +30827,7 @@ var init_mcp = __esm({
|
|
|
30827
30827
|
let task = createTaskResult.task;
|
|
30828
30828
|
const pollInterval = task.pollInterval ?? 5e3;
|
|
30829
30829
|
while (task.status !== "completed" && task.status !== "failed" && task.status !== "cancelled") {
|
|
30830
|
-
await new Promise((
|
|
30830
|
+
await new Promise((resolve15) => setTimeout(resolve15, pollInterval));
|
|
30831
30831
|
const updatedTask = await extra.taskStore.getTask(taskId);
|
|
30832
30832
|
if (!updatedTask) {
|
|
30833
30833
|
throw new McpError(ErrorCode.InternalError, `Task ${taskId} not found during polling`);
|
|
@@ -31423,12 +31423,12 @@ var init_stdio2 = __esm({
|
|
|
31423
31423
|
this.onclose?.();
|
|
31424
31424
|
}
|
|
31425
31425
|
send(message) {
|
|
31426
|
-
return new Promise((
|
|
31426
|
+
return new Promise((resolve15) => {
|
|
31427
31427
|
const json2 = serializeMessage(message);
|
|
31428
31428
|
if (this._stdout.write(json2)) {
|
|
31429
|
-
|
|
31429
|
+
resolve15();
|
|
31430
31430
|
} else {
|
|
31431
|
-
this._stdout.once("drain",
|
|
31431
|
+
this._stdout.once("drain", resolve15);
|
|
31432
31432
|
}
|
|
31433
31433
|
});
|
|
31434
31434
|
}
|
|
@@ -34433,19 +34433,28 @@ function stripBom(content) {
|
|
|
34433
34433
|
return content.charCodeAt(0) === 65279 ? content.slice(1) : content;
|
|
34434
34434
|
}
|
|
34435
34435
|
function loadPackageJson(projectRoot) {
|
|
34436
|
-
if (pkgJsonCache
|
|
34436
|
+
if (pkgJsonCache.has(projectRoot)) {
|
|
34437
|
+
const cached2 = pkgJsonCache.get(projectRoot) ?? null;
|
|
34438
|
+
pkgJsonCache.delete(projectRoot);
|
|
34439
|
+
pkgJsonCache.set(projectRoot, cached2);
|
|
34440
|
+
return cached2;
|
|
34441
|
+
}
|
|
34442
|
+
let data;
|
|
34437
34443
|
try {
|
|
34438
34444
|
const content = stripBom(fs2.readFileSync(path.join(projectRoot, "package.json"), "utf-8"));
|
|
34439
|
-
|
|
34440
|
-
pkgJsonCache = { root: projectRoot, data };
|
|
34441
|
-
return data;
|
|
34445
|
+
data = JSON.parse(content);
|
|
34442
34446
|
} catch {
|
|
34443
|
-
|
|
34444
|
-
|
|
34447
|
+
data = null;
|
|
34448
|
+
}
|
|
34449
|
+
pkgJsonCache.set(projectRoot, data);
|
|
34450
|
+
if (pkgJsonCache.size > PKG_CACHE_MAX_ENTRIES) {
|
|
34451
|
+
const oldest = pkgJsonCache.keys().next().value;
|
|
34452
|
+
if (oldest !== void 0) pkgJsonCache.delete(oldest);
|
|
34445
34453
|
}
|
|
34454
|
+
return data;
|
|
34446
34455
|
}
|
|
34447
34456
|
function resetPackageJsonCache() {
|
|
34448
|
-
pkgJsonCache
|
|
34457
|
+
pkgJsonCache.clear();
|
|
34449
34458
|
}
|
|
34450
34459
|
function fileExists(filePath) {
|
|
34451
34460
|
try {
|
|
@@ -34511,12 +34520,13 @@ function getAllProjectFiles(projectRoot) {
|
|
|
34511
34520
|
walk(projectRoot, 0);
|
|
34512
34521
|
return files;
|
|
34513
34522
|
}
|
|
34514
|
-
var pkgJsonCache, IGNORED_DIRS;
|
|
34523
|
+
var PKG_CACHE_MAX_ENTRIES, pkgJsonCache, IGNORED_DIRS;
|
|
34515
34524
|
var init_fs = __esm({
|
|
34516
34525
|
"src/utils/fs.ts"() {
|
|
34517
34526
|
"use strict";
|
|
34518
34527
|
init_define_WEB_FIRST_SEGMENTS();
|
|
34519
|
-
|
|
34528
|
+
PKG_CACHE_MAX_ENTRIES = 256;
|
|
34529
|
+
pkgJsonCache = /* @__PURE__ */ new Map();
|
|
34520
34530
|
IGNORED_DIRS = /* @__PURE__ */ new Set([
|
|
34521
34531
|
"node_modules",
|
|
34522
34532
|
".git",
|
|
@@ -40917,7 +40927,7 @@ function parseRenameLog(result, targetPath) {
|
|
|
40917
40927
|
let basenameFallback = null;
|
|
40918
40928
|
const lines = result.trim().split("\n");
|
|
40919
40929
|
for (const line of lines) {
|
|
40920
|
-
const headerMatch = line.match(/^([a-f0-9]{7,40})\s+(
|
|
40930
|
+
const headerMatch = line.match(/^([a-f0-9]{7,40})\s+(\d{4}-\d\d-\d\d\b.*)$/);
|
|
40921
40931
|
if (headerMatch) {
|
|
40922
40932
|
currentHash = headerMatch[1].substring(0, 7);
|
|
40923
40933
|
currentDateStr = headerMatch[2];
|
|
@@ -42603,10 +42613,26 @@ function extractNpxPackage(cmd) {
|
|
|
42603
42613
|
}
|
|
42604
42614
|
return null;
|
|
42605
42615
|
}
|
|
42616
|
+
function wouldNeedPackageJson(cmd) {
|
|
42617
|
+
return NPM_SCRIPT_PATTERN.test(cmd) || PKG_SHORTHAND_PATTERN.test(cmd) || /^npx\b/.test(cmd) || PKG_DEPENDENT_TOOL_PATTERN.test(cmd);
|
|
42618
|
+
}
|
|
42606
42619
|
async function checkCommands(file2, projectRoot) {
|
|
42607
42620
|
const issues = [];
|
|
42608
42621
|
const pkgJson = loadPackageJson(projectRoot);
|
|
42609
42622
|
const makefile = loadMakefile(projectRoot);
|
|
42623
|
+
if (!pkgJson) {
|
|
42624
|
+
const skipped = file2.references.commands.find((ref) => wouldNeedPackageJson(ref.value));
|
|
42625
|
+
if (skipped) {
|
|
42626
|
+
issues.push({
|
|
42627
|
+
severity: "info",
|
|
42628
|
+
check: "commands",
|
|
42629
|
+
ruleId: "commands/package-json-missing",
|
|
42630
|
+
line: skipped.line,
|
|
42631
|
+
message: "package.json missing or unparseable \u2014 command checks skipped",
|
|
42632
|
+
suggestion: "Add a parseable package.json at the project root so script, npx, and tool references can be validated."
|
|
42633
|
+
});
|
|
42634
|
+
}
|
|
42635
|
+
}
|
|
42610
42636
|
for (const ref of file2.references.commands) {
|
|
42611
42637
|
const cmd = ref.value;
|
|
42612
42638
|
const scriptMatch = cmd.match(NPM_SCRIPT_PATTERN);
|
|
@@ -42625,9 +42651,7 @@ async function checkCommands(file2, projectRoot) {
|
|
|
42625
42651
|
}
|
|
42626
42652
|
continue;
|
|
42627
42653
|
}
|
|
42628
|
-
const shorthandMatch = cmd.match(
|
|
42629
|
-
/^(npm|pnpm|yarn|bun)\s+(test|start|build|dev|lint|format|check|typecheck|clean|serve|preview|e2e)\b/
|
|
42630
|
-
);
|
|
42654
|
+
const shorthandMatch = cmd.match(PKG_SHORTHAND_PATTERN);
|
|
42631
42655
|
if (shorthandMatch && pkgJson) {
|
|
42632
42656
|
const scriptName = shorthandMatch[2];
|
|
42633
42657
|
if (pkgJson.scripts && !(scriptName in pkgJson.scripts)) {
|
|
@@ -42689,7 +42713,7 @@ async function checkCommands(file2, projectRoot) {
|
|
|
42689
42713
|
}
|
|
42690
42714
|
continue;
|
|
42691
42715
|
}
|
|
42692
|
-
const toolMatch = cmd.match(
|
|
42716
|
+
const toolMatch = cmd.match(PKG_DEPENDENT_TOOL_PATTERN);
|
|
42693
42717
|
if (toolMatch && pkgJson) {
|
|
42694
42718
|
const tool = toolMatch[1];
|
|
42695
42719
|
const allDeps = {
|
|
@@ -42727,7 +42751,7 @@ function hasMakeTarget(makefile, target) {
|
|
|
42727
42751
|
const pattern = new RegExp(`^${target.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\s*:`, "m");
|
|
42728
42752
|
return pattern.test(makefile);
|
|
42729
42753
|
}
|
|
42730
|
-
var NPM_SCRIPT_PATTERN, MAKE_PATTERN;
|
|
42754
|
+
var NPM_SCRIPT_PATTERN, MAKE_PATTERN, PKG_DEPENDENT_TOOL_PATTERN, PKG_SHORTHAND_PATTERN;
|
|
42731
42755
|
var init_commands = __esm({
|
|
42732
42756
|
"src/core/checks/commands.ts"() {
|
|
42733
42757
|
"use strict";
|
|
@@ -42735,6 +42759,8 @@ var init_commands = __esm({
|
|
|
42735
42759
|
init_fs();
|
|
42736
42760
|
NPM_SCRIPT_PATTERN = /^(?:npm\s+run|pnpm(?:\s+run)?|yarn(?:\s+run)?|bun(?:\s+run)?)\s+(\S+)/;
|
|
42737
42761
|
MAKE_PATTERN = /^make\s+(\S+)/;
|
|
42762
|
+
PKG_DEPENDENT_TOOL_PATTERN = /^(vitest|jest|pytest|mocha|eslint|prettier|tsc)\b/;
|
|
42763
|
+
PKG_SHORTHAND_PATTERN = /^(npm|pnpm|yarn|bun)\s+(test|start|build|dev|lint|format|check|typecheck|clean|serve|preview|e2e)\b/;
|
|
42738
42764
|
}
|
|
42739
42765
|
});
|
|
42740
42766
|
|
|
@@ -42925,6 +42951,7 @@ function computeSectionCosts(file2) {
|
|
|
42925
42951
|
}).sort((a, b2) => b2.tokens - a.tokens);
|
|
42926
42952
|
}
|
|
42927
42953
|
function loadSettingsSources(projectRoot) {
|
|
42954
|
+
if (settingsCache?.root === projectRoot) return settingsCache.data;
|
|
42928
42955
|
const sources = [];
|
|
42929
42956
|
const candidates = [
|
|
42930
42957
|
path7.join(projectRoot, ".claude", "settings.json"),
|
|
@@ -42944,6 +42971,7 @@ function loadSettingsSources(projectRoot) {
|
|
|
42944
42971
|
console.warn(`ctxlint: could not parse ${p2}: ${err.message}`);
|
|
42945
42972
|
}
|
|
42946
42973
|
}
|
|
42974
|
+
settingsCache = { root: projectRoot, data: sources };
|
|
42947
42975
|
return sources;
|
|
42948
42976
|
}
|
|
42949
42977
|
function canonicalizeCommand(backticked) {
|
|
@@ -43038,7 +43066,7 @@ function checkAggregateTierTokens(files, thresholds = DEFAULT_TOKEN_THRESHOLDS)
|
|
|
43038
43066
|
suggestion: "Consider moving the largest files or their heaviest sections to on-demand tiers (skills, subagents, memory)."
|
|
43039
43067
|
};
|
|
43040
43068
|
}
|
|
43041
|
-
var ALWAYS_LOADED_NAMES, TOP_SECTIONS_TO_REPORT, INVIOLABLE_WITH_COMMAND;
|
|
43069
|
+
var ALWAYS_LOADED_NAMES, TOP_SECTIONS_TO_REPORT, INVIOLABLE_WITH_COMMAND, settingsCache;
|
|
43042
43070
|
var init_tier_tokens = __esm({
|
|
43043
43071
|
"src/core/checks/tier-tokens.ts"() {
|
|
43044
43072
|
"use strict";
|
|
@@ -43068,6 +43096,7 @@ var init_tier_tokens = __esm({
|
|
|
43068
43096
|
];
|
|
43069
43097
|
TOP_SECTIONS_TO_REPORT = 3;
|
|
43070
43098
|
INVIOLABLE_WITH_COMMAND = /\b(NEVER|ALWAYS|DON'?T|DO NOT|MUST NOT)\b[^.!?`]{0,80}`([^`]+)`/i;
|
|
43099
|
+
settingsCache = null;
|
|
43071
43100
|
}
|
|
43072
43101
|
});
|
|
43073
43102
|
|
|
@@ -43164,7 +43193,8 @@ async function checkRedundancy(file2, projectRoot) {
|
|
|
43164
43193
|
ruleId: "redundancy/tech-mention",
|
|
43165
43194
|
line: i2 + 1,
|
|
43166
43195
|
message: `"${mention}" is in package.json ${pkgJson.dependencies?.[pkg] ? "dependencies" : pkgJson.devDependencies?.[pkg] ? "devDependencies" : pkgJson.peerDependencies?.[pkg] ? "peerDependencies" : "optionalDependencies"} \u2014 agent can infer this`,
|
|
43167
|
-
suggestion: `~${wastedTokens} tokens could be saved
|
|
43196
|
+
suggestion: `~${wastedTokens} tokens could be saved`,
|
|
43197
|
+
wastedTokens
|
|
43168
43198
|
});
|
|
43169
43199
|
}
|
|
43170
43200
|
}
|
|
@@ -43178,7 +43208,8 @@ async function checkRedundancy(file2, projectRoot) {
|
|
|
43178
43208
|
);
|
|
43179
43209
|
if (dirMatch) {
|
|
43180
43210
|
const dir = dirMatch[1].replace(/[`"]/g, "");
|
|
43181
|
-
const
|
|
43211
|
+
const baseDir = dir.startsWith("./") || dir.startsWith("../") ? path8.dirname(file2.filePath) : projectRoot;
|
|
43212
|
+
const fullPath = path8.resolve(baseDir, dir);
|
|
43182
43213
|
if (isDirectory(fullPath)) {
|
|
43183
43214
|
issues.push({
|
|
43184
43215
|
severity: "info",
|
|
@@ -43410,19 +43441,23 @@ var init_contradictions = __esm({
|
|
|
43410
43441
|
options: [
|
|
43411
43442
|
{
|
|
43412
43443
|
label: "Jest",
|
|
43413
|
-
|
|
43444
|
+
// The third pattern bounds the gap between `test` and `with <fw>` to a
|
|
43445
|
+
// few intra-clause chars so prose like "run our test suite and then
|
|
43446
|
+
// deploy with jest" no longer matches the framework directive — only
|
|
43447
|
+
// tight phrasings like "test with jest" / "tests run with jest" do.
|
|
43448
|
+
patterns: [/\buse\s+jest\b/i, /\bjest\s+for\s+test/i, /\btest\w*\s+with\s+jest\b/i]
|
|
43414
43449
|
},
|
|
43415
43450
|
{
|
|
43416
43451
|
label: "Vitest",
|
|
43417
|
-
patterns: [/\buse\s+vitest\b/i, /\bvitest\s+for\s+test/i, /\btest
|
|
43452
|
+
patterns: [/\buse\s+vitest\b/i, /\bvitest\s+for\s+test/i, /\btest\w*\s+with\s+vitest\b/i]
|
|
43418
43453
|
},
|
|
43419
43454
|
{
|
|
43420
43455
|
label: "Mocha",
|
|
43421
|
-
patterns: [/\buse\s+mocha\b/i, /\bmocha\s+for\s+test/i, /\btest
|
|
43456
|
+
patterns: [/\buse\s+mocha\b/i, /\bmocha\s+for\s+test/i, /\btest\w*\s+with\s+mocha\b/i]
|
|
43422
43457
|
},
|
|
43423
43458
|
{
|
|
43424
43459
|
label: "pytest",
|
|
43425
|
-
patterns: [/\buse\s+pytest\b/i, /\bpytest\s+for\s+test/i, /\btest
|
|
43460
|
+
patterns: [/\buse\s+pytest\b/i, /\bpytest\s+for\s+test/i, /\btest\w*\s+with\s+pytest\b/i]
|
|
43426
43461
|
},
|
|
43427
43462
|
{
|
|
43428
43463
|
label: "Playwright",
|
|
@@ -43477,19 +43512,11 @@ var init_contradictions = __esm({
|
|
|
43477
43512
|
},
|
|
43478
43513
|
{
|
|
43479
43514
|
label: "2 spaces",
|
|
43480
|
-
patterns: [
|
|
43481
|
-
/\b2[\s-]?space\s+indent/i,
|
|
43482
|
-
/\bindent\s+with\s+2\s+spaces/i,
|
|
43483
|
-
/\b2[\s-]?space\s+tabs?\b/i
|
|
43484
|
-
]
|
|
43515
|
+
patterns: [/\b2[\s-]?space\s+indent/i, /\bindent\s+with\s+2\s+spaces/i]
|
|
43485
43516
|
},
|
|
43486
43517
|
{
|
|
43487
43518
|
label: "4 spaces",
|
|
43488
|
-
patterns: [
|
|
43489
|
-
/\b4[\s-]?space\s+indent/i,
|
|
43490
|
-
/\bindent\s+with\s+4\s+spaces/i,
|
|
43491
|
-
/\b4[\s-]?space\s+tabs?\b/i
|
|
43492
|
-
]
|
|
43519
|
+
patterns: [/\b4[\s-]?space\s+indent/i, /\bindent\s+with\s+4\s+spaces/i]
|
|
43493
43520
|
}
|
|
43494
43521
|
]
|
|
43495
43522
|
},
|
|
@@ -43651,10 +43678,11 @@ function hasUnbalancedBracketsOrQuotes(val) {
|
|
|
43651
43678
|
if (square < 0 || curly < 0) return true;
|
|
43652
43679
|
}
|
|
43653
43680
|
if (square !== 0 || curly !== 0) return true;
|
|
43654
|
-
const
|
|
43655
|
-
|
|
43656
|
-
|
|
43657
|
-
|
|
43681
|
+
const quoteChar = val[0];
|
|
43682
|
+
if (quoteChar === '"' || quoteChar === "'") {
|
|
43683
|
+
const count = (val.match(quoteChar === '"' ? /"/g : /'/g) || []).length;
|
|
43684
|
+
if (count % 2 !== 0) return true;
|
|
43685
|
+
}
|
|
43658
43686
|
return false;
|
|
43659
43687
|
}
|
|
43660
43688
|
async function checkFrontmatter(file2, _projectRoot) {
|
|
@@ -44864,13 +44892,13 @@ var init_gitignore = __esm({
|
|
|
44864
44892
|
// src/core/session-parser.ts
|
|
44865
44893
|
import { readFile } from "node:fs/promises";
|
|
44866
44894
|
import { readFileSync as readFileSync4 } from "node:fs";
|
|
44867
|
-
import { dirname as
|
|
44895
|
+
import { dirname as dirname3, resolve as resolve4 } from "node:path";
|
|
44868
44896
|
import { fileURLToPath } from "node:url";
|
|
44869
44897
|
function loadWebFirstSegments() {
|
|
44870
44898
|
if (typeof define_WEB_FIRST_SEGMENTS_default !== "undefined") {
|
|
44871
44899
|
return new Set(define_WEB_FIRST_SEGMENTS_default);
|
|
44872
44900
|
}
|
|
44873
|
-
const __dir =
|
|
44901
|
+
const __dir = dirname3(fileURLToPath(import.meta.url));
|
|
44874
44902
|
const fixturePath = resolve4(__dir, "../../tests/fixtures/web-first-segments.json");
|
|
44875
44903
|
const raw = JSON.parse(readFileSync4(fixturePath, "utf-8"));
|
|
44876
44904
|
const list = Array.isArray(raw) ? raw : raw.segments;
|
|
@@ -44981,7 +45009,7 @@ var init_session_parser = __esm({
|
|
|
44981
45009
|
import { readdir, stat } from "node:fs/promises";
|
|
44982
45010
|
import { createReadStream } from "node:fs";
|
|
44983
45011
|
import { createInterface } from "node:readline";
|
|
44984
|
-
import { dirname as
|
|
45012
|
+
import { dirname as dirname4, join as join5, resolve as resolve5 } from "node:path";
|
|
44985
45013
|
import { existsSync } from "node:fs";
|
|
44986
45014
|
function detectProviders() {
|
|
44987
45015
|
if (!home) return [];
|
|
@@ -45076,7 +45104,7 @@ function detectAiderInSiblings(siblings) {
|
|
|
45076
45104
|
return null;
|
|
45077
45105
|
}
|
|
45078
45106
|
async function detectSiblings(projectRoot) {
|
|
45079
|
-
const parentDir =
|
|
45107
|
+
const parentDir = dirname4(resolve5(projectRoot));
|
|
45080
45108
|
let entries;
|
|
45081
45109
|
try {
|
|
45082
45110
|
entries = await readdir(parentDir);
|
|
@@ -45193,11 +45221,12 @@ async function checkMissingSecret(ctx) {
|
|
|
45193
45221
|
const issues = [];
|
|
45194
45222
|
const secrets = [];
|
|
45195
45223
|
for (const entry of ctx.history) {
|
|
45196
|
-
const match = entry.display.match(
|
|
45224
|
+
const match = entry.display.match(SECRET_SET_SIMPLE);
|
|
45197
45225
|
if (!match) continue;
|
|
45226
|
+
const repoMatch = entry.display.match(REPO_FLAG_PATTERN);
|
|
45198
45227
|
secrets.push({
|
|
45199
45228
|
name: match[1],
|
|
45200
|
-
repo:
|
|
45229
|
+
repo: repoMatch ? repoMatch[1] : void 0,
|
|
45201
45230
|
project: entry.project
|
|
45202
45231
|
});
|
|
45203
45232
|
}
|
|
@@ -45242,19 +45271,19 @@ async function checkMissingSecret(ctx) {
|
|
|
45242
45271
|
}
|
|
45243
45272
|
return issues;
|
|
45244
45273
|
}
|
|
45245
|
-
var
|
|
45274
|
+
var SECRET_SET_SIMPLE, REPO_FLAG_PATTERN;
|
|
45246
45275
|
var init_missing_secret = __esm({
|
|
45247
45276
|
"src/core/checks/session/missing-secret.ts"() {
|
|
45248
45277
|
"use strict";
|
|
45249
45278
|
init_define_WEB_FIRST_SEGMENTS();
|
|
45250
|
-
SECRET_SET_PATTERN = /gh\s+secret\s+set\s+(\S+)\s+--repo\s+(\S+)/;
|
|
45251
45279
|
SECRET_SET_SIMPLE = /gh\s+secret\s+set\s+(\S+)/;
|
|
45280
|
+
REPO_FLAG_PATTERN = /--repo\s+(\S+)/;
|
|
45252
45281
|
}
|
|
45253
45282
|
});
|
|
45254
45283
|
|
|
45255
45284
|
// src/core/checks/session/diverged-file.ts
|
|
45256
45285
|
import { readFile as readFile2, stat as stat2 } from "node:fs/promises";
|
|
45257
|
-
import { join as join6 } from "node:path";
|
|
45286
|
+
import { join as join6, resolve as resolve7 } from "node:path";
|
|
45258
45287
|
import { existsSync as existsSync2 } from "node:fs";
|
|
45259
45288
|
async function loadLineSet(absPath) {
|
|
45260
45289
|
let mtimeMs;
|
|
@@ -45303,11 +45332,16 @@ async function checkDivergedFile(ctx) {
|
|
|
45303
45332
|
bothEmptyIsIdentical: true
|
|
45304
45333
|
});
|
|
45305
45334
|
if (overlap >= 0.2 && overlap < 0.9) {
|
|
45306
|
-
diverged.push({
|
|
45335
|
+
diverged.push({
|
|
45336
|
+
sibling: sib.name,
|
|
45337
|
+
overlap: Math.round(overlap * 100),
|
|
45338
|
+
path: resolve7(sibPath)
|
|
45339
|
+
});
|
|
45307
45340
|
}
|
|
45308
45341
|
}
|
|
45309
45342
|
if (diverged.length > 0) {
|
|
45310
45343
|
const details = diverged.map((d) => `${d.sibling} (${d.overlap}% overlap)`).join(", ");
|
|
45344
|
+
const sibPaths = diverged.map((d) => ` ${d.sibling}: ${d.path}`).join("\n");
|
|
45311
45345
|
issues.push({
|
|
45312
45346
|
severity: "warning",
|
|
45313
45347
|
check: "session-diverged-file",
|
|
@@ -45315,7 +45349,9 @@ async function checkDivergedFile(ctx) {
|
|
|
45315
45349
|
line: 0,
|
|
45316
45350
|
message: `${fileName} has diverged from sibling repos: ${details}`,
|
|
45317
45351
|
suggestion: `Compare with sibling versions to identify unintentional drift`,
|
|
45318
|
-
detail: `
|
|
45352
|
+
detail: `File: ${resolve7(currentPath)}
|
|
45353
|
+
Diverged siblings:
|
|
45354
|
+
${sibPaths}`
|
|
45319
45355
|
});
|
|
45320
45356
|
}
|
|
45321
45357
|
}
|
|
@@ -45402,15 +45438,15 @@ var init_missing_workflow = __esm({
|
|
|
45402
45438
|
|
|
45403
45439
|
// src/core/checks/session/stale-memory.ts
|
|
45404
45440
|
import { existsSync as existsSync4 } from "node:fs";
|
|
45405
|
-
import { resolve as
|
|
45441
|
+
import { resolve as resolve8, isAbsolute as isAbsolute2 } from "node:path";
|
|
45406
45442
|
function resolveRef2(ref, projectRoot) {
|
|
45407
45443
|
if (ref.startsWith("~/") || ref === "~") {
|
|
45408
45444
|
const home2 = process.env.HOME || process.env.USERPROFILE;
|
|
45409
45445
|
if (!home2) return null;
|
|
45410
|
-
return ref === "~" ? home2 :
|
|
45446
|
+
return ref === "~" ? home2 : resolve8(home2, ref.slice(2));
|
|
45411
45447
|
}
|
|
45412
45448
|
if (isAbsolute2(ref)) return ref;
|
|
45413
|
-
return
|
|
45449
|
+
return resolve8(projectRoot, ref);
|
|
45414
45450
|
}
|
|
45415
45451
|
async function checkStaleMemory(ctx) {
|
|
45416
45452
|
const issues = [];
|
|
@@ -45453,7 +45489,15 @@ var init_stale_memory = __esm({
|
|
|
45453
45489
|
async function checkDuplicateMemory(ctx) {
|
|
45454
45490
|
const issues = [];
|
|
45455
45491
|
const reported = /* @__PURE__ */ new Set();
|
|
45456
|
-
const lineSets = ctx.memories.
|
|
45492
|
+
const lineSets = new Array(ctx.memories.length);
|
|
45493
|
+
const getLineSet = (idx) => {
|
|
45494
|
+
let ls2 = lineSets[idx];
|
|
45495
|
+
if (ls2 === void 0) {
|
|
45496
|
+
ls2 = toLineSet(ctx.memories[idx].content, MIN_TOKEN_LEN2);
|
|
45497
|
+
lineSets[idx] = ls2;
|
|
45498
|
+
}
|
|
45499
|
+
return ls2;
|
|
45500
|
+
};
|
|
45457
45501
|
for (let i2 = 0; i2 < ctx.memories.length; i2++) {
|
|
45458
45502
|
for (let j3 = i2 + 1; j3 < ctx.memories.length; j3++) {
|
|
45459
45503
|
const a = ctx.memories[i2];
|
|
@@ -45463,7 +45507,7 @@ async function checkDuplicateMemory(ctx) {
|
|
|
45463
45507
|
const bIsCurrent = projectDirMatchesPath(b2.projectDir, ctx.currentProject);
|
|
45464
45508
|
if (!aIsCurrent && !bIsCurrent) continue;
|
|
45465
45509
|
if (a.content.length < 50 || b2.content.length < 50) continue;
|
|
45466
|
-
const overlap = jaccardSimilarityFromSets(
|
|
45510
|
+
const overlap = jaccardSimilarityFromSets(getLineSet(i2), getLineSet(j3));
|
|
45467
45511
|
if (overlap < 0.6) continue;
|
|
45468
45512
|
const pairKey = [a.filePath, b2.filePath].sort().join("::");
|
|
45469
45513
|
if (reported.has(pairKey)) continue;
|
|
@@ -45497,9 +45541,9 @@ var init_duplicate_memory = __esm({
|
|
|
45497
45541
|
});
|
|
45498
45542
|
|
|
45499
45543
|
// src/core/checks/session/loop-detection.ts
|
|
45500
|
-
import { resolve as
|
|
45544
|
+
import { resolve as resolve9 } from "node:path";
|
|
45501
45545
|
function normalizeProject(p2) {
|
|
45502
|
-
return
|
|
45546
|
+
return resolve9(p2).replace(/\\/g, "/");
|
|
45503
45547
|
}
|
|
45504
45548
|
function findConsecutiveRepeats(displays) {
|
|
45505
45549
|
const results = [];
|
|
@@ -45509,13 +45553,13 @@ function findConsecutiveRepeats(displays) {
|
|
|
45509
45553
|
while (j3 < displays.length && displays[j3] === displays[i2]) j3++;
|
|
45510
45554
|
const count = j3 - i2;
|
|
45511
45555
|
if (count >= CONSECUTIVE_THRESHOLD) {
|
|
45512
|
-
results.push({ command: displays[i2], count });
|
|
45556
|
+
results.push({ command: displays[i2], count, startIdx: i2, endIdx: j3 });
|
|
45513
45557
|
}
|
|
45514
45558
|
i2 = j3;
|
|
45515
45559
|
}
|
|
45516
45560
|
return results;
|
|
45517
45561
|
}
|
|
45518
|
-
function findCyclicPatterns(displays) {
|
|
45562
|
+
function findCyclicPatterns(displays, consecutiveSpans) {
|
|
45519
45563
|
const results = [];
|
|
45520
45564
|
for (let cycleLen = 2; cycleLen <= MAX_CYCLE_LENGTH; cycleLen++) {
|
|
45521
45565
|
for (let start = 0; start <= displays.length - cycleLen * CYCLE_REPEAT_THRESHOLD; start++) {
|
|
@@ -45533,8 +45577,13 @@ function findCyclicPatterns(displays) {
|
|
|
45533
45577
|
}
|
|
45534
45578
|
}
|
|
45535
45579
|
if (repeats >= CYCLE_REPEAT_THRESHOLD) {
|
|
45580
|
+
const cycleEnd = start + cycleLen * repeats;
|
|
45581
|
+
const overlapsConsecutive = consecutiveSpans.some(
|
|
45582
|
+
(s) => s.startIdx < cycleEnd && start < s.endIdx
|
|
45583
|
+
);
|
|
45584
|
+
if (overlapsConsecutive) continue;
|
|
45536
45585
|
const alreadyCovered = results.some(
|
|
45537
|
-
(r2) => r2.startIdx <= start && r2.startIdx + r2.cycle.length * r2.repeats >=
|
|
45586
|
+
(r2) => r2.startIdx <= start && r2.startIdx + r2.cycle.length * r2.repeats >= cycleEnd
|
|
45538
45587
|
);
|
|
45539
45588
|
if (!alreadyCovered) {
|
|
45540
45589
|
results.push({ cycle, repeats, startIdx: start });
|
|
@@ -45563,7 +45612,8 @@ async function checkLoopDetection(ctx) {
|
|
|
45563
45612
|
suggestion: "An agent may be looping on this command. Check history.jsonl for context on what went wrong"
|
|
45564
45613
|
});
|
|
45565
45614
|
}
|
|
45566
|
-
const
|
|
45615
|
+
const consecutiveSpans = repeats.map((r2) => ({ startIdx: r2.startIdx, endIdx: r2.endIdx }));
|
|
45616
|
+
const cycles = findCyclicPatterns(displays, consecutiveSpans);
|
|
45567
45617
|
for (const { cycle, repeats: reps } of cycles) {
|
|
45568
45618
|
const cycleStr = cycle.map((c3) => c3.length > 40 ? c3.slice(0, 37) + "..." : c3).join(" -> ");
|
|
45569
45619
|
issues.push({
|
|
@@ -45668,9 +45718,12 @@ async function findReleaseWorkflows(projectRoot) {
|
|
|
45668
45718
|
}
|
|
45669
45719
|
try {
|
|
45670
45720
|
const content = stripBom(await readFile4(join9(workflowDir, f), "utf-8"));
|
|
45671
|
-
const nameMatch = content.match(
|
|
45672
|
-
if (nameMatch
|
|
45673
|
-
|
|
45721
|
+
const nameMatch = content.match(/^\s*name:\s*(.+?)\s*$/m);
|
|
45722
|
+
if (nameMatch) {
|
|
45723
|
+
const name = nameMatch[1].replace(/^(['"])(.*)\1$/, "$2");
|
|
45724
|
+
if (RELEASE_FILENAME_PATTERNS.some((p2) => p2.test(name))) {
|
|
45725
|
+
releaseWorkflows.push(f);
|
|
45726
|
+
}
|
|
45674
45727
|
}
|
|
45675
45728
|
} catch {
|
|
45676
45729
|
}
|
|
@@ -46524,6 +46577,7 @@ function extractPathsFromMessage(msg) {
|
|
|
46524
46577
|
function applyIgnoreRules(issues, rules) {
|
|
46525
46578
|
const compiled = compileRules(rules);
|
|
46526
46579
|
const kept = [];
|
|
46580
|
+
const keepMask = [];
|
|
46527
46581
|
let dropped = 0;
|
|
46528
46582
|
for (const issue2 of issues) {
|
|
46529
46583
|
let suppress = false;
|
|
@@ -46543,12 +46597,15 @@ function applyIgnoreRules(issues, rules) {
|
|
|
46543
46597
|
}
|
|
46544
46598
|
if (suppress) {
|
|
46545
46599
|
dropped++;
|
|
46600
|
+
keepMask.push(false);
|
|
46546
46601
|
} else {
|
|
46547
46602
|
kept.push(issue2);
|
|
46603
|
+
keepMask.push(true);
|
|
46548
46604
|
}
|
|
46549
46605
|
}
|
|
46550
46606
|
return {
|
|
46551
46607
|
kept,
|
|
46608
|
+
keepMask,
|
|
46552
46609
|
dropped,
|
|
46553
46610
|
unusedRules: compiled.filter((r2) => !r2.fired).map(stripCompiled),
|
|
46554
46611
|
rulesMissingReason: rules.filter((r2) => !r2.reason)
|
|
@@ -46583,12 +46640,12 @@ var init_types3 = __esm({
|
|
|
46583
46640
|
|
|
46584
46641
|
// src/version.ts
|
|
46585
46642
|
import { readFileSync as readFileSync7 } from "node:fs";
|
|
46586
|
-
import { resolve as
|
|
46643
|
+
import { resolve as resolve12, dirname as dirname6 } from "node:path";
|
|
46587
46644
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
46588
46645
|
function loadVersion() {
|
|
46589
|
-
if (true) return "0.13.
|
|
46590
|
-
const __dir =
|
|
46591
|
-
const pkgPath =
|
|
46646
|
+
if (true) return "0.13.1";
|
|
46647
|
+
const __dir = dirname6(fileURLToPath2(import.meta.url));
|
|
46648
|
+
const pkgPath = resolve12(__dir, "../package.json");
|
|
46592
46649
|
const pkg = JSON.parse(readFileSync7(pkgPath, "utf-8"));
|
|
46593
46650
|
return pkg.version;
|
|
46594
46651
|
}
|
|
@@ -46896,10 +46953,9 @@ async function runAudit(projectRoot, activeChecks, options = {}) {
|
|
|
46896
46953
|
}
|
|
46897
46954
|
});
|
|
46898
46955
|
const applied = applyIgnoreRules(flat, options.ignoreRules);
|
|
46899
|
-
const keptSet = new Set(applied.kept);
|
|
46900
46956
|
const rebuilt = fileResults.map(() => []);
|
|
46901
46957
|
flat.forEach((issue2, i2) => {
|
|
46902
|
-
if (
|
|
46958
|
+
if (applied.keepMask[i2]) rebuilt[owners[i2]].push(issue2);
|
|
46903
46959
|
});
|
|
46904
46960
|
fileResults.forEach((fr, idx) => {
|
|
46905
46961
|
fr.issues = rebuilt[idx];
|
|
@@ -46913,7 +46969,10 @@ async function runAudit(projectRoot, activeChecks, options = {}) {
|
|
|
46913
46969
|
let estimatedWaste = 0;
|
|
46914
46970
|
for (const fr of fileResults) {
|
|
46915
46971
|
for (const issue2 of fr.issues) {
|
|
46916
|
-
if (issue2.check
|
|
46972
|
+
if (issue2.check !== "redundancy") continue;
|
|
46973
|
+
if (typeof issue2.wastedTokens === "number") {
|
|
46974
|
+
estimatedWaste += issue2.wastedTokens;
|
|
46975
|
+
} else if (issue2.suggestion) {
|
|
46917
46976
|
const tokenMatch = issue2.suggestion.match(/~(\d+)\s+tokens/);
|
|
46918
46977
|
if (tokenMatch) estimatedWaste += parseInt(tokenMatch[1], 10);
|
|
46919
46978
|
}
|
|
@@ -54801,6 +54860,11 @@ function formatText(result, verbose = false) {
|
|
|
54801
54860
|
lines.push("Session audit scanned");
|
|
54802
54861
|
renderedAnySummary = true;
|
|
54803
54862
|
}
|
|
54863
|
+
if (groups.skill.length > 0) {
|
|
54864
|
+
if (renderedAnySummary) lines.push("");
|
|
54865
|
+
lines.push("Skill audit scanned");
|
|
54866
|
+
renderedAnySummary = true;
|
|
54867
|
+
}
|
|
54804
54868
|
if (!renderedAnySummary) {
|
|
54805
54869
|
lines.push(`Found ${result.files.length} file${result.files.length !== 1 ? "s" : ""}`);
|
|
54806
54870
|
}
|
package/package.json
CHANGED