让创作节点声明并注入绑定库,并新增用户需求步承接偏好。
装载时同时注入正文安全子集;编排与对话落盘都能看见可用库条目。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -10,6 +10,10 @@ import { readFile } from "node:fs/promises";
|
||||
import path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { parse as parseYaml } from "yaml";
|
||||
import {
|
||||
collectLibraryIdsFromModules,
|
||||
formatBoundLibrariesForPrompt,
|
||||
} from "../libraries/index.js";
|
||||
import { tryParseJsonDoc } from "../parse/json-doc.js";
|
||||
|
||||
export const CREATION_FLOW_TAG = "设计.创作流程";
|
||||
@@ -211,6 +215,11 @@ export type ModuleCatalogEntry = {
|
||||
* recipe = 只进工序编排 DAG;dictate = 只进对话落盘何时落盘。
|
||||
*/
|
||||
intake?: RecipeIntakeFamily;
|
||||
/**
|
||||
* 可选:本步绑定的全局库 id(如 style-packs)。
|
||||
* design-step 执行时按注册表注入对应块;库空则跳过。
|
||||
*/
|
||||
libraries?: string[];
|
||||
/** 来自 prompt.md ```meta:何时该选用(编排选型) */
|
||||
when?: string;
|
||||
/** 来自 prompt.md ```meta:何时不该选用 */
|
||||
@@ -951,6 +960,7 @@ export function parseModuleCatalog(raw: string): ModuleCatalog | null {
|
||||
const kind = parseModuleNodeKind(m.kind);
|
||||
const params = parseModuleParamSpecs(m.params);
|
||||
const intake = parseModuleIntake(m.intake);
|
||||
const libraries = parseModuleLibraries(m.libraries);
|
||||
modules.push({
|
||||
id,
|
||||
name,
|
||||
@@ -963,6 +973,7 @@ export function parseModuleCatalog(raw: string): ModuleCatalog | null {
|
||||
...(opening ? { opening } : {}),
|
||||
...(params ? { params } : {}),
|
||||
...(intake ? { intake } : {}),
|
||||
...(libraries ? { libraries } : {}),
|
||||
});
|
||||
}
|
||||
if (modules.length === 0) return null;
|
||||
@@ -992,6 +1003,19 @@ function parseModuleParamSpecs(raw: unknown): ModuleParamSpec[] | undefined {
|
||||
return out.length > 0 ? out : undefined;
|
||||
}
|
||||
|
||||
function parseModuleLibraries(raw: unknown): string[] | undefined {
|
||||
if (!Array.isArray(raw) || raw.length === 0) return undefined;
|
||||
const ids: string[] = [];
|
||||
const seen = new Set<string>();
|
||||
for (const item of raw) {
|
||||
const id = typeof item === "string" ? item.trim() : "";
|
||||
if (!id || seen.has(id)) continue;
|
||||
seen.add(id);
|
||||
ids.push(id);
|
||||
}
|
||||
return ids.length ? ids : undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* 能力 prompt.md 可切割块(fence 语言标签 = 块 id)。
|
||||
* 标准块见 MODULE_SECTION_IDS;程序只认 ```id … ```,不认散文标题 alone。
|
||||
@@ -1373,7 +1397,8 @@ const DICTATE_PROGRAM_SIDE_MODULE_IDS = new Set([
|
||||
* 不写 DAG / design-step 话术;不塞执行全文。
|
||||
*/
|
||||
export function formatModuleCatalogForDictate(catalog: ModuleCatalog): string {
|
||||
const lines = catalogModulesForIntake(catalog, "dictate").map((m) => {
|
||||
const dictateModules = catalogModulesForIntake(catalog, "dictate");
|
||||
const lines = dictateModules.map((m) => {
|
||||
const programSide = DICTATE_PROGRAM_SIDE_MODULE_IDS.has(m.id) || m.auto;
|
||||
const flags = [
|
||||
m.repeatable ? "〔可反复〕" : "",
|
||||
@@ -1393,7 +1418,7 @@ export function formatModuleCatalogForDictate(catalog: ModuleCatalog): string {
|
||||
if (m.boundary) parts.push(` 边界:${indentMultiline(m.boundary, " ")}`);
|
||||
return parts.join("\n");
|
||||
});
|
||||
return [
|
||||
const head = [
|
||||
"【能力 · 何时落盘】",
|
||||
"对照下方「何时用 / 何时不用 / 边界」与已有产物判定:条件成立才 insert(或 declare_*);含糊或未到时机 → 本轮不落该能力,改为短确认或问挡住的 1 点。",
|
||||
"同轮可多次落盘:凡本轮材料已够且「何时用」成立的能力,都可 insert / declare_*(可并行);每个 insert 各回 reply_module,可见回复分段附带。",
|
||||
@@ -1404,6 +1429,10 @@ export function formatModuleCatalogForDictate(catalog: ModuleCatalog): string {
|
||||
"能力执行全文不进聊天;规格只走 toolcall。insert 成功后按返回的 reply_module 写可见回复(见系统提示「对用户可见回复」)。",
|
||||
lines.join("\n"),
|
||||
].join("\n");
|
||||
|
||||
const libIds = collectLibraryIdsFromModules(dictateModules);
|
||||
const libBlock = formatBoundLibrariesForPrompt(libIds).trim();
|
||||
return libBlock ? `${head}\n\n${libBlock}` : head;
|
||||
}
|
||||
|
||||
function dictateLandHint(m: ModuleCatalogEntry): string {
|
||||
|
||||
+27
-2
@@ -559,11 +559,36 @@ export async function loadWorkerSkillWithContext(
|
||||
skill.skillPackRoot,
|
||||
skillsRoot,
|
||||
);
|
||||
const parts: string[] = [];
|
||||
if (shells.length) {
|
||||
rosterBlock = `${formatPresentShellsForPrompt(shells)}\n\n`;
|
||||
parts.push(formatPresentShellsForPrompt(shells));
|
||||
}
|
||||
const {
|
||||
loadMarkdownSafeSubsetCatalog,
|
||||
formatMarkdownSafeSubsetForPrompt,
|
||||
} = await import("./markdown-safe-subset.js");
|
||||
const safeSubset = await loadMarkdownSafeSubsetCatalog(
|
||||
skill.skillPackRoot,
|
||||
skillsRoot,
|
||||
);
|
||||
if (safeSubset) {
|
||||
parts.push(formatMarkdownSafeSubsetForPrompt(safeSubset));
|
||||
}
|
||||
if (parts.length) {
|
||||
rosterBlock = `${parts.join("\n\n")}\n\n`;
|
||||
}
|
||||
}
|
||||
modulePromptBlock = `${rosterBlock}${paramsBlock}\n\n## 【本步方法 · ${binding.module.name}】\n\n${binding.modulePrompt.trim()}${openingNote}${inheritNote}`;
|
||||
let libraryBlock = "";
|
||||
if (binding.module.libraries?.length) {
|
||||
const { formatBoundLibrariesForPrompt } = await import(
|
||||
"../libraries/index.js"
|
||||
);
|
||||
const libText = formatBoundLibrariesForPrompt(
|
||||
binding.module.libraries,
|
||||
).trim();
|
||||
if (libText) libraryBlock = `${libText}\n\n`;
|
||||
}
|
||||
modulePromptBlock = `${rosterBlock}${libraryBlock}${paramsBlock}\n\n## 【本步方法 · ${binding.module.name}】\n\n${binding.modulePrompt.trim()}${openingNote}${inheritNote}`;
|
||||
const depTags = inheritTag
|
||||
? binding.depTags.filter((tag) => tag !== inheritTag)
|
||||
: binding.depTags;
|
||||
|
||||
Reference in New Issue
Block a user