@vibekiln/cutline-mcp-cli 0.8.0 → 0.9.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.
- package/dist/servers/cutline-server.js +165 -10
- package/package.json +1 -1
|
@@ -2291,6 +2291,19 @@ function healBindings(entities, bindings, filePaths) {
|
|
|
2291
2291
|
total_entities: entities.length
|
|
2292
2292
|
};
|
|
2293
2293
|
}
|
|
2294
|
+
function findBoundEntities(filePath, bindings) {
|
|
2295
|
+
const normalized = filePath.replace(/\\/g, "/");
|
|
2296
|
+
const matched = /* @__PURE__ */ new Set();
|
|
2297
|
+
for (const binding of bindings) {
|
|
2298
|
+
for (const pattern of binding.file_patterns) {
|
|
2299
|
+
if (globMatch(normalized, pattern)) {
|
|
2300
|
+
matched.add(binding.entity_id);
|
|
2301
|
+
break;
|
|
2302
|
+
}
|
|
2303
|
+
}
|
|
2304
|
+
}
|
|
2305
|
+
return [...matched];
|
|
2306
|
+
}
|
|
2294
2307
|
function tokenize(name2) {
|
|
2295
2308
|
return name2.toLowerCase().replace(/[^a-z0-9\s]/g, " ").split(/\s+/).filter((t) => t.length > 2);
|
|
2296
2309
|
}
|
|
@@ -4442,6 +4455,17 @@ var UNIVERSAL_CONSTRAINTS = [
|
|
|
4442
4455
|
file_patterns: ["**/auth/**", "**/api/session*", "**/api/login*", "**/middleware/**", "**/config/**"],
|
|
4443
4456
|
framework: "baseline"
|
|
4444
4457
|
},
|
|
4458
|
+
{
|
|
4459
|
+
id_suffix: "password_reset_token_expiry",
|
|
4460
|
+
category: "security",
|
|
4461
|
+
summary: "Password reset tokens/links MUST be single-use and time-limited. Expired or reused reset tokens must fail closed.",
|
|
4462
|
+
keywords: ["password-reset", "token", "expiry", "single-use", "account-takeover"],
|
|
4463
|
+
severity: "critical",
|
|
4464
|
+
action: "Enforce reset token TTL and one-time use semantics. Invalidate outstanding reset tokens after successful password change.",
|
|
4465
|
+
checklist_ref: "A10",
|
|
4466
|
+
file_patterns: ["**/auth/**", "**/api/reset*", "**/api/forgot*", "**/api/password*"],
|
|
4467
|
+
framework: "baseline"
|
|
4468
|
+
},
|
|
4445
4469
|
{
|
|
4446
4470
|
id_suffix: "backup_testing",
|
|
4447
4471
|
category: "stability",
|
|
@@ -4687,6 +4711,83 @@ var RELIABILITY_CONSTRAINTS = [
|
|
|
4687
4711
|
checklist_ref: "J10",
|
|
4688
4712
|
file_patterns: ["**/utils/**", "**/lib/**", "**/services/**", "**/api/**"],
|
|
4689
4713
|
framework: "baseline"
|
|
4714
|
+
},
|
|
4715
|
+
{
|
|
4716
|
+
id_suffix: "startup_env_schema_validation",
|
|
4717
|
+
category: "stability",
|
|
4718
|
+
summary: "Runtime environment variables MUST be validated against an explicit schema at startup, and app boot must fail fast on invalid critical config.",
|
|
4719
|
+
keywords: ["env", "startup", "schema", "validation", "fail-fast", "configuration"],
|
|
4720
|
+
severity: "critical",
|
|
4721
|
+
action: "Create startup env schema checks for server and public runtime variables. Crash startup when required production config is missing or malformed.",
|
|
4722
|
+
checklist_ref: "J11",
|
|
4723
|
+
file_patterns: ["**/config/**", "**/env/**", "**/server/**", "**/.env*"],
|
|
4724
|
+
framework: "baseline"
|
|
4725
|
+
},
|
|
4726
|
+
{
|
|
4727
|
+
id_suffix: "ui_error_boundaries",
|
|
4728
|
+
category: "stability",
|
|
4729
|
+
summary: "Critical user-facing UI surfaces MUST be wrapped in error boundaries to prevent full-app white screens during component crashes.",
|
|
4730
|
+
keywords: ["error-boundary", "react", "ui", "crash", "fallback", "reliability"],
|
|
4731
|
+
severity: "warning",
|
|
4732
|
+
action: "Add error boundaries around major routes/layouts and high-risk widgets. Provide fallback UI and telemetry when boundaries catch errors.",
|
|
4733
|
+
checklist_ref: "J12",
|
|
4734
|
+
file_patterns: ["**/components/**", "**/pages/**", "**/app/**", "**/*error*"],
|
|
4735
|
+
framework: "baseline"
|
|
4736
|
+
},
|
|
4737
|
+
{
|
|
4738
|
+
id_suffix: "health_readiness_endpoints",
|
|
4739
|
+
category: "stability",
|
|
4740
|
+
summary: "Services MUST expose dedicated liveness and readiness endpoints (e.g., /api/health and /api/readyz) for monitoring and deployment safety checks.",
|
|
4741
|
+
keywords: ["health", "readyz", "liveness", "readiness", "monitoring", "probe"],
|
|
4742
|
+
severity: "critical",
|
|
4743
|
+
action: "Implement lightweight health/readiness endpoints with no sensitive payload data. Integrate endpoints into uptime monitoring and deployment probes.",
|
|
4744
|
+
checklist_ref: "J13",
|
|
4745
|
+
file_patterns: ["**/api/health*", "**/api/ready*", "**/monitoring/**", "**/deploy/**"],
|
|
4746
|
+
framework: "baseline"
|
|
4747
|
+
},
|
|
4748
|
+
{
|
|
4749
|
+
id_suffix: "structured_production_logging",
|
|
4750
|
+
category: "stability",
|
|
4751
|
+
summary: "Production logging MUST be structured and include correlation/request IDs, with automatic redaction of tokens, API keys, and credentials.",
|
|
4752
|
+
keywords: ["logging", "structured", "correlation-id", "request-id", "redaction", "observability"],
|
|
4753
|
+
severity: "critical",
|
|
4754
|
+
action: "Emit JSON logs in production and propagate request/correlation IDs across handlers and background jobs. Apply secret-redaction middleware before log emission.",
|
|
4755
|
+
checklist_ref: "J14",
|
|
4756
|
+
file_patterns: ["**/logger/**", "**/api/**", "**/middleware/**", "**/monitoring/**"],
|
|
4757
|
+
framework: "baseline"
|
|
4758
|
+
},
|
|
4759
|
+
{
|
|
4760
|
+
id_suffix: "typed_ai_generated_code",
|
|
4761
|
+
category: "stability",
|
|
4762
|
+
summary: "AI-generated production code MUST use TypeScript (or equivalent static typing) and pass strict type-check gates before merge.",
|
|
4763
|
+
keywords: ["ai-generated", "typescript", "typing", "typecheck", "ci-gate", "quality"],
|
|
4764
|
+
severity: "warning",
|
|
4765
|
+
action: "Require strict type-check in CI for generated code changes and block merges on type errors. Prefer typed templates for agent-generated modules.",
|
|
4766
|
+
checklist_ref: "J15",
|
|
4767
|
+
file_patterns: ["**/*.ts", "**/*.tsx", "**/ai/**", "**/agents/**", "**/.github/**"],
|
|
4768
|
+
framework: "baseline"
|
|
4769
|
+
},
|
|
4770
|
+
{
|
|
4771
|
+
id_suffix: "async_email_dispatch",
|
|
4772
|
+
category: "performance",
|
|
4773
|
+
summary: "Transactional emails SHOULD be dispatched asynchronously (queue/background workers) so request handlers do not block on provider latency.",
|
|
4774
|
+
keywords: ["email", "async", "queue", "worker", "latency", "smtp", "request-path"],
|
|
4775
|
+
severity: "warning",
|
|
4776
|
+
action: "Move email delivery to async jobs/queues and return request responses before provider completion. Add retry/backoff for transient send failures.",
|
|
4777
|
+
checklist_ref: "J16",
|
|
4778
|
+
file_patterns: ["**/api/**", "**/jobs/**", "**/workers/**", "**/email/**", "**/notifications/**"],
|
|
4779
|
+
framework: "baseline"
|
|
4780
|
+
},
|
|
4781
|
+
{
|
|
4782
|
+
id_suffix: "cdn_media_delivery",
|
|
4783
|
+
category: "performance",
|
|
4784
|
+
summary: "User-uploaded media MUST be stored in object storage and delivered through CDN caching, not served directly from app servers.",
|
|
4785
|
+
keywords: ["cdn", "media", "uploads", "object-storage", "cache", "bandwidth"],
|
|
4786
|
+
severity: "warning",
|
|
4787
|
+
action: "Store uploads in object storage (S3/GCS/etc.) and serve via CDN URLs with cache headers. Keep app servers out of large media delivery paths.",
|
|
4788
|
+
checklist_ref: "J17",
|
|
4789
|
+
file_patterns: ["**/api/upload*", "**/storage/**", "**/media/**", "**/cdn/**", "**/config/**"],
|
|
4790
|
+
framework: "baseline"
|
|
4690
4791
|
}
|
|
4691
4792
|
];
|
|
4692
4793
|
var IAC_CONSTRAINTS = [
|
|
@@ -7489,6 +7590,59 @@ function inferEntityNameFromTask(task) {
|
|
|
7489
7590
|
function normalizeName(value) {
|
|
7490
7591
|
return value.toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_|_$/g, "");
|
|
7491
7592
|
}
|
|
7593
|
+
function canonicalizeBindings(bindings, entities) {
|
|
7594
|
+
const entityTypes = new Map(entities.map((e) => [e.id, e.type]));
|
|
7595
|
+
const normalized = [];
|
|
7596
|
+
for (const raw of bindings) {
|
|
7597
|
+
const legacyRaw = raw;
|
|
7598
|
+
const entityId = typeof raw.entity_id === "string" ? raw.entity_id : typeof legacyRaw.entityId === "string" ? legacyRaw.entityId : void 0;
|
|
7599
|
+
if (!entityId || !entityTypes.has(entityId))
|
|
7600
|
+
continue;
|
|
7601
|
+
if (!Array.isArray(raw.file_patterns) || raw.file_patterns.length === 0)
|
|
7602
|
+
continue;
|
|
7603
|
+
const filePatterns = raw.file_patterns.filter((v) => typeof v === "string");
|
|
7604
|
+
if (filePatterns.length === 0)
|
|
7605
|
+
continue;
|
|
7606
|
+
normalized.push({
|
|
7607
|
+
id: typeof raw.id === "string" ? raw.id : `bind:${entityId}`,
|
|
7608
|
+
entity_id: entityId,
|
|
7609
|
+
entity_type: typeof raw.entity_type === "string" ? raw.entity_type : entityTypes.get(entityId),
|
|
7610
|
+
file_patterns: filePatterns,
|
|
7611
|
+
confidence: typeof raw.confidence === "number" ? raw.confidence : 0.5,
|
|
7612
|
+
manual: Boolean(raw.manual)
|
|
7613
|
+
});
|
|
7614
|
+
}
|
|
7615
|
+
return normalized;
|
|
7616
|
+
}
|
|
7617
|
+
function rankEntitiesForFilePath(filePath, entities) {
|
|
7618
|
+
const normalizedPath = filePath.toLowerCase().replace(/\\/g, "/");
|
|
7619
|
+
const fileStem = normalizedPath.split("/").pop()?.replace(/\.[^.]+$/, "") ?? "";
|
|
7620
|
+
const pathTokens = new Set(normalizedPath.replace(/\.[^.]+$/, "").split(/[\/._-]/).filter((t) => t.length >= 3));
|
|
7621
|
+
const scored = entities.map((entity) => {
|
|
7622
|
+
const labels = [entity.name, ...entity.aliases].filter(Boolean);
|
|
7623
|
+
if (labels.length === 0)
|
|
7624
|
+
return { entity, score: 0 };
|
|
7625
|
+
let score = 0;
|
|
7626
|
+
for (const label of labels) {
|
|
7627
|
+
const labelNorm = normalizeName(label);
|
|
7628
|
+
const labelTokens = labelNorm.split("_").filter((t) => t.length >= 3);
|
|
7629
|
+
if (labelTokens.length === 0)
|
|
7630
|
+
continue;
|
|
7631
|
+
const matchedTokens = labelTokens.filter((t) => pathTokens.has(t)).length;
|
|
7632
|
+
if (matchedTokens > 0) {
|
|
7633
|
+
score = Math.max(score, matchedTokens / labelTokens.length);
|
|
7634
|
+
}
|
|
7635
|
+
if (normalizedPath.includes(labelNorm.replace(/_/g, "/"))) {
|
|
7636
|
+
score = Math.max(score, 0.9);
|
|
7637
|
+
}
|
|
7638
|
+
if (fileStem === labelNorm || fileStem.includes(labelNorm) || labelNorm.includes(fileStem)) {
|
|
7639
|
+
score = Math.max(score, 0.8);
|
|
7640
|
+
}
|
|
7641
|
+
}
|
|
7642
|
+
return { entity, score };
|
|
7643
|
+
}).filter((item) => item.score >= 0.45).sort((a, b) => b.score - a.score);
|
|
7644
|
+
return scored.map((item) => item.entity);
|
|
7645
|
+
}
|
|
7492
7646
|
async function seedScopeEntityFromAuto(params) {
|
|
7493
7647
|
const { product_id, name: name2, entity_type, description, parent_id, tags, similarity_threshold } = params;
|
|
7494
7648
|
const slug = normalizeName(name2).slice(0, 40);
|
|
@@ -9557,7 +9711,6 @@ ${recommendation}`;
|
|
|
9557
9711
|
used_category_prefilter: usePreFilter,
|
|
9558
9712
|
phase: autoPhase,
|
|
9559
9713
|
rgr_plan: autoRgrPlan || void 0,
|
|
9560
|
-
requested_outcome: requestedOutcome,
|
|
9561
9714
|
scope_expansion: scopeExpansion,
|
|
9562
9715
|
scope_seeded: scopeSeedResult || void 0,
|
|
9563
9716
|
requested_outcome: requestedOutcome
|
|
@@ -10414,20 +10567,22 @@ ${JSON.stringify(metrics, null, 2)}` }
|
|
|
10414
10567
|
}]
|
|
10415
10568
|
};
|
|
10416
10569
|
}
|
|
10417
|
-
const
|
|
10570
|
+
const normalizedBindings = canonicalizeBindings(rgrBindings, rgrEntities);
|
|
10571
|
+
const rgrTraverser = new GraphTraverser(rgrEntities, rgrEdges, rgrConstraints, normalizedBindings);
|
|
10418
10572
|
const rgrMatched = rgrTraverser.findEntitiesByFilePath(file_path);
|
|
10419
10573
|
if (rgrMatched.length === 0) {
|
|
10420
|
-
const
|
|
10421
|
-
for (const
|
|
10422
|
-
|
|
10423
|
-
|
|
10424
|
-
|
|
10425
|
-
rgrMatched.push(found);
|
|
10426
|
-
break;
|
|
10427
|
-
}
|
|
10574
|
+
const reverseMatchedIds = findBoundEntities(file_path, normalizedBindings);
|
|
10575
|
+
for (const id of reverseMatchedIds) {
|
|
10576
|
+
const found = rgrEntities.find((entity) => entity.id === id);
|
|
10577
|
+
if (found) {
|
|
10578
|
+
rgrMatched.push(found);
|
|
10428
10579
|
}
|
|
10429
10580
|
}
|
|
10430
10581
|
}
|
|
10582
|
+
if (rgrMatched.length === 0) {
|
|
10583
|
+
const rankedFallback = rankEntitiesForFilePath(file_path, rgrEntities).slice(0, 2);
|
|
10584
|
+
rgrMatched.push(...rankedFallback);
|
|
10585
|
+
}
|
|
10431
10586
|
if (rgrMatched.length === 0) {
|
|
10432
10587
|
const governance2 = buildGovernanceEnvelope({
|
|
10433
10588
|
decision: "revise",
|
package/package.json
CHANGED