@hasna/conversations 0.2.12 → 0.2.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/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")) {
@@ -2803,6 +2806,14 @@ function markMentionsRead(agent, space) {
2803
2806
  const result = db2.prepare("UPDATE message_mentions SET notified_at = strftime('%Y-%m-%dT%H:%M:%f', 'now') WHERE mentioned_agent = ? AND notified_at IS NULL").run(agent);
2804
2807
  return result.changes;
2805
2808
  }
2809
+ function markUnreadByIds(ids) {
2810
+ if (ids.length === 0)
2811
+ return 0;
2812
+ const db2 = getDb();
2813
+ const placeholders = ids.map(() => "?").join(",");
2814
+ const result = db2.prepare(`UPDATE messages SET read_at = NULL WHERE id IN (${placeholders})`).run(...ids);
2815
+ return result.changes;
2816
+ }
2806
2817
  var init_messages = __esm(() => {
2807
2818
  init_db();
2808
2819
  init_webhooks();
@@ -4442,7 +4453,7 @@ var init_poll = __esm(() => {
4442
4453
  var require_package = __commonJS((exports, module) => {
4443
4454
  module.exports = {
4444
4455
  name: "@hasna/conversations",
4445
- version: "0.2.12",
4456
+ version: "0.2.14",
4446
4457
  description: "Real-time CLI messaging for AI agents",
4447
4458
  type: "module",
4448
4459
  bin: {
@@ -33782,6 +33793,20 @@ var init_mcp2 = __esm(() => {
33782
33793
  content: [{ type: "text", text: JSON.stringify({ marked_read: count }) }]
33783
33794
  };
33784
33795
  });
33796
+ server.registerTool("mark_unread", {
33797
+ description: "Re-flag a message (or messages) as unread so it re-appears in read_messages(unread_only:true). Useful for bookmarking messages to action later.",
33798
+ inputSchema: {
33799
+ message_id: exports_external.coerce.number().optional().describe("Single message ID"),
33800
+ ids: exports_external.array(exports_external.coerce.number()).optional().describe("Multiple message IDs")
33801
+ }
33802
+ }, async (args) => {
33803
+ if (!args.message_id && (!args.ids || args.ids.length === 0)) {
33804
+ return { content: [{ type: "text", text: "Provide message_id or ids" }], isError: true };
33805
+ }
33806
+ const ids = args.ids ?? (args.message_id ? [args.message_id] : []);
33807
+ const count = markUnreadByIds(ids);
33808
+ return { content: [{ type: "text", text: JSON.stringify({ marked_unread: count }) }] };
33809
+ });
33785
33810
  server.registerTool("mark_space_read", {
33786
33811
  description: "Mark ALL messages in a space as read without fetching them. Use this on busy spaces (200+ messages) where read_messages would overflow tokens.",
33787
33812
  inputSchema: {
@@ -34458,6 +34483,31 @@ var init_mcp2 = __esm(() => {
34458
34483
  const topics = getTrendingTopics({ hours: args.hours, project_id: args.project_id, top_n: args.top_n });
34459
34484
  return { content: [{ type: "text", text: JSON.stringify(topics) }] };
34460
34485
  });
34486
+ server.registerTool("set_space_topic", {
34487
+ 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'.",
34488
+ inputSchema: {
34489
+ space: exports_external.string().describe("Space name"),
34490
+ topic: exports_external.string().nullable().describe("New topic/status. Pass null to clear.")
34491
+ }
34492
+ }, async (args) => {
34493
+ const db2 = (await Promise.resolve().then(() => (init_db(), exports_db))).getDb();
34494
+ const existing = db2.prepare("SELECT name FROM spaces WHERE name = ?").get(args.space);
34495
+ if (!existing) {
34496
+ return { content: [{ type: "text", text: `Space not found: ${args.space}` }], isError: true };
34497
+ }
34498
+ db2.prepare("UPDATE spaces SET topic = ? WHERE name = ?").run(args.topic ?? null, args.space);
34499
+ return { content: [{ type: "text", text: args.topic ? `Topic set: ${args.topic}` : "Topic cleared" }] };
34500
+ });
34501
+ server.registerTool("get_space_topic", {
34502
+ description: "Get the current topic/status of a space.",
34503
+ inputSchema: { space: exports_external.string() }
34504
+ }, async (args) => {
34505
+ const db2 = (await Promise.resolve().then(() => (init_db(), exports_db))).getDb();
34506
+ const row = db2.prepare("SELECT topic FROM spaces WHERE name = ?").get(args.space);
34507
+ if (!row)
34508
+ return { content: [{ type: "text", text: `Space not found: ${args.space}` }], isError: true };
34509
+ return { content: [{ type: "text", text: JSON.stringify({ space: args.space, topic: row.topic }) }] };
34510
+ });
34461
34511
  server.registerTool("get_session_activity", {
34462
34512
  description: "Get activity metrics for a session: message velocity, unique agents, reply ratio, reaction count, trending status.",
34463
34513
  inputSchema: {
@@ -34991,6 +35041,8 @@ Last message: ${rows[0]?.created_at?.slice(0, 16) ?? "?"}`);
34991
35041
  get_summary: "Structured conversation summary: participants, topics, key messages, blockers. Required: session_id? or space?. Optional: limit?",
34992
35042
  get_topics: "Extract topics from space or session. Optional: space?, session_id?, limit?",
34993
35043
  trending_topics: "Trending topics across all messages. Optional: hours?, project_id?, top_n?",
35044
+ set_space_topic: "Set current topic/status of a space. Required: space, topic (pass null to clear).",
35045
+ get_space_topic: "Get current topic/status of a space. Required: space.",
34994
35046
  get_session_activity: "Get activity metrics for a session: velocity, agents, reply ratio, reactions, trending. Required: session_id",
34995
35047
  hot_sessions: "List conversations by hotness score (velocity, reactions, replies, priority, blockers). Optional: limit?, min_score?, space?, project_id?",
34996
35048
  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")) {
@@ -29306,6 +29309,14 @@ function markMentionsRead(agent, space) {
29306
29309
  const result = db2.prepare("UPDATE message_mentions SET notified_at = strftime('%Y-%m-%dT%H:%M:%f', 'now') WHERE mentioned_agent = ? AND notified_at IS NULL").run(agent);
29307
29310
  return result.changes;
29308
29311
  }
29312
+ function markUnreadByIds(ids) {
29313
+ if (ids.length === 0)
29314
+ return 0;
29315
+ const db2 = getDb();
29316
+ const placeholders = ids.map(() => "?").join(",");
29317
+ const result = db2.prepare(`UPDATE messages SET read_at = NULL WHERE id IN (${placeholders})`).run(...ids);
29318
+ return result.changes;
29319
+ }
29309
29320
 
29310
29321
  // src/lib/sessions.ts
29311
29322
  init_db();
@@ -30932,7 +30943,7 @@ function getGraphStats() {
30932
30943
  // package.json
30933
30944
  var package_default = {
30934
30945
  name: "@hasna/conversations",
30935
- version: "0.2.12",
30946
+ version: "0.2.14",
30936
30947
  description: "Real-time CLI messaging for AI agents",
30937
30948
  type: "module",
30938
30949
  bin: {
@@ -31151,6 +31162,20 @@ server.registerTool("mark_read", {
31151
31162
  content: [{ type: "text", text: JSON.stringify({ marked_read: count }) }]
31152
31163
  };
31153
31164
  });
31165
+ server.registerTool("mark_unread", {
31166
+ description: "Re-flag a message (or messages) as unread so it re-appears in read_messages(unread_only:true). Useful for bookmarking messages to action later.",
31167
+ inputSchema: {
31168
+ message_id: exports_external.coerce.number().optional().describe("Single message ID"),
31169
+ ids: exports_external.array(exports_external.coerce.number()).optional().describe("Multiple message IDs")
31170
+ }
31171
+ }, async (args) => {
31172
+ if (!args.message_id && (!args.ids || args.ids.length === 0)) {
31173
+ return { content: [{ type: "text", text: "Provide message_id or ids" }], isError: true };
31174
+ }
31175
+ const ids = args.ids ?? (args.message_id ? [args.message_id] : []);
31176
+ const count = markUnreadByIds(ids);
31177
+ return { content: [{ type: "text", text: JSON.stringify({ marked_unread: count }) }] };
31178
+ });
31154
31179
  server.registerTool("mark_space_read", {
31155
31180
  description: "Mark ALL messages in a space as read without fetching them. Use this on busy spaces (200+ messages) where read_messages would overflow tokens.",
31156
31181
  inputSchema: {
@@ -31827,6 +31852,31 @@ server.registerTool("trending_topics", {
31827
31852
  const topics = getTrendingTopics({ hours: args.hours, project_id: args.project_id, top_n: args.top_n });
31828
31853
  return { content: [{ type: "text", text: JSON.stringify(topics) }] };
31829
31854
  });
31855
+ server.registerTool("set_space_topic", {
31856
+ 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'.",
31857
+ inputSchema: {
31858
+ space: exports_external.string().describe("Space name"),
31859
+ topic: exports_external.string().nullable().describe("New topic/status. Pass null to clear.")
31860
+ }
31861
+ }, async (args) => {
31862
+ const db2 = (await Promise.resolve().then(() => (init_db(), exports_db))).getDb();
31863
+ const existing = db2.prepare("SELECT name FROM spaces WHERE name = ?").get(args.space);
31864
+ if (!existing) {
31865
+ return { content: [{ type: "text", text: `Space not found: ${args.space}` }], isError: true };
31866
+ }
31867
+ db2.prepare("UPDATE spaces SET topic = ? WHERE name = ?").run(args.topic ?? null, args.space);
31868
+ return { content: [{ type: "text", text: args.topic ? `Topic set: ${args.topic}` : "Topic cleared" }] };
31869
+ });
31870
+ server.registerTool("get_space_topic", {
31871
+ description: "Get the current topic/status of a space.",
31872
+ inputSchema: { space: exports_external.string() }
31873
+ }, async (args) => {
31874
+ const db2 = (await Promise.resolve().then(() => (init_db(), exports_db))).getDb();
31875
+ const row = db2.prepare("SELECT topic FROM spaces WHERE name = ?").get(args.space);
31876
+ if (!row)
31877
+ return { content: [{ type: "text", text: `Space not found: ${args.space}` }], isError: true };
31878
+ return { content: [{ type: "text", text: JSON.stringify({ space: args.space, topic: row.topic }) }] };
31879
+ });
31830
31880
  server.registerTool("get_session_activity", {
31831
31881
  description: "Get activity metrics for a session: message velocity, unique agents, reply ratio, reaction count, trending status.",
31832
31882
  inputSchema: {
@@ -32360,6 +32410,8 @@ server.registerTool("describe_tools", {
32360
32410
  get_summary: "Structured conversation summary: participants, topics, key messages, blockers. Required: session_id? or space?. Optional: limit?",
32361
32411
  get_topics: "Extract topics from space or session. Optional: space?, session_id?, limit?",
32362
32412
  trending_topics: "Trending topics across all messages. Optional: hours?, project_id?, top_n?",
32413
+ set_space_topic: "Set current topic/status of a space. Required: space, topic (pass null to clear).",
32414
+ get_space_topic: "Get current topic/status of a space. Required: space.",
32363
32415
  get_session_activity: "Get activity metrics for a session: velocity, agents, reply ratio, reactions, trending. Required: session_id",
32364
32416
  hot_sessions: "List conversations by hotness score (velocity, reactions, replies, priority, blockers). Optional: limit?, min_score?, space?, project_id?",
32365
32417
  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")) {
@@ -88,3 +88,7 @@ export declare function getMessagesForAgent(agent: string, opts?: {
88
88
  }>;
89
89
  /** Mark mentions as notified (agent has seen them). */
90
90
  export declare function markMentionsRead(agent: string, space?: string): number;
91
+ /** Mark a specific message as unread (resets read_at to null). */
92
+ export declare function markUnread(messageId: number): number;
93
+ /** Mark multiple messages as unread. */
94
+ export declare function markUnreadByIds(ids: number[]): number;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hasna/conversations",
3
- "version": "0.2.12",
3
+ "version": "0.2.14",
4
4
  "description": "Real-time CLI messaging for AI agents",
5
5
  "type": "module",
6
6
  "bin": {