@boyingliu01/dsh-plugin-xp-gate 0.19.3 → 0.20.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.
Files changed (41) hide show
  1. package/README.md +169 -4
  2. package/lib/command.d.ts +8 -3
  3. package/lib/command.d.ts.map +1 -1
  4. package/lib/command.js +23 -12
  5. package/lib/command.js.map +1 -1
  6. package/lib/delphi-config.d.ts +75 -0
  7. package/lib/delphi-config.d.ts.map +1 -0
  8. package/lib/delphi-config.js +155 -0
  9. package/lib/delphi-config.js.map +1 -0
  10. package/lib/delphi-run.d.ts +24 -0
  11. package/lib/delphi-run.d.ts.map +1 -0
  12. package/lib/delphi-run.js +92 -0
  13. package/lib/delphi-run.js.map +1 -0
  14. package/lib/delphi.d.ts +88 -0
  15. package/lib/delphi.d.ts.map +1 -0
  16. package/lib/delphi.js +161 -0
  17. package/lib/delphi.js.map +1 -0
  18. package/lib/dialect.d.ts +24 -0
  19. package/lib/dialect.d.ts.map +1 -0
  20. package/lib/dialect.js +36 -0
  21. package/lib/dialect.js.map +1 -0
  22. package/lib/index.d.ts.map +1 -1
  23. package/lib/index.js +62 -4
  24. package/lib/index.js.map +1 -1
  25. package/package.json +1 -1
  26. package/skills/clipboard-vision/SKILL.md +47 -0
  27. package/skills/clipboard-vision/__tests__/clipboard-vision-distribution.test.cjs +64 -0
  28. package/skills/clipboard-vision/__tests__/clipboard-vision-security.test.cjs +185 -0
  29. package/skills/clipboard-vision/__tests__/clipboard-vision.Tests.ps1 +138 -0
  30. package/skills/clipboard-vision/__tests__/clipboard-vision.test.cjs +273 -0
  31. package/skills/clipboard-vision/describe-clipboard.ps1 +115 -0
  32. package/skills/clipboard-vision/describe-clipboard.sh +161 -0
  33. package/skills/clipboard-vision/scripts/clipboard-vision.ps1 +86 -0
  34. package/skills/clipboard-vision/scripts/clipboard-vision.sh +137 -0
  35. package/skills/delphi-review/AGENTS.md +4 -4
  36. package/skills/delphi-review/SKILL.md +98 -14
  37. package/skills/delphi-review/references/code-walkthrough.md +36 -3
  38. package/skills/delphi-review/references/requirements.md +6 -6
  39. package/skills/sprint-flow/AGENTS.md +4 -4
  40. package/skills/sprint-flow/references/phase-2-design.md +3 -3
  41. package/skills/test-specification-alignment/AGENTS.md +4 -4
package/README.md CHANGED
@@ -1,14 +1,18 @@
1
1
  # @boyingliu01/dsh-plugin-xp-gate
2
2
 
3
- XP-Gate deterministic quality gates + AI workflow skills as a **DeepSeek Harness (DSH)** native plugin.
3
+ XP-Gate 确定性质量门禁 + AI 工作流技能,作为 **DeepSeek Harness (DSH)** 原生插件(bundle)。
4
+
5
+ > 关联 Issue: #393 设计文档: `docs/plans/2026-08-23-dsh-plugin-design.md`
6
+ > 集成评估: `docs/plans/2026-09-30-dsh-integration-assessment.md`
4
7
 
5
8
  ## Tools
6
9
 
7
10
  | Tool | Purpose |
8
11
  |------|---------|
9
12
  | `gate-check` | Run XP-Gate quality gates (`xp-gate check`) on a file or directory |
10
- | `gate-principles` | Run the 14 Clean Code/SOLID rules (`xp-gate principles`, Gate 4) |
13
+ | `gate-principles` | Run the Clean Code/SOLID rules (`xp-gate principles`, Gate 4) |
11
14
  | `gate-arch` | Run architecture validation (`xp-gate arch`, Gate 6) |
15
+ | `delphi-review` | **多模型交叉评审**:用 3 个不同模型独立评审同一份材料并迭代到共识(见下) |
12
16
 
13
17
  ## Install
14
18
 
@@ -16,10 +20,171 @@ XP-Gate deterministic quality gates + AI workflow skills as a **DeepSeek Harness
16
20
  dsh plugin --profile <name> add @boyingliu01/dsh-plugin-xp-gate
17
21
  ```
18
22
 
19
- The tools require a global `xp-gate` CLI:
23
+ The `gate-*` tools require a global `xp-gate` CLI:
20
24
 
21
25
  ```bash
22
26
  npm install -g @boyingliu01/xp-gate
