@hasna/conversations 0.0.3 → 0.0.5

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 CHANGED
@@ -31,10 +31,10 @@ npx @hasna/conversations
31
31
 
32
32
  ```bash
33
33
  # Basic message
34
- convo send --to claude-code "Hello from codex"
34
+ conversations send --to claude-code "Hello from codex"
35
35
 
36
36
  # With context
37
- convo send --to claude-code "Check this branch" \
37
+ conversations send --to claude-code "Check this branch" \
38
38
  --from codex \
39
39
  --priority high \
40
40
  --working-dir /path/to/project \
@@ -42,40 +42,40 @@ convo send --to claude-code "Check this branch" \
42
42
  --branch feature/auth
43
43
 
44
44
  # With metadata
45
- convo send --to gemini "Deploy ready" --metadata '{"env":"staging"}'
45
+ conversations send --to gemini "Deploy ready" --metadata '{"env":"staging"}'
46
46
  ```
47
47
 
48
48
  ### Read Messages
49
49
 
50
50
  ```bash
51
51
  # Read all messages for an agent
52
- convo read --to codex
52
+ conversations read --to codex
53
53
 
54
54
  # Unread only, as JSON
55
- convo read --to codex --unread --json
55
+ conversations read --to codex --unread --json
56
56
 
57
57
  # Filter by session
58
- convo read --session alice-bob-abc123
58
+ conversations read --session alice-bob-abc123
59
59
 
60
60
  # Read and mark as read
61
- convo read --to codex --unread --mark-read
61
+ conversations read --to codex --unread --mark-read
62
62
  ```
63
63
 
64
64
  ### Sessions
65
65
 
66
66
  ```bash
67
67
  # List all sessions
68
- convo sessions
68
+ conversations sessions
69
69
 
70
70
  # Sessions for a specific agent
71
- convo sessions --agent claude-code --json
71
+ conversations sessions --agent claude-code --json
72
72
  ```
73
73
 
74
74
  ### Reply
75
75
 
76
76
  ```bash
77
77
  # Reply to a message (auto-resolves session and recipient)
78
- convo reply --to 42 "Got it, working on it now"
78
+ conversations reply --to 42 "Got it, working on it now"
79
79
  ```
80
80
 
81
81
  ### Channels
@@ -84,44 +84,44 @@ Channels are broadcast spaces — any agent can post, all members can read.
84
84
 
85
85
  ```bash
86
86
  # Create a channel
87
- convo channel create deployments --description "Deployment notifications"
87
+ conversations channel create deployments --description "Deployment notifications"
88
88
 
89
89
  # List channels
90
- convo channel list
90
+ conversations channel list
91
91
 
92
92
  # Join a channel
93
- convo channel join deployments --from codex
93
+ conversations channel join deployments --from codex
94
94
 
95
95
  # Send to a channel
96
- convo channel send deployments "v1.2 deployed to staging" --from ops
96
+ conversations channel send deployments "v1.2 deployed to staging" --from ops
97
97
 
98
98
  # Read channel messages
99
- convo channel read deployments
99
+ conversations channel read deployments
100
100
 
101
101
  # Leave a channel
102
- convo channel leave deployments --from codex
102
+ conversations channel leave deployments --from codex
103
103
 
104
104
  # List members
105
- convo channel members deployments
105
+ conversations channel members deployments
106
106
  ```
107
107
 
108
108
  ### Mark Read
109
109
 
110
110
  ```bash
111
111
  # Mark specific messages
112
- convo mark-read 1 2 3 --agent codex
112
+ conversations mark-read 1 2 3 --agent codex
113
113
 
114
114
  # Mark entire session
115
- convo mark-read --session abc123 --agent codex
115
+ conversations mark-read --session abc123 --agent codex
116
116
 
117
117
  # Mark entire channel
118
- convo mark-read --channel deployments --agent codex
118
+ conversations mark-read --channel deployments --agent codex
119
119
  ```
120
120
 
121
121
  ### Status
122
122
 
123
123
  ```bash
124
- convo status
124
+ conversations status
125
125
  # Conversations Status
126
126
  # DB Path: ~/.conversations/messages.db
127
127
  # Messages: 47
@@ -132,7 +132,7 @@ convo status
132
132
  ### Interactive TUI
133
133
 
134
134
  ```bash
