psyclaw 0.29.20 → 0.29.21

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/README.md CHANGED
@@ -4,7 +4,7 @@ PsyClaw 是面向社会科学研究的智能体工作台。它把研究项目、
4
4
 
5
5
  PsyClaw 自有代码使用 MIT 许可证;随 npm 包内置的 Academic Research Skills 位于 `vendor/ars`,保持上游署名并单独遵循 CC BY-NC 4.0,仅限非商业用途。
6
6
 
7
- 当前版本:`0.29.20`。正式命令、用户配置目录和后续发布统一使用 `psyclaw`。
7
+ 当前版本:`0.29.21`。正式命令、用户配置目录和后续发布统一使用 `psyclaw`。
8
8
 
9
9
  ## 发布流程
10
10
 
@@ -21,27 +21,34 @@ RELEASE_MESSAGE="release: describe the change" pnpm release:push
21
21
  需要 Node.js `>=22.19.0`。官方 npm 源:
22
22
 
23
23
  ```powershell
24
- npm install -g psyclaw@0.29.20
24
+ npm install -g psyclaw@0.29.21
25
25
  ```
26
26
 
27
27
  如果本机 npm 配置把 registry 误写成带有 `~/` 的地址,请显式指定官方源:
28
28
 
29
29
  ```bash
30
- npm install -g psyclaw@0.29.20 --registry=https://registry.npmjs.org/
30
+ npm install -g psyclaw@0.29.21 --registry=https://registry.npmjs.org/
31
31
  ```
32
32
 
33
33
  中国大陆网络较慢或无法访问官方源时:
34
34
 
35
35
  ```powershell
36
- npm install -g psyclaw@0.29.20 --registry=https://registry.npmmirror.com
36
+ npm install -g psyclaw@0.29.21 --registry=https://registry.npmmirror.com
37
37
  ```
38
38
 
39
39
  或安装脚本:
40
40
 
41
41
  ```bash
42
+ # macOS / Linux
42
43
  PSYCLAW_CN=1 curl -fsSL https://exekiel179.github.io/psyclaw/install.sh | sh
43
44
  ```
44
45
 
46
+ ```powershell
47
+ # Windows PowerShell
48
+ irm https://exekiel179.github.io/psyclaw/install.ps1 | iex
49
+ # 可选:$env:PSYCLAW_CN = "1";$env:PSYCLAW_VERSION = "0.29.21"
50
+ ```
51
+
45
52
  检测到国内 npm registry(如 npmmirror)、`PSYCLAW_CN=1` 或 `PSYCLAW_GITHUB_MIRROR` 时,内置路径统一走国内可用路由:
46
53
 
47
54
  - 首启 `fd` / `ripgrep`(macOS/Linux):GitHub API 与 Release 经 `gh-proxy` 等镜像;Windows 仍用包内二进制,无需下载
@@ -166,7 +173,16 @@ npm uninstall -g psyclaw
166
173
 
167
174
  ```bash
168
175
  npm uninstall -g psyclaw
169
- node -e 'const fs=require("node:fs"),os=require("node:os"),path=require("node:path");const target=path.join(os.homedir(),`.psy${"pi"}`);if(path.dirname(target)!==os.homedir())throw new Error("unsafe PsyClaw data path");fs.rmSync(target,{recursive:true,force:true});console.log(`removed ${target}`)'
176
+ ```
177
+
178
+ ```powershell
179
+ # 删除用户配置目录 ~/.psyclaw(不会删除研究项目)
180
+ Remove-Item -Recurse -Force "$HOME\.psyclaw" -ErrorAction SilentlyContinue
181
+ ```
182
+
183
+ ```bash
184
+ # macOS / Linux
185
+ rm -rf ~/.psyclaw
170
186
  ```
171
187
 
172
188
  上述命令不会删除研究项目目录,因为其中可能包含论文、证据、原始数据和分析产物。如需删除某个项目,应单独确认该项目的准确路径后处理。单独全局安装的 `@earendil-works/pi-coding-agent` 不属于 PsyClaw 用户数据,上述命令不会删除它。
@@ -559,7 +559,8 @@
559
559
  border-radius: 14px;
560
560
  border: 1px solid var(--border-subtle);
561
561
  background: var(--bg-surface);
562
- max-width: 78ch;
562
+ max-width: 100%;
563
+ box-sizing: border-box;
563
564
  line-height: 1.65;
564
565
  color: var(--text-main);
565
566
  font-size: 13.5px;
