dsh-aris-panel 0.1.6 → 0.1.7

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 (79) hide show
  1. package/dsh/client.js +164 -11
  2. package/dsh/index.mjs +17 -0
  3. package/dsh/workbench.mjs +99 -3
  4. package/mcp-servers/claude-review/README.md +3 -0
  5. package/package.json +51 -51
  6. package/skills/auto-paper-improvement-loop/SKILL.md +50 -6
  7. package/skills/auto-paper-improvement-loop-claude/SKILL.md +633 -0
  8. package/skills/auto-review-loop-claude/SKILL.md +524 -0
  9. package/skills/citation-audit-claude/SKILL.md +512 -0
  10. package/skills/experiment-audit/SKILL.md +4 -4
  11. package/skills/experiment-audit-claude/SKILL.md +321 -0
  12. package/skills/experiment-bridge/SKILL.md +13 -0
  13. package/skills/gpt-image-2/SKILL.md +67 -0
  14. package/skills/idea-creator/SKILL.md +59 -17
  15. package/skills/idea-discovery/SKILL.md +24 -5
  16. package/skills/idea-discovery-robot/SKILL.md +13 -0
  17. package/skills/kill-argument/SKILL.md +7 -7
  18. package/skills/kill-argument-claude/SKILL.md +447 -0
  19. package/skills/meta-apply/SKILL.md +1 -1
  20. package/skills/meta-apply-claude/SKILL.md +151 -0
  21. package/skills/novelty-check/SKILL.md +49 -8
  22. package/skills/novelty-check-claude/SKILL.md +151 -0
  23. package/skills/paper-claim-audit/SKILL.md +3 -3
  24. package/skills/paper-claim-audit-claude/SKILL.md +358 -0
  25. package/skills/paper-figure/SKILL.md +36 -2
  26. package/skills/paper-figure-claude/SKILL.md +327 -0
  27. package/skills/paper-plan-claude/SKILL.md +295 -0
  28. package/skills/paper-poster-html/SKILL.md +13 -0
  29. package/skills/paper-talk/SKILL.md +13 -0
  30. package/skills/paper-write/SKILL.md +81 -3
  31. package/skills/paper-write-claude/SKILL.md +504 -0
  32. package/skills/paper-writing/SKILL.md +41 -5
  33. package/skills/proof-checker/SKILL.md +11 -11
  34. package/skills/proof-checker-claude/SKILL.md +876 -0
  35. package/skills/rebuttal/SKILL.md +13 -0
  36. package/skills/research-pipeline/SKILL.md +25 -6
  37. package/skills/research-refine-claude/SKILL.md +740 -0
  38. package/skills/research-refine-pipeline/SKILL.md +13 -0
  39. package/skills/research-review/SKILL.md +33 -8
  40. package/skills/research-review-claude/SKILL.md +185 -0
  41. package/skills/resubmit-pipeline/SKILL.md +14 -1
  42. package/skills/result-to-claim/SKILL.md +2 -2
  43. package/skills/result-to-claim-claude/SKILL.md +321 -0
  44. package/skills/shared-references/effort-contract.md +1 -1
  45. package/skills/shared-references/fan-out-pattern.md +1 -1
  46. package/skills/shared-references/reviewer-routing.md +10 -10
  47. package/skills/skills-codex/auto-paper-improvement-loop/SKILL.md +49 -5
  48. package/skills/skills-codex/experiment-audit/SKILL.md +5 -5
  49. package/skills/skills-codex/idea-creator/SKILL.md +46 -13
  50. package/skills/skills-codex/idea-discovery/SKILL.md +7 -5
  51. package/skills/skills-codex/kill-argument/SKILL.md +5 -5
  52. package/skills/skills-codex/meta-apply/SKILL.md +1 -1
  53. package/skills/skills-codex/novelty-check/SKILL.md +49 -8
  54. package/skills/skills-codex/paper-claim-audit/SKILL.md +3 -3
  55. package/skills/skills-codex/paper-write/SKILL.md +78 -2
  56. package/skills/skills-codex/paper-writing/SKILL.md +17 -4
  57. package/skills/skills-codex/proof-checker/SKILL.md +9 -9
  58. package/skills/skills-codex/research-pipeline/SKILL.md +12 -6
  59. package/skills/skills-codex/research-review/SKILL.md +32 -7
  60. package/skills/skills-codex/resubmit-pipeline/SKILL.md +1 -1
  61. package/skills/skills-codex/result-to-claim/SKILL.md +2 -2
  62. package/skills/skills-codex/shared-references/effort-contract.md +1 -1
  63. package/skills/skills-codex/shared-references/reviewer-routing.md +7 -7
  64. package/skills/skills-codex-claude-review/auto-paper-improvement-loop/SKILL.md +49 -5
  65. package/skills/skills-codex-claude-review/auto-review-loop/SKILL.md +8 -2
  66. package/skills/skills-codex-claude-review/novelty-check/SKILL.md +49 -8
  67. package/skills/skills-codex-claude-review/paper-write/SKILL.md +78 -2
  68. package/skills/skills-codex-claude-review/research-review/SKILL.md +29 -1
  69. package/skills/skills-codex-gemini-review/auto-paper-improvement-loop/SKILL.md +61 -16
  70. package/skills/skills-codex-gemini-review/idea-creator/SKILL.md +37 -13
  71. package/skills/skills-codex-gemini-review/idea-discovery/SKILL.md +4 -4
  72. package/skills/skills-codex-gemini-review/novelty-check/SKILL.md +49 -8
  73. package/skills/skills-codex-gemini-review/paper-write/SKILL.md +78 -2
  74. package/skills/skills-codex-gemini-review/paper-writing/SKILL.md +17 -4
  75. package/skills/skills-codex-gemini-review/research-review/SKILL.md +26 -1
  76. package/tools/convert_skills_to_llm_chat.py +11 -11
  77. package/tools/generate_codex_claude_review_overrides.py +67 -11
  78. package/skills/skills-codex/render-html/scripts/__pycache__/render_html.cpython-314.pyc +0 -0
  79. package/tools/__pycache__/check_skills_inventory.cpython-314.pyc +0 -0