23
27
  ```
24
28
 
25
- When `xp-gate` is missing the tools degrade gracefully (tool-missing → SKIP-style message) rather than erroring.
29
+ When `xp-gate` is missing those tools degrade gracefully (tool-missing → SKIP-style message) rather than erroring.
30
+
31
+ ## 跨平台:shell 方言层
32
+
33
+ DSH 在非 Windows 上使用 POSIX shell、在 Windows 上使用 **PowerShell executor**
34
+ (`dsh-base` bundle 里 `bash-sandbox` 在 win32 被禁用、`pwsh-sandbox` 启用)。
35
+
36
+ 插件最初硬编码 POSIX 命令串,导致三个工具在 Windows 上**全部解析失败**:
37
+
38
+ ```
39
+ [stderr] ParserError:
40
+ 1 | if command -v xp-gate >/dev/null 2>&1; then xp-gate principles 'D:\pr …
41
+ | ~
42
+ | if语句中的'if'后缺少"(”。
43
+ [exit code: 1]
44
+ ```
45
+
46
+ 现已按平台生成对应方言(`src/dialect.ts`):
47
+
48
+ | POSIX | PowerShell |
49
+ |-------|-----------|
50
+ | `command -v X` | `Get-Command X -ErrorAction SilentlyContinue` |
51
+ | `printf '%s\n' '...'` | `Write-Output '...'` |
52
+ | `'\''`(单引号转义) | `''`(单引号内双写) |
53
+
54
+ 实测(Windows + PowerShell executor):`gate-check` exit 0、`gate-principles` exit 0(`Rules run: 15`)。
55
+
56
+ ## 多模型交叉评审(`delphi-review`)
57
+
58
+ ### 为什么需要它,而不是用 DSH 的 subagent
59
+
60
+ Delphi 方法的价值**完全建立在「评审者是不同模型」之上**。DSH 的 `subagent` 工具继承宿主
61
+ 配置的**单一**模型,因此三个"专家"会是同一个模型的三个实例 —— 按 `delphi-review` skill
62
+ 自身的规则必须 BLOCK。
63
+
64
+ 所以本工具**直接调用外部 OpenAI 兼容网关**,每个席位绑定一个不同模型。
65
+
66
+ > 另一条路线是打开 DSH 的 `subagentModelSelection`(Web GUI → **Plugins**),让 subagent
67
+ > 也能选模型。两者并不冲突:那条路线让 DSH 原生 subagent 具备多模型能力,本工具则在
68
+ > **不依赖宿主配置**的前提下保证不变量成立(包括 CI / 一次性会话)。
69
+
70
+ ### 配置
71
+
72
+ 复用项目既有的 `.delphi-config.json`(skill 与工具**同一份配置**):
73
+
74
+ ```jsonc
75
+ {
76
+ "active_profile": "whalecloud",
77
+ "profiles": {
78
+ "whalecloud": {
79
+ "providers": {
80
+ "whalecloud": {
81
+ "base_url": "https://<gateway>/gpt-proxy",
82
+ "api_key": "${WHALECLOUD_API_KEY}" // 支持 ${VAR} 与 {env:VAR} 两种写法
83
+ }
84
+ },
85
+ "experts": { // 对象形式;key 即角色
86
+ "architecture": { "provider": "whalecloud", "model": "g-glm-5.3-flash" },
87
+ "technical": { "provider": "whalecloud", "model": "g-qwen3.8-flash" },
88
+ "feasibility": { "provider": "whalecloud", "model": "g-deepseek-flash" }
89
+ }
90
+ }
91
+ },
92
+ "consensus": { "threshold_percent": 90, "max_review_rounds": 5 }
93
+ }
94
+ ```
95
+
96
+ 密钥**只从环境变量读取**,绝不写入配置文件。
97
+ `experts` 同时接受**对象形式**(key 为角色)与**数组形式**(explicit `role` 字段)。
98
+
99
+ ### 强制的不变量(代码强制,非文档约定)
100
+
101
+ | 不变量 | 违反时 |
102
+ |---|---|
103
+ | 恰好 3 位专家 | `DelphiConfigError` |
104
+ | 3 个 `requested_model` 去空白后两两不同 | `DelphiConfigError` |
105
+ | 模型不得为未展开的 `${...}` 占位符 | `DelphiConfigError`(在网络调用**之前**) |
106
+ | 缺密钥 / 缺 provider / 专家跨网关 | `DelphiSetupError`,报出变量名而非回显配置 |
107
+ | 报告缺 `### VERDICT` 段 | 记为 `INVALID_NO_VERDICT`,**不计入共识**(绝不从散文里猜裁决) |
108
+ | 退出码 0 但内容为空 | 记为 ERROR(推理模型可能把预算全烧在隐藏 token 上) |
109
+ | 共识按**全体席位数**计算 | 一份畸形报告不能靠其余全票"凑"出共识 |
110
+
111
+ 共识阈值为百分比:3 席时 ≥90% 意味着**必须 3/3 通过**。
112
+
113
+ ### 轮次语义
114
+
115
+ - **Round 1 匿名且并发**:各席位互不可见。
116
+ - **Round 2+**:回传**汇总票型**(`architecture: APPROVED` …)与"已修改项",
117
+ 各席位据此修正或坚持立场。**个体报告始终匿名** —— 只共享计票,这是方法可信的前提。
118
+ - 达到阈值即停;否则到 `max_review_rounds`(默认 5)为止,并如实返回 `NO CONSENSUS`。
119
+
120
+ ### 已知行为(实测)
121
+
122
+ 用一段**故意有缺陷**的样例代码喂给三个模型,15 次调用(5 轮 × 3 模型)全部成功且
123
+ `resolved_model` 两两不同,三个模型**独立**指出同一个真实缺陷:
124
+ `{ ok: true }` 是**无条件**返回的常量,"把退出码契约换成了一个常量,而不是真正的结果信号"。
125
+ 机制**拒绝**给出共识,而不是盖章通过 —— 这正是它该有的行为。
126
+
127
+ ## Skills
128
+
129
+ 本包在 publish 时通过 `scripts/prepack.cjs` 捆绑仓库 `skills/`(当前 12 个)。
130
+
131
+ 要让 DSH 直接看到**仓库版** skill(而非 `~/.agents/skills` 里指向 `~/.qoder/skills` 的 junction),
132
+ 运行仓库提供的准备脚本:
133
+
134
+ ```powershell
135
+ pwsh -File plugins/dsh/setup-dsh.ps1 # 全部(skill 链接 + 编译 + 提示)
136
+ pwsh -File plugins/dsh/setup-dsh.ps1 -WhatIf # 只预览
137
+ ```
138
+
139
+ 该脚本在 `$DSH_HOME/skills`(DSH 发现根 rank 400)为仓库每个 skill 建 junction,保持单一真源。
140
+
141
+ > DSH 只读 skill frontmatter 的 6 个字段:`name`、`description`、`whenToUse`、
142
+ > `metadata`、`disable-model-invocation`、`user-invocable`。
143
+ > 本仓库 skill 里的 `allowed-tools` / `hooks` / `triggers` / `maturity` / `auto_continue`
144
+ > **会被静默忽略**(无害,但不生效)。`references/` 子目录可用 —— DSH 的 `skill` 工具返回
145
+ > base directory,模型按需用 read 工具加载。
146
+
147
+ ## 备选路线:启用 DSH 原生 subagent 模型选择
148
+
149
+ 上面的 `delphi-review` 工具不依赖宿主配置即可保证不变量。若你希望 DSH **原生**
150
+ `subagent` 工具也能指定模型(例如让 Agent Teams 的队友跑不同模型),需显式启用
151
+ (Web GUI → **Plugins** 页面):
152
+
153
+ ```
154
+ subagent model selection:
155
+ enabled = true
156
+ allowedModels = [
157
+ { provider: 'whalecloud', model: 'g-glm-5.3-flash' },
158
+ { provider: 'whalecloud', model: 'g-qwen3.8-flash' },
159
+ { provider: 'whalecloud', model: 'g-deepseek-flash' }
160
+ ]
161
+ ```
162
+
163
+ 启用后 `subagent` 工具会新增 `provider` / `model` / `reasoning_effort` 参数,
164
+ 并注册 `list_subagent_models` 工具。
165
+
166
+ > 注意:`dsh-tool-subagent` 的 `modelSelectionSettings: true` **只是能力开关**;
167
+ > 实际策略来自宿主设置 `SubagentModelSelectionConfig`(`enabled` 默认 `false`,
168
+ > `allowedModels` 默认 `[]`)。两者都要满足才会生效。
169
+
170
+ ## Build
171
+
172
+ ```bash
173
+ npm install --ignore-scripts # --ignore-scripts 绕过 esbuild postinstall(受限环境会 EBUSY)
174
+ npm run build # tsc -> lib/
175
+ npm run prepare-skills # 从仓库 skills/ 复制
176
+ npm test # vitest
177
+ ```
178
+
179
+ 或在仓库根目录:
180
+
181
+ ```bash
182
+ npm run build:dsh-plugin # = node scripts/build-plugin.mjs --platform dsh
183
+ ```
184
+
185
+ ## 注意
186
+
187
+ - `lib/` 与 `skills/` 是**构建产物**(已 gitignore),不提交。
188
+ - `scripts/prepack.cjs` 目前硬编码 12 个 skill,**不含 `clipboard-vision`**(仓库有 13 个)。
189
+ - `plugins/dsh/package.json` 的 peerDeps 仍 pin `0.1.1-rc.2`,但实测运行在 `0.2.0-rc.2` 上
190
+ `defineTool` / `TOOL_ABORTED` / `HarnessError` 签名未变,无需改写。
package/lib/command.d.ts CHANGED
@@ -1,3 +1,5 @@
1
+ import { type ShellDialect } from "./dialect.js";
2
+ export { type ShellDialect, detectDialect, shellQuote, shq } from "./dialect.js";
1
3
  /** Shown when the global `xp-gate` CLI is not on PATH. */
