mirror of
https://github.com/vercel/vercel-plugin.git
synced 2026-09-14 15:39:47 +08:00
8e24d0694a
Add a chainTo frontmatter field to skills that triggers follow-up skill injection when PostToolUse file contents match regex patterns. Add upgradeToSkill/upgradeWhy fields to validation rules so validate errors can recommend loading a specific skill. Register posttooluse-bash-chain.mjs in hooks.json. Add ChainToRule interface to patterns.mts and skill-map-frontmatter.mts, with parseChainToRules() parser. Add duplicate-key detection to the YAML parser. Reset dedup claim dir and session file on clear/compact events in session-start-seen-skills so skills re-inject into fresh context windows. Add chainTo rules to: agent-browser-verify, agent-browser, ai-elements, ai-gateway, ai-generation-persistence, ai-sdk, auth, bootstrap, chat-sdk, cms, cron-jobs, deployments-cicd, email, env-vars, geist, investigation-mode, json-render, marketplace, micro, ncc, next-forge, nextjs, observability, payments, react-best-practices, routing-middleware, runtime-cache, satori, shadcn, sign-in-with-vercel, swr, turbopack, turborepo, v0-dev, vercel-agent, vercel-api, vercel-cli, vercel-firewall, vercel-flags, vercel-functions, vercel-queues, vercel-sandbox, vercel-storage, verification, workflow. Add upgradeToSkill to ai-elements and ai-sdk validate rules. Expand ai-sdk validate messages with Run Skill() hints. Update nextjs, vercel-storage, runtime-cache, workflow, turborepo skill bodies. Add new skills: geistdocs (Geist design system docs), zzz-test-meta-name-mask (test fixture). Add skills/_chain-audit.md chain coverage audit doc. Delete .claude-plugin/marketplace.json, .claude-plugin/plugin.json (deprecated), skills/edge-runtime/SKILL.md (consolidated into vercel-functions). Add tests: posttooluse-chain.test.ts (4699 lines, chain injection e2e), ai-sdk-companion.test.ts (181 lines). Expand build-skill-map.test.ts (+335 lines), validate-rules.test.ts (+936 lines), session-start-seen-skills.test.ts (+74 lines), skill-map-frontmatter.test.ts (+50 lines), verification-skill.test.ts (+20 lines). Update build-manifest.ts to emit chainTo rules and upgradeToSkill fields. Rebuild generated/skill-manifest.json, generated/skill-catalog.md, generated/build-from-skills.manifest.json. Rebuild all compiled hooks/*.mjs. Update CLAUDE.md lexical prompt default to on. Update vercel.md ecosystem graph, docs, and cli-reference.
300 lines
5.1 KiB
JavaScript
300 lines
5.1 KiB
JavaScript
// hooks/src/stemmer.mts
|
|
var EXCEPTIONS_ING = /* @__PURE__ */ new Set([
|
|
"ring",
|
|
"king",
|
|
"thing",
|
|
"string",
|
|
"bring",
|
|
"sing",
|
|
"spring",
|
|
"swing",
|
|
"sting",
|
|
"cling",
|
|
"fling",
|
|
"sling",
|
|
"wring",
|
|
"during",
|
|
"ceiling",
|
|
"being",
|
|
"nothing",
|
|
"something",
|
|
"anything",
|
|
"everything",
|
|
"morning",
|
|
"evening",
|
|
"ping",
|
|
"bing",
|
|
"wing",
|
|
"ding",
|
|
"ming"
|
|
]);
|
|
var EXCEPTIONS_ED = /* @__PURE__ */ new Set([
|
|
"bed",
|
|
"red",
|
|
"fed",
|
|
"led",
|
|
"shed",
|
|
"wed",
|
|
"sled",
|
|
"bled",
|
|
"bred",
|
|
"fled",
|
|
"shred",
|
|
"sped",
|
|
"need",
|
|
"seed",
|
|
"feed",
|
|
"speed",
|
|
"indeed"
|
|
]);
|
|
var EXCEPTIONS_TION = /* @__PURE__ */ new Set([
|
|
"mention",
|
|
"question",
|
|
"caution",
|
|
"portion",
|
|
"section",
|
|
"notion",
|
|
"potion",
|
|
"fashion",
|
|
"bastion",
|
|
"function",
|
|
"junction",
|
|
"auction"
|
|
]);
|
|
var EXCEPTIONS_MENT = /* @__PURE__ */ new Set([
|
|
"element",
|
|
"comment",
|
|
"moment",
|
|
"segment",
|
|
"fragment",
|
|
"garment",
|
|
"cement",
|
|
"ament",
|
|
"torment",
|
|
"ferment",
|
|
"lament"
|
|
]);
|
|
var EXCEPTIONS_LY = /* @__PURE__ */ new Set([
|
|
"only",
|
|
"early",
|
|
"family",
|
|
"apply",
|
|
"reply",
|
|
"supply",
|
|
"daily",
|
|
"holy",
|
|
"rely",
|
|
"ugly",
|
|
"ally",
|
|
"bully",
|
|
"rally",
|
|
"tally",
|
|
"belly",
|
|
"jelly",
|
|
"folly",
|
|
"jolly",
|
|
"lily",
|
|
"fly",
|
|
"july",
|
|
"sly",
|
|
"ply",
|
|
"italy",
|
|
"anomaly"
|
|
]);
|
|
var EXCEPTIONS_ER = /* @__PURE__ */ new Set([
|
|
"user",
|
|
"server",
|
|
"number",
|
|
"member",
|
|
"remember",
|
|
"other",
|
|
"never",
|
|
"ever",
|
|
"over",
|
|
"under",
|
|
"after",
|
|
"water",
|
|
"order",
|
|
"power",
|
|
"paper",
|
|
"later",
|
|
"letter",
|
|
"center",
|
|
"computer",
|
|
"however",
|
|
"together",
|
|
"matter",
|
|
"layer",
|
|
"player",
|
|
"river",
|
|
"silver",
|
|
"finger",
|
|
"wonder",
|
|
"cluster",
|
|
"trigger",
|
|
"docker",
|
|
"buffer",
|
|
"folder",
|
|
"header",
|
|
"footer",
|
|
"render",
|
|
"handler",
|
|
"parser",
|
|
"router",
|
|
"worker",
|
|
"builder",
|
|
"loader",
|
|
"adapter",
|
|
"wrapper",
|
|
"container",
|
|
"parameter",
|
|
"character",
|
|
"manager",
|
|
"provider",
|
|
"consumer",
|
|
"producer",
|
|
"observer",
|
|
"resolver",
|
|
"scheduler",
|
|
"dispatcher",
|
|
"controller",
|
|
"middleware",
|
|
"whatever",
|
|
"whenever",
|
|
"wherever",
|
|
"either",
|
|
"neither",
|
|
"rather",
|
|
"whether",
|
|
"another",
|
|
"enter",
|
|
"offer",
|
|
"differ",
|
|
"suffer",
|
|
"prefer",
|
|
"refer",
|
|
"transfer",
|
|
"master",
|
|
"monster",
|
|
"register",
|
|
"consider",
|
|
"discover",
|
|
"deliver",
|
|
"cover",
|
|
"recover",
|
|
"flutter",
|
|
"twitter",
|
|
"inner",
|
|
"outer",
|
|
"upper",
|
|
"lower",
|
|
"proper",
|
|
"super",
|
|
"fiber",
|
|
"timber",
|
|
"cider",
|
|
"spider",
|
|
"cyber"
|
|
]);
|
|
var EXCEPTIONS_EST = /* @__PURE__ */ new Set([
|
|
"test",
|
|
"best",
|
|
"rest",
|
|
"request",
|
|
"nest",
|
|
"west",
|
|
"quest",
|
|
"jest",
|
|
"fest",
|
|
"zest",
|
|
"chest",
|
|
"guest",
|
|
"forest",
|
|
"interest",
|
|
"suggest",
|
|
"manifest",
|
|
"contest",
|
|
"protest",
|
|
"harvest",
|
|
"invest",
|
|
"digest",
|
|
"modest",
|
|
"honest",
|
|
"earnest",
|
|
"vitest",
|
|
"latest"
|
|
]);
|
|
var EXCEPTIONS_NESS = /* @__PURE__ */ new Set([
|
|
"business",
|
|
"less",
|
|
"ness"
|
|
]);
|
|
var KEEP_DOUBLED = /* @__PURE__ */ new Set(["ll", "ss", "ff", "zz"]);
|
|
var POST_STEM_MAP = {
|
|
cach: "cache",
|
|
rout: "route",
|
|
styl: "style",
|
|
revalidat: "revalidate",
|
|
configur: "configure",
|
|
configura: "configure",
|
|
optimiz: "optimize",
|
|
optimiza: "optimize",
|
|
authentica: "authenticate",
|
|
schedul: "schedule",
|
|
initializ: "initialize",
|
|
serializ: "serialize",
|
|
deserializ: "deserialize",
|
|
customiz: "customize",
|
|
synchroniz: "synchronize"
|
|
};
|
|
function undouble(stem) {
|
|
if (stem.length < 3) return stem;
|
|
const last = stem[stem.length - 1];
|
|
const prev = stem[stem.length - 2];
|
|
if (last === prev && /[bcdfghjklmnpqrstvwxyz]/.test(last)) {
|
|
const pair = `${prev}${last}`;
|
|
if (!KEEP_DOUBLED.has(pair)) {
|
|
return stem.slice(0, -1);
|
|
}
|
|
}
|
|
return stem;
|
|
}
|
|
var MIN_STEM = 3;
|
|
function stemToken(word) {
|
|
if (word.length < 4) return word;
|
|
let stem = null;
|
|
if (word.endsWith("tion") && word.length >= MIN_STEM + 4) {
|
|
if (!EXCEPTIONS_TION.has(word)) stem = word.slice(0, -4);
|
|
}
|
|
if (stem === null && word.endsWith("ment") && word.length >= MIN_STEM + 4) {
|
|
if (!EXCEPTIONS_MENT.has(word)) stem = word.slice(0, -4);
|
|
}
|
|
if (stem === null && word.endsWith("ness") && word.length >= MIN_STEM + 4) {
|
|
if (!EXCEPTIONS_NESS.has(word)) stem = word.slice(0, -4);
|
|
}
|
|
if (stem === null && word.endsWith("ing") && word.length >= MIN_STEM + 3) {
|
|
if (!EXCEPTIONS_ING.has(word)) stem = undouble(word.slice(0, -3));
|
|
}
|
|
if (stem === null && word.endsWith("est") && word.length >= MIN_STEM + 3) {
|
|
if (!EXCEPTIONS_EST.has(word)) stem = undouble(word.slice(0, -3));
|
|
}
|
|
if (stem === null && word.endsWith("ed") && word.length >= MIN_STEM + 2) {
|
|
if (!EXCEPTIONS_ED.has(word)) stem = undouble(word.slice(0, -2));
|
|
}
|
|
if (stem === null && word.endsWith("ly") && word.length >= 4 + 2) {
|
|
if (!EXCEPTIONS_LY.has(word)) stem = word.slice(0, -2);
|
|
}
|
|
if (stem === null && word.endsWith("er") && word.length >= 4 + 2) {
|
|
if (!EXCEPTIONS_ER.has(word)) stem = undouble(word.slice(0, -2));
|
|
}
|
|
if (stem === null) return word;
|
|
return POST_STEM_MAP[stem] ?? stem;
|
|
}
|
|
function stemText(text) {
|
|
return text.replace(/[a-z]+/g, (match) => stemToken(match));
|
|
}
|
|
export {
|
|
stemText,
|
|
stemToken
|
|
};
|