psyclaw 0.29.2 → 0.29.4
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 +4 -4
- package/dist/apps/panel/index.html +2222 -54
- package/dist/src/adapters/pi/extension.js +136 -41
- 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/index.d.ts +1 -0
- package/dist/src/index.js +1 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/panel/extension.js +14 -5
- package/dist/src/panel/extension.js.map +1 -1
- package/dist/src/panel/server.js +69 -1
- package/dist/src/panel/server.js.map +1 -1
- package/dist/src/session/help.d.ts +5 -0
- package/dist/src/session/help.js +46 -0
- package/dist/src/session/help.js.map +1 -0
- package/dist/src/session/modes.d.ts +1 -1
- package/dist/src/session/modes.js +5 -4
- package/dist/src/session/modes.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/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/scripts/rebrand-pi.mjs +4 -2
- 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 +682 -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
|
@@ -26,9 +26,10 @@ import { formatAcademicSoftRouteInvocation, rankAcademicSoftRoutes, resolveAcade
|
|
|
26
26
|
import { advanceAnalysisPlan, createAnalysisPlan, formatAnalysisPlanStatus, listAnalysisPlans, readActiveAnalysisPlan, syncHandoffFromAnalysisPlan, writeAnalysisPlan, } from "../../analysis/plan.js";
|
|
27
27
|
import { formatAnalysisPlanTakeover, resolveStatsIntent, } from "../../analysis/stats-router.js";
|
|
28
28
|
import { arsRoot } from "../../ars/pi-panel-executor.js";
|
|
29
|
-
import { ArsModeEditor,
|
|
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 } from "../../session/help.js";
|
|
32
33
|
const PARADIGMS = new Set([
|
|
33
34
|
"survey-observational",
|
|
34
35
|
"qualitative-thematic",
|
|
@@ -1036,11 +1037,8 @@ export default function psyclawExtension(pi) {
|
|
|
1036
1037
|
sessionMode = mode;
|
|
1037
1038
|
arsModeEditor?.setMode(mode);
|
|
1038
1039
|
arsUiContext?.ui.setStatus("mode", MODE_STATUS[mode]);
|
|
1039
|
-
// Clear legacy ars status key
|
|
1040
|
-
|
|
1041
|
-
arsUiContext?.ui.setStatus("ars", undefined);
|
|
1042
|
-
else
|
|
1043
|
-
arsUiContext?.ui.setStatus("ars", ARS_MODE_STATUS);
|
|
1040
|
+
// Clear legacy ars status key so it never duplicates with "mode".
|
|
1041
|
+
arsUiContext?.ui.setStatus("ars", undefined);
|
|
1044
1042
|
if (opts?.syncSession === false || !arsUiContext)
|
|
1045
1043
|
return;
|
|
1046
1044
|
setArsPiSessionActive(typeof pi.appendEntry === "function"
|
|
@@ -1115,10 +1113,7 @@ export default function psyclawExtension(pi) {
|
|
|
1115
1113
|
editor.onModeChange = (mode) => {
|
|
1116
1114
|
sessionMode = mode;
|
|
1117
1115
|
ctx.ui.setStatus("mode", MODE_STATUS[mode]);
|
|
1118
|
-
|
|
1119
|
-
ctx.ui.setStatus("ars", ARS_MODE_STATUS);
|
|
1120
|
-
else
|
|
1121
|
-
ctx.ui.setStatus("ars", undefined);
|
|
1116
|
+
ctx.ui.setStatus("ars", undefined);
|
|
1122
1117
|
};
|
|
1123
1118
|
editor.onCycleMode = () => {
|
|
1124
1119
|
cycleSessionMode();
|
|
@@ -1129,20 +1124,72 @@ export default function psyclawExtension(pi) {
|
|
|
1129
1124
|
editor.setMode(restore);
|
|
1130
1125
|
sessionMode = restore;
|
|
1131
1126
|
ctx.ui.setStatus("mode", MODE_STATUS[restore]);
|
|
1132
|
-
|
|
1133
|
-
ctx.ui.setStatus("ars", ARS_MODE_STATUS);
|
|
1127
|
+
ctx.ui.setStatus("ars", undefined);
|
|
1134
1128
|
return editor;
|
|
1135
1129
|
});
|
|
1136
1130
|
});
|
|
1137
1131
|
if (!legacyTestApi && typeof pi.on === "function")
|
|
1138
|
-
pi.on("input", (event, ctx) => {
|
|
1132
|
+
pi.on("input", async (event, ctx) => {
|
|
1139
1133
|
if (event.source === "extension")
|
|
1140
1134
|
return;
|
|
1141
1135
|
if (/^\/(?:login|logout)(?:\s|$)/i.test(event.text.trim())) {
|
|
1142
1136
|
ctx.ui.notify("PsyClaw 已统一隐藏底层登录命令。请使用 /provider 配置、切换或更新模型凭据。", "info");
|
|
1143
1137
|
return { action: "handled" };
|
|
1144
1138
|
}
|
|
1139
|
+
if (/^\/help(?:\s|$)/i.test(event.text.trim())) {
|
|
1140
|
+
ctx.ui.notify(formatSessionHelp(), "info");
|
|
1141
|
+
return { action: "handled" };
|
|
1142
|
+
}
|
|
1145
1143
|
const mode = arsModeEditor?.getMode() ?? sessionMode;
|
|
1144
|
+
const trimmed = event.text.trim();
|
|
1145
|
+
// Natural-language plan confirm: reply「可以」instead of forcing /plan confirm.
|
|
1146
|
+
if (mode === "analysis" && isNaturalPlanConfirm(trimmed)) {
|
|
1147
|
+
const plan = await readActiveAnalysisPlan(ctx.cwd);
|
|
1148
|
+
if (plan && (plan.status === "awaiting-confirm" || plan.status === "ready" || plan.status === "reviewing")) {
|
|
1149
|
+
const method = plan.confirmedMethod || plan.primaryAnalysis || plan.proposedMethods[0] || "按当前方案执行";
|
|
1150
|
+
let next = plan.status === "awaiting-confirm"
|
|
1151
|
+
? advanceAnalysisPlan(plan, { type: "confirm", method, backend: "local-script" })
|
|
1152
|
+
: plan;
|
|
1153
|
+
if (next.status === "reviewing" || next.status === "awaiting-confirm") {
|
|
1154
|
+
next = advanceAnalysisPlan(next, { type: "review" });
|
|
1155
|
+
}
|
|
1156
|
+
next = advanceAnalysisPlan(next, { type: "run-now" });
|
|
1157
|
+
next = await writeAnalysisPlan(ctx.cwd, next);
|
|
1158
|
+
ctx.ui.notify(`${formatAnalysisPlanStatus(next)}\n已按自然语言确认并开始执行。`, "info");
|
|
1159
|
+
return {
|
|
1160
|
+
action: "transform",
|
|
1161
|
+
text: [
|
|
1162
|
+
"/skill:analysis-plan",
|
|
1163
|
+
"",
|
|
1164
|
+
`User confirmed with natural language ("${trimmed}"). Execute plan ${next.id} now.`,
|
|
1165
|
+
`Confirmed method: ${next.confirmedMethod ?? method}`,
|
|
1166
|
+
next.approvalMode === "auto" ? "approvalMode=auto: label outputs as 未经人审批." : "Human confirmed this step.",
|
|
1167
|
+
"Before and after analysis, open Panel checklist or /crosscheck; skipping must mark 未经核对.",
|
|
1168
|
+
].join("\n"),
|
|
1169
|
+
};
|
|
1170
|
+
}
|
|
1171
|
+
}
|
|
1172
|
+
const wantsStats = mode === "analysis" && Boolean(resolveStatsIntent(trimmed));
|
|
1173
|
+
const wantsAcademic = mode === "academic" && Boolean(resolveAcademicSoftRoute(trimmed));
|
|
1174
|
+
if ((wantsStats || wantsAcademic) && !(await readActiveProject(ctx.cwd))) {
|
|
1175
|
+
if (!ctx.hasUI) {
|
|
1176
|
+
return {
|
|
1177
|
+
action: "transform",
|
|
1178
|
+
text: [
|
|
1179
|
+
"尚未 /init。请先提醒用户初始化工作区;若用户确认可无仓继续,再处理其请求。",
|
|
1180
|
+
"User request:",
|
|
1181
|
+
trimmed,
|
|
1182
|
+
].join("\n"),
|
|
1183
|
+
};
|
|
1184
|
+
}
|
|
1185
|
+
const choice = await ctx.ui.select("尚未 /init。统计/写作建议先建仓,否则交接与核对清单不完整。", ["先执行 /init", "本次继续(稍后 init)", "取消"], { timeout: 120_000 });
|
|
1186
|
+
if (choice === "取消" || choice === undefined)
|
|
1187
|
+
return { action: "handled" };
|
|
1188
|
+
if (choice === "先执行 /init") {
|
|
1189
|
+
return { action: "transform", text: `/init\n\n(用户选择先初始化,完成后再继续:${trimmed})` };
|
|
1190
|
+
}
|
|
1191
|
+
ctx.ui.notify("继续本次请求;建议随后 /init。关键结果请做交叉核验。", "warning");
|
|
1192
|
+
}
|
|
1146
1193
|
if (mode === "academic") {
|
|
1147
1194
|
const match = resolveAcademicSoftRoute(event.text);
|
|
1148
1195
|
if (!match)
|
|
@@ -1206,7 +1253,8 @@ export default function psyclawExtension(pi) {
|
|
|
1206
1253
|
`工作仓库已初始化:${project.id}`,
|
|
1207
1254
|
"已创建 data/raw|clean、analysis/、literature/、paper/、psyclaw.md、.psyclaw/",
|
|
1208
1255
|
"Shift+Tab:chat → analysis → academic",
|
|
1209
|
-
"
|
|
1256
|
+
"Thinking:Ctrl+Shift+T",
|
|
1257
|
+
"澄清与分析请切到 analysis;写作审稿请切到 academic。输入 /help 查看速览。",
|
|
1210
1258
|
].join("\n"), "info");
|
|
1211
1259
|
}
|
|
1212
1260
|
catch (error) {
|
|
@@ -1214,29 +1262,54 @@ export default function psyclawExtension(pi) {
|
|
|
1214
1262
|
}
|
|
1215
1263
|
},
|
|
1216
1264
|
});
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
const checklist = await loadVerifyChecklist(ctx.cwd);
|
|
1224
|
-
ctx.ui.notify(formatVerifyChecklist(checklist), "info");
|
|
1225
|
-
return;
|
|
1226
|
-
}
|
|
1227
|
-
const id = parts[0];
|
|
1228
|
-
const status = (parts[1] ?? "verified");
|
|
1229
|
-
if (status !== "verified" && status !== "unverified" && status !== "flagged") {
|
|
1230
|
-
ctx.ui.notify("Usage: /verify list | /verify <id> verified|unverified|flagged [备注]", "info");
|
|
1231
|
-
return;
|
|
1232
|
-
}
|
|
1233
|
-
const notes = parts.slice(2).join(" ") || undefined;
|
|
1234
|
-
const checklist = await markVerifyItem(ctx.cwd, id, status, notes);
|
|
1235
|
-
ctx.ui.notify(formatVerifyChecklist(checklist), "info");
|
|
1265
|
+
const handleCrosscheck = async (args, ctx) => {
|
|
1266
|
+
try {
|
|
1267
|
+
const parts = args.trim().split(/\s+/).filter(Boolean);
|
|
1268
|
+
if (parts.length === 0 || parts[0] === "list") {
|
|
1269
|
+
ctx.ui.notify(formatVerifyChecklist(await loadVerifyChecklist(ctx.cwd)), "info");
|
|
1270
|
+
return;
|
|
1236
1271
|
}
|
|
1237
|
-
|
|
1238
|
-
await
|
|
1272
|
+
if (parts[0] === "skip") {
|
|
1273
|
+
ctx.ui.notify(formatVerifyChecklist(await skipUnverifiedItems(ctx.cwd)), "warning");
|
|
1274
|
+
return;
|
|
1275
|
+
}
|
|
1276
|
+
if (parts[0] === "kind") {
|
|
1277
|
+
const kind = (parts[1] ?? "general");
|
|
1278
|
+
pi.sendUserMessage([
|
|
1279
|
+
"交叉核验任务:请作为助手主动提出核对项,不要等用户自己去「申请确认」。",
|
|
1280
|
+
`核验类别:${kind}(citations=引文,format=格式,requirements=研究要求,stats=统计结果,general=综合)。`,
|
|
1281
|
+
"输出可勾选清单,并建议用户打开 /panel 核对页逐项确认;若用户跳过,必须在产物中标注「未经核对」。",
|
|
1282
|
+
"分析前与分析后都要有清单,不能默认跳过。",
|
|
1283
|
+
].join("\n"), ctx.isIdle() ? {} : { deliverAs: "followUp" });
|
|
1284
|
+
ctx.ui.notify(`已启动 ${kind} 交叉核验;也可 /panel 打开核对清单。`, "info");
|
|
1285
|
+
return;
|
|
1286
|
+
}
|
|
1287
|
+
const id = parts[0];
|
|
1288
|
+
const status = (parts[1] ?? "verified");
|
|
1289
|
+
if (status !== "verified" && status !== "unverified" && status !== "flagged" && status !== "skipped") {
|
|
1290
|
+
ctx.ui.notify("Usage: /crosscheck list | skip | kind <citations|format|requirements|stats> | <id> verified|unverified|flagged|skipped [备注]", "info");
|
|
1291
|
+
return;
|
|
1239
1292
|
}
|
|
1293
|
+
const notes = parts.slice(2).join(" ") || undefined;
|
|
1294
|
+
const checklist = await markVerifyItem(ctx.cwd, id, status, notes);
|
|
1295
|
+
ctx.ui.notify(formatVerifyChecklist(checklist), "info");
|
|
1296
|
+
}
|
|
1297
|
+
catch (error) {
|
|
1298
|
+
await notifyError(ctx, error);
|
|
1299
|
+
}
|
|
1300
|
+
};
|
|
1301
|
+
pi.registerCommand("crosscheck", {
|
|
1302
|
+
description: "交叉核验:/crosscheck list|skip|kind <类型>|<id> verified|…",
|
|
1303
|
+
handler: async (args, ctx) => handleCrosscheck(args, ctx),
|
|
1304
|
+
});
|
|
1305
|
+
pi.registerCommand("verify", {
|
|
1306
|
+
description: "交叉核验(同 /crosscheck):模型提出项,人勾选或 skip 标注未经核对",
|
|
1307
|
+
handler: async (args, ctx) => handleCrosscheck(args, ctx),
|
|
1308
|
+
});
|
|
1309
|
+
pi.registerCommand("help", {
|
|
1310
|
+
description: "三种模式速览与常用命令",
|
|
1311
|
+
handler: async (_args, ctx) => {
|
|
1312
|
+
ctx.ui.notify(formatSessionHelp(), "info");
|
|
1240
1313
|
},
|
|
1241
1314
|
});
|
|
1242
1315
|
pi.registerCommand("plan", {
|
|
@@ -1246,11 +1319,33 @@ export default function psyclawExtension(pi) {
|
|
|
1246
1319
|
const trimmed = args.trim();
|
|
1247
1320
|
const [cmdRaw, ...rest] = trimmed.split(/\s+/).filter(Boolean);
|
|
1248
1321
|
const cmd = (cmdRaw ?? "status").toLowerCase();
|
|
1249
|
-
const usage = "Usage: /plan new
|
|
1322
|
+
const usage = "Usage: /plan new|status|list|confirm|auto|human|review|run|defer|handoff|reject";
|
|
1250
1323
|
if (cmd === "new") {
|
|
1251
1324
|
const goal = rest.join(" ").trim() || "从当前对话澄清的分析目标";
|
|
1252
1325
|
const plan = await writeAnalysisPlan(ctx.cwd, createAnalysisPlan({ goal }));
|
|
1253
|
-
ctx.ui.notify(`${formatAnalysisPlanStatus(plan)}\n
|
|
1326
|
+
ctx.ui.notify(`${formatAnalysisPlanStatus(plan)}\n已创建。模型会按分析项逐一请你选择;回复「可以」即可确认执行。`, "info");
|
|
1327
|
+
return;
|
|
1328
|
+
}
|
|
1329
|
+
if (cmd === "auto") {
|
|
1330
|
+
let plan = await readActiveAnalysisPlan(ctx.cwd);
|
|
1331
|
+
if (!plan) {
|
|
1332
|
+
ctx.ui.notify("没有活跃 Plan。先 /plan new 或在 analysis 模式触发 soft takeover。", "warning");
|
|
1333
|
+
return;
|
|
1334
|
+
}
|
|
1335
|
+
plan = advanceAnalysisPlan(plan, { type: "set-approval-mode", mode: "auto" });
|
|
1336
|
+
plan = await writeAnalysisPlan(ctx.cwd, plan);
|
|
1337
|
+
ctx.ui.notify(`${formatAnalysisPlanStatus(plan)}\n已启用 auto:后续结果必须标注「未经人审批」。`, "warning");
|
|
1338
|
+
return;
|
|
1339
|
+
}
|
|
1340
|
+
if (cmd === "human") {
|
|
1341
|
+
let plan = await readActiveAnalysisPlan(ctx.cwd);
|
|
1342
|
+
if (!plan) {
|
|
1343
|
+
ctx.ui.notify("没有活跃 Plan。", "warning");
|
|
1344
|
+
return;
|
|
1345
|
+
}
|
|
1346
|
+
plan = advanceAnalysisPlan(plan, { type: "set-approval-mode", mode: "human" });
|
|
1347
|
+
plan = await writeAnalysisPlan(ctx.cwd, plan);
|
|
1348
|
+
ctx.ui.notify(`${formatAnalysisPlanStatus(plan)}\n已恢复人工确认。`, "info");
|
|
1254
1349
|
return;
|
|
1255
1350
|
}
|
|
1256
1351
|
if (cmd === "list") {
|
|
@@ -1280,7 +1375,7 @@ export default function psyclawExtension(pi) {
|
|
|
1280
1375
|
plan = advanceAnalysisPlan(plan, { type: "confirm", method, backend: "local-script" });
|
|
1281
1376
|
plan = advanceAnalysisPlan(plan, { type: "review" });
|
|
1282
1377
|
plan = await writeAnalysisPlan(ctx.cwd, plan);
|
|
1283
|
-
ctx.ui.notify(`${formatAnalysisPlanStatus(plan)}\n
|
|
1378
|
+
ctx.ui.notify(`${formatAnalysisPlanStatus(plan)}\n软确认完成并已审查。回复「可以」或 /plan run 开始执行。`, "info");
|
|
1284
1379
|
return;
|
|
1285
1380
|
}
|
|
1286
1381
|
if (cmd === "review") {
|
|
@@ -1523,9 +1618,9 @@ export default function psyclawExtension(pi) {
|
|
|
1523
1618
|
ctx.ui.notify([
|
|
1524
1619
|
`PsyClaw ARS profile v${PSYCLAW_ARS_PROFILE_VERSION}`,
|
|
1525
1620
|
`当前模式:${arsModeEditor?.getMode() ?? sessionMode}(Shift+Tab:chat → analysis → academic)`,
|
|
1526
|
-
"Thinking:Ctrl+Shift+
|
|
1621
|
+
"Thinking:Ctrl+Shift+T",
|
|
1527
1622
|
`来源:${ARS_REPOSITORY_URL} @ ${ARS_UPSTREAM_REF} (${ARS_UPSTREAM_COMMIT.slice(0, 12)})`,
|
|
1528
|
-
"入口:Shift+Tab,/ars doctor,/ars start,/ars full <task>,/ars stop,/
|
|
1623
|
+
"入口:Shift+Tab,/ars doctor,/ars start,/ars full <task>,/ars stop,/crosscheck,/help",
|
|
1529
1624
|
formatNatureArsFillerStatus(natureFillersFromCommandContext(ctx)),
|
|
1530
1625
|
].join("\n"), "info");
|
|
1531
1626
|
return;
|