psyclaw 0.27.14 → 0.27.18

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 (71) hide show
  1. package/README.md +16 -7
  2. package/dist/apps/panel/index.html +8 -7
  3. package/dist/src/adapters/pi/extension.js +424 -175
  4. package/dist/src/adapters/pi/extension.js.map +1 -1
  5. package/dist/src/analysis/hooks.js +3 -3
  6. package/dist/src/analysis/hooks.js.map +1 -1
  7. package/dist/src/analysis/pre-registration.d.ts +5 -4
  8. package/dist/src/analysis/pre-registration.js +3 -3
  9. package/dist/src/analysis/pre-registration.js.map +1 -1
  10. package/dist/src/branding.d.ts +1 -1
  11. package/dist/src/branding.js +17 -12
  12. package/dist/src/branding.js.map +1 -1
  13. package/dist/src/chat.js +9 -0
  14. package/dist/src/chat.js.map +1 -1
  15. package/dist/src/cli.js +1 -18
  16. package/dist/src/cli.js.map +1 -1
  17. package/dist/src/index.d.ts +1 -0
  18. package/dist/src/index.js +1 -0
  19. package/dist/src/index.js.map +1 -1
  20. package/dist/src/orchestration/contracts.d.ts +4 -1
  21. package/dist/src/panel/events.js +28 -1
  22. package/dist/src/panel/events.js.map +1 -1
  23. package/dist/src/panel/extension.d.ts +1 -1
  24. package/dist/src/panel/extension.js +16 -1
  25. package/dist/src/panel/extension.js.map +1 -1
  26. package/dist/src/panel/projection.d.ts +1 -1
  27. package/dist/src/panel/projection.js +30 -12
  28. package/dist/src/panel/projection.js.map +1 -1
  29. package/dist/src/panel/server.d.ts +12 -0
  30. package/dist/src/panel/server.js +104 -2
  31. package/dist/src/panel/server.js.map +1 -1
  32. package/dist/src/project/approvals.d.ts +8 -0
  33. package/dist/src/project/approvals.js +6 -7
  34. package/dist/src/project/approvals.js.map +1 -1
  35. package/dist/src/project/bootstrap.d.ts +0 -17
  36. package/dist/src/project/bootstrap.js +1 -87
  37. package/dist/src/project/bootstrap.js.map +1 -1
  38. package/dist/src/project/hitl.d.ts +4 -1
  39. package/dist/src/project/hitl.js +8 -5
  40. package/dist/src/project/hitl.js.map +1 -1
  41. package/dist/src/research/brief.js +1 -1
  42. package/dist/src/research/brief.js.map +1 -1
  43. package/dist/src/research/decision.d.ts +46 -0
  44. package/dist/src/research/decision.js +85 -0
  45. package/dist/src/research/decision.js.map +1 -0
  46. package/dist/src/style/cli-ui.js +2 -3
  47. package/dist/src/style/cli-ui.js.map +1 -1
  48. package/dist/src/tui/skill-manager.d.ts +1 -0
  49. package/dist/src/tui/skill-manager.js +6 -1
  50. package/dist/src/tui/skill-manager.js.map +1 -1
  51. package/dist/src/wizard.js +16 -7
  52. package/dist/src/wizard.js.map +1 -1
  53. package/dist/src/workflows/expert-review-runner.js +4 -6
  54. package/dist/src/workflows/expert-review-runner.js.map +1 -1
  55. package/dist/src/workflows/institutional-fulltext.js +3 -3
  56. package/dist/src/workflows/institutional-fulltext.js.map +1 -1
  57. package/dist/src/workflows/publish.js +3 -2
  58. package/dist/src/workflows/publish.js.map +1 -1
  59. package/dist/src/workflows/spec.js +1 -1
  60. package/dist/src/workflows/spec.js.map +1 -1
  61. package/dist/src/workflows/writing-review.js +40 -7
  62. package/dist/src/workflows/writing-review.js.map +1 -1
  63. package/package.json +4 -2
  64. package/scripts/psyclaw.mjs +27 -0
  65. package/scripts/rebrand-pi.mjs +77 -16
  66. package/skills/core/academic-grill/SKILL.md +9 -1
  67. package/skills/core/citation-audit/SKILL.md +2 -0
  68. package/skills/core/research-brief/SKILL.md +6 -0
  69. package/skills/core/research-intake/SKILL.md +1 -1
  70. package/skills/recommended/catalog.json +32 -31
  71. package/skills/recommended/mcp-catalog.json +13 -1