2
4
  export declare const FALLBACK_MESSAGE = "[XP-Gate] xp-gate CLI not installed. Install it with: npm install -g @boyingliu01/xp-gate";
3
5
  /**
@@ -7,19 +9,22 @@ export declare const FALLBACK_MESSAGE = "[XP-Gate] xp-gate CLI not installed. In
7
9
  */
8
10
  export declare const GATE_WHITELIST: readonly ["duplicates", "complexity", "principles", "arch", "architecture", "iac", "secrets", "sast"];
9
11
  export type GateId = (typeof GATE_WHITELIST)[number];
10
- /** POSIX single-quote shell escaping: `'` becomes `'\''`. */
11
- export declare function shq(value: string): string;
12
12
  export declare function isGateAllowed(gate: string): boolean;
13
13
  export interface BuildCommandOptions {
14
14
  subcommand: "check" | "principles" | "arch";
15
15
  target?: string;
16
16
  gates?: readonly string[];
17
17
  config?: string;
18
+ /**
19
+ * Executor dialect. Defaults to `"posix"` so existing callers keep working;
20
+ * callers running under DSH on Windows must pass `"powershell"`.
21
+ */
22
+ dialect?: ShellDialect;
18
23
  }
19
24
  /**
20
25
  * Build a single guarded shell command: runs `xp-gate …` when the global CLI
21
26
  * is present, otherwise prints the graceful-degradation hint. A single
22
- * `if command -v` avoids a separate probe/run race.
27
+ * presence check avoids a separate probe/run race.
23
28
  */
