@vetta-org/plugin-cli 0.1.4 → 0.1.6
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 +39 -0
- package/README.md +10 -0
- package/dist/agents-template.d.ts +15 -0
- package/dist/agents-template.d.ts.map +1 -1
- package/dist/cli.js +187 -96
- package/dist/command.d.ts +16 -0
- package/dist/command.d.ts.map +1 -1
- package/dist/hub-template.d.ts.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +190 -97
- package/dist/init.d.ts +14 -1
- package/dist/init.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -3646,7 +3646,7 @@ var require_npa = __commonJS((exports, module) => {
|
|
|
3646
3646
|
});
|
|
3647
3647
|
|
|
3648
3648
|
// src/command.ts
|
|
3649
|
-
import { existsSync as existsSync4, statSync as statSync2 } from "node:fs";
|
|
3649
|
+
import { existsSync as existsSync4, readFileSync as readFileSync4, statSync as statSync2 } from "node:fs";
|
|
3650
3650
|
import { join as join6, resolve as resolve5 } from "node:path";
|
|
3651
3651
|
import { parseArgs } from "node:util";
|
|
3652
3652
|
|
|
@@ -15418,7 +15418,9 @@ var PluginAgentTeamMemberManifestSchema = Type.Object({
|
|
|
15418
15418
|
agent: Type.Optional(NonWhitespaceStringSchema),
|
|
15419
15419
|
role: Type.Optional(PluginAgentRoleSchema),
|
|
15420
15420
|
responsibility: Type.String({ maxLength: 2000 }),
|
|
15421
|
-
optional: Type.Optional(Type.Boolean())
|
|
15421
|
+
optional: Type.Optional(Type.Boolean()),
|
|
15422
|
+
instructions: Type.Optional(Type.String({ maxLength: 64000 })),
|
|
15423
|
+
instructionsPath: Type.Optional(NonWhitespaceStringSchema)
|
|
15422
15424
|
}, { additionalProperties: false });
|
|
15423
15425
|
var PluginAgentTeamManifestSchema = Type.Object({
|
|
15424
15426
|
id: Type.String({ pattern: "^[a-z0-9][a-z0-9-]{0,63}$" }),
|
|
@@ -18919,107 +18921,93 @@ async function resolveNpmPluginArchive(packageSpec, pack = runNpmPack) {
|
|
|
18919
18921
|
}
|
|
18920
18922
|
}
|
|
18921
18923
|
|
|
18922
|
-
// src/init.ts
|
|
18923
|
-
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
18924
|
-
import { dirname, join as join3, resolve as resolve2 } from "node:path";
|
|
18925
|
-
|
|
18926
18924
|
// src/agents-template.ts
|
|
18925
|
+
var AGENTS_GUIDE_REVISION = 3;
|
|
18926
|
+
function readAgentsGuideRevision(content) {
|
|
18927
|
+
const match2 = /<!--\s*vetta-guide-revision:\s*(\d+)\s*-->/.exec(content);
|
|
18928
|
+
if (!match2)
|
|
18929
|
+
return;
|
|
18930
|
+
return Number(match2[1]);
|
|
18931
|
+
}
|
|
18932
|
+
function renderCommands(pluginId, scripts) {
|
|
18933
|
+
const known = [
|
|
18934
|
+
["dev", "npm run dev # 开发服务器"],
|
|
18935
|
+
["build", "npm run build # 产出 dist/"],
|
|
18936
|
+
["install:vetta", "npm run install:vetta # 打包并装进正在运行的 Vetta(需要 Vetta 已启动)"]
|
|
18937
|
+
];
|
|
18938
|
+
const lines = known.filter(([name]) => scripts.includes(name)).map(([, line]) => line);
|
|
18939
|
+
if (!scripts.includes("install:vetta")) {
|
|
18940
|
+
lines.push("npx vetta-plugin-cli add . # 打包并装进正在运行的 Vetta");
|
|
18941
|
+
}
|
|
18942
|
+
return [
|
|
18943
|
+
...lines,
|
|
18944
|
+
"",
|
|
18945
|
+
"npx vetta-plugin-cli watch # 热更新:宿主改从本工程目录加载,改完即生效",
|
|
18946
|
+
`npx vetta-plugin-cli reload ${pluginId} # 装完提示有 pending 版本时用它`,
|
|
18947
|
+
"npx vetta-plugin-cli uninstall # 卸载(省略 id 即本工程对应的插件)",
|
|
18948
|
+
"npx vetta-plugin-cli sync # 在 hub 仓库根上跑:把索引与各能力目录对账"
|
|
18949
|
+
].join(`
|
|
18950
|
+
`);
|
|
18951
|
+
}
|
|
18927
18952
|
function renderAgentsGuide(input) {
|
|
18928
|
-
return
|
|
18953
|
+
return `<!-- vetta-guide-revision: ${AGENTS_GUIDE_REVISION} -->
|
|
18954
|
+
# ${input.displayName}
|
|
18929
18955
|
|
|
18930
|
-
Vetta
|
|
18956
|
+
Vetta 桌面插件工程(插件 id:\`${input.pluginId}\`)。
|
|
18931
18957
|
|
|
18932
|
-
|
|
18958
|
+
**本文件不讲规则,只告诉你去哪读。** 规则在手册里,手册随 SDK 升级;写在这里的任何一条都会
|
|
18959
|
+
停在这个工程创建那天。两者冲突时一律以手册为准。
|
|
18960
|
+
|
|
18961
|
+
## 第一步:装依赖,找到手册
|
|
18933
18962
|
|
|
18934
18963
|
\`\`\`bash
|
|
18935
18964
|
npm install
|
|
18936
|
-
npx vetta-plugin-cli docs
|
|
18965
|
+
npx vetta-plugin-cli docs --check-latest
|
|
18937
18966
|
\`\`\`
|
|
18938
18967
|
|
|
18939
18968
|
\`npm install\` 必须先跑:\`vetta-plugin-cli\` 是 \`@vetta-org/plugin-cli\` 的命令名,装完才在
|
|
18940
18969
|
\`node_modules/.bin\` 里。还没装就想跑,用全名 \`npx @vetta-org/plugin-cli docs\`。
|
|
18941
18970
|
|
|
18942
|
-
|
|
18943
|
-
|
|
18944
|
-
|
|
18945
|
-
\`\`\`bash
|
|
18946
|
-
npx vetta-plugin-cli docs --check-latest
|
|
18947
|
-
\`\`\`
|
|
18948
|
-
|
|
18949
|
-
落后就按它打印的命令升级 SDK 再重读手册。\`docs\` 的输出永远比本文件新(\`npx\` 默认取最新的
|
|
18950
|
-
CLI),**两者冲突时以它为准**。
|
|
18971
|
+
它打印手册目录的**绝对路径**、手册对应的 SDK 版本,以及本工程与所属 hub 的位置。
|
|
18972
|
+
**不要硬编码这个路径**:工作区可能把依赖提升到仓库根,一仓多插件时各插件还可能钉不同版本。
|
|
18951
18973
|
|
|
18952
|
-
|
|
18953
|
-
这份手册对应的 SDK 版本。**不要硬编码这个路径**:工作区可能把依赖提升到仓库根,一仓多插件
|
|
18954
|
-
时各插件还可能钉不同的 SDK 版本。
|
|
18974
|
+
输出里出现 \`Manual is behind\` 或 \`This brief is stale\` 就按它给的命令升级,再重跑一次。
|
|
18955
18975
|
|
|
18956
|
-
|
|
18976
|
+
## 第二步:按顺序读手册
|
|
18957
18977
|
|
|
18958
18978
|
| 顺序 | 文件 | 何时读 |
|
|
18959
18979
|
| --- | --- | --- |
|
|
18960
|
-
| 1 | \`README.md\` |
|
|
18980
|
+
| 1 | \`README.md\` | **总是先读**:能力矩阵、信任模型、**不可违反的红线**、导航 |
|
|
18961
18981
|
| 2 | \`getting-started.md\` | 首次写代码、构建、安装调试 |
|
|
18962
18982
|
| 3 | \`manifest.md\` | 写/改 \`plugin.json\`、贡献智能体与团队 |
|
|
18963
18983
|
| 4 | \`permissions.md\` | 选定权限列表之前 |
|
|
18964
|
-
| 5 | 按扩展点选读 | \`ui-slots.md\` / \`conversation-and-agent.md\` / \`message-cards.md\` / \`mcp.md\` / \`ai.md\` / \`browser.md\` / \`app-actions.md\` |
|
|
18984
|
+
| 5 | 按扩展点选读 | \`ui-slots.md\` / \`conversation-and-agent.md\` / \`message-cards.md\` / \`mcp.md\` / \`ai.md\` / \`browser.md\` / \`app-actions.md\` / \`styling-and-pitfalls.md\` |
|
|
18965
18985
|
|
|
18966
|
-
|
|
18986
|
+
实现任一扩展点**之前**再读对应那章,不要凭记忆写 SDK API——这套合同变化很快。
|
|
18967
18987
|
|
|
18968
|
-
##
|
|
18988
|
+
## 常用命令
|
|
18969
18989
|
|
|
18970
18990
|
\`\`\`bash
|
|
18971
|
-
|
|
18972
|
-
npm run build # 产出 dist/
|
|
18973
|
-
npm run install:vetta # 打包并装进正在运行的 Vetta(需要 Vetta 已启动)
|
|
18974
|
-
npx vetta-plugin-cli watch # 开热更新:宿主改从本工程目录加载,改完即生效
|
|
18975
|
-
npx vetta-plugin-cli uninstall # 卸载(省略 id 即本工程对应的插件)
|
|
18991
|
+
${renderCommands(input.pluginId, input.scripts ?? ["dev", "build", "install:vetta"])}
|
|
18976
18992
|
\`\`\`
|
|
18977
18993
|
|
|
18978
|
-
\`
|
|
18979
|
-
|
|
18980
|
-
|
|
18981
|
-
装完若提示有 pending 版本,用 \`npx vetta-plugin-cli reload ${input.pluginId}\` 让宿主应用它。
|
|
18994
|
+
细节都在 \`getting-started.md\`。\`docs\` 打印了 \`Marketplace index:\` 就说明这个目录之上有能力
|
|
18995
|
+
市场索引,改完 \`version\` / \`permissions\` / \`pluginApiVersion\` 后要回仓库根跑 \`sync\`。
|
|
18982
18996
|
|
|
18983
|
-
开发期建议开热更新(\`watch\`):之后改源码即时生效,不用每次重新打包安装。改 \`plugin.json\`
|
|
18984
|
-
的权限或命令声明时仍需重新安装一次,让宿主把授权落盘。\`watch --stop\` 关闭。
|
|
18985
|
-
|
|
18986
|
-
## 如果这个目录之上有能力市场索引
|
|
18987
|
-
|
|
18988
|
-
\`vetta-plugin-cli docs\` 会告诉你有没有(它会打印 \`Marketplace index:\`)。有的话,**改完
|
|
18989
|
-
\`version\` / \`permissions\` / \`pluginApiVersion\` 之后要回仓库根跑一次**:
|
|
18990
|
-
|
|
18991
|
-
\`\`\`bash
|
|
18992
|
-
npx @vetta-org/plugin-cli sync # 从各能力目录回填索引,并推进 marketplaceVersion
|
|
18993
|
-
npx @vetta-org/plugin-cli sync --check # 只报不写,CI 用
|
|
18994
|
-
\`\`\`
|
|
18995
|
-
|
|
18996
|
-
索引里的 \`version\` 与 \`plugin.json\` 的 \`version\` 必须**完全相等**,否则宿主同步直接失败;
|
|
18997
|
-
而内容变了却不换 \`marketplaceVersion\` 时,客户端既不报错也不更新——用户只是永远收不到。
|
|
18998
|
-
\`add .\` 装完若检测到索引还停在旧版本,会当场提醒你。
|
|
18999
|
-
|
|
19000
|
-
## 不可违反的几条
|
|
19001
|
-
|
|
19002
|
-
- **样式只用 Tailwind \`className\`**。禁止新建业务 CSS、禁止在 \`style.css\` 里写 \`button\`/\`div\`/\`*\`
|
|
19003
|
-
这类选择器——插件与宿主共享同一个页面,全局选择器会污染整个 UI。
|
|
19004
|
-
- **可能失败的路径必须上报**:读文件、解析、网络、外部库的 catch 里调用
|
|
19005
|
-
\`ctx.ui.notify({ message, error })\`(无需权限)。禁止只写死「失败」文案并丢掉原始 error。
|
|
19006
|
-
- **权限按需最小声明**。构建期会校验产物用到的能力与 \`plugin.json\` 的声明是否匹配,缺了直接
|
|
19007
|
-
构建失败。但 UI 槽位不在这条校验里——那类缺权限在运行时只是静默跳过,所以对着手册核对。
|
|
19008
|
-
- **不要写 \`agent_mode\`**(已废弃,无运行时语义)。想收窄某个工具的使用场景,把「何时不该用它 +
|
|
19009
|
-
替代做法」写进该工具 description 的反向触发段。
|
|
19010
|
-
- **顶层不要出现依赖共享 React 的 JSX**,放进组件或 \`activate\` 内(Module Federation 的加载时序)。
|
|
19011
|
-
- 依赖用 registry 上已发布的 semver,不要 \`workspace:*\`。
|
|
19012
|
-
- **\`dist/\` 要进版本库**。插件通过仓库目录分发时,宿主直接读 \`plugin.json\` 指向的 \`entry\`
|
|
19013
|
-
与 \`styles\`,它不会替你构建——目录里没有构建产物就装不上。
|
|
19014
18997
|
## 信息不足时
|
|
19015
18998
|
|
|
19016
18999
|
插件 id、展示名、要用哪些权限、功能边界、是否立刻安装——**问用户**,不要自己假定。
|
|
19017
19000
|
`;
|
|
19018
19001
|
}
|
|
19019
19002
|
|
|
19003
|
+
// src/init.ts
|
|
19004
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
19005
|
+
import { dirname, join as join3, resolve as resolve2 } from "node:path";
|
|
19006
|
+
|
|
19020
19007
|
// src/hub-template.ts
|
|
19021
19008
|
function renderHubAgentsGuide(input) {
|
|
19022
|
-
return
|
|
19009
|
+
return `<!-- vetta-guide-revision: ${AGENTS_GUIDE_REVISION} -->
|
|
19010
|
+
# ${input.name}
|
|
19023
19011
|
|
|
19024
19012
|
Vetta 能力市场仓库。本仓库索引若干**能力**(plugin / mcp / skill / scene / bundle),
|
|
19025
19013
|
每个能力是 \`abilities/\` 下的一个自包含目录。
|
|
@@ -19053,16 +19041,25 @@ npx @vetta-org/plugin-cli init --id <slug> --name "<Display Name>" abilities/plu
|
|
|
19053
19041
|
|
|
19054
19042
|
## 索引由工具对账,不要手改派生字段
|
|
19055
19043
|
|
|
19056
|
-
|
|
19057
|
-
\`config.commands\` 全都是从能力包推导出来的。改完能力后:
|
|
19044
|
+
改完能力后:
|
|
19058
19045
|
|
|
19059
19046
|
\`\`\`bash
|
|
19060
|
-
npx @vetta-org/plugin-cli sync #
|
|
19047
|
+
npx @vetta-org/plugin-cli sync # 对账并回填,看输出
|
|
19061
19048
|
npx @vetta-org/plugin-cli sync --check # 只报不写,非零退出(CI 用)
|
|
19062
19049
|
\`\`\`
|
|
19063
19050
|
|
|
19051
|
+
\`sync\` 到底做什么,分三档看清楚——它不是万能的:
|
|
19052
|
+
|
|
19053
|
+
| 字段 | \`sync\` 的行为 |
|
|
19054
|
+
| --- | --- |
|
|
19055
|
+
| 条目 \`version\` | **回填**成能力包里的版本 |
|
|
19056
|
+
| \`marketplaceVersion\` | 内容有变化时**尝试**推进。只认 semver(\`1.2.3\`)和纯整数;\`YYYY.MM.DD-NN\` 之类的格式推不动,会报出来要你手改 |
|
|
19057
|
+
| \`config.api_version\` / \`config.permissions\` / \`config.commands\` | **不写**。宿主建目录时用 \`plugin.json\` 整个重算 \`config\`,索引里的副本读都不读。已经存在且与真源不符时 \`sync\` 会提醒你删掉或改对 |
|
|
19058
|
+
|
|
19059
|
+
所以 \`sync\` 跑完要看输出:它报出来的问题(尤其是推不动的 \`marketplaceVersion\`)没人会替你处理。
|
|
19060
|
+
|
|
19064
19061
|
要**手写**的只有身份与展示:\`slug\`、\`name\`、\`description\`、\`source.path\`、\`category\`、\`tags\`、
|
|
19065
|
-
\`detail
|
|
19062
|
+
\`detail\`。新能力上架时手动加一条这样的条目。
|
|
19066
19063
|
|
|
19067
19064
|
三条容易踩的约束,\`sync --check\` 会替你守住:
|
|
19068
19065
|
|
|
@@ -19072,7 +19069,7 @@ npx @vetta-org/plugin-cli sync --check # 只报不写,非零退出(CI 用
|
|
|
19072
19069
|
| \`plugin.json\` 的 \`entry\` / \`styles\` 必须在已发布目录里真实存在 | 本地能装,市场上装不了 |
|
|
19073
19070
|
| 改了任何内容必须换 \`marketplaceVersion\` | 客户端不报错、也不更新,用户永远收不到 |
|
|
19074
19071
|
|
|
19075
|
-
|
|
19072
|
+
第三条最阴险——它不报错。\`sync\` 只在版本号是 semver 或整数时才推得动,其余格式要你自己换。
|
|
19076
19073
|
|
|
19077
19074
|
## 为什么插件目录里要提交 \`dist/\`
|
|
19078
19075
|
|
|
@@ -19240,26 +19237,80 @@ node_modules/
|
|
|
19240
19237
|
}
|
|
19241
19238
|
return { root, pluginId: input.pluginId, files: Object.keys(files).sort() };
|
|
19242
19239
|
}
|
|
19243
|
-
function refreshAgentsGuide(targetDir) {
|
|
19240
|
+
function refreshAgentsGuide(targetDir, options = {}) {
|
|
19244
19241
|
const root = resolve2(targetDir);
|
|
19242
|
+
const file = join3(root, "AGENTS.md");
|
|
19245
19243
|
const manifestPath = join3(root, "plugin.json");
|
|
19244
|
+
const hubManifestPath = join3(root, ".vetta", "marketplace.json");
|
|
19245
|
+
let kind;
|
|
19246
|
+
let content;
|
|
19246
19247
|
if (existsSync(manifestPath)) {
|
|
19247
19248
|
const manifest = JSON.parse(readFileSync(manifestPath, "utf8"));
|
|
19248
19249
|
const pluginId = typeof manifest.id === "string" ? manifest.id : undefined;
|
|
19249
19250
|
if (!pluginId)
|
|
19250
19251
|
throw new Error(`plugin.json at ${root} has no id`);
|
|
19251
|
-
|
|
19252
|
-
|
|
19253
|
-
|
|
19252
|
+
kind = "plugin";
|
|
19253
|
+
content = renderAgentsGuide({
|
|
19254
|
+
pluginId,
|
|
19255
|
+
displayName: resolveManifestText(manifest.name, root, pluginId),
|
|
19256
|
+
scripts: readPackageScripts(root)
|
|
19257
|
+
});
|
|
19258
|
+
} else if (existsSync(hubManifestPath)) {
|
|
19259
|
+
const manifest = JSON.parse(readFileSync(hubManifestPath, "utf8"));
|
|
19260
|
+
kind = "hub";
|
|
19261
|
+
content = renderHubAgentsGuide({
|
|
19262
|
+
name: typeof manifest.name === "string" && manifest.name.length > 0 ? manifest.name : "marketplace"
|
|
19263
|
+
});
|
|
19264
|
+
} else {
|
|
19265
|
+
throw new Error(`Not a plugin project or marketplace repository: ${root}`);
|
|
19254
19266
|
}
|
|
19255
|
-
|
|
19256
|
-
|
|
19257
|
-
|
|
19258
|
-
|
|
19259
|
-
|
|
19260
|
-
|
|
19267
|
+
if (!options.dryRun)
|
|
19268
|
+
assertSafeToOverwrite(file, options.force === true);
|
|
19269
|
+
if (options.dryRun)
|
|
19270
|
+
return { root, kind, file, content, written: false };
|
|
19271
|
+
writeFileSync(file, content, "utf8");
|
|
19272
|
+
return { root, kind, file, content, written: true };
|
|
19273
|
+
}
|
|
19274
|
+
function assertSafeToOverwrite(file, force) {
|
|
19275
|
+
if (force || !existsSync(file))
|
|
19276
|
+
return;
|
|
19277
|
+
if (readAgentsGuideRevision(readFileSync(file, "utf8")) !== undefined)
|
|
19278
|
+
return;
|
|
19279
|
+
throw new Error(`${file} has no vetta-guide-revision marker, so it looks hand-written rather than scaffolded. ` + "Refusing to overwrite it. Review the new template with `--dry-run`, merge what you want by hand, " + "or pass `--force` to replace the file.");
|
|
19280
|
+
}
|
|
19281
|
+
function resolveManifestText(raw2, root, fallback) {
|
|
19282
|
+
if (typeof raw2 !== "string" || raw2.length === 0)
|
|
19283
|
+
return fallback;
|
|
19284
|
+
const match2 = /^%([^%]+)%$/.exec(raw2);
|
|
19285
|
+
if (!match2)
|
|
19286
|
+
return raw2;
|
|
19287
|
+
const key = match2[1];
|
|
19288
|
+
const pluginManifest = readJsonFile(join3(root, "plugin.json"));
|
|
19289
|
+
const locale = typeof pluginManifest?.defaultLocale === "string" ? pluginManifest.defaultLocale : "zh";
|
|
19290
|
+
for (const candidate of [locale, "zh", "en"]) {
|
|
19291
|
+
const table = readJsonFile(join3(root, "locales", `${candidate}.json`));
|
|
19292
|
+
const value = table?.[key];
|
|
19293
|
+
if (typeof value === "string" && value.length > 0)
|
|
19294
|
+
return value;
|
|
19295
|
+
}
|
|
19296
|
+
return fallback;
|
|
19297
|
+
}
|
|
19298
|
+
function readPackageScripts(root) {
|
|
19299
|
+
const pkg = readJsonFile(join3(root, "package.json"));
|
|
19300
|
+
const scripts = pkg?.scripts;
|
|
19301
|
+
if (typeof scripts !== "object" || scripts === null || Array.isArray(scripts))
|
|
19302
|
+
return [];
|
|
19303
|
+
return Object.keys(scripts);
|
|
19304
|
+
}
|
|
19305
|
+
function readJsonFile(path) {
|
|
19306
|
+
if (!existsSync(path))
|
|
19307
|
+
return;
|
|
19308
|
+
try {
|
|
19309
|
+
const parsed = JSON.parse(readFileSync(path, "utf8"));
|
|
19310
|
+
return typeof parsed === "object" && parsed !== null && !Array.isArray(parsed) ? parsed : undefined;
|
|
19311
|
+
} catch {
|
|
19312
|
+
return;
|
|
19261
19313
|
}
|
|
19262
|
-
throw new Error(`Not a plugin project or marketplace repository: ${root}`);
|
|
19263
19314
|
}
|
|
19264
19315
|
var HUB_NAME_PATTERN = /^[a-z0-9][a-z0-9-]{0,63}$/;
|
|
19265
19316
|
var APP_VERSION_PATTERN = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/;
|
|
@@ -19319,7 +19370,7 @@ function detectJsonIndent(source) {
|
|
|
19319
19370
|
const match2 = /\n([ \t]+)\S/.exec(source);
|
|
19320
19371
|
return match2?.[1] ?? "\t";
|
|
19321
19372
|
}
|
|
19322
|
-
function
|
|
19373
|
+
function readJsonFile2(path) {
|
|
19323
19374
|
try {
|
|
19324
19375
|
const parsed = JSON.parse(readFileSync2(path, "utf8"));
|
|
19325
19376
|
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed))
|
|
@@ -19363,7 +19414,7 @@ function nextMarketplaceVersion(current) {
|
|
|
19363
19414
|
return;
|
|
19364
19415
|
}
|
|
19365
19416
|
function syncMarketplaceIndex(input) {
|
|
19366
|
-
const manifest =
|
|
19417
|
+
const manifest = readJsonFile2(input.manifestPath);
|
|
19367
19418
|
if (!manifest)
|
|
19368
19419
|
throw new Error(`Malformed marketplace manifest: ${input.manifestPath}`);
|
|
19369
19420
|
const abilities = Array.isArray(manifest.abilities) ? manifest.abilities : [];
|
|
@@ -19432,7 +19483,7 @@ function syncMarketplaceIndex(input) {
|
|
|
19432
19483
|
}
|
|
19433
19484
|
function reconcilePlugin(context) {
|
|
19434
19485
|
const { entry, slug, abilityDir, changes, problems } = context;
|
|
19435
|
-
const manifest =
|
|
19486
|
+
const manifest = readJsonFile2(join4(abilityDir, "plugin.json"));
|
|
19436
19487
|
if (!manifest) {
|
|
19437
19488
|
problems.push({ slug, message: "plugin.json is missing or malformed" });
|
|
19438
19489
|
return;
|
|
@@ -19473,7 +19524,7 @@ function reconcilePlugin(context) {
|
|
|
19473
19524
|
}
|
|
19474
19525
|
function reconcileIdentityFile(context) {
|
|
19475
19526
|
const { entry, slug, abilityDir, fileName, changes, problems } = context;
|
|
19476
|
-
const identity =
|
|
19527
|
+
const identity = readJsonFile2(join4(abilityDir, fileName));
|
|
19477
19528
|
if (!identity) {
|
|
19478
19529
|
problems.push({ slug, message: `${fileName} is missing or malformed` });
|
|
19479
19530
|
return;
|
|
@@ -19516,7 +19567,7 @@ function findAbilityDirectories(hubRoot) {
|
|
|
19516
19567
|
return found.sort();
|
|
19517
19568
|
}
|
|
19518
19569
|
function describeIndexDrift(input) {
|
|
19519
|
-
const manifest =
|
|
19570
|
+
const manifest = readJsonFile2(input.manifestPath);
|
|
19520
19571
|
if (!manifest || !Array.isArray(manifest.abilities))
|
|
19521
19572
|
return;
|
|
19522
19573
|
const entry = manifest.abilities.find((item) => typeof item === "object" && item !== null && !Array.isArray(item) && item.slug === input.slug);
|
|
@@ -19606,7 +19657,7 @@ Usage:
|
|
|
19606
19657
|
vetta-plugin-cli reload <plugin-id> [--json]
|
|
19607
19658
|
vetta-plugin-cli docs [--check-latest] [--json]
|
|
19608
19659
|
vetta-plugin-cli init --id <plugin-id> [--name <display>] [dir] [--json]
|
|
19609
|
-
vetta-plugin-cli init --refresh-guide [dir] [--json]
|
|
19660
|
+
vetta-plugin-cli init --refresh-guide [dir] [--dry-run] [--force] [--json]
|
|
19610
19661
|
vetta-plugin-cli init hub --name <slug> --repository <url> --min-app-version <x.y.z> [dir]
|
|
19611
19662
|
vetta-plugin-cli watch [dir] [--stop] [--json]
|
|
19612
19663
|
vetta-plugin-cli uninstall [plugin-id] [--json]
|
|
@@ -19707,7 +19758,9 @@ function parsePluginInitCommand(argv) {
|
|
|
19707
19758
|
id: { type: "string" },
|
|
19708
19759
|
name: { type: "string" },
|
|
19709
19760
|
json: { type: "boolean" },
|
|
19710
|
-
"refresh-guide": { type: "boolean" }
|
|
19761
|
+
"refresh-guide": { type: "boolean" },
|
|
19762
|
+
force: { type: "boolean" },
|
|
19763
|
+
"dry-run": { type: "boolean" }
|
|
19711
19764
|
}
|
|
19712
19765
|
});
|
|
19713
19766
|
} catch (error) {
|
|
@@ -19717,7 +19770,13 @@ function parsePluginInitCommand(argv) {
|
|
|
19717
19770
|
const [dir, extra] = parsed.positionals;
|
|
19718
19771
|
if (extra)
|
|
19719
19772
|
return { type: "error", message: `Unexpected argument: ${extra}` };
|
|
19720
|
-
return {
|
|
19773
|
+
return {
|
|
19774
|
+
type: "refresh-guide",
|
|
19775
|
+
...dir ? { targetDir: dir } : {},
|
|
19776
|
+
json: parsed.values.json === true,
|
|
19777
|
+
force: parsed.values.force === true,
|
|
19778
|
+
dryRun: parsed.values["dry-run"] === true
|
|
19779
|
+
};
|
|
19721
19780
|
}
|
|
19722
19781
|
const pluginId = parsed.values.id;
|
|
19723
19782
|
if (typeof pluginId !== "string" || pluginId.length === 0) {
|
|
@@ -20060,6 +20119,7 @@ async function runDocsCommand(command, dependencies) {
|
|
|
20060
20119
|
const sdkVersion = readManualSdkVersion(manualDir);
|
|
20061
20120
|
const latestVersion = command.checkLatest ? await dependencies.readLatestSdkVersion?.() : undefined;
|
|
20062
20121
|
const outdated = sdkVersion !== undefined && latestVersion !== undefined && compareSemver(sdkVersion, latestVersion) < 0;
|
|
20122
|
+
const guide = inspectAgentsGuide(project?.root ?? hub?.root ?? cwd);
|
|
20063
20123
|
if (command.json) {
|
|
20064
20124
|
dependencies.writeStdout(`${JSON.stringify({
|
|
20065
20125
|
ok: true,
|
|
@@ -20067,6 +20127,7 @@ async function runDocsCommand(command, dependencies) {
|
|
|
20067
20127
|
entry: join6(manualDir, "README.md"),
|
|
20068
20128
|
sdkVersion,
|
|
20069
20129
|
refreshCommand: SDK_REFRESH_COMMAND,
|
|
20130
|
+
guide,
|
|
20070
20131
|
...command.checkLatest ? { latestVersion, outdated } : {},
|
|
20071
20132
|
project: project ? { root: project.root, pluginId: project.pluginId, version: project.version } : undefined,
|
|
20072
20133
|
hub: hub ? {
|
|
@@ -20092,6 +20153,11 @@ async function runDocsCommand(command, dependencies) {
|
|
|
20092
20153
|
} else {
|
|
20093
20154
|
lines.push(`Manual follows the installed SDK. To refresh it: ${SDK_REFRESH_COMMAND}`);
|
|
20094
20155
|
}
|
|
20156
|
+
if (guide.stale) {
|
|
20157
|
+
lines.push(`This brief is stale (AGENTS.md revision ${guide.revision} < ${AGENTS_GUIDE_REVISION}). Refresh it with: ${GUIDE_REFRESH_COMMAND}`);
|
|
20158
|
+
} else if (guide.unstamped) {
|
|
20159
|
+
lines.push(`AGENTS.md has no revision marker, so it looks hand-written. Review the current template with \`${GUIDE_REFRESH_COMMAND} --dry-run\` and merge by hand; do not overwrite it blindly.`);
|
|
20160
|
+
}
|
|
20095
20161
|
if (hub) {
|
|
20096
20162
|
lines.push(`Marketplace index: ${hub.manifestPath}`);
|
|
20097
20163
|
lines.push("After changing version/permissions, run `vetta-plugin-cli sync` at the repository root.");
|
|
@@ -20102,6 +20168,24 @@ async function runDocsCommand(command, dependencies) {
|
|
|
20102
20168
|
return 0;
|
|
20103
20169
|
}
|
|
20104
20170
|
var SDK_REFRESH_COMMAND = "npm i -D @vetta-org/plugin-sdk@latest && npx vetta-plugin-cli docs";
|
|
20171
|
+
var GUIDE_REFRESH_COMMAND = "npx @vetta-org/plugin-cli init --refresh-guide";
|
|
20172
|
+
function inspectAgentsGuide(root) {
|
|
20173
|
+
const path = join6(root, "AGENTS.md");
|
|
20174
|
+
if (!existsSync4(path))
|
|
20175
|
+
return { present: false, stale: false, unstamped: false };
|
|
20176
|
+
let revision;
|
|
20177
|
+
try {
|
|
20178
|
+
revision = readAgentsGuideRevision(readFileSync4(path, "utf8"));
|
|
20179
|
+
} catch {
|
|
20180
|
+
return { present: true, stale: false, unstamped: false };
|
|
20181
|
+
}
|
|
20182
|
+
return {
|
|
20183
|
+
present: true,
|
|
20184
|
+
...revision === undefined ? {} : { revision },
|
|
20185
|
+
stale: revision !== undefined && revision < AGENTS_GUIDE_REVISION,
|
|
20186
|
+
unstamped: revision === undefined
|
|
20187
|
+
};
|
|
20188
|
+
}
|
|
20105
20189
|
function compareSemver(left, right) {
|
|
20106
20190
|
const parse2 = (value) => {
|
|
20107
20191
|
const match2 = /^(\d+)\.(\d+)\.(\d+)(-.+)?$/.exec(value.trim());
|
|
@@ -20122,11 +20206,18 @@ function compareSemver(left, right) {
|
|
|
20122
20206
|
function runRefreshGuideCommand(command, dependencies) {
|
|
20123
20207
|
const cwd = dependencies.cwd?.() ?? process.cwd();
|
|
20124
20208
|
try {
|
|
20125
|
-
const result = refreshAgentsGuide(resolve5(cwd, command.targetDir ?? ".")
|
|
20126
|
-
|
|
20127
|
-
|
|
20128
|
-
|
|
20209
|
+
const result = refreshAgentsGuide(resolve5(cwd, command.targetDir ?? "."), {
|
|
20210
|
+
force: command.force,
|
|
20211
|
+
dryRun: command.dryRun
|
|
20212
|
+
});
|
|
20213
|
+
if (command.json) {
|
|
20214
|
+
dependencies.writeStdout(`${JSON.stringify({ ok: true, ...result })}
|
|
20129
20215
|
`);
|
|
20216
|
+
return 0;
|
|
20217
|
+
}
|
|
20218
|
+
dependencies.writeStdout(result.written ? `Rewrote ${result.file}
|
|
20219
|
+
Next: npx vetta-plugin-cli docs --check-latest
|
|
20220
|
+
` : `${result.content}`);
|
|
20130
20221
|
return 0;
|
|
20131
20222
|
} catch (error) {
|
|
20132
20223
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -20354,6 +20445,7 @@ export {
|
|
|
20354
20445
|
refreshAgentsGuide,
|
|
20355
20446
|
readManualSdkVersion,
|
|
20356
20447
|
readLatestNpmVersion,
|
|
20448
|
+
readAgentsGuideRevision,
|
|
20357
20449
|
parsePluginWatchCommand,
|
|
20358
20450
|
parsePluginUninstallCommand,
|
|
20359
20451
|
parsePluginSyncCommand,
|
|
@@ -20366,5 +20458,6 @@ export {
|
|
|
20366
20458
|
findPluginProject,
|
|
20367
20459
|
findPluginHub,
|
|
20368
20460
|
DEFAULT_VITE_RANGE,
|
|
20369
|
-
DEFAULT_SDK_RANGE
|
|
20461
|
+
DEFAULT_SDK_RANGE,
|
|
20462
|
+
AGENTS_GUIDE_REVISION
|
|
20370
20463
|
};
|
package/dist/init.d.ts
CHANGED
|
@@ -14,10 +14,19 @@ export interface InitPluginResult {
|
|
|
14
14
|
readonly files: readonly string[];
|
|
15
15
|
}
|
|
16
16
|
export declare function initPluginProject(input: InitPluginInput): InitPluginResult;
|
|
17
|
+
export interface RefreshGuideOptions {
|
|
18
|
+
/** 覆盖一份没有版本戳的 `AGENTS.md`。缺省拒绝——没有戳的多半是手写的。 */
|
|
19
|
+
readonly force?: boolean;
|
|
20
|
+
/** 只算出内容,不落盘。 */
|
|
21
|
+
readonly dryRun?: boolean;
|
|
22
|
+
}
|
|
17
23
|
export interface RefreshGuideResult {
|
|
18
24
|
readonly root: string;
|
|
19
25
|
readonly kind: "plugin" | "hub";
|
|
20
26
|
readonly file: string;
|
|
27
|
+
/** 这次生成的说明书正文。`dryRun` 时用它做人工合并。 */
|
|
28
|
+
readonly content: string;
|
|
29
|
+
readonly written: boolean;
|
|
21
30
|
}
|
|
22
31
|
/**
|
|
23
32
|
* 在已有的工程或能力市场仓库里重写 `AGENTS.md`。
|
|
@@ -25,8 +34,12 @@ export interface RefreshGuideResult {
|
|
|
25
34
|
* `init` 拒绝覆盖已有工程,所以老目录里那份说明书从落地起就再也没变过——它写于某个版本的
|
|
26
35
|
* SDK,之后新增的约定一条都没有。这里只重写这一个文件:它是脚手架里唯一「纯派生、没有用户
|
|
27
36
|
* 内容」的产物,其余文件都可能被改过,不该被一次刷新抹掉。
|
|
37
|
+
*
|
|
38
|
+
* **只有确实由脚手架生成的那份才算纯派生。** 没有版本戳的文件无法与手写内容区分——能力市场
|
|
39
|
+
* 仓库的根 `AGENTS.md` 往往是一整本手写的市场规范——所以一律拒绝覆盖,让调用方拿 `dryRun`
|
|
40
|
+
* 的内容去人工合并,或显式 `force`。宁可少刷新一份,也不能悄悄删掉别人写的东西。
|
|
28
41
|
*/
|
|
29
|
-
export declare function refreshAgentsGuide(targetDir: string): RefreshGuideResult;
|
|
42
|
+
export declare function refreshAgentsGuide(targetDir: string, options?: RefreshGuideOptions): RefreshGuideResult;
|
|
30
43
|
export interface InitHubInput {
|
|
31
44
|
readonly targetDir: string;
|
|
32
45
|
/** 市场名(slug)。 */
|
package/dist/init.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../src/init.ts"],"names":[],"mappings":"AAKA,0GAAwC;AACxC,eAAO,MAAM,iBAAiB,WAAW,CAAC;AAC1C,eAAO,MAAM,kBAAkB,WAAW,CAAC;AAI3C,MAAM,WAAW,eAAe;IAC/B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,gBAAgB;IAChC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;CAClC;AAUD,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,eAAe,GAAG,gBAAgB,CAkH1E;AAED,MAAM,WAAW,kBAAkB;IAClC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,QAAQ,GAAG,KAAK,CAAC;IAChC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACtB;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,MAAM,GAAG,kBAAkB,CAqBxE;AAKD,MAAM,WAAW,YAAY;IAC5B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,6BAAiB;IACjB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,2DAA6B;IAC7B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;CAC/B;AAED,MAAM,WAAW,aAAa;IAC7B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;CAClC;AAKD,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,YAAY,GAAG,aAAa,CA6CpE","sourcesContent":["import { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { dirname, join, resolve } from \"node:path\";\nimport { renderAgentsGuide } from \"./agents-template.js\";\nimport { renderHubAgentsGuide, renderHubReadme, renderHubWorkflow } from \"./hub-template.js\";\n\n/** 与脚手架一同落地的依赖范围;两个包各自独立发布,不要合成一个版本。 */\nexport const DEFAULT_SDK_RANGE = \"^0.3.2\";\nexport const DEFAULT_VITE_RANGE = \"^0.2.0\";\n\nconst PLUGIN_ID_PATTERN = /^[a-z][a-z0-9-]{0,62}$/;\n\nexport interface InitPluginInput {\n\treadonly targetDir: string;\n\treadonly pluginId: string;\n\treadonly displayName: string;\n\treadonly sdkRange?: string;\n\treadonly viteRange?: string;\n}\n\nexport interface InitPluginResult {\n\treadonly root: string;\n\treadonly pluginId: string;\n\treadonly files: readonly string[];\n}\n\nfunction remoteNameFromId(pluginId: string): string {\n\treturn pluginId.replace(/-/g, \"_\").replace(/[^A-Za-z0-9_$]/g, \"_\");\n}\n\nfunction json(value: unknown): string {\n\treturn `${JSON.stringify(value, null, \"\\t\")}\\n`;\n}\n\nexport function initPluginProject(input: InitPluginInput): InitPluginResult {\n\tif (!PLUGIN_ID_PATTERN.test(input.pluginId)) {\n\t\tthrow new Error(`Invalid plugin id ${JSON.stringify(input.pluginId)}: use lowercase kebab-case starting with a letter`);\n\t}\n\tconst root = resolve(input.targetDir);\n\tif (existsSync(join(root, \"plugin.json\"))) {\n\t\tthrow new Error(`Refusing to overwrite an existing plugin at ${root}`);\n\t}\n\n\tconst remote = remoteNameFromId(input.pluginId);\n\n\tconst files: Record<string, string> = {\n\t\t\"plugin.json\": json({\n\t\t\tid: input.pluginId,\n\t\t\tname: input.displayName,\n\t\t\tversion: \"0.1.0\",\n\t\t\tpluginApiVersion: \"^2.0.0\",\n\t\t\tentry: \"dist/mf-manifest.json\",\n\t\t\tmoduleFederation: { remoteName: remote, expose: \"./plugin\" },\n\t\t\tstyles: [\"dist/style.css\"],\n\t\t\tpermissions: [],\n\t\t\tdescription: input.displayName,\n\t\t\tauthor: \"\",\n\t\t\ticon: \"solar:widget-add-bold\",\n\t\t\tguidingWords: [],\n\t\t}),\n\t\t\"package.json\": json({\n\t\t\tname: input.pluginId,\n\t\t\tversion: \"0.1.0\",\n\t\t\tprivate: true,\n\t\t\ttype: \"module\",\n\t\t\tscripts: {\n\t\t\t\tdev: \"vetta-plugin dev\",\n\t\t\t\tbuild: \"vite build\",\n\t\t\t\tcheck: \"tsc --noEmit\",\n\t\t\t\tpack: \"vetta-plugin pack\",\n\t\t\t\tvalidate: \"vetta-plugin validate\",\n\t\t\t\tdocs: \"vetta-plugin-cli docs\",\n\t\t\t\t// 一条命令走完「构建 → 打包 → 装进正在运行的 Vetta」。\n\t\t\t\t\"install:vetta\": \"vite build && vetta-plugin pack && vetta-plugin-cli add .\",\n\t\t\t},\n\t\t\tdevDependencies: {\n\t\t\t\t\"@tailwindcss/vite\": \"^4.1.12\",\n\t\t\t\t\"@types/react\": \"^19.1.1\",\n\t\t\t\t\"@types/react-dom\": \"^19.1.1\",\n\t\t\t\t\"@vetta-org/plugin-cli\": \"^0.1.1\",\n\t\t\t\t\"@vetta-org/plugin-sdk\": input.sdkRange ?? DEFAULT_SDK_RANGE,\n\t\t\t\t\"@vetta-org/plugin-vite\": input.viteRange ?? DEFAULT_VITE_RANGE,\n\t\t\t\treact: \"19.1.1\",\n\t\t\t\t\"react-dom\": \"19.1.1\",\n\t\t\t\ttailwindcss: \"^4.1.12\",\n\t\t\t\ttypescript: \"^5.9.2\",\n\t\t\t\tvite: \"^7.1.7\",\n\t\t\t},\n\t\t}),\n\t\t\"tsconfig.json\": json({\n\t\t\tcompilerOptions: {\n\t\t\t\ttarget: \"ES2022\",\n\t\t\t\tmodule: \"ESNext\",\n\t\t\t\tlib: [\"ES2022\", \"DOM\", \"DOM.Iterable\"],\n\t\t\t\tstrict: true,\n\t\t\t\tesModuleInterop: true,\n\t\t\t\tskipLibCheck: true,\n\t\t\t\tmoduleResolution: \"bundler\",\n\t\t\t\tjsx: \"react-jsx\",\n\t\t\t\tjsxImportSource: \"react\",\n\t\t\t\tnoEmit: true,\n\t\t\t},\n\t\t\tinclude: [\"src/**/*.ts\", \"src/**/*.tsx\"],\n\t\t}),\n\t\t\"vite.config.ts\": `import tailwindcss from \"@tailwindcss/vite\";\nimport { vettaPluginFederation } from \"@vetta-org/plugin-vite\";\nimport { defineConfig } from \"vite\";\n\nexport default defineConfig({\n\tplugins: [\n\t\ttailwindcss(),\n\t\tvettaPluginFederation({\n\t\t\tname: \"${remote}\",\n\t\t\tentry: \"./src/index.tsx\",\n\t\t}),\n\t],\n\tesbuild: { jsx: \"automatic\", jsxImportSource: \"react\" },\n});\n`,\n\t\t\"src/index.tsx\": `import { definePlugin } from \"@vetta-org/plugin-sdk\";\n// Tailwind pipeline only — business CSS here would leak into the host page.\nimport \"./style.css\";\n\nexport default definePlugin({\n\tactivate(ctx) {\n\t\t// Read the manual before adding contributions: npx vetta-plugin-cli docs\n\t\tvoid ctx;\n\t},\n});\n`,\n\t\t\"src/style.css\": `/* Tailwind entry only. No business selectors — they inject into the host page. */\n@layer theme, base, components, utilities;\n@import \"tailwindcss/theme.css\" layer(theme);\n@import \"tailwindcss/utilities.css\" layer(utilities);\n`,\n\t\t// dist/ 刻意不忽略:插件通过仓库目录分发时,宿主直接读 plugin.json 指向的 entry 与\n\t\t// styles,它不会替你构建——目录里没有构建产物就装不上,而且那是一个只在别人机器上\n\t\t// 复现的失败。\n\t\t\".gitignore\": \"release/\\nnode_modules/\\n\",\n\t\t\"AGENTS.md\": renderAgentsGuide({ pluginId: input.pluginId, displayName: input.displayName }),\n\t};\n\n\tmkdirSync(join(root, \"src\"), { recursive: true });\n\tfor (const [relativePath, content] of Object.entries(files)) {\n\t\twriteFileSync(join(root, relativePath), content, \"utf8\");\n\t}\n\n\treturn { root, pluginId: input.pluginId, files: Object.keys(files).sort() };\n}\n\nexport interface RefreshGuideResult {\n\treadonly root: string;\n\treadonly kind: \"plugin\" | \"hub\";\n\treadonly file: string;\n}\n\n/**\n * 在已有的工程或能力市场仓库里重写 `AGENTS.md`。\n *\n * `init` 拒绝覆盖已有工程,所以老目录里那份说明书从落地起就再也没变过——它写于某个版本的\n * SDK,之后新增的约定一条都没有。这里只重写这一个文件:它是脚手架里唯一「纯派生、没有用户\n * 内容」的产物,其余文件都可能被改过,不该被一次刷新抹掉。\n */\nexport function refreshAgentsGuide(targetDir: string): RefreshGuideResult {\n\tconst root = resolve(targetDir);\n\tconst manifestPath = join(root, \"plugin.json\");\n\tif (existsSync(manifestPath)) {\n\t\tconst manifest = JSON.parse(readFileSync(manifestPath, \"utf8\")) as { id?: unknown; name?: unknown };\n\t\tconst pluginId = typeof manifest.id === \"string\" ? manifest.id : undefined;\n\t\tif (!pluginId) throw new Error(`plugin.json at ${root} has no id`);\n\t\tconst displayName = typeof manifest.name === \"string\" && manifest.name.length > 0 ? manifest.name : pluginId;\n\t\twriteFileSync(join(root, \"AGENTS.md\"), renderAgentsGuide({ pluginId, displayName }), \"utf8\");\n\t\treturn { root, kind: \"plugin\", file: join(root, \"AGENTS.md\") };\n\t}\n\n\tconst hubManifest = join(root, \".vetta\", \"marketplace.json\");\n\tif (existsSync(hubManifest)) {\n\t\tconst manifest = JSON.parse(readFileSync(hubManifest, \"utf8\")) as { name?: unknown };\n\t\tconst name = typeof manifest.name === \"string\" && manifest.name.length > 0 ? manifest.name : \"marketplace\";\n\t\twriteFileSync(join(root, \"AGENTS.md\"), renderHubAgentsGuide({ name }), \"utf8\");\n\t\treturn { root, kind: \"hub\", file: join(root, \"AGENTS.md\") };\n\t}\n\n\tthrow new Error(`Not a plugin project or marketplace repository: ${root}`);\n}\n\nconst HUB_NAME_PATTERN = /^[a-z0-9][a-z0-9-]{0,63}$/;\nconst APP_VERSION_PATTERN = /^\\d+\\.\\d+\\.\\d+(?:-[0-9A-Za-z-]+(?:\\.[0-9A-Za-z-]+)*)?$/;\n\nexport interface InitHubInput {\n\treadonly targetDir: string;\n\t/** 市场名(slug)。 */\n\treadonly name: string;\n\treadonly repository: string;\n\t/** 本市场的能力所支持的最老 Vetta 版本。 */\n\treadonly minAppVersion: string;\n}\n\nexport interface InitHubResult {\n\treadonly root: string;\n\treadonly name: string;\n\treadonly files: readonly string[];\n}\n\n/** 能力按类型分目录;空目录留 .gitkeep,否则 git 不会带上它们,作者得自己猜该放哪。 */\nconst HUB_ABILITY_DIRS = [\"plugins\", \"mcp\", \"skills\", \"scenes\"] as const;\n\nexport function initHubRepository(input: InitHubInput): InitHubResult {\n\tif (!HUB_NAME_PATTERN.test(input.name)) {\n\t\tthrow new Error(`Invalid marketplace name ${JSON.stringify(input.name)}: use lowercase kebab-case`);\n\t}\n\tlet repository: URL;\n\ttry {\n\t\trepository = new URL(input.repository);\n\t} catch {\n\t\tthrow new Error(`Invalid repository URL: ${input.repository}`);\n\t}\n\tif (repository.protocol !== \"https:\") throw new Error(\"Repository URL must use https://\");\n\tif (!APP_VERSION_PATTERN.test(input.minAppVersion)) {\n\t\tthrow new Error(`Invalid --min-app-version ${JSON.stringify(input.minAppVersion)}: expected a version like 0.55.0`);\n\t}\n\n\tconst root = resolve(input.targetDir);\n\tconst manifestRelativePath = join(\".vetta\", \"marketplace.json\");\n\tif (existsSync(join(root, manifestRelativePath))) {\n\t\tthrow new Error(`Refusing to overwrite an existing marketplace at ${root}`);\n\t}\n\n\tconst files: Record<string, string> = {\n\t\t[manifestRelativePath]: json({\n\t\t\tschemaVersion: 2,\n\t\t\tname: input.name,\n\t\t\tmarketplaceVersion: \"1.0.0\",\n\t\t\trepository: repository.toString().replace(/\\/$/, \"\"),\n\t\t\tminAppVersion: input.minAppVersion,\n\t\t\tabilities: [],\n\t\t}),\n\t\t\"AGENTS.md\": renderHubAgentsGuide({ name: input.name }),\n\t\t\"README.md\": renderHubReadme({ name: input.name, repository: repository.toString().replace(/\\/$/, \"\") }),\n\t\t[join(\".github\", \"workflows\", \"marketplace.yml\")]: renderHubWorkflow(),\n\t\t// dist/ 刻意不忽略:客户端直接读能力目录安装,不会替作者构建。\n\t\t\".gitignore\": \"node_modules/\\nrelease/\\n\",\n\t};\n\tfor (const dir of HUB_ABILITY_DIRS) files[join(\"abilities\", dir, \".gitkeep\")] = \"\";\n\n\tfor (const [relativePath, content] of Object.entries(files)) {\n\t\tconst target = join(root, relativePath);\n\t\tmkdirSync(dirname(target), { recursive: true });\n\t\twriteFileSync(target, content, \"utf8\");\n\t}\n\n\treturn { root, name: input.name, files: Object.keys(files).sort() };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../src/init.ts"],"names":[],"mappings":"AAKA,0GAAwC;AACxC,eAAO,MAAM,iBAAiB,WAAW,CAAC;AAC1C,eAAO,MAAM,kBAAkB,WAAW,CAAC;AAI3C,MAAM,WAAW,eAAe;IAC/B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,gBAAgB;IAChC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;CAClC;AAUD,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,eAAe,GAAG,gBAAgB,CAkH1E;AAED,MAAM,WAAW,mBAAmB;IACnC,uGAA+C;IAC/C,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;IACzB,qCAAiB;IACjB,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED,MAAM,WAAW,kBAAkB;IAClC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,QAAQ,GAAG,KAAK,CAAC;IAChC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,4EAAoC;IACpC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;CAC1B;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,GAAE,mBAAwB,GAAG,kBAAkB,CAiC3G;AAwDD,MAAM,WAAW,YAAY;IAC5B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,6BAAiB;IACjB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,2DAA6B;IAC7B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;CAC/B;AAED,MAAM,WAAW,aAAa;IAC7B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;CAClC;AAKD,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,YAAY,GAAG,aAAa,CA6CpE","sourcesContent":["import { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { dirname, join, resolve } from \"node:path\";\nimport { readAgentsGuideRevision, renderAgentsGuide } from \"./agents-template.js\";\nimport { renderHubAgentsGuide, renderHubReadme, renderHubWorkflow } from \"./hub-template.js\";\n\n/** 与脚手架一同落地的依赖范围;两个包各自独立发布,不要合成一个版本。 */\nexport const DEFAULT_SDK_RANGE = \"^0.3.2\";\nexport const DEFAULT_VITE_RANGE = \"^0.2.0\";\n\nconst PLUGIN_ID_PATTERN = /^[a-z][a-z0-9-]{0,62}$/;\n\nexport interface InitPluginInput {\n\treadonly targetDir: string;\n\treadonly pluginId: string;\n\treadonly displayName: string;\n\treadonly sdkRange?: string;\n\treadonly viteRange?: string;\n}\n\nexport interface InitPluginResult {\n\treadonly root: string;\n\treadonly pluginId: string;\n\treadonly files: readonly string[];\n}\n\nfunction remoteNameFromId(pluginId: string): string {\n\treturn pluginId.replace(/-/g, \"_\").replace(/[^A-Za-z0-9_$]/g, \"_\");\n}\n\nfunction json(value: unknown): string {\n\treturn `${JSON.stringify(value, null, \"\\t\")}\\n`;\n}\n\nexport function initPluginProject(input: InitPluginInput): InitPluginResult {\n\tif (!PLUGIN_ID_PATTERN.test(input.pluginId)) {\n\t\tthrow new Error(`Invalid plugin id ${JSON.stringify(input.pluginId)}: use lowercase kebab-case starting with a letter`);\n\t}\n\tconst root = resolve(input.targetDir);\n\tif (existsSync(join(root, \"plugin.json\"))) {\n\t\tthrow new Error(`Refusing to overwrite an existing plugin at ${root}`);\n\t}\n\n\tconst remote = remoteNameFromId(input.pluginId);\n\n\tconst files: Record<string, string> = {\n\t\t\"plugin.json\": json({\n\t\t\tid: input.pluginId,\n\t\t\tname: input.displayName,\n\t\t\tversion: \"0.1.0\",\n\t\t\tpluginApiVersion: \"^2.0.0\",\n\t\t\tentry: \"dist/mf-manifest.json\",\n\t\t\tmoduleFederation: { remoteName: remote, expose: \"./plugin\" },\n\t\t\tstyles: [\"dist/style.css\"],\n\t\t\tpermissions: [],\n\t\t\tdescription: input.displayName,\n\t\t\tauthor: \"\",\n\t\t\ticon: \"solar:widget-add-bold\",\n\t\t\tguidingWords: [],\n\t\t}),\n\t\t\"package.json\": json({\n\t\t\tname: input.pluginId,\n\t\t\tversion: \"0.1.0\",\n\t\t\tprivate: true,\n\t\t\ttype: \"module\",\n\t\t\tscripts: {\n\t\t\t\tdev: \"vetta-plugin dev\",\n\t\t\t\tbuild: \"vite build\",\n\t\t\t\tcheck: \"tsc --noEmit\",\n\t\t\t\tpack: \"vetta-plugin pack\",\n\t\t\t\tvalidate: \"vetta-plugin validate\",\n\t\t\t\tdocs: \"vetta-plugin-cli docs\",\n\t\t\t\t// 一条命令走完「构建 → 打包 → 装进正在运行的 Vetta」。\n\t\t\t\t\"install:vetta\": \"vite build && vetta-plugin pack && vetta-plugin-cli add .\",\n\t\t\t},\n\t\t\tdevDependencies: {\n\t\t\t\t\"@tailwindcss/vite\": \"^4.1.12\",\n\t\t\t\t\"@types/react\": \"^19.1.1\",\n\t\t\t\t\"@types/react-dom\": \"^19.1.1\",\n\t\t\t\t\"@vetta-org/plugin-cli\": \"^0.1.1\",\n\t\t\t\t\"@vetta-org/plugin-sdk\": input.sdkRange ?? DEFAULT_SDK_RANGE,\n\t\t\t\t\"@vetta-org/plugin-vite\": input.viteRange ?? DEFAULT_VITE_RANGE,\n\t\t\t\treact: \"19.1.1\",\n\t\t\t\t\"react-dom\": \"19.1.1\",\n\t\t\t\ttailwindcss: \"^4.1.12\",\n\t\t\t\ttypescript: \"^5.9.2\",\n\t\t\t\tvite: \"^7.1.7\",\n\t\t\t},\n\t\t}),\n\t\t\"tsconfig.json\": json({\n\t\t\tcompilerOptions: {\n\t\t\t\ttarget: \"ES2022\",\n\t\t\t\tmodule: \"ESNext\",\n\t\t\t\tlib: [\"ES2022\", \"DOM\", \"DOM.Iterable\"],\n\t\t\t\tstrict: true,\n\t\t\t\tesModuleInterop: true,\n\t\t\t\tskipLibCheck: true,\n\t\t\t\tmoduleResolution: \"bundler\",\n\t\t\t\tjsx: \"react-jsx\",\n\t\t\t\tjsxImportSource: \"react\",\n\t\t\t\tnoEmit: true,\n\t\t\t},\n\t\t\tinclude: [\"src/**/*.ts\", \"src/**/*.tsx\"],\n\t\t}),\n\t\t\"vite.config.ts\": `import tailwindcss from \"@tailwindcss/vite\";\nimport { vettaPluginFederation } from \"@vetta-org/plugin-vite\";\nimport { defineConfig } from \"vite\";\n\nexport default defineConfig({\n\tplugins: [\n\t\ttailwindcss(),\n\t\tvettaPluginFederation({\n\t\t\tname: \"${remote}\",\n\t\t\tentry: \"./src/index.tsx\",\n\t\t}),\n\t],\n\tesbuild: { jsx: \"automatic\", jsxImportSource: \"react\" },\n});\n`,\n\t\t\"src/index.tsx\": `import { definePlugin } from \"@vetta-org/plugin-sdk\";\n// Tailwind pipeline only — business CSS here would leak into the host page.\nimport \"./style.css\";\n\nexport default definePlugin({\n\tactivate(ctx) {\n\t\t// Read the manual before adding contributions: npx vetta-plugin-cli docs\n\t\tvoid ctx;\n\t},\n});\n`,\n\t\t\"src/style.css\": `/* Tailwind entry only. No business selectors — they inject into the host page. */\n@layer theme, base, components, utilities;\n@import \"tailwindcss/theme.css\" layer(theme);\n@import \"tailwindcss/utilities.css\" layer(utilities);\n`,\n\t\t// dist/ 刻意不忽略:插件通过仓库目录分发时,宿主直接读 plugin.json 指向的 entry 与\n\t\t// styles,它不会替你构建——目录里没有构建产物就装不上,而且那是一个只在别人机器上\n\t\t// 复现的失败。\n\t\t\".gitignore\": \"release/\\nnode_modules/\\n\",\n\t\t\"AGENTS.md\": renderAgentsGuide({ pluginId: input.pluginId, displayName: input.displayName }),\n\t};\n\n\tmkdirSync(join(root, \"src\"), { recursive: true });\n\tfor (const [relativePath, content] of Object.entries(files)) {\n\t\twriteFileSync(join(root, relativePath), content, \"utf8\");\n\t}\n\n\treturn { root, pluginId: input.pluginId, files: Object.keys(files).sort() };\n}\n\nexport interface RefreshGuideOptions {\n\t/** 覆盖一份没有版本戳的 `AGENTS.md`。缺省拒绝——没有戳的多半是手写的。 */\n\treadonly force?: boolean;\n\t/** 只算出内容,不落盘。 */\n\treadonly dryRun?: boolean;\n}\n\nexport interface RefreshGuideResult {\n\treadonly root: string;\n\treadonly kind: \"plugin\" | \"hub\";\n\treadonly file: string;\n\t/** 这次生成的说明书正文。`dryRun` 时用它做人工合并。 */\n\treadonly content: string;\n\treadonly written: boolean;\n}\n\n/**\n * 在已有的工程或能力市场仓库里重写 `AGENTS.md`。\n *\n * `init` 拒绝覆盖已有工程,所以老目录里那份说明书从落地起就再也没变过——它写于某个版本的\n * SDK,之后新增的约定一条都没有。这里只重写这一个文件:它是脚手架里唯一「纯派生、没有用户\n * 内容」的产物,其余文件都可能被改过,不该被一次刷新抹掉。\n *\n * **只有确实由脚手架生成的那份才算纯派生。** 没有版本戳的文件无法与手写内容区分——能力市场\n * 仓库的根 `AGENTS.md` 往往是一整本手写的市场规范——所以一律拒绝覆盖,让调用方拿 `dryRun`\n * 的内容去人工合并,或显式 `force`。宁可少刷新一份,也不能悄悄删掉别人写的东西。\n */\nexport function refreshAgentsGuide(targetDir: string, options: RefreshGuideOptions = {}): RefreshGuideResult {\n\tconst root = resolve(targetDir);\n\tconst file = join(root, \"AGENTS.md\");\n\tconst manifestPath = join(root, \"plugin.json\");\n\tconst hubManifestPath = join(root, \".vetta\", \"marketplace.json\");\n\n\tlet kind: \"plugin\" | \"hub\";\n\tlet content: string;\n\tif (existsSync(manifestPath)) {\n\t\tconst manifest = JSON.parse(readFileSync(manifestPath, \"utf8\")) as { id?: unknown; name?: unknown };\n\t\tconst pluginId = typeof manifest.id === \"string\" ? manifest.id : undefined;\n\t\tif (!pluginId) throw new Error(`plugin.json at ${root} has no id`);\n\t\tkind = \"plugin\";\n\t\tcontent = renderAgentsGuide({\n\t\t\tpluginId,\n\t\t\t// 多语言插件的 name 是 `%plugin.name%`,直接当标题会把占位符印在文件开头。\n\t\t\tdisplayName: resolveManifestText(manifest.name, root, pluginId),\n\t\t\tscripts: readPackageScripts(root),\n\t\t});\n\t} else if (existsSync(hubManifestPath)) {\n\t\tconst manifest = JSON.parse(readFileSync(hubManifestPath, \"utf8\")) as { name?: unknown };\n\t\tkind = \"hub\";\n\t\tcontent = renderHubAgentsGuide({\n\t\t\tname: typeof manifest.name === \"string\" && manifest.name.length > 0 ? manifest.name : \"marketplace\",\n\t\t});\n\t} else {\n\t\tthrow new Error(`Not a plugin project or marketplace repository: ${root}`);\n\t}\n\n\tif (!options.dryRun) assertSafeToOverwrite(file, options.force === true);\n\tif (options.dryRun) return { root, kind, file, content, written: false };\n\twriteFileSync(file, content, \"utf8\");\n\treturn { root, kind, file, content, written: true };\n}\n\n/** 已有文件必须是本模板生成的(带版本戳)才允许重写。 */\nfunction assertSafeToOverwrite(file: string, force: boolean): void {\n\tif (force || !existsSync(file)) return;\n\tif (readAgentsGuideRevision(readFileSync(file, \"utf8\")) !== undefined) return;\n\tthrow new Error(\n\t\t`${file} has no vetta-guide-revision marker, so it looks hand-written rather than scaffolded. ` +\n\t\t\t\"Refusing to overwrite it. Review the new template with `--dry-run`, merge what you want by hand, \" +\n\t\t\t\"or pass `--force` to replace the file.\",\n\t);\n}\n\n/**\n * 解析 manifest 里的 `%key%` 占位;解析不到就退回插件 id。\n *\n * 规则与宿主一致:整串恰好是 `%key%` 才查表,否则原样返回。\n */\nfunction resolveManifestText(raw: unknown, root: string, fallback: string): string {\n\tif (typeof raw !== \"string\" || raw.length === 0) return fallback;\n\tconst match = /^%([^%]+)%$/.exec(raw);\n\tif (!match) return raw;\n\tconst key = match[1]!;\n\tconst pluginManifest = readJsonFile(join(root, \"plugin.json\")) as { defaultLocale?: unknown } | undefined;\n\tconst locale = typeof pluginManifest?.defaultLocale === \"string\" ? pluginManifest.defaultLocale : \"zh\";\n\tfor (const candidate of [locale, \"zh\", \"en\"]) {\n\t\tconst table = readJsonFile(join(root, \"locales\", `${candidate}.json`));\n\t\tconst value = table?.[key];\n\t\tif (typeof value === \"string\" && value.length > 0) return value;\n\t}\n\treturn fallback;\n}\n\n/** 工程实际有的 npm scripts;读不到就当没有。 */\nfunction readPackageScripts(root: string): string[] {\n\tconst pkg = readJsonFile(join(root, \"package.json\")) as { scripts?: unknown } | undefined;\n\tconst scripts = pkg?.scripts;\n\tif (typeof scripts !== \"object\" || scripts === null || Array.isArray(scripts)) return [];\n\treturn Object.keys(scripts as Record<string, unknown>);\n}\n\nfunction readJsonFile(path: string): Record<string, unknown> | undefined {\n\tif (!existsSync(path)) return undefined;\n\ttry {\n\t\tconst parsed: unknown = JSON.parse(readFileSync(path, \"utf8\"));\n\t\treturn typeof parsed === \"object\" && parsed !== null && !Array.isArray(parsed)\n\t\t\t? (parsed as Record<string, unknown>)\n\t\t\t: undefined;\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\nconst HUB_NAME_PATTERN = /^[a-z0-9][a-z0-9-]{0,63}$/;\nconst APP_VERSION_PATTERN = /^\\d+\\.\\d+\\.\\d+(?:-[0-9A-Za-z-]+(?:\\.[0-9A-Za-z-]+)*)?$/;\n\nexport interface InitHubInput {\n\treadonly targetDir: string;\n\t/** 市场名(slug)。 */\n\treadonly name: string;\n\treadonly repository: string;\n\t/** 本市场的能力所支持的最老 Vetta 版本。 */\n\treadonly minAppVersion: string;\n}\n\nexport interface InitHubResult {\n\treadonly root: string;\n\treadonly name: string;\n\treadonly files: readonly string[];\n}\n\n/** 能力按类型分目录;空目录留 .gitkeep,否则 git 不会带上它们,作者得自己猜该放哪。 */\nconst HUB_ABILITY_DIRS = [\"plugins\", \"mcp\", \"skills\", \"scenes\"] as const;\n\nexport function initHubRepository(input: InitHubInput): InitHubResult {\n\tif (!HUB_NAME_PATTERN.test(input.name)) {\n\t\tthrow new Error(`Invalid marketplace name ${JSON.stringify(input.name)}: use lowercase kebab-case`);\n\t}\n\tlet repository: URL;\n\ttry {\n\t\trepository = new URL(input.repository);\n\t} catch {\n\t\tthrow new Error(`Invalid repository URL: ${input.repository}`);\n\t}\n\tif (repository.protocol !== \"https:\") throw new Error(\"Repository URL must use https://\");\n\tif (!APP_VERSION_PATTERN.test(input.minAppVersion)) {\n\t\tthrow new Error(`Invalid --min-app-version ${JSON.stringify(input.minAppVersion)}: expected a version like 0.55.0`);\n\t}\n\n\tconst root = resolve(input.targetDir);\n\tconst manifestRelativePath = join(\".vetta\", \"marketplace.json\");\n\tif (existsSync(join(root, manifestRelativePath))) {\n\t\tthrow new Error(`Refusing to overwrite an existing marketplace at ${root}`);\n\t}\n\n\tconst files: Record<string, string> = {\n\t\t[manifestRelativePath]: json({\n\t\t\tschemaVersion: 2,\n\t\t\tname: input.name,\n\t\t\tmarketplaceVersion: \"1.0.0\",\n\t\t\trepository: repository.toString().replace(/\\/$/, \"\"),\n\t\t\tminAppVersion: input.minAppVersion,\n\t\t\tabilities: [],\n\t\t}),\n\t\t\"AGENTS.md\": renderHubAgentsGuide({ name: input.name }),\n\t\t\"README.md\": renderHubReadme({ name: input.name, repository: repository.toString().replace(/\\/$/, \"\") }),\n\t\t[join(\".github\", \"workflows\", \"marketplace.yml\")]: renderHubWorkflow(),\n\t\t// dist/ 刻意不忽略:客户端直接读能力目录安装,不会替作者构建。\n\t\t\".gitignore\": \"node_modules/\\nrelease/\\n\",\n\t};\n\tfor (const dir of HUB_ABILITY_DIRS) files[join(\"abilities\", dir, \".gitkeep\")] = \"\";\n\n\tfor (const [relativePath, content] of Object.entries(files)) {\n\t\tconst target = join(root, relativePath);\n\t\tmkdirSync(dirname(target), { recursive: true });\n\t\twriteFileSync(target, content, \"utf8\");\n\t}\n\n\treturn { root, name: input.name, files: Object.keys(files).sort() };\n}\n"]}
|