package/README.md CHANGED
@@ -1,27 +1,37 @@
1
1
  # PsyClaw
2
2
 
3
- PsyClaw 是面向社会科学研究的智能体工作台。它把研究项目、证据来源、Claim-Evidence 账本、完整性门禁、人工裁决、可恢复工作流和本地面板接到内置运行时上;它不替代统计软件,也不会把未经核验的引用、结果或审稿意见写成事实。
3
+ PsyClaw 是面向社会科学研究的智能体工作台。它把研究项目、证据来源、Claim-Evidence 账本、完整性门禁、可恢复工作流和本地面板接到内置运行时上;只有无法由证据和通行方法消解、且会实质改变研究设计或解释的分歧才交由研究者取舍。它不替代统计软件,也不会把未经核验的引用、结果或审稿意见写成事实。
4
4
 
5
- 当前版本:`0.27.14`。正式命令、用户配置目录和后续发布统一使用 `psyclaw`。
5
+ 当前版本:`0.27.18`。正式命令、用户配置目录和后续发布统一使用 `psyclaw`。
6
+
7
+ ## 发布流程
8
+
9
+ 发布只提交已跟踪文件,并自动递增 patch 版本、创建版本标签,推送当前分支和标签;`.DS_Store`、论文附件、图形和其他未跟踪生成物不会被加入。GitHub Actions 随标签推送执行 Node 22 检查并发布 npm:
10
+
11
+ ```bash
12
+ RELEASE_MESSAGE="release: describe the change" pnpm release:push
13
+ ```
14
+
15
+ 运行前请确认当前分支、工作区变更和 GitHub 权限。NPM 发布由 GitHub Actions 的 Trusted Publishing 完成,本机不需要 `npm login`。
6
16
 
7
17
  ## 安装
8
18
 
9
19
  需要 Node.js `>=22.19.0`。官方 npm 源:
10
20
 
11
21
  ```powershell
12
- npm install -g psyclaw@0.27.14
22
+ npm install -g psyclaw@0.27.18
13
23
  ```
14
24
 
15
25
  如果本机 npm 配置把 registry 误写成带有 `~/` 的地址,请显式指定官方源:
16
26
 
17
27
  ```bash
18
- npm install -g psyclaw@0.27.14 --registry=https://registry.npmjs.org/
28
+ npm install -g psyclaw@0.27.18 --registry=https://registry.npmjs.org/
19
29
  ```
20
30
 
21
31
  中国大陆网络较慢或无法访问官方源时:
22
32
 
23
33
  ```powershell
24
- npm install -g psyclaw@0.27.14 --registry=https://registry.npmmirror.com
34
+ npm install -g psyclaw@0.27.18 --registry=https://registry.npmmirror.com
25
35
  ```
26
36
 
27
37
  确认命令入口:
@@ -30,7 +40,7 @@ npm install -g psyclaw@0.27.14 --registry=https://registry.npmmirror.com
30
40
  psyclaw --help
