@kyun.si/agent2cursor 0.0.0-stage → 0.1.1
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/CHANGELOG.md +12 -0
- package/CONTRIBUTING.md +92 -0
- package/LICENSE +21 -0
- package/README.md +230 -2
- package/README.zh-CN.md +231 -0
- package/THIRD_PARTY_NOTICES.md +11 -0
- package/demos/full-send/LICENSE-tstack +21 -0
- package/demos/full-send/README.md +47 -0
- package/demos/full-send/TSTACK-PROVENANCE.json +37 -0
- package/demos/full-send/bot/agent.ts +9 -0
- package/demos/full-send/bot/channels/github.ts +16 -0
- package/demos/full-send/bot/instructions.md +11 -0
- package/demos/full-send/bot/lib/merge-policy.ts +9 -0
- package/demos/full-send/bot/lib/settings.ts +2 -0
- package/demos/full-send/bot/skills/babysit-pr/SKILL.md +13 -0
- package/demos/full-send/bot/skills/babysit-pr/scripts/pr-wait +556 -0
- package/demos/full-send/bot/skills/file-pr/SKILL.md +11 -0
- package/demos/full-send/bot/skills/full-send/SKILL.md +19 -0
- package/demos/full-send/bot/skills/leaving-pr-comments/SKILL.md +14 -0
- package/demos/full-send/bot/skills/sol-loop/SKILL.md +11 -0
- package/demos/full-send/bot/subagents/reviewer/agent.ts +2 -0
- package/demos/full-send/bot/subagents/reviewer/instructions.md +3 -0
- package/demos/full-send/bot/tools/merge_pr.ts +23 -0
- package/demos/full-send/bot/tools/pr_wait.ts +24 -0
- package/demos/full-send/bot/tools/pr_wait_script.ts +9 -0
- package/demos/full-send/evals/evals.config.ts +2 -0
- package/demos/full-send/evals/full-send.eval.ts +5 -0
- package/demos/full-send/evals/skills.eval.ts +8 -0
- package/demos/full-send/package.json +18 -0
- package/demos/pr-review/LICENSE-tstack +21 -0
- package/demos/pr-review/README.md +44 -0
- package/demos/pr-review/TSTACK-PROVENANCE.json +37 -0
- package/demos/pr-review/bot/agent.ts +9 -0
- package/demos/pr-review/bot/channels/github.ts +22 -0
- package/demos/pr-review/bot/instructions.md +11 -0
- package/demos/pr-review/bot/lib/pr-snapshot.ts +92 -0
- package/demos/pr-review/bot/lib/settings.ts +2 -0
- package/demos/pr-review/bot/skills/council/SKILL.md +25 -0
- package/demos/pr-review/bot/skills/leaving-pr-comments/SKILL.md +14 -0
- package/demos/pr-review/bot/subagents/reviewer-a/agent.ts +2 -0
- package/demos/pr-review/bot/subagents/reviewer-a/instructions.md +3 -0
- package/demos/pr-review/bot/subagents/reviewer-b/agent.ts +2 -0
- package/demos/pr-review/bot/subagents/reviewer-b/instructions.md +3 -0
- package/demos/pr-review/bot/subagents/reviewer-c/agent.ts +2 -0
- package/demos/pr-review/bot/subagents/reviewer-c/instructions.md +3 -0
- package/demos/pr-review/bot/tools/pr_wait.ts +24 -0
- package/demos/pr-review/evals/evals.config.ts +2 -0
- package/demos/pr-review/evals/pr-review.eval.ts +13 -0
- package/demos/pr-review/evals/skills.eval.ts +5 -0
- package/demos/pr-review/package.json +18 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +142 -0
- package/dist/emit/common.d.ts +4 -0
- package/dist/emit/common.js +15 -0
- package/dist/emit/evals.d.ts +4 -0
- package/dist/emit/evals.js +33 -0
- package/dist/emit/project.d.ts +2 -0
- package/dist/emit/project.js +165 -0
- package/dist/emit/report.d.ts +2 -0
- package/dist/emit/report.js +74 -0
- package/dist/emit/subagent.d.ts +2 -0
- package/dist/emit/subagent.js +15 -0
- package/dist/emit/tool-stub.d.ts +3 -0
- package/dist/emit/tool-stub.js +29 -0
- package/dist/emit/tstack-license.d.ts +8 -0
- package/dist/emit/tstack-license.js +39 -0
- package/dist/ir.d.ts +66 -0
- package/dist/ir.js +1 -0
- package/dist/mapping/codex-agent.d.ts +2 -0
- package/dist/mapping/codex-agent.js +39 -0
- package/dist/mapping/codex-skill.d.ts +5 -0
- package/dist/mapping/codex-skill.js +32 -0
- package/dist/mapping/foreign-tools.d.ts +2 -0
- package/dist/mapping/foreign-tools.js +33 -0
- package/dist/mapping/model.d.ts +5 -0
- package/dist/mapping/model.js +14 -0
- package/dist/mapping/names.d.ts +2 -0
- package/dist/mapping/names.js +9 -0
- package/dist/mapping/permissions.d.ts +2 -0
- package/dist/mapping/permissions.js +21 -0
- package/dist/mapping/skill.d.ts +3 -0
- package/dist/mapping/skill.js +29 -0
- package/dist/mapping/subagent.d.ts +3 -0
- package/dist/mapping/subagent.js +60 -0
- package/dist/mapping/tools-names.d.ts +6 -0
- package/dist/mapping/tools-names.js +18 -0
- package/dist/scan/codex.d.ts +1 -0
- package/dist/scan/codex.js +19 -0
- package/dist/scan/frontmatter.d.ts +11 -0
- package/dist/scan/frontmatter.js +62 -0
- package/dist/scan/index.d.ts +2 -0
- package/dist/scan/index.js +192 -0
- package/dist/scan/safety.d.ts +11 -0
- package/dist/scan/safety.js +105 -0
- package/dist/scan/tstack.d.ts +9 -0
- package/dist/scan/tstack.js +42 -0
- package/dist/validate.d.ts +5 -0
- package/dist/validate.js +135 -0
- package/dist/write.d.ts +19 -0
- package/dist/write.js +134 -0
- package/docs/acceptance.md +102 -0
- package/docs/assets/agent2cursor-demo-poster.png +0 -0
- package/docs/assets/agent2cursor-demo.gif +0 -0
- package/docs/evidence/codex-adapter.json +133 -0
- package/docs/evidence/evaluation-fixes.json +184 -0
- package/docs/evidence/independent-evaluation.json +61 -0
- package/docs/evidence/npm-publication-0.1.0.json +46 -0
- package/docs/evidence/online-2026-10-27.json +112 -0
- package/docs/evidence/readme-audit-2026-10-28.json +248 -0
- package/docs/official-examples.provenance.json +26 -0
- package/docs/upstream-verification.md +67 -0
- package/evaluation/20261027-2216//345/244/215/346/265/213/347/273/223/350/256/272.md +48 -0
- package/evaluation/README.md +16 -0
- package/package.json +30 -4
- package/templates/en/instructions.md +9 -0
- package/templates/zh/instructions.md +9 -0
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export function literal(value) {
|
|
2
|
+
return JSON.stringify(value).replaceAll("\u2028", "\\u2028").replaceAll("\u2029", "\\u2029");
|
|
3
|
+
}
|
|
4
|
+
export function todo(message) {
|
|
5
|
+
return message.split(/[\r\n\u2028\u2029]+/u).map((line) => ` // TODO(agent2cursor): ${line}`).join("\n");
|
|
6
|
+
}
|
|
7
|
+
export function assertId(id, kind) {
|
|
8
|
+
if (!/^[A-Za-z0-9][A-Za-z0-9_-]*$/u.test(id)) {
|
|
9
|
+
throw new Error(`Invalid ${kind} id: ${JSON.stringify(id)}`);
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
export function markdown(value) {
|
|
13
|
+
return value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">")
|
|
14
|
+
.replaceAll("|", "\\|").replaceAll("`", "`").replace(/[\r\n]+/gu, " ");
|
|
15
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { ConvertOptions, MigrationIR } from "../ir.js";
|
|
2
|
+
export declare function renderEvalConfig(): string;
|
|
3
|
+
export declare function renderSkillEvals(ir: MigrationIR, options: ConvertOptions): string;
|
|
4
|
+
export declare function renderSubagentEvals(ir: MigrationIR, options: ConvertOptions): string;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { literal } from "./common.js";
|
|
2
|
+
const ROUTING_TIMEOUT_MS = 240_000;
|
|
3
|
+
export function renderEvalConfig() {
|
|
4
|
+
return 'import { defineEvalConfig } from "@grok/agent-sdk/evals";\n\nexport default defineEvalConfig({ maxConcurrency: 2 });\n';
|
|
5
|
+
}
|
|
6
|
+
export function renderSkillEvals(ir, options) {
|
|
7
|
+
const cases = ir.skills.map((skill) => {
|
|
8
|
+
const trigger = skill.trigger || (options.lang === "zh" ? `请使用 ${skill.id} skill。` : `Use the ${skill.id} skill.`);
|
|
9
|
+
const scope = options.lang === "zh"
|
|
10
|
+
? `\n先读取 ${skill.id} 的 SKILL.md,再按其中的格式做一个最小例子。不要执行其中的脚本,不要写文件,不要提交或合并 PR。`
|
|
11
|
+
: `\nRead ${skill.id}'s SKILL.md, then produce one minimal example in its format. Do not run its scripts, write files, commit, or merge a PR.`;
|
|
12
|
+
return [
|
|
13
|
+
" {", ` id: ${literal(skill.id)},`,
|
|
14
|
+
" async test(t) {", ` await t.send(${literal(trigger + scope)});`,
|
|
15
|
+
" t.succeeded();", ` t.loadedSkill(${literal(skill.id)});`,
|
|
16
|
+
" },", " },",
|
|
17
|
+
].join("\n");
|
|
18
|
+
});
|
|
19
|
+
return ['import { defineEval } from "@grok/agent-sdk/evals";', "", "export default defineEval({", ' tags: ["migration", "routing"],', ` timeoutMs: ${ROUTING_TIMEOUT_MS},`, " cases: [", ...cases, " ],", "});", ""].join("\n");
|
|
20
|
+
}
|
|
21
|
+
export function renderSubagentEvals(ir, options) {
|
|
22
|
+
const cases = ir.agents.map((agent) => {
|
|
23
|
+
const prompt = options.lang === "zh"
|
|
24
|
+
? `请通过 task 把一件符合 ${agent.id} 职责的最小例子交给 ${agent.id} 子代理,不要由根代理代答。让它按自己的说明完成,并在最终回复里给出结论。`
|
|
25
|
+
: `Use task to delegate one minimal example matching the ${agent.id} subagent's role to ${agent.id}. The root agent must not answer on its behalf. Let the subagent follow its own instructions and put its conclusion in the final reply.`;
|
|
26
|
+
return [
|
|
27
|
+
" {", ` id: ${literal(agent.id)},`, " async test(t) {",
|
|
28
|
+
` await t.send(${literal(prompt)});`, " t.succeeded();",
|
|
29
|
+
` t.calledSubagent(${literal(agent.id)});`, " },", " },",
|
|
30
|
+
].join("\n");
|
|
31
|
+
});
|
|
32
|
+
return ['import { defineEval } from "@grok/agent-sdk/evals";', "", "export default defineEval({", ' tags: ["migration", "routing"],', ` timeoutMs: ${ROUTING_TIMEOUT_MS},`, " cases: [", ...cases, " ],", "});", ""].join("\n");
|
|
33
|
+
}
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import { posix, relative, sep } from "node:path";
|
|
3
|
+
import { stringify } from "yaml";
|
|
4
|
+
import { SDK_VERSION } from "../ir.js";
|
|
5
|
+
import { listSafeFiles, readSafeFile } from "../scan/safety.js";
|
|
6
|
+
import { assertId, literal, markdown } from "./common.js";
|
|
7
|
+
import { renderEvalConfig, renderSkillEvals, renderSubagentEvals } from "./evals.js";
|
|
8
|
+
import { renderReport } from "./report.js";
|
|
9
|
+
import { renderSubagent } from "./subagent.js";
|
|
10
|
+
import { renderToolStub } from "./tool-stub.js";
|
|
11
|
+
import { associatedTstackLicense } from "./tstack-license.js";
|
|
12
|
+
const MAX_PROJECT_BYTES = 128 * 1024 * 1024;
|
|
13
|
+
function packageName(name) {
|
|
14
|
+
return name.toLowerCase().replace(/[^a-z0-9-]+/gu, "-").replace(/^-+|-+$/gu, "").slice(0, 214) || "converted-agent";
|
|
15
|
+
}
|
|
16
|
+
function addFinding(ir, finding) {
|
|
17
|
+
if (!ir.findings.some((item) => item.source === finding.source && item.field === finding.field && item.message === finding.message))
|
|
18
|
+
ir.findings.push(finding);
|
|
19
|
+
}
|
|
20
|
+
function skillPreloadHints(ir, options) {
|
|
21
|
+
const agents = new Map();
|
|
22
|
+
const rootLines = [];
|
|
23
|
+
for (const agent of ir.agents) {
|
|
24
|
+
if (agent.sourceFormat === "codex")
|
|
25
|
+
continue;
|
|
26
|
+
const raw = agent.frontmatter.skills;
|
|
27
|
+
if (raw === undefined)
|
|
28
|
+
continue;
|
|
29
|
+
if (!Array.isArray(raw) || !raw.every((item) => typeof item === "string" && !!item.trim())) {
|
|
30
|
+
addFinding(ir, { source: agent.source, field: "[skills preload hint]", status: "degraded", message: "skills 不是有效的字符串数组,未生成加载提示。请检查原字段并手动配置共享 skill。", messageEn: "skills is not a valid array of names; no load hint was generated. Review the source field and configure shared skills manually." });
|
|
31
|
+
continue;
|
|
32
|
+
}
|
|
33
|
+
if (!raw.length)
|
|
34
|
+
continue;
|
|
35
|
+
const matches = raw.map((name) => ({ name, skill: ir.skills.find((skill) => skill.id === name || skill.frontmatter.name === name) }));
|
|
36
|
+
const available = matches.flatMap(({ skill }) => skill ? [skill.id] : []);
|
|
37
|
+
const missing = matches.filter(({ skill }) => !skill).map(({ name }) => name);
|
|
38
|
+
const list = available.map((id) => `\`${id}\``).join(", ");
|
|
39
|
+
const hint = options.lang === "zh"
|
|
40
|
+
? `Claude 的 skills 预加载没有原生等价行为。${available.length ? `需要时请根代理先加载共享 skill:${list},再委派给本子代理。` : "没有找到对应的已迁移共享 skill。"}${missing.length ? ` 尚未一并迁移:${missing.map(markdown).join(", ")};请先补充这些 skill。` : ""} 这只是提示,不是自动预加载或权限隔离。`
|
|
41
|
+
: `Claude skill preloading has no native equivalent. ${available.length ? `When needed, ask the root agent to load shared skills ${list} before delegating to this subagent.` : "No corresponding migrated shared skill was found."}${missing.length ? ` Missing from this conversion: ${missing.map(markdown).join(", ")}; convert these skills first.` : ""} This is a hint, not automatic preloading or permission isolation.`;
|
|
42
|
+
agents.set(agent.id, `\n\n<!-- agent2cursor migration hint -->\n${hint}\n`);
|
|
43
|
+
rootLines.push(`- \`${agent.id}\`: ${hint}`);
|
|
44
|
+
addFinding(ir, { source: agent.source, field: "[skills preload hint]", status: "degraded", target: `bot/subagents/${agent.id}/instructions.md; bot/instructions.md`, message: `已添加共享 skill 加载提示;不是自动预加载。${missing.length ? " 部分引用 skill 未包含在本次输入中,请一并转换。" : ""}`, messageEn: `Added shared-skill load hints, not automatic preloading.${missing.length ? " Some referenced skills are absent; convert them as well." : ""}` });
|
|
45
|
+
}
|
|
46
|
+
return { root: rootLines.length ? `\n## ${options.lang === "zh" ? "子代理 skill 加载提示" : "Subagent skill loading hints"}\n\n${rootLines.join("\n")}\n` : "", agents };
|
|
47
|
+
}
|
|
48
|
+
export async function planProject(source, options, validation) {
|
|
49
|
+
// Planning is pure with respect to the input IR and output filesystem.
|
|
50
|
+
const ir = structuredClone(source);
|
|
51
|
+
const files = new Map();
|
|
52
|
+
const pathKeys = new Set();
|
|
53
|
+
let totalBytes = 0;
|
|
54
|
+
const add = (path, contents) => {
|
|
55
|
+
if (path.includes("\\") || posix.isAbsolute(path) || path.split("/").some((part) => part === ".." || part === "." || part === ""))
|
|
56
|
+
throw new Error(`Unsafe output path: ${JSON.stringify(path)}`);
|
|
57
|
+
// Refuse case-folded collisions too: generated projects must be portable to macOS/Windows.
|
|
58
|
+
const key = path.normalize("NFC").toLowerCase();
|
|
59
|
+
if (pathKeys.has(key))
|
|
60
|
+
throw new Error(`Output path collision: ${JSON.stringify(path)}`);
|
|
61
|
+
const bytes = typeof contents === "string" ? Buffer.byteLength(contents, "utf8") : contents.length;
|
|
62
|
+
if (totalBytes + bytes > MAX_PROJECT_BYTES)
|
|
63
|
+
throw new Error("Generated project exceeds the 128 MiB size limit. Split the conversion into smaller inputs.");
|
|
64
|
+
totalBytes += bytes;
|
|
65
|
+
pathKeys.add(key);
|
|
66
|
+
files.set(path, contents);
|
|
67
|
+
};
|
|
68
|
+
for (const [kind, assets] of [["subagent", ir.agents], ["skill", ir.skills], ["tool", ir.scripts]]) {
|
|
69
|
+
const seen = new Set();
|
|
70
|
+
for (const asset of assets) {
|
|
71
|
+
assertId(asset.id, kind);
|
|
72
|
+
const key = asset.id.toLowerCase();
|
|
73
|
+
if (seen.has(key))
|
|
74
|
+
throw new Error(`Duplicate ${kind} id: ${asset.id}`);
|
|
75
|
+
seen.add(key);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
add("package.json", JSON.stringify({
|
|
79
|
+
name: packageName(options.name), version: "0.1.0", private: true, type: "module",
|
|
80
|
+
engines: { node: ">=22.13" },
|
|
81
|
+
scripts: { dev: "agent-sdk dev", validate: "agent-sdk validate --dir .", eval: "agent-sdk eval --dir ." },
|
|
82
|
+
dependencies: { "@grok/agent-sdk": SDK_VERSION, zod: "^3.25.0" },
|
|
83
|
+
}, null, 2) + "\n");
|
|
84
|
+
add(".gitignore", "node_modules/\n.env\n.env.*\n!.env.example\n.agent-sdk/\n.agent-serve/\n.cursor/\n");
|
|
85
|
+
add("bot/agent.ts", [
|
|
86
|
+
'import { defineAgent } from "@grok/agent-sdk";', "", "export default defineAgent({",
|
|
87
|
+
` name: ${literal(options.name)},`, " local: { sandbox: true },",
|
|
88
|
+
...(options.rootTools === undefined ? [] : [` tools: ${literal(options.rootTools)},`]),
|
|
89
|
+
"});", "",
|
|
90
|
+
].join("\n"));
|
|
91
|
+
const template = await readFile(new URL(`../../templates/${options.lang}/instructions.md`, import.meta.url), "utf8");
|
|
92
|
+
const preloadHints = skillPreloadHints(ir, options);
|
|
93
|
+
const delegation = ir.agents.length
|
|
94
|
+
? (options.lang === "zh" ? "\n## 可委派的子代理\n\n" : "\n## Available subagents\n\n") + ir.agents.map((agent) => `- \`${agent.id}\`: ${agent.description.replace(/[\r\n]+/gu, " ")}`).join("\n") + "\n"
|
|
95
|
+
: "";
|
|
96
|
+
add("bot/instructions.md", template.replaceAll("{{name}}", options.name) + delegation + preloadHints.root);
|
|
97
|
+
for (const agent of ir.agents) {
|
|
98
|
+
const hint = preloadHints.agents.get(agent.id) ?? "";
|
|
99
|
+
if (hint) {
|
|
100
|
+
const bodyFinding = { source: agent.source, field: "$body", status: "degraded", target: `bot/subagents/${agent.id}/instructions.md`,
|
|
101
|
+
message: "保留原文,正文末尾追加共享 skill 预加载提示(`<!-- agent2cursor migration hint -->`);输出正文并非逐字不变。提示不实现自动预加载。",
|
|
102
|
+
messageEn: "Original text retained, with a shared-skill preload hint appended after `<!-- agent2cursor migration hint -->`; the output body is not byte-for-byte unchanged. The hint does not implement automatic preloading." };
|
|
103
|
+
// Replace both inventories so the report cannot retain a contradictory scan-time conclusion.
|
|
104
|
+
ir.findings = ir.findings.filter(f => !(f.source === agent.source && f.field === "$body"));
|
|
105
|
+
agent.findings = agent.findings.filter(f => f.field !== "$body");
|
|
106
|
+
ir.findings.push(bodyFinding);
|
|
107
|
+
agent.findings.push(bodyFinding);
|
|
108
|
+
}
|
|
109
|
+
add(`bot/subagents/${agent.id}/agent.ts`, renderSubagent(agent, options));
|
|
110
|
+
add(`bot/subagents/${agent.id}/instructions.md`, agent.body + hint);
|
|
111
|
+
}
|
|
112
|
+
const tstackSources = new Map();
|
|
113
|
+
for (const skill of ir.skills) {
|
|
114
|
+
const license = await associatedTstackLicense(skill);
|
|
115
|
+
if (license) {
|
|
116
|
+
const source = tstackSources.get(license.root) ?? { license, skills: [] };
|
|
117
|
+
source.skills.push(skill.id);
|
|
118
|
+
tstackSources.set(license.root, source);
|
|
119
|
+
addFinding(ir, { source: license.source, field: "[tstack license]", status: "mapped", target: "LICENSE-tstack; TSTACK-PROVENANCE.json", message: "已核对并保留 t3dotgg/tstack 的 Theo Browne MIT 版权与许可全文;不表示官方关联或背书。当前本地输入的 Git 版本未核实,见来源记录。", messageEn: "Verified and preserved the complete Theo Browne MIT copyright and license from t3dotgg/tstack; no affiliation or endorsement is implied. The input's Git revision is unverified; see provenance." });
|
|
120
|
+
}
|
|
121
|
+
const safeFiles = await listSafeFiles(skill.directory, (path, reason) => {
|
|
122
|
+
addFinding(ir, { source: path, field: "[excluded path]", status: "dropped", message: `辅助目录未复制:${reason}。如确有需要,请人工审核后添加。`, messageEn: `Supporting directory excluded: ${reason}. Review and add manually if required.` });
|
|
123
|
+
});
|
|
124
|
+
const frontmatter = { ...skill.frontmatter, name: skill.id, description: skill.description };
|
|
125
|
+
add(`bot/skills/${skill.id}/SKILL.md`, `---\n${stringify(frontmatter).trimEnd()}\n---\n${skill.body}`);
|
|
126
|
+
for (const file of safeFiles) {
|
|
127
|
+
const localPath = relative(skill.directory, file).split(sep).join("/");
|
|
128
|
+
if (localPath === "SKILL.md")
|
|
129
|
+
continue;
|
|
130
|
+
add(`bot/skills/${skill.id}/${localPath}`, await readSafeFile(file));
|
|
131
|
+
addFinding(ir, { source: file, field: "[supporting file]", status: "mapped", target: `bot/skills/${skill.id}/${localPath}`, message: "原样复制辅助文件;复制不表示自动执行或支持其运行时元数据。", messageEn: "Supporting file copied unchanged; copying does not execute it or imply support for its runtime metadata." });
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
if (tstackSources.size) {
|
|
135
|
+
const sources = [...tstackSources.values()];
|
|
136
|
+
add("LICENSE-tstack", sources[0].license.bytes);
|
|
137
|
+
add("TSTACK-PROVENANCE.json", JSON.stringify({
|
|
138
|
+
repository: "https://github.com/t3dotgg/tstack", license: "MIT", copyright: "Copyright (c) 2026 Theo Browne",
|
|
139
|
+
sourceRevision: "unverified", outputLicenseSha256: sources[0].license.sha256,
|
|
140
|
+
modifications: "Skill frontmatter normalized; source bodies and supporting files retained; disabled script stubs and routing evals generated. See the migration report.",
|
|
141
|
+
inputs: sources.map(({ license, skills }) => ({ directory: license.root, licenseSha256: license.sha256, skills })),
|
|
142
|
+
}, null, 2) + "\n");
|
|
143
|
+
add("THIRD_PARTY_NOTICES.md", options.lang === "zh"
|
|
144
|
+
? "# 第三方声明\n\n本项目包含来自 [t3dotgg/tstack](https://github.com/t3dotgg/tstack) 的 skills,Copyright (c) 2026 Theo Browne,按 MIT 许可提供;许可全文见 [LICENSE-tstack](LICENSE-tstack)。与 Theo Browne / T3 无官方关联或背书关系。当前输入版本未核实;转换改动与本地来源见 TSTACK-PROVENANCE.json 及迁移报告。\n\n@grok/agent-sdk 仅作为依赖引用,使用受其许可证及 Cursor 服务条款约束。\n"
|
|
145
|
+
: "# Third-party notices\n\nThis project includes skills from [t3dotgg/tstack](https://github.com/t3dotgg/tstack), Copyright (c) 2026 Theo Browne, under the MIT license; the full notice is in [LICENSE-tstack](LICENSE-tstack). No official affiliation or endorsement by Theo Browne / T3 is implied. The input revision is unverified; see TSTACK-PROVENANCE.json and the migration report for local sources and conversion changes.\n\n@grok/agent-sdk is referenced only as a dependency and remains subject to its own license and Cursor terms.\n");
|
|
146
|
+
}
|
|
147
|
+
for (const script of ir.scripts) {
|
|
148
|
+
assertId(script.skillId, "skill");
|
|
149
|
+
if (!ir.skills.some((skill) => skill.id === script.skillId))
|
|
150
|
+
throw new Error(`Script ${script.id} references missing skill ${script.skillId}`);
|
|
151
|
+
add(`bot/tools/${script.id}.ts`, renderToolStub(script, options));
|
|
152
|
+
addFinding(ir, { source: script.source, field: "[script tool]", status: "degraded", target: `bot/tools/${script.id}.ts`, message: "已生成 execution: agent 的禁用存根,始终返回 TODO 错误。审核原脚本、补齐参数 schema 与 JSON stdin/stdout 协议后再启用;agent tool 需要 shell,且不支持 effect / dryRunResult / needsApproval。", messageEn: "Generated a disabled execution: agent stub that always returns a TODO error. Review the original script and implement a typed JSON stdin/stdout contract before enabling. Agent tools require shell and do not support effect / dryRunResult / needsApproval." });
|
|
153
|
+
}
|
|
154
|
+
if (options.rootTools && ir.agents.length && !options.rootTools.includes("task"))
|
|
155
|
+
addFinding(ir, { source: "bot/agent.ts", field: "tools", status: "degraded", message: "显式根工具白名单未包含 task,子代理不能被委派,生成的委派 eval 会失败。请按需要添加 task。", messageEn: "The explicit root allowlist omits task, so subagents cannot be delegated to and delegation evals will fail. Add task if needed." });
|
|
156
|
+
if (options.rootTools && ir.scripts.length && !options.rootTools.includes("shell"))
|
|
157
|
+
addFinding(ir, { source: "bot/agent.ts", field: "tools", status: "degraded", message: "显式根工具白名单未包含 shell,agent 脚本工具不能运行。实现并审核工具后,再决定是否添加 shell。", messageEn: "The explicit root allowlist omits shell, so agent script tools cannot run. Decide whether to add shell after implementing and reviewing them." });
|
|
158
|
+
add("evals/evals.config.ts", renderEvalConfig());
|
|
159
|
+
if (ir.skills.length)
|
|
160
|
+
add("evals/skills.eval.ts", renderSkillEvals(ir, options));
|
|
161
|
+
if (ir.agents.length)
|
|
162
|
+
add("evals/subagents.eval.ts", renderSubagentEvals(ir, options));
|
|
163
|
+
add(options.lang === "zh" ? "MIGRATION.zh-CN.md" : "MIGRATION.en.md", renderReport(ir, options, validation));
|
|
164
|
+
return { files, ir };
|
|
165
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { SDK_VERSION } from "../ir.js";
|
|
2
|
+
import { markdown } from "./common.js";
|
|
3
|
+
import { permissionWarning } from "../mapping/permissions.js";
|
|
4
|
+
import { validationMessage } from "../validate.js";
|
|
5
|
+
const statusLabels = {
|
|
6
|
+
zh: { mapped: "✅ 已映射", degraded: "⚠️ 降级", dropped: "❌ 丢弃" },
|
|
7
|
+
en: { mapped: "✅ Mapped", degraded: "⚠️ Degraded", dropped: "❌ Dropped" },
|
|
8
|
+
};
|
|
9
|
+
function allFindings(ir) {
|
|
10
|
+
const unique = new Map();
|
|
11
|
+
for (const finding of [...ir.findings, ...ir.agents.flatMap((a) => a.findings), ...ir.skills.flatMap((s) => s.findings)]) {
|
|
12
|
+
const key = JSON.stringify([finding.source, finding.field, finding.status, finding.message, finding.target]);
|
|
13
|
+
unique.set(key, finding);
|
|
14
|
+
}
|
|
15
|
+
// Defensive completeness: even callers constructing an IR manually cannot silently lose fields.
|
|
16
|
+
for (const asset of [...ir.agents, ...ir.skills]) {
|
|
17
|
+
for (const field of Object.keys(asset.frontmatter)) {
|
|
18
|
+
if (![...unique.values()].some((item) => item.source === asset.source && item.field === field)) {
|
|
19
|
+
const finding = {
|
|
20
|
+
source: asset.source, field, status: "degraded",
|
|
21
|
+
message: "没有可核实的自动映射结论。请对照原文件人工检查此字段;不要假定其运行时语义已保留。",
|
|
22
|
+
messageEn: "No verified automatic mapping is available. Review this field against its source; do not assume its runtime semantics were preserved.",
|
|
23
|
+
};
|
|
24
|
+
unique.set(JSON.stringify([asset.source, field]), finding);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return [...unique.values()];
|
|
29
|
+
}
|
|
30
|
+
export function renderReport(ir, options, validation) {
|
|
31
|
+
const zh = options.lang === "zh";
|
|
32
|
+
const findings = allFindings(ir);
|
|
33
|
+
const rows = findings.map((finding) => `| ${markdown(finding.source)} | ${markdown(finding.field)} | ${statusLabels[options.lang][finding.status]} | ${markdown(finding.target ?? "—")} | ${markdown(zh ? finding.message : finding.messageEn)} |`);
|
|
34
|
+
const defaultValidation = { status: "skipped", command: "agent-sdk validate --dir <out>", message: zh ? "尚未执行。" : "Not run yet." };
|
|
35
|
+
const result = validation ?? defaultValidation;
|
|
36
|
+
const labels = zh ? { passed: "通过", failed: "失败", "dependencies-missing": "待安装依赖(未校验)", unavailable: "不可用", skipped: "未执行" } : { passed: "Passed", failed: "Failed", "dependencies-missing": "Dependencies missing (not validated)", unavailable: "Unavailable", skipped: "Skipped" };
|
|
37
|
+
const warning = permissionWarning(ir, options.lang);
|
|
38
|
+
return [
|
|
39
|
+
`# ${zh ? "迁移报告" : "Migration report"}: ${markdown(options.name)}`,
|
|
40
|
+
...(warning ? ["", `**${markdown(warning)}**`] : []),
|
|
41
|
+
"", `agent2cursor → @grok/agent-sdk ${SDK_VERSION}`, "",
|
|
42
|
+
zh
|
|
43
|
+
? `扫描结果:${ir.agents.length} 个子代理,${ir.skills.length} 个 skill(其中 ${ir.skills.filter((s) => s.dependency).length} 个依赖),${ir.scripts.length} 个禁用脚本工具存根。`
|
|
44
|
+
: `Inventory: ${ir.agents.length} subagents, ${ir.skills.length} skills (${ir.skills.filter((s) => s.dependency).length} dependencies), ${ir.scripts.length} disabled script-tool stubs.`,
|
|
45
|
+
"", `## ${zh ? "权限和执行边界" : "Permissions and execution boundaries"}`, "",
|
|
46
|
+
zh
|
|
47
|
+
? "**原子代理的 tools / disallowedTools 限制无法在子代理层面保留。** 子代理共用父代理执行环境与 skills,子代理配置中的 tools 不受支持。不能假定根工具白名单能约束 task 子代理;请先验证,或禁用 task、拆成独立代理。只有显式传入 --root-tools 才会设置根代理工具白名单;不传时使用 SDK 默认工具。local.sandbox 已开启,但沙箱不等于只读;shell 可以写文件、执行命令。请在运行前审核实际权限。"
|
|
48
|
+
: "**Original per-subagent tools / disallowedTools restrictions cannot be preserved at subagent scope.** Subagents share the parent's execution environment and skills; subagent tools configuration is unsupported. Do not assume the root tool allowlist constrains task subagents. Verify that boundary, disable task, or use separate agents. Root tool restrictions are emitted only with explicit --root-tools; otherwise SDK defaults apply. local.sandbox is enabled, but a sandbox is not read-only; shell can write files and execute commands. Review actual permissions before running.",
|
|
49
|
+
"", zh
|
|
50
|
+
? "原 skill 正文与辅助文件保留,可能仍包含其他平台的工具名或操作指令。迁移报告中的警告不会自动改写这些指令。scripts 文件只被复制,转换过程不会执行它们;生成的工具存根始终返回 TODO 错误。原脚本经审核并实现 JSON stdin/stdout 契约后才能启用。运行时 skill 路径是 .cursor/skills/<name>/,不是源码中的 bot/skills/<name>/。"
|
|
51
|
+
: "Original skill bodies and supporting files are preserved and may still refer to another platform's tools or operational instructions. Report warnings do not rewrite those instructions automatically. Scripts are copied, never executed during conversion; generated tool stubs always return a TODO error. Review original scripts and implement the JSON stdin/stdout contract before enabling them. Runtime skill paths are .cursor/skills/<name>/, not the source paths bot/skills/<name>/.",
|
|
52
|
+
"", zh
|
|
53
|
+
? "工具名扫描使用固定名单,区分大小写匹配正文和附属 Markdown 中的标识符。未命中不表示工具兼容;命中也可能来自禁止调用的说明。请人工复核实际调用需求。"
|
|
54
|
+
: "Tool-name scanning uses a fixed, case-sensitive identifier list in bodies and supporting Markdown. No match does not establish compatibility; a match may occur in an instruction prohibiting a call. Review actual tool requirements manually.",
|
|
55
|
+
"", `## ${zh ? "逐文件、逐字段映射" : "Mapping by file and field"}`, "",
|
|
56
|
+
zh ? "| 源文件 | 字段 / 内容 | 结论 | 输出位置 | 说明与补救 |" : "| Source | Field / content | Result | Destination | Explanation and remedy |",
|
|
57
|
+
"| --- | --- | --- | --- | --- |", ...rows,
|
|
58
|
+
...(rows.length ? [] : [zh ? "| — | — | ✅ 已映射 | — | 没有发现输入字段。 |" : "| — | — | ✅ Mapped | — | No input fields found. |"]),
|
|
59
|
+
"", `## ${zh ? "自动生成的 eval" : "Generated evals"}`, "",
|
|
60
|
+
zh
|
|
61
|
+
? "每个 skill 生成 loadedSkill 用例,每个子代理生成 calledSubagent 用例。提示要求按源说明做一个最小例子,用来检查是否加载了对应 skill、是否委派给对应子代理;不要求模型忽略自己的说明。这些用例不证明能力或安全性。未运行真实模型时,不能称为 eval 通过。禁用脚本存根不生成成功调用用例;实现后请补充 calledTool 和结果断言。"
|
|
62
|
+
: "Each skill receives a loadedSkill case; each subagent receives a calledSubagent case. The prompt asks for one minimal example that follows the source instructions, so the case checks that the named skill was loaded or the named subagent was called. It does not ask the model to ignore those instructions. These cases do not prove capability or safety and are not passing evals until run against a real model. Disabled script stubs do not receive successful-call cases; add calledTool and result assertions after implementation.",
|
|
63
|
+
"", `## ${zh ? "SDK 自检" : "SDK validation"}`, "",
|
|
64
|
+
`- ${zh ? "状态" : "Status"}: ${labels[result.status]}`,
|
|
65
|
+
`- ${zh ? "命令" : "Command"}: ${markdown(result.command)}`,
|
|
66
|
+
`- ${zh ? "结果" : "Result"}: ${markdown(validationMessage(result, options.lang))}`,
|
|
67
|
+
...(result.diagnostics ?? []).map((diagnostic) => `- ${markdown(diagnostic.severity)}: ${markdown(diagnostic.message)}`),
|
|
68
|
+
"", zh
|
|
69
|
+
? "validate 只检查项目结构与配置;它不验证模型 ID 是否可用,也不证明真实模型执行、GitHub 触发或部署成功。"
|
|
70
|
+
: "validate checks project structure and configuration only; it does not establish model-ID availability, live model execution, GitHub triggers, or successful deployment.",
|
|
71
|
+
"", `## ${zh ? "下一步" : "Next steps"}`, "",
|
|
72
|
+
"```sh", "npm install", "npx agent-sdk validate --dir .", "npx agent-sdk eval --dir . --list", "npx agent-sdk login", "npx agent-sdk dev", "```", "",
|
|
73
|
+
].join("\n");
|
|
74
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { assertId, literal, todo } from "./common.js";
|
|
2
|
+
export function renderSubagent(agent, options) {
|
|
3
|
+
assertId(agent.id, "subagent");
|
|
4
|
+
const notes = agent.findings.filter((finding) => finding.status !== "mapped")
|
|
5
|
+
.map((finding) => todo(`${finding.field}: ${options.lang === "zh" ? finding.message : finding.messageEn}`));
|
|
6
|
+
return [
|
|
7
|
+
'// Generated by agent2cursor. Review the migration report before running.',
|
|
8
|
+
'import { defineAgent } from "@grok/agent-sdk";',
|
|
9
|
+
"", "export default defineAgent({",
|
|
10
|
+
` description: ${literal(agent.description)},`,
|
|
11
|
+
...(agent.model ? [` model: ${literal(agent.model)},`] : []),
|
|
12
|
+
...notes,
|
|
13
|
+
"});", "",
|
|
14
|
+
].join("\n");
|
|
15
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { assertId, literal, todo } from "./common.js";
|
|
2
|
+
// Constant shell source: no original file contents or source-controlled paths are executable.
|
|
3
|
+
export const DISABLED_TOOL_SCRIPT = [
|
|
4
|
+
"#!/usr/bin/env sh",
|
|
5
|
+
"# Disabled by agent2cursor. Review the original script and implement the JSON contract first.",
|
|
6
|
+
"cat >/dev/null",
|
|
7
|
+
"printf '%s\\n' '{\"ok\":false,\"error\":\"AGENT2CURSOR_STUB_DISABLED\",\"message\":\"TODO(agent2cursor): review the original script and implement JSON stdin/stdout before enabling this tool.\"}'",
|
|
8
|
+
"exit 78",
|
|
9
|
+
"",
|
|
10
|
+
].join("\n");
|
|
11
|
+
export function renderToolStub(script, options) {
|
|
12
|
+
assertId(script.id, "tool");
|
|
13
|
+
assertId(script.skillId, "skill");
|
|
14
|
+
const description = options.lang === "zh"
|
|
15
|
+
? `禁用的迁移存根 ${script.id}。调用始终返回 AGENT2CURSOR_STUB_DISABLED,绝不执行原脚本。请操作者先审查 .cursor/skills/${script.skillId}/${script.relativePath} 并实现 JSON 输入输出契约。`
|
|
16
|
+
: `Disabled migration stub ${script.id}. Always returns AGENT2CURSOR_STUB_DISABLED and never runs the original script. An operator must review .cursor/skills/${script.skillId}/${script.relativePath} and implement the JSON input/output contract first.`;
|
|
17
|
+
return [
|
|
18
|
+
'import { defineTool } from "@grok/agent-sdk/tools";',
|
|
19
|
+
'import { z } from "zod";', "",
|
|
20
|
+
"export default defineTool({",
|
|
21
|
+
` description: ${literal(description)},`,
|
|
22
|
+
' execution: "agent",',
|
|
23
|
+
todo(options.lang === "zh" ? "按审核后的脚本参数收紧 schema;当前存根不会运行任何原脚本。" : "Replace this permissive schema with reviewed script arguments; the stub never runs original scripts."),
|
|
24
|
+
" inputSchema: z.object({}).passthrough(),",
|
|
25
|
+
todo(options.lang === "zh" ? "agent 工具必须通过 shell 执行,不支持 effect / dryRunResult / needsApproval;需要这些保护时请实现 server 工具。" : "Agent tools require shell and do not support effect / dryRunResult / needsApproval; use a server tool when those controls are required."),
|
|
26
|
+
` script: ${literal(DISABLED_TOOL_SCRIPT)},`,
|
|
27
|
+
"});", "",
|
|
28
|
+
].join("\n");
|
|
29
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { lstat } from "node:fs/promises";
|
|
3
|
+
import { basename, dirname, join } from "node:path";
|
|
4
|
+
import { readSafeFile, readSafeText } from "../scan/safety.js";
|
|
5
|
+
// Public MIT license at t3dotgg/tstack commit 7de017b3fc9d50c03cf3ecb98fb71e0ca65a78bd.
|
|
6
|
+
// No SDK implementation or documentation is vendored by this check.
|
|
7
|
+
const TSTACK_LICENSE_SHA256 = "8095f9a9f97d074131f6a2ce09b952d12c18b36f09f4e5df0517c4e64e82c851";
|
|
8
|
+
export async function associatedTstackLicense(skill) {
|
|
9
|
+
const skillsDirectory = dirname(skill.directory);
|
|
10
|
+
if (basename(skillsDirectory) !== "skills")
|
|
11
|
+
return undefined;
|
|
12
|
+
const root = dirname(skillsDirectory);
|
|
13
|
+
const readmePath = join(root, "README.md");
|
|
14
|
+
try {
|
|
15
|
+
await lstat(readmePath);
|
|
16
|
+
}
|
|
17
|
+
catch (error) {
|
|
18
|
+
if (error.code === "ENOENT")
|
|
19
|
+
return undefined;
|
|
20
|
+
throw error;
|
|
21
|
+
}
|
|
22
|
+
const readme = await readSafeText(readmePath);
|
|
23
|
+
if (!/^#\s+tstack\s*$/mu.test(readme) || !readme.includes("t3dotgg/tstack"))
|
|
24
|
+
return undefined;
|
|
25
|
+
const source = join(root, "LICENSE");
|
|
26
|
+
let bytes;
|
|
27
|
+
try {
|
|
28
|
+
bytes = await readSafeFile(source, 16 * 1024);
|
|
29
|
+
}
|
|
30
|
+
catch (error) {
|
|
31
|
+
if (error.code === "ENOENT")
|
|
32
|
+
throw new Error(`Recognized tstack source is missing its MIT LICENSE: ${root}`);
|
|
33
|
+
throw error;
|
|
34
|
+
}
|
|
35
|
+
const normalizedHash = createHash("sha256").update(bytes.toString("utf8").replaceAll("\r\n", "\n")).digest("hex");
|
|
36
|
+
if (normalizedHash !== TSTACK_LICENSE_SHA256)
|
|
37
|
+
throw new Error(`Recognized tstack LICENSE differs from the verified MIT notice. Review its license before conversion: ${source}`);
|
|
38
|
+
return { root, source, bytes, sha256: createHash("sha256").update(bytes).digest("hex") };
|
|
39
|
+
}
|
package/dist/ir.d.ts
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
export declare const SDK_VERSION = "0.3.0";
|
|
2
|
+
export type Language = "zh" | "en";
|
|
3
|
+
export type Status = "mapped" | "degraded" | "dropped";
|
|
4
|
+
export interface Finding {
|
|
5
|
+
source: string;
|
|
6
|
+
field: string;
|
|
7
|
+
status: Status;
|
|
8
|
+
message: string;
|
|
9
|
+
messageEn: string;
|
|
10
|
+
target?: string;
|
|
11
|
+
}
|
|
12
|
+
export interface AgentAsset {
|
|
13
|
+
id: string;
|
|
14
|
+
source: string;
|
|
15
|
+
sourceFormat?: "claude" | "codex";
|
|
16
|
+
body: string;
|
|
17
|
+
frontmatter: Record<string, unknown>;
|
|
18
|
+
description: string;
|
|
19
|
+
model?: string;
|
|
20
|
+
findings: Finding[];
|
|
21
|
+
}
|
|
22
|
+
export interface SkillAsset {
|
|
23
|
+
id: string;
|
|
24
|
+
source: string;
|
|
25
|
+
directory: string;
|
|
26
|
+
body: string;
|
|
27
|
+
frontmatter: Record<string, unknown>;
|
|
28
|
+
description: string;
|
|
29
|
+
trigger?: string;
|
|
30
|
+
dependency: boolean;
|
|
31
|
+
findings: Finding[];
|
|
32
|
+
}
|
|
33
|
+
export interface ScriptAsset {
|
|
34
|
+
id: string;
|
|
35
|
+
source: string;
|
|
36
|
+
skillId: string;
|
|
37
|
+
relativePath: string;
|
|
38
|
+
}
|
|
39
|
+
export interface MigrationIR {
|
|
40
|
+
agents: AgentAsset[];
|
|
41
|
+
skills: SkillAsset[];
|
|
42
|
+
scripts: ScriptAsset[];
|
|
43
|
+
findings: Finding[];
|
|
44
|
+
}
|
|
45
|
+
export interface ConvertOptions {
|
|
46
|
+
name: string;
|
|
47
|
+
lang: Language;
|
|
48
|
+
withDeps: boolean;
|
|
49
|
+
keepModel: boolean;
|
|
50
|
+
modelMap: Record<string, string>;
|
|
51
|
+
rootTools?: string[];
|
|
52
|
+
}
|
|
53
|
+
export interface ValidationResult {
|
|
54
|
+
status: "passed" | "failed" | "dependencies-missing" | "unavailable" | "skipped";
|
|
55
|
+
command: string;
|
|
56
|
+
message: string;
|
|
57
|
+
missingDependencies?: string[];
|
|
58
|
+
diagnostics?: {
|
|
59
|
+
severity: string;
|
|
60
|
+
message: string;
|
|
61
|
+
}[];
|
|
62
|
+
}
|
|
63
|
+
export interface ProjectPlan {
|
|
64
|
+
files: Map<string, string | Buffer>;
|
|
65
|
+
ir: MigrationIR;
|
|
66
|
+
}
|
package/dist/ir.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const SDK_VERSION = "0.3.0";
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { fieldPaths } from "../scan/frontmatter.js";
|
|
2
|
+
import { mapSubagent } from "./subagent.js";
|
|
3
|
+
export function mapCodexAgent(source, config, options) {
|
|
4
|
+
const core = { name: config.name, description: config.description,
|
|
5
|
+
...(Object.hasOwn(config, "model") ? { model: config.model } : {}) };
|
|
6
|
+
const asset = mapSubagent(source, { frontmatter: core, body: config.developer_instructions }, options);
|
|
7
|
+
asset.sourceFormat = "codex";
|
|
8
|
+
asset.frontmatter = config;
|
|
9
|
+
// Reuse the shared name/model/body mappings, not Claude-specific field semantics.
|
|
10
|
+
for (const [key, value] of Object.entries(config)) {
|
|
11
|
+
if (Object.hasOwn(core, key))
|
|
12
|
+
continue;
|
|
13
|
+
let rule = { source, status: "dropped",
|
|
14
|
+
message: "目标 SDK 无已核实的 Codex 等价字段;未写入配置,请人工实现其语义。",
|
|
15
|
+
messageEn: "No verified SDK equivalent for this Codex field; omitted from configuration. Implement its semantics manually." };
|
|
16
|
+
if (key === "developer_instructions")
|
|
17
|
+
rule = { source, status: "mapped", target: `bot/subagents/${asset.id}/instructions.md`,
|
|
18
|
+
message: "TOML 指令字符串已写入 instructions.md;保留解析后的文本,不包含 TOML 引号。",
|
|
19
|
+
messageEn: "Wrote the parsed TOML instruction string to instructions.md unchanged, without TOML quoting." };
|
|
20
|
+
if (["model_reasoning_effort", "model_reasoning_summary", "model_verbosity"].includes(key))
|
|
21
|
+
rule = { source, status: "dropped",
|
|
22
|
+
message: "Codex 模型参数未映射;模型 ID 映射不包含推理强度、摘要或详细度。请按目标模型支持的参数人工配置并验证。",
|
|
23
|
+
messageEn: "Codex model settings were not mapped; model-ID mapping does not include reasoning effort, summary, or verbosity. Configure and verify supported target-model parameters manually." };
|
|
24
|
+
if (["features", "skills", "tools", "disallowedTools"].includes(key))
|
|
25
|
+
rule = { source, status: "degraded",
|
|
26
|
+
message: "Codex 子代理能力或 skill 限制未生效;SDK 子代理共享执行环境和 skills,不能假定根白名单会约束 task 子代理。请人工重建限制,必要时拆为独立代理或禁用 task。未加载任何引用的配置或 skill 文件。",
|
|
27
|
+
messageEn: "Codex per-agent capability or skill restrictions are not enforced. SDK subagents share the execution environment and skills; do not assume root allowlists constrain task children. Rebuild restrictions manually, use separate agents, or disable task. No referenced configuration or skill files were loaded." };
|
|
28
|
+
if (["sandbox_mode", "approval_policy", "sandbox_workspace_write", "permissions", "model_provider", "mcp_servers", "service_tier"].includes(key))
|
|
29
|
+
rule = { source, status: "dropped",
|
|
30
|
+
message: "这是父会话配置,不是 Codex 独立子代理的有效覆盖项;未迁移父会话的权限、provider 或 MCP 连接。请在目标项目人工配置并验证,local.sandbox 不等于原有权限边界。",
|
|
31
|
+
messageEn: "This is parent-session configuration, not an effective standalone Codex agent override. Parent permissions, provider, and MCP connections were not migrated. Configure and verify them in the target project; local.sandbox is not an equivalent permission boundary." };
|
|
32
|
+
for (const field of fieldPaths({ [key]: value }))
|
|
33
|
+
asset.findings.push({ ...rule, field });
|
|
34
|
+
}
|
|
35
|
+
asset.findings.push({ source, field: "$context", status: "degraded",
|
|
36
|
+
message: "仅迁移独立子代理文件;未读取父会话 config.toml、AGENTS.md 或个人配置,未解析旧式 agents.<name>.config_file。请一并转换所需 skills,并人工补充上下文。",
|
|
37
|
+
messageEn: "Only the standalone agent file is migrated. Parent config.toml, AGENTS.md, personal configuration, and legacy agents.<name>.config_file declarations are not loaded. Convert required skills and add inherited context manually." });
|
|
38
|
+
return asset;
|
|
39
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { fieldPaths } from "../scan/frontmatter.js";
|
|
2
|
+
import { scanForeignTools } from "./foreign-tools.js";
|
|
3
|
+
export function mapCodexSkillMetadata(source, metadata, skillId) {
|
|
4
|
+
const ui = metadata.interface;
|
|
5
|
+
const rawTrigger = ui && typeof ui === "object" && !Array.isArray(ui) ? ui.default_prompt : undefined;
|
|
6
|
+
const trigger = typeof rawTrigger === "string" && rawTrigger.trim() ? rawTrigger : undefined;
|
|
7
|
+
const copied = `bot/skills/${skillId}/agents/openai.yaml`;
|
|
8
|
+
const findings = fieldPaths(metadata).map(field => {
|
|
9
|
+
const base = { source, field, status: "degraded", target: copied };
|
|
10
|
+
if (field === "interface.default_prompt")
|
|
11
|
+
return { ...base, status: trigger ? "mapped" : "degraded", target: "evals/skills.eval.ts",
|
|
12
|
+
message: trigger ? "已用 Codex default_prompt 生成 eval 触发语;不改写其中的平台工具或路径引用。" : "未发现有效 default_prompt,将使用生成的 eval 触发语。",
|
|
13
|
+
messageEn: trigger ? "Used Codex default_prompt as the eval trigger; platform-specific tool and path references are not rewritten." : "No valid default_prompt found; using a generated eval trigger." };
|
|
14
|
+
if (field === "policy" || field.startsWith("policy."))
|
|
15
|
+
return { ...base,
|
|
16
|
+
message: "仅保留调用策略元数据;SDK 不执行 Codex policy,包括 allow_implicit_invocation: false 的禁止自动调用语义。需人工限制触发流程;复制不保留此边界。",
|
|
17
|
+
messageEn: "Invocation policy is retained as metadata only. The SDK does not enforce Codex policy, including the implicit-invocation restriction of allow_implicit_invocation: false. Constrain routing manually; copying does not preserve this boundary." };
|
|
18
|
+
if (field === "dependencies" || field.startsWith("dependencies."))
|
|
19
|
+
return { ...base,
|
|
20
|
+
message: "仅保留工具依赖声明;未连接 MCP、安装工具或转移认证。请在 bot/mcp-connections/ 人工配置所需连接,并核对名称、传输方式与权限。",
|
|
21
|
+
messageEn: "Tool dependencies are declarations only; no MCP connections, tools, or authentication were installed or transferred. Configure required connections in bot/mcp-connections/ and verify names, transport, and permissions." };
|
|
22
|
+
return { ...base,
|
|
23
|
+
message: "仅原样保留 Codex 元数据文件;目标 SDK 未应用此 UI 或未知字段,需人工适配。",
|
|
24
|
+
messageEn: "Preserved as Codex metadata only; the target SDK does not apply this UI or unknown field. Adapt it manually." };
|
|
25
|
+
});
|
|
26
|
+
if (!findings.some(finding => finding.field === "interface.default_prompt"))
|
|
27
|
+
findings.push({ source, field: "interface.default_prompt", status: "degraded", target: "evals/skills.eval.ts",
|
|
28
|
+
message: "未发现有效 default_prompt,将使用生成的 eval 触发语。", messageEn: "No valid default_prompt found; using a generated eval trigger." });
|
|
29
|
+
if (trigger)
|
|
30
|
+
findings.push(...scanForeignTools(trigger, source));
|
|
31
|
+
return { trigger, findings };
|
|
32
|
+
}
|