@kvsm/blether 0.2.0 → 0.2.2
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 +25 -10
- package/package.json +3 -3
- package/plugin/.claude-plugin/plugin.json +1 -1
- package/plugin/dist/bridge.js +10 -10
- package/plugin/dist/cli.js +76 -28
- package/plugin/skills/connect/SKILL.md +7 -4
- package/plugin/skills/setup/SKILL.md +11 -5
package/README.md
CHANGED
|
@@ -16,25 +16,40 @@ Teams also need a relay, which holds each agent's mailbox. It's published as the
|
|
|
16
16
|
|
|
17
17
|
## Quickstart
|
|
18
18
|
|
|
19
|
-
You need Node 24, and an invite link from a teammate whose team already has a relay.
|
|
19
|
+
You need Node 24, and an invite link from a teammate whose team already has a relay. To start a team instead, you need a relay: see [Run a relay](https://github.com/kvsm/blether#2-run-a-relay).
|
|
20
20
|
|
|
21
21
|
```sh
|
|
22
22
|
npm install -g @kvsm/blether
|
|
23
23
|
blether claude install # adds the plugin to Claude Code (the CLI and VS Code)
|
|
24
|
+
```
|
|
24
25
|
|
|
25
|
-
blether
|
|
26
|
-
blether join <invite> # prints "Joined <team>."
|
|
27
|
-
blether agent create <team> web # an agent you own: a named mailbox
|
|
26
|
+
Then open Claude Code in a project, with `claude` in the terminal or in VS Code, and run `/blether:setup`. Claude walks you through the rest, running the commands for you:
|
|
28
27
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
28
|
+
1. **Your identity:** your name, as teammates will see it. Once per developer.
|
|
29
|
+
2. **Your team:** joins it with your invite, or creates a new one.
|
|
30
|
+
3. **This project's agent:** the named mailbox your sessions here act as. Teammates' agents message it by name, so pick one that says what it works on, such as `web-app` or `payments-api`. You can give it roles, such as `frontend` or `reviewer`, so teammates can message every agent covering an area at once.
|
|
31
|
+
4. **Connecting:** the session connects to Blether, and is told whenever a message arrives.
|
|
32
32
|
|
|
33
|
-
|
|
33
|
+
Claude can now message your teammates' agents. In later sessions, run `/blether:connect` when you want to work with your team. Sessions you don't connect leave Blether out entirely.
|
|
34
34
|
|
|
35
35
|
By default Claude asks you before it sends any message, and before it acts on any request it receives. `blether policy` shows your Approval Policy, and `blether help` lists every command.
|
|
36
36
|
|
|
37
|
-
|
|
37
|
+
### By hand
|
|
38
|
+
|
|
39
|
+
To set up without the skill, or for an agent other than Claude Code, run the steps yourself:
|
|
40
|
+
|
|
41
|
+
```sh
|
|
42
|
+
blether init --name <name> # your identity, once per developer
|
|
43
|
+
blether join <invite> # prints "Joined <team>."
|
|
44
|
+
blether role list <team> # the team's roles, such as frontend or reviewer
|
|
45
|
+
blether role add <team> <role> # add one if what your agent does isn't listed
|
|
46
|
+
blether agent create <team> <agent name> --role <role> # an agent you own; --role is optional, and can repeat
|
|
47
|
+
|
|
48
|
+
cd <project> # each project an agent works in
|
|
49
|
+
blether use <team> <agent name> # sessions started here act as <agent name>
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
In Claude Code, start a new session in the project and run `/blether:connect`. For other agents, see [Other agents](#other-agents) below. To start a team or run a relay, see [Getting started](https://github.com/kvsm/blether#getting-started).
|
|
38
53
|
|
|
39
54
|
## Other agents
|
|
40
55
|
|
|
@@ -58,7 +73,7 @@ blether --version # shows the version now installed
|
|
|
58
73
|
blether claude install # if you use Claude Code: updates the plugin to match
|
|
59
74
|
```
|
|
60
75
|
|
|
61
|
-
Then
|
|
76
|
+
Then restart your agent sessions. Your identity, teams and projects are left as they are. See [Updating](https://github.com/kvsm/blether#updating) for the details.
|
|
62
77
|
|
|
63
78
|
## Documentation
|
|
64
79
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kvsm/blether",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.2",
|
|
4
4
|
"description": "Agent-to-agent communication for distributed teams of human developers: the blether CLI, the bridge (an MCP server), and the Claude Code plugin.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"agents",
|
|
@@ -31,8 +31,8 @@
|
|
|
31
31
|
"devDependencies": {
|
|
32
32
|
"@modelcontextprotocol/sdk": "^1.31.0",
|
|
33
33
|
"esbuild": "^0.28.2",
|
|
34
|
-
"@blether/bridge": "0.2.
|
|
35
|
-
"@blether/relay": "0.2.
|
|
34
|
+
"@blether/bridge": "0.2.2",
|
|
35
|
+
"@blether/relay": "0.2.2"
|
|
36
36
|
},
|
|
37
37
|
"publishConfig": {
|
|
38
38
|
"access": "public"
|
package/plugin/dist/bridge.js
CHANGED
|
@@ -36960,7 +36960,7 @@ var DeviceGrant = external_exports.object({
|
|
|
36960
36960
|
});
|
|
36961
36961
|
|
|
36962
36962
|
// ../protocol/dist/version.js
|
|
36963
|
-
var BLETHER_VERSION = "0.2.
|
|
36963
|
+
var BLETHER_VERSION = "0.2.2";
|
|
36964
36964
|
|
|
36965
36965
|
// ../protocol/dist/wire.js
|
|
36966
36966
|
var AudienceHint = external_exports.object({
|
|
@@ -37681,11 +37681,11 @@ function readSessionFile(path2) {
|
|
|
37681
37681
|
try {
|
|
37682
37682
|
raw = JSON.parse(readFileSync6(path2, "utf8"));
|
|
37683
37683
|
} catch (error62) {
|
|
37684
|
-
throw new SessionFileError(`${path2} isn't valid JSON (${error62.message}). Write it again with \`blether use <team> <agent>\`.`);
|
|
37684
|
+
throw new SessionFileError(`${path2} isn't valid JSON (${error62.message}). Write it again with \`blether use <team> <agent name>\`.`);
|
|
37685
37685
|
}
|
|
37686
37686
|
const parsed = SessionFileContents.safeParse(raw);
|
|
37687
37687
|
if (!parsed.success) {
|
|
37688
|
-
throw new SessionFileError(`${path2} doesn't name a team and agent. Write it again with \`blether use <team> <agent>\`.`);
|
|
37688
|
+
throw new SessionFileError(`${path2} doesn't name a team and agent. Write it again with \`blether use <team> <agent name>\`.`);
|
|
37689
37689
|
}
|
|
37690
37690
|
return parsed.data;
|
|
37691
37691
|
}
|
|
@@ -51148,7 +51148,7 @@ function createDormantServer(connect2) {
|
|
|
51148
51148
|
const offerConnect = () => {
|
|
51149
51149
|
const connectTool = server2.registerTool("connect", {
|
|
51150
51150
|
title: "Connect to Blether",
|
|
51151
|
-
description: "Connects this session to Blether, so you can message the agents of other developers on your team. Call it
|
|
51151
|
+
description: "Connects this session to Blether, so you can message the agents of other developers on your team. Call it ONLY when your developer runs /blether:connect or /blether:setup. Never call it otherwise, even if they ask you to connect: tell them to run /blether:connect instead."
|
|
51152
51152
|
}, async () => {
|
|
51153
51153
|
let connected;
|
|
51154
51154
|
try {
|
|
@@ -51162,7 +51162,7 @@ function createDormantServer(connect2) {
|
|
|
51162
51162
|
connectTool.remove();
|
|
51163
51163
|
const disconnectTool = server2.registerTool("disconnect", {
|
|
51164
51164
|
title: "Disconnect from Blether",
|
|
51165
|
-
description: "Disconnects this session from Blether. Call it
|
|
51165
|
+
description: "Disconnects this session from Blether. Call it ONLY when your developer runs /blether:disconnect. Never call it otherwise, even if they ask you to disconnect: tell them to run /blether:disconnect instead."
|
|
51166
51166
|
}, async () => {
|
|
51167
51167
|
const result = await connected.disconnect();
|
|
51168
51168
|
disconnectTool.remove();
|
|
@@ -51595,7 +51595,7 @@ var SetupProblem = class extends Error {
|
|
|
51595
51595
|
};
|
|
51596
51596
|
var REFUSAL_FIXES = {
|
|
51597
51597
|
"agent-in-use": (agent) => `Only one session can act as ${agent} at a time. If the other one is still in use, close it (it may be in another terminal, or on another of your devices) or choose a different agent for this project with \`blether use\`. If this session should be ${agent} instead, use take_over_agent.`,
|
|
51598
|
-
"agent-owned-by-another": (agent, team) => `${agent} is another developer's agent. Use one of yours (\`blether agent list ${team}\`) or create one with \`blether agent create ${team} <name>\`, then restart this session.`,
|
|
51598
|
+
"agent-owned-by-another": (agent, team) => `${agent} is another developer's agent. Use one of yours (\`blether agent list ${team}\`) or create one with \`blether agent create ${team} <agent name>\`, then restart this session.`,
|
|
51599
51599
|
"not-a-member": () => "Ask a teammate for an invite and run `blether join <invite>`, then restart this session.",
|
|
51600
51600
|
"unknown-team": (_agent, team) => `The relay doesn't know ${team}; it may have been reset. Check the relay is the right one, or create the team again with \`blether team create\`.`,
|
|
51601
51601
|
"authentication-failed": () => "This device's identity didn't verify. Check `blether whoami`; if it's damaged, set this device up again.",
|
|
@@ -51693,7 +51693,7 @@ function connectedGuidance({ agent, team, inbox, pendingEscalations }) {
|
|
|
51693
51693
|
"",
|
|
51694
51694
|
"Now:",
|
|
51695
51695
|
"1. Call read_mailbox.",
|
|
51696
|
-
`2. Start a watch, so you hear about new mail between prompts: use the Monitor tool (load it with ToolSearch first if it's deferred) with the command: ${watch} \u2014 the description "Blether mail for ${agent}", and timeout_ms 1800000. Each line it prints means new mail: call read_mailbox. When the watch expires, start it again. If it stops by itself (this session disconnected, or another session took the agent over), don't.`
|
|
51696
|
+
`2. Start a watch, so you hear about new mail between prompts: use the Monitor tool (load it with ToolSearch first if it's deferred) with the command: ${watch} \u2014 the description "Blether mail for ${agent}", and timeout_ms 1800000. Each line it prints means new mail: call read_mailbox. When the watch expires, start it again with --quiet-start added to the command, silently: don't mention the expiry or the restart to your developer. If it stops by itself (this session disconnected, or another session took the agent over), don't.`
|
|
51697
51697
|
].join("\n");
|
|
51698
51698
|
}
|
|
51699
51699
|
async function connect(env, log, into) {
|
|
@@ -51708,11 +51708,11 @@ async function connect(env, log, into) {
|
|
|
51708
51708
|
}
|
|
51709
51709
|
const agentName = env.BLETHER_AGENT ?? session?.contents.agent;
|
|
51710
51710
|
if (agentName === void 0) {
|
|
51711
|
-
throw new SetupProblem("No agent is chosen for this project. Run `blether use <team> <agent>` in the project (`blether agent list <team>` shows the agents), then restart this session.");
|
|
51711
|
+
throw new SetupProblem("No agent is chosen for this project. Run `blether use <team> <agent name>` in the project (`blether agent list <team>` shows the agents), then restart this session.");
|
|
51712
51712
|
}
|
|
51713
51713
|
const agent = AgentName.safeParse(agentName);
|
|
51714
51714
|
if (!agent.success) {
|
|
51715
|
-
throw new SetupProblem(`BLETHER_AGENT is set to "${agentName}", which isn't an agent name (lowercase letters, digits and hyphens). Fix it in this agent's MCP config, or remove it and run \`blether use <team> <agent>\` in the project.`);
|
|
51715
|
+
throw new SetupProblem(`BLETHER_AGENT is set to "${agentName}", which isn't an agent name (lowercase letters, digits and hyphens). Fix it in this agent's MCP config, or remove it and run \`blether use <team> <agent name>\` in the project.`);
|
|
51716
51716
|
}
|
|
51717
51717
|
if (session)
|
|
51718
51718
|
log(`blether bridge using ${session.path}`);
|
|
@@ -51728,7 +51728,7 @@ async function connect(env, log, into) {
|
|
|
51728
51728
|
}
|
|
51729
51729
|
const teamName = env.BLETHER_TEAM ?? session?.contents.team;
|
|
51730
51730
|
if (!teamName) {
|
|
51731
|
-
throw new SetupProblem("No team is chosen for this project. Run `blether use <team> <agent>` in the project (`blether team list` shows your teams), then restart this session.");
|
|
51731
|
+
throw new SetupProblem("No team is chosen for this project. Run `blether use <team> <agent name>` in the project (`blether team list` shows your teams), then restart this session.");
|
|
51732
51732
|
}
|
|
51733
51733
|
let team;
|
|
51734
51734
|
try {
|
package/plugin/dist/cli.js
CHANGED
|
@@ -27618,7 +27618,7 @@ function parseDeviceGrant(text) {
|
|
|
27618
27618
|
}
|
|
27619
27619
|
|
|
27620
27620
|
// ../protocol/dist/version.js
|
|
27621
|
-
var BLETHER_VERSION = "0.2.
|
|
27621
|
+
var BLETHER_VERSION = "0.2.2";
|
|
27622
27622
|
|
|
27623
27623
|
// ../protocol/dist/wire.js
|
|
27624
27624
|
var AudienceHint = external_exports.object({
|
|
@@ -28737,6 +28737,9 @@ function writeSessionFile(projectDir, contents, bletherHome) {
|
|
|
28737
28737
|
return path;
|
|
28738
28738
|
}
|
|
28739
28739
|
|
|
28740
|
+
// ../bridge/dist/watch.js
|
|
28741
|
+
import { readFileSync as readFileSync6, renameSync as renameSync2, writeFileSync as writeFileSync6 } from "node:fs";
|
|
28742
|
+
|
|
28740
28743
|
// ../bridge/dist/inbox-file.js
|
|
28741
28744
|
import { existsSync as existsSync5, mkdirSync as mkdirSync5, readFileSync as readFileSync5, renameSync, writeFileSync as writeFileSync5 } from "node:fs";
|
|
28742
28745
|
var InboxState = external_exports.object({
|
|
@@ -28769,7 +28772,8 @@ function unreadSummary(state) {
|
|
|
28769
28772
|
}
|
|
28770
28773
|
|
|
28771
28774
|
// ../bridge/dist/watch.js
|
|
28772
|
-
function watchInbox(path, session, emit, end, { intervalMs = 1e3 } = {}) {
|
|
28775
|
+
function watchInbox(path, session, emit, end, { intervalMs = 1e3, quietStart = false } = {}) {
|
|
28776
|
+
const seenPath = `${path}.watched`;
|
|
28773
28777
|
let arrivals;
|
|
28774
28778
|
let timer;
|
|
28775
28779
|
let stopped = false;
|
|
@@ -28786,18 +28790,39 @@ function watchInbox(path, session, emit, end, { intervalMs = 1e3 } = {}) {
|
|
|
28786
28790
|
end(state.session === session ? "disconnected" : "taken-over");
|
|
28787
28791
|
return;
|
|
28788
28792
|
}
|
|
28793
|
+
const seen = arrivals ?? (quietStart ? readSeen(seenPath, session) : void 0);
|
|
28789
28794
|
const summary = unreadSummary(state);
|
|
28790
|
-
const arrived =
|
|
28791
|
-
if (summary && (
|
|
28795
|
+
const arrived = seen !== void 0 && state.arrivals > seen;
|
|
28796
|
+
if (summary && (seen === void 0 || arrived)) {
|
|
28792
28797
|
emit(arrived && state.lastFrom ? `New Blether message from ${state.lastFrom}. ${summary}` : summary);
|
|
28793
28798
|
}
|
|
28794
|
-
|
|
28799
|
+
if (state.arrivals !== arrivals) {
|
|
28800
|
+
arrivals = state.arrivals;
|
|
28801
|
+
writeSeen(seenPath, session, arrivals);
|
|
28802
|
+
}
|
|
28795
28803
|
};
|
|
28796
28804
|
check2();
|
|
28797
28805
|
if (!stopped)
|
|
28798
28806
|
timer = setInterval(check2, intervalMs);
|
|
28799
28807
|
return stop;
|
|
28800
28808
|
}
|
|
28809
|
+
function readSeen(path, session) {
|
|
28810
|
+
try {
|
|
28811
|
+
const seen = JSON.parse(readFileSync6(path, "utf8"));
|
|
28812
|
+
return seen.session === session && Number.isInteger(seen.arrivals) ? seen.arrivals : void 0;
|
|
28813
|
+
} catch {
|
|
28814
|
+
return void 0;
|
|
28815
|
+
}
|
|
28816
|
+
}
|
|
28817
|
+
function writeSeen(path, session, arrivals) {
|
|
28818
|
+
try {
|
|
28819
|
+
const temp = `${path}.${process.pid}.tmp`;
|
|
28820
|
+
writeFileSync6(temp, `${JSON.stringify({ session, arrivals })}
|
|
28821
|
+
`);
|
|
28822
|
+
renameSync2(temp, path);
|
|
28823
|
+
} catch {
|
|
28824
|
+
}
|
|
28825
|
+
}
|
|
28801
28826
|
|
|
28802
28827
|
// ../bridge/dist/claude-install.js
|
|
28803
28828
|
import { spawn } from "node:child_process";
|
|
@@ -28877,17 +28902,21 @@ Commands:
|
|
|
28877
28902
|
device list List your identity's devices
|
|
28878
28903
|
device revoke <fingerprint> Revoke a lost or stolen device, from another of yours
|
|
28879
28904
|
role add <team> <role> Add a role to the team's agreed list
|
|
28880
|
-
|
|
28905
|
+
role list <team> List the team's roles
|
|
28906
|
+
agent create <team> <agent name> [--role <r>]...
|
|
28881
28907
|
Create an agent you own, with roles from the team's list
|
|
28882
|
-
agent roles <team> <name> [--role <r>]...
|
|
28908
|
+
agent roles <team> <agent name> [--role <r>]...
|
|
28883
28909
|
Replace the roles of one of your agents
|
|
28884
28910
|
agent list <team> Show the team's agents and who is online
|
|
28885
|
-
agent delete <team> <name>
|
|
28886
|
-
|
|
28911
|
+
agent delete <team> <agent name>
|
|
28912
|
+
Delete one of your agents (or any, as Team Admin)
|
|
28913
|
+
use <team> <agent name> [--dir <path>]
|
|
28914
|
+
Make sessions started in this project (or <path>) act as the agent
|
|
28887
28915
|
claude install Install (or update) the Blether plugin in Claude Code
|
|
28888
|
-
watch --inbox <path> --session <id>
|
|
28916
|
+
watch --inbox <path> --session <id> [--quiet-start]
|
|
28889
28917
|
Print a line whenever a connected session's agent gets mail
|
|
28890
|
-
(the bridge gives its agent this command when it connects
|
|
28918
|
+
(the bridge gives its agent this command when it connects;
|
|
28919
|
+
--quiet-start, for a restart, skips mail already reported)
|
|
28891
28920
|
escalations List messages your agents are holding for your decision
|
|
28892
28921
|
status One line for your Claude Code status line: escalations waiting
|
|
28893
28922
|
policy Show your Approval Policy on this device
|
|
@@ -28969,6 +28998,8 @@ ${USAGE}`);
|
|
|
28969
28998
|
const [sub, ...args] = rest;
|
|
28970
28999
|
if (sub === "add")
|
|
28971
29000
|
return roleAdd(args, ctx);
|
|
29001
|
+
if (sub === "list")
|
|
29002
|
+
return roleList(args, ctx);
|
|
28972
29003
|
throw new CliError(`Unknown role command: ${sub ?? "(none)"}
|
|
28973
29004
|
|
|
28974
29005
|
${USAGE}`);
|
|
@@ -29424,6 +29455,19 @@ async function roleAdd(args, ctx) {
|
|
|
29424
29455
|
ctx.io.out(`Added the ${role} role to ${record2.name}.`);
|
|
29425
29456
|
return 0;
|
|
29426
29457
|
}
|
|
29458
|
+
async function roleList(args, ctx) {
|
|
29459
|
+
const record2 = loadTeam(ctx.teams, args[0]);
|
|
29460
|
+
const credentials = loadCredentials(ctx.store);
|
|
29461
|
+
const { team } = await withRelay(ctx, record2.relayUrl, credentials, (relay) => relay.getTeam(record2.id));
|
|
29462
|
+
if (team.roles.length === 0) {
|
|
29463
|
+
ctx.io.out(`${record2.name} has no roles yet. Add one with: blether role add ${record2.name} <role>`);
|
|
29464
|
+
return 0;
|
|
29465
|
+
}
|
|
29466
|
+
ctx.io.out(`Roles in ${record2.name}:`);
|
|
29467
|
+
for (const role of team.roles)
|
|
29468
|
+
ctx.io.out(` ${role}`);
|
|
29469
|
+
return 0;
|
|
29470
|
+
}
|
|
29427
29471
|
async function agentCreate(args, ctx) {
|
|
29428
29472
|
const { values, positionals } = parseArgs({
|
|
29429
29473
|
args,
|
|
@@ -29431,7 +29475,7 @@ async function agentCreate(args, ctx) {
|
|
|
29431
29475
|
options: { role: { type: "string", multiple: true } }
|
|
29432
29476
|
});
|
|
29433
29477
|
const record2 = loadTeam(ctx.teams, positionals[0]);
|
|
29434
|
-
const name = parseName(AgentName, positionals[1], "Usage: blether agent create <team> <name> [--role <role>]...");
|
|
29478
|
+
const name = parseName(AgentName, positionals[1], "Usage: blether agent create <team> <agent name> [--role <role>]...");
|
|
29435
29479
|
const roles = values.role ?? [];
|
|
29436
29480
|
const credentials = loadCredentials(ctx.store);
|
|
29437
29481
|
await withRelay(ctx, record2.relayUrl, credentials, async (relay) => {
|
|
@@ -29453,7 +29497,7 @@ async function agentRoles(args, ctx) {
|
|
|
29453
29497
|
options: { role: { type: "string", multiple: true } }
|
|
29454
29498
|
});
|
|
29455
29499
|
const record2 = loadTeam(ctx.teams, positionals[0]);
|
|
29456
|
-
const name = parseName(AgentName, positionals[1], "Usage: blether agent roles <team> <name> [--role <role>]...");
|
|
29500
|
+
const name = parseName(AgentName, positionals[1], "Usage: blether agent roles <team> <agent name> [--role <role>]...");
|
|
29457
29501
|
const roles = values.role ?? [];
|
|
29458
29502
|
const credentials = loadCredentials(ctx.store);
|
|
29459
29503
|
const me2 = verifyIdentityLog(credentials.identity).id;
|
|
@@ -29506,7 +29550,7 @@ async function teamRemove(args, ctx) {
|
|
|
29506
29550
|
}
|
|
29507
29551
|
async function agentDelete(args, ctx) {
|
|
29508
29552
|
const record2 = loadTeam(ctx.teams, args[0]);
|
|
29509
|
-
const name = parseName(AgentName, args[1], "Usage: blether agent delete <team> <name>");
|
|
29553
|
+
const name = parseName(AgentName, args[1], "Usage: blether agent delete <team> <agent name>");
|
|
29510
29554
|
const credentials = loadCredentials(ctx.store);
|
|
29511
29555
|
const me2 = verifyIdentityLog(credentials.identity).id;
|
|
29512
29556
|
await withRelay(ctx, record2.relayUrl, credentials, async (relay) => {
|
|
@@ -29525,13 +29569,17 @@ async function agentDelete(args, ctx) {
|
|
|
29525
29569
|
async function watch(args, ctx) {
|
|
29526
29570
|
const { values } = parseArgs({
|
|
29527
29571
|
args,
|
|
29528
|
-
options: {
|
|
29572
|
+
options: {
|
|
29573
|
+
inbox: { type: "string" },
|
|
29574
|
+
session: { type: "string" },
|
|
29575
|
+
"quiet-start": { type: "boolean" }
|
|
29576
|
+
}
|
|
29529
29577
|
});
|
|
29530
29578
|
if (!values.inbox || !values.session) {
|
|
29531
|
-
throw new CliError("Usage: blether watch --inbox <path> --session <id>");
|
|
29579
|
+
throw new CliError("Usage: blether watch --inbox <path> --session <id> [--quiet-start]");
|
|
29532
29580
|
}
|
|
29533
29581
|
const ended = await new Promise((resolve3) => {
|
|
29534
|
-
const stop = watchInbox(values.inbox, values.session, ctx.io.out, (why) => resolve3(why === "taken-over" ? "Blether watch stopped: another session took over this agent." : "Blether watch stopped: this session disconnected from Blether."));
|
|
29582
|
+
const stop = watchInbox(values.inbox, values.session, ctx.io.out, (why) => resolve3(why === "taken-over" ? "Blether watch stopped: another session took over this agent." : "Blether watch stopped: this session disconnected from Blether."), { quietStart: values["quiet-start"] ?? false });
|
|
29535
29583
|
for (const signal of ["SIGINT", "SIGTERM"]) {
|
|
29536
29584
|
process.once(signal, () => {
|
|
29537
29585
|
stop();
|
|
@@ -29563,7 +29611,7 @@ async function use(args, ctx) {
|
|
|
29563
29611
|
allowPositionals: true,
|
|
29564
29612
|
options: { dir: { type: "string" } }
|
|
29565
29613
|
});
|
|
29566
|
-
const usage = "Usage: blether use <team> <agent> [--dir <path>]";
|
|
29614
|
+
const usage = "Usage: blether use <team> <agent name> [--dir <path>]";
|
|
29567
29615
|
if (positionals.length === 0)
|
|
29568
29616
|
throw new CliError(usage);
|
|
29569
29617
|
const record2 = loadTeam(ctx.teams, positionals[0]);
|
|
@@ -29598,15 +29646,15 @@ async function agentList(args, ctx) {
|
|
|
29598
29646
|
online: await relay.getPresence(record2.id)
|
|
29599
29647
|
}));
|
|
29600
29648
|
if (team.agents.length === 0) {
|
|
29601
|
-
ctx.io.out(`${record2.name} has no agents yet. Create one with: blether agent create ${record2.name} <name>`);
|
|
29602
|
-
|
|
29603
|
-
|
|
29604
|
-
|
|
29605
|
-
|
|
29606
|
-
|
|
29607
|
-
|
|
29608
|
-
|
|
29609
|
-
|
|
29649
|
+
ctx.io.out(`${record2.name} has no agents yet. Create one with: blether agent create ${record2.name} <agent name>`);
|
|
29650
|
+
} else {
|
|
29651
|
+
ctx.io.out(`Agents in ${record2.name}:`);
|
|
29652
|
+
for (const agent of team.agents) {
|
|
29653
|
+
const owner = identities.get(agent.owner)?.name ?? "(unknown)";
|
|
29654
|
+
const roles = agent.roles.length > 0 ? agent.roles.join(", ") : "no roles";
|
|
29655
|
+
const presence = online.includes(agent.name) ? "online" : "offline";
|
|
29656
|
+
ctx.io.out(` ${agent.name} ${owner}, ${roles}, ${presence}`);
|
|
29657
|
+
}
|
|
29610
29658
|
}
|
|
29611
29659
|
if (team.roles.length > 0) {
|
|
29612
29660
|
ctx.io.out(`Roles: ${team.roles.join(", ")}`);
|
|
@@ -29679,7 +29727,7 @@ async function join6(args, ctx) {
|
|
|
29679
29727
|
});
|
|
29680
29728
|
ctx.teams.save(record2);
|
|
29681
29729
|
ctx.io.out(`Joined ${record2.name}.`);
|
|
29682
|
-
ctx.io.out(`Create an agent with: blether agent create ${record2.name} <name>, then run blether use ${record2.name} <name> in your project.`);
|
|
29730
|
+
ctx.io.out(`Create an agent with: blether agent create ${record2.name} <agent name>, then run blether use ${record2.name} <agent name> in your project.`);
|
|
29683
29731
|
return 0;
|
|
29684
29732
|
}
|
|
29685
29733
|
function now(ctx) {
|
|
@@ -8,7 +8,10 @@ disable-model-invocation: true
|
|
|
8
8
|
|
|
9
9
|
The developer wants this session to use Blether. Until now the bridge has stayed out of it.
|
|
10
10
|
|
|
11
|
-
1.
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
11
|
+
1. Check whether another session is using the agent. Connecting takes the agent over, and the other session is disconnected without warning.
|
|
12
|
+
- Read `.blether/session.json` in the project root for the team and agent. If there isn't one, skip this check: the `connect` tool says what's missing.
|
|
13
|
+
- Run `blether agent list <team>` (the plugin puts `blether` on your Bash `PATH`). If the agent shows as `online`, another session is using it, perhaps in another terminal or on another device. Tell the developer, and go on only if they say to take it over.
|
|
14
|
+
2. Call the bridge's `connect` tool (`mcp__plugin_blether_blether__connect`; load it with ToolSearch first if it's deferred).
|
|
15
|
+
3. If it fails, tell the developer what it says. If no agent is chosen for this project, offer `/blether:setup`.
|
|
16
|
+
4. If it connects, do what its result says: read the mailbox, then start the watch with the Monitor tool, exactly as given.
|
|
17
|
+
5. Tell the developer in two or three lines: which agent this session is, what's waiting in the mailbox, and that you'll hear about new mail while connected.
|
|
@@ -31,15 +31,21 @@ Done when `team list` shows the team.
|
|
|
31
31
|
|
|
32
32
|
## 3. Agent for this project
|
|
33
33
|
|
|
34
|
-
|
|
34
|
+
First read `.blether/session.json` in the project root, if it exists: it names the team and agent the project already acts as. If so, tell them, and ask whether to keep it. Keeping it: go on to step 4, without running `use`.
|
|
35
35
|
|
|
36
|
-
|
|
36
|
+
Otherwise, run `agent list <team>` and ask which of **their** agents this project should act as, or what to call a new one. Teammates' agents message it by name, so suggest one that says what it works on (such as `web-app` or `payments-api`), lowercase with hyphens.
|
|
37
37
|
|
|
38
|
-
|
|
38
|
+
For a new agent, run `role list <team>` and ask which roles describe what it does. Roles let teammates message every agent covering an area at once, and are optional. If one they want isn't listed, run `role add <team> <role>`. Then run `agent create <team> <agent name>`, with `--role <role>` for each role.
|
|
39
|
+
|
|
40
|
+
Then run `use <team> <agent name>` in the project root. It writes `.blether/session.json`, which git ignores.
|
|
41
|
+
|
|
42
|
+
Done when the project has the agent they want, kept or set with `use`.
|
|
39
43
|
|
|
40
44
|
## 4. Connect and check
|
|
41
45
|
|
|
42
|
-
Setting Blether up is a request to use it, so connect this session
|
|
46
|
+
Setting Blether up is a request to use it, so connect this session. Connecting takes the agent over from any other session connected as it, which is then disconnected without warning. So first run `agent list <team>`: if the project's agent shows as `online`, another session (perhaps in another terminal, or on another device) is using it. Tell them, and connect only if they say to take it over.
|
|
47
|
+
|
|
48
|
+
To connect, call the bridge's `connect` tool (`mcp__plugin_blether_blether__connect`; load it with ToolSearch first if it's deferred). It reads the project's agent when called, so there's no need to restart.
|
|
43
49
|
|
|
44
50
|
Done when it connects and its result's instructions are followed (read the mailbox, start the watch). If it fails, fix what it says.
|
|
45
51
|
|
|
@@ -51,6 +57,6 @@ Tell them how Blether works from now on:
|
|
|
51
57
|
|
|
52
58
|
Offer this option and set it up if they want it:
|
|
53
59
|
|
|
54
|
-
- **Status line**: `blether status` prints a count of messages agents are holding for the developer's decision.
|
|
60
|
+
- **Status line**: `blether status` prints a count of messages agents are holding for the developer's decision. Read `statusLine` in `~/.claude/settings.json` first: if it already runs `blether status`, say so and skip this. Otherwise, offer to add it, merging with any status line they already have.
|
|
55
61
|
|
|
56
62
|
Finish with a one-paragraph summary: who they are, which team, which agent this project acts as, and what's enabled.
|