@hasna/conversations 0.2.31 → 0.2.33
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/index.js +83 -14
- package/bin/mcp.js +35 -6
- package/dist/cli/commands/agents.d.ts +13 -0
- package/dist/cli/commands/agents.test.d.ts +1 -0
- package/dist/cli/commands/projects.d.ts +1 -0
- package/dist/cli/commands/projects.test.d.ts +1 -0
- package/dist/index.js +13 -0
- package/dist/lib/projects.d.ts +2 -0
- package/dist/mcp/tools/tmux.d.ts +2 -0
- package/dist/mcp/tools/tmux.test.d.ts +1 -0
- package/package.json +1 -1
package/bin/index.js
CHANGED
|
@@ -14928,7 +14928,7 @@ var init_presence = __esm(() => {
|
|
|
14928
14928
|
var require_package = __commonJS((exports, module) => {
|
|
14929
14929
|
module.exports = {
|
|
14930
14930
|
name: "@hasna/conversations",
|
|
14931
|
-
version: "0.2.
|
|
14931
|
+
version: "0.2.33",
|
|
14932
14932
|
description: "Real-time CLI messaging for AI agents",
|
|
14933
14933
|
type: "module",
|
|
14934
14934
|
bin: {
|
|
@@ -15071,6 +15071,18 @@ function listProjects(opts) {
|
|
|
15071
15071
|
params.push(opts.status);
|
|
15072
15072
|
}
|
|
15073
15073
|
const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
15074
|
+
let pagination = "";
|
|
15075
|
+
if (typeof opts?.limit === "number" && Number.isFinite(opts.limit) && opts.limit > 0) {
|
|
15076
|
+
pagination += " LIMIT ?";
|
|
15077
|
+
params.push(Math.floor(opts.limit));
|
|
15078
|
+
}
|
|
15079
|
+
if (typeof opts?.offset === "number" && Number.isFinite(opts.offset) && opts.offset >= 0) {
|
|
15080
|
+
if (!pagination.includes("LIMIT")) {
|
|
15081
|
+
pagination += " LIMIT -1";
|
|
15082
|
+
}
|
|
15083
|
+
pagination += " OFFSET ?";
|
|
15084
|
+
params.push(Math.floor(opts.offset));
|
|
15085
|
+
}
|
|
15074
15086
|
const rows = db2.prepare(`
|
|
15075
15087
|
SELECT
|
|
15076
15088
|
p.*,
|
|
@@ -15078,6 +15090,7 @@ function listProjects(opts) {
|
|
|
15078
15090
|
FROM projects p
|
|
15079
15091
|
${where}
|
|
15080
15092
|
ORDER BY p.name ASC
|
|
15093
|
+
${pagination}
|
|
15081
15094
|
`).all(...params);
|
|
15082
15095
|
return rows.map((row) => ({
|
|
15083
15096
|
...parseProject(row),
|
|
@@ -46969,6 +46982,16 @@ var init_channel = __esm(() => {
|
|
|
46969
46982
|
function sleep2(ms) {
|
|
46970
46983
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
46971
46984
|
}
|
|
46985
|
+
function parseOptionalDelayMs(value) {
|
|
46986
|
+
return typeof value === "number" && value >= 0 ? value : undefined;
|
|
46987
|
+
}
|
|
46988
|
+
function normalizeTmuxTargets(rawTargets) {
|
|
46989
|
+
const normalized = rawTargets.map((target) => String(target).trim());
|
|
46990
|
+
if (normalized.some((target) => target.length === 0)) {
|
|
46991
|
+
throw new Error("targets must not contain empty values");
|
|
46992
|
+
}
|
|
46993
|
+
return normalized;
|
|
46994
|
+
}
|
|
46972
46995
|
function registerTmuxTools(server) {
|
|
46973
46996
|
server.registerTool("tmux_send", {
|
|
46974
46997
|
description: "Send a message to a tmux window (e.g. another agent's Claude Code session). " + "Pastes the text literally, waits for the pane to be idle, hits Enter, then verifies the message was submitted. " + "Retries up to N times on failure.",
|
|
@@ -46989,7 +47012,7 @@ function registerTmuxTools(server) {
|
|
|
46989
47012
|
}
|
|
46990
47013
|
try {
|
|
46991
47014
|
const result = await tmuxSend(target.trim(), message, {
|
|
46992
|
-
delayMs:
|
|
47015
|
+
delayMs: parseOptionalDelayMs(delay_ms),
|
|
46993
47016
|
retries: typeof retries === "number" && retries > 0 ? retries : undefined,
|
|
46994
47017
|
verify: verify !== false
|
|
46995
47018
|
});
|
|
@@ -47021,14 +47044,20 @@ function registerTmuxTools(server) {
|
|
|
47021
47044
|
return { content: [{ type: "text", text: "message cannot be empty" }], isError: true };
|
|
47022
47045
|
}
|
|
47023
47046
|
const stagger = typeof stagger_ms === "number" && stagger_ms >= 0 ? stagger_ms : 500;
|
|
47024
|
-
|
|
47025
|
-
|
|
47026
|
-
|
|
47047
|
+
let normalizedTargets;
|
|
47048
|
+
try {
|
|
47049
|
+
normalizedTargets = normalizeTmuxTargets(targets);
|
|
47050
|
+
} catch (err) {
|
|
47051
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
47052
|
+
return { content: [{ type: "text", text: msg }], isError: true };
|
|
47053
|
+
}
|
|
47054
|
+
const results = new Array(normalizedTargets.length);
|
|
47055
|
+
await Promise.all(normalizedTargets.map(async (target, i) => {
|
|
47027
47056
|
if (i > 0 && stagger > 0)
|
|
47028
47057
|
await sleep2(stagger * i);
|
|
47029
47058
|
try {
|
|
47030
47059
|
const result = await tmuxSend(target, message, {
|
|
47031
|
-
delayMs:
|
|
47060
|
+
delayMs: parseOptionalDelayMs(delay_ms),
|
|
47032
47061
|
retries: typeof retries === "number" && retries > 0 ? retries : undefined,
|
|
47033
47062
|
verify: verify !== false
|
|
47034
47063
|
});
|
|
@@ -49614,6 +49643,11 @@ init_projects();
|
|
|
49614
49643
|
init_db();
|
|
49615
49644
|
init_identity();
|
|
49616
49645
|
import chalk6 from "chalk";
|
|
49646
|
+
function requireDeleteConfirmation(confirmed) {
|
|
49647
|
+
if (!confirmed) {
|
|
49648
|
+
throw new Error("Project deletion requires --yes confirmation");
|
|
49649
|
+
}
|
|
49650
|
+
}
|
|
49617
49651
|
function registerProjectCommands(program2) {
|
|
49618
49652
|
const project = program2.command("project").description("Manage projects");
|
|
49619
49653
|
project.command("create").description("Create a new project").argument("<name>", "Project name").option("--description <text>", "Project description").option("--path <path>", "Project path on disk").option("--repository <url>", "Repository URL").option("--tags <json>", "JSON array of tags").option("--from <agent>", "Creator agent ID").option("--json", "Output as JSON").action((name, opts) => {
|
|
@@ -49660,9 +49694,15 @@ function registerProjectCommands(program2) {
|
|
|
49660
49694
|
}
|
|
49661
49695
|
closeDb();
|
|
49662
49696
|
});
|
|
49663
|
-
project.command("list").description("List all projects").option("--status <status>", "Filter by status (active/archived)").option("--json", "Output as JSON").action((opts) => {
|
|
49697
|
+
project.command("list").description("List all projects").option("--status <status>", "Filter by status (active/archived)").option("--limit <n>", "Limit results", parseInt).option("--offset <n>", "Skip first N results", parseInt).option("--json", "Output as JSON").action((opts) => {
|
|
49664
49698
|
const status = opts.status === "active" || opts.status === "archived" ? opts.status : undefined;
|
|
49665
|
-
const
|
|
49699
|
+
const limit = Number.isFinite(opts.limit) && opts.limit > 0 ? opts.limit : undefined;
|
|
49700
|
+
const offset = Number.isFinite(opts.offset) && opts.offset >= 0 ? opts.offset : undefined;
|
|
49701
|
+
const projects = listProjects({
|
|
49702
|
+
...status ? { status } : {},
|
|
49703
|
+
...limit !== undefined ? { limit } : {},
|
|
49704
|
+
...offset !== undefined ? { offset } : {}
|
|
49705
|
+
});
|
|
49666
49706
|
if (opts.json) {
|
|
49667
49707
|
console.log(JSON.stringify(projects, null, 2));
|
|
49668
49708
|
} else {
|
|
@@ -49739,8 +49779,9 @@ function registerProjectCommands(program2) {
|
|
|
49739
49779
|
}
|
|
49740
49780
|
closeDb();
|
|
49741
49781
|
});
|
|
49742
|
-
project.command("delete").description("Delete a project").argument("<id-or-name>", "Project ID or name").option("--json", "Output as JSON").action((id, opts) => {
|
|
49782
|
+
project.command("delete").description("Delete a project").argument("<id-or-name>", "Project ID or name").option("--yes", "Confirm project deletion").option("--json", "Output as JSON").action((id, opts) => {
|
|
49743
49783
|
try {
|
|
49784
|
+
requireDeleteConfirmation(opts.yes);
|
|
49744
49785
|
const isUuid = /^[0-9a-f-]{36}$/i.test(id);
|
|
49745
49786
|
const resolvedId = isUuid ? id : getProjectByName(id)?.id ?? id;
|
|
49746
49787
|
const deleted = deleteProject(resolvedId);
|
|
@@ -49754,7 +49795,11 @@ function registerProjectCommands(program2) {
|
|
|
49754
49795
|
console.log(chalk6.green(`Project deleted.`));
|
|
49755
49796
|
}
|
|
49756
49797
|
} catch (e) {
|
|
49757
|
-
|
|
49798
|
+
if (opts.json) {
|
|
49799
|
+
console.log(JSON.stringify({ id, deleted: false, error: e.message }));
|
|
49800
|
+
} else {
|
|
49801
|
+
console.error(chalk6.red(e.message));
|
|
49802
|
+
}
|
|
49758
49803
|
process.exit(1);
|
|
49759
49804
|
}
|
|
49760
49805
|
closeDb();
|
|
@@ -49767,6 +49812,26 @@ init_identity();
|
|
|
49767
49812
|
init_presence();
|
|
49768
49813
|
init_projects();
|
|
49769
49814
|
import chalk7 from "chalk";
|
|
49815
|
+
function buildWhoamiPayload(agent, source, presence, nowMs = Date.now()) {
|
|
49816
|
+
if (!presence) {
|
|
49817
|
+
return {
|
|
49818
|
+
agent,
|
|
49819
|
+
source,
|
|
49820
|
+
online: false,
|
|
49821
|
+
last_seen_at: null,
|
|
49822
|
+
last_seen_ago_seconds: null
|
|
49823
|
+
};
|
|
49824
|
+
}
|
|
49825
|
+
const lastSeenMs = new Date(`${presence.last_seen_at}Z`).getTime();
|
|
49826
|
+
const deltaSeconds = Number.isFinite(lastSeenMs) ? Math.max(0, Math.floor((nowMs - lastSeenMs) / 1000)) : null;
|
|
49827
|
+
return {
|
|
49828
|
+
agent,
|
|
49829
|
+
source,
|
|
49830
|
+
online: presence.online,
|
|
49831
|
+
last_seen_at: presence.last_seen_at,
|
|
49832
|
+
last_seen_ago_seconds: deltaSeconds
|
|
49833
|
+
};
|
|
49834
|
+
}
|
|
49770
49835
|
function registerAgentCommands(program2) {
|
|
49771
49836
|
const agents = program2.command("agents").description("Manage agents");
|
|
49772
49837
|
agents.command("list").description("List all agents with their presence status").option("--online", "Only show online agents").option("--json", "Output as JSON").action((opts) => {
|
|
@@ -49911,7 +49976,7 @@ function registerAgentCommands(program2) {
|
|
|
49911
49976
|
}
|
|
49912
49977
|
closeDb();
|
|
49913
49978
|
});
|
|
49914
|
-
program2.command("whoami").description("Show current agent identity and online status").option("--from <agent>", "Explicit agent identity").action((opts) => {
|
|
49979
|
+
program2.command("whoami").description("Show current agent identity and online status").option("--from <agent>", "Explicit agent identity").option("--json", "Output as JSON").action((opts) => {
|
|
49915
49980
|
const envValue = process.env.CONVERSATIONS_AGENT_ID?.trim();
|
|
49916
49981
|
const agent = resolveIdentity(opts.from);
|
|
49917
49982
|
let source;
|
|
@@ -49927,11 +49992,15 @@ function registerAgentCommands(program2) {
|
|
|
49927
49992
|
source = `auto-generated (${agentIdFile})`;
|
|
49928
49993
|
}
|
|
49929
49994
|
const presence = getPresence(agent);
|
|
49995
|
+
const payload = buildWhoamiPayload(agent, source, presence);
|
|
49996
|
+
if (opts.json) {
|
|
49997
|
+
console.log(JSON.stringify(payload, null, 2));
|
|
49998
|
+
closeDb();
|
|
49999
|
+
return;
|
|
50000
|
+
}
|
|
49930
50001
|
let onlineStatus;
|
|
49931
50002
|
if (presence && presence.online) {
|
|
49932
|
-
const
|
|
49933
|
-
const agoMs = Date.now() - lastSeenMs;
|
|
49934
|
-
const agoSec = Math.floor(agoMs / 1000);
|
|
50003
|
+
const agoSec = payload.last_seen_ago_seconds ?? 0;
|
|
49935
50004
|
const agoStr = agoSec < 60 ? `${agoSec}s ago` : `${Math.floor(agoSec / 60)}m ago`;
|
|
49936
50005
|
onlineStatus = chalk7.green(`yes`) + chalk7.dim(` (last seen ${agoStr})`);
|
|
49937
50006
|
} else if (presence) {
|
package/bin/mcp.js
CHANGED
|
@@ -42094,6 +42094,18 @@ function listProjects(opts) {
|
|
|
42094
42094
|
params.push(opts.status);
|
|
42095
42095
|
}
|
|
42096
42096
|
const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
42097
|
+
let pagination = "";
|
|
42098
|
+
if (typeof opts?.limit === "number" && Number.isFinite(opts.limit) && opts.limit > 0) {
|
|
42099
|
+
pagination += " LIMIT ?";
|
|
42100
|
+
params.push(Math.floor(opts.limit));
|
|
42101
|
+
}
|
|
42102
|
+
if (typeof opts?.offset === "number" && Number.isFinite(opts.offset) && opts.offset >= 0) {
|
|
42103
|
+
if (!pagination.includes("LIMIT")) {
|
|
42104
|
+
pagination += " LIMIT -1";
|
|
42105
|
+
}
|
|
42106
|
+
pagination += " OFFSET ?";
|
|
42107
|
+
params.push(Math.floor(opts.offset));
|
|
42108
|
+
}
|
|
42097
42109
|
const rows = db2.prepare(`
|
|
42098
42110
|
SELECT
|
|
42099
42111
|
p.*,
|
|
@@ -42101,6 +42113,7 @@ function listProjects(opts) {
|
|
|
42101
42113
|
FROM projects p
|
|
42102
42114
|
${where}
|
|
42103
42115
|
ORDER BY p.name ASC
|
|
42116
|
+
${pagination}
|
|
42104
42117
|
`).all(...params);
|
|
42105
42118
|
return rows.map((row) => ({
|
|
42106
42119
|
...parseProject(row),
|
|
@@ -44157,6 +44170,16 @@ async function tmuxSend(target, message, opts = {}) {
|
|
|
44157
44170
|
function sleep2(ms) {
|
|
44158
44171
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
44159
44172
|
}
|
|
44173
|
+
function parseOptionalDelayMs(value) {
|
|
44174
|
+
return typeof value === "number" && value >= 0 ? value : undefined;
|
|
44175
|
+
}
|
|
44176
|
+
function normalizeTmuxTargets(rawTargets) {
|
|
44177
|
+
const normalized = rawTargets.map((target) => String(target).trim());
|
|
44178
|
+
if (normalized.some((target) => target.length === 0)) {
|
|
44179
|
+
throw new Error("targets must not contain empty values");
|
|
44180
|
+
}
|
|
44181
|
+
return normalized;
|
|
44182
|
+
}
|
|
44160
44183
|
function registerTmuxTools(server) {
|
|
44161
44184
|
server.registerTool("tmux_send", {
|
|
44162
44185
|
description: "Send a message to a tmux window (e.g. another agent's Claude Code session). " + "Pastes the text literally, waits for the pane to be idle, hits Enter, then verifies the message was submitted. " + "Retries up to N times on failure.",
|
|
@@ -44177,7 +44200,7 @@ function registerTmuxTools(server) {
|
|
|
44177
44200
|
}
|
|
44178
44201
|
try {
|
|
44179
44202
|
const result = await tmuxSend(target.trim(), message, {
|
|
44180
|
-
delayMs:
|
|
44203
|
+
delayMs: parseOptionalDelayMs(delay_ms),
|
|
44181
44204
|
retries: typeof retries === "number" && retries > 0 ? retries : undefined,
|
|
44182
44205
|
verify: verify !== false
|
|
44183
44206
|
});
|
|
@@ -44209,14 +44232,20 @@ function registerTmuxTools(server) {
|
|
|
44209
44232
|
return { content: [{ type: "text", text: "message cannot be empty" }], isError: true };
|
|
44210
44233
|
}
|
|
44211
44234
|
const stagger = typeof stagger_ms === "number" && stagger_ms >= 0 ? stagger_ms : 500;
|
|
44212
|
-
|
|
44213
|
-
|
|
44214
|
-
|
|
44235
|
+
let normalizedTargets;
|
|
44236
|
+
try {
|
|
44237
|
+
normalizedTargets = normalizeTmuxTargets(targets);
|
|
44238
|
+
} catch (err) {
|
|
44239
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
44240
|
+
return { content: [{ type: "text", text: msg }], isError: true };
|
|
44241
|
+
}
|
|
44242
|
+
const results = new Array(normalizedTargets.length);
|
|
44243
|
+
await Promise.all(normalizedTargets.map(async (target, i) => {
|
|
44215
44244
|
if (i > 0 && stagger > 0)
|
|
44216
44245
|
await sleep2(stagger * i);
|
|
44217
44246
|
try {
|
|
44218
44247
|
const result = await tmuxSend(target, message, {
|
|
44219
|
-
delayMs:
|
|
44248
|
+
delayMs: parseOptionalDelayMs(delay_ms),
|
|
44220
44249
|
retries: typeof retries === "number" && retries > 0 ? retries : undefined,
|
|
44221
44250
|
verify: verify !== false
|
|
44222
44251
|
});
|
|
@@ -44240,7 +44269,7 @@ function registerTmuxTools(server) {
|
|
|
44240
44269
|
// package.json
|
|
44241
44270
|
var package_default = {
|
|
44242
44271
|
name: "@hasna/conversations",
|
|
44243
|
-
version: "0.2.
|
|
44272
|
+
version: "0.2.33",
|
|
44244
44273
|
description: "Real-time CLI messaging for AI agents",
|
|
44245
44274
|
type: "module",
|
|
44246
44275
|
bin: {
|
|
@@ -1,2 +1,15 @@
|
|
|
1
1
|
import type { Command } from "commander";
|
|
2
|
+
type PresenceView = {
|
|
3
|
+
online: boolean;
|
|
4
|
+
last_seen_at: string;
|
|
5
|
+
} | null;
|
|
6
|
+
export type WhoamiPayload = {
|
|
7
|
+
agent: string;
|
|
8
|
+
source: string;
|
|
9
|
+
online: boolean;
|
|
10
|
+
last_seen_at: string | null;
|
|
11
|
+
last_seen_ago_seconds: number | null;
|
|
12
|
+
};
|
|
13
|
+
export declare function buildWhoamiPayload(agent: string, source: string, presence: PresenceView, nowMs?: number): WhoamiPayload;
|
|
2
14
|
export declare function registerAgentCommands(program: Command): void;
|
|
15
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/index.js
CHANGED
|
@@ -12428,6 +12428,18 @@ function listProjects(opts) {
|
|
|
12428
12428
|
params.push(opts.status);
|
|
12429
12429
|
}
|
|
12430
12430
|
const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
12431
|
+
let pagination = "";
|
|
12432
|
+
if (typeof opts?.limit === "number" && Number.isFinite(opts.limit) && opts.limit > 0) {
|
|
12433
|
+
pagination += " LIMIT ?";
|
|
12434
|
+
params.push(Math.floor(opts.limit));
|
|
12435
|
+
}
|
|
12436
|
+
if (typeof opts?.offset === "number" && Number.isFinite(opts.offset) && opts.offset >= 0) {
|
|
12437
|
+
if (!pagination.includes("LIMIT")) {
|
|
12438
|
+
pagination += " LIMIT -1";
|
|
12439
|
+
}
|
|
12440
|
+
pagination += " OFFSET ?";
|
|
12441
|
+
params.push(Math.floor(opts.offset));
|
|
12442
|
+
}
|
|
12431
12443
|
const rows = db2.prepare(`
|
|
12432
12444
|
SELECT
|
|
12433
12445
|
p.*,
|
|
@@ -12435,6 +12447,7 @@ function listProjects(opts) {
|
|
|
12435
12447
|
FROM projects p
|
|
12436
12448
|
${where}
|
|
12437
12449
|
ORDER BY p.name ASC
|
|
12450
|
+
${pagination}
|
|
12438
12451
|
`).all(...params);
|
|
12439
12452
|
return rows.map((row) => ({
|
|
12440
12453
|
...parseProject(row),
|
package/dist/lib/projects.d.ts
CHANGED
|
@@ -11,6 +11,8 @@ export declare function createProject(opts: {
|
|
|
11
11
|
}): Project;
|
|
12
12
|
export declare function listProjects(opts?: {
|
|
13
13
|
status?: "active" | "archived";
|
|
14
|
+
limit?: number;
|
|
15
|
+
offset?: number;
|
|
14
16
|
}): ProjectInfo[];
|
|
15
17
|
export declare function getProject(id: string): ProjectInfo | null;
|
|
16
18
|
export declare function getProjectByName(name: string): ProjectInfo | null;
|
package/dist/mcp/tools/tmux.d.ts
CHANGED
|
@@ -5,4 +5,6 @@
|
|
|
5
5
|
* smart paste → wait → Enter → verify behavior.
|
|
6
6
|
*/
|
|
7
7
|
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
8
|
+
export declare function parseOptionalDelayMs(value: unknown): number | undefined;
|
|
9
|
+
export declare function normalizeTmuxTargets(rawTargets: unknown[]): string[];
|
|
8
10
|
export declare function registerTmuxTools(server: McpServer): void;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|