@@ -1985,28 +1986,6 @@
1985
1986
  <main class="main">
1986
1987
  <!-- 视图 1:项目工作台 (默认主界面) -->
1987
1988
  <section id="view-project" class="view active">
1988
- <!-- 科研全流程阶段导航条 Pipeline Stepper -->
1989
- <div class="pipeline-stepper" aria-label="科研流程阶段">
1990
- <div class="step-item active" data-stage="intake" title="研究问题与范式目标 (notes/goal.md)">
1991
- <span>① 课题界定</span>
1992
- </div>
1993
- <span class="step-arrow">➔</span>
1994
- <div class="step-item" data-stage="evidence" title="证据溯源与文献账本 (notes/evidence.jsonl, claims.jsonl)">
1995
- <span>② 证据账本</span>
1996
- </div>
1997
- <span class="step-arrow">➔</span>
1998
- <div class="step-item" data-stage="plan" title="统计分析方案与 EDA (analysis/plans/)">
1999
- <span>③ 统计方案</span>
2000
- </div>
2001
- <span class="step-arrow">➔</span>
2002
- <div class="step-item" data-stage="manuscript" title="论文撰写与图表编排 (paper/, outputs/)">
2003
- <span>④ 手稿编排</span>
2004
- </div>
2005
- <span class="step-arrow">➔</span>
2006
- <div class="step-item" data-stage="review" title="多角色同行盲审与交叉核验 (notes/review.md)">
2007
- <span>⑤ 严苛审稿</span>
2008
- </div>
2009
- </div>
2010
1989
 
2011
1990
  <div class="page-head">
2012
1991
  <div>
@@ -2058,28 +2037,6 @@
2058
2037
  </div>
2059
2038
  </div>
2060
2039
 
2061
- <div class="console-quick-chips">
2062
- <span class="chip" data-cmd="/plan status">
2063
- <svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="18" height="18" rx="2"/><path d="M3 9h18M9 21V9"/></svg>
2064
- /plan status
2065
- </span>
2066
- <span class="chip" data-cmd="/crosscheck list">
2067
- <svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M9 11l3 3L22 4"/></svg>
2068
- /crosscheck
2069
- </span>
2070
- <span class="chip" data-cmd="/grill">
2071
- <svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3"/></svg>
2072
- /grill 压力追问
2073
- </span>
2074
- <span class="chip" data-cmd="/review">
2075
- <svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/></svg>
2076
- /review
2077
- </span>
2078
- <span class="chip" data-cmd="/agents">
2079
- <svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/></svg>
2080
- /agents
2081
- </span>
2082
- </div>
2083
2040
 
2084
2041
  <div class="console-input-wrap">
2085
2042
  <div class="console-input-container">
