psyclaw 0.28.2 → 0.28.3

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.28.2`。正式命令、用户配置目录和后续发布统一使用 `psyclaw`。
7
+ 当前版本:`0.28.3`。正式命令、用户配置目录和后续发布统一使用 `psyclaw`。
8
8
 
9
9
  ## 发布流程
10
10
 
@@ -21,19 +21,19 @@ 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.28.2
24
+ npm install -g psyclaw@0.28.3
25
25
  ```
26
26
 
27
27
  如果本机 npm 配置把 registry 误写成带有 `~/` 的地址,请显式指定官方源:
28
28
 
29
29
  ```bash
30
- npm install -g psyclaw@0.28.2 --registry=https://registry.npmjs.org/
30
+ npm install -g psyclaw@0.28.3 --registry=https://registry.npmjs.org/
31
31
  ```
32
32
 
33
33
  中国大陆网络较慢或无法访问官方源时:
34
34
 
35
35
  ```powershell
36
- npm install -g psyclaw@0.28.2 --registry=https://registry.npmmirror.com
36
+ npm install -g psyclaw@0.28.3 --registry=https://registry.npmmirror.com
37
37
  ```
38
38
 
39
39
  PsyClaw 的 npm 发布包直接携带 Windows x64/arm64 版 `ripgrep` 和 `fd`,Windows 启动时直接使用包内可执行文件,不再联网下载。其他平台缺少这两个工具时仍由锁定的 Pi 原生工具管理器处理:代理环境走 GitHub 官方 API 与 Release,国内 npm registry 环境统一走国内镜像。
@@ -92,7 +92,7 @@ psyclaw brief
92
92
  | 创建项目能力 | `/create-skill`、`/create-hook`、`/create-rule`、`/create-subagent`(均先预览并确认) |
93
93
  | 运行自定义只读角色 | `/agents --agent <id> <task>` 或 `/agents --agents <id,...> <task>`(最多四个隔离 worker) |
94
94
  | 调用已加载 Skill | `/skill:<name>` |
95
- | 使用 PsyClaw ARS | 输入 `ars` 后按 Tab(或 `ars: <task>`);也可用 `/ars doctor` / `/ars full <task>` |
95
+ | 使用 PsyClaw ARS | `Shift+Tab` 进入 academic mode(底栏显示);thinking level 改用 `Ctrl+Shift+Tab`;也可用 `/ars doctor` / `/ars full <task>` |
96
96
  | 管理 Plugin | `/plugin`;终端使用 `psyclaw plugin install|remove|list` |
97
97
  | 调用已配置 MCP | 模型通过 `psyclaw_mcp` 自动发现并调用 `.psyclaw/mcp/*.json` 中启用的服务器 |
98
98
 
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "schemaVersion": "psyclaw/recommended-agents/v1",
3
- "documentVersion": "0.28.2",
3
+ "documentVersion": "0.28.3",
4
4
  "syncedAt": "2026-09-06",
5
5
  "sourceDocument": "bundled literature/review subagents",
6
6
  "sourceRef": "bundled",