24
29
  export declare function buildCommand(options: BuildCommandOptions): string;
25
30
  /** Resolve a model-supplied path against the session cwd; absolutes pass through. */
@@ -1 +1 @@
1
- {"version":3,"file":"command.d.ts","sourceRoot":"","sources":["../src/command.ts"],"names":[],"mappings":"AAEA,0DAA0D;AAC1D,eAAO,MAAM,gBAAgB,8FACgE,CAAA;AAE7F;;;;GAIG;AACH,eAAO,MAAM,cAAc,uGASjB,CAAA;AAEV,MAAM,MAAM,MAAM,GAAG,CAAC,OAAO,cAAc,CAAC,CAAC,MAAM,CAAC,CAAA;AAEpD,6DAA6D;AAC7D,wBAAgB,GAAG,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEzC;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEnD;AAED,MAAM,WAAW,mBAAmB;IAClC,UAAU,EAAE,OAAO,GAAG,YAAY,GAAG,MAAM,CAAA;IAC3C,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,KAAK,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IACzB,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB;AAuBD;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,mBAAmB,GAAG,MAAM,CAGjE;AAED,qFAAqF;AACrF,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAElE"}
1
+ {"version":3,"file":"command.d.ts","sourceRoot":"","sources":["../src/command.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,YAAY,EAAc,MAAM,cAAc,CAAA;AAI5D,OAAO,EAAE,KAAK,YAAY,EAAE,aAAa,EAAE,UAAU,EAAE,GAAG,EAAE,MAAM,cAAc,CAAA;AAEhF,0DAA0D;AAC1D,eAAO,MAAM,gBAAgB,8FACgE,CAAA;AAE7F;;;;GAIG;AACH,eAAO,MAAM,cAAc,uGASjB,CAAA;AAEV,MAAM,MAAM,MAAM,GAAG,CAAC,OAAO,cAAc,CAAC,CAAC,MAAM,CAAC,CAAA;AAEpD,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEnD;AAED,MAAM,WAAW,mBAAmB;IAClC,UAAU,EAAE,OAAO,GAAG,YAAY,GAAG,MAAM,CAAA;IAC3C,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,KAAK,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IACzB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf;;;OAGG;IACH,OAAO,CAAC,EAAE,YAAY,CAAA;CACvB;AAwBD;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,mBAAmB,GAAG,MAAM,CAmBjE;AAED,qFAAqF;AACrF,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAElE"}
package/lib/command.js CHANGED
@@ -1,4 +1,8 @@
1
1
  import { isAbsolute, resolve } from "node:path";
2
+ import { shellQuote } from "./dialect.js";
3
+ // Re-exported so existing importers of this module keep working; the dialect
4
+ // primitives themselves live in ./dialect.ts.
5
+ export { detectDialect, shellQuote, shq } from "./dialect.js";
2
6
  /** Shown when the global `xp-gate` CLI is not on PATH. */
3
7
  export const FALLBACK_MESSAGE = "[XP-Gate] xp-gate CLI not installed. Install it with: npm install -g @boyingliu01/xp-gate";
4
8
  /**
@@ -16,44 +20,51 @@ export const GATE_WHITELIST = [
16
20
  "secrets",
17
21
  "sast",
18
22
  ];
19
- /** POSIX single-quote shell escaping: `'` becomes `'\''`. */
20
- export function shq(value) {
21
- return `'${value.replace(/'/g, "'\\''")}'`;
22
- }
23
23
  export function isGateAllowed(gate) {
24
24
  return GATE_WHITELIST.includes(gate);
25
25
  }
26
- function buildInner(options) {
26
+ function buildInner(options, dialect) {
27
27
  const { subcommand, target, gates, config } = options;
28
+ const q = (value) => shellQuote(value, dialect);
28
29
  if (subcommand === "check") {
29
30
  const tokens = ["xp-gate", "check"];
30
31
  if (target !== undefined)
31
- tokens.push(shq(target));
32
+ tokens.push(q(target));
32
33
  if (gates !== undefined && gates.length > 0) {
33
34
  // Defense-in-depth: the JSON-schema enum already rejects unknown gate ids,
34
35
  // but we also whitelist here so a non-whitelisted value can never reach the shell.
35
36
  const allowed = gates.filter(isGateAllowed);
36
37
  if (allowed.length > 0)
37
- tokens.push("--gates", shq(allowed.join(",")));
38
+ tokens.push("--gates", q(allowed.join(",")));
38
39
  }
39
40
  return tokens.join(" ");
40
41
  }
41
42
  if (subcommand === "principles") {
42
43
  const tokens = ["xp-gate", "principles"];
43
44
  if (target !== undefined)
44
- tokens.push(shq(target));
45
+ tokens.push(q(target));
45
46
  return tokens.join(" ");
46
47
  }
47
- return ["xp-gate", "arch", "--config", shq(config ?? "architecture.yaml")].join(" ");
48
+ return ["xp-gate", "arch", "--config", q(config ?? "architecture.yaml")].join(" ");
48
49
  }
49
50
  /**
50
51
  * Build a single guarded shell command: runs `xp-gate …` when the global CLI
51
52
  * is present, otherwise prints the graceful-degradation hint. A single
52
- * `if command -v` avoids a separate probe/run race.
53
+ * presence check avoids a separate probe/run race.
53
54
  */
54
55
  export function buildCommand(options) {
55
- const inner = buildInner(options);
56
- return `if command -v xp-gate >/dev/null 2>&1; then ${inner}; else printf '%s\\n' ${shq(FALLBACK_MESSAGE)}; fi`;
56
+ const dialect = options.dialect ?? "posix";
57
+ const inner = buildInner(options, dialect);
58
+ if (dialect === "posix") {
59
+ return `if command -v xp-gate >/dev/null 2>&1; then ${inner}; else printf '%s\\n' ${shellQuote(FALLBACK_MESSAGE, dialect)}; fi`;
60
+ }
61
+ // PowerShell: `Get-Command` resolves the .cmd/.ps1 shims npm writes to
62
+ // %APPDATA%\npm. Those shims are invisible to a Git-Bash PATH lookup, so this
63
+ // branch is more reliable than `command -v` on Windows.
64
+ //
65
+ // The hint branch leaves $LASTEXITCODE untouched at 0, matching the POSIX
66
+ // branch's `printf` behaviour.
67
+ return `if (Get-Command xp-gate -ErrorAction SilentlyContinue) { ${inner} } else { Write-Output ${shellQuote(FALLBACK_MESSAGE, dialect)} }`;
57
68
  }
58
69
  /** Resolve a model-supplied path against the session cwd; absolutes pass through. */
59
70
  export function resolveTarget(rawPath, cwd) {
@@ -1 +1 @@
1
- {"version":3,"file":"command.js","sourceRoot":"","sources":["../src/command.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAE/C,0DAA0D;AAC1D,MAAM,CAAC,MAAM,gBAAgB,GAC3B,2FAA2F,CAAA;AAE7F;;;;GAIG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG;IAC5B,YAAY;IACZ,YAAY;IACZ,YAAY;IACZ,MAAM;IACN,cAAc;IACd,KAAK;IACL,SAAS;IACT,MAAM;CACE,CAAA;AAIV,6DAA6D;AAC7D,MAAM,UAAU,GAAG,CAAC,KAAa;IAC/B,OAAO,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,CAAA;AAC5C,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,IAAY;IACxC,OAAQ,cAAoC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAA;AAC7D,CAAC;AASD,SAAS,UAAU,CAAC,OAA4B;IAC9C,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAA;IACrD,IAAI,UAAU,KAAK,OAAO,EAAE,CAAC;QAC3B,MAAM,MAAM,GAAG,CAAC,SAAS,EAAE,OAAO,CAAC,CAAA;QACnC,IAAI,MAAM,KAAK,SAAS;YAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAA;QAClD,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC5C,2EAA2E;YAC3E,mFAAmF;YACnF,MAAM,OAAO,GAAG,KAAK,CAAC,MAAM,CAAC,aAAa,CAAC,CAAA;YAC3C,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC;gBAAE,MAAM,CAAC,IAAI,CAAC,SAAS,EAAE,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAA;QACxE,CAAC;QACD,OAAO,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;IACzB,CAAC;IACD,IAAI,UAAU,KAAK,YAAY,EAAE,CAAC;QAChC,MAAM,MAAM,GAAG,CAAC,SAAS,EAAE,YAAY,CAAC,CAAA;QACxC,IAAI,MAAM,KAAK,SAAS;YAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAA;QAClD,OAAO,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;IACzB,CAAC;IACD,OAAO,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,GAAG,CAAC,MAAM,IAAI,mBAAmB,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;AACtF,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,YAAY,CAAC,OAA4B;IACvD,MAAM,KAAK,GAAG,UAAU,CAAC,OAAO,CAAC,CAAA;IACjC,OAAO,+CAA+C,KAAK,yBAAyB,GAAG,CAAC,gBAAgB,CAAC,MAAM,CAAA;AACjH,CAAC;AAED,qFAAqF;AACrF,MAAM,UAAU,aAAa,CAAC,OAAe,EAAE,GAAW;IACxD,OAAO,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,EAAE,OAAO,CAAC,CAAA;AAC9D,CAAC"}
1
+ {"version":3,"file":"command.js","sourceRoot":"","sources":["../src/command.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,75 @@
1
+ /**
2
+ * Delphi configuration loading for the DSH plugin.
3
+ *
4
+ * Reads the same `.delphi-config.json` the xp-gate skill already uses, so a
5
+ * project configures its reviewers once and both the skill and the DSH tool
6
+ * agree on who they are.
7
+ *
8
+ * @test REQ-DSH-016
9
+ * @intent 复用项目既有的 .delphi-config.json,把 provider/api_key/专家席位解析成
10
+ * DSH 工具可用的结构,并对 ${ENV} 占位符与缺失密钥给出可操作的报错。
11
+ * @covers AC-DSH-016-02
12
+ */
13
+ import type { ExpertSeat, DelphiOptions } from "./delphi.js";
14
+ /** Shape of one expert entry in .delphi-config.json. */
15
+ interface RawExpert {
16
+ id?: string;
17
+ role?: string;
18
+ name?: string;
19
+ model?: string;
20
+ requested_model?: string;
21
+ max_tokens?: number;
22
+ maxTokens?: number;
23
+ /** Per-expert provider override; falls back to the profile default. */
24
+ provider?: string;
25
+ }
26
+ interface RawProfile {
27
+ provider?: string;
28
+ providers?: Record<string, {
29
+ base_url?: string;
30
+ baseUrl?: string;
31
+ api_key?: string;
32
+ apiKey?: string;
33
+ }>;
34
+ /**
35
+ * Experts appear in two shapes in the wild: an object keyed by role (what
36
+ * xp-gate actually writes) and an array. Both are accepted.
37
+ */
38
+ experts?: RawExpert[] | Record<string, RawExpert>;
39
+ consensus?: {
40
+ threshold_percent?: number;
41
+ max_review_rounds?: number;
42
+ };
43
+ }
44
+ export interface RawDelphiConfig extends RawProfile {
45
+ active_profile?: string;
46
+ profiles?: Record<string, RawProfile>;
47
+ }
48
+ export interface ResolvedDelphiConfig {
49
+ readonly seats: ExpertSeat[];
50
+ readonly baseUrl: string;
51
+ readonly apiKey: string;
52
+ readonly thresholdPercent: number;
53
+ readonly maxRounds: number;
54
+ readonly provider: string;
55
+ }
56
+ /** Raised when config on disk cannot be turned into a usable review setup. */
57
+ export declare class DelphiSetupError extends Error {
58
+ }
59
+ /**
60
+ * Expand `${VAR}` and `{env:VAR}` placeholders from the environment.
61
+ * Supports the two spellings already present in xp-gate config files.
62
+ */
63
+ export declare function expandEnv(value: string, env?: NodeJS.ProcessEnv): string;
64
+ /**
65
+ * Turn raw config into a ready-to-run review setup.
66
+ * Fails loudly and specifically: a missing key or an unexpanded placeholder is
67
+ * the most common way a Delphi run silently degrades.
68
+ */
69
+ export declare function resolveConfig(cfg: RawDelphiConfig, env?: NodeJS.ProcessEnv): ResolvedDelphiConfig;
70
+ /** Read and resolve `.delphi-config.json` from a project root. */
71
+ export declare function loadDelphiConfig(cwd: string, env?: NodeJS.ProcessEnv): ResolvedDelphiConfig;
72
+ /** Build the transport options a round needs from a resolved config. */
73
+ export declare function toDelphiOptions(cfg: ResolvedDelphiConfig): DelphiOptions;
74
+ export {};
75
+ //# sourceMappingURL=delphi-config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"delphi-config.d.ts","sourceRoot":"","sources":["../src/delphi-config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAIH,OAAO,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAG5D,wDAAwD;AACxD,UAAU,SAAS;IACjB,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,uEAAuE;IACvE,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB;AAED,UAAU,UAAU;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE;QAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;IACtG;;;OAGG;IACH,OAAO,CAAC,EAAE,SAAS,EAAE,GAAG,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAA;IACjD,SAAS,CAAC,EAAE;QAAE,iBAAiB,CAAC,EAAE,MAAM,CAAC;QAAC,iBAAiB,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;CACvE;AAED,MAAM,WAAW,eAAgB,SAAQ,UAAU;IACjD,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAA;CACtC;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,KAAK,EAAE,UAAU,EAAE,CAAA;IAC5B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAA;IACjC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;CAC1B;AAED,8EAA8E;AAC9E,qBAAa,gBAAiB,SAAQ,KAAK;CAAG;AAE9C;;;GAGG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,MAAM,CAIrF;AAkCD;;;;GAIG;AACH,wBAAgB,aAAa,CAC3B,GAAG,EAAE,eAAe,EACpB,GAAG,GAAE,MAAM,CAAC,UAAwB,GACnC,oBAAoB,CAiFtB;AAED,kEAAkE;AAClE,wBAAgB,gBAAgB,CAC9B,GAAG,EAAE,MAAM,EACX,GAAG,GAAE,MAAM,CAAC,UAAwB,GACnC,oBAAoB,CAetB;AAED,wEAAwE;AACxE,wBAAgB,eAAe,CAAC,GAAG,EAAE,oBAAoB,GAAG,aAAa,CAQxE"}
@@ -0,0 +1,155 @@
1
+ /**
2
+ * Delphi configuration loading for the DSH plugin.
3
+ *
4
+ * Reads the same `.delphi-config.json` the xp-gate skill already uses, so a
5
+ * project configures its reviewers once and both the skill and the DSH tool
6
+ * agree on who they are.
7
+ *
8
+ * @test REQ-DSH-016
9
+ * @intent 复用项目既有的 .delphi-config.json,把 provider/api_key/专家席位解析成
10
+ * DSH 工具可用的结构,并对 ${ENV} 占位符与缺失密钥给出可操作的报错。
11
+ * @covers AC-DSH-016-02
12
+ */
13
+ import { readFileSync } from "node:fs";
14
+ import { resolve } from "node:path";
15
+ import { validateSeats } from "./delphi.js";
16
+ /** Raised when config on disk cannot be turned into a usable review setup. */
17
+ export class DelphiSetupError extends Error {
18
+ }
19
+ /**
20
+ * Expand `${VAR}` and `{env:VAR}` placeholders from the environment.
21
+ * Supports the two spellings already present in xp-gate config files.
22
+ */
23
+ export function expandEnv(value, env = process.env) {
24
+ return value
25
+ .replace(/\$\{([A-Za-z_][A-Za-z0-9_]*)\}/g, (_m, name) => env[name] ?? "")
26
+ .replace(/\{env:([A-Za-z_][A-Za-z0-9_]*)\}/g, (_m, name) => env[name] ?? "");
27
+ }
28
+ /** Pick the active profile, falling back to the top level. */
29
+ function selectProfile(cfg) {
30
+ const active = cfg.active_profile;
31
+ if (active && cfg.profiles?.[active])
32
+ return cfg.profiles[active];
33
+ if (cfg.profiles && Object.keys(cfg.profiles).length > 0) {
34
+ throw new DelphiSetupError(`No active_profile set. Available: ${Object.keys(cfg.profiles).join(", ")}`);
35
+ }
36
+ return cfg;
37
+ }
38
+ function seatFromRaw(raw, index, fallbackRole) {
39
+ const model = raw.model ?? raw.requested_model ?? "";
40
+ return {
41
+ id: raw.id ?? raw.name ?? `Expert ${String.fromCharCode(65 + index)}`,
42
+ role: raw.role ?? fallbackRole,
43
+ model,
44
+ maxTokens: raw.max_tokens ?? raw.maxTokens,
45
+ };
46
+ }
47
+ /** Normalize either experts shape into an ordered list with roles attached. */
48
+ function normalizeExperts(experts) {
49
+ if (!experts)
50
+ return [];
51
+ if (Array.isArray(experts)) {
52
+ return experts.map((raw, i) => ({ raw, role: raw.role ?? `expert${i}` }));
53
+ }
54
+ // Object form: the KEY is the role, which is how xp-gate writes it.
55
+ return Object.entries(experts).map(([role, raw]) => ({ raw, role }));
56
+ }
57
+ /**
58
+ * Turn raw config into a ready-to-run review setup.
59
+ * Fails loudly and specifically: a missing key or an unexpanded placeholder is
60
+ * the most common way a Delphi run silently degrades.
61
+ */
62
+ export function resolveConfig(cfg, env = process.env) {
63
+ const profile = selectProfile(cfg);
64
+ const normalized = normalizeExperts(profile.experts);
65
+ // An expert may name its own provider; otherwise there must be a profile-wide
66
+ // default. Resolve base_url/api_key per provider so mixed setups work.
67
+ const providerNames = new Set(normalized.map((e) => e.raw.provider ?? profile.provider).filter((p) => !!p));
68
+ if (providerNames.size === 0) {
69
+ throw new DelphiSetupError('No provider configured. Set "provider" on the profile or on each expert.');
70
+ }
71
+ const providerTable = profile.providers ?? {};
72
+ const resolvedProviders = new Map();
73
+ for (const name of providerNames) {
74
+ const entry = providerTable[name];
75
+ if (!entry) {
76
+ throw new DelphiSetupError(`Provider "${name}" is not defined under "providers". ` +
77
+ `Known: ${Object.keys(providerTable).join(", ") || "(none)"}`);
78
+ }
79
+ const baseUrl = entry.base_url ?? entry.baseUrl ?? "";
80
+ if (!baseUrl)
81
+ throw new DelphiSetupError(`Provider "${name}" has no base_url.`);
82
+ const rawKey = entry.api_key ?? entry.apiKey ?? "";
83
+ const apiKey = expandEnv(rawKey, env);
84
+ if (!apiKey) {
85
+ // Name the variable rather than echoing config, which may hold a literal key.
86
+ const varName = /\$\{([A-Za-z_][A-Za-z0-9_]*)\}/.exec(rawKey)?.[1] ??
87
+ /\{env:([A-Za-z_][A-Za-z0-9_]*)\}/.exec(rawKey)?.[1];
88
+ throw new DelphiSetupError(varName
89
+ ? `API key variable ${varName} is not set in the environment.`
90
+ : `Provider "${name}" has no api_key.`);
91
+ }
92
+ resolvedProviders.set(name, { baseUrl, apiKey });
93
+ }
94
+ const seats = normalized.map((e, i) => seatFromRaw(e.raw, i, e.role));
95
+ // Surface an unexpanded placeholder as a config error, not a provider 404.
96
+ for (const s of seats) {
97
+ if (s.model.includes("${") || s.model.includes("{env:")) {
98
+ throw new DelphiSetupError(`Expert "${s.id}" model "${s.model}" is an unexpanded placeholder.`);
99
+ }
100
+ }
101
+ validateSeats(seats);
102
+ // All seats share one gateway in the supported setup; require agreement so a
103
+ // mixed list cannot silently send one seat's key to another host.
104
+ const chosen = resolvedProviders.get(normalized[0].raw.provider ?? profile.provider ?? "");
105
+ if (!chosen)
106
+ throw new DelphiSetupError("Unable to resolve a provider for the first expert.");
107
+ for (const e of normalized) {
108
+ const name = e.raw.provider ?? profile.provider ?? "";
109
+ const other = resolvedProviders.get(name);
110
+ if (!other || other.baseUrl !== chosen.baseUrl) {
111
+ throw new DelphiSetupError(`All experts must share one gateway base_url; "${name}" differs. ` +
112
+ `Split the review or align the providers.`);
113
+ }
114
+ }
115
+ // consensus may sit on the profile or at the document root; both occur.
116
+ const consensus = profile.consensus ?? cfg.consensus;
117
+ return {
118
+ seats,
119
+ baseUrl: chosen.baseUrl,
120
+ apiKey: chosen.apiKey,
121
+ thresholdPercent: consensus?.threshold_percent ?? 90,
122
+ maxRounds: consensus?.max_review_rounds ?? 5,
123
+ provider: normalized[0].raw.provider ?? profile.provider ?? "",
124
+ };
125
+ }
126
+ /** Read and resolve `.delphi-config.json` from a project root. */
127
+ export function loadDelphiConfig(cwd, env = process.env) {
128
+ const path = resolve(cwd, ".delphi-config.json");
129
+ let raw;
130
+ try {
131
+ raw = readFileSync(path, "utf8");
132
+ }
133
+ catch {
134
+ throw new DelphiSetupError(`No .delphi-config.json found at ${path}.`);
135
+ }
136
+ let parsed;
137
+ try {
138
+ parsed = JSON.parse(raw);
139
+ }
140
+ catch (e) {
141
+ throw new DelphiSetupError(`.delphi-config.json is not valid JSON: ${e.message}`);
142
+ }
143
+ return resolveConfig(parsed, env);
144
+ }
145
+ /** Build the transport options a round needs from a resolved config. */
146
+ export function toDelphiOptions(cfg) {
147
+ return {
148
+ baseUrl: cfg.baseUrl,
149
+ apiKey: cfg.apiKey,
150
+ seats: cfg.seats,
151
+ thresholdPercent: cfg.thresholdPercent,
152
+ maxRounds: cfg.maxRounds,
153
+ };
154
+ }
155
+ //# sourceMappingURL=delphi-config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"delphi-config.js","sourceRoot":"","sources":["../src/delphi-config.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Delphi review orchestration: iterate rounds until consensus or the cap.
3
+ *
4
+ * @test REQ-DSH-016
5
+ * @intent 编排多轮交叉评审:首轮匿名独立评审,后续轮次反馈上一轮汇总,
6
+ * 直到达到共识阈值(默认 90%)或触及轮次上限(默认 5),
7
+ * 并把结果整理成可写成 .code-walkthrough-result.json 的证据。
8
+ * @covers AC-DSH-016-03
9
+ */
10
+ import type { DelphiOptions, RoundResult } from "./delphi.js";
11
+ export interface ReviewOutcome {
12
+ readonly rounds: readonly RoundResult[];
13
+ readonly consensus: boolean;
14
+ readonly finalRatio: number;
15
+ readonly totalRounds: number;
16
+ }
17
+ /**
18
+ * Run a full review. Round 1 is anonymous; each later round sees the aggregate
19
+ * plus whatever the caller says was addressed, then re-votes.
20
+ */
21
+ export declare function runReview(opts: DelphiOptions, subject: string, addressed?: string): Promise<ReviewOutcome>;
22
+ /** Human-readable transcript for the tool result. */
23
+ export declare function formatOutcome(outcome: ReviewOutcome): string;
24
+ //# sourceMappingURL=delphi-run.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"delphi-run.d.ts","sourceRoot":"","sources":["../src/delphi-run.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAc,WAAW,EAAE,MAAM,aAAa,CAAA;AAOzE,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,MAAM,EAAE,SAAS,WAAW,EAAE,CAAA;IACvC,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAA;IAC3B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;IAC3B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;CAC7B;AA0CD;;;GAGG;AACH,wBAAsB,SAAS,CAC7B,IAAI,EAAE,aAAa,EACnB,OAAO,EAAE,MAAM,EACf,SAAS,SAAK,GACb,OAAO,CAAC,aAAa,CAAC,CAoCxB;AAED,qDAAqD;AACrD,wBAAgB,aAAa,CAAC,OAAO,EAAE,aAAa,GAAG,MAAM,CAiB5D"}