@@ -2912,6 +2869,11 @@ async function loadProvider() {
2912
2869
  `).join("");
2913
2870
  if (active.provider && providers.includes(active.provider)) {
2914
2871
  $("provider-select").value = active.provider;
2872
+ } else {
2873
+ const firstConfigured = state.models.find(m => m.configured);
2874
+ if (firstConfigured && providers.includes(firstConfigured.provider)) {
2875
+ $("provider-select").value = firstConfigured.provider;
2876
+ }
2915
2877
  }
2916
2878
  updateModels();
2917
2879
  renderCost();
@@ -2925,16 +2887,18 @@ function updateProviderStatusBadge(active, models) {
2925
2887
  const badge = $("provider-status-badge");
2926
2888
  const label = $("provider-status-label");
2927
2889
  if (!badge || !label) return;
2928
- const provider = active?.provider;
2929
- const model = active?.model;
2930
- const providerConfigured = Boolean(provider) && (models || []).some(entry =>
2931
- entry.provider === provider && entry.configured
2890
+ const configuredModels = (models || []).filter(entry => entry.configured);
2891
+ const provider = active?.provider || configuredModels[0]?.provider;
2892
+ const model = active?.model || configuredModels[0]?.version || configuredModels[0]?.name;
2893
+ const providerConfigured = Boolean(provider) && (
2894
+ configuredModels.some(entry => entry.provider === provider) ||
2895
+ configuredModels.length > 0
2932
2896
  );
2933
- const ready = Boolean(provider && model && providerConfigured);
2897
+ const ready = Boolean(provider && providerConfigured);
2934
2898
  badge.classList.toggle("warn", !ready);
2935
2899
  label.textContent = ready ? "模型已配置" : "未配置模型";
2936
2900
  badge.title = ready
2937
- ? `当前默认:${provider}/${model}(点击打开模型与成本)`
2901
+ ? `当前默认:${provider}/${model || "默认"}(点击打开模型与成本)`
2938
2902
  : "尚未配置可用模型或凭据(点击打开模型与成本)";
2939
2903
  }
2940
2904
 
@@ -3002,8 +2966,16 @@ $("configure-provider").addEventListener("click", async () => {
3002
2966
  ...(key ? { apiKey: key } : {})
3003
2967
  })
3004
2968
  });
2969
+ if (!state.active?.provider) {
2970
+ await json("/api/active-provider", {
2971
+ method: "POST",
2972
+ headers: { "content-type": "application/json" },
2973
+ body: JSON.stringify({ provider: model.provider, model: $("model-select").value })
2974
+ }).catch(() => {});
2975
+ }
3005
2976
  $("provider-key").value = "";
3006
2977
  feedback.textContent = "✓ 凭据配置已安全保存,重启会话后生效。";
2978
+ await refreshProviderStatus();
3007
2979
  setTimeout(() => { feedback.textContent = ""; }, 3000);
3008
2980
  } catch (error) {
3009
2981
  feedback.textContent = `保存失败: ${error.message}`;
@@ -181,6 +181,21 @@ function academicGrillRequest(subject, mode) {
181
181
  : "持久化模式:review。追问完成后先展示拟更新摘要和受影响文件,并询问我是否写回;得到明确确认前不得修改项目文档。",
182
182
  ].join("\n");
183
183
  }
184
+ /** Lightweight ideation path — never routes through academic-grill / /grill. */
185
+ function academicBrainstormRequest(subject) {
186
+ return [
187
+ "这是 /brainstorm,不是 /grill:不要加载 academic-grill Skill,不要进入逐题压力测试或研究规格访谈。",
188
+ subject
189
+ ? `头脑风暴主题:${subject}`
190
+ : "根据当前对话与项目材料确定头脑风暴主题;已有信息不要重复询问。",
191
+ "请完成一次选题向的头脑风暴:",
192
+ "1. 提出 2-4 个有理论意义、可由现有或可获取材料回答的研究方向 / 研究问题 / 假设。",
193
+ "2. 简要比较各自的价值、可行性、数据需求与推断边界。",
194
+ "3. 明确推荐其中一个,并说明推荐理由与主要取舍。",
195
+ "4. 最多再问 1-2 个真正影响选题的澄清问题(可一次给出);不要展开完整的设计/测量/估计量审讯。",
196
+ "不要执行分析、不要写论文、不要伪造文献或结果。若用户接下来需要严格压力测试,请提示他们另用 /grill。",
197
+ ].join("\n");
198
+ }
184
199
  async function readActiveProject(root) {
185
200
  try {
186
201
  const project = await readProject(root);
@@ -1305,15 +1320,11 @@ export default function psyclawExtension(pi) {
1305
1320
  });
1306
1321
  if (!legacyTestApi)
1307
1322
  pi.registerCommand("brainstorm", {
1308
- description: "显式启动研究方向头脑风暴(逐题澄清)",
1323
+ description: "研究方向头脑风暴:提出并比较可选问题,不做压力测试",
1309
1324
  handler: async (args, ctx) => {
1310
1325
  const subject = args.trim();
1311
- pi.sendUserMessage([
1312
- "使用 academic-grill Skill 的头脑风暴入口,先提出 2-4 个有理论意义、可回答的研究方向,比较价值、可行性与边界并推荐一个。",
1313
- "随后按研究者需要逐题澄清,每轮只问一个实质问题,同时给出推荐答案和主要取舍;不要直接替用户执行完整研究或写论文。",
1314
- subject ? `本次头脑风暴主题:${subject}` : "请根据当前对话和项目材料确定头脑风暴主题;已有信息不要重复询问。",
1315
- ].join("\n"), ctx.isIdle() ? {} : { deliverAs: "followUp" });
1316
- ctx.ui.notify("已启动研究方向头脑风暴。完成后可用 /grill 继续严格压力测试。", "info");
1326
+ pi.sendUserMessage(academicBrainstormRequest(subject), ctx.isIdle() ? {} : { deliverAs: "followUp" });
1327
+ ctx.ui.notify("已启动研究方向头脑风暴(不经过 /grill)。若要严格压力测试,请另开 /grill。", "info");
1317
1328
  },
1318
1329
  });
1319
1330
  if (!legacyTestApi)