@tekmidian/pai 0.9.16 → 0.10.0

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.
@@ -146,6 +146,67 @@ function extractCompletedMessage(lines: string[]): string {
146
146
  return '';
147
147
  }
148
148
 
149
+ // ---------------------------------------------------------------------------
150
+ // AIBroker IPC helper — persistent name re-assertion
151
+ // ---------------------------------------------------------------------------
152
+
153
+ const AIBROKER_SOCKET = '/tmp/aibroker.sock';
154
+ const AIBROKER_TIMEOUT_MS = 2_000;
155
+
156
+ /**
157
+ * Ask AIBroker for the persisted user-chosen name for the current iTerm2 session.
158
+ * Returns the name string, or null if AIBroker is not running or no name is set.
159
+ * Uses ITERM_SESSION_ID environment variable as the session identifier.
160
+ */
161
+ async function getPersistentTabName(): Promise<string | null> {
162
+ const itermSessionId = process.env.ITERM_SESSION_ID;
163
+ if (!itermSessionId) return null;
164
+
165
+ return new Promise((resolve) => {
166
+ let done = false;
167
+ let buffer = '';
168
+ let timer: ReturnType<typeof setTimeout> | null = null;
169
+
170
+ function finish(result: string | null): void {
171
+ if (done) return;
172
+ done = true;
173
+ if (timer !== null) { clearTimeout(timer); timer = null; }
174
+ try { client.destroy(); } catch { /* ignore */ }
175
+ resolve(result);
176
+ }
177
+
178
+ const client = connect(AIBROKER_SOCKET, () => {
179
+ const msg = JSON.stringify({
180
+ id: randomUUID(),
181
+ method: 'get_persistent_name',
182
+ params: { itermSessionId },
183
+ }) + '\n';
184
+ client.write(msg);
185
+ });
186
+
187
+ client.on('data', (chunk: Buffer) => {
188
+ buffer += chunk.toString();
189
+ const nl = buffer.indexOf('\n');
190
+ if (nl === -1) return;
191
+ const line = buffer.slice(0, nl);
192
+ try {
193
+ const response = JSON.parse(line) as { ok: boolean; result?: { name: string | null } };
194
+ if (response.ok && response.result?.name) {
195
+ finish(response.result.name);
196
+ } else {
197
+ finish(null);
198
+ }
199
+ } catch {
200
+ finish(null);
201
+ }
202
+ });
203
+
204
+ client.on('error', () => finish(null));
205
+ client.on('end', () => { if (!done) finish(null); });
206
+ timer = setTimeout(() => finish(null), AIBROKER_TIMEOUT_MS);
207
+ });
208
+ }
209
+
149
210
  // ---------------------------------------------------------------------------
150
211
  // Daemon IPC relay — fast path
151
212
  // ---------------------------------------------------------------------------
@@ -775,6 +836,25 @@ async function main() {
775
836
  process.stderr.write(`\x1b]2;${message.slice(0, 50)}\x07`);
776
837
  }
777
838
 
839
+ // Re-assert persistent name — overrides any auto-title Claude Code may have set.
840
+ // If the user ran /Name "Solar" earlier, that name sticks for the life of the session.
841
+ {
842
+ const persistentName = await getPersistentTabName();
843
+ if (persistentName) {
844
+ try {
845
+ const { execSync } = await import('child_process');
846
+ const escaped = persistentName.replace(/'/g, "'\\''");
847
+ execSync(`printf '\\033]0;${escaped}\\007' >&2`);
848
+ execSync(`printf '\\033]2;${escaped}\\007' >&2`);
849
+ execSync(`printf '\\033]30;${escaped}\\007' >&2`);
850
+ process.stderr.write(`\x1b]2;${persistentName.slice(0, 50)}\x07`);
851
+ console.error(`Tab title re-asserted to persistent name: "${persistentName}"`);
852
+ } catch (e) {
853
+ console.error(`Failed to re-assert persistent tab title: ${e}`);
854
+ }
855
+ }
856
+ }
857
+
778
858
  // Send ntfy.sh notification (fast, fire-and-forget)
779
859
  if (message) {
780
860
  await sendNtfyNotification(message);