psyclaw 0.30.15 → 0.30.17
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/dist/apps/panel/index.html +52 -17
- package/dist/src/adapters/pi/extension.js +22 -3
- package/dist/src/adapters/pi/extension.js.map +1 -1
- package/dist/src/panel/server.js +93 -17
- package/dist/src/panel/server.js.map +1 -1
- package/dist/src/skills/recommended.d.ts +5 -0
- package/dist/src/skills/recommended.js +26 -2
- package/dist/src/skills/recommended.js.map +1 -1
- package/dist/src/tui/skill-manager.js +24 -1
- package/dist/src/tui/skill-manager.js.map +1 -1
- package/dist/src/verify/checklist.d.ts +1 -0
- package/dist/src/verify/checklist.js +18 -2
- package/dist/src/verify/checklist.js.map +1 -1
- package/package.json +1 -1
package/dist/src/panel/server.js
CHANGED
|
@@ -31,10 +31,11 @@ import { readPublishedVersions } from "../workflows/publish.js";
|
|
|
31
31
|
export { verifyDoi } from "../core/doi.js";
|
|
32
32
|
import { PSYCLAW_IDENTITY_PROMPT } from "../branding.js";
|
|
33
33
|
import { sessionHelpDocument } from "../session/help.js";
|
|
34
|
-
import { recommendedSkillTarget } from "../skills/recommended.js";
|
|
34
|
+
import { normalizeRecommendedSkillId, readMergedRecommendationState, readRecommendationState as readCanonicalRecommendationState, recommendedSkillTarget, saveRecommendationState, validateModelInstalledRecommendedSkill, } from "../skills/recommended.js";
|
|
35
35
|
import { buildRecommendedSkillInstallTask } from "../skills/install-guidance.js";
|
|
36
36
|
import { readUserSkillState, scanLocalSkills, setLocalSkillEnabled, userSkillId, } from "../skills/user-skills.js";
|
|
37
|
-
import {
|
|
37
|
+
import { RuntimeMcpRegistry } from "../integrations/mcp-runtime.js";
|
|
38
|
+
import { defaultVerifyChecklist, loadVerifyChecklist, markVerifyItem, skipUnverifiedItems } from "../verify/checklist.js";
|
|
38
39
|
import { readActiveAnalysisPlan } from "../analysis/plan.js";
|
|
39
40
|
import { browserConfigForPreference, injectBrowserObservabilityConfig } from "../observability/config.js";
|
|
40
41
|
import { readTelemetryPreference, telemetryPreferenceOptions, writeTelemetryPreference } from "../observability/preference.js";
|
|
@@ -53,6 +54,61 @@ async function readRecommendationState(root) {
|
|
|
53
54
|
async function writeRecommendationState(root, state) {
|
|
54
55
|
await atomicWriteFile(await assertSafeProjectPath(root, ".psyclaw/recommendations.json"), `${JSON.stringify({ ...state, skills: [...new Set(state.skills)].sort(), mcp: [...new Set(state.mcp)].sort() }, null, 2)}\n`);
|
|
55
56
|
}
|
|
57
|
+
/** Resolve Skill installed/enabled the same way the TUI /skill manager does. */
|
|
58
|
+
async function panelRecommendedSkillStatuses(root) {
|
|
59
|
+
const state = await readMergedRecommendationState(root);
|
|
60
|
+
const catalog = await recommendedSkills();
|
|
61
|
+
return Promise.all((catalog.items ?? []).map(async (item) => {
|
|
62
|
+
const id = normalizeRecommendedSkillId(String(item.id ?? ""));
|
|
63
|
+
const name = typeof item.name === "string" ? item.name : id;
|
|
64
|
+
const scope = state.skillScopes?.[id] ?? "project";
|
|
65
|
+
const desired = state.skills.includes(id);
|
|
66
|
+
try {
|
|
67
|
+
await validateModelInstalledRecommendedSkill(root, id, scope);
|
|
68
|
+
return { id, name, installed: true, enabled: desired, scope };
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
let onDisk = false;
|
|
72
|
+
try {
|
|
73
|
+
const target = recommendedSkillTarget(root, id, scope);
|
|
74
|
+
const stat = await lstat(target);
|
|
75
|
+
onDisk = stat.isDirectory();
|
|
76
|
+
}
|
|
77
|
+
catch { /* missing */ }
|
|
78
|
+
if (!onDisk && scope === "project") {
|
|
79
|
+
try {
|
|
80
|
+
const userTarget = recommendedSkillTarget(root, id, "user");
|
|
81
|
+
const stat = await lstat(userTarget);
|
|
82
|
+
if (stat.isDirectory()) {
|
|
83
|
+
return { id, name, installed: true, enabled: desired, scope: "user" };
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
catch { /* missing */ }
|
|
87
|
+
}
|
|
88
|
+
return { id, name, installed: onDisk, enabled: onDisk && desired, scope };
|
|
89
|
+
}
|
|
90
|
+
}));
|
|
91
|
+
}
|
|
92
|
+
/** Resolve MCP installed/enabled from config files + recommendation desire. */
|
|
93
|
+
async function panelRecommendedMcpStatuses(root) {
|
|
94
|
+
const state = await readMergedRecommendationState(root);
|
|
95
|
+
const catalog = await recommendedMcps();
|
|
96
|
+
const configured = await new RuntimeMcpRegistry().listUserConfigs(root);
|
|
97
|
+
const byId = new Map();
|
|
98
|
+
for (const entry of configured) {
|
|
99
|
+
const previous = byId.get(entry.id);
|
|
100
|
+
if (!previous || entry.enabled)
|
|
101
|
+
byId.set(entry.id, { enabled: entry.enabled });
|
|
102
|
+
}
|
|
103
|
+
return (catalog.items ?? []).map((item) => {
|
|
104
|
+
const id = String(item.id ?? "");
|
|
105
|
+
const name = typeof item.name === "string" ? item.name : id;
|
|
106
|
+
const installed = byId.has(id);
|
|
107
|
+
const desired = state.mcp.includes(id);
|
|
108
|
+
// Match TUI recommended rows: installed = config present; enabled = desire.
|
|
109
|
+
return { id, name, installed, enabled: installed && desired };
|
|
110
|
+
});
|
|
111
|
+
}
|
|
56
112
|
/** Bundled capabilities, listed separately from the downloadable ecosystem. */
|
|
57
113
|
const CORE_SKILLS = [
|
|
58
114
|
{ id: "academic-grill", name: "学术追问" },
|
|
@@ -404,7 +460,7 @@ async function panelStats(root) {
|
|
|
404
460
|
const files = await listProjectFiles(root);
|
|
405
461
|
return { schemaVersion: "psyclaw/panel-stats/v1", runs: (await listRuns(root)).length, evidence: await countLines(paths.evidence), claims: await countLines(paths.claims), auditEvents: await countLines(paths.audit), trackedFiles: files.length, outputs: files.filter((path) => path.startsWith("outputs/")).length, generatedAt: new Date().toISOString() };
|
|
406
462
|
}
|
|
407
|
-
const PANEL_FILE_ROOTS = ["notes/", "paper/", "docs/", "outputs/", "logs/"];
|
|
463
|
+
const PANEL_FILE_ROOTS = ["notes/", "paper/", "docs/", "outputs/", "logs/", "analysis/results/"];
|
|
408
464
|
const PANEL_FILE_EXTENSIONS = /\.(md|markdown|txt|json|csv)$/i;
|
|
409
465
|
function panelFileAllowed(relative) {
|
|
410
466
|
const normalized = relative.replaceAll("\\", "/");
|
|
@@ -1232,16 +1288,11 @@ export function createPanelServer(root, options = {}) {
|
|
|
1232
1288
|
return;
|
|
1233
1289
|
}
|
|
1234
1290
|
if (url.pathname === "/api/enabled-capabilities") {
|
|
1235
|
-
const state = await readRecommendationState(root);
|
|
1236
1291
|
const disabled = await readDisabledCapabilities(root);
|
|
1237
|
-
const [
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
const catalog = kind === "skill" ? skills : mcps;
|
|
1242
|
-
const item = (catalog.items ?? []).find((candidate) => candidate.id === id);
|
|
1243
|
-
return typeof item?.name === "string" ? item.name : id;
|
|
1244
|
-
};
|
|
1292
|
+
const [skillStatuses, mcpStatuses] = await Promise.all([
|
|
1293
|
+
panelRecommendedSkillStatuses(root),
|
|
1294
|
+
panelRecommendedMcpStatuses(root),
|
|
1295
|
+
]);
|
|
1245
1296
|
// User-installed (non-catalog) skills from the expanded discovery
|
|
1246
1297
|
// roots, with their individual enable state.
|
|
1247
1298
|
const userSkillState = await readUserSkillState(root);
|
|
@@ -1252,6 +1303,7 @@ export function createPanelServer(root, options = {}) {
|
|
|
1252
1303
|
description: skill.description,
|
|
1253
1304
|
path: skill.path,
|
|
1254
1305
|
enabled: !disabledUserSkills.has(skill.name),
|
|
1306
|
+
installed: true,
|
|
1255
1307
|
}));
|
|
1256
1308
|
response.writeHead(200, { "content-type": "application/json" });
|
|
1257
1309
|
response.end(JSON.stringify({
|
|
@@ -1259,9 +1311,10 @@ export function createPanelServer(root, options = {}) {
|
|
|
1259
1311
|
coreSkills: CORE_SKILLS.map((skill) => ({
|
|
1260
1312
|
...skill,
|
|
1261
1313
|
enabled: "locked" in skill && skill.locked ? true : !disabled.coreSkills.includes(skill.id),
|
|
1314
|
+
installed: true,
|
|
1262
1315
|
})),
|
|
1263
|
-
skills:
|
|
1264
|
-
mcp:
|
|
1316
|
+
skills: skillStatuses,
|
|
1317
|
+
mcp: mcpStatuses,
|
|
1265
1318
|
localSkills,
|
|
1266
1319
|
}));
|
|
1267
1320
|
return;
|
|
@@ -1304,7 +1357,15 @@ export function createPanelServer(root, options = {}) {
|
|
|
1304
1357
|
}
|
|
1305
1358
|
if (url.pathname === "/api/crosscheck") {
|
|
1306
1359
|
if (request.method === "GET") {
|
|
1307
|
-
const
|
|
1360
|
+
const existing = await loadVerifyChecklist(root);
|
|
1361
|
+
const defaults = defaultVerifyChecklist();
|
|
1362
|
+
const checklist = {
|
|
1363
|
+
...existing,
|
|
1364
|
+
items: [
|
|
1365
|
+
...existing.items,
|
|
1366
|
+
...defaults.items.filter((item) => !existing.items.some((row) => row.id === item.id)),
|
|
1367
|
+
],
|
|
1368
|
+
};
|
|
1308
1369
|
response.writeHead(200, { "content-type": "application/json" });
|
|
1309
1370
|
response.end(JSON.stringify({ schemaVersion: "psyclaw/crosscheck/v1", checklist }));
|
|
1310
1371
|
return;
|
|
@@ -1552,14 +1613,29 @@ export function createPanelServer(root, options = {}) {
|
|
|
1552
1613
|
response.end(JSON.stringify({ schemaVersion: "psyclaw/local-skill-state-receipt/v1", ok: true, id, enabled: body.enabled, disabled: state.disabled }));
|
|
1553
1614
|
return;
|
|
1554
1615
|
}
|
|
1555
|
-
const state = await
|
|
1616
|
+
const state = await readCanonicalRecommendationState(root);
|
|
1617
|
+
if (kind === "skills" && body.enabled) {
|
|
1618
|
+
const scope = state.skillScopes?.[id] ?? "project";
|
|
1619
|
+
try {
|
|
1620
|
+
await validateModelInstalledRecommendedSkill(root, id, scope);
|
|
1621
|
+
}
|
|
1622
|
+
catch {
|
|
1623
|
+
throw new Error(`Skill 尚未安装到本地,无法启用:${id}。请先安装或重新安装。`);
|
|
1624
|
+
}
|
|
1625
|
+
}
|
|
1626
|
+
if (kind === "mcp" && body.enabled) {
|
|
1627
|
+
const configs = await new RuntimeMcpRegistry().listUserConfigs(root);
|
|
1628
|
+
if (!configs.some((entry) => entry.id === id)) {
|
|
1629
|
+
throw new Error(`MCP 尚未安装到本地,无法启用:${id}。请先安装或重新安装。`);
|
|
1630
|
+
}
|
|
1631
|
+
}
|
|
1556
1632
|
const values = new Set(state[kind]);
|
|
1557
1633
|
if (body.enabled)
|
|
1558
1634
|
values.add(id);
|
|
1559
1635
|
else
|
|
1560
1636
|
values.delete(id);
|
|
1561
1637
|
state[kind] = [...values];
|
|
1562
|
-
await
|
|
1638
|
+
await saveRecommendationState(root, state);
|
|
1563
1639
|
response.writeHead(200, { "content-type": "application/json" });
|
|
1564
1640
|
response.end(JSON.stringify({ schemaVersion: "psyclaw/recommendation-state-receipt/v1", ok: true, state }));
|
|
1565
1641
|
return;
|