@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 +37 -37
- package/bin/index.js +187 -127
- package/bin/mcp.js +1 -1
- package/dist/cli/components/ChatView.d.ts +4 -3
- package/dist/cli/components/SessionList.d.ts +2 -1
- package/dist/index.d.ts +4 -4
- package/dist/mcp/index.d.ts +2 -2
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -31,10 +31,10 @@ npx @hasna/conversations
|
|
|
31
31
|
|
|
32
32
|
```bash
|
|
33
33
|
# Basic message
|
|
34
|
-
|
|
34
|
+
conversations send --to claude-code "Hello from codex"
|
|
35
35
|
|
|
36
36
|
# With context
|
|
37
|
-
|
|
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
|
-
|
|
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
|
-
|
|
52
|
+
conversations read --to codex
|
|
53
53
|
|
|
54
54
|
# Unread only, as JSON
|
|
55
|
-
|
|
55
|
+
conversations read --to codex --unread --json
|
|
56
56
|
|
|
57
57
|
# Filter by session
|
|
58
|
-
|
|
58
|
+
conversations read --session alice-bob-abc123
|
|
59
59
|
|
|
60
60
|
# Read and mark as read
|
|
61
|
-
|
|
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
|
-
|
|
68
|
+
conversations sessions
|
|
69
69
|
|
|
70
70
|
# Sessions for a specific agent
|
|
71
|
-
|
|
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
|
-
|
|
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
|
-
|
|
87
|
+
conversations channel create deployments --description "Deployment notifications"
|
|
88
88
|
|
|
89
89
|
# List channels
|
|
90
|
-
|
|
90
|
+
conversations channel list
|
|
91
91
|
|
|
92
92
|
# Join a channel
|
|
93
|
-
|
|
93
|
+
conversations channel join deployments --from codex
|
|
94
94
|
|
|
95
95
|
# Send to a channel
|
|
96
|
-
|
|
96
|
+
conversations channel send deployments "v1.2 deployed to staging" --from ops
|
|
97
97
|
|
|
98
98
|
# Read channel messages
|
|
99
|
-
|
|
99
|
+
conversations channel read deployments
|
|
100
100
|
|
|
101
101
|
# Leave a channel
|
|
102
|
-
|
|
102
|
+
conversations channel leave deployments --from codex
|
|
103
103
|
|
|
104
104
|
# List members
|
|
105
|
-
|
|
105
|
+
conversations channel members deployments
|
|
106
106
|
```
|
|
107
107
|
|
|
108
108
|
### Mark Read
|
|
109
109
|
|
|
110
110
|
```bash
|
|
111
111
|
# Mark specific messages
|
|
112
|
-
|
|
112
|
+
conversations mark-read 1 2 3 --agent codex
|
|
113
113
|
|
|
114
114
|
# Mark entire session
|
|
115
|
-
|
|
115
|
+
conversations mark-read --session abc123 --agent codex
|
|
116
116
|
|
|
117
117
|
# Mark entire channel
|
|
118
|
-
|
|
118
|
+
conversations mark-read --channel deployments --agent codex
|
|
119
119
|
```
|
|
120
120
|
|
|
121
121
|
### Status
|
|
122
122
|
|
|
123
123
|
```bash
|
|
124
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
│
|
|
227
|
-
│
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
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.
|
|
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
|
|
31250
|
+
import React8 from "react";
|
|
31251
31251
|
|
|
31252
31252
|
// src/cli/components/App.tsx
|
|
31253
|
-
import { useState as
|
|
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
|
|
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
|
-
|
|
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
|
-
"
|
|
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
|
-
|
|
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:
|
|
31815
|
-
|
|
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
|
-
|
|
31822
|
-
|
|
31823
|
-
|
|
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
|
|
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
|
|
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(
|
|
31905
|
-
|
|
31919
|
+
/* @__PURE__ */ jsxDEV2(Text5, {
|
|
31920
|
+
dimColor: true,
|
|
31906
31921
|
children: [
|
|
31907
|
-
|
|
31908
|
-
|
|
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(
|
|
31931
|
-
|
|
31932
|
-
|
|
31933
|
-
children:
|
|
31934
|
-
|
|
31935
|
-
|
|
31936
|
-
|
|
31937
|
-
|
|
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({
|
|
31945
|
-
const messages =
|
|
31946
|
-
const [input, setInput] =
|
|
31947
|
-
const
|
|
31948
|
-
const
|
|
31949
|
-
|
|
31950
|
-
|
|
31951
|
-
|
|
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
|
-
|
|
31960
|
-
|
|
31961
|
-
|
|
31962
|
-
|
|
31963
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
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] =
|
|
32029
|
-
const [currentSession, setCurrentSession] =
|
|
32030
|
-
const [
|
|
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
|
-
|
|
32047
|
-
session_id:
|
|
32097
|
+
setCurrentSession({
|
|
32098
|
+
session_id: "",
|
|
32048
32099
|
participants: [agent, to.trim()],
|
|
32049
|
-
last_message_at:
|
|
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: "
|
|
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
|
-
|
|
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("
|
|
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(
|
|
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.
|
|
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({
|
|
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
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
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
|
-
*
|
|
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";
|
package/dist/mcp/index.d.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Exposes tools for sending, reading, and managing messages and channels between agents.
|
|
5
5
|
*
|
|
6
6
|
* Usage:
|
|
7
|
-
*
|
|
8
|
-
*
|
|
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
|
+
"version": "0.0.5",
|
|
4
4
|
"description": "Real-time CLI messaging for AI agents",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
|
-
"
|
|
8
|
-
"
|
|
7
|
+
"conversations": "bin/index.js",
|
|
8
|
+
"conversations-mcp": "bin/mcp.js"
|
|
9
9
|
},
|
|
10
10
|
"exports": {
|
|
11
11
|
".": {
|