psyclaw 0.30.16 → 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 +51 -17
- package/dist/src/adapters/pi/extension.js +2 -2
- package/dist/src/adapters/pi/extension.js.map +1 -1
- package/dist/src/panel/server.js +82 -14
- 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/package.json +1 -1
package/dist/src/panel/server.js
CHANGED
|
@@ -31,9 +31,10 @@ 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 { RuntimeMcpRegistry } from "../integrations/mcp-runtime.js";
|
|
37
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";
|
|
@@ -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: "学术追问" },
|
|
@@ -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;
|
|
@@ -1560,14 +1613,29 @@ export function createPanelServer(root, options = {}) {
|
|
|
1560
1613
|
response.end(JSON.stringify({ schemaVersion: "psyclaw/local-skill-state-receipt/v1", ok: true, id, enabled: body.enabled, disabled: state.disabled }));
|
|
1561
1614
|
return;
|
|
1562
1615
|
}
|
|
1563
|
-
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
|
+
}
|
|
1564
1632
|
const values = new Set(state[kind]);
|
|
1565
1633
|
if (body.enabled)
|
|
1566
1634
|
values.add(id);
|
|
1567
1635
|
else
|
|
1568
1636
|
values.delete(id);
|
|
1569
1637
|
state[kind] = [...values];
|
|
1570
|
-
await
|
|
1638
|
+
await saveRecommendationState(root, state);
|
|
1571
1639
|
response.writeHead(200, { "content-type": "application/json" });
|
|
1572
1640
|
response.end(JSON.stringify({ schemaVersion: "psyclaw/recommendation-state-receipt/v1", ok: true, state }));
|
|
1573
1641
|
return;
|