@hasna/conversations 0.2.11 → 0.2.13
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/bin/hook.js +3 -0
- package/bin/index.js +93 -1
- package/bin/mcp.js +93 -1
- package/dist/index.js +3 -0
- package/package.json +1 -1
package/bin/hook.js
CHANGED
|
@@ -205,6 +205,9 @@ function getDb() {
|
|
|
205
205
|
if (!spaceColNames.includes("archived_at")) {
|
|
206
206
|
db.exec("ALTER TABLE spaces ADD COLUMN archived_at TEXT");
|
|
207
207
|
}
|
|
208
|
+
if (!spaceColNames.includes("topic")) {
|
|
209
|
+
db.exec("ALTER TABLE spaces ADD COLUMN topic TEXT");
|
|
210
|
+
}
|
|
208
211
|
const msgCols2 = db.prepare("PRAGMA table_info(messages)").all();
|
|
209
212
|
const colNames2 = msgCols2.map((c) => c.name);
|
|
210
213
|
if (!colNames2.includes("edited_at")) {
|
package/bin/index.js
CHANGED
|
@@ -2070,6 +2070,9 @@ function getDb() {
|
|
|
2070
2070
|
if (!spaceColNames.includes("archived_at")) {
|
|
2071
2071
|
db.exec("ALTER TABLE spaces ADD COLUMN archived_at TEXT");
|
|
2072
2072
|
}
|
|
2073
|
+
if (!spaceColNames.includes("topic")) {
|
|
2074
|
+
db.exec("ALTER TABLE spaces ADD COLUMN topic TEXT");
|
|
2075
|
+
}
|
|
2073
2076
|
const msgCols2 = db.prepare("PRAGMA table_info(messages)").all();
|
|
2074
2077
|
const colNames2 = msgCols2.map((c) => c.name);
|
|
2075
2078
|
if (!colNames2.includes("edited_at")) {
|
|
@@ -4442,7 +4445,7 @@ var init_poll = __esm(() => {
|
|
|
4442
4445
|
var require_package = __commonJS((exports, module) => {
|
|
4443
4446
|
module.exports = {
|
|
4444
4447
|
name: "@hasna/conversations",
|
|
4445
|
-
version: "0.2.
|
|
4448
|
+
version: "0.2.13",
|
|
4446
4449
|
description: "Real-time CLI messaging for AI agents",
|
|
4447
4450
|
type: "module",
|
|
4448
4451
|
bin: {
|
|
@@ -34458,6 +34461,31 @@ var init_mcp2 = __esm(() => {
|
|
|
34458
34461
|
const topics = getTrendingTopics({ hours: args.hours, project_id: args.project_id, top_n: args.top_n });
|
|
34459
34462
|
return { content: [{ type: "text", text: JSON.stringify(topics) }] };
|
|
34460
34463
|
});
|
|
34464
|
+
server.registerTool("set_space_topic", {
|
|
34465
|
+
description: "Set the current topic/status of a space. Separate from the static description \u2014 use this for live status like '\uD83D\uDD34 blocked on auth' or '\u2705 shipping v2'.",
|
|
34466
|
+
inputSchema: {
|
|
34467
|
+
space: exports_external.string().describe("Space name"),
|
|
34468
|
+
topic: exports_external.string().nullable().describe("New topic/status. Pass null to clear.")
|
|
34469
|
+
}
|
|
34470
|
+
}, async (args) => {
|
|
34471
|
+
const db2 = (await Promise.resolve().then(() => (init_db(), exports_db))).getDb();
|
|
34472
|
+
const existing = db2.prepare("SELECT name FROM spaces WHERE name = ?").get(args.space);
|
|
34473
|
+
if (!existing) {
|
|
34474
|
+
return { content: [{ type: "text", text: `Space not found: ${args.space}` }], isError: true };
|
|
34475
|
+
}
|
|
34476
|
+
db2.prepare("UPDATE spaces SET topic = ? WHERE name = ?").run(args.topic ?? null, args.space);
|
|
34477
|
+
return { content: [{ type: "text", text: args.topic ? `Topic set: ${args.topic}` : "Topic cleared" }] };
|
|
34478
|
+
});
|
|
34479
|
+
server.registerTool("get_space_topic", {
|
|
34480
|
+
description: "Get the current topic/status of a space.",
|
|
34481
|
+
inputSchema: { space: exports_external.string() }
|
|
34482
|
+
}, async (args) => {
|
|
34483
|
+
const db2 = (await Promise.resolve().then(() => (init_db(), exports_db))).getDb();
|
|
34484
|
+
const row = db2.prepare("SELECT topic FROM spaces WHERE name = ?").get(args.space);
|
|
34485
|
+
if (!row)
|
|
34486
|
+
return { content: [{ type: "text", text: `Space not found: ${args.space}` }], isError: true };
|
|
34487
|
+
return { content: [{ type: "text", text: JSON.stringify({ space: args.space, topic: row.topic }) }] };
|
|
34488
|
+
});
|
|
34461
34489
|
server.registerTool("get_session_activity", {
|
|
34462
34490
|
description: "Get activity metrics for a session: message velocity, unique agents, reply ratio, reaction count, trending status.",
|
|
34463
34491
|
inputSchema: {
|
|
@@ -34522,6 +34550,68 @@ var init_mcp2 = __esm(() => {
|
|
|
34522
34550
|
const reactions = getReactions(args.message_id);
|
|
34523
34551
|
return { content: [{ type: "text", text: JSON.stringify(reactions) }] };
|
|
34524
34552
|
});
|
|
34553
|
+
server.registerTool("summarize_space", {
|
|
34554
|
+
description: "Get a structured catch-up summary of a space for a time window \u2014 participants, topics, key messages, blockers, activity counts. No LLM required.",
|
|
34555
|
+
inputSchema: {
|
|
34556
|
+
space: exports_external.string().describe("Space name"),
|
|
34557
|
+
since: exports_external.string().optional().describe("ISO 8601 timestamp \u2014 only include messages after this. Defaults to 24h ago."),
|
|
34558
|
+
limit: exports_external.coerce.number().optional().describe("Max messages to analyze (default: 100)")
|
|
34559
|
+
}
|
|
34560
|
+
}, async (args) => {
|
|
34561
|
+
const { space, since, limit } = args;
|
|
34562
|
+
const sinceTs = since ?? new Date(Date.now() - 24 * 60 * 60 * 1000).toISOString();
|
|
34563
|
+
const db2 = (await Promise.resolve().then(() => (init_db(), exports_db))).getDb();
|
|
34564
|
+
const total = db2.prepare("SELECT COUNT(*) as c FROM messages WHERE space = ? AND created_at >= ?").get(space, sinceTs).c;
|
|
34565
|
+
if (total === 0) {
|
|
34566
|
+
return { content: [{ type: "text", text: `No messages in #${space} since ${sinceTs.slice(0, 10)}.` }] };
|
|
34567
|
+
}
|
|
34568
|
+
const rows = db2.prepare(`SELECT * FROM messages WHERE space = ? AND created_at >= ? ORDER BY created_at DESC LIMIT ?`).all(space, sinceTs, limit ?? 100);
|
|
34569
|
+
const agents = new Set;
|
|
34570
|
+
const agentCounts = {};
|
|
34571
|
+
const blockers = [];
|
|
34572
|
+
const mentions = {};
|
|
34573
|
+
for (const m of rows) {
|
|
34574
|
+
const from = m.from_agent;
|
|
34575
|
+
agents.add(from);
|
|
34576
|
+
agentCounts[from] = (agentCounts[from] ?? 0) + 1;
|
|
34577
|
+
if (m.blocking) {
|
|
34578
|
+
blockers.push({ id: m.id, from, content: m.content.slice(0, 150), created_at: m.created_at });
|
|
34579
|
+
}
|
|
34580
|
+
const mentionedAgents = m.content.match(/@([a-zA-Z0-9_-]+)/g) ?? [];
|
|
34581
|
+
for (const mention of mentionedAgents) {
|
|
34582
|
+
const a = mention.slice(1).toLowerCase();
|
|
34583
|
+
mentions[a] = (mentions[a] ?? 0) + 1;
|
|
34584
|
+
}
|
|
34585
|
+
}
|
|
34586
|
+
const parts = [
|
|
34587
|
+
`Space: #${space} | Since: ${sinceTs.slice(0, 10)} | ${total} messages (showing ${rows.length})`,
|
|
34588
|
+
`
|
|
34589
|
+
Participants (${agents.size}): ${Object.entries(agentCounts).sort((a, b) => b[1] - a[1]).map(([n, c]) => `${n}(${c})`).join(", ")}`
|
|
34590
|
+
];
|
|
34591
|
+
if (Object.keys(mentions).length > 0) {
|
|
34592
|
+
const topMentions = Object.entries(mentions).sort((a, b) => b[1] - a[1]).slice(0, 5);
|
|
34593
|
+
parts.push(`Most mentioned: ${topMentions.map(([n, c]) => `@${n}(${c})`).join(", ")}`);
|
|
34594
|
+
}
|
|
34595
|
+
if (blockers.length > 0) {
|
|
34596
|
+
parts.push(`
|
|
34597
|
+
\u26D4 ${blockers.length} blocking message(s):`);
|
|
34598
|
+
for (const b of blockers.slice(0, 5)) {
|
|
34599
|
+
parts.push(` \u2022 [#${b.id}] ${b.from}: ${b.content}${b.content.length > 150 ? "..." : ""}`);
|
|
34600
|
+
}
|
|
34601
|
+
}
|
|
34602
|
+
const highPri = rows.filter((m) => m.priority === "high" || m.priority === "urgent").slice(0, 5);
|
|
34603
|
+
if (highPri.length > 0) {
|
|
34604
|
+
parts.push(`
|
|
34605
|
+
\uD83D\uDD34 High priority (${highPri.length}):`);
|
|
34606
|
+
for (const m of highPri) {
|
|
34607
|
+
parts.push(` \u2022 [${m.priority}] ${m.from_agent}: ${m.content.slice(0, 100)}`);
|
|
34608
|
+
}
|
|
34609
|
+
}
|
|
34610
|
+
parts.push(`
|
|
34611
|
+
Last message: ${rows[0]?.created_at?.slice(0, 16) ?? "?"}`);
|
|
34612
|
+
return { content: [{ type: "text", text: parts.join(`
|
|
34613
|
+
`) }] };
|
|
34614
|
+
});
|
|
34525
34615
|
server.registerTool("get_reaction_summary", {
|
|
34526
34616
|
description: "Get emoji reaction counts and agent lists for a message.",
|
|
34527
34617
|
inputSchema: {
|
|
@@ -34929,6 +35019,8 @@ var init_mcp2 = __esm(() => {
|
|
|
34929
35019
|
get_summary: "Structured conversation summary: participants, topics, key messages, blockers. Required: session_id? or space?. Optional: limit?",
|
|
34930
35020
|
get_topics: "Extract topics from space or session. Optional: space?, session_id?, limit?",
|
|
34931
35021
|
trending_topics: "Trending topics across all messages. Optional: hours?, project_id?, top_n?",
|
|
35022
|
+
set_space_topic: "Set current topic/status of a space. Required: space, topic (pass null to clear).",
|
|
35023
|
+
get_space_topic: "Get current topic/status of a space. Required: space.",
|
|
34932
35024
|
get_session_activity: "Get activity metrics for a session: velocity, agents, reply ratio, reactions, trending. Required: session_id",
|
|
34933
35025
|
hot_sessions: "List conversations by hotness score (velocity, reactions, replies, priority, blockers). Optional: limit?, min_score?, space?, project_id?",
|
|
34934
35026
|
add_reaction: "Add emoji reaction to a message. Required: message_id, emoji. Optional: from?",
|
package/bin/mcp.js
CHANGED
|
@@ -6702,6 +6702,9 @@ function getDb() {
|
|
|
6702
6702
|
if (!spaceColNames.includes("archived_at")) {
|
|
6703
6703
|
db.exec("ALTER TABLE spaces ADD COLUMN archived_at TEXT");
|
|
6704
6704
|
}
|
|
6705
|
+
if (!spaceColNames.includes("topic")) {
|
|
6706
|
+
db.exec("ALTER TABLE spaces ADD COLUMN topic TEXT");
|
|
6707
|
+
}
|
|
6705
6708
|
const msgCols2 = db.prepare("PRAGMA table_info(messages)").all();
|
|
6706
6709
|
const colNames2 = msgCols2.map((c) => c.name);
|
|
6707
6710
|
if (!colNames2.includes("edited_at")) {
|
|
@@ -30932,7 +30935,7 @@ function getGraphStats() {
|
|
|
30932
30935
|
// package.json
|
|
30933
30936
|
var package_default = {
|
|
30934
30937
|
name: "@hasna/conversations",
|
|
30935
|
-
version: "0.2.
|
|
30938
|
+
version: "0.2.13",
|
|
30936
30939
|
description: "Real-time CLI messaging for AI agents",
|
|
30937
30940
|
type: "module",
|
|
30938
30941
|
bin: {
|
|
@@ -31827,6 +31830,31 @@ server.registerTool("trending_topics", {
|
|
|
31827
31830
|
const topics = getTrendingTopics({ hours: args.hours, project_id: args.project_id, top_n: args.top_n });
|
|
31828
31831
|
return { content: [{ type: "text", text: JSON.stringify(topics) }] };
|
|
31829
31832
|
});
|
|
31833
|
+
server.registerTool("set_space_topic", {
|
|
31834
|
+
description: "Set the current topic/status of a space. Separate from the static description \u2014 use this for live status like '\uD83D\uDD34 blocked on auth' or '\u2705 shipping v2'.",
|
|
31835
|
+
inputSchema: {
|
|
31836
|
+
space: exports_external.string().describe("Space name"),
|
|
31837
|
+
topic: exports_external.string().nullable().describe("New topic/status. Pass null to clear.")
|
|
31838
|
+
}
|
|
31839
|
+
}, async (args) => {
|
|
31840
|
+
const db2 = (await Promise.resolve().then(() => (init_db(), exports_db))).getDb();
|
|
31841
|
+
const existing = db2.prepare("SELECT name FROM spaces WHERE name = ?").get(args.space);
|
|
31842
|
+
if (!existing) {
|
|
31843
|
+
return { content: [{ type: "text", text: `Space not found: ${args.space}` }], isError: true };
|
|
31844
|
+
}
|
|
31845
|
+
db2.prepare("UPDATE spaces SET topic = ? WHERE name = ?").run(args.topic ?? null, args.space);
|
|
31846
|
+
return { content: [{ type: "text", text: args.topic ? `Topic set: ${args.topic}` : "Topic cleared" }] };
|
|
31847
|
+
});
|
|
31848
|
+
server.registerTool("get_space_topic", {
|
|
31849
|
+
description: "Get the current topic/status of a space.",
|
|
31850
|
+
inputSchema: { space: exports_external.string() }
|
|
31851
|
+
}, async (args) => {
|
|
31852
|
+
const db2 = (await Promise.resolve().then(() => (init_db(), exports_db))).getDb();
|
|
31853
|
+
const row = db2.prepare("SELECT topic FROM spaces WHERE name = ?").get(args.space);
|
|
31854
|
+
if (!row)
|
|
31855
|
+
return { content: [{ type: "text", text: `Space not found: ${args.space}` }], isError: true };
|
|
31856
|
+
return { content: [{ type: "text", text: JSON.stringify({ space: args.space, topic: row.topic }) }] };
|
|
31857
|
+
});
|
|
31830
31858
|
server.registerTool("get_session_activity", {
|
|
31831
31859
|
description: "Get activity metrics for a session: message velocity, unique agents, reply ratio, reaction count, trending status.",
|
|
31832
31860
|
inputSchema: {
|
|
@@ -31891,6 +31919,68 @@ server.registerTool("get_reactions", {
|
|
|
31891
31919
|
const reactions = getReactions(args.message_id);
|
|
31892
31920
|
return { content: [{ type: "text", text: JSON.stringify(reactions) }] };
|
|
31893
31921
|
});
|
|
31922
|
+
server.registerTool("summarize_space", {
|
|
31923
|
+
description: "Get a structured catch-up summary of a space for a time window \u2014 participants, topics, key messages, blockers, activity counts. No LLM required.",
|
|
31924
|
+
inputSchema: {
|
|
31925
|
+
space: exports_external.string().describe("Space name"),
|
|
31926
|
+
since: exports_external.string().optional().describe("ISO 8601 timestamp \u2014 only include messages after this. Defaults to 24h ago."),
|
|
31927
|
+
limit: exports_external.coerce.number().optional().describe("Max messages to analyze (default: 100)")
|
|
31928
|
+
}
|
|
31929
|
+
}, async (args) => {
|
|
31930
|
+
const { space, since, limit } = args;
|
|
31931
|
+
const sinceTs = since ?? new Date(Date.now() - 24 * 60 * 60 * 1000).toISOString();
|
|
31932
|
+
const db2 = (await Promise.resolve().then(() => (init_db(), exports_db))).getDb();
|
|
31933
|
+
const total = db2.prepare("SELECT COUNT(*) as c FROM messages WHERE space = ? AND created_at >= ?").get(space, sinceTs).c;
|
|
31934
|
+
if (total === 0) {
|
|
31935
|
+
return { content: [{ type: "text", text: `No messages in #${space} since ${sinceTs.slice(0, 10)}.` }] };
|
|
31936
|
+
}
|
|
31937
|
+
const rows = db2.prepare(`SELECT * FROM messages WHERE space = ? AND created_at >= ? ORDER BY created_at DESC LIMIT ?`).all(space, sinceTs, limit ?? 100);
|
|
31938
|
+
const agents = new Set;
|
|
31939
|
+
const agentCounts = {};
|
|
31940
|
+
const blockers = [];
|
|
31941
|
+
const mentions = {};
|
|
31942
|
+
for (const m of rows) {
|
|
31943
|
+
const from = m.from_agent;
|
|
31944
|
+
agents.add(from);
|
|
31945
|
+
agentCounts[from] = (agentCounts[from] ?? 0) + 1;
|
|
31946
|
+
if (m.blocking) {
|
|
31947
|
+
blockers.push({ id: m.id, from, content: m.content.slice(0, 150), created_at: m.created_at });
|
|
31948
|
+
}
|
|
31949
|
+
const mentionedAgents = m.content.match(/@([a-zA-Z0-9_-]+)/g) ?? [];
|
|
31950
|
+
for (const mention of mentionedAgents) {
|
|
31951
|
+
const a = mention.slice(1).toLowerCase();
|
|
31952
|
+
mentions[a] = (mentions[a] ?? 0) + 1;
|
|
31953
|
+
}
|
|
31954
|
+
}
|
|
31955
|
+
const parts = [
|
|
31956
|
+
`Space: #${space} | Since: ${sinceTs.slice(0, 10)} | ${total} messages (showing ${rows.length})`,
|
|
31957
|
+
`
|
|
31958
|
+
Participants (${agents.size}): ${Object.entries(agentCounts).sort((a, b) => b[1] - a[1]).map(([n, c]) => `${n}(${c})`).join(", ")}`
|
|
31959
|
+
];
|
|
31960
|
+
if (Object.keys(mentions).length > 0) {
|
|
31961
|
+
const topMentions = Object.entries(mentions).sort((a, b) => b[1] - a[1]).slice(0, 5);
|
|
31962
|
+
parts.push(`Most mentioned: ${topMentions.map(([n, c]) => `@${n}(${c})`).join(", ")}`);
|
|
31963
|
+
}
|
|
31964
|
+
if (blockers.length > 0) {
|
|
31965
|
+
parts.push(`
|
|
31966
|
+
\u26D4 ${blockers.length} blocking message(s):`);
|
|
31967
|
+
for (const b of blockers.slice(0, 5)) {
|
|
31968
|
+
parts.push(` \u2022 [#${b.id}] ${b.from}: ${b.content}${b.content.length > 150 ? "..." : ""}`);
|
|
31969
|
+
}
|
|
31970
|
+
}
|
|
31971
|
+
const highPri = rows.filter((m) => m.priority === "high" || m.priority === "urgent").slice(0, 5);
|
|
31972
|
+
if (highPri.length > 0) {
|
|
31973
|
+
parts.push(`
|
|
31974
|
+
\uD83D\uDD34 High priority (${highPri.length}):`);
|
|
31975
|
+
for (const m of highPri) {
|
|
31976
|
+
parts.push(` \u2022 [${m.priority}] ${m.from_agent}: ${m.content.slice(0, 100)}`);
|
|
31977
|
+
}
|
|
31978
|
+
}
|
|
31979
|
+
parts.push(`
|
|
31980
|
+
Last message: ${rows[0]?.created_at?.slice(0, 16) ?? "?"}`);
|
|
31981
|
+
return { content: [{ type: "text", text: parts.join(`
|
|
31982
|
+
`) }] };
|
|
31983
|
+
});
|
|
31894
31984
|
server.registerTool("get_reaction_summary", {
|
|
31895
31985
|
description: "Get emoji reaction counts and agent lists for a message.",
|
|
31896
31986
|
inputSchema: {
|
|
@@ -32298,6 +32388,8 @@ server.registerTool("describe_tools", {
|
|
|
32298
32388
|
get_summary: "Structured conversation summary: participants, topics, key messages, blockers. Required: session_id? or space?. Optional: limit?",
|
|
32299
32389
|
get_topics: "Extract topics from space or session. Optional: space?, session_id?, limit?",
|
|
32300
32390
|
trending_topics: "Trending topics across all messages. Optional: hours?, project_id?, top_n?",
|
|
32391
|
+
set_space_topic: "Set current topic/status of a space. Required: space, topic (pass null to clear).",
|
|
32392
|
+
get_space_topic: "Get current topic/status of a space. Required: space.",
|
|
32301
32393
|
get_session_activity: "Get activity metrics for a session: velocity, agents, reply ratio, reactions, trending. Required: session_id",
|
|
32302
32394
|
hot_sessions: "List conversations by hotness score (velocity, reactions, replies, priority, blockers). Optional: limit?, min_score?, space?, project_id?",
|
|
32303
32395
|
add_reaction: "Add emoji reaction to a message. Required: message_id, emoji. Optional: from?",
|
package/dist/index.js
CHANGED
|
@@ -229,6 +229,9 @@ function getDb() {
|
|
|
229
229
|
if (!spaceColNames.includes("archived_at")) {
|
|
230
230
|
db.exec("ALTER TABLE spaces ADD COLUMN archived_at TEXT");
|
|
231
231
|
}
|
|
232
|
+
if (!spaceColNames.includes("topic")) {
|
|
233
|
+
db.exec("ALTER TABLE spaces ADD COLUMN topic TEXT");
|
|
234
|
+
}
|
|
232
235
|
const msgCols2 = db.prepare("PRAGMA table_info(messages)").all();
|
|
233
236
|
const colNames2 = msgCols2.map((c) => c.name);
|
|
234
237
|
if (!colNames2.includes("edited_at")) {
|