@lousy-agents/mcp 5.18.1 → 5.18.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.
Files changed (2) hide show
  1. package/dist/mcp-server.js +140 -121
  2. package/package.json +3 -2
@@ -34967,8 +34967,9 @@ async function readVersionFileContent(targetDir, relativePath) {
34967
34967
  continue;
34968
34968
  }
34969
34969
  if (await file_system_utils_pathExistsWithinRoot(targetDir, pmConfig.manifestFile)) {
34970
- const lockfilePath = pmConfig.lockfile ? await file_system_utils_resolveSafePath(targetDir, pmConfig.lockfile) : undefined;
34971
- const hasLockfile = lockfilePath ? await file_system_utils_pathExistsWithinRoot(targetDir, pmConfig.lockfile) : false;
34970
+ const { lockfile } = pmConfig;
34971
+ const lockfilePath = lockfile ? await file_system_utils_resolveSafePath(targetDir, lockfile) : undefined;
34972
+ const hasLockfile = lockfilePath && lockfile ? await file_system_utils_pathExistsWithinRoot(targetDir, lockfile) : false;
34972
34973
  if (pmConfig.requiresLockfile && !hasLockfile) {
34973
34974
  continue;
34974
34975
  }
@@ -34985,8 +34986,9 @@ async function readVersionFileContent(targetDir, relativePath) {
34985
34986
  const packageManagers = [];
34986
34987
  for (const pmConfig of otherPackageManagers){
34987
34988
  if (await file_system_utils_pathExistsWithinRoot(targetDir, pmConfig.manifestFile)) {
34988
- const lockfilePath = pmConfig.lockfile ? await file_system_utils_resolveSafePath(targetDir, pmConfig.lockfile) : undefined;
34989
- const hasLockfile = lockfilePath ? await file_system_utils_pathExistsWithinRoot(targetDir, pmConfig.lockfile) : false;
34989
+ const { lockfile } = pmConfig;
34990
+ const lockfilePath = lockfile ? await file_system_utils_resolveSafePath(targetDir, lockfile) : undefined;
34991
+ const hasLockfile = lockfilePath && lockfile ? await file_system_utils_pathExistsWithinRoot(targetDir, lockfile) : false;
34990
34992
  if (pmConfig.requiresLockfile && !hasLockfile) {
34991
34993
  continue;
34992
34994
  }
@@ -35148,11 +35150,18 @@ function defaultExec(command, args, options) {
35148
35150
  throw new Error("Not authenticated");
35149
35151
  }
35150
35152
  try {
35151
- await this.octokit.rest.repos.createRepoRuleset({
35153
+ const params = {
35152
35154
  owner,
35153
35155
  repo,
35154
- ...payload
35155
- });
35156
+ name: payload.name,
35157
+ enforcement: payload.enforcement,
35158
+ target: payload.target,
35159
+ // biome-ignore lint/style/useNamingConvention: GitHub API schema requires snake_case
35160
+ bypass_actors: payload.bypass_actors,
35161
+ conditions: payload.conditions,
35162
+ rules: payload.rules
35163
+ };
35164
+ await this.octokit.rest.repos.createRepoRuleset(params);
35156
35165
  } catch (error) {
35157
35166
  const details = formatOctokitError(error);
35158
35167
  throw new Error(`Failed to create ruleset for ${owner}/${repo}: ${details}`);
@@ -57474,120 +57483,6 @@ function lib_isUint8Array(value) {
57474
57483
  */
57475
57484
  const remark = unified().use(remarkParse).use(remarkStringify).freeze()
57476
57485
 
57477
- ;// CONCATENATED MODULE: ../core/src/gateways/markdown-ast-gateway.ts
57478
- /**
57479
- * Gateway for parsing Markdown files into AST using remark.
57480
- * Extracts structural features like headings, code blocks, and paragraphs.
57481
- */
57482
-
57483
-
57484
- /** Maximum instruction file size: 1 MB */ const markdown_ast_gateway_MAX_INSTRUCTION_FILE_BYTES = 1_048_576;
57485
- /**
57486
- * Remark-based implementation of the Markdown AST gateway.
57487
- */ class RemarkMarkdownAstGateway {
57488
- async parseFile(filePath) {
57489
- const content = await read_file_no_follow_readFileNoFollow(filePath, markdown_ast_gateway_MAX_INSTRUCTION_FILE_BYTES);
57490
- return this.parseContent(content);
57491
- }
57492
- parseContent(content) {
57493
- const processor = remark();
57494
- const ast = processor.parse(content);
57495
- const headings = [];
57496
- const codeBlocks = [];
57497
- const inlineCodes = [];
57498
- lib_visit(ast, "heading", (node)=>{
57499
- const text = this.extractHeadingText(node);
57500
- headings.push({
57501
- text,
57502
- depth: node.depth,
57503
- position: {
57504
- line: node.position?.start.line ?? 0
57505
- }
57506
- });
57507
- });
57508
- // Track code blocks with their node index in root children
57509
- for(let i = 0; i < ast.children.length; i++){
57510
- const child = ast.children[i];
57511
- if (child.type === "code") {
57512
- const codeNode = child;
57513
- codeBlocks.push({
57514
- value: codeNode.value,
57515
- lang: codeNode.lang ?? undefined,
57516
- position: {
57517
- line: codeNode.position?.start.line ?? 0
57518
- },
57519
- nodeIndex: i
57520
- });
57521
- }
57522
- }
57523
- lib_visit(ast, "inlineCode", (node)=>{
57524
- inlineCodes.push({
57525
- value: node.value,
57526
- position: {
57527
- line: node.position?.start.line ?? 0
57528
- }
57529
- });
57530
- });
57531
- return {
57532
- headings,
57533
- codeBlocks,
57534
- inlineCodes,
57535
- ast
57536
- };
57537
- }
57538
- findConditionalKeywordsInProximity(structure, codeBlockNodeIndex, proximityWindow, keywords) {
57539
- const ast = structure.ast;
57540
- const endIndex = Math.min(ast.children.length, codeBlockNodeIndex + 1 + proximityWindow);
57541
- for(let i = codeBlockNodeIndex + 1; i < endIndex; i++){
57542
- const sibling = ast.children[i];
57543
- const text = extractTextFromNode(sibling);
57544
- const lowerText = text.toLowerCase();
57545
- for (const keyword of keywords){
57546
- if (containsWordBoundary(lowerText, keyword.toLowerCase())) {
57547
- return true;
57548
- }
57549
- }
57550
- }
57551
- return false;
57552
- }
57553
- extractHeadingText(node) {
57554
- const parts = [];
57555
- lib_visit(node, "text", (textNode)=>{
57556
- parts.push(textNode.value);
57557
- });
57558
- return parts.join("");
57559
- }
57560
- }
57561
- /**
57562
- * Checks if text contains a keyword at a word boundary (safe string matching, no regex).
57563
- */ function containsWordBoundary(text, keyword) {
57564
- const index = text.indexOf(keyword);
57565
- if (index === -1) {
57566
- return false;
57567
- }
57568
- const charBefore = index > 0 ? text[index - 1] : " ";
57569
- const charAfter = index + keyword.length < text.length ? text[index + keyword.length] : " ";
57570
- const isWordBoundaryBefore = !/[a-z0-9]/i.test(charBefore);
57571
- const isWordBoundaryAfter = !/[a-z0-9]/i.test(charAfter);
57572
- return isWordBoundaryBefore && isWordBoundaryAfter;
57573
- }
57574
- /**
57575
- * Extracts plain text content from any AST node recursively.
57576
- */ function extractTextFromNode(node) {
57577
- if (node.value && typeof node.value === "string") {
57578
- return node.value;
57579
- }
57580
- if (Array.isArray(node.children)) {
57581
- return node.children.map((child)=>extractTextFromNode(child)).join(" ");
57582
- }
57583
- return "";
57584
- }
57585
- /**
57586
- * Creates and returns the default Markdown AST gateway.
57587
- */ function createMarkdownAstGateway() {
57588
- return new RemarkMarkdownAstGateway();
57589
- }
57590
-
57591
57486
  ;// CONCATENATED MODULE: ../core/src/entities/instruction-quality.ts
57592
57487
  /**
57593
57488
  * Core domain entities for instruction quality analysis.
@@ -57639,6 +57534,9 @@ const HEADING_PATTERN_DESCRIPTIONS = new Map(Object.entries(HEADING_PATTERN_DESC
57639
57534
  * Orchestrates discovery, AST parsing, and scoring of feedback loop documentation.
57640
57535
  */
57641
57536
 
57537
+ /** Type guard narrowing an `unknown` AST child to {@link AstNodeLike}. */ function isAstNodeLike(node) {
57538
+ return typeof node === "object" && node !== null && "type" in node && typeof node.type === "string";
57539
+ }
57642
57540
  /** Maximum number of raw heading pattern entries accepted before deduplication. */ const MAX_RAW_HEADING_PATTERNS = 1000;
57643
57541
  /** Maximum number of heading patterns accepted in a single execute() call. */ const MAX_HEADING_PATTERNS = 50;
57644
57542
  /** Maximum character length for a single heading pattern. */ const analyze_instruction_quality_MAX_PATTERN_LENGTH = 200;
@@ -57994,6 +57892,9 @@ const HEADING_PATTERN_DESCRIPTIONS = new Map(Object.entries(HEADING_PATTERN_DESC
57994
57892
  return parts.join(" ");
57995
57893
  }
57996
57894
  collectText(node, parts) {
57895
+ if (!isAstNodeLike(node)) {
57896
+ return;
57897
+ }
57997
57898
  if (node.type === "text" && typeof node.value === "string") {
57998
57899
  parts.push(node.value);
57999
57900
  }
@@ -58140,6 +58041,124 @@ const HEADING_PATTERN_DESCRIPTIONS = new Map(Object.entries(HEADING_PATTERN_DESC
58140
58041
  }
58141
58042
  }
58142
58043
 
58044
+ ;// CONCATENATED MODULE: ../core/src/gateways/markdown-ast-gateway.ts
58045
+ /**
58046
+ * Gateway for parsing Markdown files into AST using remark.
58047
+ * Extracts structural features like headings, code blocks, and paragraphs.
58048
+ */
58049
+
58050
+
58051
+
58052
+ /** Maximum instruction file size: 1 MB */ const markdown_ast_gateway_MAX_INSTRUCTION_FILE_BYTES = 1_048_576;
58053
+ /**
58054
+ * Remark-based implementation of the Markdown AST gateway.
58055
+ */ class RemarkMarkdownAstGateway {
58056
+ async parseFile(filePath) {
58057
+ const content = await read_file_no_follow_readFileNoFollow(filePath, markdown_ast_gateway_MAX_INSTRUCTION_FILE_BYTES);
58058
+ return this.parseContent(content);
58059
+ }
58060
+ parseContent(content) {
58061
+ const processor = remark();
58062
+ const ast = processor.parse(content);
58063
+ const headings = [];
58064
+ const codeBlocks = [];
58065
+ const inlineCodes = [];
58066
+ lib_visit(ast, "heading", (node)=>{
58067
+ const text = this.extractHeadingText(node);
58068
+ headings.push({
58069
+ text,
58070
+ depth: node.depth,
58071
+ position: {
58072
+ line: node.position?.start.line ?? 0
58073
+ }
58074
+ });
58075
+ });
58076
+ // Track code blocks with their node index in root children
58077
+ for(let i = 0; i < ast.children.length; i++){
58078
+ const child = ast.children[i];
58079
+ if (child.type === "code") {
58080
+ const codeNode = child;
58081
+ codeBlocks.push({
58082
+ value: codeNode.value,
58083
+ lang: codeNode.lang ?? undefined,
58084
+ position: {
58085
+ line: codeNode.position?.start.line ?? 0
58086
+ },
58087
+ nodeIndex: i
58088
+ });
58089
+ }
58090
+ }
58091
+ lib_visit(ast, "inlineCode", (node)=>{
58092
+ inlineCodes.push({
58093
+ value: node.value,
58094
+ position: {
58095
+ line: node.position?.start.line ?? 0
58096
+ }
58097
+ });
58098
+ });
58099
+ return {
58100
+ headings,
58101
+ codeBlocks,
58102
+ inlineCodes,
58103
+ ast
58104
+ };
58105
+ }
58106
+ findConditionalKeywordsInProximity(structure, codeBlockNodeIndex, proximityWindow, keywords) {
58107
+ const ast = structure.ast;
58108
+ const endIndex = Math.min(ast.children.length, codeBlockNodeIndex + 1 + proximityWindow);
58109
+ for(let i = codeBlockNodeIndex + 1; i < endIndex; i++){
58110
+ const sibling = ast.children[i];
58111
+ const text = extractTextFromNode(sibling);
58112
+ const lowerText = text.toLowerCase();
58113
+ for (const keyword of keywords){
58114
+ if (containsWordBoundary(lowerText, keyword.toLowerCase())) {
58115
+ return true;
58116
+ }
58117
+ }
58118
+ }
58119
+ return false;
58120
+ }
58121
+ extractHeadingText(node) {
58122
+ const parts = [];
58123
+ lib_visit(node, "text", (textNode)=>{
58124
+ parts.push(textNode.value);
58125
+ });
58126
+ return parts.join("");
58127
+ }
58128
+ }
58129
+ /**
58130
+ * Checks if text contains a keyword at a word boundary (safe string matching, no regex).
58131
+ */ function containsWordBoundary(text, keyword) {
58132
+ const index = text.indexOf(keyword);
58133
+ if (index === -1) {
58134
+ return false;
58135
+ }
58136
+ const charBefore = index > 0 ? text[index - 1] : " ";
58137
+ const charAfter = index + keyword.length < text.length ? text[index + keyword.length] : " ";
58138
+ const isWordBoundaryBefore = !/[a-z0-9]/i.test(charBefore);
58139
+ const isWordBoundaryAfter = !/[a-z0-9]/i.test(charAfter);
58140
+ return isWordBoundaryBefore && isWordBoundaryAfter;
58141
+ }
58142
+ /**
58143
+ * Extracts plain text content from any AST node recursively.
58144
+ */ function extractTextFromNode(node) {
58145
+ if (!isAstNodeLike(node)) {
58146
+ return "";
58147
+ }
58148
+ if (node.value && typeof node.value === "string") {
58149
+ return node.value;
58150
+ }
58151
+ if (Array.isArray(node.children)) {
58152
+ return node.children.map((child)=>extractTextFromNode(child)).join(" ");
58153
+ }
58154
+ return "";
58155
+ }
58156
+ /**
58157
+ * Creates and returns the default Markdown AST gateway.
58158
+ */ function createMarkdownAstGateway() {
58159
+ return new RemarkMarkdownAstGateway();
58160
+ }
58161
+
58143
58162
  ;// CONCATENATED MODULE: ./src/tools/analyze-instruction-quality.ts
58144
58163
  /**
58145
58164
  * MCP tool handler for analyzing instruction quality.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lousy-agents/mcp",
3
- "version": "5.18.1",
3
+ "version": "5.18.2",
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": {
@@ -33,7 +33,8 @@
33
33
  "lousy-agents-mcp": "dist/mcp-server.js"
34
34
  },
35
35
  "scripts": {
36
- "build": "rspack build --config rspack.config.ts && chmod +x dist/mcp-server.js"
36
+ "build": "rspack build --config rspack.config.ts && chmod +x dist/mcp-server.js",
37
+ "typecheck": "tsc --noEmit"
37
38
  },
38
39
  "dependencies": {
39
40
  "@modelcontextprotocol/sdk": "1.30.0",