@boyingliu01/xp-gate 0.18.5 → 0.19.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/adapter-common.sh +27 -0
- package/adapters/gate-3.sh +0 -0
- package/adapters/gate-4.sh +0 -0
- package/adapters/gate-7.sh +0 -0
- package/adapters/gate-8.sh +0 -0
- package/adapters/gate-9.sh +0 -0
- package/adapters/powershell.sh +114 -35
- package/adapters/typescript.sh +8 -8
- package/build-integrity/__tests__/import-resolver.test.ts +22 -0
- package/build-integrity/__tests__/orchestrator.test.ts +1 -2
- package/build-integrity/gate-10.ts +7 -0
- package/gate-12-file-hygiene.sh +230 -0
- package/hooks/adapter-common.sh +27 -0
- package/hooks/gate-10.sh +105 -0
- package/hooks/gate-12-file-hygiene.sh +0 -0
- package/hooks/lib/validate-code-walkthrough.cjs +125 -0
- package/hooks/pre-commit +130 -33
- package/hooks/pre-push +15 -91
- package/lib/__tests__/check-version.test.js +67 -46
- package/lib/__tests__/doctor.test.js +7 -6
- package/lib/__tests__/gate-runner.test.js +267 -0
- package/lib/__tests__/init.test.js +32 -0
- package/lib/__tests__/npm-package.test.js +13 -0
- package/lib/__tests__/phase-transition.test.js +604 -12
- package/lib/__tests__/sprint-discovery.test.ts +0 -1
- package/lib/__tests__/sprint-status-rework.test.js +100 -1
- package/lib/__tests__/update-hooks.test.js +143 -10
- package/lib/__tests__/upgrade-exec.test.js +73 -45
- package/lib/__tests__/upgrade-skill-sync.test.js +1 -1
- package/lib/__tests__/upgrade.test.js +12 -11
- package/lib/doctor.js +2 -1
- package/lib/gate-runner.js +68 -23
- package/lib/init.js +34 -5
- package/lib/install-skill.js +1 -0
- package/lib/mattpocock-license-verdict.json +1 -1
- package/lib/phase-transition.js +203 -23
- package/lib/shared-paths.js +2 -0
- package/lib/sprint-status.js +15 -4
- package/lib/test-alignment.ts +3 -13
- package/lib/update-hooks.js +129 -15
- package/mock-policy/AGENTS.md +4 -4
- package/mock-policy/__tests__/gate-m3.test.ts +0 -5
- package/mock-policy/__tests__/scope-scanner.test.ts +8 -2
- package/mutation/AGENTS.md +3 -3
- package/mutation/__tests__/mutmut-runner.test.ts +443 -2
- package/mutation/__tests__/update-baseline.test.ts +0 -2
- package/mutation/runners/mutmut-runner.ts +67 -17
- package/package.json +9 -3
- package/plugins/claude-code/.claude-plugin/plugin.json +1 -1
- package/plugins/claude-code/skills/clipboard-vision/SKILL.md +47 -0
- package/plugins/claude-code/skills/clipboard-vision/__tests__/clipboard-vision-distribution.test.cjs +65 -0
- package/plugins/claude-code/skills/clipboard-vision/__tests__/clipboard-vision-security.test.cjs +185 -0
- package/plugins/claude-code/skills/clipboard-vision/__tests__/clipboard-vision.Tests.ps1 +138 -0
- package/plugins/claude-code/skills/clipboard-vision/__tests__/clipboard-vision.test.cjs +273 -0
- package/plugins/claude-code/skills/clipboard-vision/describe-clipboard.ps1 +115 -0
- package/plugins/claude-code/skills/clipboard-vision/describe-clipboard.sh +161 -0
- package/plugins/claude-code/skills/clipboard-vision/scripts/clipboard-vision.ps1 +86 -0
- package/plugins/claude-code/skills/clipboard-vision/scripts/clipboard-vision.sh +137 -0
- package/plugins/claude-code/skills/delphi-review/.delphi-config.json.example +24 -27
- package/plugins/claude-code/skills/delphi-review/AGENTS.md +12 -11
- package/plugins/claude-code/skills/delphi-review/INSTALL.md +15 -17
- package/plugins/claude-code/skills/delphi-review/SKILL.md +30 -29
- package/plugins/claude-code/skills/delphi-review/opencode.json.delphi.example +6 -10
- package/plugins/claude-code/skills/delphi-review/references/code-walkthrough.md +33 -49
- package/plugins/claude-code/skills/delphi-review/references/requirements.md +66 -54
- package/plugins/claude-code/skills/delphi-review/references/round-templates.md +7 -7
- package/plugins/claude-code/skills/sprint-flow/AGENTS.md +4 -4
- package/plugins/claude-code/skills/sprint-flow/SKILL.md +4 -4
- package/plugins/claude-code/skills/sprint-flow/references/components/skill-invocations.md +2 -2
- package/plugins/claude-code/skills/sprint-flow/references/force-levels.md +37 -35
- package/plugins/claude-code/skills/sprint-flow/references/orchestration-rules.md +1 -1
- package/plugins/claude-code/skills/sprint-flow/references/phase-1-prep.md +1 -1
- package/plugins/claude-code/skills/sprint-flow/references/phase-2-design.md +24 -14
- package/plugins/claude-code/skills/sprint-flow/references/phase-4-verify.md +6 -6
- package/plugins/claude-code/skills/sprint-flow/references/phase-overview.md +2 -2
- package/plugins/claude-code/skills/test-specification-alignment/AGENTS.md +3 -3
- package/plugins/dsh/README.md +25 -0
- package/plugins/dsh/cordis.patch.yml +7 -0
- package/plugins/dsh/package.json +70 -0
- package/plugins/dsh/scripts/prepack.cjs +82 -0
- package/plugins/dsh/skills/admin-template-guidelines/SKILL.md +113 -0
- package/plugins/dsh/skills/admin-template-guidelines/references/rule-1.md +91 -0
- package/plugins/dsh/skills/admin-template-guidelines/references/rule-2.md +155 -0
- package/plugins/dsh/skills/admin-template-guidelines/references/rule-3.md +70 -0
- package/plugins/dsh/skills/admin-template-guidelines/references/rule-4.md +154 -0
- package/plugins/dsh/skills/admin-template-guidelines/references/rule-5.md +126 -0
- package/plugins/dsh/skills/admin-template-guidelines/references/rule-6.md +180 -0
- package/plugins/dsh/skills/batch-grill-me/SKILL.md +15 -0
- package/plugins/dsh/skills/clipboard-vision/SKILL.md +47 -0
- package/plugins/dsh/skills/clipboard-vision/__tests__/clipboard-vision-distribution.test.cjs +65 -0
- package/plugins/dsh/skills/clipboard-vision/__tests__/clipboard-vision-security.test.cjs +185 -0
- package/plugins/dsh/skills/clipboard-vision/__tests__/clipboard-vision.Tests.ps1 +138 -0
- package/plugins/dsh/skills/clipboard-vision/__tests__/clipboard-vision.test.cjs +273 -0
- package/plugins/dsh/skills/clipboard-vision/describe-clipboard.ps1 +115 -0
- package/plugins/dsh/skills/clipboard-vision/describe-clipboard.sh +161 -0
- package/plugins/dsh/skills/clipboard-vision/scripts/clipboard-vision.ps1 +86 -0
- package/plugins/dsh/skills/clipboard-vision/scripts/clipboard-vision.sh +137 -0
- package/plugins/dsh/skills/delphi-review/.delphi-config.json.example +70 -0
- package/plugins/dsh/skills/delphi-review/AGENTS.md +67 -0
- package/plugins/dsh/skills/delphi-review/INSTALL.md +150 -0
- package/plugins/dsh/skills/delphi-review/SKILL.md +559 -0
- package/plugins/dsh/skills/delphi-review/hooks.md +19 -0
- package/plugins/dsh/skills/delphi-review/opencode.json.delphi.example +52 -0
- package/plugins/dsh/skills/delphi-review/references/code-walkthrough.md +470 -0
- package/plugins/dsh/skills/delphi-review/references/orchestrator-dispatch.md +64 -0
- package/plugins/dsh/skills/delphi-review/references/requirements.md +402 -0
- package/plugins/dsh/skills/delphi-review/references/round-templates.md +134 -0
- package/plugins/dsh/skills/delphi-review/scripts/verify-consensus.sh +43 -0
- package/plugins/dsh/skills/delphi-review/tools.md +29 -0
- package/plugins/dsh/skills/domain-modeling/ADR-FORMAT.md +64 -0
- package/plugins/dsh/skills/domain-modeling/CONTEXT-FORMAT.md +60 -0
- package/plugins/dsh/skills/domain-modeling/SKILL.md +74 -0
- package/plugins/dsh/skills/grill-with-docs/SKILL.md +7 -0
- package/plugins/dsh/skills/grilling/SKILL.md +12 -0
- package/plugins/dsh/skills/improve-codebase-architecture/SKILL.md +156 -0
- package/plugins/dsh/skills/ralph-loop/SKILL.md +198 -0
- package/plugins/dsh/skills/ralph-loop/references/components.md +113 -0
- package/plugins/dsh/skills/ralph-loop/references/phase-2-build-ralph.md +89 -0
- package/plugins/dsh/skills/ralph-loop/references/workflow-details.md +99 -0
- package/plugins/dsh/skills/ralph-loop/templates/progress-log.md +36 -0
- package/plugins/dsh/skills/sprint-flow/AGENTS.md +103 -0
- package/plugins/dsh/skills/sprint-flow/SKILL.md +411 -0
- package/plugins/dsh/skills/sprint-flow/__tests__/sprint-flow.test.cjs +457 -0
- package/plugins/dsh/skills/sprint-flow/hooks.md +18 -0
- package/plugins/dsh/skills/sprint-flow/references/components/memory.md +87 -0
- package/plugins/dsh/skills/sprint-flow/references/components/middleware.md +75 -0
- package/plugins/dsh/skills/sprint-flow/references/components/skill-invocations.md +105 -0
- package/plugins/dsh/skills/sprint-flow/references/components/system-prompt.md +27 -0
- package/plugins/dsh/skills/sprint-flow/references/components/tool-descriptions.md +95 -0
- package/plugins/dsh/skills/sprint-flow/references/force-levels.md +204 -0
- package/plugins/dsh/skills/sprint-flow/references/orchestration-rules.md +236 -0
- package/plugins/dsh/skills/sprint-flow/references/phase-1-prep.md +294 -0
- package/plugins/dsh/skills/sprint-flow/references/phase-2-design.md +273 -0
- package/plugins/dsh/skills/sprint-flow/references/phase-3-build.md +348 -0
- package/plugins/dsh/skills/sprint-flow/references/phase-4-verify.md +225 -0
- package/plugins/dsh/skills/sprint-flow/references/phase-5-ship.md +222 -0
- package/plugins/dsh/skills/sprint-flow/references/phase-6-close.md +266 -0
- package/plugins/dsh/skills/sprint-flow/references/phase-overview.md +486 -0
- package/plugins/dsh/skills/sprint-flow/scripts/verify-sprint-state.sh +38 -0
- package/plugins/dsh/skills/sprint-flow/templates/auto-estimate-learning-log.md +136 -0
- package/plugins/dsh/skills/sprint-flow/templates/auto-estimate-output-template.md +110 -0
- package/plugins/dsh/skills/sprint-flow/templates/emergent-issues-template.md +120 -0
- package/plugins/dsh/skills/sprint-flow/templates/pain-document-template.md +115 -0
- package/plugins/dsh/skills/sprint-flow/templates/sprint-progress-template.md +140 -0
- package/plugins/dsh/skills/sprint-flow/templates/sprint-summary-template.md +121 -0
- package/plugins/dsh/skills/sprint-flow/tools.md +29 -0
- package/plugins/dsh/skills/test-driven-development/SKILL.md +371 -0
- package/plugins/dsh/skills/test-driven-development/testing-anti-patterns.md +299 -0
- package/plugins/dsh/skills/test-specification-alignment/AGENTS.md +56 -0
- package/plugins/dsh/skills/test-specification-alignment/SKILL.md +205 -0
- package/plugins/dsh/skills/test-specification-alignment/references/alignment-verification-algorithm.md +493 -0
- package/plugins/dsh/skills/test-specification-alignment/references/phase2-constraint-enforcement.md +431 -0
- package/plugins/dsh/skills/test-specification-alignment/references/specification-format.md +348 -0
- package/plugins/dsh/skills/to-issues/SKILL.md +277 -0
- package/plugins/dsh/src/command.test.ts +102 -0
- package/plugins/dsh/src/command.ts +75 -0
- package/plugins/dsh/src/contract.test.ts +62 -0
- package/plugins/dsh/src/gate-runner.test.ts +100 -0
- package/plugins/dsh/src/gate-runner.ts +58 -0
- package/plugins/dsh/src/index.test.ts +97 -0
- package/plugins/dsh/src/index.ts +94 -0
- package/plugins/dsh/src/skills.test.ts +58 -0
- package/plugins/dsh/tsconfig.build.json +13 -0
- package/plugins/dsh/tsconfig.json +19 -0
- package/plugins/dsh/vitest.config.ts +8 -0
- package/plugins/opencode/package.json +1 -1
- package/plugins/opencode/scripts/prepack.cjs +1 -0
- package/plugins/opencode/skills/clipboard-vision/SKILL.md +47 -0
- package/plugins/opencode/skills/clipboard-vision/__tests__/clipboard-vision-distribution.test.cjs +65 -0
- package/plugins/opencode/skills/clipboard-vision/__tests__/clipboard-vision-security.test.cjs +185 -0
- package/plugins/opencode/skills/clipboard-vision/__tests__/clipboard-vision.Tests.ps1 +138 -0
- package/plugins/opencode/skills/clipboard-vision/__tests__/clipboard-vision.test.cjs +273 -0
- package/plugins/opencode/skills/clipboard-vision/describe-clipboard.ps1 +115 -0
- package/plugins/opencode/skills/clipboard-vision/describe-clipboard.sh +161 -0
- package/plugins/opencode/skills/clipboard-vision/scripts/clipboard-vision.ps1 +86 -0
- package/plugins/opencode/skills/clipboard-vision/scripts/clipboard-vision.sh +137 -0
- package/plugins/opencode/skills/delphi-review/.delphi-config.json.example +24 -27
- package/plugins/opencode/skills/delphi-review/AGENTS.md +12 -11
- package/plugins/opencode/skills/delphi-review/INSTALL.md +15 -17
- package/plugins/opencode/skills/delphi-review/SKILL.md +30 -29
- package/plugins/opencode/skills/delphi-review/opencode.json.delphi.example +6 -10
- package/plugins/opencode/skills/delphi-review/references/code-walkthrough.md +33 -49
- package/plugins/opencode/skills/delphi-review/references/requirements.md +66 -54
- package/plugins/opencode/skills/delphi-review/references/round-templates.md +7 -7
- package/plugins/opencode/skills/sprint-flow/AGENTS.md +4 -4
- package/plugins/opencode/skills/sprint-flow/SKILL.md +4 -4
- package/plugins/opencode/skills/sprint-flow/references/components/skill-invocations.md +2 -2
- package/plugins/opencode/skills/sprint-flow/references/force-levels.md +37 -35
- package/plugins/opencode/skills/sprint-flow/references/orchestration-rules.md +1 -1
- package/plugins/opencode/skills/sprint-flow/references/phase-1-prep.md +1 -1
- package/plugins/opencode/skills/sprint-flow/references/phase-2-design.md +24 -14
- package/plugins/opencode/skills/sprint-flow/references/phase-4-verify.md +6 -6
- package/plugins/opencode/skills/sprint-flow/references/phase-overview.md +2 -2
- package/plugins/opencode/skills/test-specification-alignment/AGENTS.md +3 -3
- package/plugins/qoder/plugin.json +1 -1
- package/plugins/qoder/skills/clipboard-vision/SKILL.md +47 -0
- package/plugins/qoder/skills/clipboard-vision/__tests__/clipboard-vision-distribution.test.cjs +65 -0
- package/plugins/qoder/skills/clipboard-vision/__tests__/clipboard-vision-security.test.cjs +185 -0
- package/plugins/qoder/skills/clipboard-vision/__tests__/clipboard-vision.Tests.ps1 +138 -0
- package/plugins/qoder/skills/clipboard-vision/__tests__/clipboard-vision.test.cjs +273 -0
- package/plugins/qoder/skills/clipboard-vision/clipboard-vision/SKILL.md +47 -0
- package/plugins/qoder/skills/clipboard-vision/clipboard-vision/__tests__/clipboard-vision-distribution.test.cjs +65 -0
- package/plugins/qoder/skills/clipboard-vision/clipboard-vision/__tests__/clipboard-vision-security.test.cjs +185 -0
- package/plugins/qoder/skills/clipboard-vision/clipboard-vision/__tests__/clipboard-vision.Tests.ps1 +138 -0
- package/plugins/qoder/skills/clipboard-vision/clipboard-vision/__tests__/clipboard-vision.test.cjs +273 -0
- package/plugins/qoder/skills/clipboard-vision/clipboard-vision/scripts/clipboard-vision.ps1 +86 -0
- package/plugins/qoder/skills/clipboard-vision/clipboard-vision/scripts/clipboard-vision.sh +137 -0
- package/plugins/qoder/skills/clipboard-vision/scripts/clipboard-vision.ps1 +86 -0
- package/plugins/qoder/skills/clipboard-vision/scripts/clipboard-vision.sh +137 -0
- package/plugins/qoder/skills/delphi-review/.delphi-config.json.example +3 -11
- package/plugins/qoder/skills/delphi-review/AGENTS.md +14 -16
- package/plugins/qoder/skills/delphi-review/INSTALL.md +12 -12
- package/plugins/qoder/skills/delphi-review/SKILL.md +58 -25
- package/plugins/qoder/skills/delphi-review/opencode.json.delphi.example +1 -1
- package/plugins/qoder/skills/delphi-review/references/code-walkthrough.md +33 -49
- package/plugins/qoder/skills/sprint-flow/AGENTS.md +4 -4
- package/plugins/qoder/skills/sprint-flow/SKILL.md +2 -2
- package/plugins/qoder/skills/sprint-flow/references/force-levels.md +38 -35
- package/plugins/qoder/skills/sprint-flow/references/phase-1-prep.md +1 -1
- package/plugins/qoder/skills/sprint-flow/references/phase-2-design.md +48 -6
- package/plugins/qoder/skills/sprint-flow/references/phase-4-verify.md +5 -6
- package/plugins/qoder/skills/test-specification-alignment/AGENTS.md +3 -3
- package/principles/AGENTS.md +3 -3
- package/scripts/__tests__/sync-package-content.test.js +30 -0
- package/scripts/delphi-external-review.cjs +194 -63
- package/scripts/sync-package-content.js +9 -2
- package/skills/clipboard-vision/SKILL.md +47 -0
- package/skills/clipboard-vision/__tests__/clipboard-vision-distribution.test.cjs +65 -0
- package/skills/clipboard-vision/__tests__/clipboard-vision-security.test.cjs +185 -0
- package/skills/clipboard-vision/__tests__/clipboard-vision.Tests.ps1 +138 -0
- package/skills/clipboard-vision/__tests__/clipboard-vision.test.cjs +273 -0
- package/skills/clipboard-vision/describe-clipboard.ps1 +115 -0
- package/skills/clipboard-vision/describe-clipboard.sh +161 -0
- package/skills/clipboard-vision/scripts/clipboard-vision.ps1 +86 -0
- package/skills/clipboard-vision/scripts/clipboard-vision.sh +137 -0
- package/skills/delphi-review/.delphi-config.json.example +24 -27
- package/skills/delphi-review/AGENTS.md +12 -11
- package/skills/delphi-review/INSTALL.md +15 -17
- package/skills/delphi-review/SKILL.md +30 -29
- package/skills/delphi-review/opencode.json.delphi.example +6 -10
- package/skills/delphi-review/references/code-walkthrough.md +33 -49
- package/skills/delphi-review/references/requirements.md +66 -54
- package/skills/delphi-review/references/round-templates.md +7 -7
- package/skills/sprint-flow/AGENTS.md +4 -4
- package/skills/sprint-flow/SKILL.md +4 -4
- package/skills/sprint-flow/references/components/skill-invocations.md +2 -2
- package/skills/sprint-flow/references/force-levels.md +37 -35
- package/skills/sprint-flow/references/orchestration-rules.md +1 -1
- package/skills/sprint-flow/references/phase-1-prep.md +1 -1
- package/skills/sprint-flow/references/phase-2-design.md +24 -14
- package/skills/sprint-flow/references/phase-4-verify.md +6 -6
- package/skills/sprint-flow/references/phase-overview.md +2 -2
- package/skills/test-specification-alignment/AGENTS.md +3 -3
- package/sprint-gate.sh +291 -0
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# describe-clipboard.ps1
|
|
2
|
+
# Reads image from clipboard, sends to vision model API, writes description to stdout.
|
|
3
|
+
# API key is read from CLIPBOARD_VISION_API_KEY environment variable.
|
|
4
|
+
# WARNING: Do not enable -Verbose or Set-PSDebug -Trace 2 — the API key would be logged.
|
|
5
|
+
param(
|
|
6
|
+
[string]$OutputFile = "$env:TEMP\clipboard_desc.txt"
|
|
7
|
+
)
|
|
8
|
+
|
|
9
|
+
$ErrorActionPreference = "Stop"
|
|
10
|
+
|
|
11
|
+
# Platform guard
|
|
12
|
+
if (-not $IsWindows) {
|
|
13
|
+
Write-Host "ERROR: This script requires Windows (uses .NET System.Windows.Forms)"
|
|
14
|
+
exit 1
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
$apiKey = $env:CLIPBOARD_VISION_API_KEY
|
|
18
|
+
if (-not $apiKey) {
|
|
19
|
+
Write-Host "ERROR: CLIPBOARD_VISION_API_KEY environment variable not set"
|
|
20
|
+
exit 1
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
$tempPng = $null
|
|
24
|
+
$img = $null
|
|
25
|
+
|
|
26
|
+
try {
|
|
27
|
+
# --- Read clipboard image ---
|
|
28
|
+
Add-Type -AssemblyName System.Windows.Forms -ErrorAction Stop
|
|
29
|
+
Add-Type -AssemblyName System.Drawing -ErrorAction Stop
|
|
30
|
+
|
|
31
|
+
$img = [System.Windows.Forms.Clipboard]::GetImage()
|
|
32
|
+
if (-not $img) {
|
|
33
|
+
Write-Host "ERROR: No image found in clipboard"
|
|
34
|
+
exit 1
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
# Save to temp PNG
|
|
38
|
+
$tempPng = Join-Path $env:TEMP "clipboard_$(Get-Date -Format 'yyyyMMdd_HHmmss').png"
|
|
39
|
+
$img.Save($tempPng, [System.Drawing.Imaging.ImageFormat]::Png)
|
|
40
|
+
|
|
41
|
+
# Convert to base64
|
|
42
|
+
$bytes = [System.IO.File]::ReadAllBytes($tempPng)
|
|
43
|
+
$base64 = [System.Convert]::ToBase64String($bytes)
|
|
44
|
+
|
|
45
|
+
# --- Build API request ---
|
|
46
|
+
$apiBase = "https://lab.iwhalecloud.com/gpt-proxy"
|
|
47
|
+
$model = "LOCAL/Qwen3.5-122B-A10B"
|
|
48
|
+
|
|
49
|
+
$body = @{
|
|
50
|
+
model = $model
|
|
51
|
+
messages = @(
|
|
52
|
+
@{
|
|
53
|
+
role = "user"
|
|
54
|
+
content = @(
|
|
55
|
+
@{
|
|
56
|
+
type = "text"
|
|
57
|
+
text = "Describe this image in detail. Include all visible text, UI elements, code, error messages, objects, people, and any information conveyed. Be specific and thorough but concise. If it contains code or terminal output, quote it exactly."
|
|
58
|
+
},
|
|
59
|
+
@{
|
|
60
|
+
type = "image_url"
|
|
61
|
+
image_url = @{
|
|
62
|
+
url = "data:image/png;base64,$base64"
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
)
|
|
66
|
+
}
|
|
67
|
+
)
|
|
68
|
+
max_tokens = 1024
|
|
69
|
+
temperature = 0.1
|
|
70
|
+
} | ConvertTo-Json -Depth 5
|
|
71
|
+
|
|
72
|
+
# --- Call API ---
|
|
73
|
+
$response = Invoke-RestMethod -Uri "$apiBase/chat/completions" `
|
|
74
|
+
-Method Post `
|
|
75
|
+
-Headers @{
|
|
76
|
+
"Authorization" = "Bearer $apiKey"
|
|
77
|
+
"Content-Type" = "application/json"
|
|
78
|
+
} `
|
|
79
|
+
-Body ([System.Text.Encoding]::UTF8.GetBytes($body)) `
|
|
80
|
+
-TimeoutSec 30
|
|
81
|
+
|
|
82
|
+
# Validate response structure
|
|
83
|
+
if (-not $response.choices -or -not $response.choices[0].message -or -not $response.choices[0].message.content) {
|
|
84
|
+
Write-Host "ERROR: Unexpected API response structure"
|
|
85
|
+
Write-Host "Response: $($response | ConvertTo-Json -Compress -Depth 2)"
|
|
86
|
+
exit 1
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
$description = $response.choices[0].message.content
|
|
90
|
+
} catch {
|
|
91
|
+
Write-Host "ERROR: $_"
|
|
92
|
+
exit 1
|
|
93
|
+
} finally {
|
|
94
|
+
# Cleanup temp file and GDI+ handle
|
|
95
|
+
if ($tempPng -and (Test-Path $tempPng)) {
|
|
96
|
+
Remove-Item $tempPng -Force -ErrorAction SilentlyContinue
|
|
97
|
+
}
|
|
98
|
+
if ($img) {
|
|
99
|
+
$img.Dispose()
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if (-not $description) {
|
|
104
|
+
Write-Host "ERROR: Empty response from vision model"
|
|
105
|
+
exit 1
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
# Write to output file
|
|
109
|
+
$description | Out-File -FilePath $OutputFile -Encoding UTF8 -Force
|
|
110
|
+
|
|
111
|
+
# Output to stdout
|
|
112
|
+
Write-Host "Clipboard image description saved to: $OutputFile"
|
|
113
|
+
Write-Host "---"
|
|
114
|
+
Write-Host $description
|
|
115
|
+
Write-Host "---"
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# describe-clipboard.sh
|
|
3
|
+
# Cross-platform: reads clipboard image, calls vision API, writes description.
|
|
4
|
+
# Works on: WSL/Linux (xclip), macOS (osascript), Windows Git Bash (PowerShell)
|
|
5
|
+
#
|
|
6
|
+
# Requires CLIPBOARD_VISION_API_KEY environment variable.
|
|
7
|
+
# WARNING: Do not run with `set -x` — the API key would be logged.
|
|
8
|
+
set -euo pipefail
|
|
9
|
+
|
|
10
|
+
OUTPUT_FILE="${1:-/tmp/clipboard_desc.txt}"
|
|
11
|
+
API_KEY="${CLIPBOARD_VISION_API_KEY:-}"
|
|
12
|
+
API_BASE="${CLIPBOARD_VISION_API_BASE:-https://lab.iwhalecloud.com/gpt-proxy}"
|
|
13
|
+
MODEL="LOCAL/Qwen3.5-122B-A10B"
|
|
14
|
+
TEMP_PNG=""
|
|
15
|
+
|
|
16
|
+
if [ -z "$API_KEY" ]; then
|
|
17
|
+
echo "ERROR: CLIPBOARD_VISION_API_KEY environment variable not set" >&2
|
|
18
|
+
exit 1
|
|
19
|
+
fi
|
|
20
|
+
|
|
21
|
+
cleanup() {
|
|
22
|
+
if [ -n "$TEMP_PNG" ] && [ -f "$TEMP_PNG" ]; then
|
|
23
|
+
rm -f "$TEMP_PNG"
|
|
24
|
+
fi
|
|
25
|
+
}
|
|
26
|
+
trap cleanup EXIT
|
|
27
|
+
|
|
28
|
+
# --- Detect platform and read clipboard ---
|
|
29
|
+
get_base64_from_clipboard() {
|
|
30
|
+
local platform b64_cmd
|
|
31
|
+
platform="$(uname -s)"
|
|
32
|
+
|
|
33
|
+
if [ "$platform" = "Linux" ]; then
|
|
34
|
+
b64_cmd="base64 -w 0"
|
|
35
|
+
# WSL / Linux: use xclip or wl-paste
|
|
36
|
+
if command -v xclip &>/dev/null; then
|
|
37
|
+
TARGETS="$(xclip -selection clipboard -o -t TARGETS 2>/dev/null || echo "")"
|
|
38
|
+
if echo "$TARGETS" | grep -qx "image/png"; then
|
|
39
|
+
TEMP_PNG="$(mktemp /tmp/clipboard_XXXXXX.png)"
|
|
40
|
+
xclip -selection clipboard -o -t image/png > "$TEMP_PNG"
|
|
41
|
+
$b64_cmd "$TEMP_PNG"
|
|
42
|
+
else
|
|
43
|
+
echo "ERROR: No PNG image in clipboard (xclip targets: ${TARGETS:-none})" >&2
|
|
44
|
+
exit 1
|
|
45
|
+
fi
|
|
46
|
+
elif command -v wl-paste &>/dev/null; then
|
|
47
|
+
TEMP_PNG="$(mktemp /tmp/clipboard_XXXXXX.png)"
|
|
48
|
+
wl-paste -t image/png > "$TEMP_PNG"
|
|
49
|
+
$b64_cmd "$TEMP_PNG"
|
|
50
|
+
else
|
|
51
|
+
echo "ERROR: No clipboard tool found (install xclip or wl-clipboard)" >&2
|
|
52
|
+
exit 1
|
|
53
|
+
fi
|
|
54
|
+
elif [ "$platform" = "MINGW64_NT" ] || [ "$platform" = "MSYS_NT" ] || [ "${OS:-}" = "Windows_NT" ]; then
|
|
55
|
+
b64_cmd="base64 -w 0"
|
|
56
|
+
# Windows Git Bash: delegate to PowerShell
|
|
57
|
+
PS_EXE="/c/Windows/System32/WindowsPowerShell/v1.0/powershell.exe"
|
|
58
|
+
if [ ! -f "$PS_EXE" ]; then
|
|
59
|
+
PS_EXE="powershell.exe"
|
|
60
|
+
fi
|
|
61
|
+
TEMP_PNG="$(mktemp --suffix=.png)"
|
|
62
|
+
WIN_TEMP="$(cygpath -w "$TEMP_PNG" 2>/dev/null || echo "$TEMP_PNG")"
|
|
63
|
+
"$PS_EXE" -NoProfile -NonInteractive -Command "
|
|
64
|
+
Add-Type -AssemblyName System.Windows.Forms;
|
|
65
|
+
Add-Type -AssemblyName System.Drawing;
|
|
66
|
+
\$img = [System.Windows.Forms.Clipboard]::GetImage();
|
|
67
|
+
if (-not \$img) { Write-Host 'NO_IMAGE'; exit 1 };
|
|
68
|
+
\$img.Save('$WIN_TEMP', [System.Drawing.Imaging.ImageFormat]::Png);
|
|
69
|
+
Write-Host 'OK';
|
|
70
|
+
" > /dev/null
|
|
71
|
+
if [ ! -f "$TEMP_PNG" ] || [ ! -s "$TEMP_PNG" ]; then
|
|
72
|
+
echo "ERROR: No image found in clipboard" >&2
|
|
73
|
+
exit 1
|
|
74
|
+
fi
|
|
75
|
+
$b64_cmd "$TEMP_PNG"
|
|
76
|
+
else
|
|
77
|
+
# macOS
|
|
78
|
+
b64_cmd="base64 -b 0"
|
|
79
|
+
if command -v osascript &>/dev/null; then
|
|
80
|
+
TEMP_PNG="$(mktemp /tmp/clipboard_XXXXXX.png)"
|
|
81
|
+
if ! osascript -e "set theFile to (open for access POSIX file \"$TEMP_PNG\" with write permission)" \
|
|
82
|
+
-e "write (the clipboard as «class PNGf») to theFile" \
|
|
83
|
+
-e "close access theFile" 2>/dev/null; then
|
|
84
|
+
echo "ERROR: Failed to read image from macOS clipboard" >&2
|
|
85
|
+
exit 1
|
|
86
|
+
fi
|
|
87
|
+
if [ ! -s "$TEMP_PNG" ]; then
|
|
88
|
+
echo "ERROR: No image in clipboard" >&2
|
|
89
|
+
exit 1
|
|
90
|
+
fi
|
|
91
|
+
$b64_cmd "$TEMP_PNG"
|
|
92
|
+
else
|
|
93
|
+
echo "ERROR: Unsupported platform ($platform)" >&2
|
|
94
|
+
exit 1
|
|
95
|
+
fi
|
|
96
|
+
fi
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
# --- Build and send API request via python3 (safe JSON construction) ---
|
|
100
|
+
call_vision_api() {
|
|
101
|
+
local b64="$1"
|
|
102
|
+
python3 -c "
|
|
103
|
+
import json, sys, os, urllib.request
|
|
104
|
+
|
|
105
|
+
api_key = os.environ['CLIPBOARD_VISION_API_KEY']
|
|
106
|
+
api_base = os.environ.get('CLIPBOARD_VISION_API_BASE', 'https://lab.iwhalecloud.com/gpt-proxy')
|
|
107
|
+
model = 'LOCAL/Qwen3.5-122B-A10B'
|
|
108
|
+
|
|
109
|
+
body = json.dumps({
|
|
110
|
+
'model': model,
|
|
111
|
+
'messages': [{
|
|
112
|
+
'role': 'user',
|
|
113
|
+
'content': [
|
|
114
|
+
{'type': 'text', 'text': 'Describe this image in detail. Include all visible text, UI elements, code, error messages, objects, people, and any information conveyed. Be specific and thorough but concise. If it contains code or terminal output, quote it exactly.'},
|
|
115
|
+
{'type': 'image_url', 'image_url': {'url': f'data:image/png;base64,{sys.argv[1]}'}}
|
|
116
|
+
]
|
|
117
|
+
}],
|
|
118
|
+
'max_tokens': 1024,
|
|
119
|
+
'temperature': 0.1
|
|
120
|
+
}).encode('utf-8')
|
|
121
|
+
|
|
122
|
+
req = urllib.request.Request(
|
|
123
|
+
f'{api_base}/chat/completions',
|
|
124
|
+
data=body,
|
|
125
|
+
headers={
|
|
126
|
+
'Authorization': f'Bearer {api_key}',
|
|
127
|
+
'Content-Type': 'application/json'
|
|
128
|
+
}
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
try:
|
|
132
|
+
with urllib.request.urlopen(req, timeout=30) as resp:
|
|
133
|
+
data = json.loads(resp.read().decode('utf-8'))
|
|
134
|
+
if not data.get('choices') or not data['choices'][0].get('message'):
|
|
135
|
+
print('ERROR: Unexpected API response structure', file=sys.stderr)
|
|
136
|
+
sys.exit(1)
|
|
137
|
+
content = data['choices'][0]['message'].get('content', '')
|
|
138
|
+
if not content:
|
|
139
|
+
print('ERROR: Empty response from vision model', file=sys.stderr)
|
|
140
|
+
sys.exit(1)
|
|
141
|
+
print(content)
|
|
142
|
+
except Exception as e:
|
|
143
|
+
print(f'ERROR: API call failed: {e}', file=sys.stderr)
|
|
144
|
+
sys.exit(1)
|
|
145
|
+
" "$b64"
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
# --- Main ---
|
|
149
|
+
echo "[clipboard-vision] Reading clipboard..." >&2
|
|
150
|
+
B64="$(get_base64_from_clipboard)"
|
|
151
|
+
|
|
152
|
+
echo "[clipboard-vision] Calling vision model ($MODEL)..." >&2
|
|
153
|
+
|
|
154
|
+
DESCRIPTION="$(call_vision_api "$B64")"
|
|
155
|
+
|
|
156
|
+
echo "$DESCRIPTION" > "$OUTPUT_FILE"
|
|
157
|
+
echo "[clipboard-vision] Description saved to: $OUTPUT_FILE" >&2
|
|
158
|
+
|
|
159
|
+
echo "---"
|
|
160
|
+
echo "$DESCRIPTION"
|
|
161
|
+
echo "---"
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
[CmdletBinding()]
|
|
2
|
+
param(
|
|
3
|
+
[switch]$AllowRemote,
|
|
4
|
+
[string]$ConfirmRemote
|
|
5
|
+
)
|
|
6
|
+
|
|
7
|
+
$ErrorActionPreference = 'Stop'
|
|
8
|
+
$ApiBase = if ($env:CLIPBOARD_VISION_API_BASE) { $env:CLIPBOARD_VISION_API_BASE } else { 'http://127.0.0.1:11434/v1' }
|
|
9
|
+
$Model = $env:CLIPBOARD_VISION_MODEL
|
|
10
|
+
$ApiKey = $env:CLIPBOARD_VISION_API_KEY
|
|
11
|
+
$MaxBytes = if ($env:CLIPBOARD_VISION_MAX_BYTES) { [long]$env:CLIPBOARD_VISION_MAX_BYTES } else { 10485760 }
|
|
12
|
+
|
|
13
|
+
if (-not $Model) { throw 'CLIPBOARD_VISION_MODEL is required' }
|
|
14
|
+
if ($MaxBytes -le 0) { throw 'CLIPBOARD_VISION_MAX_BYTES must be positive' }
|
|
15
|
+
$Endpoint = $null
|
|
16
|
+
if (-not [Uri]::TryCreate($ApiBase, [UriKind]::Absolute, [ref]$Endpoint) -or
|
|
17
|
+
-not $Endpoint.IsAbsoluteUri -or
|
|
18
|
+
$Endpoint.Scheme -notin @('http', 'https') -or
|
|
19
|
+
$Endpoint.UserInfo.Length -ne 0 -or
|
|
20
|
+
$Endpoint.Query.Length -ne 0 -or
|
|
21
|
+
$Endpoint.Fragment.Length -ne 0) {
|
|
22
|
+
throw 'CLIPBOARD_VISION_API_BASE must be an absolute HTTP(S) URI without credentials'
|
|
23
|
+
}
|
|
24
|
+
$Address = $null
|
|
25
|
+
$LiteralAddress = [Net.IPAddress]::TryParse($Endpoint.Host.Trim('[', ']'), [ref]$Address)
|
|
26
|
+
$Loopback = $LiteralAddress -and [Net.IPAddress]::IsLoopback($Address)
|
|
27
|
+
$NormalizedHost = $Endpoint.Host.Trim('[', ']').ToLowerInvariant()
|
|
28
|
+
$EffectivePort = if ($Endpoint.IsDefaultPort) { if ($Endpoint.Scheme -eq 'https') { 443 } else { 80 } } else { $Endpoint.Port }
|
|
29
|
+
$DisplayHost = if ($NormalizedHost.Contains(':')) { "[$NormalizedHost]" } else { $NormalizedHost }
|
|
30
|
+
$BasePath = $Endpoint.AbsolutePath.TrimEnd('/')
|
|
31
|
+
$EffectiveEndpoint = "$($Endpoint.Scheme.ToLowerInvariant())://$DisplayHost`:$EffectivePort$BasePath/chat/completions"
|
|
32
|
+
if (-not $Loopback) {
|
|
33
|
+
$ConsentBytes = [Text.Encoding]::UTF8.GetBytes("$EffectiveEndpoint`n$Model")
|
|
34
|
+
$Hasher = [Security.Cryptography.SHA256]::Create()
|
|
35
|
+
try { $ExpectedConfirmation = ([BitConverter]::ToString($Hasher.ComputeHash($ConsentBytes))).Replace('-', '').ToLowerInvariant() } finally { $Hasher.Dispose() }
|
|
36
|
+
[Console]::Error.WriteLine("Remote disclosure: clipboard image data will be sent to $EffectiveEndpoint using model $Model.")
|
|
37
|
+
[Console]::Error.WriteLine("Confirmation token for this canonical endpoint and model: $ExpectedConfirmation")
|
|
38
|
+
if (-not $AllowRemote -or $ConfirmRemote -cne $ExpectedConfirmation) {
|
|
39
|
+
throw 'Remote endpoint requires -AllowRemote and the disclosed -ConfirmRemote token'
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
$TempDirectory = Join-Path ([IO.Path]::GetTempPath()) ("clipboard-vision-" + [Guid]::NewGuid())
|
|
44
|
+
$ImagePath = Join-Path $TempDirectory 'clipboard.png'
|
|
45
|
+
try {
|
|
46
|
+
New-Item -ItemType Directory -Path $TempDirectory | Out-Null
|
|
47
|
+
if ($env:CLIPBOARD_VISION_TEST_IMAGE) {
|
|
48
|
+
Copy-Item -LiteralPath $env:CLIPBOARD_VISION_TEST_IMAGE -Destination $ImagePath
|
|
49
|
+
} else {
|
|
50
|
+
Add-Type -AssemblyName System.Windows.Forms
|
|
51
|
+
Add-Type -AssemblyName System.Drawing
|
|
52
|
+
$Image = [Windows.Forms.Clipboard]::GetImage()
|
|
53
|
+
if ($null -eq $Image) { throw 'Clipboard does not contain an image' }
|
|
54
|
+
try { $Image.Save($ImagePath, [Drawing.Imaging.ImageFormat]::Png) } finally { $Image.Dispose() }
|
|
55
|
+
}
|
|
56
|
+
if ((Get-Item $ImagePath).Length -gt $MaxBytes) { throw "Clipboard image exceeds $MaxBytes bytes" }
|
|
57
|
+
|
|
58
|
+
$Encoded = [Convert]::ToBase64String([IO.File]::ReadAllBytes($ImagePath))
|
|
59
|
+
$Body = @{
|
|
60
|
+
model = $Model
|
|
61
|
+
messages = @(@{ role = 'user'; content = @(
|
|
62
|
+
@{ type = 'text'; text = 'Describe this clipboard image.' },
|
|
63
|
+
@{ type = 'image_url'; image_url = @{ url = "data:image/png;base64,$Encoded" } }
|
|
64
|
+
) })
|
|
65
|
+
} | ConvertTo-Json -Depth 8 -Compress
|
|
66
|
+
$Headers = @{}
|
|
67
|
+
if ($ApiKey) { $Headers.Authorization = "Bearer $ApiKey" }
|
|
68
|
+
try {
|
|
69
|
+
if ($env:CLIPBOARD_VISION_TEST_HTTP_CAPTURE) {
|
|
70
|
+
Set-Content -LiteralPath $env:CLIPBOARD_VISION_TEST_HTTP_CAPTURE -Value $Body -Encoding utf8
|
|
71
|
+
if ($env:CV_TEST_HTTP_FAILURE) { throw 'test HTTP failure body secret' }
|
|
72
|
+
if ($env:CV_TEST_MALFORMED_JSON) { $Response = $null }
|
|
73
|
+
elseif ($env:CV_TEST_NON_STRING) { $Response = @{ choices = @(@{ message = @{ content = @{ value = 1 } } }) } }
|
|
74
|
+
else { $Response = @{ choices = @(@{ message = @{ content = 'test description' } }) } }
|
|
75
|
+
} else {
|
|
76
|
+
$Response = Invoke-RestMethod -Method Post -Uri $EffectiveEndpoint -Headers $Headers -ContentType 'application/json' -Body $Body -MaximumRedirection 0
|
|
77
|
+
}
|
|
78
|
+
} catch {
|
|
79
|
+
throw 'Vision API request failed (response body redacted)'
|
|
80
|
+
}
|
|
81
|
+
$Result = $Response.choices[0].message.content
|
|
82
|
+
if ($Result -isnot [string]) { throw 'Invalid vision API response (body redacted)' }
|
|
83
|
+
$Result
|
|
84
|
+
} finally {
|
|
85
|
+
Remove-Item -LiteralPath $TempDirectory -Recurse -Force -ErrorAction SilentlyContinue
|
|
86
|
+
}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
api_base="${CLIPBOARD_VISION_API_BASE:-http://127.0.0.1:11434/v1}"
|
|
5
|
+
model="${CLIPBOARD_VISION_MODEL:-}"
|
|
6
|
+
api_key="${CLIPBOARD_VISION_API_KEY:-}"
|
|
7
|
+
max_bytes="${CLIPBOARD_VISION_MAX_BYTES:-10485760}"
|
|
8
|
+
allow_remote=false
|
|
9
|
+
confirm_remote=""
|
|
10
|
+
while [ "$#" -gt 0 ]; do
|
|
11
|
+
case "$1" in
|
|
12
|
+
--allow-remote) allow_remote=true; shift ;;
|
|
13
|
+
--confirm-remote)
|
|
14
|
+
[ "$#" -ge 2 ] || { printf '%s\n' 'Error: --confirm-remote requires the disclosed token' >&2; exit 2; }
|
|
15
|
+
confirm_remote="$2"
|
|
16
|
+
shift 2
|
|
17
|
+
;;
|
|
18
|
+
--help)
|
|
19
|
+
printf '%s\n' 'Usage: clipboard-vision.sh [--allow-remote --confirm-remote TOKEN]'
|
|
20
|
+
exit 0
|
|
21
|
+
;;
|
|
22
|
+
*) printf 'Error: unknown option: %s\n' "$1" >&2; exit 2 ;;
|
|
23
|
+
esac
|
|
24
|
+
done
|
|
25
|
+
|
|
26
|
+
[ -n "$model" ] || { printf '%s\n' 'Error: CLIPBOARD_VISION_MODEL is required' >&2; exit 2; }
|
|
27
|
+
case "$max_bytes" in *[!0-9]*|'') printf '%s\n' 'Error: CLIPBOARD_VISION_MAX_BYTES must be a positive integer' >&2; exit 2 ;; esac
|
|
28
|
+
[ "$max_bytes" -gt 0 ] || { printf '%s\n' 'Error: CLIPBOARD_VISION_MAX_BYTES must be positive' >&2; exit 2; }
|
|
29
|
+
|
|
30
|
+
endpoint_info=$(python3 - "$api_base" "$model" <<'PY'
|
|
31
|
+
import hashlib
|
|
32
|
+
import ipaddress
|
|
33
|
+
import sys
|
|
34
|
+
import urllib.parse
|
|
35
|
+
|
|
36
|
+
url = urllib.parse.urlparse(sys.argv[1])
|
|
37
|
+
if url.scheme not in {"http", "https"} or not url.hostname or url.username or url.password or url.query or url.fragment:
|
|
38
|
+
raise SystemExit(2)
|
|
39
|
+
try:
|
|
40
|
+
loopback = ipaddress.ip_address(url.hostname).is_loopback
|
|
41
|
+
except ValueError:
|
|
42
|
+
loopback = False
|
|
43
|
+
try:
|
|
44
|
+
port = url.port or (443 if url.scheme == "https" else 80)
|
|
45
|
+
except ValueError:
|
|
46
|
+
raise SystemExit(2)
|
|
47
|
+
print("local" if loopback else "remote")
|
|
48
|
+
hostname = url.hostname.lower()
|
|
49
|
+
print(hostname)
|
|
50
|
+
display_host = f"[{hostname}]" if ":" in hostname else hostname
|
|
51
|
+
base_path = url.path.rstrip("/")
|
|
52
|
+
effective = f"{url.scheme.lower()}://{display_host}:{port}{base_path}/chat/completions"
|
|
53
|
+
print(effective)
|
|
54
|
+
print(hashlib.sha256(f"{effective}\n{sys.argv[2]}".encode()).hexdigest())
|
|
55
|
+
PY
|
|
56
|
+
) || { printf '%s\n' 'Error: invalid CLIPBOARD_VISION_API_BASE' >&2; exit 2; }
|
|
57
|
+
endpoint_kind=$(printf '%s\n' "$endpoint_info" | sed -n '1p')
|
|
58
|
+
effective_endpoint=$(printf '%s\n' "$endpoint_info" | sed -n '3p')
|
|
59
|
+
expected_confirmation=$(printf '%s\n' "$endpoint_info" | sed -n '4p')
|
|
60
|
+
|
|
61
|
+
if [ "$endpoint_kind" = remote ]; then
|
|
62
|
+
printf 'Remote disclosure: clipboard image data will be sent to %s using model %s.\n' "$effective_endpoint" "$model" >&2
|
|
63
|
+
printf 'Confirmation token for this canonical endpoint and model: %s\n' "$expected_confirmation" >&2
|
|
64
|
+
if [ "$allow_remote" != true ] || [ "$confirm_remote" != "$expected_confirmation" ]; then
|
|
65
|
+
printf '%s\n' 'Error: remote endpoint requires --allow-remote and the disclosed --confirm-remote token' >&2
|
|
66
|
+
exit 2
|
|
67
|
+
fi
|
|
68
|
+
fi
|
|
69
|
+
|
|
70
|
+
work_dir=$(mktemp -d "${TMPDIR:-/tmp}/clipboard-vision.XXXXXX")
|
|
71
|
+
image_file="$work_dir/clipboard.png"
|
|
72
|
+
request_file="$work_dir/request.json"
|
|
73
|
+
response_file="$work_dir/response.json"
|
|
74
|
+
auth_file="$work_dir/auth-header"
|
|
75
|
+
cleanup() { rm -rf -- "$work_dir"; }
|
|
76
|
+
trap cleanup EXIT HUP INT TERM
|
|
77
|
+
|
|
78
|
+
if command -v pngpaste >/dev/null 2>&1; then
|
|
79
|
+
pngpaste "$image_file"
|
|
80
|
+
elif command -v wl-paste >/dev/null 2>&1; then
|
|
81
|
+
wl-paste --no-newline --type image/png > "$image_file"
|
|
82
|
+
elif command -v import >/dev/null 2>&1; then
|
|
83
|
+
import clipboard: "$image_file"
|
|
84
|
+
else
|
|
85
|
+
printf '%s\n' 'Error: install pngpaste, wl-paste, or ImageMagick import' >&2
|
|
86
|
+
exit 2
|
|
87
|
+
fi
|
|
88
|
+
|
|
89
|
+
[ -s "$image_file" ] || { printf '%s\n' 'Error: clipboard does not contain an image' >&2; exit 2; }
|
|
90
|
+
image_bytes=$(wc -c < "$image_file" | tr -d '[:space:]')
|
|
91
|
+
[ "$image_bytes" -le "$max_bytes" ] || { printf 'Error: clipboard image exceeds %s bytes\n' "$max_bytes" >&2; exit 2; }
|
|
92
|
+
|
|
93
|
+
python3 - "$image_file" "$request_file" "$model" <<'PY'
|
|
94
|
+
import base64
|
|
95
|
+
import json
|
|
96
|
+
import pathlib
|
|
97
|
+
import sys
|
|
98
|
+
|
|
99
|
+
image = base64.b64encode(pathlib.Path(sys.argv[1]).read_bytes()).decode("ascii")
|
|
100
|
+
request = {
|
|
101
|
+
"model": sys.argv[3],
|
|
102
|
+
"messages": [{"role": "user", "content": [
|
|
103
|
+
{"type": "text", "text": "Describe this clipboard image."},
|
|
104
|
+
{"type": "image_url", "image_url": {"url": "data:image/png;base64," + image}},
|
|
105
|
+
]}],
|
|
106
|
+
}
|
|
107
|
+
pathlib.Path(sys.argv[2]).write_text(json.dumps(request), encoding="utf-8")
|
|
108
|
+
PY
|
|
109
|
+
|
|
110
|
+
curl_args=(--silent --show-error --max-redirs 0 --output "$response_file" --write-out '%{http_code}' --header 'Content-Type: application/json' --data-binary "@$request_file")
|
|
111
|
+
if [ -n "$api_key" ]; then
|
|
112
|
+
case "$api_key" in *$'\n'*|*$'\r'*) printf '%s\n' 'Error: invalid CLIPBOARD_VISION_API_KEY' >&2; exit 2 ;; esac
|
|
113
|
+
umask 077
|
|
114
|
+
printf 'Authorization: Bearer %s\n' "$api_key" > "$auth_file"
|
|
115
|
+
chmod 600 "$auth_file"
|
|
116
|
+
curl_args+=(--header "@$auth_file")
|
|
117
|
+
fi
|
|
118
|
+
status=$(curl "${curl_args[@]}" "$effective_endpoint") || { printf '%s\n' 'Error: vision API request failed' >&2; exit 1; }
|
|
119
|
+
case "$status" in 2??) ;; *) printf 'Error: vision API returned HTTP %s (body redacted)\n' "$status" >&2; exit 1 ;; esac
|
|
120
|
+
|
|
121
|
+
result=$(python3 - "$response_file" <<'PY'
|
|
122
|
+
import json
|
|
123
|
+
import pathlib
|
|
124
|
+
import sys
|
|
125
|
+
|
|
126
|
+
try:
|
|
127
|
+
data = json.loads(pathlib.Path(sys.argv[1]).read_text(encoding="utf-8"))
|
|
128
|
+
value = data["choices"][0]["message"]["content"]
|
|
129
|
+
if not isinstance(value, str):
|
|
130
|
+
raise ValueError
|
|
131
|
+
except (KeyError, IndexError, TypeError, ValueError, json.JSONDecodeError):
|
|
132
|
+
raise SystemExit(2)
|
|
133
|
+
print(value)
|
|
134
|
+
PY
|
|
135
|
+
) || { printf '%s\n' 'Error: invalid vision API response (body redacted)' >&2; exit 1; }
|
|
136
|
+
|
|
137
|
+
printf '%s\n' "$result"
|
|
@@ -4,9 +4,10 @@
|
|
|
4
4
|
"======================================",
|
|
5
5
|
"Copy this file to .delphi-config.json and fill in your API keys.",
|
|
6
6
|
"Place this file alongside SKILL.md in the delphi-review skill directory.",
|
|
7
|
-
"
|
|
7
|
+
"Exactly three roles are required, with distinct trimmed executable model IDs.",
|
|
8
8
|
"",
|
|
9
|
-
"Provider
|
|
9
|
+
"Provider and vendor are unrestricted. provider=local is a fallback and cannot count as a successful expert execution.",
|
|
10
|
+
"cross_provider_required is deprecated and ignored with warning code cross_provider_required_ignored."
|
|
10
11
|
],
|
|
11
12
|
|
|
12
13
|
"active_profile": "default",
|
|
@@ -14,53 +15,47 @@
|
|
|
14
15
|
"profiles": {
|
|
15
16
|
"default": {
|
|
16
17
|
"providers": {
|
|
17
|
-
"
|
|
18
|
-
"base_url": "https://api.deepseek.com/v1",
|
|
19
|
-
"api_key": "YOUR_DEEPSEEK_API_KEY"
|
|
20
|
-
},
|
|
21
|
-
"zhipu": {
|
|
22
|
-
"base_url": "https://open.bigmodel.cn/api/paas/v4",
|
|
23
|
-
"api_key": "YOUR_ZHIPU_API_KEY"
|
|
24
|
-
},
|
|
25
|
-
"dashscope": {
|
|
18
|
+
"bailian-tp": {
|
|
26
19
|
"base_url": "https://dashscope.aliyuncs.com/compatible-mode/v1",
|
|
27
|
-
"api_key": "
|
|
20
|
+
"api_key": "YOUR_API_KEY"
|
|
28
21
|
}
|
|
29
22
|
},
|
|
30
23
|
"experts": {
|
|
31
24
|
"architecture": {
|
|
32
|
-
"provider": "
|
|
33
|
-
"model": "
|
|
25
|
+
"provider": "bailian-tp",
|
|
26
|
+
"model": "qwen-plus"
|
|
34
27
|
},
|
|
35
28
|
"technical": {
|
|
36
|
-
"provider": "
|
|
37
|
-
"model": "
|
|
29
|
+
"provider": "bailian-tp",
|
|
30
|
+
"model": "deepseek-v3"
|
|
38
31
|
},
|
|
39
32
|
"feasibility": {
|
|
40
|
-
"provider": "
|
|
41
|
-
"model": "
|
|
33
|
+
"provider": "bailian-tp",
|
|
34
|
+
"model": "glm-4.5"
|
|
42
35
|
}
|
|
43
36
|
}
|
|
44
37
|
},
|
|
45
38
|
|
|
46
39
|
"starter": {
|
|
47
|
-
"$comment": "
|
|
40
|
+
"$comment": "One provider and token plan serving three distinct models",
|
|
48
41
|
"providers": {
|
|
49
|
-
"
|
|
50
|
-
"base_url": "https://
|
|
51
|
-
"api_key": "
|
|
42
|
+
"bailian-tp": {
|
|
43
|
+
"base_url": "https://dashscope.aliyuncs.com/compatible-mode/v1",
|
|
44
|
+
"api_key": "YOUR_API_KEY"
|
|
52
45
|
}
|
|
53
46
|
},
|
|
54
47
|
"experts": {
|
|
55
48
|
"architecture": {
|
|
56
|
-
"provider": "
|
|
57
|
-
"model": "
|
|
49
|
+
"provider": "bailian-tp",
|
|
50
|
+
"model": "qwen-plus"
|
|
58
51
|
},
|
|
59
52
|
"technical": {
|
|
60
|
-
"provider": "
|
|
53
|
+
"provider": "bailian-tp",
|
|
54
|
+
"model": "deepseek-v3"
|
|
61
55
|
},
|
|
62
56
|
"feasibility": {
|
|
63
|
-
"provider": "
|
|
57
|
+
"provider": "bailian-tp",
|
|
58
|
+
"model": "glm-4.5"
|
|
64
59
|
}
|
|
65
60
|
}
|
|
66
61
|
}
|
|
@@ -68,6 +63,8 @@
|
|
|
68
63
|
|
|
69
64
|
"consensus": {
|
|
70
65
|
"threshold_percent": 90,
|
|
71
|
-
"max_review_rounds": 5
|
|
66
|
+
"max_review_rounds": 5,
|
|
67
|
+
"distinct_models_required": true,
|
|
68
|
+
"cross_provider_required": true
|
|
72
69
|
}
|
|
73
70
|
}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
# SKILLS/DELPHI-REVIEW KNOWLEDGE BASE
|
|
2
2
|
|
|
3
|
-
**Generated:** 2026-
|
|
4
|
-
**Commit:**
|
|
3
|
+
**Generated:** 2026-08-30
|
|
4
|
+
**Commit:** 0848858
|
|
5
5
|
**Branch:** main
|
|
6
|
-
**Version:** 0.
|
|
6
|
+
**Version:** 0.19.1.0
|
|
7
7
|
|
|
8
8
|
## OVERVIEW
|
|
9
|
-
Delphi Consensus Review — multi-round anonymous expert review (≥90% threshold, 3 experts
|
|
9
|
+
Delphi Consensus Review — multi-round anonymous expert review (≥90% threshold, exactly 3 experts with distinct executable model IDs). Supports design, requirements, and code-walkthrough modes.
|
|
10
10
|
|
|
11
11
|
## STRUCTURE
|
|
12
12
|
```
|
|
@@ -25,14 +25,14 @@ skills/delphi-review/
|
|
|
25
25
|
| Task | Location | Notes |
|
|
26
26
|
|------|----------|-------|
|
|
27
27
|
| Core methodology | SKILL.md | Delphi process, expert roles, consensus rules |
|
|
28
|
-
| Code-walkthrough | references/code-walkthrough.md | Pre-push mode:
|
|
29
|
-
| Config example | .delphi-config.json.example | 3 experts,
|
|
28
|
+
| Code-walkthrough | references/code-walkthrough.md | Pre-push mode: complete review evidence with no hard file/LOC threshold |
|
|
29
|
+
| Config example | .delphi-config.json.example | 3 experts, distinct models |
|
|
30
30
|
|
|
31
31
|
## CONVENTIONS
|
|
32
32
|
- 3 experts anonymous in Round 1 (no cross-expert bias)
|
|
33
33
|
- ≥90% consensus threshold (was 95%, now unified to 90%)
|
|
34
34
|
- Max 5 rounds before forcing decision
|
|
35
|
-
-
|
|
35
|
+
- Provider/vendor/nationality unrestricted; exactly three distinct trimmed requested model IDs are required
|
|
36
36
|
- Model selection: reads `delphi-reviewer-*` agent `model` fields from `opencode.json`
|
|
37
37
|
- No hardcoded model lists — models defined by user's `opencode.json` configuration
|
|
38
38
|
- Code-walkthrough mode: triggered on git push, stores result in .code-walkthrough-result.json
|
|
@@ -42,25 +42,26 @@ skills/delphi-review/
|
|
|
42
42
|
- Do NOT terminate before achieving true consensus (≥90%)
|
|
43
43
|
- Do NOT reveal other experts' opinions during Round 1
|
|
44
44
|
- Do NOT accept partial agreement without resolution
|
|
45
|
-
- Do NOT skip code-walkthrough
|
|
45
|
+
- Do NOT auto-skip or bypass code-walkthrough based on change size; review large diffs completely or split them by user choice
|
|
46
46
|
- Do NOT degrade to single model on API errors (BLOCK)
|
|
47
47
|
- Do NOT declare complete without writing .code-walkthrough-result.json
|
|
48
|
-
- Do NOT
|
|
48
|
+
- Do NOT treat `provider: local` fallback as an executed expert
|
|
49
49
|
|
|
50
50
|
## UNIQUE STYLES
|
|
51
51
|
- Anonymous expert reviews (Round 1)
|
|
52
52
|
- Statistical consensus measurement (≥90% threshold)
|
|
53
|
-
-
|
|
53
|
+
- Three modes: requirements review + design review + code-walkthrough
|
|
54
54
|
- Pre-push integration: .code-walkthrough-result.json stores commit hash + verdict
|
|
55
55
|
- Delphi guard in claude-code plugin: blocks Edit/Write before APPROVAL
|
|
56
56
|
|
|
57
57
|
## COMMANDS
|
|
58
58
|
```bash
|
|
59
59
|
/delphi-review # Design review mode
|
|
60
|
+
/delphi-review --mode requirements # Phase 2 R1 requirements review
|
|
60
61
|
/delphi-review --mode code-walkthrough # Pre-push code walkthrough
|
|
61
62
|
```
|
|
62
63
|
|
|
63
64
|
## NOTES
|
|
64
65
|
- Used by pre-push hook for code-walkthrough validation
|
|
65
66
|
- Delphi guard (claude-code plugin) reads .sprint-state/delphi-reviewed.json
|
|
66
|
-
- Code-walkthrough result must match commit hash for verification
|
|
67
|
+
- Code-walkthrough result must match commit hash for verification
|