135
- convo
135
+ conversations
136
136
  ```
137
137
 
138
138
  Arrow keys to navigate sessions, Enter to open, `n` for new conversation, `q` to quit, Esc to go back.
@@ -142,7 +142,7 @@ Arrow keys to navigate sessions, Enter to open, `n` for new conversation, `q` to
142
142
  For native AI agent integration via the Model Context Protocol:
143
143
 
144
144
  ```bash
145
- convo mcp
145
+ conversations mcp
146
146
  ```
147
147
 
148
148
  ### Agent Configuration
@@ -222,20 +222,20 @@ const channelMsgs = readMessages({ channel: "deploys" });
222
222
  ## Architecture
223
223
 
224
224
  ```
225
- ┌─────────────┐ ┌──────────────┐ ┌─────────────┐
226
- Ink TUI Headless MCP Server
227
- `convo` `convo send`│ `convo mcp`
228
- └──────┬──────┘ └──────┬───────┘ └──────┬───────┘
229
-
230
- └──────────┬───────┴───────────────────┘
231
-
232
- ┌───────▼────────┐
233
- Core Library
234
- │ SQLite WAL │
235
- │ 200ms polling │
236
- └────────────────┘
237
-
238
- ~/.conversations/messages.db
225
+ ┌──────────────────┐ ┌─────────────────────┐ ┌──────────────────────┐
226
+ Ink TUI Headless MCP Server
227
+ `conversations` `conversations send`│ `conversations mcp`
228
+ └────────┬─────────┘ └──────────┬──────────┘ └──────────┬───────────┘
229
+
230
+ └───────────┬───────────┴────────────────────────┘
231
+
232
+ ┌───────▼────────┐
233
+ Core Library
234
+ │ SQLite WAL │
235
+ │ 200ms polling │
236
+ └────────────────┘
237
+
238
+ ~/.conversations/messages.db
239
239
  ```
240
240
 
241
241
  - **SQLite WAL mode** for concurrent read/write across processes
package/bin/index.js CHANGED
@@ -31003,7 +31003,7 @@ var init_mcp2 = __esm(() => {
31003
31003
  init_channels();
31004
31004
  server = new McpServer({
31005
31005
  name: "conversations",
31006
- version: "0.0.3"
31006
+ version: "0.0.5"
31007
31007
  });
31008
31008
  server.registerTool("send_message", {
31009
31009
  title: "Send Message",
@@ -31247,10 +31247,10 @@ init_channels();
31247
31247
  init_db();
31248
31248
  import chalk2 from "chalk";
31249
31249
  import { render } from "ink";
31250
- import React7 from "react";
31250
+ import React8 from "react";
31251
31251
 
31252
31252
  // src/cli/components/App.tsx
31253
- import { useState as useState5 } from "react";
31253
+ import { useState as useState6 } from "react";
31254
31254
  import { Box as Box6, Text as Text7, useApp, useInput as useInput5 } from "ink";
31255
31255
 
31256
31256
  // node_modules/ink-text-input/build/index.js
@@ -31346,6 +31346,7 @@ function TextInput({ value: originalValue, placeholder = "", focus = true, mask,
31346
31346
  var build_default = TextInput;
31347
31347
 
31348
31348
  // src/cli/components/SessionList.tsx
31349
+ import { useState as useState3, useEffect as useEffect3 } from "react";
31349
31350
  import { Box as Box3, Text as Text4, useInput as useInput3 } from "ink";
31350
31351
 
31351
31352
  // node_modules/ink-select-input/build/Indicator.js
@@ -31753,23 +31754,37 @@ function SelectInput({ items = [], isFocused = true, initialIndex = 0, indicator
31753
31754
  var SelectInput_default = SelectInput;
31754
31755
  // src/cli/components/SessionList.tsx
31755
31756
  init_sessions();
31757
+ init_channels();
31756
31758
  import { jsxDEV } from "react/jsx-dev-runtime";
31757
- function SessionList({ agent, onSelect, onNew }) {
31758
- const sessions = listSessions(agent);
31759
+ function SessionList({ agent, onSelect, onSelectChannel, onNew }) {
31760
+ const [sessions, setSessions] = useState3(() => listSessions(agent));
31761
+ const [channels, setChannels] = useState3(() => listChannels());
31762
+ useEffect3(() => {
31763
+ const timer = setInterval(() => {
31764
+ setSessions(listSessions(agent));
31765
+ setChannels(listChannels());
31766
+ }, 1000);
31767
+ return () => clearInterval(timer);
31768
+ }, [agent]);
31759
31769
  useInput3((input) => {
31760
31770
  if (input === "n")
31761
31771
  onNew();
31762
31772
  });
31763
- const items = sessions.map((s) => {
31773
+ const channelItems = channels.map((ch) => ({
31774
+ label: `#${ch.name}${ch.description ? ` \u2014 ${ch.description}` : ""} (${ch.message_count} msgs, ${ch.member_count} members)`,
31775
+ value: `channel:${ch.name}`
31776
+ }));
31777
+ const dmSessions = sessions.filter((s) => !s.session_id.startsWith("channel:"));
31778
+ const sessionItems = dmSessions.map((s) => {
31764
31779
  const others = s.participants.filter((p) => p !== agent).join(", ") || agent;
31765
31780
  const unread = s.unread_count > 0 ? ` (${s.unread_count} unread)` : "";
31766
31781
  return {
31767
31782
  label: `${others} \u2014 ${s.message_count} msgs${unread}`,
31768
- value: s.session_id,
31769
- session: s
31783
+ value: s.session_id
31770
31784
  };
31771
31785
  });
