@lousy-agents/mcp 5.20.2 → 5.21.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/mcp-server.js +726 -20
  2. package/package.json +1 -1
@@ -6559,7 +6559,7 @@ function escapeJsonPtr(str) {
6559
6559
 
6560
6560
 
6561
6561
  },
6562
- 4583(__unused_rspack_module, __unused_rspack___webpack_exports__, __webpack_require__) {
6562
+ 1977(__unused_rspack_module, __unused_rspack___webpack_exports__, __webpack_require__) {
6563
6563
  // NAMESPACE OBJECT: ../../node_modules/micromark/lib/constructs.js
6564
6564
  var constructs_namespaceObject = {};
6565
6565
  __webpack_require__.r(constructs_namespaceObject);
@@ -30367,6 +30367,21 @@ async function read_opened_file_readOpenedFileSafely(params) {
30367
30367
  };
30368
30368
  }
30369
30369
 
30370
+ ;// CONCATENATED MODULE: ../../node_modules/@openclaw/fs-safe/dist/read-open-flags.js
30371
+
30372
+ function resolveReadOpenFlags(options) {
30373
+ const constants = options?.constants ?? external_node_fs_.constants;
30374
+ const noFollow = process.platform !== "win32" &&
30375
+ options?.followSymlinks !== true &&
30376
+ typeof constants.O_NOFOLLOW === "number"
30377
+ ? constants.O_NOFOLLOW
30378
+ : 0;
30379
+ const nonBlocking = process.platform !== "win32" && typeof constants.O_NONBLOCK === "number"
30380
+ ? constants.O_NONBLOCK
30381
+ : 0;
30382
+ return constants.O_RDONLY | noFollow | nonBlocking;
30383
+ }
30384
+
30370
30385
  ;// CONCATENATED MODULE: ../../node_modules/@openclaw/fs-safe/dist/home-dir.js
30371
30386
 
30372
30387
 
@@ -30864,6 +30879,7 @@ async function serializePathWrite(key, run) {
30864
30879
 
30865
30880
 
30866
30881
 
30882
+
30867
30883
 
30868
30884
 
30869
30885
  function logWarn(message) {
@@ -30872,11 +30888,8 @@ function logWarn(message) {
30872
30888
  }
30873
30889
  }
30874
30890
  const SUPPORTS_NOFOLLOW = process.platform !== "win32" && "O_NOFOLLOW" in external_node_fs_.constants;
30875
- const NONBLOCK_OPEN_FLAG = process.platform !== "win32" && "O_NONBLOCK" in external_node_fs_.constants ? external_node_fs_.constants.O_NONBLOCK : 0;
30876
- const OPEN_READ_FLAGS = external_node_fs_.constants.O_RDONLY |
30877
- (SUPPORTS_NOFOLLOW ? external_node_fs_.constants.O_NOFOLLOW : 0) |
30878
- NONBLOCK_OPEN_FLAG;
30879
- const OPEN_READ_FOLLOW_FLAGS = external_node_fs_.constants.O_RDONLY | NONBLOCK_OPEN_FLAG;
30891
+ const OPEN_READ_FLAGS = resolveReadOpenFlags();
30892
+ const OPEN_READ_FOLLOW_FLAGS = resolveReadOpenFlags({ followSymlinks: true });
30880
30893
  const OPEN_WRITE_EXISTING_FLAGS = external_node_fs_.constants.O_WRONLY | (SUPPORTS_NOFOLLOW ? external_node_fs_.constants.O_NOFOLLOW : 0);
30881
30894
  const OPEN_WRITE_CREATE_FLAGS = external_node_fs_.constants.O_WRONLY |
30882
30895
  external_node_fs_.constants.O_CREAT |
@@ -38932,6 +38945,612 @@ function createWorkflowGateway(cwd) {
38932
38945
  }
38933
38946
  };
38934
38947
 
38948
+ ;// CONCATENATED MODULE: ../core/src/entities/source-position.ts
38949
+ /**
38950
+ * Pure helpers for mapping string offsets to 1-based line/column positions.
38951
+ */ /** 1-based line and column within a text document */ /**
38952
+ * Maps a 0-based string offset into a 1-based line and column.
38953
+ * Offsets are clamped to [0, content.length]. Newlines (`\n`) advance the line.
38954
+ */ function offsetToSourcePosition(content, offset) {
38955
+ const safeOffset = Math.max(0, Math.min(offset, content.length));
38956
+ let line = 1;
38957
+ let column = 1;
38958
+ for(let i = 0; i < safeOffset; i++){
38959
+ if (content[i] === "\n") {
38960
+ line += 1;
38961
+ column = 1;
38962
+ } else {
38963
+ column += 1;
38964
+ }
38965
+ }
38966
+ return {
38967
+ line,
38968
+ column
38969
+ };
38970
+ }
38971
+
38972
+ ;// CONCATENATED MODULE: ../core/src/lib/instruction-import-expand.ts
38973
+ /**
38974
+ * Pure Claude `@` import expander that builds an ordered EffectiveDocument.
38975
+ */
38976
+
38977
+ const DEFAULT_MAX_IMPORT_DEPTH = 4;
38978
+ const DEFAULT_MAX_UNIQUE_FILES = 64;
38979
+ const DEFAULT_MAX_EDGES = 256;
38980
+ const DEFAULT_MAX_EMITTED_BYTES = 512_000;
38981
+ const DEFAULT_MAX_FILE_BYTES = 1_048_576;
38982
+ /** Aligns with doctor HARD_IMPORT: line-start `@path` where path includes `/`. */ const HARD_IMPORT_GLOBAL_RE = /^@([^\s@][^\s]*)/gm;
38983
+ const FENCED_CODE_RE = /^(`{3,}|~{3,})[^\r\n]*\r?\n[\s\S]*?^\1[ \t]*$/gm;
38984
+ const INLINE_CODE_RE = /`+[^`\r\n]*`+/g;
38985
+ function resolveLimits(overrides) {
38986
+ return {
38987
+ maxDepth: overrides?.maxDepth ?? DEFAULT_MAX_IMPORT_DEPTH,
38988
+ maxUniqueFiles: overrides?.maxUniqueFiles ?? DEFAULT_MAX_UNIQUE_FILES,
38989
+ maxEdges: overrides?.maxEdges ?? DEFAULT_MAX_EDGES,
38990
+ maxEmittedBytes: overrides?.maxEmittedBytes ?? DEFAULT_MAX_EMITTED_BYTES,
38991
+ maxFileBytes: overrides?.maxFileBytes ?? DEFAULT_MAX_FILE_BYTES
38992
+ };
38993
+ }
38994
+ function toPosixRelative(pathValue) {
38995
+ return pathValue.split(external_node_path_.sep).join("/");
38996
+ }
38997
+ function isInsideCodeRegion(index, regions) {
38998
+ return regions.some((region)=>index >= region.start && index < region.end);
38999
+ }
39000
+ function collectRegexRanges(content, pattern, skipInside) {
39001
+ const regions = [];
39002
+ for (const match of content.matchAll(pattern)){
39003
+ if (match.index === undefined) {
39004
+ continue;
39005
+ }
39006
+ if (skipInside && isInsideCodeRegion(match.index, skipInside)) {
39007
+ continue;
39008
+ }
39009
+ regions.push({
39010
+ start: match.index,
39011
+ end: match.index + match[0].length
39012
+ });
39013
+ }
39014
+ return regions;
39015
+ }
39016
+ function findCodeRegions(content) {
39017
+ const fenced = collectRegexRanges(content, FENCED_CODE_RE);
39018
+ const inline = collectRegexRanges(content, INLINE_CODE_RE, fenced);
39019
+ return [
39020
+ ...fenced,
39021
+ ...inline
39022
+ ];
39023
+ }
39024
+ function findImportTokens(content) {
39025
+ const codeRegions = findCodeRegions(content);
39026
+ const tokens = [];
39027
+ for (const match of content.matchAll(HARD_IMPORT_GLOBAL_RE)){
39028
+ if (match.index === undefined) {
39029
+ continue;
39030
+ }
39031
+ const rawTarget = match[1];
39032
+ if (!rawTarget?.includes("/")) {
39033
+ continue;
39034
+ }
39035
+ if (isInsideCodeRegion(match.index, codeRegions)) {
39036
+ continue;
39037
+ }
39038
+ tokens.push({
39039
+ start: match.index,
39040
+ end: match.index + match[0].length,
39041
+ rawTarget
39042
+ });
39043
+ }
39044
+ return tokens;
39045
+ }
39046
+ function rebaseSegments(segments, offset) {
39047
+ return segments.map((segment)=>({
39048
+ ...segment,
39049
+ effectiveRange: {
39050
+ start: segment.effectiveRange.start + offset,
39051
+ end: segment.effectiveRange.end + offset
39052
+ }
39053
+ }));
39054
+ }
39055
+ function createLiteralSegment(contentOffset, sourcePath, sourceStart, text, importChain) {
39056
+ if (text.length === 0) {
39057
+ return undefined;
39058
+ }
39059
+ return {
39060
+ effectiveRange: {
39061
+ start: contentOffset,
39062
+ end: contentOffset + text.length
39063
+ },
39064
+ sourcePath,
39065
+ sourceRange: {
39066
+ start: sourceStart,
39067
+ end: sourceStart + text.length
39068
+ },
39069
+ importChain: [
39070
+ ...importChain
39071
+ ]
39072
+ };
39073
+ }
39074
+ const RULE_ID_BY_STATUS = {
39075
+ unresolved: "instruction/import-unresolved",
39076
+ "not-regular": "instruction/import-unresolved",
39077
+ escape: "instruction/import-escape",
39078
+ absolute: "instruction/import-escape",
39079
+ home: "instruction/import-escape",
39080
+ symlink: "instruction/import-symlink",
39081
+ cycle: "instruction/import-cycle",
39082
+ "depth-exceeded": "instruction/import-depth-exceeded",
39083
+ "size-exceeded": "instruction/import-size-exceeded",
39084
+ resolved: undefined
39085
+ };
39086
+ const FAILURE_MESSAGE_BY_STATUS = {
39087
+ unresolved: (rawTarget)=>`Import target could not be resolved: ${rawTarget}`,
39088
+ escape: (rawTarget)=>`Import target escapes the repository root: ${rawTarget}`,
39089
+ absolute: (rawTarget)=>`Absolute import paths are not allowed: ${rawTarget}`,
39090
+ home: (rawTarget)=>`Home-directory import paths are not allowed: ${rawTarget}`,
39091
+ symlink: (rawTarget)=>`Import target path contains a symbolic link: ${rawTarget}`,
39092
+ cycle: (rawTarget)=>`Import cycle detected while resolving: ${rawTarget}`,
39093
+ "depth-exceeded": (rawTarget)=>`Import depth limit exceeded while resolving: ${rawTarget}`,
39094
+ "size-exceeded": (rawTarget)=>`Import expansion size or graph limit exceeded while resolving: ${rawTarget}`,
39095
+ "not-regular": (rawTarget)=>`Import target is not a regular file: ${rawTarget}`,
39096
+ resolved: ()=>""
39097
+ };
39098
+ function ruleIdForStatus(status) {
39099
+ return RULE_ID_BY_STATUS[status];
39100
+ }
39101
+ function messageForFailure(status, rawTarget) {
39102
+ return FAILURE_MESSAGE_BY_STATUS[status](rawTarget);
39103
+ }
39104
+ function classifyPathError(error) {
39105
+ if (!(error instanceof Error)) {
39106
+ return "unresolved";
39107
+ }
39108
+ const message = error.message;
39109
+ if (message.includes("outside target directory") || message.includes("invalid-path") || message.includes("outside-workspace")) {
39110
+ return "escape";
39111
+ }
39112
+ if (message.includes("Symlinks are not allowed") || message.includes("symbolic link") || message.includes("path-alias")) {
39113
+ return "symlink";
39114
+ }
39115
+ if (message.includes("exceeds size limit") || message.includes("too-large")) {
39116
+ return "size-exceeded";
39117
+ }
39118
+ return "unresolved";
39119
+ }
39120
+ function normalizeRelativeWithinRoot(importerRelativePath, rawTarget) {
39121
+ if (rawTarget === "~" || rawTarget.startsWith("~/") || rawTarget.startsWith("~\\")) {
39122
+ return {
39123
+ ok: false,
39124
+ status: "home"
39125
+ };
39126
+ }
39127
+ if ((0,external_node_path_.isAbsolute)(rawTarget)) {
39128
+ return {
39129
+ ok: false,
39130
+ status: "absolute"
39131
+ };
39132
+ }
39133
+ const importerDir = (0,external_node_path_.dirname)(importerRelativePath);
39134
+ const joined = (0,external_node_path_.normalize)((0,external_node_path_.join)(importerDir, rawTarget));
39135
+ const normalized = joined === "." ? "" : joined;
39136
+ if (normalized === ".." || normalized.startsWith(`..${external_node_path_.sep}`) || normalized.startsWith("../") || normalized.startsWith("..\\")) {
39137
+ return {
39138
+ ok: false,
39139
+ status: "escape"
39140
+ };
39141
+ }
39142
+ if (!normalized) {
39143
+ return {
39144
+ ok: false,
39145
+ status: "unresolved"
39146
+ };
39147
+ }
39148
+ return {
39149
+ ok: true,
39150
+ relativePath: toPosixRelative(normalized)
39151
+ };
39152
+ }
39153
+ function isUnsafeRootPath(rootRelativePath) {
39154
+ return !rootRelativePath || rootRelativePath === ".." || rootRelativePath.startsWith(`..${external_node_path_.sep}`) || rootRelativePath.startsWith("../") || (0,external_node_path_.isAbsolute)(rootRelativePath);
39155
+ }
39156
+ /**
39157
+ * Owns expansion bookkeeping for a single buildEffectiveDocument call.
39158
+ * Mutations stay on the session instance rather than shared parameter bags.
39159
+ */ class ExpansionSession {
39160
+ limits;
39161
+ contentCache = new Map();
39162
+ uniqueFiles = new Set();
39163
+ edges = [];
39164
+ diagnostics = [];
39165
+ emittedBytes = 0;
39166
+ constructor(limits){
39167
+ this.limits = limits;
39168
+ }
39169
+ get edgeCount() {
39170
+ return this.edges.length;
39171
+ }
39172
+ get remainingEmitBudget() {
39173
+ return this.limits.maxEmittedBytes - this.emittedBytes;
39174
+ }
39175
+ isEmitBudgetExhausted() {
39176
+ return this.emittedBytes >= this.limits.maxEmittedBytes;
39177
+ }
39178
+ snapshot() {
39179
+ return {
39180
+ edges: [
39181
+ ...this.edges
39182
+ ],
39183
+ diagnostics: [
39184
+ ...this.diagnostics
39185
+ ]
39186
+ };
39187
+ }
39188
+ recordEdge(edge) {
39189
+ this.edges.push(edge);
39190
+ // Failures always carry ruleId; resolved edges do not.
39191
+ if (!edge.ruleId) {
39192
+ return;
39193
+ }
39194
+ this.diagnostics.push({
39195
+ ruleId: edge.ruleId,
39196
+ message: messageForFailure(edge.status, edge.rawTarget),
39197
+ filePath: edge.importer,
39198
+ range: edge.tokenRange
39199
+ });
39200
+ }
39201
+ recordFailure(importer, token, status, target) {
39202
+ this.recordEdge({
39203
+ importer,
39204
+ tokenRange: {
39205
+ start: token.start,
39206
+ end: token.end
39207
+ },
39208
+ rawTarget: token.rawTarget,
39209
+ target,
39210
+ status,
39211
+ ruleId: ruleIdForStatus(status)
39212
+ });
39213
+ }
39214
+ recordResolved(importer, token, target) {
39215
+ this.recordEdge({
39216
+ importer,
39217
+ tokenRange: {
39218
+ start: token.start,
39219
+ end: token.end
39220
+ },
39221
+ rawTarget: token.rawTarget,
39222
+ target,
39223
+ status: "resolved"
39224
+ });
39225
+ }
39226
+ /**
39227
+ * Emit up to `text` against the remaining byte budget.
39228
+ * Returns emitted text (possibly clipped) and whether the full text fit.
39229
+ */ takeEmitBudget(text) {
39230
+ if (text.length === 0) {
39231
+ return {
39232
+ emitted: "",
39233
+ complete: true
39234
+ };
39235
+ }
39236
+ const room = this.remainingEmitBudget;
39237
+ if (room <= 0) {
39238
+ return {
39239
+ emitted: "",
39240
+ complete: false
39241
+ };
39242
+ }
39243
+ if (text.length <= room) {
39244
+ this.emittedBytes += text.length;
39245
+ return {
39246
+ emitted: text,
39247
+ complete: true
39248
+ };
39249
+ }
39250
+ this.emittedBytes += room;
39251
+ return {
39252
+ emitted: text.slice(0, room),
39253
+ complete: false
39254
+ };
39255
+ }
39256
+ async readFile(repoRoot, relativePath) {
39257
+ const cached = this.contentCache.get(relativePath);
39258
+ if (cached !== undefined) {
39259
+ return {
39260
+ ok: true,
39261
+ content: cached
39262
+ };
39263
+ }
39264
+ try {
39265
+ await file_system_utils_resolvePathWithinRoot(repoRoot, relativePath);
39266
+ } catch (error) {
39267
+ return {
39268
+ ok: false,
39269
+ status: classifyPathError(error)
39270
+ };
39271
+ }
39272
+ try {
39273
+ const stats = await file_system_utils_statWithinRoot(repoRoot, relativePath);
39274
+ if (stats.isSymbolicLink) {
39275
+ return {
39276
+ ok: false,
39277
+ status: "symlink"
39278
+ };
39279
+ }
39280
+ if (!stats.isFile) {
39281
+ return {
39282
+ ok: false,
39283
+ status: "not-regular"
39284
+ };
39285
+ }
39286
+ } catch (error) {
39287
+ return {
39288
+ ok: false,
39289
+ status: classifyPathError(error)
39290
+ };
39291
+ }
39292
+ const isNewUnique = !this.uniqueFiles.has(relativePath);
39293
+ if (isNewUnique && this.uniqueFiles.size >= this.limits.maxUniqueFiles) {
39294
+ return {
39295
+ ok: false,
39296
+ status: "size-exceeded"
39297
+ };
39298
+ }
39299
+ try {
39300
+ const content = await file_system_utils_readTextWithinRoot(repoRoot, relativePath, this.limits.maxFileBytes);
39301
+ this.contentCache.set(relativePath, content);
39302
+ if (isNewUnique) {
39303
+ this.uniqueFiles.add(relativePath);
39304
+ }
39305
+ return {
39306
+ ok: true,
39307
+ content
39308
+ };
39309
+ } catch (error) {
39310
+ return {
39311
+ ok: false,
39312
+ status: classifyPathError(error)
39313
+ };
39314
+ }
39315
+ }
39316
+ }
39317
+ class ContentBuilder {
39318
+ outputText = "";
39319
+ builtSegments = [];
39320
+ get output() {
39321
+ return this.outputText;
39322
+ }
39323
+ get length() {
39324
+ return this.outputText.length;
39325
+ }
39326
+ get segments() {
39327
+ return this.builtSegments;
39328
+ }
39329
+ appendLiteral(sourcePath, sourceStart, text, importChain) {
39330
+ const segment = createLiteralSegment(this.outputText.length, sourcePath, sourceStart, text, importChain);
39331
+ if (!segment) {
39332
+ return;
39333
+ }
39334
+ this.builtSegments.push(segment);
39335
+ this.outputText += text;
39336
+ }
39337
+ appendExpanded(content, segments) {
39338
+ this.outputText += content;
39339
+ this.builtSegments.push(...segments);
39340
+ }
39341
+ toResult() {
39342
+ return {
39343
+ content: this.outputText,
39344
+ segments: [
39345
+ ...this.builtSegments
39346
+ ]
39347
+ };
39348
+ }
39349
+ }
39350
+ function emitSourceSlice(session, builder, sourcePath, content, from, to, importChain) {
39351
+ if (to <= from) {
39352
+ return true;
39353
+ }
39354
+ const { emitted, complete } = session.takeEmitBudget(content.slice(from, to));
39355
+ builder.appendLiteral(sourcePath, from, emitted, importChain);
39356
+ return complete;
39357
+ }
39358
+ async function expandToken(session, repoRoot, importerPath, token, hop, stack, importChain, outputOffset) {
39359
+ if (session.edgeCount >= session.limits.maxEdges) {
39360
+ session.recordFailure(importerPath, token, "size-exceeded");
39361
+ return {
39362
+ kind: "unexpanded"
39363
+ };
39364
+ }
39365
+ const nextHop = hop + 1;
39366
+ if (nextHop > session.limits.maxDepth) {
39367
+ session.recordFailure(importerPath, token, "depth-exceeded");
39368
+ return {
39369
+ kind: "unexpanded"
39370
+ };
39371
+ }
39372
+ const normalized = normalizeRelativeWithinRoot(importerPath, token.rawTarget);
39373
+ if (!normalized.ok) {
39374
+ session.recordFailure(importerPath, token, normalized.status);
39375
+ return {
39376
+ kind: "unexpanded"
39377
+ };
39378
+ }
39379
+ const targetPath = normalized.relativePath;
39380
+ if (stack.includes(targetPath)) {
39381
+ session.recordFailure(importerPath, token, "cycle", targetPath);
39382
+ return {
39383
+ kind: "unexpanded"
39384
+ };
39385
+ }
39386
+ const readResult = await session.readFile(repoRoot, targetPath);
39387
+ if (!readResult.ok) {
39388
+ session.recordFailure(importerPath, token, readResult.status, targetPath);
39389
+ return {
39390
+ kind: "unexpanded"
39391
+ };
39392
+ }
39393
+ if (session.isEmitBudgetExhausted()) {
39394
+ session.recordFailure(importerPath, token, "size-exceeded", targetPath);
39395
+ return {
39396
+ kind: "unexpanded"
39397
+ };
39398
+ }
39399
+ const child = await expandContent(session, repoRoot, targetPath, readResult.content, nextHop, [
39400
+ ...stack,
39401
+ targetPath
39402
+ ], [
39403
+ ...importChain,
39404
+ targetPath
39405
+ ]);
39406
+ session.recordResolved(importerPath, token, targetPath);
39407
+ return {
39408
+ kind: "expanded",
39409
+ content: child.content,
39410
+ segments: rebaseSegments(child.segments, outputOffset)
39411
+ };
39412
+ }
39413
+ async function expandContent(session, repoRoot, sourcePath, content, hop, stack, importChain) {
39414
+ const tokens = findImportTokens(content);
39415
+ const builder = new ContentBuilder();
39416
+ let cursor = 0;
39417
+ for (const token of tokens){
39418
+ const literalOk = emitSourceSlice(session, builder, sourcePath, content, cursor, token.start, importChain);
39419
+ if (!literalOk) {
39420
+ session.recordFailure(sourcePath, token, "size-exceeded");
39421
+ emitSourceSlice(session, builder, sourcePath, content, token.start, content.length, importChain);
39422
+ return builder.toResult();
39423
+ }
39424
+ const expansion = await expandToken(session, repoRoot, sourcePath, token, hop, stack, importChain, builder.length);
39425
+ if (expansion.kind === "expanded") {
39426
+ builder.appendExpanded(expansion.content, expansion.segments);
39427
+ } else {
39428
+ const tokenOk = emitSourceSlice(session, builder, sourcePath, content, token.start, token.end, importChain);
39429
+ if (!tokenOk) {
39430
+ return builder.toResult();
39431
+ }
39432
+ }
39433
+ cursor = token.end;
39434
+ }
39435
+ emitSourceSlice(session, builder, sourcePath, content, cursor, content.length, importChain);
39436
+ return builder.toResult();
39437
+ }
39438
+ /**
39439
+ * Build an ordered effective document by expanding verified Claude `@` imports.
39440
+ */ async function buildEffectiveDocument(input) {
39441
+ const limits = resolveLimits(input.limits);
39442
+ const rootRelativePath = toPosixRelative((0,external_node_path_.normalize)(input.rootRelativePath));
39443
+ if (isUnsafeRootPath(rootRelativePath)) {
39444
+ throw new Error(`Root path is outside repository root: ${input.rootRelativePath}`);
39445
+ }
39446
+ const session = new ExpansionSession(limits);
39447
+ const rootRead = await session.readFile(input.repoRoot, rootRelativePath);
39448
+ if (!rootRead.ok) {
39449
+ throw new Error(`Unable to read root instruction file ${rootRelativePath}: ${rootRead.status}`);
39450
+ }
39451
+ const expanded = await expandContent(session, input.repoRoot, rootRelativePath, rootRead.content, 0, [
39452
+ rootRelativePath
39453
+ ], [
39454
+ rootRelativePath
39455
+ ]);
39456
+ const snapshot = session.snapshot();
39457
+ return {
39458
+ root: rootRelativePath,
39459
+ content: expanded.content,
39460
+ orderedSegments: expanded.segments,
39461
+ edges: snapshot.edges,
39462
+ expansionDiagnostics: snapshot.diagnostics
39463
+ };
39464
+ }
39465
+
39466
+ ;// CONCATENATED MODULE: ../core/src/gateways/claude-instruction-import-expander.ts
39467
+ /**
39468
+ * Adapter that expands Claude `@path` imports via the pure expander library.
39469
+ */
39470
+
39471
+
39472
+
39473
+ function toRepoRelativePosix(repoRoot, absoluteFilePath) {
39474
+ const relativePath = (0,external_node_path_.relative)(repoRoot, absoluteFilePath);
39475
+ if (relativePath.length === 0 || relativePath.startsWith(`..${external_node_path_.sep}`) || relativePath === ".." || (0,external_node_path_.isAbsolute)(relativePath)) {
39476
+ throw new Error(`Claude instruction path is outside repository root: ${absoluteFilePath}`);
39477
+ }
39478
+ return relativePath.split(external_node_path_.sep).join("/");
39479
+ }
39480
+ function toAbsolutePath(repoRoot, relativePosixPath) {
39481
+ return (0,external_node_path_.join)(repoRoot, ...relativePosixPath.split("/"));
39482
+ }
39483
+ async function mapExpansionDiagnostics(repoRoot, diagnostics) {
39484
+ const contentByRelative = new Map();
39485
+ const mapped = [];
39486
+ for (const diagnostic of diagnostics){
39487
+ const absolutePath = toAbsolutePath(repoRoot, diagnostic.filePath);
39488
+ let content = contentByRelative.get(diagnostic.filePath);
39489
+ if (content === undefined) {
39490
+ content = await (0,promises_.readFile)(absolutePath, "utf8");
39491
+ contentByRelative.set(diagnostic.filePath, content);
39492
+ }
39493
+ if (diagnostic.range === undefined) {
39494
+ mapped.push({
39495
+ ruleId: diagnostic.ruleId,
39496
+ message: diagnostic.message,
39497
+ filePath: absolutePath,
39498
+ line: 1,
39499
+ column: 1
39500
+ });
39501
+ continue;
39502
+ }
39503
+ const start = offsetToSourcePosition(content, diagnostic.range.start);
39504
+ const end = offsetToSourcePosition(content, diagnostic.range.end);
39505
+ mapped.push({
39506
+ ruleId: diagnostic.ruleId,
39507
+ message: diagnostic.message,
39508
+ filePath: absolutePath,
39509
+ line: start.line,
39510
+ column: start.column,
39511
+ endLine: end.line,
39512
+ endColumn: end.column
39513
+ });
39514
+ }
39515
+ return mapped;
39516
+ }
39517
+ function collectResolvedImports(repoRoot, edges) {
39518
+ const resolved = [];
39519
+ const seen = new Set();
39520
+ for (const edge of edges){
39521
+ if (edge.status !== "resolved" || edge.target === undefined) {
39522
+ continue;
39523
+ }
39524
+ const absolute = toAbsolutePath(repoRoot, edge.target);
39525
+ if (seen.has(absolute)) {
39526
+ continue;
39527
+ }
39528
+ seen.add(absolute);
39529
+ resolved.push(absolute);
39530
+ }
39531
+ return resolved;
39532
+ }
39533
+ /**
39534
+ * Creates the default Claude instruction import expander used by composition roots.
39535
+ */ function createClaudeInstructionImportExpander() {
39536
+ return {
39537
+ async expandClaudeEntrypoint (input) {
39538
+ const rootRelativePath = toRepoRelativePosix(input.repoRoot, input.absoluteFilePath);
39539
+ const document = await buildEffectiveDocument({
39540
+ repoRoot: input.repoRoot,
39541
+ rootRelativePath
39542
+ });
39543
+ const expansionDiagnostics = await mapExpansionDiagnostics(input.repoRoot, document.expansionDiagnostics);
39544
+ return {
39545
+ content: document.content,
39546
+ effectiveRoot: input.absoluteFilePath,
39547
+ resolvedImports: collectResolvedImports(input.repoRoot, document.edges),
39548
+ expansionDiagnostics
39549
+ };
39550
+ }
39551
+ };
39552
+ }
39553
+
38935
39554
  ;// CONCATENATED MODULE: ../core/src/gateways/instruction-file-discovery-gateway.ts
38936
39555
  /**
38937
39556
  * Gateway for discovering instruction files across multiple formats.
@@ -57915,10 +58534,12 @@ const HEADING_PATTERN_DESCRIPTIONS = new Map(Object.entries(HEADING_PATTERN_DESC
57915
58534
  discoveryGateway;
57916
58535
  astGateway;
57917
58536
  commandsGateway;
57918
- constructor(discoveryGateway, astGateway, commandsGateway){
58537
+ claudeImportExpander;
58538
+ constructor(discoveryGateway, astGateway, commandsGateway, claudeImportExpander){
57919
58539
  this.discoveryGateway = discoveryGateway;
57920
58540
  this.astGateway = astGateway;
57921
58541
  this.commandsGateway = commandsGateway;
58542
+ this.claudeImportExpander = claudeImportExpander;
57922
58543
  }
57923
58544
  /**
57924
58545
  * Returns true if the given heading-pattern string contains any characters
@@ -58037,13 +58658,19 @@ const HEADING_PATTERN_DESCRIPTIONS = new Map(Object.entries(HEADING_PATTERN_DESC
58037
58658
  diagnostics: []
58038
58659
  };
58039
58660
  }
58040
- // Analyze each file
58661
+ // Analyze each file (Claude entrypoints use effective import-expanded content)
58041
58662
  const fileStructures = new Map();
58042
58663
  const parsingErrors = [];
58664
+ const importDiagnostics = [];
58665
+ const effectiveDocuments = [];
58043
58666
  for (const file of discoveredFiles){
58044
58667
  try {
58045
- const structure = await this.astGateway.parseFile(file.filePath);
58046
- fileStructures.set(file.filePath, structure);
58668
+ const resolved = await this.resolveAnalysisStructure(file, parsed.targetDir);
58669
+ fileStructures.set(file.filePath, resolved.structure);
58670
+ importDiagnostics.push(...resolved.importDiagnostics);
58671
+ if (resolved.provenance !== undefined) {
58672
+ effectiveDocuments.push(resolved.provenance);
58673
+ }
58047
58674
  } catch (error) {
58048
58675
  const errorMessage = error instanceof Error ? error.message : "Unknown parsing error";
58049
58676
  parsingErrors.push({
@@ -58068,6 +58695,8 @@ const HEADING_PATTERN_DESCRIPTIONS = new Map(Object.entries(HEADING_PATTERN_DESC
58068
58695
  target: "instruction"
58069
58696
  });
58070
58697
  }
58698
+ // Import-expansion failures (stable ruleIds, importer-token provenance)
58699
+ diagnostics.push(...importDiagnostics);
58071
58700
  // Check each successfully parsed file for missing structural headings
58072
58701
  const sortedFilePaths = Array.from(fileStructures.keys()).sort((a, b)=>a < b ? -1 : a > b ? 1 : 0);
58073
58702
  for (const filePath of sortedFilePaths){
@@ -58112,17 +58741,76 @@ const HEADING_PATTERN_DESCRIPTIONS = new Map(Object.entries(HEADING_PATTERN_DESC
58112
58741
  ruleId: "instruction/parse-error"
58113
58742
  });
58114
58743
  }
58744
+ const result = {
58745
+ discoveredFiles,
58746
+ commandScores,
58747
+ overallQualityScore,
58748
+ suggestions,
58749
+ parsingErrors,
58750
+ ...effectiveDocuments.length > 0 ? {
58751
+ effectiveDocuments
58752
+ } : {}
58753
+ };
58115
58754
  return {
58116
- result: {
58117
- discoveredFiles,
58118
- commandScores,
58119
- overallQualityScore,
58120
- suggestions,
58121
- parsingErrors
58122
- },
58755
+ result,
58123
58756
  diagnostics
58124
58757
  };
58125
58758
  }
58759
+ /**
58760
+ * Resolves the Markdown structure used for content-sensitive rules on one
58761
+ * discovered entrypoint. Claude entrypoints expand verified `@` imports when
58762
+ * an expander is injected; all other formats parse the physical file only.
58763
+ */ async resolveAnalysisStructure(file, targetDir) {
58764
+ if (file.format === "claude-md" && this.claudeImportExpander !== undefined) {
58765
+ const effective = await this.claudeImportExpander.expandClaudeEntrypoint({
58766
+ repoRoot: targetDir,
58767
+ absoluteFilePath: file.filePath
58768
+ });
58769
+ const importDiagnostics = effective.expansionDiagnostics.map((diagnostic)=>AnalyzeInstructionQualityUseCase.toImportLintDiagnostic(diagnostic));
58770
+ importDiagnostics.sort(AnalyzeInstructionQualityUseCase.compareImportDiagnostics);
58771
+ return {
58772
+ structure: this.astGateway.parseContent(effective.content),
58773
+ importDiagnostics,
58774
+ provenance: {
58775
+ effectiveRoot: effective.effectiveRoot,
58776
+ resolvedImports: effective.resolvedImports
58777
+ }
58778
+ };
58779
+ }
58780
+ return {
58781
+ structure: await this.astGateway.parseFile(file.filePath),
58782
+ importDiagnostics: []
58783
+ };
58784
+ }
58785
+ static toImportLintDiagnostic(diagnostic) {
58786
+ return {
58787
+ filePath: diagnostic.filePath,
58788
+ line: diagnostic.line,
58789
+ column: diagnostic.column,
58790
+ endLine: diagnostic.endLine,
58791
+ endColumn: diagnostic.endColumn,
58792
+ severity: "warning",
58793
+ message: diagnostic.message,
58794
+ ruleId: diagnostic.ruleId,
58795
+ target: "instruction"
58796
+ };
58797
+ }
58798
+ static compareImportDiagnostics(a, b) {
58799
+ if (a.filePath !== b.filePath) {
58800
+ return a.filePath < b.filePath ? -1 : 1;
58801
+ }
58802
+ if (a.line !== b.line) {
58803
+ return a.line - b.line;
58804
+ }
58805
+ const aCol = a.column ?? 0;
58806
+ const bCol = b.column ?? 0;
58807
+ if (aCol !== bCol) {
58808
+ return aCol - bCol;
58809
+ }
58810
+ const aRule = a.ruleId ?? "";
58811
+ const bRule = b.ruleId ?? "";
58812
+ return aRule < bRule ? -1 : aRule > bRule ? 1 : 0;
58813
+ }
58126
58814
  findBestScore(command, discoveredFiles, fileStructures, headingPatterns, proximityWindow, diagnostics) {
58127
58815
  let bestAnalysis = null;
58128
58816
  let bestComposite = -1;
@@ -58523,6 +59211,7 @@ const HEADING_PATTERN_DESCRIPTIONS = new Map(Object.entries(HEADING_PATTERN_DESC
58523
59211
 
58524
59212
 
58525
59213
 
59214
+
58526
59215
  /**
58527
59216
  * Analyzes the structural quality of feedback loop documentation in instruction files.
58528
59217
  * Assesses structural context, execution clarity, and loop completeness.
@@ -58535,7 +59224,7 @@ const HEADING_PATTERN_DESCRIPTIONS = new Map(Object.entries(HEADING_PATTERN_DESC
58535
59224
  const discoveryGateway = createInstructionFileDiscoveryGateway();
58536
59225
  const astGateway = createMarkdownAstGateway();
58537
59226
  const commandsGateway = createFeedbackLoopCommandsGateway();
58538
- const useCase = new AnalyzeInstructionQualityUseCase(discoveryGateway, astGateway, commandsGateway);
59227
+ const useCase = new AnalyzeInstructionQualityUseCase(discoveryGateway, astGateway, commandsGateway, createClaudeInstructionImportExpander());
58539
59228
  const output = await useCase.execute({
58540
59229
  targetDir: dir
58541
59230
  });
@@ -58558,10 +59247,27 @@ const HEADING_PATTERN_DESCRIPTIONS = new Map(Object.entries(HEADING_PATTERN_DESC
58558
59247
  diagnostics: output.diagnostics.map((d)=>({
58559
59248
  filePath: d.filePath,
58560
59249
  line: d.line,
59250
+ ...d.column !== undefined ? {
59251
+ column: d.column
59252
+ } : {},
59253
+ ...d.endLine !== undefined ? {
59254
+ endLine: d.endLine
59255
+ } : {},
59256
+ ...d.endColumn !== undefined ? {
59257
+ endColumn: d.endColumn
59258
+ } : {},
58561
59259
  severity: d.severity,
58562
59260
  message: d.message,
58563
59261
  ruleId: d.ruleId
58564
- }))
59262
+ })),
59263
+ ...output.result.effectiveDocuments !== undefined ? {
59264
+ effectiveDocuments: output.result.effectiveDocuments.map((doc)=>({
59265
+ effectiveRoot: doc.effectiveRoot,
59266
+ resolvedImports: [
59267
+ ...doc.resolvedImports
59268
+ ]
59269
+ }))
59270
+ } : {}
58565
59271
  });
58566
59272
  } catch (error) {
58567
59273
  return types_errorResponse(`Failed to analyze instruction quality: ${error instanceof Error ? error.message : "Unknown error"}`);
@@ -70391,4 +71097,4 @@ if (installedChunkData !== 0) { // 0 means "already installed".'
70391
71097
  // module factories are used so entry inlining is disabled
70392
71098
  // startup
70393
71099
  // Load entry module and return exports
70394
- var __webpack_exports__ = __webpack_require__(4583);
71100
+ var __webpack_exports__ = __webpack_require__(1977);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lousy-agents/mcp",
3
- "version": "5.20.2",
3
+ "version": "5.21.1",
4
4
  "description": "MCP server for lousy-agents - provides AI coding assistant tools via the Model Context Protocol",
5
5
  "type": "module",
6
6
  "repository": {