@ifrankwang/openspec-agents 0.144.0 → 0.146.0
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/.dsh-plugin/opx-tools.mjs +9054 -0
- package/.mcp-server/cli.mjs +86 -75
- package/README.md +5 -1
- package/assets/skills/openspec-standard/SKILL.md +1 -1
- package/assets/skills/openspec-standard/templates/openspec/config.yaml +3 -0
- package/assets/skills/openspec-standard/templates/schemas/spec-driven-standard/README.md +5 -3
- package/assets/skills/openspec-standard/templates/schemas/spec-driven-standard/schema.yaml +31 -2
- package/assets/skills/openspec-standard/templates/schemas/spec-driven-standard/templates/tasks.md +5 -0
- package/assets/skills/orchestrator/SKILL.md +15 -18
- package/assets/workflows/review-simple.yaml +2 -2
- package/assets/workflows/review.yaml +2 -2
- package/assets/workflows/task-simple.yaml +1 -1
- package/assets/workflows/task.yaml +2 -2
- package/dsh/cordis.patch.yml +21 -25
- package/package.json +6 -3
- package/src/adapters/deepseek-harness/index.ts +51 -37
- package/src/adapters/deepseek-harness/native-tools.ts +196 -0
- package/src/adapters/deepseek-harness/plugin.ts +61 -0
- package/src/adapters/deepseek-harness/schema-dsl.ts +129 -0
- package/src/adapters/mcp-common/cli.ts +1 -1
- package/src/adapters/mcp-common/server.ts +14 -41
- package/src/adapters/plugin-common/index.ts +35 -1
- package/src/adapters/tool-specs.ts +75 -0
- package/src/types/dsh-runtime.d.ts +162 -0
|
@@ -29,8 +29,10 @@ export function readPkgVersion(): string {
|
|
|
29
29
|
/**
|
|
30
30
|
* 构建 MCP server 自包含 bundle(node cli.ts 直跑依赖 node_modules,插件安装后无此环境,
|
|
31
31
|
* 故 bundle 单文件至插件包内,node 直接执行)。打包依赖本机 bun CLI(spawnSync("bun"))。
|
|
32
|
+
*
|
|
33
|
+
* 返回 bundle 入口的绝对路径,供调用方记录/断言实际产出位置(各 harness 的 MCP 入口即此文件)。
|
|
32
34
|
*/
|
|
33
|
-
export function bundleMcpServer(pluginDir: string):
|
|
35
|
+
export function bundleMcpServer(pluginDir: string): string {
|
|
34
36
|
const out = join(pluginDir, ".mcp-server", "cli.mjs")
|
|
35
37
|
mkdirSync(join(pluginDir, ".mcp-server"), { recursive: true })
|
|
36
38
|
// dashboard 页面资源随 bundle 同目录放置(page.ts 按此布局探测)
|
|
@@ -52,6 +54,38 @@ export function bundleMcpServer(pluginDir: string): void {
|
|
|
52
54
|
if (result.status !== 0) {
|
|
53
55
|
throw new Error(`MCP server bundle 失败:${result.stderr ?? result.error?.message ?? "未知错误"}`)
|
|
54
56
|
}
|
|
57
|
+
return out
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* 构建 DSH 原生工具插件 bundle(node 直接被 cordis import,不依赖 node_modules)。
|
|
62
|
+
*
|
|
63
|
+
* 与 bundleMcpServer 的差异:DSH 官方包(@deepseek-ai/*)必须保持 external——它们由 DSH 安装目录
|
|
64
|
+
* 提供,插件在 profile 的 node_modules 下被 import 时由 DSH 进程自身解析;本项目内核与其余依赖
|
|
65
|
+
* 全部内联,使插件包无需附带任何 node_modules。
|
|
66
|
+
*/
|
|
67
|
+
export function bundleDshNativeTools(pluginDir: string): void {
|
|
68
|
+
const outDir = join(pluginDir, ".dsh-plugin")
|
|
69
|
+
mkdirSync(outDir, { recursive: true })
|
|
70
|
+
const result = spawnSync(
|
|
71
|
+
"bun",
|
|
72
|
+
[
|
|
73
|
+
"build",
|
|
74
|
+
resolve("src", "adapters", "deepseek-harness", "plugin.ts"),
|
|
75
|
+
"--outfile",
|
|
76
|
+
join(outDir, "opx-tools.mjs"),
|
|
77
|
+
"--target",
|
|
78
|
+
"node",
|
|
79
|
+
"--external",
|
|
80
|
+
"@deepseek-ai/*",
|
|
81
|
+
"--define",
|
|
82
|
+
`__OPX_PKG_VERSION__="${readPkgVersion()}"`,
|
|
83
|
+
],
|
|
84
|
+
{ encoding: "utf-8", stdio: ["ignore", "ignore", "pipe"] },
|
|
85
|
+
)
|
|
86
|
+
if (result.status !== 0) {
|
|
87
|
+
throw new Error(`DSH 原生工具插件 bundle 失败:${result.stderr ?? result.error?.message ?? "未知错误"}`)
|
|
88
|
+
}
|
|
55
89
|
}
|
|
56
90
|
|
|
57
91
|
/** 转换 agents:从 assets/agents/*.md 取子代理(排除主代理模板),frontmatter 仅保留 name/description。 */
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 6 个 opx_* 工具的名称、描述与参数 schema 单一事实源(DRY)。
|
|
3
|
+
*
|
|
4
|
+
* MCP 形态(claude-code / codex / zcode / opencode 外的各 harness)与 DSH 原生工具形态
|
|
5
|
+
* 消费同一张表:描述文案与参数 schema 只在此定义一次,任一形态改动不会与另一形态漂移。
|
|
6
|
+
* 两种承载的差异只在参数投影与上下文注入(见各自 adapter)。
|
|
7
|
+
*/
|
|
8
|
+
import type { JSONSchema } from "../core/provider.ts"
|
|
9
|
+
import {
|
|
10
|
+
orchInitSchema, setWorktreeSchema, statusSchema,
|
|
11
|
+
completeTaskGroupSchema, setUnattendedSchema, agentSubmitSchema,
|
|
12
|
+
} from "../core/tools/schemas.ts"
|
|
13
|
+
|
|
14
|
+
export interface OpxToolSpec {
|
|
15
|
+
/** 工具注册名(DSH 原生与 MCP 一律 opx_ 前缀)。 */
|
|
16
|
+
name: string
|
|
17
|
+
description: string
|
|
18
|
+
schema: JSONSchema
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export const OPX_TOOL_NAMES = [
|
|
22
|
+
"opx_orch_init",
|
|
23
|
+
"opx_orch_set_worktree",
|
|
24
|
+
"opx_status",
|
|
25
|
+
"opx_orch_complete_task_group",
|
|
26
|
+
"opx_orch_set_unattended",
|
|
27
|
+
"opx_agent_submit",
|
|
28
|
+
] as const
|
|
29
|
+
|
|
30
|
+
/** 6 个工具的规格表,顺序即注册顺序。 */
|
|
31
|
+
export const OPX_TOOL_SPECS: readonly OpxToolSpec[] = [
|
|
32
|
+
{
|
|
33
|
+
name: "opx_orch_init",
|
|
34
|
+
description:
|
|
35
|
+
"初始化编排会话。传入变更 ID 和任务组 ID,工具自动解析 tasks.md 提取全部任务组并解析目标组子任务。可通过 recovery 参数恢复到指定阶段。无 recovery 重复初始化当前任务组时保留其阶段和进度;切换到其它任务组时初始化该组。可选 mode 参数(full/simple,缺省 simple)选择流程模式:首次新建状态时固化;已开始的变更仅切组(其他任务组均已完成或从未激活)或 recovery.phase=task_analysis 重制当前组(其他任务组同样须已完成或从未激活)时可更新,其余场景传不同 mode 报错。",
|
|
36
|
+
schema: orchInitSchema,
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
name: "opx_orch_set_worktree",
|
|
40
|
+
description:
|
|
41
|
+
"确保目标组的 git worktree 就绪。change 会话按 change 模型 create-or-reuse:分支 change/{changeId} 不存在时从基准分支 tip 创建,worktree 常驻于 .worktree/{changeId}/ws,全部任务组串行复用(复用校验:目录缺失自愈重建、openspec 文档脏自动提交、代码文件脏拒绝)。只补齐资源,不改变阶段。",
|
|
42
|
+
schema: setWorktreeSchema,
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
name: "opx_status",
|
|
46
|
+
description:
|
|
47
|
+
"统一状态/上下文查询(只读为主)。按调用者角色路由:编排视角→统计+worktree;architect→spec/blocker;developer→worktree/boundary/task/issue;reviewer-tool→tool 层控件 issue;reviewer-task→task 验证状态;quality reviewer→自维度既有 issue。",
|
|
48
|
+
schema: statusSchema,
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
name: "opx_orch_complete_task_group",
|
|
52
|
+
description:
|
|
53
|
+
"完成任务组收尾。非最后任务组仅做门禁与范围标记(不合并、不销毁);最后一个任务组收口时把 change 分支(change/{changeId})一次性合并回 baseBranch(漂移无害——文档、openspec 规划路径或仅版本号变更的 package.json——时直接合并收口;漂移含任一其他文件或文本冲突时回退到收尾验证 verify_cleanup 并返回 blocked),成功后销毁 worktree 并删分支。须在收尾验证(verify_cleanup)通过后调用。主仓库本地改动文件与合并写入文件重合、或存在部分暂存文件时中止并返回 blocked(保留 worktree/分支);主仓库无关脏文件不阻塞合并。",
|
|
54
|
+
schema: completeTaskGroupSchema,
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
name: "opx_orch_set_unattended",
|
|
58
|
+
description:
|
|
59
|
+
"开启/关闭无人值守模式。开启后编排流程不再向用户提问:analyze 确认模式由架构师自行裁决(不确认用户);重试检查点、状态异常、blocker 处理等需拍板事项由主代理按编排行为准则自行决策并提交——检查点决策是 opx_agent_submit 工具调用,主代理可自行执行,不因无人值守而抑制。",
|
|
60
|
+
schema: setUnattendedSchema,
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
name: "opx_agent_submit",
|
|
64
|
+
description:
|
|
65
|
+
"通用 step 提交,按 step_id 路由到 workflow 对应 step。校验调用者属于该 step 的 agents(越权直接拒绝),提交后推进 workflow 状态机并写回编排状态。可通过 exempt_adjudications 对已申请豁免的 issue 进行裁定(dismissed→cancelled、rejected→回 todo);可通过 recheck_adjudications 复核已修复待复核(review 态)的 issue(passed→done、rejected→回 todo 并记 refix_count 与 reject_reason,谁提谁裁定)。",
|
|
66
|
+
schema: agentSubmitSchema,
|
|
67
|
+
},
|
|
68
|
+
]
|
|
69
|
+
|
|
70
|
+
/** 规格表查表。 */
|
|
71
|
+
export function opxToolSpec(name: string): OpxToolSpec {
|
|
72
|
+
const spec = OPX_TOOL_SPECS.find((s) => s.name === name)
|
|
73
|
+
if (!spec) throw new Error(`未注册的 opx 工具:${name}`)
|
|
74
|
+
return spec
|
|
75
|
+
}
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DSH 运行时包的类型声明(ambient)。
|
|
3
|
+
*
|
|
4
|
+
* `@deepseek-ai/*` 由 DSH 安装目录在运行时提供(DSH 进程自身解析 node_modules),本项目
|
|
5
|
+
* 不把它们作为运行期/打包依赖——原生工具插件 bundle 把它们标记为 external,打包后仍从 DSH 进程解析。
|
|
6
|
+
* 例外:`@deepseek-ai/schemastery` 另以 devDependency 固定为 DSH 运行时同版本,供插件测试用
|
|
7
|
+
* 真实 Standard Schema 实现校验 Config;它同样不参与打包(仅在测试进程内解析)。
|
|
8
|
+
* 此处只按官方公开的类型形状声明本地所需的最小子集,使 typecheck 可在无 DSH 安装的环境通过。
|
|
9
|
+
*
|
|
10
|
+
* 本文件不写任何顶层 export/import(保持为全局脚本文件),否则其中的 `declare module`
|
|
11
|
+
* 会被 TypeScript 当作模块增强而非 ambient 声明。
|
|
12
|
+
*
|
|
13
|
+
* 形状依据(DSH 0.2.0-rc.2 源码):
|
|
14
|
+
* - defineTool 与 schema DSL:dsh-tools/lib/types/schema.js:274
|
|
15
|
+
* - ctx.tools.register:dsh-tools/README.zh.md:41-57
|
|
16
|
+
* - exec 上下文字段:dsh-tools/lib/types/index.js:790-802(token/callId/rootCallId/name/signal/
|
|
17
|
+
* agent/parent/schema/arguments)
|
|
18
|
+
* - Agent:dsh-agent 的 Agent 接口声明(id/options/session/inbox/status/ctx)
|
|
19
|
+
* - SessionHeader:dsh-session 的 SessionHeader 声明(id/cwd/parentSession/origin/agentPreset/...)
|
|
20
|
+
* - schemastery 的 Standard Schema 接口:schemastery/src/index.ts:154、290(`~standard` getter)
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
declare module "@deepseek-ai/schemastery" {
|
|
24
|
+
/** Standard Schema 的校验问题项(失败时返回)。 */
|
|
25
|
+
export interface SchemaIssue {
|
|
26
|
+
readonly message: string
|
|
27
|
+
readonly path?: readonly unknown[]
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** Standard Schema v1 的校验结果:成功带 value,失败带 issues。 */
|
|
31
|
+
export interface SchemaResult<T = any> {
|
|
32
|
+
readonly value?: T
|
|
33
|
+
readonly issues?: readonly SchemaIssue[]
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* schemastery schema 节点。本项目只消费两处:
|
|
38
|
+
* - `~standard`:cordis 校验插件 Config 的唯一入口(cordis/lib/index.js:956-962 取
|
|
39
|
+
* `Config["~standard"].validate`,故 Config 必须是本类型而非普通对象);
|
|
40
|
+
* - 链式约束 `default`:给未提供的配置项注入默认值。
|
|
41
|
+
*/
|
|
42
|
+
export interface Schema<T = any> {
|
|
43
|
+
readonly "~standard": {
|
|
44
|
+
readonly version: 1
|
|
45
|
+
readonly vendor: "schemastery"
|
|
46
|
+
validate(value: unknown): SchemaResult<T>
|
|
47
|
+
}
|
|
48
|
+
default(value: T): Schema<T>
|
|
49
|
+
required(): Schema<T>
|
|
50
|
+
description(text: string): Schema<T>
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* 根导出即 schemastery 的 `Schema` 本身(可调用/可 new,带 schema 工厂静态方法)。
|
|
55
|
+
* 这里以工厂对象表达本项目用到的最小集合。
|
|
56
|
+
*/
|
|
57
|
+
export const Schema: {
|
|
58
|
+
object<T extends Record<string, any> = Record<string, any>>(dict: { [K in keyof T]: Schema<T[K]> }): Schema<T>
|
|
59
|
+
boolean(): Schema<boolean>
|
|
60
|
+
number(): Schema<number>
|
|
61
|
+
string(): Schema<string>
|
|
62
|
+
array<T>(inner: Schema<T>): Schema<T[]>
|
|
63
|
+
dict<T>(inner: Schema<T>): Schema<Record<string, T>>
|
|
64
|
+
any<T = any>(): Schema<T>
|
|
65
|
+
const<T>(value: T): Schema<T>
|
|
66
|
+
}
|
|
67
|
+
export default Schema
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
declare module "@deepseek-ai/dsh-tools" {
|
|
71
|
+
/** DSH 会话头(dsh-session 的 SessionHeader)。 */
|
|
72
|
+
export interface DshSessionHeader {
|
|
73
|
+
readonly id: string
|
|
74
|
+
readonly cwd?: string
|
|
75
|
+
readonly parentSession?: string
|
|
76
|
+
readonly origin?: "subagent"
|
|
77
|
+
readonly delegationDepth?: number
|
|
78
|
+
readonly agentPreset?: string
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export interface DshSession {
|
|
82
|
+
readonly id: string
|
|
83
|
+
readonly header: DshSessionHeader
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/** DSH 运行时 Agent(本项目只读 id 与 session)。 */
|
|
87
|
+
export interface DshAgent {
|
|
88
|
+
readonly id: string
|
|
89
|
+
readonly session: DshSession
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* 工具执行上下文。本项目只消费 `signal` 与 `agent` 两处:
|
|
94
|
+
* - agent.session.header.cwd 是会话项目目录的唯一来源(原生工具解析 worktree 的依据)。
|
|
95
|
+
* - signal 是协作式取消信号。
|
|
96
|
+
*/
|
|
97
|
+
export interface ToolExecution {
|
|
98
|
+
token: symbol
|
|
99
|
+
callId: string
|
|
100
|
+
rootCallId?: string
|
|
101
|
+
name: string
|
|
102
|
+
signal: AbortSignal
|
|
103
|
+
agent?: DshAgent
|
|
104
|
+
parent?: DshAgent
|
|
105
|
+
arguments?: unknown
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/** DSH 工具内容块(模型可见的返回体)。 */
|
|
109
|
+
export interface ToolContentBlock {
|
|
110
|
+
type: "text"
|
|
111
|
+
text: string
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* DSH 统一 schema DSL 的 value schema / 参数 schema 根。
|
|
116
|
+
* 官方按「未知 key 即作者错误」的封闭词汇编译,这里以开放索引签名表达,
|
|
117
|
+
* 使本项目投影出的强类型 value schema 可直接赋值。
|
|
118
|
+
*/
|
|
119
|
+
export interface ValueSchemaSpec {
|
|
120
|
+
[key: string]: unknown
|
|
121
|
+
}
|
|
122
|
+
export type ParameterSchemaSpec = Record<string, ValueSchemaSpec>
|
|
123
|
+
|
|
124
|
+
export interface ToolOutput<Args = any> {
|
|
125
|
+
schema: ValueSchemaSpec
|
|
126
|
+
render: (args: Args, value: any) => ToolContentBlock[]
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export interface ToolDefinition<Args = any> {
|
|
130
|
+
name: string
|
|
131
|
+
description: string
|
|
132
|
+
parameters: ParameterSchemaSpec
|
|
133
|
+
output: ToolOutput<Args>
|
|
134
|
+
execute(args: Args, exec: ToolExecution): unknown
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export function defineTool<Args = any>(options: {
|
|
138
|
+
name: string
|
|
139
|
+
description: string
|
|
140
|
+
parameters: ParameterSchemaSpec
|
|
141
|
+
output: ToolOutput<Args>
|
|
142
|
+
execute(args: Args, exec: ToolExecution): unknown
|
|
143
|
+
timeoutMs?: number
|
|
144
|
+
deferLoading?: boolean
|
|
145
|
+
presentCall?: (args: Args) => unknown
|
|
146
|
+
presentResult?: (args: Args, result: unknown) => unknown
|
|
147
|
+
isConcurrencySafe?: (args: Args) => boolean
|
|
148
|
+
}): ToolDefinition<Args>
|
|
149
|
+
|
|
150
|
+
/** 工具注册表服务(本项目只用 register)。 */
|
|
151
|
+
export interface ToolRegistry {
|
|
152
|
+
register(definition: ToolDefinition): unknown
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
declare module "@deepseek-ai/cordis" {
|
|
157
|
+
/** 本项目插件只用到 ctx.tools;其余 cordis 服务不消费。 */
|
|
158
|
+
export interface Context {
|
|
159
|
+
tools: import("@deepseek-ai/dsh-tools").ToolRegistry
|
|
160
|
+
[key: string]: any
|
|
161
|
+
}
|
|
162
|
+
}
|