psyclaw 0.29.3 → 0.29.5
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 +20 -5
- package/dist/apps/panel/index.html +262 -2
- package/dist/src/adapters/pi/extension.js +151 -26
- package/dist/src/adapters/pi/extension.js.map +1 -1
- package/dist/src/analysis/plan.d.ts +23 -0
- package/dist/src/analysis/plan.js +39 -2
- package/dist/src/analysis/plan.js.map +1 -1
- package/dist/src/analysis/stats-router.js +3 -2
- package/dist/src/analysis/stats-router.js.map +1 -1
- package/dist/src/ars/keybindings.d.ts +10 -5
- package/dist/src/ars/keybindings.js +20 -10
- package/dist/src/ars/keybindings.js.map +1 -1
- package/dist/src/branding.js +3 -1
- package/dist/src/branding.js.map +1 -1
- package/dist/src/cli.js +12 -2
- package/dist/src/cli.js.map +1 -1
- package/dist/src/index.d.ts +1 -0
- package/dist/src/index.js +1 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/network-routing.d.ts +10 -0
- package/dist/src/network-routing.js +46 -4
- package/dist/src/network-routing.js.map +1 -1
- package/dist/src/panel/extension.d.ts +2 -5
- package/dist/src/panel/extension.js +15 -91
- package/dist/src/panel/extension.js.map +1 -1
- package/dist/src/panel/server.js +37 -1
- package/dist/src/panel/server.js.map +1 -1
- package/dist/src/panel/workbench.d.ts +16 -0
- package/dist/src/panel/workbench.js +78 -0
- package/dist/src/panel/workbench.js.map +1 -0
- package/dist/src/session/help.d.ts +24 -0
- package/dist/src/session/help.js +64 -0
- package/dist/src/session/help.js.map +1 -0
- package/dist/src/session/modes.js +3 -2
- package/dist/src/session/modes.js.map +1 -1
- package/dist/src/skills/recommended.js +3 -1
- package/dist/src/skills/recommended.js.map +1 -1
- package/dist/src/style/cli-ui.js +7 -6
- package/dist/src/style/cli-ui.js.map +1 -1
- package/dist/src/updates/registry.d.ts +12 -1
- package/dist/src/updates/registry.js +12 -4
- package/dist/src/updates/registry.js.map +1 -1
- package/dist/src/updates/update.d.ts +2 -0
- package/dist/src/updates/update.js +11 -8
- package/dist/src/updates/update.js.map +1 -1
- package/dist/src/verify/checklist.d.ts +9 -2
- package/dist/src/verify/checklist.js +70 -15
- package/dist/src/verify/checklist.js.map +1 -1
- package/package.json +4 -1
- package/skills/core/analysis-plan/SKILL.md +21 -11
- package/skills/recommended/catalog.json +84 -2
- package/skills/scholars/README.md +20 -0
- package/skills/scholars/freud-perspective/SKILL.md +70 -0
- package/skills/scholars/gelman-perspective/SKILL.md +66 -0
- package/skills/scholars/kahneman-perspective/SKILL.md +71 -0
- package/vendor/nuwa-skill/LICENSE +21 -0
- package/vendor/nuwa-skill/NOTICE.md +9 -0
- package/vendor/nuwa-skill/PROVENANCE.json +1 -0
- package/vendor/nuwa-skill/README.md +378 -0
- package/vendor/nuwa-skill/README_EN.md +290 -0
- package/vendor/nuwa-skill/SKILL.md +709 -0
- package/vendor/nuwa-skill/references/extraction-framework.md +151 -0
- package/vendor/nuwa-skill/references/fidelity-scorecard.md +63 -0
- package/vendor/nuwa-skill/references/skill-template.md +117 -0
- package/vendor/nuwa-skill/scripts/merge_research.py +150 -0
- package/vendor/nuwa-skill/scripts/quality_check.py +152 -0
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.
|
|
7
|
+
当前版本:`0.29.5`。正式命令、用户配置目录和后续发布统一使用 `psyclaw`。
|
|
8
8
|
|
|
9
9
|
## 发布流程
|
|
10
10
|
|
|
@@ -21,22 +21,37 @@ 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.
|
|
24
|
+
npm install -g psyclaw@0.29.5
|
|
25
25
|
```
|
|
26
26
|
|
|
27
27
|
如果本机 npm 配置把 registry 误写成带有 `~/` 的地址,请显式指定官方源:
|
|
28
28
|
|
|
29
29
|
```bash
|
|
30
|
-
npm install -g psyclaw@0.29.
|
|
30
|
+
npm install -g psyclaw@0.29.5 --registry=https://registry.npmjs.org/
|
|
31
31
|
```
|
|
32
32
|
|
|
33
33
|
中国大陆网络较慢或无法访问官方源时:
|
|
34
34
|
|
|
35
35
|
```powershell
|
|
36
|
-
npm install -g psyclaw@0.29.
|
|
36
|
+
npm install -g psyclaw@0.29.5 --registry=https://registry.npmmirror.com
|
|
37
37
|
```
|
|
38
38
|
|
|
39
|
-
|
|
39
|
+
或安装脚本:
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
PSYCLAW_CN=1 curl -fsSL https://exekiel179.github.io/psyclaw/install.sh | sh
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
检测到国内 npm registry(如 npmmirror)、`PSYCLAW_CN=1` 或 `PSYCLAW_GITHUB_MIRROR` 时,内置路径统一走国内可用路由:
|
|
46
|
+
|
|
47
|
+
- 首启 `fd` / `ripgrep`(macOS/Linux):GitHub API 与 Release 经 `gh-proxy` 等镜像;Windows 仍用包内二进制,无需下载
|
|
48
|
+
- `psyclaw update` / `check-updates`:读版本与安装命令使用 `PSYCLAW_REGISTRY` 或 npmmirror,不再写死官方 npm
|
|
49
|
+
- 推荐 Skill 的托管 `git clone`:经 GitHub 镜像前缀
|
|
50
|
+
- `fetch` 覆盖 `api.github.com`、`github.com`、`raw.githubusercontent.com`、`codeload.github.com`
|
|
51
|
+
|
|
52
|
+
建议把 `registry=https://registry.npmmirror.com` 写入 `~/.npmrc`,以免下次启动检测不到国内源。已有 HTTP(S) 代理时优先走代理、保持官方 GitHub URL。
|
|
53
|
+
|
|
54
|
+
PsyClaw 的 npm 发布包直接携带 Windows x64/arm64 版 `ripgrep` 和 `fd`,Windows 启动时直接使用包内可执行文件,不再联网下载。
|
|
40
55
|
|
|
41
56
|
确认命令入口:
|
|
42
57
|
|
|
@@ -266,6 +266,85 @@
|
|
|
266
266
|
font-size: 12.5px;
|
|
267
267
|
}
|
|
268
268
|
|
|
269
|
+
.help-grid {
|
|
270
|
+
display: grid;
|
|
271
|
+
grid-template-columns: repeat(3, minmax(0, 1fr));
|
|
272
|
+
gap: 12px;
|
|
273
|
+
margin-bottom: 22px;
|
|
274
|
+
}
|
|
275
|
+
.help-mode-card {
|
|
276
|
+
border: 1px solid var(--border-subtle);
|
|
277
|
+
background: var(--bg-surface);
|
|
278
|
+
border-radius: var(--radius-md);
|
|
279
|
+
padding: 14px 16px;
|
|
280
|
+
box-shadow: var(--shadow-xs);
|
|
281
|
+
}
|
|
282
|
+
.help-mode-card .mode-id {
|
|
283
|
+
font-family: var(--font-mono);
|
|
284
|
+
font-size: 11px;
|
|
285
|
+
color: var(--accent);
|
|
286
|
+
letter-spacing: 0.04em;
|
|
287
|
+
text-transform: uppercase;
|
|
288
|
+
margin-bottom: 6px;
|
|
289
|
+
}
|
|
290
|
+
.help-mode-card p {
|
|
291
|
+
margin: 0;
|
|
292
|
+
font-size: 13px;
|
|
293
|
+
line-height: 1.55;
|
|
294
|
+
color: var(--text-secondary);
|
|
295
|
+
}
|
|
296
|
+
.help-block {
|
|
297
|
+
margin-bottom: 22px;
|
|
298
|
+
}
|
|
299
|
+
.help-block h2 {
|
|
300
|
+
margin: 0 0 10px;
|
|
301
|
+
font-size: 14px;
|
|
302
|
+
font-weight: 650;
|
|
303
|
+
color: var(--text-main);
|
|
304
|
+
}
|
|
305
|
+
.help-steps {
|
|
306
|
+
margin: 0;
|
|
307
|
+
padding-left: 1.2rem;
|
|
308
|
+
color: var(--text-secondary);
|
|
309
|
+
font-size: 13px;
|
|
310
|
+
line-height: 1.7;
|
|
311
|
+
}
|
|
312
|
+
.help-commands {
|
|
313
|
+
display: grid;
|
|
314
|
+
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
315
|
+
gap: 8px;
|
|
316
|
+
}
|
|
317
|
+
.help-cmd {
|
|
318
|
+
display: flex;
|
|
319
|
+
gap: 10px;
|
|
320
|
+
align-items: baseline;
|
|
321
|
+
border: 1px solid var(--border-subtle);
|
|
322
|
+
border-radius: var(--radius-sm);
|
|
323
|
+
padding: 10px 12px;
|
|
324
|
+
background: var(--bg-surface-subtle);
|
|
325
|
+
}
|
|
326
|
+
.help-cmd code {
|
|
327
|
+
font-family: var(--font-mono);
|
|
328
|
+
font-size: 12px;
|
|
329
|
+
color: var(--text-main);
|
|
330
|
+
white-space: nowrap;
|
|
331
|
+
}
|
|
332
|
+
.help-cmd span {
|
|
333
|
+
font-size: 12.5px;
|
|
334
|
+
color: var(--text-muted);
|
|
335
|
+
line-height: 1.45;
|
|
336
|
+
}
|
|
337
|
+
.help-notes {
|
|
338
|
+
margin: 0;
|
|
339
|
+
padding-left: 1.2rem;
|
|
340
|
+
color: var(--text-muted);
|
|
341
|
+
font-size: 12.5px;
|
|
342
|
+
line-height: 1.65;
|
|
343
|
+
}
|
|
344
|
+
@media (max-width: 960px) {
|
|
345
|
+
.help-grid, .help-commands { grid-template-columns: 1fr; }
|
|
346
|
+
}
|
|
347
|
+
|
|
269
348
|
.button {
|
|
270
349
|
display: inline-flex;
|
|
271
350
|
align-items: center;
|
|
@@ -1174,10 +1253,22 @@
|
|
|
1174
1253
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="2" y="3" width="20" height="14" rx="2" ry="2"/><line x1="8" y1="21" x2="16" y2="21"/><line x1="12" y1="17" x2="12" y2="21"/></svg>
|
|
1175
1254
|
模型与成本
|
|
1176
1255
|
</button>
|
|
1256
|
+
<button data-view="help">
|
|
1257
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3"/><line x1="12" y1="17" x2="12.01" y2="17"/></svg>
|
|
1258
|
+
使用速览
|
|
1259
|
+
</button>
|
|
1260
|
+
<button data-view="checklist">
|
|
1261
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M9 11l3 3L22 4"/><path d="M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11"/></svg>
|
|
1262
|
+
核对清单
|
|
1263
|
+
</button>
|
|
1264
|
+
<button data-view="chat">
|
|
1265
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/></svg>
|
|
1266
|
+
对话
|
|
1267
|
+
</button>
|
|
1177
1268
|
</div>
|
|
1178
1269
|
<div class="nav-footer">
|
|
1179
1270
|
<div style="font-weight:600;margin-bottom:2px;color:var(--text-secondary)">PsyClaw 运行提示</div>
|
|
1180
|
-
|
|
1271
|
+
终端 <code>Shift+Tab</code> 切换模式;Thinking 用 <code>Ctrl+Shift+T</code>。
|
|
1181
1272
|
</div>
|
|
1182
1273
|
</nav>
|
|
1183
1274
|
|
|
@@ -1373,6 +1464,61 @@
|
|
|
1373
1464
|
</div>
|
|
1374
1465
|
</div>
|
|
1375
1466
|
</section>
|
|
1467
|
+
|
|
1468
|
+
<section id="view-help" class="view">
|
|
1469
|
+
<div class="page-head">
|
|
1470
|
+
<div>
|
|
1471
|
+
<h1 id="help-title">使用速览</h1>
|
|
1472
|
+
<p id="help-thinking">加载中…</p>
|
|
1473
|
+
</div>
|
|
1474
|
+
</div>
|
|
1475
|
+
<div class="help-grid" id="help-modes"></div>
|
|
1476
|
+
<div class="help-block">
|
|
1477
|
+
<h2>建议流程</h2>
|
|
1478
|
+
<ol id="help-steps" class="help-steps"></ol>
|
|
1479
|
+
</div>
|
|
1480
|
+
<div class="help-block">
|
|
1481
|
+
<h2>常用命令</h2>
|
|
1482
|
+
<div id="help-commands" class="help-commands"></div>
|
|
1483
|
+
</div>
|
|
1484
|
+
<div class="help-block">
|
|
1485
|
+
<h2>说明</h2>
|
|
1486
|
+
<ul id="help-notes" class="help-notes"></ul>
|
|
1487
|
+
</div>
|
|
1488
|
+
</section>
|
|
1489
|
+
|
|
1490
|
+
<section id="view-checklist" class="view">
|
|
1491
|
+
<div class="page-head">
|
|
1492
|
+
<div>
|
|
1493
|
+
<h1>交叉核验清单</h1>
|
|
1494
|
+
<p>分析前/后由模型提出项,人在此勾选。可跳过,但会标注「未经核对」。本地写入 <code>.psyclaw/verify-checklist.json</code>。</p>
|
|
1495
|
+
</div>
|
|
1496
|
+
<div style="display:flex;gap:8px">
|
|
1497
|
+
<button id="refresh-checklist" class="button">刷新</button>
|
|
1498
|
+
<button id="skip-checklist" class="button" title="其余未核项标为跳过/未经核对">跳过未核项</button>
|
|
1499
|
+
</div>
|
|
1500
|
+
</div>
|
|
1501
|
+
<div class="panel section">
|
|
1502
|
+
<table>
|
|
1503
|
+
<thead>
|
|
1504
|
+
<tr><th>状态</th><th>ID</th><th>类别</th><th>阶段</th><th>说明</th><th>操作</th></tr>
|
|
1505
|
+
</thead>
|
|
1506
|
+
<tbody id="checklist-body">
|
|
1507
|
+
<tr><td colspan="6" class="empty">正在读取核对清单...</td></tr>
|
|
1508
|
+
</tbody>
|
|
1509
|
+
</table>
|
|
1510
|
+
</div>
|
|
1511
|
+
</section>
|
|
1512
|
+
|
|
1513
|
+
<section id="view-chat" class="view">
|
|
1514
|
+
<div class="page-head">
|
|
1515
|
+
<div>
|
|
1516
|
+
<h1>交互对话</h1>
|
|
1517
|
+
<p>整页对话视图(不再只依赖右下角抽屉)。打开本页时会把控制台嵌到此处。</p>
|
|
1518
|
+
</div>
|
|
1519
|
+
</div>
|
|
1520
|
+
<div id="chat-console-host" class="panel section" style="min-height:62vh;padding:0;overflow:hidden;display:flex;flex-direction:column"></div>
|
|
1521
|
+
</section>
|
|
1376
1522
|
</main>
|
|
1377
1523
|
</div>
|
|
1378
1524
|
</div>
|
|
@@ -1492,6 +1638,9 @@ async function json(url, options) {
|
|
|
1492
1638
|
|
|
1493
1639
|
function setView(view) {
|
|
1494
1640
|
state.view = view;
|
|
1641
|
+
if (location.hash.replace(/^#/, "") !== view) {
|
|
1642
|
+
history.replaceState(null, "", `#${view}`);
|
|
1643
|
+
}
|
|
1495
1644
|
document.querySelectorAll(".nav button").forEach(button => {
|
|
1496
1645
|
button.classList.toggle("active", button.dataset.view === view);
|
|
1497
1646
|
});
|
|
@@ -1500,8 +1649,109 @@ function setView(view) {
|
|
|
1500
1649
|
});
|
|
1501
1650
|
if (view === "ecosystem") loadEcosystem();
|
|
1502
1651
|
if (view === "provider") loadProvider();
|
|
1652
|
+
if (view === "checklist") loadChecklist();
|
|
1653
|
+
if (view === "help") loadHelp();
|
|
1654
|
+
if (view === "chat") mountConsoleInChat();
|
|
1655
|
+
else restoreConsoleDrawer();
|
|
1656
|
+
}
|
|
1657
|
+
|
|
1658
|
+
async function loadHelp() {
|
|
1659
|
+
try {
|
|
1660
|
+
const doc = await json("/api/help");
|
|
1661
|
+
$("help-title").textContent = doc.title || "使用速览";
|
|
1662
|
+
$("help-thinking").textContent = doc.thinking || "";
|
|
1663
|
+
$("help-modes").innerHTML = (doc.modes || []).map(mode => `
|
|
1664
|
+
<article class="help-mode-card">
|
|
1665
|
+
<div class="mode-id">${esc(mode.label || mode.id)}</div>
|
|
1666
|
+
<p>${esc(mode.blurb)}</p>
|
|
1667
|
+
</article>
|
|
1668
|
+
`).join("");
|
|
1669
|
+
$("help-steps").innerHTML = (doc.steps || []).map(step => `<li>${esc(step)}</li>`).join("");
|
|
1670
|
+
$("help-commands").innerHTML = (doc.commands || []).map(row => `
|
|
1671
|
+
<div class="help-cmd"><code>${esc(row.cmd)}</code><span>${esc(row.blurb)}</span></div>
|
|
1672
|
+
`).join("");
|
|
1673
|
+
$("help-notes").innerHTML = (doc.notes || []).map(note => `<li>${esc(note)}</li>`).join("");
|
|
1674
|
+
} catch (error) {
|
|
1675
|
+
$("help-thinking").textContent = `加载失败: ${error.message}`;
|
|
1676
|
+
}
|
|
1677
|
+
}
|
|
1678
|
+
|
|
1679
|
+
function mountConsoleInChat() {
|
|
1680
|
+
const host = $("chat-console-host");
|
|
1681
|
+
const drawer = $("console-drawer");
|
|
1682
|
+
if (!host || !drawer) return;
|
|
1683
|
+
host.appendChild(drawer);
|
|
1684
|
+
drawer.classList.add("open");
|
|
1685
|
+
drawer.style.position = "relative";
|
|
1686
|
+
drawer.style.inset = "auto";
|
|
1687
|
+
drawer.style.width = "100%";
|
|
1688
|
+
drawer.style.height = "100%";
|
|
1689
|
+
drawer.style.minHeight = "58vh";
|
|
1690
|
+
drawer.style.boxShadow = "none";
|
|
1691
|
+
drawer.style.borderRadius = "0";
|
|
1503
1692
|
}
|
|
1504
1693
|
|
|
1694
|
+
function restoreConsoleDrawer() {
|
|
1695
|
+
const drawer = $("console-drawer");
|
|
1696
|
+
if (!drawer || drawer.parentElement === document.body) return;
|
|
1697
|
+
document.body.appendChild(drawer);
|
|
1698
|
+
drawer.style.position = "";
|
|
1699
|
+
drawer.style.inset = "";
|
|
1700
|
+
drawer.style.width = "";
|
|
1701
|
+
drawer.style.height = "";
|
|
1702
|
+
drawer.style.minHeight = "";
|
|
1703
|
+
drawer.style.boxShadow = "";
|
|
1704
|
+
drawer.style.borderRadius = "";
|
|
1705
|
+
}
|
|
1706
|
+
|
|
1707
|
+
async function loadChecklist() {
|
|
1708
|
+
const body = $("checklist-body");
|
|
1709
|
+
if (!body) return;
|
|
1710
|
+
body.innerHTML = `<tr><td colspan="6" class="empty">正在读取核对清单...</td></tr>`;
|
|
1711
|
+
try {
|
|
1712
|
+
const data = await json("/api/crosscheck");
|
|
1713
|
+
const items = data.checklist?.items || [];
|
|
1714
|
+
body.innerHTML = items.map(item => {
|
|
1715
|
+
const status = item.status || "unverified";
|
|
1716
|
+
return `<tr>
|
|
1717
|
+
<td>${esc(status)}</td>
|
|
1718
|
+
<td><code>${esc(item.id)}</code></td>
|
|
1719
|
+
<td>${esc(item.kind || "general")}</td>
|
|
1720
|
+
<td>${esc(item.phase || "general")}</td>
|
|
1721
|
+
<td>${esc(item.label)}${item.notes ? `<div style="color:var(--text-muted);font-size:11px">${esc(item.notes)}</div>` : ""}</td>
|
|
1722
|
+
<td style="white-space:nowrap">
|
|
1723
|
+
<button class="button" data-check-id="${esc(item.id)}" data-check-status="verified">核实</button>
|
|
1724
|
+
<button class="button" data-check-id="${esc(item.id)}" data-check-status="flagged">存疑</button>
|
|
1725
|
+
<button class="button" data-check-id="${esc(item.id)}" data-check-status="skipped">跳过</button>
|
|
1726
|
+
</td>
|
|
1727
|
+
</tr>`;
|
|
1728
|
+
}).join("") || `<tr><td colspan="6" class="empty">清单为空</td></tr>`;
|
|
1729
|
+
body.querySelectorAll("[data-check-id]").forEach(btn => {
|
|
1730
|
+
btn.addEventListener("click", async () => {
|
|
1731
|
+
await json("/api/crosscheck", {
|
|
1732
|
+
method: "POST",
|
|
1733
|
+
headers: { "content-type": "application/json" },
|
|
1734
|
+
body: JSON.stringify({ id: btn.dataset.checkId, status: btn.dataset.checkStatus }),
|
|
1735
|
+
});
|
|
1736
|
+
await loadChecklist();
|
|
1737
|
+
});
|
|
1738
|
+
});
|
|
1739
|
+
} catch (error) {
|
|
1740
|
+
body.innerHTML = `<tr><td colspan="6" class="empty">加载失败: ${esc(error.message)}</td></tr>`;
|
|
1741
|
+
}
|
|
1742
|
+
}
|
|
1743
|
+
|
|
1744
|
+
$("refresh-checklist")?.addEventListener("click", loadChecklist);
|
|
1745
|
+
$("skip-checklist")?.addEventListener("click", async () => {
|
|
1746
|
+
if (!confirm("将把所有未核项标为跳过,并写入「未经核对」。继续?")) return;
|
|
1747
|
+
await json("/api/crosscheck", {
|
|
1748
|
+
method: "POST",
|
|
1749
|
+
headers: { "content-type": "application/json" },
|
|
1750
|
+
body: JSON.stringify({ action: "skip" }),
|
|
1751
|
+
});
|
|
1752
|
+
await loadChecklist();
|
|
1753
|
+
});
|
|
1754
|
+
|
|
1505
1755
|
document.querySelectorAll(".nav button").forEach(button => {
|
|
1506
1756
|
button.addEventListener("click", () => setView(button.dataset.view));
|
|
1507
1757
|
});
|
|
@@ -2105,8 +2355,10 @@ $("cmd-plan-btn").addEventListener("click", async () => {
|
|
|
2105
2355
|
});
|
|
2106
2356
|
|
|
2107
2357
|
$("cmd-verify-btn").addEventListener("click", async () => {
|
|
2358
|
+
setView("checklist");
|
|
2359
|
+
await loadChecklist();
|
|
2108
2360
|
openConsole();
|
|
2109
|
-
await sendConsoleMessage("/
|
|
2361
|
+
await sendConsoleMessage("/crosscheck list");
|
|
2110
2362
|
});
|
|
2111
2363
|
|
|
2112
2364
|
/* --- 加载项目状态并更新 Stepper 状态 --- */
|
|
@@ -2130,6 +2382,14 @@ loadProject().catch(error => {
|
|
|
2130
2382
|
$("viewer-body").textContent = `加载失败: ${error.message}`;
|
|
2131
2383
|
});
|
|
2132
2384
|
loadProjectStatus();
|
|
2385
|
+
|
|
2386
|
+
const initialView = decodeURIComponent((location.hash || "").replace(/^#/, "").trim());
|
|
2387
|
+
const knownViews = new Set(["project", "ecosystem", "provider", "checklist", "chat", "help"]);
|
|
2388
|
+
if (knownViews.has(initialView)) setView(initialView);
|
|
2389
|
+
window.addEventListener("hashchange", () => {
|
|
2390
|
+
const next = decodeURIComponent((location.hash || "").replace(/^#/, "").trim());
|
|
2391
|
+
if (knownViews.has(next) && next !== state.view) setView(next);
|
|
2392
|
+
});
|
|
2133
2393
|
</script>
|
|
2134
2394
|
</body>
|
|
2135
2395
|
</html>
|
|
@@ -28,7 +28,9 @@ import { formatAnalysisPlanTakeover, resolveStatsIntent, } from "../../analysis/
|
|
|
28
28
|
import { arsRoot } from "../../ars/pi-panel-executor.js";
|
|
29
29
|
import { ArsModeEditor, isArsModeEditorText } from "../../ars/mode-editor.js";
|
|
30
30
|
import { MODE_STATUS, nextSessionMode, parseSessionMode, sessionModePrompt, } from "../../session/modes.js";
|
|
31
|
-
import { formatVerifyChecklist, loadVerifyChecklist, markVerifyItem } from "../../verify/checklist.js";
|
|
31
|
+
import { formatVerifyChecklist, isNaturalPlanConfirm, loadVerifyChecklist, markVerifyItem, skipUnverifiedItems } from "../../verify/checklist.js";
|
|
32
|
+
import { formatSessionHelp, formatSessionHelpBrief } from "../../session/help.js";
|
|
33
|
+
import { openResearchWorkbench } from "../../panel/workbench.js";
|
|
32
34
|
const PARADIGMS = new Set([
|
|
33
35
|
"survey-observational",
|
|
34
36
|
"qualitative-thematic",
|
|
@@ -1128,14 +1130,78 @@ export default function psyclawExtension(pi) {
|
|
|
1128
1130
|
});
|
|
1129
1131
|
});
|
|
1130
1132
|
if (!legacyTestApi && typeof pi.on === "function")
|
|
1131
|
-
pi.on("input", (event, ctx) => {
|
|
1133
|
+
pi.on("input", async (event, ctx) => {
|
|
1132
1134
|
if (event.source === "extension")
|
|
1133
1135
|
return;
|
|
1134
1136
|
if (/^\/(?:login|logout)(?:\s|$)/i.test(event.text.trim())) {
|
|
1135
1137
|
ctx.ui.notify("PsyClaw 已统一隐藏底层登录命令。请使用 /provider 配置、切换或更新模型凭据。", "info");
|
|
1136
1138
|
return { action: "handled" };
|
|
1137
1139
|
}
|
|
1140
|
+
if (/^\/help(?:\s|$)/i.test(event.text.trim())) {
|
|
1141
|
+
try {
|
|
1142
|
+
const { url, opened } = await openResearchWorkbench({
|
|
1143
|
+
cwd: ctx.cwd,
|
|
1144
|
+
isProjectTrusted: () => ctx.isProjectTrusted(),
|
|
1145
|
+
isIdle: () => ctx.isIdle(),
|
|
1146
|
+
sendUserMessage: (message, options) => pi.sendUserMessage(message, options ?? {}),
|
|
1147
|
+
}, { view: "help" });
|
|
1148
|
+
ctx.ui.notify(formatSessionHelpBrief(url) + (opened ? "" : "\n(未能自动打开浏览器时请手动访问)"), opened ? "info" : "warning");
|
|
1149
|
+
}
|
|
1150
|
+
catch (error) {
|
|
1151
|
+
ctx.ui.notify(`${formatSessionHelp()}\n\n(Panel 未启动:${error instanceof Error ? error.message : String(error)})`, "info");
|
|
1152
|
+
}
|
|
1153
|
+
return { action: "handled" };
|
|
1154
|
+
}
|
|
1138
1155
|
const mode = arsModeEditor?.getMode() ?? sessionMode;
|
|
1156
|
+
const trimmed = event.text.trim();
|
|
1157
|
+
// Natural-language plan confirm: reply「可以」instead of forcing /plan confirm.
|
|
1158
|
+
if (mode === "analysis" && isNaturalPlanConfirm(trimmed)) {
|
|
1159
|
+
const plan = await readActiveAnalysisPlan(ctx.cwd);
|
|
1160
|
+
if (plan && (plan.status === "awaiting-confirm" || plan.status === "ready" || plan.status === "reviewing")) {
|
|
1161
|
+
const method = plan.confirmedMethod || plan.primaryAnalysis || plan.proposedMethods[0] || "按当前方案执行";
|
|
1162
|
+
let next = plan.status === "awaiting-confirm"
|
|
1163
|
+
? advanceAnalysisPlan(plan, { type: "confirm", method, backend: "local-script" })
|
|
1164
|
+
: plan;
|
|
1165
|
+
if (next.status === "reviewing" || next.status === "awaiting-confirm") {
|
|
1166
|
+
next = advanceAnalysisPlan(next, { type: "review" });
|
|
1167
|
+
}
|
|
1168
|
+
next = advanceAnalysisPlan(next, { type: "run-now" });
|
|
1169
|
+
next = await writeAnalysisPlan(ctx.cwd, next);
|
|
1170
|
+
ctx.ui.notify(`${formatAnalysisPlanStatus(next)}\n已按自然语言确认并开始执行。`, "info");
|
|
1171
|
+
return {
|
|
1172
|
+
action: "transform",
|
|
1173
|
+
text: [
|
|
1174
|
+
"/skill:analysis-plan",
|
|
1175
|
+
"",
|
|
1176
|
+
`User confirmed with natural language ("${trimmed}"). Execute plan ${next.id} now.`,
|
|
1177
|
+
`Confirmed method: ${next.confirmedMethod ?? method}`,
|
|
1178
|
+
next.approvalMode === "auto" ? "approvalMode=auto: label outputs as 未经人审批." : "Human confirmed this step.",
|
|
1179
|
+
"Before and after analysis, open Panel checklist or /crosscheck; skipping must mark 未经核对.",
|
|
1180
|
+
].join("\n"),
|
|
1181
|
+
};
|
|
1182
|
+
}
|
|
1183
|
+
}
|
|
1184
|
+
const wantsStats = mode === "analysis" && Boolean(resolveStatsIntent(trimmed));
|
|
1185
|
+
const wantsAcademic = mode === "academic" && Boolean(resolveAcademicSoftRoute(trimmed));
|
|
1186
|
+
if ((wantsStats || wantsAcademic) && !(await readActiveProject(ctx.cwd))) {
|
|
1187
|
+
if (!ctx.hasUI) {
|
|
1188
|
+
return {
|
|
1189
|
+
action: "transform",
|
|
1190
|
+
text: [
|
|
1191
|
+
"尚未 /init。请先提醒用户初始化工作区;若用户确认可无仓继续,再处理其请求。",
|
|
1192
|
+
"User request:",
|
|
1193
|
+
trimmed,
|
|
1194
|
+
].join("\n"),
|
|
1195
|
+
};
|
|
1196
|
+
}
|
|
1197
|
+
const choice = await ctx.ui.select("尚未 /init。统计/写作建议先建仓,否则交接与核对清单不完整。", ["先执行 /init", "本次继续(稍后 init)", "取消"], { timeout: 120_000 });
|
|
1198
|
+
if (choice === "取消" || choice === undefined)
|
|
1199
|
+
return { action: "handled" };
|
|
1200
|
+
if (choice === "先执行 /init") {
|
|
1201
|
+
return { action: "transform", text: `/init\n\n(用户选择先初始化,完成后再继续:${trimmed})` };
|
|
1202
|
+
}
|
|
1203
|
+
ctx.ui.notify("继续本次请求;建议随后 /init。关键结果请做交叉核验。", "warning");
|
|
1204
|
+
}
|
|
1139
1205
|
if (mode === "academic") {
|
|
1140
1206
|
const match = resolveAcademicSoftRoute(event.text);
|
|
1141
1207
|
if (!match)
|
|
@@ -1199,7 +1265,8 @@ export default function psyclawExtension(pi) {
|
|
|
1199
1265
|
`工作仓库已初始化:${project.id}`,
|
|
1200
1266
|
"已创建 data/raw|clean、analysis/、literature/、paper/、psyclaw.md、.psyclaw/",
|
|
1201
1267
|
"Shift+Tab:chat → analysis → academic",
|
|
1202
|
-
"
|
|
1268
|
+
"Thinking:Ctrl+Shift+T",
|
|
1269
|
+
"澄清与分析请切到 analysis;写作审稿请切到 academic。输入 /help 查看速览。",
|
|
1203
1270
|
].join("\n"), "info");
|
|
1204
1271
|
}
|
|
1205
1272
|
catch (error) {
|
|
@@ -1207,28 +1274,64 @@ export default function psyclawExtension(pi) {
|
|
|
1207
1274
|
}
|
|
1208
1275
|
},
|
|
1209
1276
|
});
|
|
1277
|
+
const handleCrosscheck = async (args, ctx) => {
|
|
1278
|
+
try {
|
|
1279
|
+
const parts = args.trim().split(/\s+/).filter(Boolean);
|
|
1280
|
+
if (parts.length === 0 || parts[0] === "list") {
|
|
1281
|
+
ctx.ui.notify(formatVerifyChecklist(await loadVerifyChecklist(ctx.cwd)), "info");
|
|
1282
|
+
return;
|
|
1283
|
+
}
|
|
1284
|
+
if (parts[0] === "skip") {
|
|
1285
|
+
ctx.ui.notify(formatVerifyChecklist(await skipUnverifiedItems(ctx.cwd)), "warning");
|
|
1286
|
+
return;
|
|
1287
|
+
}
|
|
1288
|
+
if (parts[0] === "kind") {
|
|
1289
|
+
const kind = (parts[1] ?? "general");
|
|
1290
|
+
pi.sendUserMessage([
|
|
1291
|
+
"交叉核验任务:请作为助手主动提出核对项,不要等用户自己去「申请确认」。",
|
|
1292
|
+
`核验类别:${kind}(citations=引文,format=格式,requirements=研究要求,stats=统计结果,general=综合)。`,
|
|
1293
|
+
"输出可勾选清单,并建议用户打开 /panel 核对页逐项确认;若用户跳过,必须在产物中标注「未经核对」。",
|
|
1294
|
+
"分析前与分析后都要有清单,不能默认跳过。",
|
|
1295
|
+
].join("\n"), ctx.isIdle() ? {} : { deliverAs: "followUp" });
|
|
1296
|
+
ctx.ui.notify(`已启动 ${kind} 交叉核验;也可 /panel 打开核对清单。`, "info");
|
|
1297
|
+
return;
|
|
1298
|
+
}
|
|
1299
|
+
const id = parts[0];
|
|
1300
|
+
const status = (parts[1] ?? "verified");
|
|
1301
|
+
if (status !== "verified" && status !== "unverified" && status !== "flagged" && status !== "skipped") {
|
|
1302
|
+
ctx.ui.notify("Usage: /crosscheck list | skip | kind <citations|format|requirements|stats> | <id> verified|unverified|flagged|skipped [备注]", "info");
|
|
1303
|
+
return;
|
|
1304
|
+
}
|
|
1305
|
+
const notes = parts.slice(2).join(" ") || undefined;
|
|
1306
|
+
const checklist = await markVerifyItem(ctx.cwd, id, status, notes);
|
|
1307
|
+
ctx.ui.notify(formatVerifyChecklist(checklist), "info");
|
|
1308
|
+
}
|
|
1309
|
+
catch (error) {
|
|
1310
|
+
await notifyError(ctx, error);
|
|
1311
|
+
}
|
|
1312
|
+
};
|
|
1313
|
+
pi.registerCommand("crosscheck", {
|
|
1314
|
+
description: "交叉核验:/crosscheck list|skip|kind <类型>|<id> verified|…",
|
|
1315
|
+
handler: async (args, ctx) => handleCrosscheck(args, ctx),
|
|
1316
|
+
});
|
|
1210
1317
|
pi.registerCommand("verify", {
|
|
1211
|
-
description: "
|
|
1212
|
-
handler: async (args, ctx) =>
|
|
1318
|
+
description: "交叉核验(同 /crosscheck):模型提出项,人勾选或 skip 标注未经核对",
|
|
1319
|
+
handler: async (args, ctx) => handleCrosscheck(args, ctx),
|
|
1320
|
+
});
|
|
1321
|
+
pi.registerCommand("help", {
|
|
1322
|
+
description: "打开 Panel 使用速览",
|
|
1323
|
+
handler: async (_args, ctx) => {
|
|
1213
1324
|
try {
|
|
1214
|
-
const
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
ctx.
|
|
1218
|
-
|
|
1219
|
-
}
|
|
1220
|
-
|
|
1221
|
-
const status = (parts[1] ?? "verified");
|
|
1222
|
-
if (status !== "verified" && status !== "unverified" && status !== "flagged") {
|
|
1223
|
-
ctx.ui.notify("Usage: /verify list | /verify <id> verified|unverified|flagged [备注]", "info");
|
|
1224
|
-
return;
|
|
1225
|
-
}
|
|
1226
|
-
const notes = parts.slice(2).join(" ") || undefined;
|
|
1227
|
-
const checklist = await markVerifyItem(ctx.cwd, id, status, notes);
|
|
1228
|
-
ctx.ui.notify(formatVerifyChecklist(checklist), "info");
|
|
1325
|
+
const { url, opened } = await openResearchWorkbench({
|
|
1326
|
+
cwd: ctx.cwd,
|
|
1327
|
+
isProjectTrusted: () => ctx.isProjectTrusted(),
|
|
1328
|
+
isIdle: () => ctx.isIdle(),
|
|
1329
|
+
sendUserMessage: (message, options) => pi.sendUserMessage(message, options ?? {}),
|
|
1330
|
+
}, { view: "help" });
|
|
1331
|
+
ctx.ui.notify(formatSessionHelpBrief(url) + (opened ? "" : "\n(未能自动打开浏览器时请手动访问)"), opened ? "info" : "warning");
|
|
1229
1332
|
}
|
|
1230
1333
|
catch (error) {
|
|
1231
|
-
|
|
1334
|
+
ctx.ui.notify(`${formatSessionHelp()}\n\n(Panel 未启动:${error instanceof Error ? error.message : String(error)})`, "info");
|
|
1232
1335
|
}
|
|
1233
1336
|
},
|
|
1234
1337
|
});
|
|
@@ -1239,11 +1342,33 @@ export default function psyclawExtension(pi) {
|
|
|
1239
1342
|
const trimmed = args.trim();
|
|
1240
1343
|
const [cmdRaw, ...rest] = trimmed.split(/\s+/).filter(Boolean);
|
|
1241
1344
|
const cmd = (cmdRaw ?? "status").toLowerCase();
|
|
1242
|
-
const usage = "Usage: /plan new
|
|
1345
|
+
const usage = "Usage: /plan new|status|list|confirm|auto|human|review|run|defer|handoff|reject";
|
|
1243
1346
|
if (cmd === "new") {
|
|
1244
1347
|
const goal = rest.join(" ").trim() || "从当前对话澄清的分析目标";
|
|
1245
1348
|
const plan = await writeAnalysisPlan(ctx.cwd, createAnalysisPlan({ goal }));
|
|
1246
|
-
ctx.ui.notify(`${formatAnalysisPlanStatus(plan)}\n
|
|
1349
|
+
ctx.ui.notify(`${formatAnalysisPlanStatus(plan)}\n已创建。模型会按分析项逐一请你选择;回复「可以」即可确认执行。`, "info");
|
|
1350
|
+
return;
|
|
1351
|
+
}
|
|
1352
|
+
if (cmd === "auto") {
|
|
1353
|
+
let plan = await readActiveAnalysisPlan(ctx.cwd);
|
|
1354
|
+
if (!plan) {
|
|
1355
|
+
ctx.ui.notify("没有活跃 Plan。先 /plan new 或在 analysis 模式触发 soft takeover。", "warning");
|
|
1356
|
+
return;
|
|
1357
|
+
}
|
|
1358
|
+
plan = advanceAnalysisPlan(plan, { type: "set-approval-mode", mode: "auto" });
|
|
1359
|
+
plan = await writeAnalysisPlan(ctx.cwd, plan);
|
|
1360
|
+
ctx.ui.notify(`${formatAnalysisPlanStatus(plan)}\n已启用 auto:后续结果必须标注「未经人审批」。`, "warning");
|
|
1361
|
+
return;
|
|
1362
|
+
}
|
|
1363
|
+
if (cmd === "human") {
|
|
1364
|
+
let plan = await readActiveAnalysisPlan(ctx.cwd);
|
|
1365
|
+
if (!plan) {
|
|
1366
|
+
ctx.ui.notify("没有活跃 Plan。", "warning");
|
|
1367
|
+
return;
|
|
1368
|
+
}
|
|
1369
|
+
plan = advanceAnalysisPlan(plan, { type: "set-approval-mode", mode: "human" });
|
|
1370
|
+
plan = await writeAnalysisPlan(ctx.cwd, plan);
|
|
1371
|
+
ctx.ui.notify(`${formatAnalysisPlanStatus(plan)}\n已恢复人工确认。`, "info");
|
|
1247
1372
|
return;
|
|
1248
1373
|
}
|
|
1249
1374
|
if (cmd === "list") {
|
|
@@ -1273,7 +1398,7 @@ export default function psyclawExtension(pi) {
|
|
|
1273
1398
|
plan = advanceAnalysisPlan(plan, { type: "confirm", method, backend: "local-script" });
|
|
1274
1399
|
plan = advanceAnalysisPlan(plan, { type: "review" });
|
|
1275
1400
|
plan = await writeAnalysisPlan(ctx.cwd, plan);
|
|
1276
|
-
ctx.ui.notify(`${formatAnalysisPlanStatus(plan)}\n
|
|
1401
|
+
ctx.ui.notify(`${formatAnalysisPlanStatus(plan)}\n软确认完成并已审查。回复「可以」或 /plan run 开始执行。`, "info");
|
|
1277
1402
|
return;
|
|
1278
1403
|
}
|
|
1279
1404
|
if (cmd === "review") {
|
|
@@ -1516,9 +1641,9 @@ export default function psyclawExtension(pi) {
|
|
|
1516
1641
|
ctx.ui.notify([
|
|
1517
1642
|
`PsyClaw ARS profile v${PSYCLAW_ARS_PROFILE_VERSION}`,
|
|
1518
1643
|
`当前模式:${arsModeEditor?.getMode() ?? sessionMode}(Shift+Tab:chat → analysis → academic)`,
|
|
1519
|
-
"Thinking:Ctrl+Shift+
|
|
1644
|
+
"Thinking:Ctrl+Shift+T",
|
|
1520
1645
|
`来源:${ARS_REPOSITORY_URL} @ ${ARS_UPSTREAM_REF} (${ARS_UPSTREAM_COMMIT.slice(0, 12)})`,
|
|
1521
|
-
"入口:Shift+Tab,/ars doctor,/ars start,/ars full <task>,/ars stop,/
|
|
1646
|
+
"入口:Shift+Tab,/ars doctor,/ars start,/ars full <task>,/ars stop,/crosscheck,/help",
|
|
1522
1647
|
formatNatureArsFillerStatus(natureFillersFromCommandContext(ctx)),
|
|
1523
1648
|
].join("\n"), "info");
|
|
1524
1649
|
return;
|