31
41
  ```
32
42
 
33
- 直接运行 `psyclaw` 会启动交互研究工作台;首次没有模型配置时会进入配置向导。向导会检查当前进程、macOS `launchctl` 和已有用户凭据,也允许直接遮罩输入 Key。`launchctl` Key 只注入当前运行进程、不落盘;登录 shell 仅在用户明确按 `Ctrl+I` 后读取。直接输入或明确导入的 Key 只保存到 PsyClaw 用户级 `auth.json`,不会写入项目或 `models.json`。也可显式运行 `psyclaw wizard`,或使用 `psyclaw setup --provider deepseek` 写入仅引用环境变量的预设。不要把 API key 写进命令参数、项目、README 或 Git 仓库。
43
+ 直接运行 `psyclaw` 会启动交互研究工作台;首次没有模型配置时会自动进入配置流程。配置界面会检查当前进程、macOS `launchctl` 和已有用户凭据,也允许直接遮罩输入 Key。`launchctl` Key 只注入当前运行进程、不落盘;登录 shell 仅在用户明确按 `Ctrl+I` 后读取。直接输入或明确导入的 Key 只保存到 PsyClaw 用户级 `auth.json`,不会写入项目或 `models.json`。模型、凭据、会话和宿主资源统一保存在 `~/.psyclaw/agent`,PsyClaw 不创建或使用 `~/.pi`。不要把 API key 写进命令参数、项目、README 或 Git 仓库。
34
44
 
35
45
  ## 五分钟开始
36
46
 
@@ -64,7 +74,6 @@ psyclaw brief
64
74
  | 模拟同行评审 | 论文完成后在对话中输入 `/review` |
65
75
  | 登记本地证据 | `psyclaw evidence add <path> --level user\|fulltext` |
66
76
  | 生成离线简报 | `psyclaw brief` |
67
- | 创建人工裁决模板 | `psyclaw hitl init` |
68
77
  | 写研究移交记录 | `psyclaw handoff` |
69
78
  | 扫描本机其他 Agent | `psyclaw agents` |
70
79
  | 查看 PsyClaw 和内置运行时更新 | `psyclaw check-updates` |
@@ -17,7 +17,7 @@
17
17
  <div class="layout">
18
18
  <nav class="nav" aria-label="Panel 导航">
19
19
  <button class="active" data-view="project">项目文件</button>
20
- <button data-view="ecosystem">Skill / MCP</button>
20
+ <button data-view="ecosystem">Skill / Plugin / MCP</button>
21
21
  <button data-view="provider">Provider 与成本</button>
22
22
  </nav>
23
23
  <main class="main">
@@ -33,8 +33,8 @@
33
33
  </section>
34
34
 
35
35
  <section id="view-ecosystem" class="view">
36
- <div class="page-head"><div><h1>Skill / MCP 生态</h1><p>安装、启用和停用研究能力。配置变更后使用 <code>/reload</code>。</p></div><button id="refresh-ecosystem" class="button">刷新</button></div>
37
- <div class="controls" style="margin-bottom:12px"><input id="eco-search" placeholder="搜索名称或描述"><select id="eco-kind"><option value="all">全部类型</option><option value="skill">Skill</option><option value="local">用户 Skill</option><option value="mcp">MCP</option><option value="external">外部工具</option></select><select id="eco-status"><option value="all">全部状态</option><option value="enabled">已启用</option><option value="available">未启用</option></select></div>
36
+ <div class="page-head"><div><h1>Skill / Plugin / MCP 生态</h1><p>在同一页面管理研究 Skill、Pi Plugin 与 MCP。配置变更后使用 <code>/reload</code>。</p></div><button id="refresh-ecosystem" class="button">刷新</button></div>
37
+ <div class="controls" style="margin-bottom:12px"><input id="eco-search" placeholder="搜索名称或描述"><div class="segmented" aria-label="能力类型"><button class="active" data-eco-kind="all">全部</button><button data-eco-kind="skill">Skill</button><button data-eco-kind="plugin">Plugin</button><button data-eco-kind="mcp">MCP</button></div><select id="eco-kind" aria-label="更多类型"><option value="all">全部来源</option><option value="local">用户 Skill</option><option value="external">外部工具</option></select><select id="eco-status"><option value="all">全部状态</option><option value="enabled">已启用</option><option value="available">未启用</option></select></div>
38
38
  <div class="table-wrap"><table class="data"><thead><tr><th>名称</th><th>类型</th><th>来源</th><th>状态</th><th>操作</th></tr></thead><tbody id="ecosystem-body"><tr><td colspan="5">读取中...</td></tr></tbody></table></div>
39
39
  </section>
40
40
 
@@ -50,7 +50,7 @@
50
50
  </div>
51
51
  </div>
52
52
  <script>
53
- const state={view:"project",files:[],eco:[],models:[],active:{},usage:null,currency:"cny"};
53
+ const state={view:"project",files:[],eco:[],ecoKind:"all",models:[],active:{},usage:null,currency:"cny"};
54
54
  const $=id=>document.getElementById(id);
55
55
  const esc=value=>String(value??"").replace(/[&<>"']/g,ch=>({"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;"}[ch]));
56
56
  async function json(url,options){const response=await fetch(url,options);const body=await response.json().catch(()=>({error:"请求失败"}));if(!response.ok)throw new Error(body.error||"请求失败");return body}
@@ -65,11 +65,12 @@ function selectTree(button){document.querySelectorAll(".tree-item").forEach(item
65
65
  async function openFile(path,button){selectTree(button);$("viewer-path").textContent=path;$("viewer-kind").textContent="读取中";$("viewer-body").className="viewer-body empty";$("viewer-body").textContent="读取中...";try{const file=await json(`/api/project-file?path=${encodeURIComponent(path)}`);$("viewer-kind").textContent=file.format;$("viewer-body").className="viewer-body";$("viewer-body").innerHTML=file.format==="markdown"?`<article class="markdown">${renderMarkdown(file.content)}</article>`:`<pre class="raw">${esc(file.format==="json"?JSON.stringify(JSON.parse(file.content),null,2):file.content)}</pre>`}catch(error){$("viewer-body").className="viewer-body empty";$("viewer-body").textContent=error.message}}
66
66
  async function loadProject(){const files=await json("/api/project-files");state.files=files.files||[];renderTree();const goal=document.querySelector('[data-file="notes/goal.md"]')||document.querySelector("[data-file]");if(goal)goal.click()}
67
67
 
68
- async function loadEcosystem(){try{const[skills,mcps,enabled]=await Promise.all([json("/api/recommended-skills"),json("/api/recommended-mcps"),json("/api/enabled-capabilities")]);const skillSet=new Set((enabled.skills||[]).map(item=>item.id)),mcpSet=new Set((enabled.mcp||[]).map(item=>item.id));state.eco=[...(enabled.coreSkills||[]).map(item=>({...item,kind:"core",description:"PsyClaw 内置 Skill",sourceRef:"bundled",enabled:item.enabled,installed:true})),...(skills.items||[]).map(item=>({...item,kind:"skill",enabled:skillSet.has(item.id),installed:skillSet.has(item.id)})),...(skills.plugins||[]).map(item=>({...item,kind:"plugin",enabled:false,installed:false})),...(enabled.localSkills||[]).map(item=>({...item,kind:"local",sourceRef:item.path||"用户目录",installed:true})),...(mcps.items||[]).map(item=>({...item,kind:"mcp",enabled:mcpSet.has(item.id),installed:mcpSet.has(item.id)})),...(skills.externalTools||[]).map(item=>({...item,kind:"external",enabled:false,installed:false}))];renderEcosystem()}catch(error){$("ecosystem-body").innerHTML=`<tr><td colspan="5">${esc(error.message)}</td></tr>`}}
69
- function renderEcosystem(){const query=$("eco-search").value.trim().toLowerCase(),kind=$("eco-kind").value,status=$("eco-status").value;const items=state.eco.filter(item=>(kind==="all"||item.kind===kind)&&(status==="all"||(status==="enabled"?item.enabled:!item.enabled))&&`${item.name} ${item.description||""}`.toLowerCase().includes(query));$("ecosystem-body").innerHTML=items.map(item=>`<tr><td><div class="name">${esc(item.name)}</div><div class="subtle">${esc(item.description||"")}</div></td><td>${esc(item.kind==="core"?"Core Skill":item.kind==="local"?"用户 Skill":item.kind.toUpperCase())}</td><td>${/^https?:/.test(item.sourceRef||"")?`<a href="${esc(item.sourceRef)}" target="_blank" rel="noreferrer">来源</a>`:esc(item.sourceRef||"bundled")}</td><td><span class="tag ${item.enabled?"ok":""}">${item.enabled?"已启用":item.kind==="external"?"外部工具":"未启用"}</span></td><td>${item.kind==="external"?"—":item.enabled?`<button class="button small" onclick="toggleCapability('${item.kind}','${esc(item.id)}',false)">停用</button>`:item.kind==="local"?`<button class="button small primary" onclick="toggleCapability('local','${esc(item.id)}',true)">启用</button>`:`<button class="button small primary" onclick="installCapability('${item.kind}','${esc(item.id)}')">${item.kind==="core"?"启用":"安装"}</button>`}</td></tr>`).join("")||'<tr><td colspan="5">没有匹配项</td></tr>'}
68
+ async function loadEcosystem(){try{const[skills,plugins,mcps,enabled]=await Promise.all([json("/api/recommended-skills"),json("/api/recommended-plugins"),json("/api/recommended-mcps"),json("/api/enabled-capabilities")]);const skillSet=new Set((enabled.skills||[]).map(item=>item.id)),mcpSet=new Set((enabled.mcp||[]).map(item=>item.id));state.eco=[...(enabled.coreSkills||[]).map(item=>({...item,kind:"core",description:"PsyClaw 内置 Skill",sourceRef:"bundled",enabled:item.enabled,installed:true})),...(skills.items||[]).map(item=>({...item,kind:"skill",enabled:skillSet.has(item.id),installed:skillSet.has(item.id)})),...(plugins.items||[]).map(item=>({...item,kind:"plugin",enabled:Boolean(item.enabled),installed:Boolean(item.installed)})),...(enabled.localSkills||[]).map(item=>({...item,kind:"local",sourceRef:item.path||"用户目录",installed:true})),...(mcps.items||[]).map(item=>({...item,kind:"mcp",enabled:mcpSet.has(item.id),installed:mcpSet.has(item.id)})),...(skills.externalTools||[]).map(item=>({...item,kind:"external",enabled:false,installed:false}))];renderEcosystem()}catch(error){$("ecosystem-body").innerHTML=`<tr><td colspan="5">${esc(error.message)}</td></tr>`}}
69
+ function renderEcosystem(){const query=$("eco-search").value.trim().toLowerCase(),sourceKind=$("eco-kind").value,status=$("eco-status").value,primaryKind=state.ecoKind;const primaryMatches=item=>primaryKind==="all"||(primaryKind==="skill"?(item.kind==="skill"||item.kind==="core"||item.kind==="local"):item.kind===primaryKind);const items=state.eco.filter(item=>primaryMatches(item)&&(sourceKind==="all"||item.kind===sourceKind)&&(status==="all"||(status==="enabled"?item.enabled:!item.enabled))&&`${item.name} ${item.description||""}`.toLowerCase().includes(query));$("ecosystem-body").innerHTML=items.map(item=>`<tr><td><div class="name">${esc(item.name)}</div><div class="subtle">${esc(item.description||"")}${item.kind==="plugin"&&item.scope?` · ${item.scope==="project"?"当前项目":"所有项目"}${item.filtered?" · 已限定资源范围":""}`:""}</div></td><td>${esc(item.kind==="core"?"Core Skill":item.kind==="local"?"用户 Skill":item.kind.toUpperCase())}</td><td>${/^https?:/.test(item.sourceRef||"")?`<a href="${esc(item.sourceRef)}" target="_blank" rel="noreferrer">来源</a>`:esc(item.sourceRef||"bundled")}</td><td><span class="tag ${item.enabled?"ok":item.configured?"warn":""}">${item.enabled?(item.kind==="plugin"?"已安装":"已启用"):item.kind==="plugin"&&item.configured?"配置存在,文件缺失":item.kind==="external"?"可安装外部工具":"未启用"}</span></td><td>${item.kind==="plugin"&&item.local?"—":item.kind==="plugin"?`<button class="button small ${item.installed?"":"primary"}" onclick="installCapability('plugin','${esc(item.id)}')">${item.installed?"重新安装":"安装"}</button>`:item.kind==="external"?`<button class="button small primary" onclick="installCapability('external','${esc(item.id)}')">安装</button>`:item.enabled?`<button class="button small" onclick="toggleCapability('${item.kind}','${esc(item.id)}',false)">停用</button>`:item.kind==="local"?`<button class="button small primary" onclick="toggleCapability('local','${esc(item.id)}',true)">启用</button>`:`<button class="button small primary" onclick="installCapability('${item.kind}','${esc(item.id)}')">${item.kind==="core"?"启用":"安装"}</button>`}</td></tr>`).join("")||'<tr><td colspan="5">没有匹配项</td></tr>'}
70
70
  async function toggleCapability(kind,id,enabled){await json("/api/recommendation-state",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({kind:kind==="core"?"core-skill":kind==="skill"?"skills":kind==="local"?"local-skill":kind,id,enabled})});await loadEcosystem()}
71
- async function installCapability(kind,id){if(kind==="core"){await toggleCapability(kind,id,true);return}const scope=kind==="skill"?(confirm("安装到系统目录?\n确定:所有项目使用;取消:仅当前项目使用。")?"user":"project"):undefined;if(!confirm(`确认安装 ${id}?安装完成后需要 /reload。`))return;const result=await json("/api/install/execute",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({kind,id,scope,approved:true,actor:"researcher"})});alert(result.message||(result.ok?"安装任务已处理,请执行 /reload。":"安装未完成"));await loadEcosystem()}
71
+ async function installCapability(kind,id){if(kind==="core"){await toggleCapability(kind,id,true);return}const scope=kind==="skill"||kind==="plugin"?(confirm("安装到系统目录?\n确定:所有项目使用;取消:仅当前项目使用。")?"user":"project"):undefined;if(!confirm(`确认安装 ${id}?${kind==="external"?"模型将按官方来源安装并验证。":"安装完成后需要 /reload。"}`))return;const result=await json("/api/install/execute",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({kind,id,scope,approved:true,actor:"researcher"})});alert(result.message||(result.ok?"安装任务已处理。":"安装未完成"));await loadEcosystem()}
72
72
  [$("eco-search"),$("eco-kind"),$("eco-status")].forEach(control=>control.addEventListener("input",renderEcosystem));
73
+ document.querySelectorAll("[data-eco-kind]").forEach(button=>button.addEventListener("click",()=>{state.ecoKind=button.dataset.ecoKind;document.querySelectorAll("[data-eco-kind]").forEach(item=>item.classList.toggle("active",item===button));renderEcosystem()}));
73
74
 
74
75
  function providerModels(provider){return state.models.filter(model=>model.provider===provider)}
75
76
  function updateModels(){const models=providerModels($("provider-select").value);$("model-select").innerHTML=models.map(model=>`<option value="${esc(model.version||model.id.split("/").slice(1).join("/"))}">${esc(model.name)}</option>`).join("");if(state.active.provider===$("provider-select").value&&state.active.model)$("model-select").value=state.active.model}