@webinson/peer 0.4.26 → 0.4.28
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 +4 -4
- package/bin/peer.mjs +15 -4
- package/mod/peer-thread-mark/.claude-plugin/plugin.json +1 -1
- package/mod/peer-thread-mark/hooks/register.tsx +2 -2
- package/mod/peer-thread-mark/tests/collision-cases.mjs +6 -6
- package/mod/peer-thread-mark/tests/collision.test.tsx +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -146,16 +146,16 @@ follow their own settings. `peer uninstall --codex` removes the plugin from Code
|
|
|
146
146
|
## Same file: who else was on an edited file
|
|
147
147
|
|
|
148
148
|
After each edit in a shared thread, Peer sends the edited paths to the Hub and learns who else is on them: an agent
|
|
149
|
-
that claimed the file in the last 3 minutes, or
|
|
150
|
-
those while
|
|
149
|
+
that claimed the file in the last 3 minutes, or another person's session that changed the file in its work (the Peer
|
|
150
|
+
app keeps reporting those files while the session lasts, committed or not). In Claude Code the thread mark runs `peer collide` after each `Edit`, `Write`, `MultiEdit` or
|
|
151
151
|
`NotebookEdit`; in Codex the Peer Thread Mark plugin checks after each `apply_patch`.
|
|
152
152
|
|
|
153
153
|
The first time a set of people meets on a file, Claude Code draws a line under the edit (and under a folded group of
|
|
154
154
|
tool calls) with a **Same file** badge:
|
|
155
155
|
|
|
156
156
|
- `bob@… was on it 2 min ago`: another person's agent claimed the file;
|
|
157
|
-
- `bob@…
|
|
158
|
-
- `Your other thread was on it just now`, dimmed: another session of yours.
|
|
157
|
+
- `bob@… changed it`: a session of another person's changed the file in its work (it may be committed already);
|
|
158
|
+
- `Your other thread was on it just now`, dimmed: another session of yours. Files you changed yourself are not shown.
|
|
159
159
|
|
|
160
160
|
For another person, the agent also gets one sentence with the tool result, in Claude Code and in Codex, fenced as data
|
|
161
161
|
and built by the CLI from the Hub's fields only:
|
package/bin/peer.mjs
CHANGED
|
@@ -901,6 +901,7 @@ function threadUses(session, prompts) {
|
|
|
901
901
|
const name = (text) => path.basename(String(text).trim(), ".md").replace(/[_-]+/g, " ");
|
|
902
902
|
const clean = (text) => plain(String(text).replace(/\*\*/g, "").replace(/`/g, "")).slice(0, 120);
|
|
903
903
|
const uses = [];
|
|
904
|
+
const asking = new Map();
|
|
904
905
|
let last;
|
|
905
906
|
for (const line of fs.readFileSync(file, "utf8").split("\n")) {
|
|
906
907
|
let row;
|
|
@@ -936,7 +937,17 @@ function threadUses(session, prompts) {
|
|
|
936
937
|
if (block?.type !== "tool_use" || !/openviking/i.test(String(block.name))) continue;
|
|
937
938
|
const input = block.input ?? {};
|
|
938
939
|
const detail = input.query ?? input.pattern ?? input.uri ?? (Array.isArray(input.uris) ? input.uris.join(", ") : "") ?? "";
|
|
939
|
-
|
|
940
|
+
const use = { at, where: "tool", from: "yours", kind: String(block.name).split("__").pop(), items: detail ? [clean(String(detail).startsWith("viking://") ? name(detail) : detail)] : [], ...near("tool") };
|
|
941
|
+
uses.push(use);
|
|
942
|
+
if (block.id) asking.set(block.id, use);
|
|
943
|
+
}
|
|
944
|
+
}
|
|
945
|
+
// A call that was refused (Jellyfish or Inactive holds PeerMemory back) or failed asked nothing of the memory.
|
|
946
|
+
if (row.type === "user" && Array.isArray(row.message?.content)) {
|
|
947
|
+
for (const block of row.message.content) {
|
|
948
|
+
if (block?.type !== "tool_result" || !block.is_error || !asking.has(block.tool_use_id)) continue;
|
|
949
|
+
const at = uses.indexOf(asking.get(block.tool_use_id));
|
|
950
|
+
if (at >= 0) uses.splice(at, 1);
|
|
940
951
|
}
|
|
941
952
|
}
|
|
942
953
|
}
|
|
@@ -1375,8 +1386,8 @@ function minutesAgo(at, now = Date.now()) {
|
|
|
1375
1386
|
return mins === 1 ? "1 minute ago" : `${mins} minutes ago`;
|
|
1376
1387
|
}
|
|
1377
1388
|
|
|
1378
|
-
// The people on a path, one entry each: a claim wins over
|
|
1379
|
-
//
|
|
1389
|
+
// The people on a path, one entry each: a claim wins over a file their session only reported changing in its work,
|
|
1390
|
+
// with the claim's own time. Your own changed files are no news to you and are left out.
|
|
1380
1391
|
function peopleOn(holders, me) {
|
|
1381
1392
|
const people = new Map();
|
|
1382
1393
|
for (const holder of holders) {
|
|
@@ -1404,7 +1415,7 @@ function collisionSentence(results, now = Date.now()) {
|
|
|
1404
1415
|
for (const person of result.people.filter((p) => !p.own).slice(0, 3)) {
|
|
1405
1416
|
lines.push(
|
|
1406
1417
|
person.kind === "changed"
|
|
1407
|
-
? `Another person, ${person.email},
|
|
1418
|
+
? `Another person, ${person.email}, changed ${result.path} in their work.`
|
|
1408
1419
|
: `Another person, ${person.email}, had an agent on ${result.path} ${minutesAgo(person.at, now)}.`,
|
|
1409
1420
|
);
|
|
1410
1421
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "peer-thread-mark",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.28",
|
|
4
4
|
"description": "Marks a Claude Code thread shared with Peer (/peer) with a band above the prompt, with /peer stop and /peer start; panes for /peer-list, /peer-thread and /peer-all (search your memories)",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"types": "./types/index.d.ts"
|
|
@@ -538,8 +538,8 @@ function collisionLine(ui: { Box: (p: never) => RenderNode; Text: (p: never) =>
|
|
|
538
538
|
const [newest] = others
|
|
539
539
|
const who = `${plain(newest!.email.split('@')[0])}@…`
|
|
540
540
|
const rest = others.length > 1 ? ` and ${others.length - 1} others` : ''
|
|
541
|
-
//
|
|
542
|
-
const what = newest!.kind === 'changed' ? '
|
|
541
|
+
// A file a session reported changing carries no age: the report is renewed every few seconds.
|
|
542
|
+
const what = newest!.kind === 'changed' ? 'changed it' : `was on it ${whenAgo(newest!.at, now)}`
|
|
543
543
|
const badge =
|
|
544
544
|
surface === 'terminal' ? (
|
|
545
545
|
<Text backgroundColor={COLLISION_COLOR} color="#ffffff" bold size="caption">{' Same file '}</Text>
|
|
@@ -25,7 +25,7 @@ export default {
|
|
|
25
25
|
"line": "bob@… was on it 2 min ago"
|
|
26
26
|
},
|
|
27
27
|
{
|
|
28
|
-
"name": "another person
|
|
28
|
+
"name": "another person changed the file in their work",
|
|
29
29
|
"holders": [
|
|
30
30
|
{
|
|
31
31
|
"email": "carol@acme.test",
|
|
@@ -41,9 +41,9 @@ export default {
|
|
|
41
41
|
}
|
|
42
42
|
],
|
|
43
43
|
"sentence": [
|
|
44
|
-
"Another person, carol@acme.test,
|
|
44
|
+
"Another person, carol@acme.test, changed src/cart.ts in their work."
|
|
45
45
|
],
|
|
46
|
-
"line": "carol@…
|
|
46
|
+
"line": "carol@… changed it"
|
|
47
47
|
},
|
|
48
48
|
{
|
|
49
49
|
"name": "one person in several sessions",
|
|
@@ -117,7 +117,7 @@ export default {
|
|
|
117
117
|
"line": "Your other thread was on it just now"
|
|
118
118
|
},
|
|
119
119
|
{
|
|
120
|
-
"name": "
|
|
120
|
+
"name": "a file you changed yourself",
|
|
121
121
|
"holders": [
|
|
122
122
|
{
|
|
123
123
|
"email": "ana@acme.test",
|
|
@@ -156,10 +156,10 @@ export default {
|
|
|
156
156
|
}
|
|
157
157
|
],
|
|
158
158
|
"sentence": [
|
|
159
|
-
"Another person, carol@acme.test,
|
|
159
|
+
"Another person, carol@acme.test, changed src/cart.ts in their work.",
|
|
160
160
|
"Another person, bob@acme.test, had an agent on src/cart.ts 2 minutes ago."
|
|
161
161
|
],
|
|
162
|
-
"line": "carol@…
|
|
162
|
+
"line": "carol@… changed it and 1 others"
|
|
163
163
|
},
|
|
164
164
|
{
|
|
165
165
|
"name": "a Hub before kinds",
|
|
@@ -162,7 +162,7 @@ test('after a timeout the turn makes no more calls, and an old CLI draws nothing
|
|
|
162
162
|
|
|
163
163
|
import CASES from './collision-cases.mjs'
|
|
164
164
|
|
|
165
|
-
test('the line under an edit follows the shared cases:
|
|
165
|
+
test('the line under an edit follows the shared cases: a changed file carries no age, your own are not drawn', async ($, on) => {
|
|
166
166
|
const state = setUp(on)
|
|
167
167
|
await $.classic.UserPromptSubmit({ prompt: 'go' } as never)
|
|
168
168
|
let n = 0
|