31772
- if (items.length === 0) {
31786
+ const allItems = [...channelItems, ...sessionItems];
31787
+ if (allItems.length === 0) {
31773
31788
  return /* @__PURE__ */ jsxDEV(Box3, {
31774
31789
  flexDirection: "column",
31775
31790
  padding: 1,
@@ -31781,17 +31796,37 @@ function SessionList({ agent, onSelect, onNew }) {
31781
31796
  }, undefined, false, undefined, this),
31782
31797
  /* @__PURE__ */ jsxDEV(Text4, {
31783
31798
  dimColor: true,
31784
- children: "No conversations yet."
31785
- }, undefined, false, undefined, this),
31786
- /* @__PURE__ */ jsxDEV(Text4, {
31787
- dimColor: true,
31788
31799
  children: [
31789
- "Press ",
31800
+ " as ",
31801
+ /* @__PURE__ */ jsxDEV(Text4, {
31802
+ color: "yellow",
31803
+ children: agent
31804
+ }, undefined, false, undefined, this)
31805
+ ]
31806
+ }, undefined, true, undefined, this),
31807
+ /* @__PURE__ */ jsxDEV(Box3, {
31808
+ marginTop: 1,
31809
+ children: [
31810
+ /* @__PURE__ */ jsxDEV(Text4, {
31811
+ dimColor: true,
31812
+ children: "No conversations yet. Press "
31813
+ }, undefined, false, undefined, this),
31790
31814
  /* @__PURE__ */ jsxDEV(Text4, {
31791
31815
  bold: true,
31792
31816
  children: "n"
31793
31817
  }, undefined, false, undefined, this),
31794
- " to start a new conversation."
31818
+ /* @__PURE__ */ jsxDEV(Text4, {
31819
+ dimColor: true,
31820
+ children: " to start one, or "
31821
+ }, undefined, false, undefined, this),
31822
+ /* @__PURE__ */ jsxDEV(Text4, {
31823
+ bold: true,
31824
+ children: "q"
31825
+ }, undefined, false, undefined, this),
31826
+ /* @__PURE__ */ jsxDEV(Text4, {
31827
+ dimColor: true,
31828
+ children: " to quit."
31829
+ }, undefined, false, undefined, this)
31795
31830
  ]
31796
31831
  }, undefined, true, undefined, this)
31797
31832
  ]
@@ -31803,6 +31838,7 @@ function SessionList({ agent, onSelect, onNew }) {
31803
31838
  children: [
31804
31839
  /* @__PURE__ */ jsxDEV(Box3, {
31805
31840
  marginBottom: 1,
31841
+ flexDirection: "column",
31806
31842
  children: [
31807
31843
  /* @__PURE__ */ jsxDEV(Text4, {
31808
31844
  bold: true,
@@ -31811,16 +31847,27 @@ function SessionList({ agent, onSelect, onNew }) {
31811
31847
  }, undefined, false, undefined, this),
31812
31848
  /* @__PURE__ */ jsxDEV(Text4, {
31813
31849
  dimColor: true,
31814
- children: " (n: new, q: quit)"
31815
- }, undefined, false, undefined, this)
31850
+ children: [
31851
+ " as ",
31852
+ /* @__PURE__ */ jsxDEV(Text4, {
31853
+ color: "yellow",
31854
+ children: agent
31855
+ }, undefined, false, undefined, this),
31856
+ " (n: new, q: quit)"
31857
+ ]
31858
+ }, undefined, true, undefined, this)
31816
31859
  ]
31817
31860
  }, undefined, true, undefined, this),
31818
31861
  /* @__PURE__ */ jsxDEV(SelectInput_default, {
31819
- items,
31862
+ items: allItems,
31820
31863
  onSelect: (item) => {
31821
- const session = sessions.find((s) => s.session_id === item.value);
31822
- if (session)
31823
- onSelect(session);
31864
+ if (item.value.startsWith("channel:")) {
31865
+ onSelectChannel(item.value.slice(8));
31866
+ } else {
31867
+ const session = dmSessions.find((s) => s.session_id === item.value);
31868
+ if (session)
31869
+ onSelect(session);
31870
+ }
31824
31871
  }
31825
31872
  }, undefined, false, undefined, this)
31826
31873
  ]
@@ -31828,12 +31875,13 @@ function SessionList({ agent, onSelect, onNew }) {
31828
31875
  }
31829
31876
 
31830
31877
  // src/cli/components/ChatView.tsx
31831
- import React5, { useState as useState4 } from "react";
31878
+ import { useState as useState5, useEffect as useEffect5 } from "react";
31832
31879
  import { Box as Box5, Text as Text6, useInput as useInput4 } from "ink";
31880
+ init_messages();
31833
31881
 
31834
31882
  // src/lib/poll.ts
31835
31883
  init_messages();
31836
- import { useState as useState3, useEffect as useEffect3, useRef as useRef2 } from "react";
31884
+ import { useState as useState4, useEffect as useEffect4, useRef as useRef2 } from "react";
31837
31885
  function startPolling(opts) {
31838
31886
  const interval = opts.interval_ms ?? 200;
31839
31887
  let lastSeen = new Date().toISOString();
@@ -31860,95 +31908,78 @@ function startPolling(opts) {
31860
31908
  }
31861
31909
  };
31862
31910
  }
31863
- function useMessages(sessionId, agent) {
31864
- const [messages, setMessages] = useState3([]);
31865
- const initialLoad = useRef2(false);
31866
- useEffect3(() => {
31867
- if (!initialLoad.current) {
31868
- const existing = readMessages({ session_id: sessionId });
31869
- setMessages(existing);
31870
- initialLoad.current = true;
31871
- }
31872
- const { stop } = startPolling({
31873
- session_id: sessionId,
31874
- interval_ms: 200,
31875
- on_messages: (newMessages) => {
31876
- setMessages((prev) => [...prev, ...newMessages]);
31877
- }
31878
- });
31879
- return stop;
31880
- }, [sessionId, agent]);
31881
- return messages;
31882
- }
31883
-
31884
- // src/cli/components/ChatView.tsx
31885
- init_messages();
31886
31911
 
31887
31912
  // src/cli/components/MessageBubble.tsx
31888
31913
  import { Box as Box4, Text as Text5 } from "ink";
31889
31914
  import { jsxDEV as jsxDEV2 } from "react/jsx-dev-runtime";
31890
- var priorityColors = {
31891
- urgent: "red",
31892
- high: "yellow",
31893
- normal: "",
31894
- low: "dim"
31895
- };
31896
31915
  function MessageBubble({ message, isOwn }) {
31897
31916
  const time = message.created_at.slice(11, 19);
31898
- const color = priorityColors[message.priority] || "";
31899
31917
  return /* @__PURE__ */ jsxDEV2(Box4, {
31900
- flexDirection: "column",
31901
- alignItems: isOwn ? "flex-end" : "flex-start",
31902
- marginBottom: 0,
31903
31918
  children: [
31904
- /* @__PURE__ */ jsxDEV2(Box4, {
31905
- gap: 1,
31919
+ /* @__PURE__ */ jsxDEV2(Text5, {
31920
+ dimColor: true,
31906
31921
  children: [
31907
- /* @__PURE__ */ jsxDEV2(Text5, {
31908
- dimColor: true,
31909
- children: time
31910
- }, undefined, false, undefined, this),
31911
- /* @__PURE__ */ jsxDEV2(Text5, {
31912
- bold: true,
31913
- color: isOwn ? "cyan" : "green",
31914
- children: message.from_agent
31915
- }, undefined, false, undefined, this),
31916
- message.priority !== "normal" && /* @__PURE__ */ jsxDEV2(Text5, {
31917
- color,
31918
- children: [
31919
- "[",
31920
- message.priority,
31921
- "]"
31922
- ]
31923
- }, undefined, true, undefined, this),
31924
- !message.read_at && !isOwn && /* @__PURE__ */ jsxDEV2(Text5, {
31925
- color: "green",
31926
- children: "*"
31927
- }, undefined, false, undefined, this)
31922
+ time,
31923
+ " "
31928
31924
  ]
31929
31925
  }, undefined, true, undefined, this),
31930
- /* @__PURE__ */ jsxDEV2(Box4, {
31931
- marginLeft: isOwn ? 2 : 0,
31932
- marginRight: isOwn ? 0 : 2,
31933
- children: /* @__PURE__ */ jsxDEV2(Text5, {
31934
- wrap: "wrap",
31935
- children: message.content
31936
- }, undefined, false, undefined, this)
31937
- }, undefined, false, undefined, this)
31926
+ /* @__PURE__ */ jsxDEV2(Text5, {
31927
+ bold: true,
31928
+ color: isOwn ? "cyan" : "green",
31929
+ children: message.from_agent
31930
+ }, undefined, false, undefined, this),
31931
+ message.priority !== "normal" && /* @__PURE__ */ jsxDEV2(Text5, {
31932
+ color: message.priority === "urgent" ? "red" : "yellow",
31933
+ children: [
31934
+ " [",
31935
+ message.priority,
31936
+ "]"
31937
+ ]
31938
+ }, undefined, true, undefined, this),
31939
+ /* @__PURE__ */ jsxDEV2(Text5, {
31940
+ children: [
31941
+ ": ",
31942
+ message.content
31943
+ ]
31944
+ }, undefined, true, undefined, this)
31938
31945
  ]
31939
31946
  }, undefined, true, undefined, this);
31940
31947
  }
31941
31948
 
31942
31949
  // src/cli/components/ChatView.tsx
31943
31950
  import { jsxDEV as jsxDEV3 } from "react/jsx-dev-runtime";
31944
- function ChatView({ sessionId, agent, participants, onBack }) {
31945
- const messages = useMessages(sessionId, agent);
31946
- const [input, setInput] = useState4("");
31947
- const others = participants.filter((p) => p !== agent);
31948
- const recipient = others[0] || agent;
31949
- React5.useEffect(() => {
31950
- markSessionRead(sessionId, agent);
31951
- }, [messages.length, sessionId, agent]);
31951
+ function ChatView({ agent, onBack, sessionId: initialSessionId, recipient, channelName }) {
31952
+ const [messages, setMessages] = useState5([]);
31953
+ const [input, setInput] = useState5("");
31954
+ const [sessionId, setSessionId] = useState5(initialSessionId);
31955
+ const isChannel = !!channelName;
31956
+ useEffect5(() => {
31957
+ const opts = isChannel ? { channel: channelName } : sessionId ? { session_id: sessionId } : {};
31958
+ if (isChannel || sessionId) {
31959
+ const existing = readMessages(opts);
31960
+ setMessages(existing);
31961
+ }
31962
+ const pollOpts = isChannel ? { channel: channelName } : sessionId ? { session_id: sessionId } : null;
31963
+ if (!pollOpts)
31964
+ return;
31965
+ const { stop } = startPolling({
31966
+ ...pollOpts,
31967
+ interval_ms: 200,
31968
+ on_messages: (newMsgs) => {
31969
+ setMessages((prev) => [...prev, ...newMsgs]);
31970
+ }
31971
+ });
31972
+ return stop;
31973
+ }, [sessionId, channelName]);
31974
+ useEffect5(() => {
31975
+ if (messages.length === 0)
31976
+ return;
31977
+ if (isChannel && channelName) {
31978
+ markChannelRead(channelName, agent);
31979
+ } else if (sessionId) {
31980
+ markSessionRead(sessionId, agent);
31981
+ }
31982
+ }, [messages.length]);
31952
31983
  useInput4((_, key) => {
31953
31984
  if (key.escape)
31954
31985
  onBack();
@@ -31956,33 +31987,46 @@ function ChatView({ sessionId, agent, participants, onBack }) {
31956
31987
  const handleSubmit = (value) => {
31957
31988
  if (!value.trim())
31958
31989
  return;
31959
- sendMessage({
31960
- from: agent,
31961
- to: recipient,
31962
- content: value.trim(),
31963
- session_id: sessionId
31964
- });
31990
+ if (isChannel && channelName) {
31991
+ const msg = sendMessage({
31992
+ from: agent,
31993
+ to: channelName,
31994
+ content: value.trim(),
31995
+ channel: channelName,
31996
+ session_id: `channel:${channelName}`
31997
+ });
31998
+ setMessages((prev) => [...prev, msg]);
31999
+ } else {
32000
+ const to = recipient || agent;
32001
+ const msg = sendMessage({
32002
+ from: agent,
32003
+ to,
32004
+ content: value.trim(),
32005
+ session_id: sessionId
32006
+ });
32007
+ if (!sessionId) {
32008
+ setSessionId(msg.session_id);
32009
+ }
32010
+ }
31965
32011
  setInput("");
31966
32012
  };
32013
+ const title = isChannel ? `#${channelName}` : recipient || "self";
32014
+ const prompt = isChannel ? `${agent} \u2192 #${channelName}` : `${agent} \u2192 ${recipient || "self"}`;
31967
32015
  return /* @__PURE__ */ jsxDEV3(Box5, {
31968
32016
  flexDirection: "column",
31969
32017
  padding: 1,
31970
32018
  children: [
31971
32019
  /* @__PURE__ */ jsxDEV3(Box5, {
31972
32020
  marginBottom: 1,
31973
- gap: 1,
31974
32021
  children: [
31975
32022
  /* @__PURE__ */ jsxDEV3(Text6, {
31976
32023
  bold: true,
31977
- color: "cyan",
31978
- children: [
31979
- "Chat: ",
31980
- others.join(", ") || "self"
31981
- ]
31982
- }, undefined, true, undefined, this),
32024
+ color: isChannel ? "magenta" : "cyan",
32025
+ children: title
32026
+ }, undefined, false, undefined, this),
31983
32027
  /* @__PURE__ */ jsxDEV3(Text6, {
31984
32028
  dimColor: true,
31985
- children: "(Esc: back)"
32029
+ children: " (Esc: back)"
31986
32030
  }, undefined, false, undefined, this)
31987
32031
  ]
31988
32032
  }, undefined, true, undefined, this),
@@ -31991,7 +32035,7 @@ function ChatView({ sessionId, agent, participants, onBack }) {
31991
32035
  flexGrow: 1,
31992
32036
  children: messages.length === 0 ? /* @__PURE__ */ jsxDEV3(Text6, {
31993
32037
  dimColor: true,
31994
- children: "No messages yet. Start typing below."
32038
+ children: "No messages yet. Type below and press Enter."
31995
32039
  }, undefined, false, undefined, this) : messages.map((msg) => /* @__PURE__ */ jsxDEV3(MessageBubble, {
31996
32040
  message: msg,
31997
32041
  isOwn: msg.from_agent === agent
@@ -32001,11 +32045,9 @@ function ChatView({ sessionId, agent, participants, onBack }) {
32001
32045
  marginTop: 1,
32002
32046
  children: [
32003
32047
  /* @__PURE__ */ jsxDEV3(Text6, {
32004
- color: "cyan",
32048
+ color: isChannel ? "magenta" : "cyan",
32005
32049
  children: [
32006
- agent,
32007
- " \u2192 ",
32008
- recipient,
32050
+ prompt,
32009
32051
  ": "
32010
32052
  ]
32011
32053
  }, undefined, true, undefined, this),
@@ -32025,37 +32067,46 @@ function ChatView({ sessionId, agent, participants, onBack }) {
32025
32067
  import { jsxDEV as jsxDEV4 } from "react/jsx-dev-runtime";
32026
32068
  function App({ agent }) {
32027
32069
  const { exit } = useApp();
32028
- const [view, setView] = useState5("sessions");
32029
- const [currentSession, setCurrentSession] = useState5(null);
32030
- const [newTo, setNewTo] = useState5("");
32070
+ const [view, setView] = useState6("sessions");
32071
+ const [currentSession, setCurrentSession] = useState6(null);
32072
+ const [currentChannel, setCurrentChannel] = useState6(null);
32073
+ const [newTo, setNewTo] = useState6("");
32031
32074
  useInput5((input, key) => {
32032
32075
  if (input === "q" && view === "sessions") {
32033
32076
  exit();
32034
32077
  }
32078
+ if (key.escape && view === "new") {
32079
+ setNewTo("");
32080
+ setView("sessions");
32081
+ }
32035
32082
  });
32036
32083
  const handleSelectSession = (session) => {
32037
32084
  setCurrentSession(session);
32038
32085
  setView("chat");
32039
32086
  };
32087
+ const handleSelectChannel = (channelName) => {
32088
+ setCurrentChannel(channelName);
32089
+ setView("channel");
32090
+ };
32040
32091
  const handleNewConversation = () => {
32041
32092
  setView("new");
32042
32093
  };
32043
32094
  const handleStartNew = (to) => {
32044
32095
  if (!to.trim())
32045
32096
  return;
32046
- const tempSession = {
32047
- session_id: `${[agent, to.trim()].sort().join("-")}-new`,
32097
+ setCurrentSession({
32098
+ session_id: "",
32048
32099
  participants: [agent, to.trim()],
32049
- last_message_at: new Date().toISOString(),
32100
+ last_message_at: "",
32050
32101
  message_count: 0,
32051
32102
  unread_count: 0
32052
- };
32053
- setCurrentSession(tempSession);
32103
+ });
32054
32104
  setNewTo("");
32055
32105
  setView("chat");
32056
32106
  };
32057
32107
  const handleBack = () => {
32058
32108
  setCurrentSession(null);
32109
+ setCurrentChannel(null);
32059
32110
  setView("sessions");
32060
32111
  };
32061
32112
  if (view === "new") {
@@ -32084,29 +32135,38 @@ function App({ agent }) {
32084
32135
  }, undefined, true, undefined, this),
32085
32136
  /* @__PURE__ */ jsxDEV4(Text7, {
32086
32137
  dimColor: true,
32087
- children: "Press Enter to start, Esc to cancel"
32138
+ children: "Enter to start, Esc to cancel"
32088
32139
  }, undefined, false, undefined, this)
32089
32140
  ]
32090
32141
  }, undefined, true, undefined, this);
32091
32142
  }
32143
+ if (view === "channel" && currentChannel) {
32144
+ return /* @__PURE__ */ jsxDEV4(ChatView, {
32145
+ agent,
32146
+ channelName: currentChannel,
32147
+ onBack: handleBack
32148
+ }, undefined, false, undefined, this);
32149
+ }
32092
32150
  if (view === "chat" && currentSession) {
32151
+ const others = currentSession.participants.filter((p) => p !== agent);
32093
32152
  return /* @__PURE__ */ jsxDEV4(ChatView, {
32094
- sessionId: currentSession.session_id,
32095
32153
  agent,
32096
- participants: currentSession.participants,
32154
+ sessionId: currentSession.session_id || undefined,
32155
+ recipient: others[0] || agent,
32097
32156
  onBack: handleBack
32098
32157
  }, undefined, false, undefined, this);
32099
32158
  }
32100
32159
  return /* @__PURE__ */ jsxDEV4(SessionList, {
32101
32160
  agent,
32102
32161
  onSelect: handleSelectSession,
32162
+ onSelectChannel: handleSelectChannel,
32103
32163
  onNew: handleNewConversation
32104
32164
  }, undefined, false, undefined, this);
32105
32165
  }
32106
32166
 
32107
32167
  // src/cli/index.tsx
32108
32168
  var program2 = new Command;
32109
- program2.name("convo").description("Real-time CLI messaging for AI agents").version("0.0.3");
32169
+ program2.name("conversations").description("Real-time CLI messaging for AI agents").version("0.0.5");
32110
32170
  program2.command("send").description("Send a message to an agent").argument("<message>", "Message content").requiredOption("--to <agent>", "Recipient agent ID").option("--from <agent>", "Sender agent ID").option("--session <id>", "Session ID (auto-generated if omitted)").option("--priority <level>", "Priority: low, normal, high, urgent", "normal").option("--working-dir <path>", "Working directory context").option("--repository <repo>", "Repository context").option("--branch <branch>", "Branch context").option("--metadata <json>", "JSON metadata string").option("--json", "Output as JSON").action((message, opts) => {
32111
32171
  const from = resolveIdentity(opts.from);
32112
32172
  const metadata = opts.metadata ? JSON.parse(opts.metadata) : undefined;
@@ -32374,6 +32434,6 @@ program2.command("mcp").description("Start MCP server").action(async () => {
32374
32434
  });
32375
32435
  program2.action(() => {
32376
32436
  const agent = resolveIdentity();
32377
- render(React7.createElement(App, { agent }));
32437
+ render(React8.createElement(App, { agent }));
32378
32438
  });
32379
32439
  program2.parse();
package/bin/mcp.js CHANGED
@@ -28537,7 +28537,7 @@ function resolveIdentity(explicit) {
28537
28537
  // src/mcp/index.ts
28538
28538
  var server = new McpServer({
28539
28539
  name: "conversations",
28540
- version: "0.0.3"
28540
+ version: "0.0.5"
28541
28541
  });
28542
28542
  server.registerTool("send_message", {
28543
28543
  title: "Send Message",
@@ -1,8 +1,9 @@
1
1
  interface ChatViewProps {
2
- sessionId: string;
3
2
  agent: string;
4
- participants: string[];
5
3
  onBack: () => void;
4
+ sessionId?: string;
5
+ recipient?: string;
6
+ channelName?: string;
6
7
  }
7
- export declare function ChatView({ sessionId, agent, participants, onBack }: ChatViewProps): import("react/jsx-runtime").JSX.Element;
8
+ export declare function ChatView({ agent, onBack, sessionId: initialSessionId, recipient, channelName }: ChatViewProps): import("react/jsx-runtime").JSX.Element;
8
9
  export {};
@@ -2,7 +2,8 @@ import type { Session } from "../../types.js";
2
2
  interface SessionListProps {
3
3
  agent: string;
4
4
  onSelect: (session: Session) => void;
5
+ onSelectChannel: (channelName: string) => void;
5
6
  onNew: () => void;
6
7
  }
7
- export declare function SessionList({ agent, onSelect, onNew }: SessionListProps): import("react/jsx-runtime").JSX.Element;
8
+ export declare function SessionList({ agent, onSelect, onSelectChannel, onNew }: SessionListProps): import("react/jsx-runtime").JSX.Element;
8
9
  export {};
package/dist/index.d.ts CHANGED
@@ -2,12 +2,12 @@
2
2
  * @hasna/conversations - Real-time CLI messaging for AI agents
3
3
  *
4
4
  * Send and receive messages between AI agents on the same machine:
5
- * convo send --to claude-code "hello from codex"
6
- * convo read --to codex --json
7
- * convo channel send deployments "v1.2 deployed"
5
+ * conversations send --to claude-code "hello from codex"
6
+ * conversations read --to codex --json
7
+ * conversations channel send deployments "v1.2 deployed"
8
8
  *
9
9
  * Or use the interactive TUI:
10
- * convo
10
+ * conversations
11
11
  */
12
12
  export { sendMessage, readMessages, markRead, markSessionRead, markChannelRead, getMessageById, } from "./lib/messages.js";
13
13
  export { listSessions, getSession, } from "./lib/sessions.js";
@@ -4,7 +4,7 @@
4
4
  * Exposes tools for sending, reading, and managing messages and channels between agents.
5
5
  *
6
6
  * Usage:
7
- * convo mcp # Start MCP server on stdio
8
- * convo-mcp # Direct binary
7
+ * conversations mcp # Start MCP server on stdio
8
+ * conversations-mcp # Direct binary
9
9
  */
10
10
  export declare function startMcpServer(): Promise<void>;
package/package.json CHANGED
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "@hasna/conversations",
3
- "version": "0.0.3",
3
+ "version": "0.0.5",
4
4
  "description": "Real-time CLI messaging for AI agents",
5
5
  "type": "module",
6
6
  "bin": {
7
- "convo": "bin/index.js",
8
- "convo-mcp": "bin/mcp.js"
7
+ "conversations": "bin/index.js",
8
+ "conversations-mcp": "bin/mcp.js"
9
9
  },
10
10
  "exports": {
11
11
  ".": {