package/dsh/client.js CHANGED
@@ -204,6 +204,39 @@ window.__ModuleLoader__.load({
204
204
 
205
205
  const WB_CHANNEL = '/aris-wb'
206
206
  const WB_TICK = String.fromCharCode(96)
207
+ const IEEE_CONF_VENUES = ['ICASSP', 'ICIP', 'ICC', 'GLOBECOM', 'INFOCOM', 'ISCAS', 'IJCNN', 'WCNC', 'VTC', 'ICME', 'ACSSC', 'MMSP']
208
+ const IEEE_JOURNAL_VENUES = ['T-PAMI', 'TIP', 'TSP', 'TWC', 'TCOM', 'JSAC', 'TASLP', 'TNNLS', 'TCSVT', 'SPL', 'TIFS', 'TCYB']
209
+ const CLAUDE_VARIANT_MAP = {
210
+ 'research-review': 'research-review-claude',
211
+ 'novelty-check': 'novelty-check-claude',
212
+ 'research-refine': 'research-refine-claude',
213
+ 'auto-review-loop': 'auto-review-loop-claude',
214
+ 'paper-plan': 'paper-plan-claude',
215
+ 'paper-figure': 'paper-figure-claude',
216
+ 'paper-write': 'paper-write-claude',
217
+ 'auto-paper-improvement-loop': 'auto-paper-improvement-loop-claude',
218
+ 'proof-checker': 'proof-checker-claude',
219
+ 'kill-argument': 'kill-argument-claude',
220
+ 'paper-claim-audit': 'paper-claim-audit-claude',
221
+ 'citation-audit': 'citation-audit-claude',
222
+ 'result-to-claim': 'result-to-claim-claude',
223
+ 'experiment-audit': 'experiment-audit-claude',
224
+ 'meta-apply': 'meta-apply-claude',
225
+ }
226
+ const CLAUDE_VARIANT_NAMES = 'research-review、novelty-check、research-refine、auto-review-loop、paper-plan、paper-figure、paper-write、auto-paper-improvement-loop、proof-checker、kill-argument、paper-claim-audit、citation-audit、result-to-claim、experiment-audit、meta-apply'
227
+ const CLAUDE_WORKFLOWS = ['idea-discovery', 'idea-discovery-robot', 'research-pipeline', 'research-refine-pipeline', 'experiment-bridge', 'paper-writing', 'rebuttal', 'resubmit-pipeline', 'paper-talk', 'paper-poster-html']
228
+ function wbClaudeUnsupportedHint(skillName) {
229
+ return '当前技能「' + String(skillName || '') + '」暂无 Claude 审稿变体,也不是受支持的工作流。' +
230
+ 'Claude 审稿可用:独立审稿/写作技能(' + CLAUDE_VARIANT_NAMES + '),或工作流(' + CLAUDE_WORKFLOWS.join('、') + ')。请改选其一,或把审稿路由改回 codex/auto。'
231
+ }
232
+ function wbStripParam(text, key) {
233
+ if (typeof text !== 'string' || text === '') return ''
234
+ const safe = String(key).replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
235
+ let out = text.replace(new RegExp('(^|,\\s*)' + safe + '\\s*:\\s*[^,]+', 'gi'), ',')
236
+ out = out.replace(/,\s*,/g, ',').replace(/^,\s*/, '').replace(/,\s*$/, '').trim()
237
+ return out
238
+ }
239
+ function wbStripReviewerModel(text) { return wbStripParam(text, 'reviewer-model') }
207
240
  const FLOW_STAGES = [
208
241
  { id: 1, title: '课题现状与可行性', desc: '提出方向,调研现状与可行性', skills: ['research-lit', 'idea-discovery', 'arxiv', 'deepxiv'], next: '用 research-lit 调研现状,或用 idea-discovery 一次跑完「现状→想法→排名」' },
209
242
  { id: 2, title: '细化理解形成思路', desc: '来回交流,打磨出研究思路', skills: ['idea-creator', 'novelty-check', 'kill-argument', 'research-review'], next: 'idea-creator 补新想法、novelty-check 查新、kill-argument 对抗检验后敲定思路' },
@@ -223,7 +256,7 @@ function inferStage(ov) {
223
256
 
224
257
  const WORKFLOW_CHIPS = [
225
258
  { num: 'W1', label: 'Idea Discovery 想法发现', entry: 'idea-discovery', when: '开始新研究,有方向但还没想法时', desc: '完整想法发现管线:多源文献调研 → 头脑风暴 8-12 个具体想法 → 可行性与算力过滤 → 深度新颖性验证 + 魔鬼代言人审 → top 2-3 想法并行 GPU 试跑(30min-2h) → 按实验信号排名 → 精炼出问题锚定提案 → claim 驱动的实验路线图。产出 IDEA_REPORT.md 排名报告 + FINAL_PROPOSAL + EXPERIMENT_PLAN。' },
226
- { num: 'W1.5', label: 'Experiment Bridge 实验桥接', entry: 'experiment-bridge', when: '已有实验计划,需要落地实现代码并跑出结果时', desc: '把实验计划变成运行结果:解析 EXPERIMENT_PLAN.md → 实现实验代码(带 argparse/日志/种子) → GPT-5.6-Sol 审代码抓逻辑 bug → 最小实验 sanity check → 部署 GPU 跑全套 → 收集结果更新实验追踪。' },
259
+ { num: 'W1.5', label: 'Experiment Bridge 实验桥接', entry: 'experiment-bridge', when: '已有实验计划,需要落地实现代码并跑出结果时', desc: '把实验计划变成运行结果:解析 EXPERIMENT_PLAN.md → 实现实验代码(带 argparse/日志/种子) → GPT-6-Astra 审代码抓逻辑 bug → 最小实验 sanity check → 部署 GPU 跑全套 → 收集结果更新实验追踪。' },
227
260
  { num: 'W2', label: 'Auto Review Loop 自动审稿循环', entry: 'auto-review-loop', when: '论文/结果需要迭代打磨、想挂机自动改进时', desc: '无人值守审稿循环:外部审稿人深审论文/claims/实验指出弱点 → 自动修复(重写章节/加 baseline/补实验) → 重新评估,循环直到 ≥6/10 或 4 轮上限;>4 GPU 小时的实验自动跳过并标记人工跟进,断点可从 REVIEW_STATE.json 恢复。' },
228
261
  { num: 'W3', label: 'Paper Writing 论文写作', entry: 'paper-writing', when: '叙事报告就绪、准备把研究写成论文时', desc: '从叙事报告到可投稿 PDF:claims-evidence 矩阵 + 章节规划 → 数据驱动图表 → 逐节 LaTeX → 编译 PDF 修错 + 页数检查 → 两轮外部内容审 + 最终格式检查。' },
229
262
  { num: 'W4', label: 'Rebuttal 回复审稿', entry: 'rebuttal', when: '收到会议/期刊审稿意见时', desc: '安全回复审稿意见:解析意见拆成 issue 卡片 → 全局策略 + 逐审稿人优先级 → 可选自动补实验 → 起草回复 → 6 项安全检查(覆盖/溯源/承诺/语气/一致性/字数) → GPT 压力测试 → 输出限字版 + 富文本版。' },
@@ -394,6 +427,14 @@ const SKILL_ZH = {
394
427
  "wiki-enrich": "wiki 补全",
395
428
  "writing-systems-papers": "系统论文写作指南",
396
429
  "xlsx": "Excel 表格处理",
430
+ "research-review-claude": "研究深度审稿(Claude Code)",
431
+ "novelty-check-claude": "新颖性查新(Claude Code)",
432
+ "research-refine-claude": "研究方案精炼(Claude Code)",
433
+ "auto-review-loop-claude": "自动审稿循环(Claude Code)",
434
+ "paper-plan-claude": "论文大纲规划(Claude Code)",
435
+ "paper-figure-claude": "论文图表生成(Claude Code)",
436
+ "paper-write-claude": "论文写作(Claude Code)",
437
+ "auto-paper-improvement-loop-claude": "论文自动改进循环(Claude Code)",
397
438
  }
398
439
 
399
440
  function WbProgress(props) {
@@ -430,18 +471,68 @@ function WbTrigger(props) {
430
471
  const [effort, setEffort] = React.useState('balanced')
431
472
  const [assurance, setAssurance] = React.useState('draft')
432
473
  const [difficulty, setDifficulty] = React.useState('medium')
474
+ const [illustration, setIllustration] = React.useState('figurespec')
475
+ const [figureCheck, setFigureCheck] = React.useState('visual')
433
476
  const [extra, setExtra] = React.useState('')
434
477
  const [venue, setVenue] = React.useState('')
478
+ const [ieeeVenue, setIeeeVenue] = React.useState('')
435
479
  const [gpu, setGpu] = React.useState('')
436
480
  const [reviewer, setReviewer] = React.useState('auto')
481
+ const [reviewerModel, setReviewerModel] = React.useState('')
482
+ const [codexModels, setCodexModels] = React.useState([])
483
+ const [claudeModels, setClaudeModels] = React.useState([])
437
484
  const [humanCheckpoint, setHumanCheckpoint] = React.useState(false)
438
485
  const [autoProceed, setAutoProceed] = React.useState(true)
439
486
  const [busy, setBusy] = React.useState(false)
487
+ const [modelOpen, setModelOpen] = React.useState(false)
488
+ const modelDirtyRef = React.useRef(false)
489
+
490
+ const loadModels = function (route) {
491
+ const isClaude = route === 'claude'
492
+ if (!isClaude && route !== 'codex' && route !== 'auto') {
493
+ setCodexModels([])
494
+ if (!modelDirtyRef.current) setReviewerModel('')
495
+ return
496
+ }
497
+ const apply = function (ok, models, suggested) {
498
+ if (isClaude) setClaudeModels(models); else setCodexModels(models)
499
+ if (modelDirtyRef.current) return
500
+ const preferred = isClaude ? 'claude-fable-5' : 'gpt-5.6-sol'
501
+ const sug = ok && typeof suggested === 'string' && suggested !== '' ? suggested : ''
502
+ const hasPreferred = models.some(function (m) { return m && m.slug === preferred })
503
+ setReviewerModel(sug !== '' ? sug : (hasPreferred ? preferred : (models.length > 0 ? models[0].slug : preferred)))
504
+ }
505
+ if (isClaude) setClaudeModels([]); else setCodexModels([])
506
+ connection.rpc.call(WB_CHANNEL, isClaude ? 'claude-models' : 'codex-models', {}).then(function (res) {
507
+ const ok = !!(res && res.ok)
508
+ apply(ok, ok && Array.isArray(res.value.models) ? res.value.models : [], ok ? res.value.suggested : undefined)
509
+ }).catch(function () { apply(false, [], undefined) })
510
+ }
511
+ React.useEffect(function () { loadModels(reviewer) }, [reviewer])
512
+ React.useEffect(function () {
513
+ if (String(skill || '').endsWith('-claude') && reviewer !== 'claude') {
514
+ modelDirtyRef.current = false
515
+ loadModels('claude')
516
+ }
517
+ }, [skill])
440
518
 
441
519
  const doLaunch = function () {
442
520
  if (!skill) { setStatus('请先选择技能'); return }
521
+ let effSkill = skill
522
+ if (reviewer === 'claude' && !String(skill || '').endsWith('-claude')) {
523
+ effSkill = CLAUDE_VARIANT_MAP[skill] || (CLAUDE_WORKFLOWS.indexOf(String(skill || '')) !== -1 ? skill : '')
524
+ }
525
+ if (reviewer === 'claude' && !effSkill) {
526
+ setStatus(wbClaudeUnsupportedHint(skill))
527
+ return
528
+ }
529
+ const effVenue = venue === 'ieee-conf' || venue === 'ieee-journal' ? ieeeVenue : venue
530
+ if ((venue === 'ieee-conf' || venue === 'ieee-journal') && !ieeeVenue) {
531
+ setStatus('已选 IEEE 大类:请在下拉中选择具体的会议/期刊')
532
+ return
533
+ }
443
534
  setBusy(true)
444
- connection.rpc.call(WB_CHANNEL, 'launch', { sessionId: sessionId, skill: skill, args: args, autoProceed: autoProceed, effort: effort, assurance: assurance, difficulty: difficulty, venue: venue, gpu: gpu, reviewer: reviewer, humanCheckpoint: humanCheckpoint, targetSessionId: props.targetSessionId && props.targetSessionId !== '__new__' ? props.targetSessionId : undefined, newSession: props.targetSessionId === '__new__', extra: extra })
535
+ connection.rpc.call(WB_CHANNEL, 'launch', { sessionId: sessionId, skill: effSkill || skill, args: args, autoProceed: autoProceed, effort: effort, assurance: assurance, difficulty: difficulty, illustration: illustration, figureCheck: figureCheck, venue: effVenue, gpu: gpu, reviewer: reviewer, reviewerModel: reviewerModel, humanCheckpoint: humanCheckpoint, targetSessionId: props.targetSessionId && props.targetSessionId !== '__new__' ? props.targetSessionId : undefined, newSession: props.targetSessionId === '__new__', extra: extra })
445
536
  .then(function (res) {
446
537
  if (res.ok) {
447
538
  setStatus(res.value.injected ? '已注入会话(' + res.value.method + '),执行已唤醒,请到会话窗口查看进度' : '未能注入,请复制提示词手动发送:' + res.value.prompt)
@@ -458,14 +549,33 @@ function WbTrigger(props) {
458
549
  if (effort !== 'balanced') params.push('effort: ' + effort)
459
550
  if (assurance !== 'draft') params.push('assurance: ' + assurance)
460
551
  if (difficulty !== 'medium') params.push('difficulty: ' + difficulty)
552
+ if (illustration && illustration !== 'figurespec') params.push('illustration: ' + illustration)
553
+ if (figureCheck && figureCheck !== 'visual') params.push('figure check: ' + figureCheck)
461
554
  if (humanCheckpoint === true) params.push('human checkpoint: true')
462
- if (venue) params.push('venue: ' + venue)
555
+ const effVenue = venue === 'ieee-conf' || venue === 'ieee-journal' ? ieeeVenue : venue
556
+ if (effVenue) params.push('venue: ' + effVenue)
463
557
  if (gpu) params.push('gpu: ' + gpu)
464
- if (reviewer !== 'auto') params.push('reviewer: ' + reviewer)
465
- const ex = extra.trim()
558
+ const isClaudeSkill = String(skill || '').endsWith('-claude')
559
+ if (reviewer !== 'auto' && (reviewer !== 'claude' || !isClaudeSkill)) params.push('reviewer: ' + reviewer)
560
+ let ex = extra.trim()
561
+ if (effVenue) ex = wbStripParam(ex, 'venue')
562
+ const useClaude = reviewer === 'claude' || isClaudeSkill
563
+ const rm = reviewerModel.trim()
564
+ if (rm && useClaude) {
565
+ params.push('reviewer-model: ' + rm)
566
+ ex = wbStripReviewerModel(ex)
567
+ } else if (rm && (reviewer === 'auto' || reviewer === 'codex')) {
568
+ params.push('reviewer-model: ' + rm)
569
+ ex = wbStripReviewerModel(ex)
570
+ }
466
571
  if (ex) params.push(ex)
467
572
  const arg = args.trim()
468
- return '/' + skill + (arg ? ' "' + arg + '"' : '') + (params.length > 0 ? ' — ' + params.join(', ') : '')
573
+ let effSkill = String(skill || '')
574
+ if (reviewer === 'claude' && !effSkill.endsWith('-claude')) {
575
+ effSkill = CLAUDE_VARIANT_MAP[skill] || (CLAUDE_WORKFLOWS.indexOf(skill) !== -1 ? skill : '')
576
+ }
577
+ if (reviewer === 'claude' && !effSkill) return ''
578
+ return '/' + effSkill + (arg ? ' "' + arg + '"' : '') + (params.length > 0 ? ' — ' + params.join(', ') : '')
469
579
  }
470
580
  const doCopy = function () {
471
581
  if (!skill) { setStatus('请先选择技能'); return }
@@ -477,11 +587,13 @@ function WbTrigger(props) {
477
587
  ;(skills || []).forEach(function (s) {
478
588
  selectOptions.push(h('option', { key: s.name, value: s.name }, s.name + ' — ' + (SKILL_ZH[s.name] || s.description || '').slice(0, 60)))
479
589
  })
590
+ const isClaudeModel = reviewer === 'claude' || String(skill || '').endsWith('-claude')
591
+ const modelChoices = isClaudeModel ? claudeModels : codexModels
480
592
 
481
593
  return h('div', { className: 'wb-trigger' },
482
594
  h('div', { className: 'wb-guide-title' }, '技能触发'),
483
595
  h('div', { className: 'wb-trigger-row' },
484
- h('select', { className: 'wb-skill-select', value: skill, onChange: function (e) { props.onSkill(e.target.value) } }, selectOptions),
596
+ h('select', { className: 'wb-skill-select', value: skill, onChange: function (e) { const v = e.target.value; props.onSkill(v); if (String(v).endsWith('-claude')) { if (reviewer !== 'claude') { modelDirtyRef.current = false; loadModels('claude') } } else if (reviewer !== 'claude') loadModels(reviewer) } }, selectOptions),
485
597
  h('input', { className: 'wb-args', placeholder: '研究方向 (arguments):例如 discrete diffusion LMs 的 factorized gap', value: args, onChange: function (e) { props.onArgs(e.target.value) } })),
486
598
  h('div', { className: 'wb-trigger-row wb-param-row' },
487
599
  h('label', { className: 'wb-param-label' }, '无人值守: '),
@@ -505,28 +617,60 @@ function WbTrigger(props) {
505
617
  h('option', { value: 'medium' }, 'medium (默认)'),
506
618
  h('option', { value: 'hard' }, 'hard (更强)'),
507
619
  h('option', { value: 'nightmare' }, 'nightmare (极限)'))),
620
+ h('div', { className: 'wb-trigger-row wb-param-row' },
621
+ h('label', { className: 'wb-param-label' }, '示意图: '),
622
+ h('select', { className: 'wb-param-select', value: illustration, onChange: function (e) { setIllustration(e.target.value) } },
623
+ h('option', { value: 'figurespec' }, 'figurespec (默认)'),
624
+ h('option', { value: 'gemini' }, 'gemini (AI)'),
625
+ h('option', { value: 'codex-image2' }, 'codex-image2 (AI)'),
626
+ h('option', { value: 'gpt-image-2' }, 'gpt-image-2 (本机转接)'),
627
+ h('option', { value: 'mermaid' }, 'mermaid'),
628
+ h('option', { value: 'false' }, 'false (手动)')),
629
+ h('label', { className: 'wb-param-label' }, '图面检查: '),
630
+ h('select', { className: 'wb-param-select', value: figureCheck, onChange: function (e) { setFigureCheck(e.target.value) } },
631
+ h('option', { value: 'visual' }, 'visual (看图评分, 默认)'),
632
+ h('option', { value: 'text' }, 'text (仅文字)'),
633
+ h('option', { value: 'off' }, 'off (跳过)'))),
508
634
  h('div', { className: 'wb-trigger-row wb-param-row' },
509
635
  h('label', { className: 'wb-param-label' }, '会议: '),
510
- h('select', { className: 'wb-param-select', value: venue, onChange: function (e) { setVenue(e.target.value) } },
636
+ h('select', { className: 'wb-param-select', value: venue, onChange: function (e) { const v = e.target.value; setVenue(v); if (v !== 'ieee-conf' && v !== 'ieee-journal') setIeeeVenue('') } },
511
637
  h('option', { value: '' }, '(默认)'),
512
638
  h('option', { value: 'ICLR' }, 'ICLR'), h('option', { value: 'NeurIPS' }, 'NeurIPS'),
513
639
  h('option', { value: 'ICML' }, 'ICML'), h('option', { value: 'CVPR' }, 'CVPR'),
514
- h('option', { value: 'AAAI' }, 'AAAI'), h('option', { value: 'ACL' }, 'ACL')),
640
+ h('option', { value: 'AAAI' }, 'AAAI'), h('option', { value: 'ACL' }, 'ACL'),
641
+ h('option', { value: 'ieee-conf' }, 'IEEE Conf (IEEEtran)…'),
642
+ h('option', { value: 'ieee-journal' }, 'IEEE Journal (IEEEtran)…')),
643
+ (venue === 'ieee-conf' || venue === 'ieee-journal') ? h('select', { className: 'wb-param-select', value: ieeeVenue, onChange: function (e) { setIeeeVenue(e.target.value) } },
644
+ h('option', { value: '' }, '(选择具体' + (venue === 'ieee-conf' ? '会议' : '期刊') + '…)'),
645
+ (venue === 'ieee-conf' ? IEEE_CONF_VENUES : IEEE_JOURNAL_VENUES).map(function (v) { return h('option', { key: v, value: v }, v) })) : null,
515
646
  h('label', { className: 'wb-param-label' }, 'GPU: '),
516
647
  h('select', { className: 'wb-param-select', value: gpu, onChange: function (e) { setGpu(e.target.value) } },
517
648
  h('option', { value: '' }, '(默认)'), h('option', { value: 'local' }, 'local'),
518
649
  h('option', { value: 'remote' }, 'remote'), h('option', { value: 'vast' }, 'vast'),
519
650
  h('option', { value: 'modal' }, 'modal')),
520
651
  h('label', { className: 'wb-param-label' }, '审稿路由: '),
521
- h('select', { className: 'wb-param-select', value: reviewer, onChange: function (e) { setReviewer(e.target.value) } },
652
+ h('select', { className: 'wb-param-select', value: reviewer, onChange: function (e) { const v = e.target.value; modelDirtyRef.current = false; setReviewer(v) } },
522
653
  h('option', { value: 'auto' }, 'auto (默认)'), h('option', { value: 'codex' }, 'codex'),
654
+ h('option', { value: 'claude' }, 'claude (Claude Code)'),
523
655
  h('option', { value: 'oracle-pro' }, 'oracle-pro'), h('option', { value: 'manual' }, 'manual')),
656
+ (reviewer === 'auto' || reviewer === 'codex' || reviewer === 'claude' || String(skill || '').endsWith('-claude')) ? [
657
+ h('label', { className: 'wb-param-label', key: 'wb-rm-label' }, '审稿模型: '),
658
+ h('span', { className: 'wb-model-combo', key: 'wb-rm-combo' },
659
+ h('input', { className: 'wb-param-input', placeholder: isClaudeModel ? '选择或手动输入;留空=claude-fable-5' : '选择或手动输入;留空=默认 gpt-5.6-sol', value: reviewerModel, onFocus: function () { setModelOpen(true) }, onBlur: function () { setTimeout(function () { setModelOpen(false) }, 150) }, onChange: function (e) { modelDirtyRef.current = true; setReviewerModel(e.target.value); setModelOpen(false) } }),
660
+ h('button', { type: 'button', className: 'wb-model-btn', onMouseDown: function (e) { e.preventDefault() }, onClick: function () { setModelOpen(!modelOpen) } }, '▾'),
661
+ modelOpen ? h('div', { className: 'wb-model-pop', key: 'wb-rm-pop' },
662
+ modelChoices.length > 0 ? modelChoices.map(function (m) {
663
+ return h('div', { className: 'wb-model-opt', key: m.slug, onMouseDown: function (e) { e.preventDefault() }, onClick: function () { modelDirtyRef.current = true; setReviewerModel(m.slug); setModelOpen(false) } },
664
+ (m.display || m.slug) + (Array.isArray(m.levels) && m.levels.length ? ' · 推理档:' + m.levels.join('/') : ''))
665
+ }) : [h('div', { className: 'wb-model-empty', key: 'empty' }, isClaudeModel ? 'Claude 模型列表加载中或不可用,可手动输入' : 'Codex 模型列表加载中或不可用,可手动输入')])
666
+ : null),
667
+ ] : null,
524
668
  h('label', { className: 'wb-param-label' }, '人工确认: '),
525
669
  h('select', { className: 'wb-param-select', value: humanCheckpoint ? 'true' : 'false', onChange: function (e) { setHumanCheckpoint(e.target.value === 'true') } },
526
670
  h('option', { value: 'false' }, 'false (默认)'), h('option', { value: 'true' }, 'true (暂停批准)'))),
527
671
  h('div', { className: 'wb-trigger-row' },
528
672
  h('input', { className: 'wb-extra', placeholder: '其他参数 key: value, key2: value2(可选,如 sources: arxiv, web / gpu: vast)', value: extra, onChange: function (e) { setExtra(e.target.value) } })),
529
- skill ? h('div', { className: 'wb-cmd-preview' }, 'ARIS 命令: ' + buildCmd()) : null,
673
+ skill ? h('div', { className: 'wb-cmd-preview', style: buildCmd() ? undefined : { color: '#c42b1c' } }, 'ARIS 命令: ' + (buildCmd() || wbClaudeUnsupportedHint(skill))) : null,
530
674
  h('div', { className: 'wb-trigger-row' },
531
675
  h('button', { className: 'wb-btn', onClick: doLaunch, disabled: busy }, busy ? '触发中…' : '触发'),
532
676
  h('button', { className: 'wb-btn wb-btn-copy', onClick: doCopy }, '复制提示词')),
@@ -948,6 +1092,15 @@ const WB_CSS = `.wb-overlay { position: fixed; inset: 0; z-index: 9999; backgrou
948
1092
  .wb-verdict-? { background: #f6f8fa; color: #57606a; }
949
1093
  .wb-extra { flex: 1 1 100%; background: #ffffff; color: #1f1f1f; border: 1px solid #c8c8c8; border-radius: 4px; padding: 5px 10px; font-size: 12px; min-width: 200px; }
950
1094
  .wb-cmd-preview { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 12px; color: #24292e; background: #f5f5f5; border: 1px solid #e0e0e0; border-radius: 6px; padding: 8px 10px; margin: 6px 0 10px; word-break: break-all; }
1095
+ .wb-param-input { background: #ffffff; color: #1f1f1f; border: 1px solid #c8c8c8; border-radius: 4px; padding: 3px 6px; font-size: 12px; min-width: 170px; width: 200px; }
1096
+ .wb-model-combo { position: relative; display: inline-flex; align-items: center; }
1097
+ .wb-model-btn { margin-left: 3px; padding: 2px 7px; background: #f2f2f2; color: #1f1f1f; border: 1px solid #c8c8c8; border-radius: 4px; font-size: 11px; cursor: pointer; line-height: 1.4; }
1098
+ .wb-model-btn:hover { background: #e3e3e3; }
1099
+ .wb-model-pop { position: absolute; top: calc(100% + 4px); left: 0; z-index: 80; background: #ffffff; color: #1f1f1f; border: 1px solid #c8c8c8; border-radius: 4px; box-shadow: 0 2px 10px rgba(0, 0, 0, .18); max-height: 240px; overflow-y: auto; min-width: 330px; }
1100
+ .wb-model-opt { padding: 6px 10px; font-size: 12px; cursor: pointer; border-bottom: 1px solid #f0f0f0; white-space: nowrap; }
1101
+ .wb-model-opt:hover { background: #e8f0fe; }
1102
+ .wb-model-opt:last-child { border-bottom: none; }
1103
+ .wb-model-empty { padding: 6px 10px; font-size: 12px; color: #6e7781; }
951
1104
 
952
1105
  .wb-pano-card-when { font-size: 13px; color: #1f1f1f; }
953
1106
 
package/dsh/index.mjs CHANGED
@@ -10,6 +10,7 @@
10
10
 
11
11
  import { fileURLToPath } from 'node:url'
12
12
  import * as skillFilesystem from '@deepseek-ai/dsh-skill-filesystem'
13
+ import * as mcpClient from '@deepseek-ai/dsh-mcp-client'
13
14
  import { registerRunStatus } from './run-status.mjs'
14
15
  import { registerWorkbench } from './workbench.mjs'
15
16
 
@@ -19,6 +20,7 @@ export const inject = ['skills', 'tools']
19
20
  /** Package root; identical in a git checkout and under node_modules. */
20
21
  const PACKAGE_ROOT = fileURLToPath(new URL('../', import.meta.url))
21
22
  const SKILL_ROOT = fileURLToPath(new URL('../skills/', import.meta.url))
23
+ const CLAUDE_REVIEW_SERVER = fileURLToPath(new URL('../mcp-servers/claude-review/server.py', import.meta.url))
22
24
 
23
25
  /** Codex MCP tools whose reply carries a continuation threadId. */
24
26
  const CODEX_TOOLS = new Set(['mcp__codex__codex', 'mcp__codex__codex-reply'])
@@ -44,6 +46,21 @@ export async function apply(ctx) {
44
46
  watch: false,
45
47
  })
46
48
 
49
+ // Claude reviewer bridge (claude-review MCP): drives the local Claude Code CLI
50
+ // as an external reviewer. Each review call may carry its own `model`; the
51
+ // workbench picker resolves to a default of claude-fable-5. Not fatal on
52
+ // startup failure — the Codex route keeps working and the Claude route shows
53
+ // the underlying error.
54
+ await ctx.plugin(mcpClient, {
55
+ serverName: 'claude-review',
56
+ transport: 'stdio',
57
+ command: 'python',
58
+ args: [CLAUDE_REVIEW_SERVER],
59
+ cwd: PACKAGE_ROOT,
60
+ toolCallTimeoutMs: 1200000,
61
+ failOnStartupError: false,
62
+ })
63
+
47
64
  registerRunStatus(ctx)
48
65
  registerWorkbench(ctx)
49
66
 
package/dsh/workbench.mjs CHANGED
@@ -10,6 +10,7 @@
10
10
  import { readdir, readFile, stat } from 'node:fs/promises'
11
11
  import { isAbsolute, join } from 'node:path'
12
12
  import { homedir } from 'node:os'
13
+ import { DatabaseSync } from 'node:sqlite'
13
14
 
14
15
  export const WORKBENCH_RPC_CHANNEL = '/aris-wb'
15
16
 
@@ -25,6 +26,49 @@ async function readJson(workspace, rel) {
25
26
  try { return JSON.parse(await readFile(join(workspace, rel), 'utf8')) } catch { return undefined }
26
27
  }
27
28
 
29
+ async function readCodexModelCatalog() {
30
+ const home = homedir()
31
+ const candidates = []
32
+ try {
33
+ const cfg = await readFile(join(home, '.codex', 'config.toml'), 'utf8')
34
+ const m = /model_catalog_json\s*=\s*["']([^"']+)["']/.exec(cfg)
35
+ if (m) {
36
+ const p = String(m[1]).replace(/\\\\/g, '\\')
37
+ candidates.push(isAbsolute(p) ? p : join(home, '.codex', p))
38
+ }
39
+ } catch { /* ignore */ }
40
+ candidates.push(join(home, '.codex', 'cc-switch-model-catalog.json'))
41
+ for (const p of candidates) {
42
+ try {
43
+ const doc = JSON.parse(await readFile(p, 'utf8'))
44
+ if (doc && Array.isArray(doc.models) && doc.models.length > 0) return { path: p, models: doc.models }
45
+ } catch { /* ignore */ }
46
+ }
47
+ return undefined
48
+ }
49
+
50
+ async function readClaudeModelCatalog() {
51
+ const home = homedir()
52
+ const dbs = [join(home, '.cc-switch', 'cc-switch.db'), join(home, '.ccswitchmulti', 'cc-switch.db')]
53
+ for (const dbPath of dbs) {
54
+ try {
55
+ const db = new DatabaseSync(dbPath, { readOnly: true })
56
+ const rows = db.prepare("SELECT model_id, display_name FROM model_pricing WHERE model_id LIKE 'claude-%' OR display_name LIKE 'Claude %' ORDER BY model_id").all()
57
+ db.close()
58
+ if (rows && rows.length > 0) return { path: dbPath, models: rows }
59
+ } catch { /* try next db */ }
60
+ }
61
+ return undefined
62
+ }
63
+
64
+ function stripKey(text, key) {
65
+ if (typeof text !== 'string' || text === '') return ''
66
+ const safe = String(key).replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
67
+ let out = text.replace(new RegExp('(^|,\\s*)' + safe + '\\s*:\\s*[^,]+', 'gi'), ',')
68
+ out = out.replace(/,\s*,/g, ',').replace(/^,\s*/, '').replace(/,\s*$/, '').trim()
69
+ return out
70
+ }
71
+
28
72
  async function statFile(workspace, rel) {
29
73
  try {
30
74
  const info = await stat(join(workspace, rel))
@@ -262,6 +306,43 @@ export function registerWorkbench(ctx) {
262
306
  return { ok: false, error: { code: 'internal', message: String(error && error.message || error), details: { issues: [] } } }
263
307
  }
264
308
  }
309
+ if (endpoint === 'codex-models') {
310
+ try {
311
+ const cat = await readCodexModelCatalog()
312
+ const models = []
313
+ if (cat) {
314
+ for (const m of cat.models) {
315
+ if (!m || typeof m.slug !== 'string' || m.slug === '') continue
316
+ if (m.supported_in_api === false || m.visibility === 'hidden') continue
317
+ const levels = Array.isArray(m.supported_reasoning_levels)
318
+ ? m.supported_reasoning_levels.map(function (l) { return l && l.effort }).filter(Boolean)
319
+ : []
320
+ models.push({ slug: m.slug, display: m.display_name || m.slug, levels: levels, priority: Number(m.priority) || 0 })
321
+ }
322
+ models.sort(function (a, b) { return (b.priority || 0) - (a.priority || 0) || String(a.slug).localeCompare(String(b.slug)) })
323
+ }
324
+ let suggested
325
+ try {
326
+ const cfg = await readFile(join(homedir(), '.codex', 'config.toml'), 'utf8')
327
+ const m = /^\s*model\s*=\s*["']([^"']+)["']/m.exec(cfg)
328
+ if (m) suggested = m[1]
329
+ } catch { /* ignore */ }
330
+ return { ok: true, value: { models: models, suggested: suggested, source: cat ? cat.path : undefined } }
331
+ } catch (error) {
332
+ return { ok: false, error: { code: 'internal', message: String(error && error.message || error), details: { issues: [] } } }
333
+ }
334
+ }
335
+ if (endpoint === 'claude-models') {
336
+ try {
337
+ const cat = await readClaudeModelCatalog()
338
+ const models = (cat && cat.models ? cat.models : []).map(function (m) {
339
+ return { slug: m.model_id, display: m.display_name || m.model_id }
340
+ })
341
+ return { ok: true, value: { models: models, suggested: models.some(function (m) { return m.slug === 'claude-fable-5' }) ? 'claude-fable-5' : (models.length > 0 ? models[0].slug : undefined), source: cat ? cat.path : undefined } }
342
+ } catch (error) {
343
+ return { ok: false, error: { code: 'internal', message: String(error && error.message || error), details: { issues: [] } } }
344
+ }
345
+ }
265
346
  const sessionId = typeof payload === 'object' && payload !== null ? payload.sessionId : undefined
266
347
  if (typeof sessionId !== 'string') {
267
348
  return { ok: false, error: { code: 'bad-request', message: 'sessionId is required', details: { issues: [] } } }
@@ -333,22 +414,37 @@ export function registerWorkbench(ctx) {
333
414
  const effort = typeof payload === 'object' && payload !== null ? payload.effort : undefined
334
415
  const assurance = typeof payload === 'object' && payload !== null ? payload.assurance : undefined
335
416
  const difficulty = typeof payload === 'object' && payload !== null ? payload.difficulty : undefined
417
+ const illustration = typeof payload === 'object' && payload !== null ? payload.illustration : undefined
418
+ const figureCheck = typeof payload === 'object' && payload !== null ? payload.figureCheck : undefined
336
419
  const extra = typeof payload === 'object' && payload !== null ? payload.extra : undefined
337
420
  const venue = typeof payload === 'object' && payload !== null ? payload.venue : undefined
338
421
  const gpu = typeof payload === 'object' && payload !== null ? payload.gpu : undefined
339
422
  const reviewer = typeof payload === 'object' && payload !== null ? payload.reviewer : undefined
423
+ const reviewerModel = typeof payload === 'object' && payload !== null ? payload.reviewerModel : undefined
340
424
  const humanCheckpoint = typeof payload === 'object' && payload !== null ? payload.humanCheckpoint : undefined
341
425
  const extraText = typeof extra === 'string' ? extra.trim() : ''
426
+ const venueText = typeof venue === 'string' ? venue.trim() : ''
427
+ const reviewerModelText = typeof reviewerModel === 'string' ? reviewerModel.trim() : ''
342
428
  const params = []
343
429
  if (autoProceed === false) params.push('AUTO_PROCEED: false')
344
430
  if (humanCheckpoint === true) params.push('human checkpoint: true')
345
431
  if (effort && effort !== 'balanced') params.push('effort: ' + effort)
346
432
  if (assurance && assurance !== 'draft') params.push('assurance: ' + assurance)
347
433
  if (difficulty && difficulty !== 'medium') params.push('difficulty: ' + difficulty)
348
- if (venue) params.push('venue: ' + venue)
434
+ if (illustration && illustration !== 'figurespec') params.push('illustration: ' + illustration)
435
+ if (figureCheck && figureCheck !== 'visual') params.push('figure check: ' + figureCheck)
436
+ let extraParam = extraText
437
+ if (venueText) {
438
+ params.push('venue: ' + venueText)
439
+ extraParam = stripKey(extraParam, 'venue')
440
+ }
349
441
  if (gpu) params.push('gpu: ' + gpu)
350
- if (reviewer && reviewer !== 'auto') params.push('reviewer: ' + reviewer)
351
- if (extraText) params.push(extraText)
442
+ if (reviewer && reviewer !== 'auto' && (reviewer !== 'claude' || !String(skill || '').endsWith('-claude'))) params.push('reviewer: ' + reviewer)
443
+ if (reviewerModelText) {
444
+ params.push('reviewer-model: ' + reviewerModelText)
445
+ extraParam = stripKey(extraParam, 'reviewer-model')
446
+ }
447
+ if (extraParam) params.push(extraParam)
352
448
  const cmd = '/' + skill + (argText ? ' "' + argText + '"' : '') + (params.length > 0 ? ' — ' + params.join(', ') : '')
353
449
  const prompt = '请执行以下 ARIS 命令:\n' + cmd
354
450
  const messageId = 'aris-launch-' + Date.now() + '-' + Math.random().toString(36).slice(2, 8)
@@ -47,6 +47,9 @@ codex mcp add claude-review -- ~/.codex/mcp-servers/claude-review/run_with_claud
47
47
 
48
48
  - The bridge runs Claude in non-interactive `-p` mode.
49
49
  - By default the reviewer gets **no tools**. This matches the original ARIS pattern where the external reviewer only sees the prompt context prepared by the executor.
50
+ - A prompt that hands the reviewer **artifact paths** instead of pasted context must therefore opt in per call, e.g. `"tools": "Read,Grep,Glob"`. Without it the reviewer is asked to read files it has no tool to open, and it answers from your framing alone — exactly what artifact-grounded review is meant to avoid. The `skills-codex-claude-review` overlay passes this on its artifact-grounded review blocks; the server default stays tool-free, so prompt-only reviews are unchanged.
51
+ - The opt-in is **per call, not per thread**: every call re-sends `--tools`, so a `review_reply` / `review_reply_start` continuing an artifact-grounded thread has to repeat it.
52
+ - Keep the opt-in read-only. The bridge already runs Claude with `--permission-mode plan`, and a reviewer has no reason to hold `Bash`, `Edit`, or `Write`.
50
53
  - `threadId` is the native Claude session id and can be passed directly to `review_reply`.
51
54
  - `jobId` is a bridge-local background task id stored on disk under `~/.codex/state/claude-review/jobs/` by default, so status can be resumed across MCP server restarts.
52
55
 
package/package.json CHANGED
@@ -1,51 +1,51 @@
1
- {
2
- "name": "dsh-aris-panel",
3
- "version": "0.1.6",
4
- "description": "ARIS research-workflow skills + interactive workbench panel for DeepSeek Harness (user fork of dsh-aris: 82 skills, cross-model Codex review, idea/workflow launcher, research status board).",
5
- "license": "MIT",
6
- "type": "module",
7
- "engines": {
8
- "node": "^22.19.0 || >=24.0.0"
9
- },
10
- "keywords": [
11
- "dsh-plugin",
12
- "deepseek-harness",
13
- "aris",
14
- "research",
15
- "agent",
16
- "workbench"
17
- ],
18
- "exports": {
19
- ".": "./dsh/index.mjs",
20
- "./client": "./dsh/client.js",
21
- "./scope-limits": "./dsh/scope-limits.mjs",
22
- "./cordis.patch.yml": "./dsh/cordis.patch.yml",
23
- "./checkout.patch.yml": "./dsh/checkout.patch.yml",
24
- "./package.json": "./package.json"
25
- },
26
- "files": [
27
- "dsh",
28
- "skills",
29
- "tools",
30
- "templates",
31
- "mcp-servers",
32
- "README.md",
33
- "README_CN.md",
34
- "LICENSE"
35
- ],
36
- "publishConfig": {
37
- "access": "public"
38
- },
39
- "dsh": {
40
- "bundle": {
41
- "patch": "./dsh/cordis.patch.yml"
42
- },
43
- "client": {
44
- "platform": "web",
45
- "inject": [
46
- "@deepseek-ai/dsh-client-ui-conversation",
47
- "@deepseek-ai/dsh-client-connection"
48
- ]
49
- }
50
- }
51
- }
1
+ {
2
+ "name": "dsh-aris-panel",
3
+ "version": "0.1.7",
4
+ "description": "ARIS research-workflow skills + interactive workbench panel for DeepSeek Harness (user fork of dsh-aris: 82 skills, cross-model Codex review, idea/workflow launcher, research status board).",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "engines": {
8
+ "node": "^22.19.0 || >=24.0.0"
9
+ },
10
+ "keywords": [
11
+ "dsh-plugin",
12
+ "deepseek-harness",
13
+ "aris",
14
+ "research",
15
+ "agent",
16
+ "workbench"
17
+ ],
18
+ "exports": {
19
+ ".": "./dsh/index.mjs",
20
+ "./client": "./dsh/client.js",
21
+ "./scope-limits": "./dsh/scope-limits.mjs",
22
+ "./cordis.patch.yml": "./dsh/cordis.patch.yml",
23
+ "./checkout.patch.yml": "./dsh/checkout.patch.yml",
24
+ "./package.json": "./package.json"
25
+ },
26
+ "files": [
27
+ "dsh",
28
+ "skills",
29
+ "tools",
30
+ "templates",
31
+ "mcp-servers",
32
+ "README.md",
33
+ "README_CN.md",
34
+ "LICENSE"
35
+ ],
36
+ "publishConfig": {
37
+ "access": "public"
38
+ },
39
+ "dsh": {
40
+ "bundle": {
41
+ "patch": "./dsh/cordis.patch.yml"
42
+ },
43
+ "client": {
44
+ "platform": "web",
45
+ "inject": [
46
+ "@deepseek-ai/dsh-client-ui-conversation",
47
+ "@deepseek-ai/dsh-client-connection"
48
+ ]
49
+ }
50
+ }
51
+ }