@chorus-aidlc/chorus-openclaw-plugin 0.4.0 → 0.5.3
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 +208 -278
- package/dist/commands.d.ts +5 -0
- package/dist/commands.d.ts.map +1 -0
- package/dist/commands.js +147 -0
- package/dist/commands.js.map +1 -0
- package/dist/config.d.ts +38 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +57 -0
- package/dist/config.js.map +1 -0
- package/dist/event-router.d.ts +55 -0
- package/dist/event-router.d.ts.map +1 -0
- package/dist/event-router.js +157 -0
- package/dist/event-router.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +108 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp-client.d.ts +37 -0
- package/dist/mcp-client.d.ts.map +1 -0
- package/dist/mcp-client.js +137 -0
- package/dist/mcp-client.js.map +1 -0
- package/dist/mcp-registration.d.ts +25 -0
- package/dist/mcp-registration.d.ts.map +1 -0
- package/dist/mcp-registration.js +93 -0
- package/dist/mcp-registration.js.map +1 -0
- package/dist/sse-listener.d.ts +37 -0
- package/dist/sse-listener.d.ts.map +1 -0
- package/dist/sse-listener.js +152 -0
- package/dist/sse-listener.js.map +1 -0
- package/dist/wake.d.ts +67 -0
- package/dist/wake.d.ts.map +1 -0
- package/dist/wake.js +234 -0
- package/dist/wake.js.map +1 -0
- package/openclaw.plugin.json +13 -12
- package/package.json +23 -5
- package/skills/brainstorm/SKILL.md +163 -0
- package/skills/chorus/SKILL.md +114 -97
- package/skills/develop/SKILL.md +197 -52
- package/skills/idea/SKILL.md +136 -150
- package/skills/openspec-aware/SKILL.md +425 -0
- package/skills/proposal/SKILL.md +162 -153
- package/skills/proposal-reviewer/SKILL.md +118 -0
- package/skills/quick-dev/SKILL.md +34 -10
- package/skills/review/SKILL.md +109 -35
- package/skills/task-reviewer/SKILL.md +113 -0
- package/skills/yolo/SKILL.md +501 -0
- package/src/commands.ts +138 -71
- package/src/config.ts +23 -10
- package/src/event-router.ts +46 -54
- package/src/index.ts +56 -83
- package/src/mcp-client.ts +17 -0
- package/src/mcp-registration.ts +142 -0
- package/src/openclaw-sdk.d.ts +95 -0
- package/src/wake.ts +310 -0
- package/src/tools/admin-tools.ts +0 -126
- package/src/tools/common-tools.ts +0 -575
- package/src/tools/dev-tools.ts +0 -105
- package/src/tools/pm-tools.ts +0 -411
package/dist/commands.js
ADDED
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
// ===== Skill catalog =====
|
|
2
|
+
//
|
|
3
|
+
// All 9 skills bundled with the Chorus OpenClaw plugin
|
|
4
|
+
// (packages/openclaw-plugin/skills/*/SKILL.md). The `name` here matches each
|
|
5
|
+
// skill's SKILL.md frontmatter `name`, which is exactly the slash command
|
|
6
|
+
// OpenClaw exposes (see invocation hint below).
|
|
7
|
+
const PLUGIN_SKILLS = [
|
|
8
|
+
{ name: "chorus", description: "Platform overview, common tools, setup, and workflow routing" },
|
|
9
|
+
{ name: "idea", description: "Claim ideas, run elaboration rounds, prepare for proposal" },
|
|
10
|
+
{ name: "brainstorm", description: "Optional divergent-then-convergent dialogue for fuzzy ideas" },
|
|
11
|
+
{ name: "proposal", description: "Create proposals with document & task drafts, manage the dependency DAG" },
|
|
12
|
+
{ name: "develop", description: "Claim tasks, report work, manage sessions, run wave-based execution" },
|
|
13
|
+
{ name: "quick-dev", description: "Skip Idea→Proposal — create tasks directly, execute, verify" },
|
|
14
|
+
{ name: "review", description: "Approve/reject proposals, verify tasks, project governance" },
|
|
15
|
+
{ name: "yolo", description: "Full-auto AI-DLC pipeline — from prompt to done" },
|
|
16
|
+
{ name: "openspec-aware", description: "Opt-in OpenSpec authoring for PM workflows when the openspec CLI is present" },
|
|
17
|
+
];
|
|
18
|
+
// ===== Formatting helpers =====
|
|
19
|
+
// OpenClaw invokes a skill via a BARE slash command of its SKILL.md `name`
|
|
20
|
+
// (verified against OpenClaw 2026.5.30 docs/tools/skills.md: "the skill's
|
|
21
|
+
// visible name, slash command ... come from SKILL.md frontmatter name ... a
|
|
22
|
+
// nested skill with name: research is still invoked as /research"). OpenClaw
|
|
23
|
+
// does NOT use Claude Code's `/chorus:<skill>` namespace form.
|
|
24
|
+
function skillInvocation(name) {
|
|
25
|
+
return `/${name}`;
|
|
26
|
+
}
|
|
27
|
+
function formatSkillsList() {
|
|
28
|
+
const nameWidth = Math.max(...PLUGIN_SKILLS.map((s) => skillInvocation(s.name).length));
|
|
29
|
+
const lines = PLUGIN_SKILLS.map((s) => ` ${skillInvocation(s.name).padEnd(nameWidth)} ${s.description}`);
|
|
30
|
+
return [
|
|
31
|
+
`Chorus skills (${PLUGIN_SKILLS.length}):`,
|
|
32
|
+
...lines,
|
|
33
|
+
"",
|
|
34
|
+
"Invoke a skill with its slash command, e.g. /develop or /idea.",
|
|
35
|
+
].join("\n");
|
|
36
|
+
}
|
|
37
|
+
// Sum a count across every project bucket in a tracker Record.
|
|
38
|
+
function countTracker(tracker, key) {
|
|
39
|
+
if (!tracker)
|
|
40
|
+
return 0;
|
|
41
|
+
return Object.values(tracker).reduce((total, project) => {
|
|
42
|
+
const items = key === "ideas" ? project.ideas : project.tasks;
|
|
43
|
+
return total + (items?.length ?? 0);
|
|
44
|
+
}, 0);
|
|
45
|
+
}
|
|
46
|
+
function formatStatus(checkin, connectionStatus) {
|
|
47
|
+
const ideaCount = countTracker(checkin?.ideaTracker, "ideas");
|
|
48
|
+
const lines = [
|
|
49
|
+
`Connection: ${connectionStatus}`,
|
|
50
|
+
`Agent: ${checkin?.agent?.name ?? "unknown"}`,
|
|
51
|
+
`Assigned ideas: ${ideaCount}`,
|
|
52
|
+
`Notifications: ${checkin?.notifications?.unread ?? 0} unread`,
|
|
53
|
+
`Skills: ${PLUGIN_SKILLS.map((s) => s.name).join(", ")}`,
|
|
54
|
+
];
|
|
55
|
+
return lines.join("\n");
|
|
56
|
+
}
|
|
57
|
+
function formatTaskList(taskTracker) {
|
|
58
|
+
const lines = [];
|
|
59
|
+
let total = 0;
|
|
60
|
+
for (const project of Object.values(taskTracker ?? {})) {
|
|
61
|
+
for (const t of project.tasks ?? []) {
|
|
62
|
+
total += 1;
|
|
63
|
+
const ac = t.ac && typeof t.ac.total === "number" && t.ac.total > 0
|
|
64
|
+
? ` (AC ${t.ac.passed ?? 0}/${t.ac.total})`
|
|
65
|
+
: "";
|
|
66
|
+
lines.push(`[${t.status}] [${t.priority}] ${t.title} (${project.name})${ac}`);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
if (total === 0) {
|
|
70
|
+
return "No assigned tasks.";
|
|
71
|
+
}
|
|
72
|
+
return `Assigned tasks (${total}):\n${lines.join("\n")}`;
|
|
73
|
+
}
|
|
74
|
+
function formatIdeaList(ideaTracker) {
|
|
75
|
+
const lines = [];
|
|
76
|
+
let total = 0;
|
|
77
|
+
for (const project of Object.values(ideaTracker ?? {})) {
|
|
78
|
+
for (const i of project.ideas ?? []) {
|
|
79
|
+
total += 1;
|
|
80
|
+
lines.push(`[${i.status}] ${i.title} (${project.name})`);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
if (total === 0) {
|
|
84
|
+
return "No assigned ideas.";
|
|
85
|
+
}
|
|
86
|
+
return `Assigned ideas (${total}):\n${lines.join("\n")}`;
|
|
87
|
+
}
|
|
88
|
+
const HELP_TEXT = [
|
|
89
|
+
"Chorus commands:",
|
|
90
|
+
" /chorus Show connection status and summary",
|
|
91
|
+
" /chorus status Same as above",
|
|
92
|
+
" /chorus tasks List assigned tasks",
|
|
93
|
+
" /chorus ideas List assigned ideas",
|
|
94
|
+
" /chorus skills List available Chorus skills",
|
|
95
|
+
].join("\n");
|
|
96
|
+
function errorText(prefix, err) {
|
|
97
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
98
|
+
return `${prefix}: ${detail}`;
|
|
99
|
+
}
|
|
100
|
+
// ===== Registration =====
|
|
101
|
+
export function registerChorusCommands(api, mcpClient, getStatus) {
|
|
102
|
+
api.registerCommand({
|
|
103
|
+
name: "chorus",
|
|
104
|
+
description: "Chorus plugin commands: status, tasks, ideas, skills",
|
|
105
|
+
acceptsArgs: true,
|
|
106
|
+
async handler(ctx) {
|
|
107
|
+
const sub = (ctx.args ?? "").trim().toLowerCase();
|
|
108
|
+
// /chorus or /chorus status — connection + checkin summary via slim client.
|
|
109
|
+
if (!sub || sub === "status") {
|
|
110
|
+
try {
|
|
111
|
+
const checkin = (await mcpClient.callTool("chorus_checkin", {}));
|
|
112
|
+
return { text: formatStatus(checkin, getStatus()) };
|
|
113
|
+
}
|
|
114
|
+
catch (err) {
|
|
115
|
+
return { text: errorText("Failed to check in", err), isError: true };
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
// /chorus tasks — assigned tasks via chorus_get_my_assignments.
|
|
119
|
+
if (sub === "tasks") {
|
|
120
|
+
try {
|
|
121
|
+
const data = (await mcpClient.callTool("chorus_get_my_assignments", {}));
|
|
122
|
+
return { text: formatTaskList(data?.taskTracker) };
|
|
123
|
+
}
|
|
124
|
+
catch (err) {
|
|
125
|
+
return { text: errorText("Failed to fetch tasks", err), isError: true };
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
// /chorus ideas — assigned ideas via chorus_get_my_assignments.
|
|
129
|
+
if (sub === "ideas") {
|
|
130
|
+
try {
|
|
131
|
+
const data = (await mcpClient.callTool("chorus_get_my_assignments", {}));
|
|
132
|
+
return { text: formatIdeaList(data?.ideaTracker) };
|
|
133
|
+
}
|
|
134
|
+
catch (err) {
|
|
135
|
+
return { text: errorText("Failed to fetch ideas", err), isError: true };
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
// /chorus skills — static catalog of all 9 bundled skills.
|
|
139
|
+
if (sub === "skills") {
|
|
140
|
+
return { text: formatSkillsList() };
|
|
141
|
+
}
|
|
142
|
+
// Unknown subcommand → help.
|
|
143
|
+
return { text: HELP_TEXT };
|
|
144
|
+
},
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
//# sourceMappingURL=commands.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"commands.js","sourceRoot":"","sources":["../src/commands.ts"],"names":[],"mappings":"AAwDA,4BAA4B;AAC5B,EAAE;AACF,uDAAuD;AACvD,6EAA6E;AAC7E,0EAA0E;AAC1E,gDAAgD;AAEhD,MAAM,aAAa,GAAG;IACpB,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,8DAA8D,EAAE;IAC/F,EAAE,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,2DAA2D,EAAE;IAC1F,EAAE,IAAI,EAAE,YAAY,EAAE,WAAW,EAAE,6DAA6D,EAAE;IAClG,EAAE,IAAI,EAAE,UAAU,EAAE,WAAW,EAAE,yEAAyE,EAAE;IAC5G,EAAE,IAAI,EAAE,SAAS,EAAE,WAAW,EAAE,qEAAqE,EAAE;IACvG,EAAE,IAAI,EAAE,WAAW,EAAE,WAAW,EAAE,6DAA6D,EAAE;IACjG,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,4DAA4D,EAAE;IAC7F,EAAE,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,iDAAiD,EAAE;IAChF,EAAE,IAAI,EAAE,gBAAgB,EAAE,WAAW,EAAE,6EAA6E,EAAE;CAC9G,CAAC;AAEX,iCAAiC;AAEjC,2EAA2E;AAC3E,0EAA0E;AAC1E,4EAA4E;AAC5E,6EAA6E;AAC7E,+DAA+D;AAC/D,SAAS,eAAe,CAAC,IAAY;IACnC,OAAO,IAAI,IAAI,EAAE,CAAC;AACpB,CAAC;AAED,SAAS,gBAAgB;IACvB,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,eAAe,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC;IACxF,MAAM,KAAK,GAAG,aAAa,CAAC,GAAG,CAC7B,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,eAAe,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,WAAW,EAAE,CAC1E,CAAC;IACF,OAAO;QACL,kBAAkB,aAAa,CAAC,MAAM,IAAI;QAC1C,GAAG,KAAK;QACR,EAAE;QACF,gEAAgE;KACjE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC;AAED,+DAA+D;AAC/D,SAAS,YAAY,CACnB,OAAiE,EACjE,GAAsB;IAEtB,IAAI,CAAC,OAAO;QAAE,OAAO,CAAC,CAAC;IACvB,OAAO,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE;QACtD,MAAM,KAAK,GAAG,GAAG,KAAK,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC;QAC9D,OAAO,KAAK,GAAG,CAAC,KAAK,EAAE,MAAM,IAAI,CAAC,CAAC,CAAC;IACtC,CAAC,EAAE,CAAC,CAAC,CAAC;AACR,CAAC;AAED,SAAS,YAAY,CAAC,OAAwB,EAAE,gBAAwB;IACtE,MAAM,SAAS,GAAG,YAAY,CAAC,OAAO,EAAE,WAAW,EAAE,OAAO,CAAC,CAAC;IAC9D,MAAM,KAAK,GAAa;QACtB,eAAe,gBAAgB,EAAE;QACjC,UAAU,OAAO,EAAE,KAAK,EAAE,IAAI,IAAI,SAAS,EAAE;QAC7C,mBAAmB,SAAS,EAAE;QAC9B,kBAAkB,OAAO,EAAE,aAAa,EAAE,MAAM,IAAI,CAAC,SAAS;QAC9D,WAAW,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE;KACzD,CAAC;IACF,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC;AAED,SAAS,cAAc,CAAC,WAA2D;IACjF,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,KAAK,MAAM,OAAO,IAAI,MAAM,CAAC,MAAM,CAAC,WAAW,IAAI,EAAE,CAAC,EAAE,CAAC;QACvD,KAAK,MAAM,CAAC,IAAI,OAAO,CAAC,KAAK,IAAI,EAAE,EAAE,CAAC;YACpC,KAAK,IAAI,CAAC,CAAC;YACX,MAAM,EAAE,GACN,CAAC,CAAC,EAAE,IAAI,OAAO,CAAC,CAAC,EAAE,CAAC,KAAK,KAAK,QAAQ,IAAI,CAAC,CAAC,EAAE,CAAC,KAAK,GAAG,CAAC;gBACtD,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC,MAAM,IAAI,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,KAAK,GAAG;gBAC3C,CAAC,CAAC,EAAE,CAAC;YACT,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,MAAM,MAAM,CAAC,CAAC,QAAQ,KAAK,CAAC,CAAC,KAAK,MAAM,OAAO,CAAC,IAAI,IAAI,EAAE,EAAE,CAAC,CAAC;QACjF,CAAC;IACH,CAAC;IACD,IAAI,KAAK,KAAK,CAAC,EAAE,CAAC;QAChB,OAAO,oBAAoB,CAAC;IAC9B,CAAC;IACD,OAAO,mBAAmB,KAAK,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;AAC3D,CAAC;AAED,SAAS,cAAc,CAAC,WAA2D;IACjF,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,KAAK,MAAM,OAAO,IAAI,MAAM,CAAC,MAAM,CAAC,WAAW,IAAI,EAAE,CAAC,EAAE,CAAC;QACvD,KAAK,MAAM,CAAC,IAAI,OAAO,CAAC,KAAK,IAAI,EAAE,EAAE,CAAC;YACpC,KAAK,IAAI,CAAC,CAAC;YACX,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,KAAK,MAAM,OAAO,CAAC,IAAI,GAAG,CAAC,CAAC;QAC5D,CAAC;IACH,CAAC;IACD,IAAI,KAAK,KAAK,CAAC,EAAE,CAAC;QAChB,OAAO,oBAAoB,CAAC;IAC9B,CAAC;IACD,OAAO,mBAAmB,KAAK,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;AAC3D,CAAC;AAED,MAAM,SAAS,GAAG;IAChB,kBAAkB;IAClB,wDAAwD;IACxD,mCAAmC;IACnC,yCAAyC;IACzC,yCAAyC;IACzC,kDAAkD;CACnD,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAEb,SAAS,SAAS,CAAC,MAAc,EAAE,GAAY;IAC7C,MAAM,MAAM,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IAChE,OAAO,GAAG,MAAM,KAAK,MAAM,EAAE,CAAC;AAChC,CAAC;AAED,2BAA2B;AAE3B,MAAM,UAAU,sBAAsB,CACpC,GAAoD,EACpD,SAA0B,EAC1B,SAAuB;IAEvB,GAAG,CAAC,eAAe,CAAC;QAClB,IAAI,EAAE,QAAQ;QACd,WAAW,EAAE,sDAAsD;QACnE,WAAW,EAAE,IAAI;QACjB,KAAK,CAAC,OAAO,CAAC,GAAsB;YAClC,MAAM,GAAG,GAAG,CAAC,GAAG,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;YAElD,4EAA4E;YAC5E,IAAI,CAAC,GAAG,IAAI,GAAG,KAAK,QAAQ,EAAE,CAAC;gBAC7B,IAAI,CAAC;oBACH,MAAM,OAAO,GAAG,CAAC,MAAM,SAAS,CAAC,QAAQ,CAAC,gBAAgB,EAAE,EAAE,CAAC,CAAoB,CAAC;oBACpF,OAAO,EAAE,IAAI,EAAE,YAAY,CAAC,OAAO,EAAE,SAAS,EAAE,CAAC,EAAE,CAAC;gBACtD,CAAC;gBAAC,OAAO,GAAG,EAAE,CAAC;oBACb,OAAO,EAAE,IAAI,EAAE,SAAS,CAAC,oBAAoB,EAAE,GAAG,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;gBACvE,CAAC;YACH,CAAC;YAED,gEAAgE;YAChE,IAAI,GAAG,KAAK,OAAO,EAAE,CAAC;gBACpB,IAAI,CAAC;oBACH,MAAM,IAAI,GAAG,CAAC,MAAM,SAAS,CAAC,QAAQ,CACpC,2BAA2B,EAC3B,EAAE,CACH,CAAwB,CAAC;oBAC1B,OAAO,EAAE,IAAI,EAAE,cAAc,CAAC,IAAI,EAAE,WAAW,CAAC,EAAE,CAAC;gBACrD,CAAC;gBAAC,OAAO,GAAG,EAAE,CAAC;oBACb,OAAO,EAAE,IAAI,EAAE,SAAS,CAAC,uBAAuB,EAAE,GAAG,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;gBAC1E,CAAC;YACH,CAAC;YAED,gEAAgE;YAChE,IAAI,GAAG,KAAK,OAAO,EAAE,CAAC;gBACpB,IAAI,CAAC;oBACH,MAAM,IAAI,GAAG,CAAC,MAAM,SAAS,CAAC,QAAQ,CACpC,2BAA2B,EAC3B,EAAE,CACH,CAAwB,CAAC;oBAC1B,OAAO,EAAE,IAAI,EAAE,cAAc,CAAC,IAAI,EAAE,WAAW,CAAC,EAAE,CAAC;gBACrD,CAAC;gBAAC,OAAO,GAAG,EAAE,CAAC;oBACb,OAAO,EAAE,IAAI,EAAE,SAAS,CAAC,uBAAuB,EAAE,GAAG,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;gBAC1E,CAAC;YACH,CAAC;YAED,2DAA2D;YAC3D,IAAI,GAAG,KAAK,QAAQ,EAAE,CAAC;gBACrB,OAAO,EAAE,IAAI,EAAE,gBAAgB,EAAE,EAAE,CAAC;YACtC,CAAC;YAED,6BAA6B;YAC7B,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;QAC7B,CAAC;KACF,CAAC,CAAC;AACL,CAAC"}
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export declare const CONFIG_FILE_PATH = "~/.openclaw/openclaw.json";
|
|
3
|
+
export declare const CONFIG_KEY_PATH = "plugins.entries.chorus-openclaw-plugin.config";
|
|
4
|
+
/**
|
|
5
|
+
* In-code zod schema for typing `api.pluginConfig`.
|
|
6
|
+
*
|
|
7
|
+
* The canonical config contract is the JSON-Schema `configSchema` in
|
|
8
|
+
* `openclaw.plugin.json`, which OpenClaw validates BEFORE plugin code runs.
|
|
9
|
+
* This zod schema exists only for in-code typing and friendly missing-field
|
|
10
|
+
* messages — its accepted property set (chorusUrl, apiKey) MUST stay identical
|
|
11
|
+
* to the manifest `configSchema.properties`.
|
|
12
|
+
*/
|
|
13
|
+
export declare const chorusConfigSchema: z.ZodObject<{
|
|
14
|
+
chorusUrl: z.ZodOptional<z.ZodString>;
|
|
15
|
+
apiKey: z.ZodOptional<z.ZodString>;
|
|
16
|
+
}, "strip", z.ZodTypeAny, {
|
|
17
|
+
chorusUrl?: string | undefined;
|
|
18
|
+
apiKey?: string | undefined;
|
|
19
|
+
}, {
|
|
20
|
+
chorusUrl?: string | undefined;
|
|
21
|
+
apiKey?: string | undefined;
|
|
22
|
+
}>;
|
|
23
|
+
export type ChorusPluginConfig = z.infer<typeof chorusConfigSchema>;
|
|
24
|
+
/**
|
|
25
|
+
* Normalize a raw `api.pluginConfig` bag into a typed `ChorusPluginConfig`.
|
|
26
|
+
*
|
|
27
|
+
* The host has already validated the bag against the manifest JSON Schema, so
|
|
28
|
+
* this only fills defaults and narrows types; it does not re-validate shape.
|
|
29
|
+
*/
|
|
30
|
+
export declare function resolveConfig(pluginConfig: Record<string, unknown> | undefined): ChorusPluginConfig;
|
|
31
|
+
/**
|
|
32
|
+
* Check required config fields and warn about missing ones.
|
|
33
|
+
* Returns true if all required fields are present, false otherwise.
|
|
34
|
+
*/
|
|
35
|
+
export declare function validateConfigWithWarnings(config: ChorusPluginConfig, logger: {
|
|
36
|
+
warn: (msg: string) => void;
|
|
37
|
+
}): boolean;
|
|
38
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,eAAO,MAAM,gBAAgB,8BAA8B,CAAC;AAC5D,eAAO,MAAM,eAAe,kDAAkD,CAAC;AAE/E;;;;;;;;GAQG;AACH,eAAO,MAAM,kBAAkB;;;;;;;;;EAW7B,CAAC;AAEH,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAEpE;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,GAAG,kBAAkB,CAMnG;AAED;;;GAGG;AACH,wBAAgB,0BAA0B,CACxC,MAAM,EAAE,kBAAkB,EAC1B,MAAM,EAAE;IAAE,IAAI,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAA;CAAE,GACtC,OAAO,CAkBT"}
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export const CONFIG_FILE_PATH = "~/.openclaw/openclaw.json";
|
|
3
|
+
export const CONFIG_KEY_PATH = "plugins.entries.chorus-openclaw-plugin.config";
|
|
4
|
+
/**
|
|
5
|
+
* In-code zod schema for typing `api.pluginConfig`.
|
|
6
|
+
*
|
|
7
|
+
* The canonical config contract is the JSON-Schema `configSchema` in
|
|
8
|
+
* `openclaw.plugin.json`, which OpenClaw validates BEFORE plugin code runs.
|
|
9
|
+
* This zod schema exists only for in-code typing and friendly missing-field
|
|
10
|
+
* messages — its accepted property set (chorusUrl, apiKey) MUST stay identical
|
|
11
|
+
* to the manifest `configSchema.properties`.
|
|
12
|
+
*/
|
|
13
|
+
export const chorusConfigSchema = z.object({
|
|
14
|
+
chorusUrl: z
|
|
15
|
+
.string()
|
|
16
|
+
.url()
|
|
17
|
+
.optional()
|
|
18
|
+
.describe("Chorus server URL (e.g. https://chorus.example.com)"),
|
|
19
|
+
apiKey: z
|
|
20
|
+
.string()
|
|
21
|
+
.startsWith("cho_")
|
|
22
|
+
.optional()
|
|
23
|
+
.describe("Chorus API Key (cho_ prefix)"),
|
|
24
|
+
});
|
|
25
|
+
/**
|
|
26
|
+
* Normalize a raw `api.pluginConfig` bag into a typed `ChorusPluginConfig`.
|
|
27
|
+
*
|
|
28
|
+
* The host has already validated the bag against the manifest JSON Schema, so
|
|
29
|
+
* this only fills defaults and narrows types; it does not re-validate shape.
|
|
30
|
+
*/
|
|
31
|
+
export function resolveConfig(pluginConfig) {
|
|
32
|
+
const raw = pluginConfig ?? {};
|
|
33
|
+
return {
|
|
34
|
+
chorusUrl: raw.chorusUrl || undefined,
|
|
35
|
+
apiKey: raw.apiKey || undefined,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Check required config fields and warn about missing ones.
|
|
40
|
+
* Returns true if all required fields are present, false otherwise.
|
|
41
|
+
*/
|
|
42
|
+
export function validateConfigWithWarnings(config, logger) {
|
|
43
|
+
const missing = [];
|
|
44
|
+
if (!config.chorusUrl) {
|
|
45
|
+
missing.push(` - "chorusUrl": set at ${CONFIG_KEY_PATH}.chorusUrl in ${CONFIG_FILE_PATH}`);
|
|
46
|
+
}
|
|
47
|
+
if (!config.apiKey) {
|
|
48
|
+
missing.push(` - "apiKey": set at ${CONFIG_KEY_PATH}.apiKey in ${CONFIG_FILE_PATH}`);
|
|
49
|
+
}
|
|
50
|
+
if (missing.length > 0) {
|
|
51
|
+
logger.warn(`[Chorus] Plugin is missing required configuration. Features will be disabled until configured:\n` +
|
|
52
|
+
missing.join("\n"));
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
return true;
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,MAAM,CAAC,MAAM,gBAAgB,GAAG,2BAA2B,CAAC;AAC5D,MAAM,CAAC,MAAM,eAAe,GAAG,+CAA+C,CAAC;AAE/E;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,CAAC,MAAM,CAAC;IACzC,SAAS,EAAE,CAAC;SACT,MAAM,EAAE;SACR,GAAG,EAAE;SACL,QAAQ,EAAE;SACV,QAAQ,CAAC,qDAAqD,CAAC;IAClE,MAAM,EAAE,CAAC;SACN,MAAM,EAAE;SACR,UAAU,CAAC,MAAM,CAAC;SAClB,QAAQ,EAAE;SACV,QAAQ,CAAC,8BAA8B,CAAC;CAC5C,CAAC,CAAC;AAIH;;;;;GAKG;AACH,MAAM,UAAU,aAAa,CAAC,YAAiD;IAC7E,MAAM,GAAG,GAAG,YAAY,IAAI,EAAE,CAAC;IAC/B,OAAO;QACL,SAAS,EAAG,GAAG,CAAC,SAAgC,IAAI,SAAS;QAC7D,MAAM,EAAG,GAAG,CAAC,MAA6B,IAAI,SAAS;KACxD,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,0BAA0B,CACxC,MAA0B,EAC1B,MAAuC;IAEvC,MAAM,OAAO,GAAa,EAAE,CAAC;IAE7B,IAAI,CAAC,MAAM,CAAC,SAAS,EAAE,CAAC;QACtB,OAAO,CAAC,IAAI,CAAC,2BAA2B,eAAe,iBAAiB,gBAAgB,EAAE,CAAC,CAAC;IAC9F,CAAC;IACD,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;QACnB,OAAO,CAAC,IAAI,CAAC,wBAAwB,eAAe,cAAc,gBAAgB,EAAE,CAAC,CAAC;IACxF,CAAC;IAED,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACvB,MAAM,CAAC,IAAI,CACT,kGAAkG;YAClG,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CACnB,CAAC;QACF,OAAO,KAAK,CAAC;IACf,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import type { ChorusMcpClient } from "./mcp-client.js";
|
|
2
|
+
import type { SseNotificationEvent } from "./sse-listener.js";
|
|
3
|
+
/**
|
|
4
|
+
* Wake callback injected by the entry. Runs an embedded agent turn on the main
|
|
5
|
+
* agent's session with `message` as the prompt (see `wake.ts` → createWake,
|
|
6
|
+
* which calls `api.runtime.agent.runEmbeddedAgent`).
|
|
7
|
+
*
|
|
8
|
+
* `contextKey` identifies the originating Chorus action+entity (e.g.
|
|
9
|
+
* `chorus:mentioned:<uuid>`); it is used for the run id / logging. The wake
|
|
10
|
+
* resolves the main agent session + model and DROPS (logs + returns) when it
|
|
11
|
+
* cannot run — it never throws, so the SSE service stays alive.
|
|
12
|
+
*/
|
|
13
|
+
export type ChorusWakeFn = (message: string, contextKey: string) => void;
|
|
14
|
+
export interface ChorusEventRouterOptions {
|
|
15
|
+
mcpClient: ChorusMcpClient;
|
|
16
|
+
wake: ChorusWakeFn;
|
|
17
|
+
logger: {
|
|
18
|
+
info: (msg: string) => void;
|
|
19
|
+
warn: (msg: string) => void;
|
|
20
|
+
error: (msg: string) => void;
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
export declare class ChorusEventRouter {
|
|
24
|
+
private readonly mcpClient;
|
|
25
|
+
private readonly wake;
|
|
26
|
+
private readonly logger;
|
|
27
|
+
constructor(opts: ChorusEventRouterOptions);
|
|
28
|
+
/**
|
|
29
|
+
* Route an incoming SSE notification event to the appropriate handler.
|
|
30
|
+
* Never throws — all errors are caught and logged internally.
|
|
31
|
+
*/
|
|
32
|
+
dispatch(event: SseNotificationEvent): void;
|
|
33
|
+
/**
|
|
34
|
+
* Build the dedupe contextKey for a notification. Identical action+entity
|
|
35
|
+
* bursts collapse to the same key so OpenClaw's queue suppresses the
|
|
36
|
+
* duplicate wake.
|
|
37
|
+
*/
|
|
38
|
+
private contextKeyFor;
|
|
39
|
+
private fetchAndRoute;
|
|
40
|
+
/**
|
|
41
|
+
* Build @mention guidance for agent messages.
|
|
42
|
+
* Instructs the agent to @mention the actor after completing work.
|
|
43
|
+
*/
|
|
44
|
+
private buildMentionGuidance;
|
|
45
|
+
private handleTaskAssigned;
|
|
46
|
+
private handleMentioned;
|
|
47
|
+
private handleElaborationRequested;
|
|
48
|
+
private handleProposalRejected;
|
|
49
|
+
private handleProposalApproved;
|
|
50
|
+
private handleIdeaClaimed;
|
|
51
|
+
private handleTaskVerified;
|
|
52
|
+
private handleTaskReopened;
|
|
53
|
+
private handleElaborationAnswered;
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=event-router.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"event-router.d.ts","sourceRoot":"","sources":["../src/event-router.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AACvD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AAE9D;;;;;;;;;GASG;AACH,MAAM,MAAM,YAAY,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,KAAK,IAAI,CAAC;AAEzE,MAAM,WAAW,wBAAwB;IACvC,SAAS,EAAE,eAAe,CAAC;IAC3B,IAAI,EAAE,YAAY,CAAC;IACnB,MAAM,EAAE;QAAE,IAAI,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;QAAC,IAAI,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;QAAC,KAAK,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAA;KAAE,CAAC;CACpG;AAmBD,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAkB;IAC5C,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAe;IACpC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAqC;gBAEhD,IAAI,EAAE,wBAAwB;IAM1C;;;OAGG;IACH,QAAQ,CAAC,KAAK,EAAE,oBAAoB,GAAG,IAAI;IAsB3C;;;;OAIG;IACH,OAAO,CAAC,aAAa;YAIP,aAAa;IA4D3B;;;OAGG;IACH,OAAO,CAAC,oBAAoB;IAO5B,OAAO,CAAC,kBAAkB;IAS1B,OAAO,CAAC,eAAe;IAWvB,OAAO,CAAC,0BAA0B;IAOlC,OAAO,CAAC,sBAAsB;IAY9B,OAAO,CAAC,sBAAsB;IAY9B,OAAO,CAAC,iBAAiB;IAWzB,OAAO,CAAC,kBAAkB;IAQ1B,OAAO,CAAC,kBAAkB;IAU1B,OAAO,CAAC,yBAAyB;CAalC"}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
export class ChorusEventRouter {
|
|
2
|
+
mcpClient;
|
|
3
|
+
wake;
|
|
4
|
+
logger;
|
|
5
|
+
constructor(opts) {
|
|
6
|
+
this.mcpClient = opts.mcpClient;
|
|
7
|
+
this.wake = opts.wake;
|
|
8
|
+
this.logger = opts.logger;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Route an incoming SSE notification event to the appropriate handler.
|
|
12
|
+
* Never throws — all errors are caught and logged internally.
|
|
13
|
+
*/
|
|
14
|
+
dispatch(event) {
|
|
15
|
+
// Only handle new_notification events (ignore count_update, etc.)
|
|
16
|
+
if (event.type !== "new_notification") {
|
|
17
|
+
this.logger.info(`SSE event type "${event.type}" ignored`);
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
if (!event.notificationUuid) {
|
|
21
|
+
this.logger.warn("new_notification event missing notificationUuid, skipping");
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
// Fetch full notification details and route asynchronously
|
|
25
|
+
this.fetchAndRoute(event.notificationUuid).catch((err) => {
|
|
26
|
+
this.logger.error(`Failed to fetch/route notification ${event.notificationUuid}: ${err}`);
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
// ---------------------------------------------------------------------------
|
|
30
|
+
// Internal
|
|
31
|
+
// ---------------------------------------------------------------------------
|
|
32
|
+
/**
|
|
33
|
+
* Build the dedupe contextKey for a notification. Identical action+entity
|
|
34
|
+
* bursts collapse to the same key so OpenClaw's queue suppresses the
|
|
35
|
+
* duplicate wake.
|
|
36
|
+
*/
|
|
37
|
+
contextKeyFor(action, entityUuid) {
|
|
38
|
+
return `chorus:${action}:${entityUuid}`;
|
|
39
|
+
}
|
|
40
|
+
async fetchAndRoute(notificationUuid) {
|
|
41
|
+
// Fetch notification details via MCP — use autoMarkRead=false so we don't
|
|
42
|
+
// consume all unread notifications, and status=unread since we just received it
|
|
43
|
+
const result = await this.mcpClient.callTool("chorus_get_notifications", {
|
|
44
|
+
status: "unread",
|
|
45
|
+
limit: 50,
|
|
46
|
+
autoMarkRead: false,
|
|
47
|
+
});
|
|
48
|
+
const notifications = result?.notifications;
|
|
49
|
+
if (!notifications || !Array.isArray(notifications)) {
|
|
50
|
+
this.logger.warn(`Could not fetch notifications list`);
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
const notification = notifications.find((n) => n.uuid === notificationUuid);
|
|
54
|
+
if (!notification) {
|
|
55
|
+
this.logger.warn(`Notification ${notificationUuid} not found in unread list`);
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
// Route based on action (which corresponds to notificationType)
|
|
59
|
+
try {
|
|
60
|
+
switch (notification.action) {
|
|
61
|
+
case "task_assigned":
|
|
62
|
+
this.handleTaskAssigned(notification);
|
|
63
|
+
break;
|
|
64
|
+
case "mentioned":
|
|
65
|
+
this.handleMentioned(notification);
|
|
66
|
+
break;
|
|
67
|
+
case "elaboration_requested":
|
|
68
|
+
this.handleElaborationRequested(notification);
|
|
69
|
+
break;
|
|
70
|
+
case "elaboration_answered":
|
|
71
|
+
this.handleElaborationAnswered(notification);
|
|
72
|
+
break;
|
|
73
|
+
case "proposal_rejected":
|
|
74
|
+
this.handleProposalRejected(notification);
|
|
75
|
+
break;
|
|
76
|
+
case "proposal_approved":
|
|
77
|
+
this.handleProposalApproved(notification);
|
|
78
|
+
break;
|
|
79
|
+
case "idea_claimed":
|
|
80
|
+
this.handleIdeaClaimed(notification);
|
|
81
|
+
break;
|
|
82
|
+
case "task_verified":
|
|
83
|
+
this.handleTaskVerified(notification);
|
|
84
|
+
break;
|
|
85
|
+
case "task_reopened":
|
|
86
|
+
this.handleTaskReopened(notification);
|
|
87
|
+
break;
|
|
88
|
+
default:
|
|
89
|
+
this.logger.info(`Unhandled notification action: "${notification.action}"`);
|
|
90
|
+
break;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
catch (err) {
|
|
94
|
+
this.logger.error(`Error handling ${notification.action} notification: ${err}`);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Build @mention guidance for agent messages.
|
|
99
|
+
* Instructs the agent to @mention the actor after completing work.
|
|
100
|
+
*/
|
|
101
|
+
buildMentionGuidance(n, entityType) {
|
|
102
|
+
return (`After completing your work, post a comment on this ${entityType} using chorus_add_comment with @mention:\n` +
|
|
103
|
+
`Use this exact mention format: @[${n.actorName}](${n.actorType}:${n.actorUuid})`);
|
|
104
|
+
}
|
|
105
|
+
handleTaskAssigned(n) {
|
|
106
|
+
const mentionGuidance = this.buildMentionGuidance(n, "task");
|
|
107
|
+
this.wake(`[Chorus] Task assigned: ${n.entityTitle}. Task UUID: ${n.entityUuid}, Project UUID: ${n.projectUuid}. Use chorus_get_task to review the task, then chorus_claim_task to start work.\n${mentionGuidance}`, this.contextKeyFor("task_assigned", n.entityUuid));
|
|
108
|
+
}
|
|
109
|
+
handleMentioned(n) {
|
|
110
|
+
const mentionGuidance = this.buildMentionGuidance(n, n.entityType);
|
|
111
|
+
this.wake(`[Chorus] You were @mentioned in ${n.entityType} '${n.entityTitle}' (entityType: ${n.entityType}, entityUuid: ${n.entityUuid}, projectUuid: ${n.projectUuid}): ${n.message}\n` +
|
|
112
|
+
`Review the ${n.entityType} content and use chorus_get_comments (targetType: "${n.entityType}", targetUuid: "${n.entityUuid}") to see the full conversation, then respond.\n` +
|
|
113
|
+
mentionGuidance, this.contextKeyFor("mentioned", n.entityUuid));
|
|
114
|
+
}
|
|
115
|
+
handleElaborationRequested(n) {
|
|
116
|
+
this.wake(`[Chorus] Elaboration requested for idea '${n.entityTitle}' (ideaUuid: ${n.entityUuid}, projectUuid: ${n.projectUuid}). Use chorus_get_elaboration to review questions.`, this.contextKeyFor("elaboration_requested", n.entityUuid));
|
|
117
|
+
}
|
|
118
|
+
handleProposalRejected(n) {
|
|
119
|
+
const mentionGuidance = this.buildMentionGuidance(n, "proposal");
|
|
120
|
+
this.wake(`[Chorus] Proposal '${n.entityTitle}' was REJECTED (proposalUuid: ${n.entityUuid}, projectUuid: ${n.projectUuid}). Review note: "${n.message}". ` +
|
|
121
|
+
`Use chorus_get_proposal to review the proposal, then fix issues with chorus_update_task_draft / chorus_update_document_draft. ` +
|
|
122
|
+
`After fixing, call chorus_validate_proposal then chorus_submit_proposal to resubmit.\n` +
|
|
123
|
+
mentionGuidance, this.contextKeyFor("proposal_rejected", n.entityUuid));
|
|
124
|
+
}
|
|
125
|
+
handleProposalApproved(n) {
|
|
126
|
+
const mentionGuidance = this.buildMentionGuidance(n, "proposal");
|
|
127
|
+
const reviewInfo = n.message.includes("Note: ") ? ` Review note: "${n.message.split("Note: ").pop()}"` : "";
|
|
128
|
+
this.wake(`[Chorus] Proposal '${n.entityTitle}' was APPROVED (proposalUuid: ${n.entityUuid}, projectUuid: ${n.projectUuid})!${reviewInfo} Documents and tasks have been created. ` +
|
|
129
|
+
`Use chorus_get_available_tasks with projectUuid: "${n.projectUuid}" to see the new tasks ready for work.\n` +
|
|
130
|
+
mentionGuidance, this.contextKeyFor("proposal_approved", n.entityUuid));
|
|
131
|
+
}
|
|
132
|
+
handleIdeaClaimed(n) {
|
|
133
|
+
const mentionGuidance = this.buildMentionGuidance(n, "idea");
|
|
134
|
+
this.wake(`[Chorus] Idea '${n.entityTitle}' has been assigned to you (ideaUuid: ${n.entityUuid}, projectUuid: ${n.projectUuid}). ` +
|
|
135
|
+
`Use chorus_get_idea to review the idea, then chorus_claim_idea to start elaboration.\n` +
|
|
136
|
+
mentionGuidance, this.contextKeyFor("idea_claimed", n.entityUuid));
|
|
137
|
+
}
|
|
138
|
+
handleTaskVerified(n) {
|
|
139
|
+
this.wake(`[Chorus] Task '${n.entityTitle}' has been verified and is now done (taskUuid: ${n.entityUuid}, projectUuid: ${n.projectUuid}). ` +
|
|
140
|
+
`Check if this unblocks other tasks: use chorus_get_unblocked_tasks with projectUuid "${n.projectUuid}" to find tasks that are now ready to start.`, this.contextKeyFor("task_verified", n.entityUuid));
|
|
141
|
+
}
|
|
142
|
+
handleTaskReopened(n) {
|
|
143
|
+
const mentionGuidance = this.buildMentionGuidance(n, "task");
|
|
144
|
+
this.wake(`[Chorus] Task '${n.entityTitle}' has been reopened and needs rework (taskUuid: ${n.entityUuid}, projectUuid: ${n.projectUuid}). ` +
|
|
145
|
+
`Use chorus_get_task to review the task and chorus_get_comments to see verification feedback, then fix the issues.\n${mentionGuidance}`, this.contextKeyFor("task_reopened", n.entityUuid));
|
|
146
|
+
}
|
|
147
|
+
handleElaborationAnswered(n) {
|
|
148
|
+
const mentionGuidance = this.buildMentionGuidance(n, "idea");
|
|
149
|
+
this.wake(`[Chorus] Elaboration answers submitted for idea '${n.entityTitle}' (ideaUuid: ${n.entityUuid}, projectUuid: ${n.projectUuid}). ` +
|
|
150
|
+
`Review the answers with chorus_get_elaboration, then either:\n` +
|
|
151
|
+
`- Call chorus_validate_elaboration with empty issues [] to resolve and proceed to proposal creation\n` +
|
|
152
|
+
`- Call chorus_validate_elaboration with issues + followUpQuestions for another round\n\n` +
|
|
153
|
+
`After reviewing, @mention the answerer to ask if they have any further questions before you proceed.\n` +
|
|
154
|
+
mentionGuidance, this.contextKeyFor("elaboration_answered", n.entityUuid));
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
//# sourceMappingURL=event-router.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"event-router.js","sourceRoot":"","sources":["../src/event-router.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAiCA,wBAuFG"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry";
|
|
2
|
+
import { resolveConfig, validateConfigWithWarnings } from "./config.js";
|
|
3
|
+
import { ensureChorusMcpServer } from "./mcp-registration.js";
|
|
4
|
+
import { ChorusMcpClient } from "./mcp-client.js";
|
|
5
|
+
import { ChorusSseListener } from "./sse-listener.js";
|
|
6
|
+
import { ChorusEventRouter } from "./event-router.js";
|
|
7
|
+
import { createWake } from "./wake.js";
|
|
8
|
+
import { registerChorusCommands } from "./commands.js";
|
|
9
|
+
/**
|
|
10
|
+
* JSON-Schema config contract for the Chorus plugin.
|
|
11
|
+
*
|
|
12
|
+
* This mirrors the canonical `configSchema` in `openclaw.plugin.json`
|
|
13
|
+
* (the manifest is validated by the host BEFORE this code loads). Keep the two
|
|
14
|
+
* in sync: same property set, same `additionalProperties: false`.
|
|
15
|
+
*/
|
|
16
|
+
const CHORUS_JSON_SCHEMA = {
|
|
17
|
+
type: "object",
|
|
18
|
+
additionalProperties: false,
|
|
19
|
+
properties: {
|
|
20
|
+
chorusUrl: {
|
|
21
|
+
type: "string",
|
|
22
|
+
description: "Chorus server URL (e.g. https://chorus.example.com)",
|
|
23
|
+
},
|
|
24
|
+
apiKey: {
|
|
25
|
+
type: "string",
|
|
26
|
+
description: "Chorus API Key (cho_ prefix)",
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
export default definePluginEntry({
|
|
31
|
+
id: "chorus-openclaw-plugin",
|
|
32
|
+
name: "Chorus",
|
|
33
|
+
description: "Chorus AI-DLC collaboration platform — native MCP + SSE real-time events",
|
|
34
|
+
configSchema: {
|
|
35
|
+
jsonSchema: CHORUS_JSON_SCHEMA,
|
|
36
|
+
uiHints: { apiKey: { sensitive: true } },
|
|
37
|
+
},
|
|
38
|
+
register(api) {
|
|
39
|
+
// 1. Discovery-mode guard: heavy runtime wiring (MCP connect, SSE socket,
|
|
40
|
+
// config mutation) must run ONLY in "full" registration mode. In
|
|
41
|
+
// discovery / cli-metadata / setup-* modes we declare nothing heavy.
|
|
42
|
+
if (api.registrationMode !== "full")
|
|
43
|
+
return;
|
|
44
|
+
// 2. Resolve + validate config from the host-validated pluginConfig bag.
|
|
45
|
+
const config = resolveConfig(api.pluginConfig);
|
|
46
|
+
const logger = api.logger;
|
|
47
|
+
if (!validateConfigWithWarnings(config, logger)) {
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
// After validateConfigWithWarnings, chorusUrl and apiKey are present.
|
|
51
|
+
const chorusUrl = config.chorusUrl;
|
|
52
|
+
const apiKey = config.apiKey;
|
|
53
|
+
logger.info(`Chorus plugin initializing — ${chorusUrl}`);
|
|
54
|
+
// 3. Ensure the Chorus MCP server is registered with OpenClaw so the agent
|
|
55
|
+
// gains native chorus__* tools (fire-and-log; real impl in sibling task).
|
|
56
|
+
void ensureChorusMcpServer(api, config).catch((err) => logger.error(`MCP registration failed: ${err}`));
|
|
57
|
+
// 4. Slim MCP client for the plugin's own synchronous calls (checkin,
|
|
58
|
+
// assignments, notifications back-fill).
|
|
59
|
+
const mcpClient = new ChorusMcpClient({ chorusUrl, apiKey, logger });
|
|
60
|
+
// 5. Event router. Wakes the agent in-process by running an embedded agent
|
|
61
|
+
// turn via `api.runtime.agent.runEmbeddedAgent` (see wake.ts). `createWake`
|
|
62
|
+
// resolves the main agent session + configured model on each wake and
|
|
63
|
+
// gracefully DROPS (logs + returns) when it cannot run — it never throws,
|
|
64
|
+
// so the SSE service stays alive even on a host that exposes no session.
|
|
65
|
+
const eventRouter = new ChorusEventRouter({
|
|
66
|
+
mcpClient,
|
|
67
|
+
logger,
|
|
68
|
+
wake: createWake(api, logger),
|
|
69
|
+
});
|
|
70
|
+
// 6. Background SSE service. The SSE socket opens only inside start(), which
|
|
71
|
+
// the host calls in full mode — keeping the heavy socket gated.
|
|
72
|
+
let sseListener = null;
|
|
73
|
+
api.registerService({
|
|
74
|
+
id: "chorus-sse",
|
|
75
|
+
async start() {
|
|
76
|
+
sseListener = new ChorusSseListener({
|
|
77
|
+
chorusUrl,
|
|
78
|
+
apiKey,
|
|
79
|
+
logger,
|
|
80
|
+
onEvent: (event) => eventRouter.dispatch(event),
|
|
81
|
+
onReconnect: async () => {
|
|
82
|
+
try {
|
|
83
|
+
const result = (await mcpClient.callTool("chorus_get_notifications", {
|
|
84
|
+
status: "unread",
|
|
85
|
+
autoMarkRead: false,
|
|
86
|
+
}));
|
|
87
|
+
const count = result?.notifications?.length ?? 0;
|
|
88
|
+
if (count > 0) {
|
|
89
|
+
logger.info(`SSE reconnect: ${count} unread notifications to process`);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
catch (err) {
|
|
93
|
+
logger.warn(`Failed to back-fill notifications: ${err}`);
|
|
94
|
+
}
|
|
95
|
+
},
|
|
96
|
+
});
|
|
97
|
+
await sseListener.connect();
|
|
98
|
+
},
|
|
99
|
+
async stop() {
|
|
100
|
+
sseListener?.disconnect();
|
|
101
|
+
await mcpClient.disconnect();
|
|
102
|
+
},
|
|
103
|
+
});
|
|
104
|
+
// 7. /chorus command (status | tasks | ideas | skills).
|
|
105
|
+
registerChorusCommands(api, mcpClient, () => sseListener?.status ?? "disconnected");
|
|
106
|
+
},
|
|
107
|
+
});
|
|
108
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|