@plumpslabs/kuma 2.1.2 → 2.1.3
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/dist/index.js +103 -11
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -2303,7 +2303,7 @@ function getGitChangedFiles() {
|
|
|
2303
2303
|
}
|
|
2304
2304
|
}
|
|
2305
2305
|
async function handleCodeReviewer(params) {
|
|
2306
|
-
const { files: inputFiles, focus = "correctness", customCriteria, format = "text" } = params;
|
|
2306
|
+
const { files: inputFiles, focus = "correctness", customCriteria, format = "text", convention } = params;
|
|
2307
2307
|
let files = inputFiles ?? [];
|
|
2308
2308
|
let isAutoDetected = false;
|
|
2309
2309
|
if (files.length === 0) {
|
|
@@ -2362,6 +2362,9 @@ Pass an explicit 'files' array to review specific files.`;
|
|
|
2362
2362
|
checkOverEngineering(filePath, content, allIssues);
|
|
2363
2363
|
break;
|
|
2364
2364
|
}
|
|
2365
|
+
if (convention === "matcha") {
|
|
2366
|
+
checkMatchaConventions(filePath, content, allIssues);
|
|
2367
|
+
}
|
|
2365
2368
|
} catch (err) {
|
|
2366
2369
|
allIssues.push({
|
|
2367
2370
|
file: filePath,
|
|
@@ -2389,6 +2392,7 @@ Pass an explicit 'files' array to review specific files.`;
|
|
|
2389
2392
|
filesReviewed,
|
|
2390
2393
|
filesRequested: files.length,
|
|
2391
2394
|
focus,
|
|
2395
|
+
convention,
|
|
2392
2396
|
autoDetected: isAutoDetected,
|
|
2393
2397
|
...customCriteria ? { customCriteria } : {}
|
|
2394
2398
|
};
|
|
@@ -2915,8 +2919,8 @@ function collectBlockLines(lines, startIdx) {
|
|
|
2915
2919
|
let depth = 0;
|
|
2916
2920
|
let started = false;
|
|
2917
2921
|
const block = [];
|
|
2918
|
-
for (let
|
|
2919
|
-
const line = lines[
|
|
2922
|
+
for (let i = startIdx; i < lines.length; i++) {
|
|
2923
|
+
const line = lines[i];
|
|
2920
2924
|
block.push(line);
|
|
2921
2925
|
for (const ch of line) {
|
|
2922
2926
|
if (ch === "{") {
|
|
@@ -2933,6 +2937,73 @@ function extractBodyAfter(lines, match) {
|
|
|
2933
2937
|
if (startIdx < 0) return "";
|
|
2934
2938
|
return collectBlockLines(lines, startIdx).join("\n");
|
|
2935
2939
|
}
|
|
2940
|
+
function checkMatchaConventions(filePath, content, issues) {
|
|
2941
|
+
const lines = content.split("\n");
|
|
2942
|
+
const text = content.replace(/\/\/.*$/gm, "").replace(/\/\*[\s\S]*?\*\//g, "");
|
|
2943
|
+
for (let i = 0; i < lines.length; i++) {
|
|
2944
|
+
const line = stripCommentsAndStrings(lines[i]);
|
|
2945
|
+
const lineNum = i + 1;
|
|
2946
|
+
if (/\b(?:const|let|var)\s+\w+\s*=\s*(?:[2-9]|\d{2,})\b/.test(line)) {
|
|
2947
|
+
if (!/eslint-disable/.test(lines[i])) {
|
|
2948
|
+
issues.push({
|
|
2949
|
+
file: filePath,
|
|
2950
|
+
line: lineNum,
|
|
2951
|
+
severity: "info",
|
|
2952
|
+
rule: "matcha/no-hardcoded-values",
|
|
2953
|
+
message: "Possible hardcoded numeric value (magic number)",
|
|
2954
|
+
suggestion: "Extract to a named constant"
|
|
2955
|
+
});
|
|
2956
|
+
}
|
|
2957
|
+
}
|
|
2958
|
+
const envMatch = line.match(/process\.env\.([A-Z0-9_]+)/);
|
|
2959
|
+
if (envMatch) {
|
|
2960
|
+
const envName = envMatch[1];
|
|
2961
|
+
if (!envName.includes("_")) {
|
|
2962
|
+
issues.push({
|
|
2963
|
+
file: filePath,
|
|
2964
|
+
line: lineNum,
|
|
2965
|
+
severity: "warning",
|
|
2966
|
+
rule: "matcha/env-prefix",
|
|
2967
|
+
message: `Environment variable "${envName}" doesn't seem to have a prefix`,
|
|
2968
|
+
suggestion: "Use an APPNAME_ prefix for environment variables"
|
|
2969
|
+
});
|
|
2970
|
+
}
|
|
2971
|
+
}
|
|
2972
|
+
}
|
|
2973
|
+
const interfaceNames = [];
|
|
2974
|
+
for (let i = 0; i < lines.length; i++) {
|
|
2975
|
+
const m = lines[i].match(/^\s*(export\s+)?interface\s+(\w+)/);
|
|
2976
|
+
if (m) interfaceNames.push(m[2]);
|
|
2977
|
+
}
|
|
2978
|
+
for (const iface of interfaceNames) {
|
|
2979
|
+
const implCount = (text.match(new RegExp(`implements\\s+.*\\b${iface}\\b`, "g")) || []).length;
|
|
2980
|
+
if (implCount <= 1) {
|
|
2981
|
+
const lineNum = lines.findIndex((l) => l.includes(`interface ${iface}`)) + 1;
|
|
2982
|
+
issues.push({
|
|
2983
|
+
file: filePath,
|
|
2984
|
+
line: lineNum,
|
|
2985
|
+
severity: "info",
|
|
2986
|
+
rule: "matcha/no-premature-abstraction",
|
|
2987
|
+
message: `Interface "${iface}" has only 1 implementation \u2014 premature abstraction?`,
|
|
2988
|
+
suggestion: "Wait for a 2nd use case before abstracting"
|
|
2989
|
+
});
|
|
2990
|
+
}
|
|
2991
|
+
}
|
|
2992
|
+
const funcBodies = findBlockBodies(lines, /^\s*(export\s+)?(async\s+)?function\s+\w+/);
|
|
2993
|
+
for (const { body, nameLine, name } of funcBodies) {
|
|
2994
|
+
const nonEmptyLines = body.split("\n").filter((l) => l.trim()).length;
|
|
2995
|
+
if (nonEmptyLines > 40) {
|
|
2996
|
+
issues.push({
|
|
2997
|
+
file: filePath,
|
|
2998
|
+
line: nameLine,
|
|
2999
|
+
severity: "warning",
|
|
3000
|
+
rule: "matcha/single-responsibility",
|
|
3001
|
+
message: `Function "${name}" is ${nonEmptyLines} lines long \u2014 might be doing >1 thing`,
|
|
3002
|
+
suggestion: "Extract into smaller, focused functions"
|
|
3003
|
+
});
|
|
3004
|
+
}
|
|
3005
|
+
}
|
|
3006
|
+
}
|
|
2936
3007
|
function formatReviewOutput(issues, filesReviewed, totalFiles, focus, customCriteria, autoDetected) {
|
|
2937
3008
|
const errors = issues.filter((i) => i.severity === "error");
|
|
2938
3009
|
const warnings = issues.filter((i) => i.severity === "warning");
|
|
@@ -5973,9 +6044,9 @@ function handleCodewhaleSecondary(root, results) {
|
|
|
5973
6044
|
});
|
|
5974
6045
|
}
|
|
5975
6046
|
}
|
|
5976
|
-
function runInit(
|
|
5977
|
-
const root = projectRoot ?? getProjectRoot();
|
|
5978
|
-
const selected = types.length > 0 ? types : ALL_CONFIG_TYPES;
|
|
6047
|
+
function runInit(options) {
|
|
6048
|
+
const root = options.projectRoot ?? getProjectRoot();
|
|
6049
|
+
const selected = options.types.length > 0 ? options.types : ALL_CONFIG_TYPES;
|
|
5979
6050
|
const results = [];
|
|
5980
6051
|
const selectedSet = new Set(selected);
|
|
5981
6052
|
const agentsMdSelected = AGENTS_MD_TYPES.filter((t) => selectedSet.has(t));
|
|
@@ -5989,12 +6060,16 @@ function runInit(types, projectRoot) {
|
|
|
5989
6060
|
agentsMdHandled = true;
|
|
5990
6061
|
const combinedContent = getCombinedAgentsMd(new Set(agentsMdSelected));
|
|
5991
6062
|
if (fs16.existsSync(fullPath)) {
|
|
5992
|
-
|
|
5993
|
-
if (existingContent.includes("_Generated by Kuma MCP_")) {
|
|
6063
|
+
if (options.skipExisting) {
|
|
5994
6064
|
results.push({ type, filePath: relativePath, action: "skipped" });
|
|
5995
6065
|
} else {
|
|
5996
|
-
fs16.
|
|
5997
|
-
|
|
6066
|
+
const existingContent = fs16.readFileSync(fullPath, "utf-8");
|
|
6067
|
+
if (existingContent.includes("_Generated by Kuma MCP_")) {
|
|
6068
|
+
results.push({ type, filePath: relativePath, action: "skipped" });
|
|
6069
|
+
} else {
|
|
6070
|
+
fs16.writeFileSync(fullPath, existingContent.trimEnd() + "\n\n" + combinedContent, "utf-8");
|
|
6071
|
+
results.push({ type, filePath: relativePath, action: "appended" });
|
|
6072
|
+
}
|
|
5998
6073
|
}
|
|
5999
6074
|
} else {
|
|
6000
6075
|
const dir = path16.dirname(fullPath);
|
|
@@ -6011,6 +6086,10 @@ function runInit(types, projectRoot) {
|
|
|
6011
6086
|
} else {
|
|
6012
6087
|
const template = getTemplate();
|
|
6013
6088
|
if (fs16.existsSync(fullPath)) {
|
|
6089
|
+
if (options.skipExisting) {
|
|
6090
|
+
results.push({ type, filePath: relativePath, action: "skipped" });
|
|
6091
|
+
continue;
|
|
6092
|
+
}
|
|
6014
6093
|
const existingContent = fs16.readFileSync(fullPath, "utf-8");
|
|
6015
6094
|
if (existingContent.includes("_Generated by Kuma MCP_")) {
|
|
6016
6095
|
if (type === "antigravity") {
|
|
@@ -6110,6 +6189,8 @@ Usage:
|
|
|
6110
6189
|
npx @plumpslabs/kuma Start MCP server (default)
|
|
6111
6190
|
npx @plumpslabs/kuma init Generate AI agent config files
|
|
6112
6191
|
npx @plumpslabs/kuma init --all Generate ALL config files
|
|
6192
|
+
npx @plumpslabs/kuma init --merge Append to existing files (default)
|
|
6193
|
+
npx @plumpslabs/kuma init --skip-existing Skip generation if file exists
|
|
6113
6194
|
npx @plumpslabs/kuma init --claude --cursor Generate specific files
|
|
6114
6195
|
npx @plumpslabs/kuma init --help Show this help
|
|
6115
6196
|
|
|
@@ -6183,9 +6264,20 @@ async function main() {
|
|
|
6183
6264
|
}
|
|
6184
6265
|
}
|
|
6185
6266
|
}
|
|
6186
|
-
const
|
|
6267
|
+
const skipExisting = requestedFlags.includes("--skip-existing");
|
|
6268
|
+
const merge = requestedFlags.includes("--merge");
|
|
6269
|
+
const results = runInit({ types: selectedTypes, projectRoot: process.cwd(), skipExisting });
|
|
6187
6270
|
const output = formatInitResults(results);
|
|
6188
6271
|
console.log(output);
|
|
6272
|
+
const fs17 = await import("fs");
|
|
6273
|
+
const path17 = await import("path");
|
|
6274
|
+
const matchaSkills = path17.resolve(process.cwd(), "skills/matcha/SKILL.md");
|
|
6275
|
+
const matchaAgents = path17.resolve(process.cwd(), ".agents/skills/matcha/SKILL.md");
|
|
6276
|
+
const matchaCursor = path17.resolve(process.cwd(), ".cursor/rules/matcha.mdc");
|
|
6277
|
+
const matchaWindsurf = path17.resolve(process.cwd(), ".windsurf/rules/matcha.md");
|
|
6278
|
+
if (fs17.existsSync(matchaSkills) || fs17.existsSync(matchaAgents) || fs17.existsSync(matchaCursor) || fs17.existsSync(matchaWindsurf)) {
|
|
6279
|
+
console.error("\n\u{1F375} Hey, I see matcha is installed \u2014 they pair well together!");
|
|
6280
|
+
}
|
|
6189
6281
|
process.exit(0);
|
|
6190
6282
|
}
|
|
6191
6283
|
sessionMemory.init({
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@plumpslabs/kuma",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.3",
|
|
4
4
|
"description": "Zero-setup safety toolkit for AI coding agents. MCP server with code review, git analysis, file editing, session memory, and static analysis — works with Claude Code, Cursor, Gemini CLI.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
"git-diff",
|
|
57
57
|
"cli"
|
|
58
58
|
],
|
|
59
|
-
"repository": {
|
|
59
|
+
"repository": {
|
|
60
60
|
"type": "git",
|
|
61
61
|
"url": "git+https://github.com/plumpslabs/kuma.git"
|
|
62
62
|
},
|
|
@@ -65,4 +65,4 @@
|
|
|
65
65
|
},
|
|
66
66
|
"homepage": "https://github.com/plumpslabs/kuma#readme",
|
|
67
67
|
"license": "MIT"
|
|
68
|
-
}
|
|
68
|
+
}
|