@lousy-agents/cli 5.11.3 → 5.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -18,11 +18,11 @@
|
|
|
18
18
|
},
|
|
19
19
|
"devDependencies": {
|
|
20
20
|
"@biomejs/biome": "2.4.13",
|
|
21
|
-
"@lousy-agents/mcp": "5.11.
|
|
21
|
+
"@lousy-agents/mcp": "5.11.4",
|
|
22
22
|
"@modelcontextprotocol/server-sequential-thinking": "2025.12.18",
|
|
23
23
|
"@testcontainers/postgresql": "11.14.0",
|
|
24
24
|
"@types/node": "24.12.2",
|
|
25
|
-
"@upstash/context7-mcp": "2.2.
|
|
25
|
+
"@upstash/context7-mcp": "2.2.1",
|
|
26
26
|
"chance": "1.1.13",
|
|
27
27
|
"testcontainers": "11.14.0",
|
|
28
28
|
"tsx": "4.21.0",
|
|
@@ -956,9 +956,9 @@
|
|
|
956
956
|
}
|
|
957
957
|
},
|
|
958
958
|
"node_modules/@lousy-agents/mcp": {
|
|
959
|
-
"version": "5.11.
|
|
960
|
-
"resolved": "https://registry.npmjs.org/@lousy-agents/mcp/-/mcp-5.11.
|
|
961
|
-
"integrity": "sha512-
|
|
959
|
+
"version": "5.11.4",
|
|
960
|
+
"resolved": "https://registry.npmjs.org/@lousy-agents/mcp/-/mcp-5.11.4.tgz",
|
|
961
|
+
"integrity": "sha512-3hhhpJcopMWdXBT8cR4gHF9ieCNzDtwQDGWidKeSzbaq8+dW2t0zU0Znow+JW1lxjx3QuI68S09J4id6FpZqow==",
|
|
962
962
|
"dev": true,
|
|
963
963
|
"license": "BSD-2-Clause-Patent",
|
|
964
964
|
"dependencies": {
|
|
@@ -1649,9 +1649,9 @@
|
|
|
1649
1649
|
"license": "MIT"
|
|
1650
1650
|
},
|
|
1651
1651
|
"node_modules/@upstash/context7-mcp": {
|
|
1652
|
-
"version": "2.2.
|
|
1653
|
-
"resolved": "https://registry.npmjs.org/@upstash/context7-mcp/-/context7-mcp-2.2.
|
|
1654
|
-
"integrity": "sha512-
|
|
1652
|
+
"version": "2.2.1",
|
|
1653
|
+
"resolved": "https://registry.npmjs.org/@upstash/context7-mcp/-/context7-mcp-2.2.1.tgz",
|
|
1654
|
+
"integrity": "sha512-0Uu7GekKYaP/sgSCeoClYFqCm00rqehMFfHhj14krs0cVkVVz9vrChhZdV5kmkYDNNnQ7Vd6F5b57evina2+5w==",
|
|
1655
1655
|
"dev": true,
|
|
1656
1656
|
"license": "MIT",
|
|
1657
1657
|
"dependencies": {
|
|
@@ -26,11 +26,11 @@
|
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
28
28
|
"@biomejs/biome": "2.4.13",
|
|
29
|
-
"@lousy-agents/mcp": "5.11.
|
|
29
|
+
"@lousy-agents/mcp": "5.11.4",
|
|
30
30
|
"@modelcontextprotocol/server-sequential-thinking": "2025.12.18",
|
|
31
31
|
"@testcontainers/postgresql": "11.14.0",
|
|
32
32
|
"@types/node": "24.12.2",
|
|
33
|
-
"@upstash/context7-mcp": "2.2.
|
|
33
|
+
"@upstash/context7-mcp": "2.2.1",
|
|
34
34
|
"chance": "1.1.13",
|
|
35
35
|
"testcontainers": "11.14.0",
|
|
36
36
|
"tsx": "4.21.0",
|
|
@@ -20,10 +20,10 @@
|
|
|
20
20
|
},
|
|
21
21
|
"devDependencies": {
|
|
22
22
|
"@biomejs/biome": "2.4.13",
|
|
23
|
-
"@lousy-agents/mcp": "5.11.
|
|
23
|
+
"@lousy-agents/mcp": "5.11.4",
|
|
24
24
|
"@modelcontextprotocol/server-sequential-thinking": "2025.12.18",
|
|
25
25
|
"@types/node": "24.12.2",
|
|
26
|
-
"@upstash/context7-mcp": "2.2.
|
|
26
|
+
"@upstash/context7-mcp": "2.2.1",
|
|
27
27
|
"chance": "1.1.13",
|
|
28
28
|
"tsx": "4.21.0",
|
|
29
29
|
"typescript": "6.0.3",
|
package/dist/index.js
CHANGED
|
@@ -47095,7 +47095,8 @@ const remark = unified().use(remarkParse).use(remarkStringify).freeze()
|
|
|
47095
47095
|
"instruction/parse-error": "warn",
|
|
47096
47096
|
"instruction/command-not-in-code-block": "warn",
|
|
47097
47097
|
"instruction/command-outside-section": "warn",
|
|
47098
|
-
"instruction/missing-error-handling": "warn"
|
|
47098
|
+
"instruction/missing-error-handling": "warn",
|
|
47099
|
+
"instruction/missing-structural-heading": "warn"
|
|
47099
47100
|
},
|
|
47100
47101
|
skills: {
|
|
47101
47102
|
"skill/invalid-frontmatter": "error",
|
|
@@ -47105,7 +47106,8 @@ const remark = unified().use(remarkParse).use(remarkStringify).freeze()
|
|
|
47105
47106
|
"skill/name-mismatch": "error",
|
|
47106
47107
|
"skill/missing-description": "error",
|
|
47107
47108
|
"skill/invalid-description": "error",
|
|
47108
|
-
"skill/missing-allowed-tools": "warn"
|
|
47109
|
+
"skill/missing-allowed-tools": "warn",
|
|
47110
|
+
"skill/missing-argument-hint": "warn"
|
|
47109
47111
|
}
|
|
47110
47112
|
};
|
|
47111
47113
|
|
|
@@ -47187,6 +47189,24 @@ const remark = unified().use(remarkParse).use(remarkStringify).freeze()
|
|
|
47187
47189
|
"Validation Suite",
|
|
47188
47190
|
"Commands"
|
|
47189
47191
|
];
|
|
47192
|
+
/**
|
|
47193
|
+
* Descriptions for each default structural heading pattern explaining why it is recommended.
|
|
47194
|
+
* Keyed by Record<StructuralHeadingPattern, string> to ensure compile-time coverage — every
|
|
47195
|
+
* entry in DEFAULT_STRUCTURAL_HEADING_PATTERNS must have a description here.
|
|
47196
|
+
*/ const HEADING_PATTERN_DESCRIPTION_RECORD = {
|
|
47197
|
+
// biome-ignore lint/style/useNamingConvention: heading pattern keys use PascalCase by design
|
|
47198
|
+
Validation: "Agents need this section to understand how to validate that their changes meet quality standards.",
|
|
47199
|
+
// biome-ignore lint/style/useNamingConvention: heading pattern keys use PascalCase by design
|
|
47200
|
+
Verification: "Agents need this section to understand how to verify their implementation is correct.",
|
|
47201
|
+
"Feedback Loop": "Agents need this section to understand the iterative improvement process to follow.",
|
|
47202
|
+
// biome-ignore lint/style/useNamingConvention: heading pattern keys use PascalCase by design
|
|
47203
|
+
Mandatory: "Agents need this section to understand which steps are required and cannot be skipped.",
|
|
47204
|
+
"Before Commit": "Agents need this section to know what checks to run before committing changes.",
|
|
47205
|
+
"Validation Suite": "Agents need this section to know which validation commands to run against the codebase.",
|
|
47206
|
+
// biome-ignore lint/style/useNamingConvention: heading pattern keys use PascalCase by design
|
|
47207
|
+
Commands: "Agents need this section to know which commands and tools are available in the project."
|
|
47208
|
+
};
|
|
47209
|
+
const HEADING_PATTERN_DESCRIPTIONS = new Map(Object.entries(HEADING_PATTERN_DESCRIPTION_RECORD));
|
|
47190
47210
|
/** Conditional keywords indicating error handling near code blocks */ const CONDITIONAL_KEYWORDS = [
|
|
47191
47211
|
"if",
|
|
47192
47212
|
"fail",
|
|
@@ -47206,6 +47226,24 @@ const remark = unified().use(remarkParse).use(remarkStringify).freeze()
|
|
|
47206
47226
|
* Use case for analyzing instruction quality across a repository.
|
|
47207
47227
|
* Orchestrates discovery, AST parsing, and scoring of feedback loop documentation.
|
|
47208
47228
|
*/
|
|
47229
|
+
|
|
47230
|
+
/** Maximum number of raw heading pattern entries accepted before deduplication. */ const MAX_RAW_HEADING_PATTERNS = 1000;
|
|
47231
|
+
/** Maximum number of heading patterns accepted in a single execute() call. */ const MAX_HEADING_PATTERNS = 50;
|
|
47232
|
+
/** Maximum character length for a single heading pattern. */ const MAX_PATTERN_LENGTH = 200;
|
|
47233
|
+
/**
|
|
47234
|
+
* Lowercase-keyed lookup built once from HEADING_PATTERN_DESCRIPTIONS.
|
|
47235
|
+
* Enables description lookup regardless of the casing callers supply for patterns.
|
|
47236
|
+
*/ const HEADING_DESCRIPTIONS_BY_LOWER = new Map(Array.from(HEADING_PATTERN_DESCRIPTIONS, ([k, v])=>[
|
|
47237
|
+
k.toLowerCase(),
|
|
47238
|
+
v
|
|
47239
|
+
]));
|
|
47240
|
+
/**
|
|
47241
|
+
* Input for the analyze instruction quality use case.
|
|
47242
|
+
*/ const AnalyzeInstructionQualityInputSchema = schemas_object({
|
|
47243
|
+
targetDir: schemas_string().min(1, "Target directory is required"),
|
|
47244
|
+
headingPatterns: schemas_array(schemas_string().max(MAX_PATTERN_LENGTH * 2)).max(MAX_RAW_HEADING_PATTERNS).optional(),
|
|
47245
|
+
proximityWindow: schemas_number().int().positive().optional()
|
|
47246
|
+
});
|
|
47209
47247
|
/**
|
|
47210
47248
|
* Use case for analyzing instruction quality.
|
|
47211
47249
|
*/ class AnalyzeInstructionQualityUseCase {
|
|
@@ -47217,16 +47255,107 @@ const remark = unified().use(remarkParse).use(remarkStringify).freeze()
|
|
|
47217
47255
|
this.astGateway = astGateway;
|
|
47218
47256
|
this.commandsGateway = commandsGateway;
|
|
47219
47257
|
}
|
|
47258
|
+
/**
|
|
47259
|
+
* Returns true if the given heading-pattern string contains any characters
|
|
47260
|
+
* that could cause terminal injection or garbled output when the pattern
|
|
47261
|
+
* is embedded in a diagnostic message.
|
|
47262
|
+
*
|
|
47263
|
+
* Rejects: ASCII C0 (0x00–0x1F), DEL (0x7F), C1 (0x80–0x9F), **lone**
|
|
47264
|
+
* Unicode surrogates (U+D800–U+DFFF — but valid surrogate pairs like emoji
|
|
47265
|
+
* are accepted), line/paragraph separators (U+2028–U+2029), and bidi
|
|
47266
|
+
* override characters (U+202A–U+202E, U+2066–U+2069).
|
|
47267
|
+
*/ static patternHasControlCharacters(value) {
|
|
47268
|
+
for(let i = 0; i < value.length; i++){
|
|
47269
|
+
const code = value.charCodeAt(i);
|
|
47270
|
+
if (code <= 0x1f) return true;
|
|
47271
|
+
if (code === 0x7f) return true;
|
|
47272
|
+
if (code >= 0x80 && code <= 0x9f) return true;
|
|
47273
|
+
// Detect lone surrogates only; valid surrogate pairs (e.g., emoji)
|
|
47274
|
+
// should be accepted. A high surrogate (0xD800–0xDBFF) is lone if
|
|
47275
|
+
// it is not immediately followed by a low surrogate (0xDC00–0xDFFF).
|
|
47276
|
+
if (code >= 0xd800 && code <= 0xdbff) {
|
|
47277
|
+
const next = value.charCodeAt(i + 1);
|
|
47278
|
+
if (next >= 0xdc00 && next <= 0xdfff) {
|
|
47279
|
+
i++; // valid pair — skip the low surrogate
|
|
47280
|
+
} else {
|
|
47281
|
+
return true; // lone high surrogate
|
|
47282
|
+
}
|
|
47283
|
+
} else if (code >= 0xdc00 && code <= 0xdfff) {
|
|
47284
|
+
return true; // lone low surrogate
|
|
47285
|
+
}
|
|
47286
|
+
if (code === 0x2028 || code === 0x2029) return true;
|
|
47287
|
+
if (code >= 0x202a && code <= 0x202e) return true;
|
|
47288
|
+
if (code >= 0x2066 && code <= 0x2069) return true;
|
|
47289
|
+
}
|
|
47290
|
+
return false;
|
|
47291
|
+
}
|
|
47292
|
+
/**
|
|
47293
|
+
* Serializes a heading pattern for safe inclusion in error messages.
|
|
47294
|
+
* Produces a quoted, unambiguous representation by:
|
|
47295
|
+
* - Escaping `"` as `\"` and `\` as `\\` within printable ASCII (0x20–0x7E)
|
|
47296
|
+
* - Escaping every code unit outside that range to `\uXXXX`
|
|
47297
|
+
*
|
|
47298
|
+
* The \uXXXX escaping prevents terminal injection via bidi override
|
|
47299
|
+
* characters and other non-printable code points that JSON.stringify does
|
|
47300
|
+
* not escape.
|
|
47301
|
+
*/ static serializePatternForError(value) {
|
|
47302
|
+
let result = '"';
|
|
47303
|
+
for(let i = 0; i < value.length; i++){
|
|
47304
|
+
const code = value.charCodeAt(i);
|
|
47305
|
+
if (code >= 0x20 && code <= 0x7e) {
|
|
47306
|
+
if (code === 0x22) {
|
|
47307
|
+
result += '\\"';
|
|
47308
|
+
} else if (code === 0x5c) {
|
|
47309
|
+
result += "\\\\";
|
|
47310
|
+
} else {
|
|
47311
|
+
result += value[i];
|
|
47312
|
+
}
|
|
47313
|
+
} else {
|
|
47314
|
+
result += `\\u${code.toString(16).padStart(4, "0")}`;
|
|
47315
|
+
}
|
|
47316
|
+
}
|
|
47317
|
+
return `${result}"`;
|
|
47318
|
+
}
|
|
47220
47319
|
async execute(input) {
|
|
47221
|
-
|
|
47222
|
-
|
|
47320
|
+
let parsed;
|
|
47321
|
+
try {
|
|
47322
|
+
parsed = AnalyzeInstructionQualityInputSchema.parse(input);
|
|
47323
|
+
} catch (error) {
|
|
47324
|
+
if (error instanceof ZodError) {
|
|
47325
|
+
throw new Error(error.issues[0]?.message ?? "Validation failed: unable to parse error details");
|
|
47326
|
+
}
|
|
47327
|
+
throw error;
|
|
47223
47328
|
}
|
|
47224
|
-
const
|
|
47329
|
+
const seenLower = new Set();
|
|
47330
|
+
const headingPatterns = [];
|
|
47331
|
+
for (const raw of parsed.headingPatterns ?? [
|
|
47225
47332
|
...DEFAULT_STRUCTURAL_HEADING_PATTERNS
|
|
47226
|
-
]
|
|
47227
|
-
|
|
47228
|
-
|
|
47229
|
-
|
|
47333
|
+
]){
|
|
47334
|
+
// Validate BEFORE trim() — U+2028/U+2029 are JavaScript whitespace
|
|
47335
|
+
// and would be silently stripped by trim(), bypassing the check.
|
|
47336
|
+
// Filter before transform.
|
|
47337
|
+
if (AnalyzeInstructionQualityUseCase.patternHasControlCharacters(raw)) {
|
|
47338
|
+
throw new Error(`headingPatterns must not contain control characters, bidi override characters, or lone surrogate code points: ${AnalyzeInstructionQualityUseCase.serializePatternForError(raw)}`);
|
|
47339
|
+
}
|
|
47340
|
+
const trimmed = raw.trim();
|
|
47341
|
+
if (trimmed.length === 0) {
|
|
47342
|
+
throw new Error("headingPatterns must not contain empty or whitespace-only entries");
|
|
47343
|
+
}
|
|
47344
|
+
if (trimmed.length > MAX_PATTERN_LENGTH) {
|
|
47345
|
+
throw new Error(`headingPatterns entries must not exceed ${MAX_PATTERN_LENGTH} characters`);
|
|
47346
|
+
}
|
|
47347
|
+
const lower = trimmed.toLowerCase();
|
|
47348
|
+
if (!seenLower.has(lower)) {
|
|
47349
|
+
seenLower.add(lower);
|
|
47350
|
+
if (seenLower.size > MAX_HEADING_PATTERNS) {
|
|
47351
|
+
throw new Error(`headingPatterns must contain at most ${MAX_HEADING_PATTERNS} unique entries (case-insensitive)`);
|
|
47352
|
+
}
|
|
47353
|
+
headingPatterns.push(trimmed);
|
|
47354
|
+
}
|
|
47355
|
+
}
|
|
47356
|
+
const proximityWindow = parsed.proximityWindow ?? 3;
|
|
47357
|
+
const discoveredFiles = await this.discoveryGateway.discoverInstructionFiles(parsed.targetDir);
|
|
47358
|
+
const mandatoryCommands = await this.commandsGateway.getMandatoryCommands(parsed.targetDir);
|
|
47230
47359
|
if (discoveredFiles.length === 0) {
|
|
47231
47360
|
return {
|
|
47232
47361
|
result: {
|
|
@@ -47259,7 +47388,7 @@ const remark = unified().use(remarkParse).use(remarkStringify).freeze()
|
|
|
47259
47388
|
}
|
|
47260
47389
|
}
|
|
47261
47390
|
// Sort parsing errors for deterministic output
|
|
47262
|
-
parsingErrors.sort((a, b)=>a.filePath.
|
|
47391
|
+
parsingErrors.sort((a, b)=>a.filePath < b.filePath ? -1 : a.filePath > b.filePath ? 1 : 0);
|
|
47263
47392
|
// Score each mandatory command across all files
|
|
47264
47393
|
const commandScores = [];
|
|
47265
47394
|
const diagnostics = [];
|
|
@@ -47274,6 +47403,16 @@ const remark = unified().use(remarkParse).use(remarkStringify).freeze()
|
|
|
47274
47403
|
target: "instruction"
|
|
47275
47404
|
});
|
|
47276
47405
|
}
|
|
47406
|
+
// Check each successfully parsed file for missing structural headings
|
|
47407
|
+
const sortedFilePaths = Array.from(fileStructures.keys()).sort((a, b)=>a < b ? -1 : a > b ? 1 : 0);
|
|
47408
|
+
for (const filePath of sortedFilePaths){
|
|
47409
|
+
const structure = fileStructures.get(filePath);
|
|
47410
|
+
if (!structure) {
|
|
47411
|
+
continue;
|
|
47412
|
+
}
|
|
47413
|
+
const missingDiagnostics = this.checkMissingHeadings(filePath, structure, headingPatterns);
|
|
47414
|
+
diagnostics.push(...missingDiagnostics);
|
|
47415
|
+
}
|
|
47277
47416
|
for (const command of mandatoryCommands){
|
|
47278
47417
|
const bestAnalysis = this.findBestScore(command, discoveredFiles, fileStructures, headingPatterns, proximityWindow, diagnostics);
|
|
47279
47418
|
if (bestAnalysis) {
|
|
@@ -47515,6 +47654,43 @@ const remark = unified().use(remarkParse).use(remarkStringify).freeze()
|
|
|
47515
47654
|
}
|
|
47516
47655
|
return false;
|
|
47517
47656
|
}
|
|
47657
|
+
/**
|
|
47658
|
+
* Emits a warning diagnostic for each heading pattern that is absent from the file.
|
|
47659
|
+
* Headings are matched case-insensitively using substring inclusion, matching the
|
|
47660
|
+
* same logic used elsewhere in the use case.
|
|
47661
|
+
*/ checkMissingHeadings(filePath, structure, headingPatterns) {
|
|
47662
|
+
// Precondition: `headingPatterns` must already be deduplicated
|
|
47663
|
+
// (case-insensitively) and validated. This invariant is enforced by
|
|
47664
|
+
// `execute()` before calling this method.
|
|
47665
|
+
const diagnostics = [];
|
|
47666
|
+
const fileHeadingTexts = structure.headings.map((h)=>h.text.toLowerCase());
|
|
47667
|
+
for (const pattern of headingPatterns){
|
|
47668
|
+
const patternLower = pattern.toLowerCase();
|
|
47669
|
+
const hasHeading = fileHeadingTexts.some((text)=>{
|
|
47670
|
+
if (!text.includes(patternLower)) {
|
|
47671
|
+
return false;
|
|
47672
|
+
}
|
|
47673
|
+
// Don't let a heading satisfy a shorter pattern when it also satisfies
|
|
47674
|
+
// a longer, more-specific pattern that starts with the shorter one.
|
|
47675
|
+
// E.g., a "Validation Suite" heading satisfies "Validation Suite" but
|
|
47676
|
+
// should NOT also satisfy "Validation".
|
|
47677
|
+
const supersededByMoreSpecific = headingPatterns.some((other)=>other !== pattern && other.toLowerCase().startsWith(patternLower) && text.includes(other.toLowerCase()));
|
|
47678
|
+
return !supersededByMoreSpecific;
|
|
47679
|
+
});
|
|
47680
|
+
if (!hasHeading) {
|
|
47681
|
+
const description = HEADING_DESCRIPTIONS_BY_LOWER.get(patternLower) ?? "This heading helps guide coding agents through structured workflows.";
|
|
47682
|
+
diagnostics.push({
|
|
47683
|
+
filePath,
|
|
47684
|
+
line: 1,
|
|
47685
|
+
severity: "warning",
|
|
47686
|
+
message: `Missing '${pattern}' heading section. ${description}`,
|
|
47687
|
+
ruleId: "instruction/missing-structural-heading",
|
|
47688
|
+
target: "instruction"
|
|
47689
|
+
});
|
|
47690
|
+
}
|
|
47691
|
+
}
|
|
47692
|
+
return diagnostics;
|
|
47693
|
+
}
|
|
47518
47694
|
generateSuggestions(commandScores) {
|
|
47519
47695
|
const suggestions = [];
|
|
47520
47696
|
const lowStructural = commandScores.filter((s)=>s.structuralContext === 0 && s.bestSourceFile !== "");
|
|
@@ -47971,13 +48147,16 @@ const AgentSkillFrontmatterSchema = schemas_object({
|
|
|
47971
48147
|
license: schemas_string().optional(),
|
|
47972
48148
|
compatibility: schemas_string().max(500, "Compatibility must be 500 characters or fewer").optional(),
|
|
47973
48149
|
metadata: record(schemas_string(), schemas_string()).optional(),
|
|
47974
|
-
"allowed-tools": schemas_string().optional()
|
|
48150
|
+
"allowed-tools": schemas_string().optional(),
|
|
48151
|
+
"argument-hint": schemas_string().optional()
|
|
47975
48152
|
});
|
|
47976
48153
|
const RECOMMENDED_FIELDS = [
|
|
47977
|
-
"allowed-tools"
|
|
48154
|
+
"allowed-tools",
|
|
48155
|
+
"argument-hint"
|
|
47978
48156
|
];
|
|
47979
48157
|
const RECOMMENDED_FIELD_RULE_IDS = {
|
|
47980
|
-
"allowed-tools": "skill/missing-allowed-tools"
|
|
48158
|
+
"allowed-tools": "skill/missing-allowed-tools",
|
|
48159
|
+
"argument-hint": "skill/missing-argument-hint"
|
|
47981
48160
|
};
|
|
47982
48161
|
class LintSkillFrontmatterUseCase {
|
|
47983
48162
|
gateway;
|
package/package.json
CHANGED
|
@@ -23,20 +23,20 @@
|
|
|
23
23
|
"devDependencies": {
|
|
24
24
|
"@biomejs/biome": "2.4.13",
|
|
25
25
|
"@eslint/eslintrc": "3.3.5",
|
|
26
|
-
"@lousy-agents/mcp": "5.11.
|
|
26
|
+
"@lousy-agents/mcp": "5.11.4",
|
|
27
27
|
"@modelcontextprotocol/server-sequential-thinking": "2025.12.18",
|
|
28
28
|
"@testing-library/jest-dom": "6.9.1",
|
|
29
29
|
"@testing-library/react": "16.3.2",
|
|
30
30
|
"@types/node": "24.12.2",
|
|
31
31
|
"@types/react": "19.2.14",
|
|
32
32
|
"@types/react-dom": "19.2.3",
|
|
33
|
-
"@upstash/context7-mcp": "2.2.
|
|
33
|
+
"@upstash/context7-mcp": "2.2.1",
|
|
34
34
|
"@vitejs/plugin-react": "6.0.1",
|
|
35
35
|
"@vitest/ui": "4.1.5",
|
|
36
36
|
"eslint": "10.2.1",
|
|
37
37
|
"eslint-config-next": "16.2.4",
|
|
38
38
|
"happy-dom": "20.9.0",
|
|
39
|
-
"jsdom": "29.0
|
|
39
|
+
"jsdom": "29.1.0",
|
|
40
40
|
"typescript": "6.0.3",
|
|
41
41
|
"vitest": "4.1.5",
|
|
42
42
|
"whatwg-fetch": "3.6.20"
|