psyclaw 0.27.12 → 0.27.14
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 +11 -5
- package/dist/apps/panel/index.html +1 -1
- package/dist/src/adapters/pi/extension.js +28 -42
- package/dist/src/adapters/pi/extension.js.map +1 -1
- package/dist/src/analysis/hooks.js +12 -12
- package/dist/src/analysis/hooks.js.map +1 -1
- package/dist/src/branding.js +2 -2
- package/dist/src/branding.js.map +1 -1
- package/dist/src/cli.js +1 -12
- package/dist/src/cli.js.map +1 -1
- package/dist/src/index.d.ts +0 -1
- package/dist/src/index.js +0 -1
- package/dist/src/index.js.map +1 -1
- package/dist/src/integrations/mcp-runtime.js +3 -1
- package/dist/src/integrations/mcp-runtime.js.map +1 -1
- package/dist/src/panel/server.js +4 -0
- package/dist/src/panel/server.js.map +1 -1
- package/dist/src/setup.js +32 -10
- package/dist/src/setup.js.map +1 -1
- package/dist/src/skills/contracts.d.ts +4 -4
- package/dist/src/skills/preflight.d.ts +3 -3
- package/dist/src/skills/preflight.js +3 -3
- package/dist/src/skills/recommended.d.ts +1 -0
- package/dist/src/skills/recommended.js +9 -8
- package/dist/src/skills/recommended.js.map +1 -1
- package/dist/src/skills/registry.js +23 -37
- package/dist/src/skills/registry.js.map +1 -1
- package/dist/src/style/cli-ui.js +2 -2
- package/dist/src/style/cli-ui.js.map +1 -1
- package/dist/src/tui/provider-picker.js +3 -3
- package/dist/src/tui/provider-picker.js.map +1 -1
- package/dist/src/updates/update.js +3 -3
- package/dist/src/updates/update.js.map +1 -1
- package/dist/src/wizard.js +8 -4
- package/dist/src/wizard.js.map +1 -1
- package/package.json +1 -1
- package/skills/recommended/catalog.json +47 -0
- package/dist/src/telemetry/export.d.ts +0 -58
- package/dist/src/telemetry/export.js +0 -428
- package/dist/src/telemetry/export.js.map +0 -1
package/README.md
CHANGED
|
@@ -2,20 +2,26 @@
|
|
|
2
2
|
|
|
3
3
|
PsyClaw 是面向社会科学研究的智能体工作台。它把研究项目、证据来源、Claim-Evidence 账本、完整性门禁、人工裁决、可恢复工作流和本地面板接到内置运行时上;它不替代统计软件,也不会把未经核验的引用、结果或审稿意见写成事实。
|
|
4
4
|
|
|
5
|
-
当前版本:`0.27.
|
|
5
|
+
当前版本:`0.27.14`。正式命令、用户配置目录和后续发布统一使用 `psyclaw`。
|
|
6
6
|
|
|
7
7
|
## 安装
|
|
8
8
|
|
|
9
9
|
需要 Node.js `>=22.19.0`。官方 npm 源:
|
|
10
10
|
|
|
11
11
|
```powershell
|
|
12
|
-
npm install -g psyclaw@0.27.
|
|
12
|
+
npm install -g psyclaw@0.27.14
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
如果本机 npm 配置把 registry 误写成带有 `~/` 的地址,请显式指定官方源:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm install -g psyclaw@0.27.14 --registry=https://registry.npmjs.org/
|
|
13
19
|
```
|
|
14
20
|
|
|
15
21
|
中国大陆网络较慢或无法访问官方源时:
|
|
16
22
|
|
|
17
23
|
```powershell
|
|
18
|
-
npm install -g psyclaw@0.27.
|
|
24
|
+
npm install -g psyclaw@0.27.14 --registry=https://registry.npmmirror.com
|
|
19
25
|
```
|
|
20
26
|
|
|
21
27
|
确认命令入口:
|
|
@@ -63,7 +69,7 @@ psyclaw brief
|
|
|
63
69
|
| 扫描本机其他 Agent | `psyclaw agents` |
|
|
64
70
|
| 查看 PsyClaw 和内置运行时更新 | `psyclaw check-updates` |
|
|
65
71
|
| 更新 PsyClaw 和内置运行时 | `psyclaw update` |
|
|
66
|
-
|
|
|
72
|
+
| 导出会话 | 使用 Pi 内置的 `/export` |
|
|
67
73
|
| 打开科研面板 | 在对话中输入 `/panel` |
|
|
68
74
|
| 查看或切换 Provider | 在对话中输入 `/provider` 或 `/provider <id>` |
|
|
69
75
|
| 管理启动横幅宠物 | `/pet status`、`/pet on`、`/pet off`(默认关闭) |
|
|
@@ -82,7 +88,7 @@ psyclaw brief
|
|
|
82
88
|
/export
|
|
83
89
|
```
|
|
84
90
|
|
|
85
|
-
|
|
91
|
+
`/export` 由 Pi 内置命令提供。PsyClaw 不重复实现会话导出或 Trace 运行时。
|
|
86
92
|
|
|
87
93
|
## 开发者本地安装
|
|
88
94
|
|
|
@@ -65,7 +65,7 @@ function selectTree(button){document.querySelectorAll(".tree-item").forEach(item
|
|
|
65
65
|
async function openFile(path,button){selectTree(button);$("viewer-path").textContent=path;$("viewer-kind").textContent="读取中";$("viewer-body").className="viewer-body empty";$("viewer-body").textContent="读取中...";try{const file=await json(`/api/project-file?path=${encodeURIComponent(path)}`);$("viewer-kind").textContent=file.format;$("viewer-body").className="viewer-body";$("viewer-body").innerHTML=file.format==="markdown"?`<article class="markdown">${renderMarkdown(file.content)}</article>`:`<pre class="raw">${esc(file.format==="json"?JSON.stringify(JSON.parse(file.content),null,2):file.content)}</pre>`}catch(error){$("viewer-body").className="viewer-body empty";$("viewer-body").textContent=error.message}}
|
|
66
66
|
async function loadProject(){const files=await json("/api/project-files");state.files=files.files||[];renderTree();const goal=document.querySelector('[data-file="notes/goal.md"]')||document.querySelector("[data-file]");if(goal)goal.click()}
|
|
67
67
|
|
|
68
|
-
async function loadEcosystem(){try{const[skills,mcps,enabled]=await Promise.all([json("/api/recommended-skills"),json("/api/recommended-mcps"),json("/api/enabled-capabilities")]);const skillSet=new Set((enabled.skills||[]).map(item=>item.id)),mcpSet=new Set((enabled.mcp||[]).map(item=>item.id));state.eco=[...(enabled.coreSkills||[]).map(item=>({...item,kind:"core",description:"PsyClaw 内置 Skill",sourceRef:"bundled",enabled:item.enabled,installed:true})),...(skills.items||[]).map(item=>({...item,kind:"skill",enabled:skillSet.has(item.id),installed:skillSet.has(item.id)})),...(enabled.localSkills||[]).map(item=>({...item,kind:"local",sourceRef:item.path||"用户目录",installed:true})),...(mcps.items||[]).map(item=>({...item,kind:"mcp",enabled:mcpSet.has(item.id),installed:mcpSet.has(item.id)})),...(skills.externalTools||[]).map(item=>({...item,kind:"external",enabled:false,installed:false}))];renderEcosystem()}catch(error){$("ecosystem-body").innerHTML=`<tr><td colspan="5">${esc(error.message)}</td></tr>`}}
|
|
68
|
+
async function loadEcosystem(){try{const[skills,mcps,enabled]=await Promise.all([json("/api/recommended-skills"),json("/api/recommended-mcps"),json("/api/enabled-capabilities")]);const skillSet=new Set((enabled.skills||[]).map(item=>item.id)),mcpSet=new Set((enabled.mcp||[]).map(item=>item.id));state.eco=[...(enabled.coreSkills||[]).map(item=>({...item,kind:"core",description:"PsyClaw 内置 Skill",sourceRef:"bundled",enabled:item.enabled,installed:true})),...(skills.items||[]).map(item=>({...item,kind:"skill",enabled:skillSet.has(item.id),installed:skillSet.has(item.id)})),...(skills.plugins||[]).map(item=>({...item,kind:"plugin",enabled:false,installed:false})),...(enabled.localSkills||[]).map(item=>({...item,kind:"local",sourceRef:item.path||"用户目录",installed:true})),...(mcps.items||[]).map(item=>({...item,kind:"mcp",enabled:mcpSet.has(item.id),installed:mcpSet.has(item.id)})),...(skills.externalTools||[]).map(item=>({...item,kind:"external",enabled:false,installed:false}))];renderEcosystem()}catch(error){$("ecosystem-body").innerHTML=`<tr><td colspan="5">${esc(error.message)}</td></tr>`}}
|
|
69
69
|
function renderEcosystem(){const query=$("eco-search").value.trim().toLowerCase(),kind=$("eco-kind").value,status=$("eco-status").value;const items=state.eco.filter(item=>(kind==="all"||item.kind===kind)&&(status==="all"||(status==="enabled"?item.enabled:!item.enabled))&&`${item.name} ${item.description||""}`.toLowerCase().includes(query));$("ecosystem-body").innerHTML=items.map(item=>`<tr><td><div class="name">${esc(item.name)}</div><div class="subtle">${esc(item.description||"")}</div></td><td>${esc(item.kind==="core"?"Core Skill":item.kind==="local"?"用户 Skill":item.kind.toUpperCase())}</td><td>${/^https?:/.test(item.sourceRef||"")?`<a href="${esc(item.sourceRef)}" target="_blank" rel="noreferrer">来源</a>`:esc(item.sourceRef||"bundled")}</td><td><span class="tag ${item.enabled?"ok":""}">${item.enabled?"已启用":item.kind==="external"?"外部工具":"未启用"}</span></td><td>${item.kind==="external"?"—":item.enabled?`<button class="button small" onclick="toggleCapability('${item.kind}','${esc(item.id)}',false)">停用</button>`:item.kind==="local"?`<button class="button small primary" onclick="toggleCapability('local','${esc(item.id)}',true)">启用</button>`:`<button class="button small primary" onclick="installCapability('${item.kind}','${esc(item.id)}')">${item.kind==="core"?"启用":"安装"}</button>`}</td></tr>`).join("")||'<tr><td colspan="5">没有匹配项</td></tr>'}
|
|
70
70
|
async function toggleCapability(kind,id,enabled){await json("/api/recommendation-state",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({kind:kind==="core"?"core-skill":kind==="skill"?"skills":kind==="local"?"local-skill":kind,id,enabled})});await loadEcosystem()}
|
|
71
71
|
async function installCapability(kind,id){if(kind==="core"){await toggleCapability(kind,id,true);return}const scope=kind==="skill"?(confirm("安装到系统目录?\n确定:所有项目使用;取消:仅当前项目使用。")?"user":"project"):undefined;if(!confirm(`确认安装 ${id}?安装完成后需要 /reload。`))return;const result=await json("/api/install/execute",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({kind,id,scope,approved:true,actor:"researcher"})});alert(result.message||(result.ok?"安装任务已处理,请执行 /reload。":"安装未完成"));await loadEcosystem()}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { getAgentDir } from "@earendil-works/pi-coding-agent";
|
|
2
2
|
import { Type } from "typebox";
|
|
3
|
-
import { appendApproval, approvalInputDigest, approvePrimaryDocument, asProject, bootstrapProject, DEFAULT_RUN_APPROVAL_NODES,
|
|
3
|
+
import { appendApproval, approvalInputDigest, approvePrimaryDocument, asProject, bootstrapProject, DEFAULT_RUN_APPROVAL_NODES, PRIMARY_PLAN_DOCUMENTS, primaryPlanApprovalStatus, projectPaths, runOfflineBrief, runInstitutionalFulltext, runLiteratureReview, runExpertReview, runAnalysisDelegation, runWritingReview, runMetaAnalysis, createStageRunner, exportAcademicDocument, recordCitationUse, runParallelPeerReview, writeHandoff } from "../../index.js";
|
|
4
4
|
import { runPlanWithPi } from "../../orchestration/pi-executor.js";
|
|
5
5
|
import { atomicWriteFile } from "../../project/jsonl.js";
|
|
6
6
|
import { RunEventLog } from "../../panel/events.js";
|
|
@@ -14,7 +14,7 @@ import { coreSkillNames, enabledRecommendedSkillPaths, normalizeRecommendedSkill
|
|
|
14
14
|
import { SkillManagerComponent } from "../../tui/skill-manager.js";
|
|
15
15
|
import { enabledLocalSkillPaths, enabledLocalPromptPaths, readUserSkillState, scanLocalSkills, setLocalSkillEnabled, setLocalSkillsEnabled, skillNamesInPaths, installLocalSkill, userSkillId, } from "../../skills/user-skills.js";
|
|
16
16
|
import { RuntimeMcpRegistry, setUserMcpConfigEnabled, } from "../../integrations/mcp-runtime.js";
|
|
17
|
-
import { SecretInputComponent, } from "../../tui/provider-picker.js";
|
|
17
|
+
import { SecretInputComponent, ProviderPickerComponent, } from "../../tui/provider-picker.js";
|
|
18
18
|
const PARADIGMS = new Set([
|
|
19
19
|
"survey-observational",
|
|
20
20
|
"qualitative-thematic",
|
|
@@ -56,7 +56,6 @@ async function runPluginCommand(args) {
|
|
|
56
56
|
throw new Error(`不支持的 Plugin 操作:${args[0] ?? ""}`);
|
|
57
57
|
}
|
|
58
58
|
const activeAgentRuns = new Set();
|
|
59
|
-
const pendingInitApprovals = new Set();
|
|
60
59
|
const activeApprovalDialogs = new Set();
|
|
61
60
|
const CORE_SKILLS = new Set(["academic-grill", "research-intake", "evidence-capture", "citation-audit", "research-brief"]);
|
|
62
61
|
function coreSkillPath(name) {
|
|
@@ -120,10 +119,14 @@ async function saveDefaultModel(provider, model) {
|
|
|
120
119
|
await atomicWriteFile(path, `${JSON.stringify({ ...settings, defaultProvider: provider, defaultModel: model }, null, 2)}\n`);
|
|
121
120
|
}
|
|
122
121
|
async function pickProviderItem(ctx, title, items) {
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
122
|
+
if (typeof ctx.ui.custom !== "function") {
|
|
123
|
+
const labels = items.map((item) => `${item.current ? "* " : ""}${item.label} (${item.id})`);
|
|
124
|
+
const selected = await ctx.ui.select(title, labels, { timeout: 60_000 });
|
|
125
|
+
const index = selected === undefined ? -1 : labels.indexOf(selected);
|
|
126
|
+
return index < 0 ? undefined : items[index].id;
|
|
127
|
+
}
|
|
128
|
+
const selected = await ctx.ui.custom((tui, theme, keybindings, done) => new ProviderPickerComponent(title, items, tui, theme, keybindings, done));
|
|
129
|
+
return selected.type === "select" ? selected.id : undefined;
|
|
127
130
|
}
|
|
128
131
|
async function promptProviderKey(ctx, providerName, envName) {
|
|
129
132
|
const result = await ctx.ui.custom((tui, theme, keybindings, done) => new SecretInputComponent(`配置 ${providerName}`, envName, tui, theme, keybindings, done));
|
|
@@ -354,13 +357,14 @@ async function recommendedItems(kind) {
|
|
|
354
357
|
const value = JSON.parse(await readFile(path, "utf8"));
|
|
355
358
|
return {
|
|
356
359
|
items: Array.isArray(value.items) ? value.items.filter((item) => Boolean(item && typeof item === "object")) : [],
|
|
360
|
+
plugins: Array.isArray(value.plugins) ? value.plugins.filter((item) => Boolean(item && typeof item === "object")) : [],
|
|
357
361
|
externalTools: Array.isArray(value.externalTools) ? value.externalTools.filter((item) => Boolean(item && typeof item === "object")) : [],
|
|
358
362
|
installPrep: Array.isArray(value.installPrep) ? value.installPrep.filter((item) => Boolean(item && typeof item === "object")) : [],
|
|
359
363
|
};
|
|
360
364
|
}
|
|
361
365
|
catch { /* try next candidate */ }
|
|
362
366
|
}
|
|
363
|
-
return { items: [], externalTools: [], installPrep: [] };
|
|
367
|
+
return { items: [], plugins: [], externalTools: [], installPrep: [] };
|
|
364
368
|
}
|
|
365
369
|
function skillScopeLabel(scope) {
|
|
366
370
|
if (scope === "local")
|
|
@@ -1000,13 +1004,6 @@ export default function psyclawExtension(pi) {
|
|
|
1000
1004
|
});
|
|
1001
1005
|
return approved ? undefined : { block: true, terminate: true, reason: "用户拒绝该执行步骤或审批超时" };
|
|
1002
1006
|
});
|
|
1003
|
-
if (!legacyTestApi && typeof pi.on === "function")
|
|
1004
|
-
pi.on("agent_end", async (_event, ctx) => {
|
|
1005
|
-
if (!pendingInitApprovals.has(ctx.cwd) || !(await planDocumentsReady(ctx.cwd)))
|
|
1006
|
-
return;
|
|
1007
|
-
pendingInitApprovals.delete(ctx.cwd);
|
|
1008
|
-
await reviewPrimaryDocuments(ctx);
|
|
1009
|
-
});
|
|
1010
1007
|
pi.registerCommand("init", {
|
|
1011
1008
|
description: "初始化可追溯的研究项目",
|
|
1012
1009
|
handler: async (args, ctx) => {
|
|
@@ -1014,9 +1011,8 @@ export default function psyclawExtension(pi) {
|
|
|
1014
1011
|
const parsed = parseInitArgs(args);
|
|
1015
1012
|
const project = await bootstrapProject({ root: ctx.cwd, ...parsed });
|
|
1016
1013
|
if (typeof pi.sendUserMessage === "function") {
|
|
1017
|
-
pendingInitApprovals.add(ctx.cwd);
|
|
1018
1014
|
pi.sendUserMessage(academicGrillRequest(project.goal, "init"), ctx.isIdle() ? {} : { deliverAs: "followUp" });
|
|
1019
|
-
ctx.ui.notify(`研究项目已初始化:${project.id}(${project.paradigm}
|
|
1015
|
+
ctx.ui.notify(`研究项目已初始化:${project.id}(${project.paradigm})。当前保持普通对话;如需进入受控研究流程,请明确运行 /run。`, "info");
|
|
1020
1016
|
}
|
|
1021
1017
|
else {
|
|
1022
1018
|
ctx.ui.notify(`研究项目已初始化:${project.id}(${project.paradigm})。使用 /run 启动受控研究流程。`, "info");
|
|
@@ -1066,8 +1062,11 @@ export default function psyclawExtension(pi) {
|
|
|
1066
1062
|
const planApproval = await primaryPlanApprovalStatus(ctx.cwd);
|
|
1067
1063
|
if (!planApproval.ok) {
|
|
1068
1064
|
const missing = planApproval.documents.filter((item) => !item.approved).map((item) => item.path);
|
|
1069
|
-
|
|
1070
|
-
|
|
1065
|
+
// `/run` is the sole switch into the controlled workflow. A plan
|
|
1066
|
+
// can still be incomplete when execution starts; keep the status
|
|
1067
|
+
// visible and let the model repair it through the normal workflow
|
|
1068
|
+
// instead of forcing a separate approval command first.
|
|
1069
|
+
ctx.ui.notify(`检测到计划尚未确认或已修改:${missing.join("、")}。将由受控流程继续处理;如需先人工确认,可运行 /approve。`, "warning");
|
|
1071
1070
|
}
|
|
1072
1071
|
const available = await selectableRunSkills(ctx.cwd);
|
|
1073
1072
|
let requestedSkills = parsed.requestedSkills;
|
|
@@ -1329,8 +1328,9 @@ export default function psyclawExtension(pi) {
|
|
|
1329
1328
|
const [skills, mcps] = await Promise.all([recommendedItems("skills"), recommendedItems("mcp")]);
|
|
1330
1329
|
const skillLines = skills.items.slice(0, 8).map((item) => `Skill: ${String(item.id)} — ${String(item.name)}`);
|
|
1331
1330
|
const mcpLines = mcps.items.slice(0, 8).map((item) => `MCP: ${String(item.id)} — ${String(item.name)}`);
|
|
1331
|
+
const pluginLines = skills.plugins.slice(0, 8).map((item) => `Plugin: ${String(item.name)} — ${String(item.sourceRef ?? "请查看项目文档")}`);
|
|
1332
1332
|
const toolLines = skills.externalTools.slice(0, 8).map((item) => `外部工具: ${String(item.name)} — ${String(item.sourceRef ?? "请查看项目文档")}`);
|
|
1333
|
-
ctx.ui.notify(["推荐安装入口", "", ...skillLines, ...mcpLines, "", ...toolLines, "", "安装:/install skill|mcp <id>", "管理:/skill 或 /mcp", "外部工具不通过 Skill 安装器安装", "也可以打开 /panel 查看推荐页面"].join("\n"), "info");
|
|
1333
|
+
ctx.ui.notify(["推荐安装入口", "", ...skillLines, ...pluginLines, ...mcpLines, "", ...toolLines, "", "安装:/install skill|mcp <id>", "Plugin:/plugin install <source>", "管理:/skill 或 /mcp", "外部工具不通过 Skill 安装器安装", "也可以打开 /panel 查看推荐页面"].join("\n"), "info");
|
|
1334
1334
|
return;
|
|
1335
1335
|
}
|
|
1336
1336
|
if (kind !== "skill" && kind !== "mcp")
|
|
@@ -1415,9 +1415,16 @@ export default function psyclawExtension(pi) {
|
|
|
1415
1415
|
}
|
|
1416
1416
|
if (preset && ctx.hasUI && typeof ctx.ui.custom === "function") {
|
|
1417
1417
|
const credential = await providerCredentialSource(preset);
|
|
1418
|
-
|
|
1418
|
+
// Always show the credential step when switching a built-in
|
|
1419
|
+
// provider. An empty submission deliberately keeps an existing
|
|
1420
|
+
// environment/auth-store credential; this makes the flow explicit
|
|
1421
|
+
// and avoids silently skipping the key screen on another machine.
|
|
1422
|
+
const key = await promptProviderKey(ctx, preset.name, preset.apiKeyEnv);
|
|
1419
1423
|
if (key === undefined)
|
|
1420
1424
|
return;
|
|
1425
|
+
if (!key && credential === "missing") {
|
|
1426
|
+
throw new Error(`未找到 ${preset.apiKeyEnv};请输入 API Key 后再继续`);
|
|
1427
|
+
}
|
|
1421
1428
|
await saveProviderConfig({ ...preset, ...(key ? { apiKey: key } : {}) });
|
|
1422
1429
|
const refreshed = await Promise.race([
|
|
1423
1430
|
ctx.modelRegistry.refresh().then(() => true),
|
|
@@ -1460,27 +1467,6 @@ export default function psyclawExtension(pi) {
|
|
|
1460
1467
|
ctx.ui.notify(`启动横幅宠物已${action === "on" ? "开启" : "关闭"},下次启动生效`, "info");
|
|
1461
1468
|
},
|
|
1462
1469
|
});
|
|
1463
|
-
const traceCommand = {
|
|
1464
|
-
description: "导出包含正文与工具参数的完整使用路径,供 Langfuse 或 LangSmith 分析",
|
|
1465
|
-
handler: async (args, ctx) => {
|
|
1466
|
-
try {
|
|
1467
|
-
if (args.trim())
|
|
1468
|
-
throw new Error("Usage: /export");
|
|
1469
|
-
const result = await exportTraces({ root: ctx.cwd });
|
|
1470
|
-
ctx.ui.notify([
|
|
1471
|
-
"使用路径已导出(未上传)",
|
|
1472
|
-
`文件:${result.output}`,
|
|
1473
|
-
`轨迹:${result.traces}`,
|
|
1474
|
-
`步骤:${result.spans}`,
|
|
1475
|
-
"包含对话正文、工具参数、原始 ID 与绝对路径,便于排查各环节问题;请勿将导出文件提交到公开仓库。",
|
|
1476
|
-
].join("\n"), "info");
|
|
1477
|
-
}
|
|
1478
|
-
catch (error) {
|
|
1479
|
-
await notifyError(ctx, error);
|
|
1480
|
-
}
|
|
1481
|
-
},
|
|
1482
|
-
};
|
|
1483
|
-
pi.registerCommand("export", traceCommand);
|
|
1484
1470
|
if (typeof pi.registerTool === "function") {
|
|
1485
1471
|
pi.registerTool({
|
|
1486
1472
|
name: "psyclaw_mcp",
|