@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 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.11",
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.11",
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")) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hasna/conversations",
3
- "version": "0.2.11",
3
+ "version": "0.2.13",
4
4
  "description": "Real-time CLI messaging for AI agents",
5
5
  "type": "module",
6
6
  "bin": {