@termfleet/core 0.2.0 → 0.2.1

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.
@@ -302,6 +302,21 @@ export declare function resolveClaudeTranscriptPath(options: {
302
302
  home?: string;
303
303
  sessionId: string;
304
304
  }): string | undefined;
305
+ export declare function findLocalClaudeSession(sessionId: string, options?: {
306
+ home?: string;
307
+ }): {
308
+ sessionId: string;
309
+ cwd: string | undefined;
310
+ transcriptPath: string;
311
+ } | undefined;
312
+ export declare function findLocalAgentSession(agentSessionId: string, options?: {
313
+ home?: string;
314
+ }): {
315
+ agent: "claude" | "codex" | "gemini";
316
+ sessionId: string;
317
+ cwd: string | undefined;
318
+ transcriptPath: string;
319
+ } | undefined;
305
320
  export declare function emptyClaudeSession(options: {
306
321
  cwd: string;
307
322
  home?: string;
@@ -74,6 +74,59 @@ export function resolveClaudeTranscriptPath(options) {
74
74
  const found = findClaudeTranscriptById(join(options.home ?? agentHome(), ".claude", "projects"), assertSafeAgentSessionId(normalizeClaudeSessionId(options.sessionId)));
75
75
  return existsSync(found) ? found : undefined;
76
76
  }
77
+ // Read the working dir a Claude session ran in, straight from its transcript. The `cwd` is on the message
78
+ // records, not always the first header line, so scan a bounded prefix (headers are small). undefined if absent.
79
+ function readClaudeSessionCwd(transcriptPath, maxBytes = 65_536) {
80
+ let head;
81
+ try {
82
+ const fd = openSync(transcriptPath, "r");
83
+ try {
84
+ const buffer = Buffer.alloc(maxBytes);
85
+ const bytesRead = readSync(fd, buffer, 0, maxBytes, 0);
86
+ head = buffer.toString("utf8", 0, bytesRead);
87
+ }
88
+ finally {
89
+ closeSync(fd);
90
+ }
91
+ }
92
+ catch {
93
+ return undefined;
94
+ }
95
+ for (const line of head.split("\n")) {
96
+ if (!line)
97
+ continue;
98
+ try {
99
+ const row = JSON.parse(line);
100
+ if (typeof row.cwd === "string" && row.cwd)
101
+ return row.cwd;
102
+ }
103
+ catch { /* a truncated final line in the bounded read — ignore */ }
104
+ }
105
+ return undefined;
106
+ }
107
+ // Resolve a Claude session from its id ALONE (no cwd needed): find its transcript across every project dir and
108
+ // read the session's cwd from it. Local + provider-free — the primitive any tool needs when it has a session
109
+ // id but not its working dir (a session mirror, a resumer, a cross-project browser). undefined when the
110
+ // transcript doesn't exist yet (a just-launched session writes it only once it produces messages).
111
+ export function findLocalClaudeSession(sessionId, options = {}) {
112
+ const bareId = assertSafeAgentSessionId(normalizeClaudeSessionId(sessionId));
113
+ const transcriptPath = findClaudeTranscriptById(join(options.home ?? agentHome(), ".claude", "projects"), bareId);
114
+ if (!existsSync(transcriptPath))
115
+ return undefined;
116
+ return { sessionId: bareId, cwd: readClaudeSessionCwd(transcriptPath), transcriptPath };
117
+ }
118
+ // The agent-generic form: resolve a (prefixed or bare) agentSessionId → its provider, cwd, and transcript
119
+ // path, locally. A bare id is treated as Claude. (Codex/Gemini keep transcripts elsewhere; extend here when
120
+ // needed — non-Claude returns undefined for now.)
121
+ export function findLocalAgentSession(agentSessionId, options = {}) {
122
+ const separator = agentSessionId.indexOf(":");
123
+ const prefix = separator > 0 ? agentSessionId.slice(0, separator) : "";
124
+ const agent = prefix === "codex" ? "codex" : prefix === "gemini" ? "gemini" : "claude";
125
+ if (agent !== "claude")
126
+ return undefined;
127
+ const resolved = findLocalClaudeSession(agentSessionId, options);
128
+ return resolved ? { agent, ...resolved } : undefined;
129
+ }
77
130
  // A real, openable chat for a session that has no transcript yet — just an empty
78
131
  // conversation. Parsing empty JSONL yields a well-formed session with zero
79
132
  // messages, so a just-launched agent displays as an empty chat instead of 500ing.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@termfleet/core",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "Termfleet core: contracts, the provider SDK, and the agent-transcript/session library (Claude Code / Codex / Gemini). The reusable layer, usable beyond the console.",
5
5
  "keywords": [
6
6
  "claude-code",