@ifrankwang/openspec-agents 0.119.1 → 0.120.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/.mcp-server/cli.mjs +39583 -0
- package/.mcp-server/dashboard/index.html +295 -0
- package/README.md +17 -3
- package/assets/skills/java-quality-gate/SKILL.md +4 -0
- package/assets/skills/orchestrator/SKILL.md +33 -1
- package/assets/skills/quality-gate/SKILL.md +4 -0
- package/assets/workflows/task.yaml +4 -2
- package/dsh/cordis.patch.yml +614 -0
- package/package.json +19 -2
- package/src/adapters/codex/index.ts +5 -4
- package/src/adapters/deepseek-harness/index.ts +205 -0
- package/src/adapters/mcp-common/cli.ts +4 -1
- package/src/adapters/mcp-common/server.ts +4 -3
- package/src/adapters/plugin-common/index.ts +12 -6
- package/src/core/workflow/status.ts +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ifrankwang/openspec-agents",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.120.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./src/core/index.ts",
|
|
6
6
|
"exports": {
|
|
@@ -36,6 +36,14 @@
|
|
|
36
36
|
"import": "./src/adapters/zcode/index.ts",
|
|
37
37
|
"types": "./src/adapters/zcode/index.ts"
|
|
38
38
|
},
|
|
39
|
+
"./deepseek-harness": {
|
|
40
|
+
"import": "./src/adapters/deepseek-harness/index.ts",
|
|
41
|
+
"types": "./src/adapters/deepseek-harness/index.ts"
|
|
42
|
+
},
|
|
43
|
+
"./dsh": {
|
|
44
|
+
"import": "./src/adapters/deepseek-harness/index.ts",
|
|
45
|
+
"types": "./src/adapters/deepseek-harness/index.ts"
|
|
46
|
+
},
|
|
39
47
|
"./mcp": {
|
|
40
48
|
"import": "./src/adapters/mcp-common/index.ts",
|
|
41
49
|
"types": "./src/adapters/mcp-common/index.ts"
|
|
@@ -47,11 +55,15 @@
|
|
|
47
55
|
},
|
|
48
56
|
"files": [
|
|
49
57
|
"src",
|
|
50
|
-
"assets"
|
|
58
|
+
"assets",
|
|
59
|
+
"dsh",
|
|
60
|
+
".mcp-server"
|
|
51
61
|
],
|
|
52
62
|
"scripts": {
|
|
53
63
|
"sync": "bun scripts/sync.ts",
|
|
54
64
|
"build:plugins": "bun scripts/build-plugins.ts",
|
|
65
|
+
"build:dsh": "bun scripts/build-dsh-root.ts",
|
|
66
|
+
"prepack": "bun run build:dsh",
|
|
55
67
|
"test": "bun test",
|
|
56
68
|
"typecheck": "tsc --noEmit"
|
|
57
69
|
},
|
|
@@ -65,6 +77,11 @@
|
|
|
65
77
|
"js-yaml": "^4.1.0",
|
|
66
78
|
"@opencode-ai/plugin": "^1.17.0"
|
|
67
79
|
},
|
|
80
|
+
"dsh": {
|
|
81
|
+
"bundle": {
|
|
82
|
+
"patch": "./dsh/cordis.patch.yml"
|
|
83
|
+
}
|
|
84
|
+
},
|
|
68
85
|
"publishConfig": {
|
|
69
86
|
"access": "public"
|
|
70
87
|
},
|
|
@@ -73,12 +73,12 @@ export function injectCodexMcp(repoRoot: string, serverEntry: string): void {
|
|
|
73
73
|
mkdirSync(dirname(target), { recursive: true })
|
|
74
74
|
const existing = existsSync(target) ? readFileSync(target, "utf-8") : ""
|
|
75
75
|
const block = [
|
|
76
|
-
`[mcp_servers.
|
|
76
|
+
`[mcp_servers.opx]`,
|
|
77
77
|
`command = "node"`,
|
|
78
|
-
`args = ["${tomlEscape(serverEntry)}", "--transport", "stdio", "--worktree", ".", "--unattended"]`,
|
|
78
|
+
`args = ["${tomlEscape(serverEntry)}", "--transport", "stdio", "--worktree", ".", "--unattended", "--strip-opx-prefix"]`,
|
|
79
79
|
].join("\n")
|
|
80
80
|
// 已存在同名 server 块则整体替换,否则追加
|
|
81
|
-
const re = /\[mcp_servers\.
|
|
81
|
+
const re = /\[mcp_servers\.opx\][\s\S]*?(?=\n\[|\n*$)/m
|
|
82
82
|
const next = re.test(existing) ? existing.replace(re, block) : `${existing.trimEnd()}\n\n${block}\n`
|
|
83
83
|
writeFileSync(target, next, "utf-8")
|
|
84
84
|
}
|
|
@@ -137,7 +137,7 @@ export function buildCodexPlugin(outDir: string = CODEX_PLUGIN_DIR): PluginPacka
|
|
|
137
137
|
JSON.stringify(
|
|
138
138
|
{
|
|
139
139
|
mcpServers: {
|
|
140
|
-
|
|
140
|
+
opx: {
|
|
141
141
|
command: "node",
|
|
142
142
|
args: [
|
|
143
143
|
"./.mcp-server/cli.mjs",
|
|
@@ -146,6 +146,7 @@ export function buildCodexPlugin(outDir: string = CODEX_PLUGIN_DIR): PluginPacka
|
|
|
146
146
|
"--worktree",
|
|
147
147
|
".",
|
|
148
148
|
"--unattended",
|
|
149
|
+
"--strip-opx-prefix",
|
|
149
150
|
],
|
|
150
151
|
cwd: ".",
|
|
151
152
|
},
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DeepSeek Harness (DSH) 适配器:生成 DSH bundle 插件包。
|
|
3
|
+
*
|
|
4
|
+
* DSH 的插件形态不是 Claude/Codex/ZCode 的 plugin.json,而是 npm 包 +
|
|
5
|
+
* package.json 中 `dsh.bundle.patch` 指向的 cordis patch 文件。本适配器复用
|
|
6
|
+
* plugin-common 的 MCP server bundle / skills / workflows 资产构建,生成一个
|
|
7
|
+
* 可直接用 `dsh plugin --profile <name> add <dir>` 安装的 bundle 包。
|
|
8
|
+
*
|
|
9
|
+
* 生成的 patch 不引入自研 Cordis 插件,而是直接插入 DSH 官方能力:
|
|
10
|
+
* - `@deepseek-ai/dsh-mcp-client`:把本项目 MCP server(opx_* 工具)桥接为
|
|
11
|
+
* DSH 原生工具(mcp__opx__*)。
|
|
12
|
+
* - `@deepseek-ai/dsh-skill-filesystem`:把 assets/skills 注册为额外 skill 根,
|
|
13
|
+
* 使 orchestrator 与各质量维度 skill 可被 DSH agent 发现/加载。
|
|
14
|
+
* - `@deepseek-ai/dsh-tool-subagent`:为每个 assets/agents 子代理生成一个
|
|
15
|
+
* DSH 原生 subagent 工具(如 openspec_architect / openspec_developer),
|
|
16
|
+
* 子代理 persona 取自对应 agent.md,真正接入 DSH 子代理体系。
|
|
17
|
+
*/
|
|
18
|
+
import { writeFileSync, mkdirSync, rmSync, readFileSync, readdirSync } from "node:fs"
|
|
19
|
+
import { join } from "node:path"
|
|
20
|
+
import * as yaml from "js-yaml"
|
|
21
|
+
import { parseAgentMd, resolve, EXCLUDED_AGENTS } from "../agent-md.ts"
|
|
22
|
+
import {
|
|
23
|
+
PLUGIN_DESCRIPTION,
|
|
24
|
+
bundleMcpServer,
|
|
25
|
+
buildAgents,
|
|
26
|
+
buildSkills,
|
|
27
|
+
copyWorkflows,
|
|
28
|
+
readPkgVersion,
|
|
29
|
+
type PluginPackageResult,
|
|
30
|
+
} from "../plugin-common/index.ts"
|
|
31
|
+
|
|
32
|
+
/** DSH bundle 的 npm 包名(与项目发布包一致,便于 `dsh plugin add` 与 sync 识别)。 */
|
|
33
|
+
export const DSH_PLUGIN_NAME = "@ifrankwang/openspec-agents"
|
|
34
|
+
|
|
35
|
+
/** 默认插件包输出目录(dist/ 已 gitignore,生成物不入库)。 */
|
|
36
|
+
export const DEEP_SEEK_HARNESS_PLUGIN_DIR = resolve("dist", "deepseek-harness-plugin")
|
|
37
|
+
/** 简写别名,便于与 `dsh` 缩写保持一致。 */
|
|
38
|
+
export const DSH_PLUGIN_DIR = DEEP_SEEK_HARNESS_PLUGIN_DIR
|
|
39
|
+
|
|
40
|
+
const MANIFEST_PATCH_FILENAME = "cordis.patch.yml"
|
|
41
|
+
|
|
42
|
+
/** MCP client 与 skill 根的基础 patch;子代理工具行由 buildDshPatchContent 动态追加。 */
|
|
43
|
+
const DSH_BASE_PATCH_YAML = `# DeepSeek Harness (DSH) bundle patch: expose OpenSpec MCP tools and skills.
|
|
44
|
+
# The patch is applied as a bundle layer under dsh.profile.bundles.
|
|
45
|
+
# baseUrl 指向 profile 根目录;bundle 安装后位于 profile 的 node_modules 下,
|
|
46
|
+
# 因此从这里引用 node_modules/@ifrankwang/openspec-agents/... 是稳定的。
|
|
47
|
+
- insert:
|
|
48
|
+
- id: openspec-mcp
|
|
49
|
+
name: '@deepseek-ai/dsh-mcp-client'
|
|
50
|
+
config:
|
|
51
|
+
serverName: opx
|
|
52
|
+
transport: stdio
|
|
53
|
+
command: node
|
|
54
|
+
args:
|
|
55
|
+
- !!js "process.getBuiltinModule('node:url').fileURLToPath(new URL('./node_modules/@ifrankwang/openspec-agents/.mcp-server/cli.mjs', baseUrl))"
|
|
56
|
+
- '--transport'
|
|
57
|
+
- 'stdio'
|
|
58
|
+
- '--worktree'
|
|
59
|
+
- !!js "process.cwd()"
|
|
60
|
+
- '--unattended'
|
|
61
|
+
- '--strip-opx-prefix'
|
|
62
|
+
cwd: !!js "process.getBuiltinModule('node:url').fileURLToPath(new URL('./node_modules/@ifrankwang/openspec-agents', baseUrl))"
|
|
63
|
+
- id: openspec-skills
|
|
64
|
+
name: '@deepseek-ai/dsh-skill-filesystem'
|
|
65
|
+
config:
|
|
66
|
+
providerName: openspec-filesystem
|
|
67
|
+
includeDefaultRoots: false
|
|
68
|
+
customSkillDirs:
|
|
69
|
+
- !!js "process.getBuiltinModule('node:url').fileURLToPath(new URL('./node_modules/@ifrankwang/openspec-agents/assets/skills', baseUrl))"
|
|
70
|
+
`
|
|
71
|
+
|
|
72
|
+
interface DshSubagentEntry {
|
|
73
|
+
id: string
|
|
74
|
+
name: string
|
|
75
|
+
config: Record<string, unknown>
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/** 将 openspec-architect 转换为 openspec_architect 这类 DSH 工具名后缀。 */
|
|
79
|
+
function dshAgentSuffix(name: string): string {
|
|
80
|
+
return name.replace(/^openspec-/, "").replace(/-/g, "_")
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/** 根据 agent.md frontmatter 判断是否应禁止 DSH 子代理的写文件类工具。 */
|
|
84
|
+
function denyEditTools(frontmatter: Record<string, unknown>): boolean {
|
|
85
|
+
const permission = frontmatter.permission as Record<string, unknown> | undefined
|
|
86
|
+
const edit = permission?.edit
|
|
87
|
+
if (edit === "deny") return true
|
|
88
|
+
if (edit && typeof edit === "object") {
|
|
89
|
+
const rule = edit as Record<string, unknown>
|
|
90
|
+
// architect 允许编辑 md,不能整体禁止写工具
|
|
91
|
+
return rule["*"] === "deny" && rule["*.md"] !== "allow"
|
|
92
|
+
}
|
|
93
|
+
return false
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/** DSH 子代理 persona 前置的工具接入说明,避免子代理在首次 opx_status 前用 shell 全盘搜索工具。 */
|
|
97
|
+
const DSH_SUBAGENT_TOOL_ACCESS_PREAMBLE = `## DSH 工具接入(必读)
|
|
98
|
+
|
|
99
|
+
- 本流程的 opx_* 工具在 DSH 中以 \`mcp__opx__*\` 形式出现,子代理通常只需要:
|
|
100
|
+
- \`mcp__opx__status\`
|
|
101
|
+
- \`mcp__opx__agent_submit\`
|
|
102
|
+
- 如果当前工具列表中没有这些工具,请立即调用 \`dev_tool_search\` 搜索 \`opx\`,或一次性解锁:
|
|
103
|
+
\`\`\`json
|
|
104
|
+
{"toolNames": ["mcp__opx__status", "mcp__opx__agent_submit"]}
|
|
105
|
+
\`\`\`
|
|
106
|
+
如果计划使用 \`todo_write\` 跟踪任务,也请在同一轮一并解锁。
|
|
107
|
+
- 严禁使用 \`find\` / \`which\` / \`grep\` 等 shell 命令搜索 \`opx\`、\`openspec\`、MCP 配置或安装路径;工具未出现是因为尚未解锁,不是未安装。
|
|
108
|
+
- 解锁后第一件事是调用 \`mcp__opx__status\` 获取上下文,不要先探查仓库或读取状态文件。
|
|
109
|
+
`
|
|
110
|
+
|
|
111
|
+
/** 从 assets/agents/*.md 收集 DSH 子代理工具(跳过主代理模板)。 */
|
|
112
|
+
function collectDshSubagentEntries(): DshSubagentEntry[] {
|
|
113
|
+
const agentsRoot = resolve("assets", "agents")
|
|
114
|
+
const entries: DshSubagentEntry[] = []
|
|
115
|
+
for (const file of readdirSync(agentsRoot)) {
|
|
116
|
+
if (!file.endsWith(".md")) continue
|
|
117
|
+
if (EXCLUDED_AGENTS.has(file)) continue
|
|
118
|
+
const { frontmatter, body } = parseAgentMd(readFileSync(join(agentsRoot, file), "utf-8"))
|
|
119
|
+
const name = (frontmatter.name as string) ?? file.replace(/\.md$/, "")
|
|
120
|
+
if (!name) continue
|
|
121
|
+
const description = (frontmatter.description as string) ?? ""
|
|
122
|
+
const persona = [DSH_SUBAGENT_TOOL_ACCESS_PREAMBLE, description, body.trim()].filter(Boolean).join("\n\n")
|
|
123
|
+
const suffix = dshAgentSuffix(name)
|
|
124
|
+
entries.push({
|
|
125
|
+
id: `openspec-subagent-${suffix}`,
|
|
126
|
+
name: "@deepseek-ai/dsh-tool-subagent",
|
|
127
|
+
config: {
|
|
128
|
+
provider: "spawn",
|
|
129
|
+
toolName: `openspec_${suffix}`,
|
|
130
|
+
persona,
|
|
131
|
+
enableRunInBackground: false,
|
|
132
|
+
backgroundMode: "one-shot",
|
|
133
|
+
...(denyEditTools(frontmatter)
|
|
134
|
+
? { toolFilter: { deny: ["write", "edit", "apply_patch", "str_replace_editor"] } }
|
|
135
|
+
: {}),
|
|
136
|
+
},
|
|
137
|
+
})
|
|
138
|
+
}
|
|
139
|
+
return entries
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/** 生成完整的 DSH cordis patch 内容:基础 MCP/skills + 每个子代理一个 DSH 原生 subagent 工具。 */
|
|
143
|
+
export function buildDshPatchContent(): string {
|
|
144
|
+
const subagents = collectDshSubagentEntries()
|
|
145
|
+
if (subagents.length === 0) return DSH_BASE_PATCH_YAML
|
|
146
|
+
const subagentsYaml = yaml
|
|
147
|
+
.dump(subagents, { lineWidth: -1 })
|
|
148
|
+
.split("\n")
|
|
149
|
+
.map((line) => (line ? ` ${line}` : line))
|
|
150
|
+
.join("\n")
|
|
151
|
+
return `${DSH_BASE_PATCH_YAML.trimEnd()}\n${subagentsYaml}\n`
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
export type DeepSeekHarnessPluginBuildResult = PluginPackageResult
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* 生成 DeepSeek Harness bundle 插件包到 outDir(默认 dist/deepseek-harness-plugin/):
|
|
158
|
+
* package.json(含 dsh.bundle.patch)、cordis.patch.yml、.mcp-server/cli.mjs bundle、
|
|
159
|
+
* assets/skills/、assets/workflows/。
|
|
160
|
+
*
|
|
161
|
+
* 返回生成的组件清单供调用方/测试断言;DSH 原生不消费 Markdown agent 文件,
|
|
162
|
+
* 但为保持与其它插件包一致仍会复制 agents/ 目录。
|
|
163
|
+
*/
|
|
164
|
+
export function buildDeepSeekHarnessPlugin(outDir: string = DEEP_SEEK_HARNESS_PLUGIN_DIR): DeepSeekHarnessPluginBuildResult {
|
|
165
|
+
rmSync(outDir, { recursive: true, force: true })
|
|
166
|
+
mkdirSync(join(outDir, "agents"), { recursive: true })
|
|
167
|
+
mkdirSync(join(outDir, "skills"), { recursive: true })
|
|
168
|
+
mkdirSync(join(outDir, "assets"), { recursive: true })
|
|
169
|
+
mkdirSync(join(outDir, ".mcp-server"), { recursive: true })
|
|
170
|
+
|
|
171
|
+
const version = readPkgVersion()
|
|
172
|
+
// DSH 通过上方 subagent 工具消费 agent 定义;agents/ 目录仍保留原始 markdown 供参考/审计。
|
|
173
|
+
const agents = buildAgents(join(outDir, "agents"))
|
|
174
|
+
const skills = buildSkills(join(outDir, "skills"))
|
|
175
|
+
bundleMcpServer(outDir)
|
|
176
|
+
copyWorkflows(outDir)
|
|
177
|
+
|
|
178
|
+
writeFileSync(
|
|
179
|
+
join(outDir, "package.json"),
|
|
180
|
+
JSON.stringify(
|
|
181
|
+
{
|
|
182
|
+
name: DSH_PLUGIN_NAME,
|
|
183
|
+
version,
|
|
184
|
+
description: PLUGIN_DESCRIPTION,
|
|
185
|
+
type: "module",
|
|
186
|
+
private: true,
|
|
187
|
+
dsh: {
|
|
188
|
+
bundle: {
|
|
189
|
+
patch: `./${MANIFEST_PATCH_FILENAME}`,
|
|
190
|
+
},
|
|
191
|
+
},
|
|
192
|
+
},
|
|
193
|
+
null,
|
|
194
|
+
2,
|
|
195
|
+
) + "\n",
|
|
196
|
+
"utf-8",
|
|
197
|
+
)
|
|
198
|
+
|
|
199
|
+
writeFileSync(join(outDir, MANIFEST_PATCH_FILENAME), buildDshPatchContent(), "utf-8")
|
|
200
|
+
|
|
201
|
+
return { pluginDir: outDir, version, agents, skills }
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/** `dsh` 缩写的构建函数别名。 */
|
|
205
|
+
export const buildDshPlugin = buildDeepSeekHarnessPlugin
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* - --port <n> HTTP transport 端口(默认 4525)
|
|
5
5
|
* - --transport http|stdio(默认 http)
|
|
6
6
|
* - --unattended 默认无人值守(claude code / codex / zcode 适配器分发时开启)
|
|
7
|
+
* - --strip-opx-prefix 去掉 MCP 工具名的 opx_ 前缀(DSH 使用,得到 mcp__opx__status 这类短名)
|
|
7
8
|
*
|
|
8
9
|
* 运行:node <entry> --transport stdio --worktree . --unattended
|
|
9
10
|
*/
|
|
@@ -18,6 +19,7 @@ interface CliOptions {
|
|
|
18
19
|
port: number
|
|
19
20
|
transport: "http" | "stdio"
|
|
20
21
|
unattended: boolean
|
|
22
|
+
stripOpxPrefix: boolean
|
|
21
23
|
}
|
|
22
24
|
|
|
23
25
|
function parseArgs(argv: string[]): CliOptions {
|
|
@@ -41,13 +43,14 @@ function parseArgs(argv: string[]): CliOptions {
|
|
|
41
43
|
port: args.get("port") ? Number(args.get("port")) : DEFAULT_PORT,
|
|
42
44
|
transport,
|
|
43
45
|
unattended: flags.has("unattended"),
|
|
46
|
+
stripOpxPrefix: flags.has("strip-opx-prefix"),
|
|
44
47
|
}
|
|
45
48
|
}
|
|
46
49
|
|
|
47
50
|
export async function main(argv: string[]): Promise<void> {
|
|
48
51
|
const opts = parseArgs(argv)
|
|
49
52
|
if (opts.transport === "stdio") {
|
|
50
|
-
const mcp = buildMcpServer(opts.worktree, { unattended: opts.unattended })
|
|
53
|
+
const mcp = buildMcpServer(opts.worktree, { unattended: opts.unattended, stripOpxPrefix: opts.stripOpxPrefix })
|
|
51
54
|
await mcp.connect(new StdioServerTransport())
|
|
52
55
|
return
|
|
53
56
|
}
|
|
@@ -127,13 +127,14 @@ const PKG_VERSION =
|
|
|
127
127
|
) as { version: string }).version
|
|
128
128
|
|
|
129
129
|
/** 构建承载 6 个 opx_* 工具的 MCP server。unattended=true 时启用默认无人值守(spec: unattended-default)。 */
|
|
130
|
-
export function buildMcpServer(worktree: string, opts: { unattended?: boolean } = {}): McpServer {
|
|
130
|
+
export function buildMcpServer(worktree: string, opts: { unattended?: boolean; stripOpxPrefix?: boolean } = {}): McpServer {
|
|
131
131
|
const mcp = new McpServer({ name: "openspec-agents", version: PKG_VERSION })
|
|
132
132
|
for (const [name, spec] of Object.entries(TOOL_SPECS)) {
|
|
133
|
+
const toolName = opts.stripOpxPrefix ? name.replace(/^opx_/, "") : name
|
|
133
134
|
mcp.registerTool<any, any>(
|
|
134
|
-
|
|
135
|
+
toolName,
|
|
135
136
|
{
|
|
136
|
-
title:
|
|
137
|
+
title: toolName,
|
|
137
138
|
description: spec.description,
|
|
138
139
|
inputSchema: jsonSchemaToZod(withAgentArg(spec.schema)),
|
|
139
140
|
},
|
|
@@ -22,7 +22,7 @@ export const PLUGIN_DESCRIPTION =
|
|
|
22
22
|
/** plugin.json author(package.json 无 author 字段,取固定值)。 */
|
|
23
23
|
export const PLUGIN_AUTHOR = "openspec-agents maintainers"
|
|
24
24
|
|
|
25
|
-
function readPkgVersion(): string {
|
|
25
|
+
export function readPkgVersion(): string {
|
|
26
26
|
return (JSON.parse(readFileSync(resolve("package.json"), "utf-8")) as { version: string }).version
|
|
27
27
|
}
|
|
28
28
|
|
|
@@ -30,7 +30,7 @@ function readPkgVersion(): string {
|
|
|
30
30
|
* 构建 MCP server 自包含 bundle(node cli.ts 直跑依赖 node_modules,插件安装后无此环境,
|
|
31
31
|
* 故 bundle 单文件至插件包内,node 直接执行)。打包依赖本机 bun CLI(spawnSync("bun"))。
|
|
32
32
|
*/
|
|
33
|
-
function bundleMcpServer(pluginDir: string): void {
|
|
33
|
+
export function bundleMcpServer(pluginDir: string): void {
|
|
34
34
|
const out = join(pluginDir, ".mcp-server", "cli.mjs")
|
|
35
35
|
mkdirSync(join(pluginDir, ".mcp-server"), { recursive: true })
|
|
36
36
|
// dashboard 页面资源随 bundle 同目录放置(page.ts 按此布局探测)
|
|
@@ -55,7 +55,7 @@ function bundleMcpServer(pluginDir: string): void {
|
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
/** 转换 agents:从 assets/agents/*.md 取子代理(排除主代理模板),frontmatter 仅保留 name/description。 */
|
|
58
|
-
function buildAgents(targetDir: string): string[] {
|
|
58
|
+
export function buildAgents(targetDir: string): string[] {
|
|
59
59
|
const agentsRoot = resolve("assets", "agents")
|
|
60
60
|
const injected: string[] = []
|
|
61
61
|
for (const file of readdirSync(agentsRoot)) {
|
|
@@ -80,7 +80,7 @@ function buildAgents(targetDir: string): string[] {
|
|
|
80
80
|
}
|
|
81
81
|
|
|
82
82
|
/** 复制 skills:整个 skill 目录(SKILL.md + reference/ 等附属文件),含 orchestrator skill。 */
|
|
83
|
-
function buildSkills(targetDir: string): string[] {
|
|
83
|
+
export function buildSkills(targetDir: string): string[] {
|
|
84
84
|
const skillsRoot = resolve("assets", "skills")
|
|
85
85
|
const injected: string[] = []
|
|
86
86
|
for (const entry of readdirSync(skillsRoot, { withFileTypes: true })) {
|
|
@@ -93,6 +93,11 @@ function buildSkills(targetDir: string): string[] {
|
|
|
93
93
|
return injected
|
|
94
94
|
}
|
|
95
95
|
|
|
96
|
+
/** 复制 workflow 定义到插件包(bundle 内按部署深度逐级上溯探测读取)。 */
|
|
97
|
+
export function copyWorkflows(targetDir: string): void {
|
|
98
|
+
cpSync(resolve("assets", "workflows"), join(targetDir, "assets", "workflows"), { recursive: true })
|
|
99
|
+
}
|
|
100
|
+
|
|
96
101
|
export interface PluginPackageParams {
|
|
97
102
|
/** 输出目录(如 dist/zcode-plugin/) */
|
|
98
103
|
outDir: string
|
|
@@ -123,7 +128,7 @@ export function buildPluginPackage({ outDir, manifestDirName }: PluginPackagePar
|
|
|
123
128
|
bundleMcpServer(outDir)
|
|
124
129
|
// workflow 定义随包分发:bundle 内 TASK_WORKFLOW_PATH 按部署深度逐级上溯探测
|
|
125
130
|
// assets/workflows/task.yaml,插件根(dist/cache 形态)上溯 1 级即命中
|
|
126
|
-
|
|
131
|
+
copyWorkflows(outDir)
|
|
127
132
|
|
|
128
133
|
writeFileSync(
|
|
129
134
|
join(outDir, manifestDirName, "plugin.json"),
|
|
@@ -149,7 +154,7 @@ export function buildPluginPackage({ outDir, manifestDirName }: PluginPackagePar
|
|
|
149
154
|
JSON.stringify(
|
|
150
155
|
{
|
|
151
156
|
mcpServers: {
|
|
152
|
-
|
|
157
|
+
opx: {
|
|
153
158
|
type: "stdio",
|
|
154
159
|
command: "node",
|
|
155
160
|
args: [
|
|
@@ -159,6 +164,7 @@ export function buildPluginPackage({ outDir, manifestDirName }: PluginPackagePar
|
|
|
159
164
|
"--worktree",
|
|
160
165
|
"${CLAUDE_PROJECT_DIR}",
|
|
161
166
|
"--unattended",
|
|
167
|
+
"--strip-opx-prefix",
|
|
162
168
|
],
|
|
163
169
|
},
|
|
164
170
|
},
|
|
@@ -565,7 +565,7 @@ function renderAgentWorking(
|
|
|
565
565
|
lines.push(...eff.lines)
|
|
566
566
|
n = eff.nextNum
|
|
567
567
|
if (tg.worktreePath && tg.baseRef) {
|
|
568
|
-
lines.push(`${n++}. 用上方「变更范围」命令获取本 change
|
|
568
|
+
lines.push(`${n++}. 用上方「变更范围」命令获取本 change 全部已提交变更文件清单,作为本次实施/修改范围;审查/报告范围以当前 step 指令为准`)
|
|
569
569
|
}
|
|
570
570
|
lines.push(`${n++}. 执行当前 step(\`${rec.stepId}\`)职责范围内的全部工作——遵循所有已加载 skill 的全部规范与约束`)
|
|
571
571
|
lines.push(`${n++}. 逐项检视所有已加载 skill 的 MUST 规范,确认全部满足(不满足则补做,不得跳过)`)
|