mirror of
https://github.com/vercel/vercel-plugin.git
synced 2026-09-14 15:39:47 +08:00
266f79d026
The orchestrator now has to treat skill bodies as external, cacheable state so projects stay clean and the plugin can ship without bundled skill content. This keeps runtime behavior aligned with the zero-bundle design, makes first-session installs happen against the hashed home-state cache, and prevents prompts and status banners from sending users back to the retired project-local .skills flow. The test updates also lock cache-dependent behavior to an isolated plugin home so verification exercises the hashed cache contract instead of ambient machine state. That keeps the migration reliable across environments and catches regressions in the new cache resolution model. Ploop-Iter: 3
106 lines
3.4 KiB
JavaScript
106 lines
3.4 KiB
JavaScript
#!/usr/bin/env node
|
|
|
|
// hooks/src/inject-claude-md.mts
|
|
import { readFileSync } from "fs";
|
|
import { join, resolve } from "path";
|
|
import { fileURLToPath } from "url";
|
|
import { formatOutput } from "./compat.mjs";
|
|
import { pluginRoot, safeReadFile } from "./hook-env.mjs";
|
|
import { createSkillStore } from "./skill-store.mjs";
|
|
var GREENFIELD_CONTEXT = `<!-- vercel-plugin:greenfield-execution -->
|
|
## Greenfield execution mode
|
|
|
|
This directory is empty.
|
|
Do not stop in planning mode or spin up a read-only planning subagent.
|
|
Choose sensible defaults immediately.
|
|
Start executing with real tool calls.
|
|
Use non-interactive scaffolding commands (--yes) where available.
|
|
Only ask follow-up questions when blocked by missing credentials or irreversible decisions.`;
|
|
function parseInjectClaudeMdInput(raw) {
|
|
try {
|
|
if (!raw.trim()) return null;
|
|
return JSON.parse(raw);
|
|
} catch {
|
|
return null;
|
|
}
|
|
}
|
|
function detectInjectClaudeMdPlatform(input, _env = process.env) {
|
|
if (input && ("conversation_id" in input || "cursor_version" in input)) {
|
|
return "cursor";
|
|
}
|
|
return "claude-code";
|
|
}
|
|
function buildInjectClaudeMdParts(content, env = process.env, knowledgeUpdate = null) {
|
|
const parts = [];
|
|
if (content !== null) {
|
|
parts.push(content);
|
|
}
|
|
if (knowledgeUpdate !== null) {
|
|
parts.push(knowledgeUpdate);
|
|
}
|
|
if (env.VERCEL_PLUGIN_GREENFIELD === "true") {
|
|
parts.push(GREENFIELD_CONTEXT);
|
|
}
|
|
return parts;
|
|
}
|
|
function formatInjectClaudeMdOutput(platform, content) {
|
|
if (platform === "cursor") {
|
|
return JSON.stringify(formatOutput(platform, { additionalContext: content }));
|
|
}
|
|
return content;
|
|
}
|
|
function debugInjectClaudeMd(event, data) {
|
|
if (process.env.VERCEL_PLUGIN_DEBUG !== "1") return;
|
|
process.stderr.write(`${JSON.stringify({ event, ...data })}
|
|
`);
|
|
}
|
|
function loadKnowledgeUpdate(projectRoot) {
|
|
const store = createSkillStore({
|
|
projectRoot,
|
|
pluginRoot: pluginRoot()
|
|
});
|
|
const payload = store.resolveSkillPayload("knowledge-update");
|
|
if (!payload) {
|
|
debugInjectClaudeMd("inject-claude-md-knowledge-update", {
|
|
mode: "missing",
|
|
projectRoot
|
|
});
|
|
return null;
|
|
}
|
|
debugInjectClaudeMd("inject-claude-md-knowledge-update", {
|
|
mode: payload.mode,
|
|
source: payload.source,
|
|
projectRoot
|
|
});
|
|
if (payload.mode === "body" && payload.body) {
|
|
return payload.body.trim();
|
|
}
|
|
const lines = [
|
|
payload.summary.trim() !== "" ? `Summary: ${payload.summary.trim()}` : null,
|
|
payload.docs.length > 0 ? `Docs: ${payload.docs.join(", ")}` : null
|
|
].filter((line) => Boolean(line));
|
|
return lines.length > 0 ? lines.join("\n") : null;
|
|
}
|
|
function main() {
|
|
const input = parseInjectClaudeMdInput(readFileSync(0, "utf8"));
|
|
const platform = detectInjectClaudeMdPlatform(input);
|
|
const knowledgeUpdate = loadKnowledgeUpdate(process.cwd());
|
|
const parts = buildInjectClaudeMdParts(safeReadFile(join(pluginRoot(), "vercel.md")), process.env, knowledgeUpdate);
|
|
if (parts.length === 0) {
|
|
return;
|
|
}
|
|
process.stdout.write(formatInjectClaudeMdOutput(platform, parts.join("\n\n")));
|
|
}
|
|
var INJECT_CLAUDE_MD_ENTRYPOINT = fileURLToPath(import.meta.url);
|
|
var isInjectClaudeMdEntrypoint = process.argv[1] ? resolve(process.argv[1]) === INJECT_CLAUDE_MD_ENTRYPOINT : false;
|
|
if (isInjectClaudeMdEntrypoint) {
|
|
main();
|
|
}
|
|
export {
|
|
buildInjectClaudeMdParts,
|
|
detectInjectClaudeMdPlatform,
|
|
formatInjectClaudeMdOutput,
|
|
loadKnowledgeUpdate,
|
|
parseInjectClaudeMdInput
|
|
};
|