@@ -21,7 +21,7 @@ import { enabledLocalSkillPaths, enabledLocalPromptPaths, readUserSkillState, sc
21
21
  import { RuntimeMcpRegistry, setUserMcpConfigEnabled, } from "../../integrations/mcp-runtime.js";
22
22
  import { SecretInputComponent, ProviderPickerComponent, } from "../../tui/provider-picker.js";
23
23
  import { ARS_REPOSITORY_URL, ARS_UPSTREAM_COMMIT, ARS_UPSTREAM_REF, PSYCLAW_ARS_PROFILE_VERSION, detectNatureArsFillers, formatNatureArsFillerStatus, isArsPiActive, isArsPiTurn, psyclawArsPatch, } from "../../ars/profile.js";
24
- import { ArsModeEditor, enterArsModeEditorText, isArsModeEditorText } from "../../ars/mode-editor.js";
24
+ import { ArsModeEditor, ARS_MODE_STATUS, isArsModeEditorText } from "../../ars/mode-editor.js";
25
25
  const PARADIGMS = new Set([
26
26
  "survey-observational",
27
27
  "qualitative-thematic",
@@ -1118,6 +1118,36 @@ export default function psyclawExtension(pi) {
1118
1118
  const developerCommands = process.env.PSYCLAW_DEVELOPER_COMMANDS === "1";
1119
1119
  const legacyTestApi = typeof pi.registerTool !== "function";
1120
1120
  const runtimeMcps = new RuntimeMcpRegistry();
1121
+ let arsModeEditor;
1122
+ let arsUiContext;
1123
+ const applyArsUiMode = (active, opts) => {
1124
+ arsModeEditor?.setConversationMode(active);
1125
+ arsUiContext?.ui.setStatus("ars", active ? ARS_MODE_STATUS : undefined);
1126
+ if (opts?.syncSession === false || !arsUiContext)
1127
+ return;
1128
+ const sessionActive = isArsPiActive(arsUiContext.sessionManager);
1129
+ if (active && !sessionActive) {
1130
+ pi.sendUserMessage("/ars-pi-start", {
1131
+ ...(!arsUiContext.isIdle() ? { deliverAs: "followUp" } : {}),
1132
+ expandPromptTemplates: true,
1133
+ });
1134
+ }
1135
+ else if (!active && sessionActive) {
1136
+ pi.sendUserMessage("/ars-pi-stop", {
1137
+ ...(!arsUiContext.isIdle() ? { deliverAs: "followUp" } : {}),
1138
+ expandPromptTemplates: true,
1139
+ });
1140
+ }
1141
+ };
1142
+ if (!legacyTestApi && typeof pi.registerShortcut === "function") {
1143
+ pi.registerShortcut("shift+tab", {
1144
+ description: "Toggle academic mode",
1145
+ handler: () => {
1146
+ const next = !(arsModeEditor?.isConversationMode() ?? isArsPiActive(arsUiContext?.sessionManager));
1147
+ applyArsUiMode(next);
1148
+ },
1149
+ });
1150
+ }
1121
1151
  if (!legacyTestApi && typeof pi.on === "function")
1122
1152
  pi.on("resources_discover", async (event) => {
1123
1153
  const enabled = await enabledRecommendedSkillPaths(event.cwd);
@@ -1155,13 +1185,22 @@ export default function psyclawExtension(pi) {
1155
1185
  });
1156
1186
  if (!legacyTestApi && typeof pi.on === "function")
1157
1187
  pi.on("session_start", (_event, ctx) => {
1188
+ arsUiContext = ctx;
1158
1189
  if (!ctx.hasUI || typeof ctx.ui.setEditorComponent !== "function")
1159
1190
  return;
1160
1191
  ctx.ui.setEditorComponent((tui, theme, keybindings) => {
1161
1192
  const editor = new ArsModeEditor(tui, theme, keybindings);
1193
+ arsModeEditor = editor;
1162
1194
  editor.onArsModeChange = (active) => {
1163
- ctx.ui.setStatus("ars", active ? "ARS" : undefined);
1195
+ ctx.ui.setStatus("ars", active ? ARS_MODE_STATUS : undefined);
1196
+ };
1197
+ editor.onToggleConversationMode = () => {
1198
+ applyArsUiMode(!editor.isConversationMode());
1164
1199
  };
1200
+ const restore = isArsPiActive(ctx.sessionManager);
1201
+ editor.setConversationMode(restore);
1202
+ if (restore)
1203
+ ctx.ui.setStatus("ars", ARS_MODE_STATUS);
1165
1204
  return editor;
1166
1205
  });
1167
1206
  });
@@ -1174,21 +1213,6 @@ export default function psyclawExtension(pi) {
1174
1213
  ctx.ui.notify("PsyClaw 已统一隐藏底层登录命令。请使用 /provider 配置、切换或更新模型凭据。", "info");
1175
1214
  return { action: "handled" };
1176
1215
  });
1177
- if (!legacyTestApi && typeof pi.on === "function")
1178
- pi.on("input", async (event, ctx) => {
1179
- if (event.source === "extension" || isArsPiActive(ctx.sessionManager))
1180
- return;
1181
- const text = event.text.trim();
1182
- const entersArs = isArsModeEditorText(text)
1183
- || /^\/(?:ars-[a-z0-9-]+|skill:(?:deep-research|academic-paper|academic-paper-reviewer|academic-pipeline))(?:\s|$)/i.test(text);
1184
- if (!entersArs)
1185
- return;
1186
- if (!ctx.hasUI)
1187
- return { action: "handled" };
1188
- const approved = await ctx.ui.confirm("启用 ARS?", `来源:${ARS_REPOSITORY_URL} @ ${ARS_UPSTREAM_REF}\n许可:CC BY-NC 4.0,仅限非商业用途。关键研究阶段仍会请求确认。`);
1189
- if (!approved)
1190
- return { action: "handled" };
1191
- });
1192
1216
  if (!legacyTestApi && typeof pi.on === "function")
1193
1217
  pi.on("tool_call", async (event, ctx) => {
1194
1218
  const run = await readControlledRun(ctx.cwd);
@@ -1467,44 +1491,35 @@ export default function psyclawExtension(pi) {
1467
1491
  });
1468
1492
  if (!legacyTestApi)
1469
1493
  pi.registerCommand("ars", {
1470
- description: "进入 ARS 对话模式,或管理 doctor/start/full/stop",
1494
+ description: "切换 academic mode,或管理 doctor/start/full/stop",
1471
1495
  handler: async (args, ctx) => {
1472
1496
  try {
1473
1497
  const [action, ...rest] = args.trim().split(/\s+/).filter(Boolean);
1474
- // Bare /ars → conversation mode (ars: + tint). No profile popup.
1498
+ // Bare /ars → sticky academic mode (same as Shift+Tab on). No profile popup.
1475
1499
  if (!action) {
1476
1500
  if (!ctx.hasUI) {
1477
- ctx.ui.notify("当前环境无编辑器;请改用 /ars start 或发送 ars: <任务>。", "info");
1501
+ ctx.ui.notify("当前环境无编辑器;请改用 Shift+Tab 或 /ars start", "info");
1478
1502
  return;
1479
1503
  }
1480
- if (!isArsPiActive(ctx.sessionManager)) {
1481
- const approved = await ctx.ui.confirm("启用 ARS?", `来源:${ARS_REPOSITORY_URL} @ ${ARS_UPSTREAM_REF}\n许可:CC BY-NC 4.0,仅限非商业用途。关键研究阶段仍会请求确认。`);
1482
- if (!approved)
1483
- return;
1484
- }
1485
- ctx.ui.setEditorText(enterArsModeEditorText(ctx.ui.getEditorText?.() ?? ""));
1486
- ctx.ui.setStatus("ars", "ARS");
1504
+ arsUiContext = ctx;
1505
+ applyArsUiMode(true);
1487
1506
  return;
1488
1507
  }
1489
1508
  if (action === "status") {
1490
1509
  ctx.ui.notify([
1491
1510
  `PsyClaw ARS profile v${PSYCLAW_ARS_PROFILE_VERSION}`,
1492
- "模式:轻量 ARS(输入 ars 后按 Tab,或以 ars: 前缀对话)",
1511
+ "模式:academic mode(Shift+Tab 切换;开启后直接对话)",
1512
+ "Thinking:Ctrl+Shift+Tab(Shift+Tab 已让给 academic mode)",
1493
1513
  `来源:${ARS_REPOSITORY_URL} @ ${ARS_UPSTREAM_REF} (${ARS_UPSTREAM_COMMIT.slice(0, 12)})`,
1494
- "入口:ars: <task>,/ars doctor,/ars start,/ars full <task>,/ars stop",
1514
+ "入口:Shift+Tab,/ars doctor,/ars start,/ars full <task>,/ars stop",
1495
1515
  formatNatureArsFillerStatus(natureFillersFromCommandContext(ctx)),
1496
1516
  ].join("\n"), "info");
1497
1517
  return;
1498
1518
  }
1499
1519
  if (action === "install") {
1500
- ctx.ui.notify("ARS 与 Nature/compose 技能已内置:输入 ars 后按 Tab,或以 /ars start、/ars full <task> 使用。", "info");
1520
+ ctx.ui.notify("ARS 与 Nature/compose 技能已内置:按 Shift+Tab 进入 academic mode。", "info");
1501
1521
  return;
1502
1522
  }
1503
- if ((action === "start" || action === "full") && !isArsPiActive(ctx.sessionManager)) {
1504
- const approved = await ctx.ui.confirm("启用 ARS?", `来源:${ARS_REPOSITORY_URL} @ ${ARS_UPSTREAM_REF}\n许可:CC BY-NC 4.0,仅限非商业用途。关键研究阶段仍会请求确认。`);
1505
- if (!approved)
1506
- return;
1507
- }
1508
1523
  const upstreamCommand = action === "doctor"
1509
1524
  ? "/ars-pi-doctor"
1510
1525
  : action === "start"
@@ -1515,11 +1530,16 @@ export default function psyclawExtension(pi) {
1515
1530
  ? `/ars-full${rest.length > 0 ? ` ${rest.join(" ")}` : ""}`
1516
1531
  : undefined;
1517
1532
  if (!upstreamCommand) {
1518
- ctx.ui.notify("Usage: /ars [status|doctor|start|full <task>|stop] 或直接输入 ars 后按 Tab", "info");
1533
+ ctx.ui.notify("Usage: /ars [status|doctor|start|full <task>|stop] 或按 Shift+Tab 切换 academic mode", "info");
1519
1534
  return;
1520
1535
  }
1536
+ if (action === "start") {
1537
+ arsUiContext = ctx;
1538
+ applyArsUiMode(true, { syncSession: false });
1539
+ }
1521
1540
  if (action === "stop") {
1522
- ctx.ui.setStatus("ars", undefined);
1541
+ arsUiContext = ctx;
1542
+ applyArsUiMode(false, { syncSession: false });
1523
1543
  if (ctx.hasUI && isArsModeEditorText(ctx.ui.getEditorText?.() ?? "")) {
1524
1544
  ctx.ui.setEditorText("");
1525
1545
  }