@trim21/personal-pi-extensions 0.0.344 → 0.0.346

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
@@ -321,7 +321,7 @@ sqlite 文件路径按优先级取第一个可用值:
321
321
  ```
322
322
  /talk-group-join # 无参:自动创建一个新 group(uuid 作为组名)并加入
323
323
  /talk-group-join <name> # 加入名为 name 的 group;不存在则创建(名字允许字母/数字/-/_)
324
- /talk-group-join-last # 加入最近创建的 group(方便新开 session 快速归队)
324
+ /talk-group-join-last # 加入最近创建的 group(方便新开 session 快速归队);支持 --name <alias> 设置显示名
325
325
  /talk-group-leave # 离开当前 group(组空了自动删除)
326
326
  /talk-group-list # 列出所有 group 及其成员,最新创建的在前
327
327
  /talk-group-del <name> # 删除指定 group(成员随之变为未入组)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@trim21/personal-pi-extensions",
3
- "version": "0.0.344",
3
+ "version": "0.0.346",
4
4
  "type": "module",
5
5
  "description": "Custom pi coding-agent extensions: bwrap sandbox, workspace guard, opencode edit, and more",
6
6
  "keywords": [
package/src/talk/core.ts CHANGED
@@ -530,7 +530,7 @@ export class TalkCore {
530
530
  return `Invalid group name '${name}'. Allowed: letters, digits, '-' and '_' (max 64 chars).`;
531
531
  }
532
532
  if (agentName !== undefined) {
533
- await this.writeSelf({ name: agentName });
533
+ await this.writeSelf({ name: agentName, alias: agentName });
534
534
  }
535
535
  const nameNote = agentName === undefined ? "" : ` You are visible as "${agentName}".`;
536
536
  const existing = await readGroup(this.storage, name);
@@ -556,12 +556,15 @@ export class TalkCore {
556
556
  return `Created group ${name}.${nameNote} Other agents join it with /talk-group-join ${name}.`;
557
557
  }
558
558
 
559
- /** Join the most recently created group; no-op when already in it. */
560
- async groupJoinLast(): Promise<string> {
559
+ /**
560
+ * Join the most recently created group; no-op when already in it. When
561
+ * `agentName` is given, the agent's display name is set to it.
562
+ */
563
+ async groupJoinLast(agentName?: string): Promise<string> {
561
564
  const groups = await listGroups(this.storage);
562
565
  if (groups.length === 0) return "No groups. Create one with /talk-group-join.";
563
566
  const latest = groups.reduce((a, b) => (b.createdAt > a.createdAt ? b : a));
564
- return this.groupJoin(latest.id);
567
+ return this.groupJoin(latest.id, agentName);
565
568
  }
566
569
 
567
570
  /** Leave the current group; an emptied group is deleted. */
@@ -618,6 +621,18 @@ export class TalkCore {
618
621
  return `Groups (${groups.length}):\n${lines.join("\n")}`;
619
622
  }
620
623
 
624
+ /**
625
+ * Status-bar text for the caller: "alias@group" when an explicit alias was
626
+ * set via `talk-group-join --name`, "@group" otherwise; undefined when the
627
+ * caller is in no group (the adapter clears its status bar).
628
+ */
629
+ async groupStatus(): Promise<string | undefined> {
630
+ const self = this.requireSelf();
631
+ const group = await groupForAgent(this.storage, self.agentId);
632
+ if (!group) return undefined;
633
+ return self.alias ? `${self.alias}@${group.id}` : `@${group.id}`;
634
+ }
635
+
621
636
  async send(to: string, body: string): Promise<string> {
622
637
  if (!to) return 'send requires "to".';
623
638
  if (!body) return 'send requires "message".';
package/src/talk/index.ts CHANGED
@@ -14,7 +14,9 @@ import * as path from "node:path";
14
14
 
15
15
  import {
16
16
  type ExtensionAPI,
17
+ type ExtensionCommandContext,
17
18
  type ExtensionContext,
19
+ type ExtensionUIContext,
18
20
  getAgentDir,
19
21
  truncateToVisualLines,
20
22
  } from "@earendil-works/pi-coding-agent";
@@ -161,6 +163,19 @@ export default function talk(pi: ExtensionAPI) {
161
163
  return undefined;
162
164
  }
163
165
 
166
+ /** Refresh the talk footer/status-bar text: "alias@group", or "@group" with no explicit alias. */
167
+ function refreshGroupStatus(ui: ExtensionUIContext): void {
168
+ void (async () => {
169
+ if (!self) return;
170
+ try {
171
+ const text = await core.groupStatus();
172
+ ui.setStatus("talk", text && ui.theme.fg("accent", text));
173
+ } catch {
174
+ // status bar is best-effort; never break the agent
175
+ }
176
+ })();
177
+ }
178
+
164
179
  // ── Lifecycle ──────────────────────────────────────────────────────────
165
180
 
166
181
  pi.on("session_start", (_event, ctx: ExtensionContext) => {
@@ -177,7 +192,7 @@ export default function talk(pi: ExtensionAPI) {
177
192
  lastSeenAt: now,
178
193
  status: "idle",
179
194
  };
180
- void core.start(self);
195
+ void core.start(self).then(() => refreshGroupStatus(ctx.ui));
181
196
  });
182
197
 
183
198
  pi.on("agent_start", () => core.setWorking());
@@ -278,6 +293,7 @@ export default function talk(pi: ExtensionAPI) {
278
293
  function handleCommand<TFlags extends TObject>(
279
294
  spec: CommandSpec<TFlags>,
280
295
  args: string,
296
+ ctx: ExtensionCommandContext,
281
297
  run: (parsed: OkResult<TFlags>) => Promise<string> | string,
282
298
  ): Promise<void> {
283
299
  const parsed = parseCommand(spec, args);
@@ -289,6 +305,9 @@ export default function talk(pi: ExtensionAPI) {
289
305
  const initError = requireInit();
290
306
  const text = initError ?? (await run(parsed));
291
307
  pi.sendMessage({ customType: LIST_TYPE, content: text, display: true });
308
+ // Group membership can change under any of these commands; keep the
309
+ // footer/status-bar text in sync.
310
+ refreshGroupStatus(ctx.ui);
292
311
  })();
293
312
  }
294
313
 
@@ -327,9 +346,13 @@ export default function talk(pi: ExtensionAPI) {
327
346
 
328
347
  const TALK_GROUP_JOIN_LAST_SPEC = {
329
348
  name: "talk-group-join-last",
330
- usage: "",
331
- description: "Join the most recently created agent group (no-op when already in it).",
332
- flags: Type.Object({}),
349
+ usage: "[options]",
350
+ description:
351
+ "Join the most recently created agent group (no-op when already in it); --name <alias> additionally sets this agent's display name",
352
+ flags: Type.Object({
353
+ name: Type.Optional(Type.String({ description: "Set this agent's display name" })),
354
+ }),
355
+ flagMeta: { name: { short: "n", valuePlaceholder: "<alias>" } },
333
356
  arity: { max: 0 },
334
357
  };
335
358
 
@@ -368,13 +391,13 @@ export default function talk(pi: ExtensionAPI) {
368
391
 
369
392
  pi.registerCommand("talk", {
370
393
  description: TALK_SPEC.description,
371
- handler: (args) => handleCommand(TALK_SPEC, args, async () => core.list()),
394
+ handler: (args, ctx) => handleCommand(TALK_SPEC, args, ctx, async () => core.list()),
372
395
  });
373
396
 
374
397
  pi.registerCommand("talk-dead", {
375
398
  description: TALK_DEAD_SPEC.description,
376
- handler: (args) =>
377
- handleCommand(TALK_DEAD_SPEC, args, async (parsed) => {
399
+ handler: (args, ctx) =>
400
+ handleCommand(TALK_DEAD_SPEC, args, ctx, async (parsed) => {
378
401
  if (parsed.flags.all && parsed.args.length > 0) {
379
402
  return "--all cannot be combined with an agent id.\nTry '/talk-dead --help' for usage.";
380
403
  }
@@ -388,8 +411,8 @@ export default function talk(pi: ExtensionAPI) {
388
411
 
389
412
  pi.registerCommand("talk-group-join", {
390
413
  description: TALK_GROUP_JOIN_SPEC.description,
391
- handler: (args) =>
392
- handleCommand(TALK_GROUP_JOIN_SPEC, args, async (parsed) => {
414
+ handler: (args, ctx) =>
415
+ handleCommand(TALK_GROUP_JOIN_SPEC, args, ctx, async (parsed) => {
393
416
  const agentName = parsed.flags.name?.trim() || undefined;
394
417
  if (agentName !== undefined) explicitName = agentName;
395
418
  return core.groupJoin(parsed.args[0], agentName);
@@ -398,29 +421,38 @@ export default function talk(pi: ExtensionAPI) {
398
421
 
399
422
  pi.registerCommand("talk-group-join-last", {
400
423
  description: TALK_GROUP_JOIN_LAST_SPEC.description,
401
- handler: (args) =>
402
- handleCommand(TALK_GROUP_JOIN_LAST_SPEC, args, async () => core.groupJoinLast()),
424
+ handler: (args, ctx) =>
425
+ handleCommand(TALK_GROUP_JOIN_LAST_SPEC, args, ctx, async (parsed) => {
426
+ const agentName = parsed.flags.name?.trim() || undefined;
427
+ if (agentName !== undefined) explicitName = agentName;
428
+ return core.groupJoinLast(agentName);
429
+ }),
403
430
  });
404
431
 
405
432
  pi.registerCommand("talk-group-leave", {
406
433
  description: TALK_GROUP_LEAVE_SPEC.description,
407
- handler: (args) => handleCommand(TALK_GROUP_LEAVE_SPEC, args, async () => core.groupLeave()),
434
+ handler: (args, ctx) =>
435
+ handleCommand(TALK_GROUP_LEAVE_SPEC, args, ctx, async () => core.groupLeave()),
408
436
  });
409
437
 
410
438
  pi.registerCommand("talk-group-list", {
411
439
  description: TALK_GROUP_LIST_SPEC.description,
412
- handler: (args) => handleCommand(TALK_GROUP_LIST_SPEC, args, async () => core.groupList()),
440
+ handler: (args, ctx) =>
441
+ handleCommand(TALK_GROUP_LIST_SPEC, args, ctx, async () => core.groupList()),
413
442
  });
414
443
 
415
444
  pi.registerCommand("talk-group-del", {
416
445
  description: TALK_GROUP_DEL_SPEC.description,
417
- handler: (args) =>
418
- handleCommand(TALK_GROUP_DEL_SPEC, args, async (parsed) => core.groupDelete(parsed.args[0])),
446
+ handler: (args, ctx) =>
447
+ handleCommand(TALK_GROUP_DEL_SPEC, args, ctx, async (parsed) =>
448
+ core.groupDelete(parsed.args[0]),
449
+ ),
419
450
  });
420
451
 
421
452
  pi.registerCommand("talk-group-clear", {
422
453
  description: TALK_GROUP_CLEAR_SPEC.description,
423
- handler: (args) => handleCommand(TALK_GROUP_CLEAR_SPEC, args, async () => core.groupClear()),
454
+ handler: (args, ctx) =>
455
+ handleCommand(TALK_GROUP_CLEAR_SPEC, args, ctx, async () => core.groupClear()),
424
456
  });
425
457
 
426
458
  // ── Delivery card ──────────────────────────────────────────────────────
@@ -36,6 +36,8 @@ export const AgentRecordSchema = Type.Object({
36
36
  addr: Type.String(),
37
37
  agentId: Type.String(),
38
38
  name: Type.String(),
39
+ /** Display name explicitly set via `talk-group-join --name`, distinct from the session name. */
40
+ alias: Type.Optional(Type.String()),
39
41
  cwd: Type.String(),
40
42
  pid: Type.Number(),
41
43
  pidStart: Type.Optional(Type.Number()),