feat(broker+cli): topics — conversation scope within a mesh (v0.2.0)
Adds the third axis of mesh organization: mesh = trust boundary, group = identity tag, topic = conversation scope. Topic-tagged messages filter delivery by topic_member rows and persist to a topic_message history table for back-scroll on reconnect. Schema (additive): - mesh.topic, mesh.topic_member, mesh.topic_message tables - topic_visibility (public|private|dm) and topic_member_role (lead|member|observer) enums - migration 0022_topics.sql, hand-written following project convention (drizzle journal has been drifting since 0011) Broker: - 10 helpers (createTopic, listTopics, findTopicByName, joinTopic, leaveTopic, topicMembers, getMemberTopicIds, appendTopicMessage, topicHistory, markTopicRead) - drainForMember matches "#<topicId>" target_specs via member's topic memberships - 7 WS handlers (topic_create/list/join/leave/members/history/mark_read) + resolveTopicId helper accepting id-or-name - handleSend auto-persists topic-tagged messages to history CLI: - claudemesh topic create/list/join/leave/members/history/read - claudemesh send "#deploys" "..." resolves topic name to id - bundled skill teaches Claude the DM/group/topic decision matrix - policy-classify recognizes topic create/join/leave as writes Spec: .artifacts/specs/2026-05-02-v0.2.0-scope.md Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -102,6 +102,16 @@ Profile / presence (resource form)
|
||||
claudemesh group join @<name> join a group (--role X)
|
||||
claudemesh group leave @<name> leave a group
|
||||
|
||||
Topic (conversation scope, v0.2.0)
|
||||
claudemesh topic create <name> create a topic [--description --visibility]
|
||||
claudemesh topic list list topics in the mesh
|
||||
claudemesh topic join <topic> subscribe (via name or id)
|
||||
claudemesh topic leave <topic> unsubscribe
|
||||
claudemesh topic members <t> list topic subscribers
|
||||
claudemesh topic history <t> fetch message history [--limit --before]
|
||||
claudemesh topic read <topic> mark all as read
|
||||
claudemesh send "#topic" "msg" send to a topic
|
||||
|
||||
Schedule (resource form)
|
||||
claudemesh schedule msg <m> one-shot or recurring (alias: remind)
|
||||
claudemesh schedule list list pending
|
||||
@@ -499,6 +509,30 @@ async function main(): Promise<void> {
|
||||
break;
|
||||
}
|
||||
|
||||
// topic — conversational primitive within a mesh (v0.2.0)
|
||||
case "topic": {
|
||||
const sub = positionals[0];
|
||||
const f = {
|
||||
mesh: flags.mesh as string,
|
||||
json: !!flags.json,
|
||||
description: flags.description as string,
|
||||
visibility: flags.visibility as "public" | "private" | "dm" | undefined,
|
||||
role: flags.role as "lead" | "member" | "observer" | undefined,
|
||||
limit: flags.limit as string | undefined,
|
||||
before: flags.before as string | undefined,
|
||||
};
|
||||
const arg = positionals[1] ?? "";
|
||||
if (sub === "create") { const { runTopicCreate } = await import("~/commands/topic.js"); process.exit(await runTopicCreate(arg, f)); }
|
||||
else if (sub === "list") { const { runTopicList } = await import("~/commands/topic.js"); process.exit(await runTopicList(f)); }
|
||||
else if (sub === "join") { const { runTopicJoin } = await import("~/commands/topic.js"); process.exit(await runTopicJoin(arg, f)); }
|
||||
else if (sub === "leave") { const { runTopicLeave } = await import("~/commands/topic.js"); process.exit(await runTopicLeave(arg, f)); }
|
||||
else if (sub === "members") { const { runTopicMembers } = await import("~/commands/topic.js"); process.exit(await runTopicMembers(arg, f)); }
|
||||
else if (sub === "history") { const { runTopicHistory } = await import("~/commands/topic.js"); process.exit(await runTopicHistory(arg, f)); }
|
||||
else if (sub === "read") { const { runTopicMarkRead } = await import("~/commands/topic.js"); process.exit(await runTopicMarkRead(arg, f)); }
|
||||
else { console.error("Usage: claudemesh topic <create|list|join|leave|members|history|read>"); process.exit(EXIT.INVALID_ARGS); }
|
||||
break;
|
||||
}
|
||||
|
||||
// task — extends broker-actions.ts (claim/complete) with list/create
|
||||
case "task": {
|
||||
const sub = positionals[0];
|
||||
|
||||
Reference in New Issue
Block a user