@gitkraken/core-gitlens 0.13.0 → 0.14.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.
Files changed (67) hide show
  1. package/CHANGELOG.md +23 -2
  2. package/dist/git/features.d.ts +1 -1
  3. package/dist/git/features.js +1 -0
  4. package/dist/git/features.js.map +1 -1
  5. package/dist/git/models/branch.d.ts +5 -0
  6. package/dist/git/models/branch.js +1 -1
  7. package/dist/git/models/branch.js.map +1 -1
  8. package/dist/git/models/reference.d.ts +3 -0
  9. package/dist/git/models/status.js +1 -1
  10. package/dist/git/parsers/diffParser.js +1 -1
  11. package/dist/git/providers/operations.d.ts +3 -2
  12. package/dist/git/providers/provider.d.ts +15 -4
  13. package/dist/git/providers/revision.d.ts +17 -3
  14. package/dist/git/run.types.d.ts +12 -0
  15. package/dist/git/utils/branch.utils.d.ts +4 -1
  16. package/dist/git/utils/branch.utils.js +20 -5
  17. package/dist/git/utils/branch.utils.js.map +1 -1
  18. package/dist/git/utils/reference.utils.d.ts +1 -0
  19. package/dist/git/utils/reference.utils.js.map +1 -1
  20. package/dist/git-cli/cliGitProvider.d.ts +8 -2
  21. package/dist/git-cli/cliGitProvider.js +75 -13
  22. package/dist/git-cli/cliGitProvider.js.map +1 -1
  23. package/dist/git-cli/exec/git.d.ts +5 -0
  24. package/dist/git-cli/exec/git.js +28 -5
  25. package/dist/git-cli/exec/git.js.map +1 -1
  26. package/dist/git-cli/exec/indexLock.d.ts +3 -2
  27. package/dist/git-cli/exec/indexLock.js +16 -6
  28. package/dist/git-cli/exec/indexLock.js.map +1 -1
  29. package/dist/git-cli/parsers/blameParser.js +2 -2
  30. package/dist/git-cli/parsers/refParser.js +4 -2
  31. package/dist/git-cli/parsers/refParser.js.map +1 -1
  32. package/dist/git-cli/parsers/statusParser.d.ts +6 -0
  33. package/dist/git-cli/parsers/statusParser.js +33 -3
  34. package/dist/git-cli/parsers/statusParser.js.map +1 -1
  35. package/dist/git-cli/providers/branchRemote.d.ts +14 -0
  36. package/dist/git-cli/providers/branchRemote.js +36 -0
  37. package/dist/git-cli/providers/branchRemote.js.map +1 -0
  38. package/dist/git-cli/providers/branches.js +6 -3
  39. package/dist/git-cli/providers/branches.js.map +1 -1
  40. package/dist/git-cli/providers/commits.js +2 -2
  41. package/dist/git-cli/providers/config.js +2 -2
  42. package/dist/git-cli/providers/contributors.js +1 -1
  43. package/dist/git-cli/providers/graph.js +9 -6
  44. package/dist/git-cli/providers/graph.js.map +1 -1
  45. package/dist/git-cli/providers/operations.js +20 -16
  46. package/dist/git-cli/providers/operations.js.map +1 -1
  47. package/dist/git-cli/providers/pausedOperations.js +2 -2
  48. package/dist/git-cli/providers/pausedOperations.js.map +1 -1
  49. package/dist/git-cli/providers/revision.d.ts +6 -2
  50. package/dist/git-cli/providers/revision.js +38 -12
  51. package/dist/git-cli/providers/revision.js.map +1 -1
  52. package/dist/git-cli/providers/staging.js +1 -1
  53. package/dist/git-cli/providers/status.js +2 -2
  54. package/dist/ipc/discovery.js +1 -1
  55. package/dist/plus/agents/providers/claudeCodeTranscript.js +6 -6
  56. package/dist/plus/agents/providers/claudeCodeTranscript.js.map +1 -1
  57. package/dist/plus/ai/providers/openAICompatibleProviderBase.js +1 -1
  58. package/dist/plus/git-github/providers/github/revision.d.ts +6 -2
  59. package/dist/plus/git-github/providers/github/revision.js +15 -10
  60. package/dist/plus/git-github/providers/github/revision.js.map +1 -1
  61. package/dist/plus/git-github/providers/github/status.js +1 -1
  62. package/dist/plus/integrations/providers/azure/azure.js +1 -1
  63. package/dist/plus/integrations/providers/azure/workItemStateCategories.d.ts +5 -0
  64. package/dist/plus/integrations/providers/azure/workItemStateCategories.js +5 -0
  65. package/dist/plus/integrations/providers/azure/workItemStateCategories.js.map +1 -1
  66. package/dist/utils/env/node/exec.js +2 -2
  67. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"claudeCodeTranscript.js","names":[],"sources":["../../../../../plus/agents/src/providers/claudeCodeTranscript.ts"],"sourcesContent":["import { createReadStream } from 'fs';\nimport { open, readdir, readFile, stat } from 'fs/promises';\nimport { homedir } from 'os';\nimport { basename, dirname, join } from 'path';\nimport { createInterface } from 'readline';\n\nexport interface TranscriptTitles {\n\tcustom?: string;\n\tai?: string;\n\tagent?: string;\n}\n\n/** A transcript file discovered by {@link ClaudeCodeTranscriptReader.listSessions}, before its\n * contents are read — everything here comes from `readdir` + `stat`. */\nexport interface TranscriptSessionEntry {\n\treadonly sessionId: string;\n\treadonly path: string;\n\treadonly lastActivityMs: number;\n\treadonly size: number;\n}\n\n/** A {@link TranscriptSessionEntry} enriched with the fields recovered from the file's head/tail. */\nexport interface TranscriptSessionSummary extends TranscriptSessionEntry {\n\treadonly titles: TranscriptTitles;\n\treadonly lastPrompt?: string;\n}\n\nexport interface TranscriptSessionListing {\n\treadonly sessions: TranscriptSessionSummary[];\n\t/** Every transcript in the directory, not just the summarized slice — drives \"Showing N of M\". */\n\treadonly total: number;\n}\n\n/** A transcript found outside the queried cwd's project directory, plus the directory Claude\n * actually uses to resolve it for `--resume`. */\nexport interface ResumableTranscriptSessionSummary extends TranscriptSessionSummary {\n\treadonly cwd: string;\n}\n\nexport interface ResumableTranscriptSessionListing {\n\treadonly sessions: ResumableTranscriptSessionSummary[];\n\treadonly total: number;\n}\n\nexport interface EndedTranscriptDetails {\n\ttitles: TranscriptTitles;\n\tfirstPrompt?: string;\n\tlastPrompt?: string;\n}\n\ninterface CacheEntry {\n\tpath: string;\n\tmtimeMs: number;\n\tsize: number;\n\tnextOffset: number;\n\ttitles: TranscriptTitles;\n}\n\ninterface ListingCacheEntry {\n\tentries: TranscriptSessionEntry[];\n\tresolvedAt: number;\n}\n\ninterface NameCacheEntry {\n\tnames: string[];\n\tresolvedAt: number;\n}\n\ninterface SummaryCacheEntry {\n\tpath: string;\n\tmtimeMs: number;\n\tsize: number;\n\tsummary: TranscriptSessionSummary;\n}\n\ninterface TitleEntry {\n\ttype: string;\n\tsessionId?: string;\n\tcustomTitle?: string;\n\taiTitle?: string;\n\tagentName?: string;\n\tlastPrompt?: string;\n}\n\n/** Bytes read from each end of a transcript when summarizing. Titles and prompts cluster at both\n * extremes, and files run to tens of MB, so reading whole files is not an option. */\nconst summaryWindowSize = 64 * 1024;\n/** Listings are cheap (readdir + stat) but re-run per panel/sheet open; a short TTL absorbs bursts.\n * Time-based rather than dir-mtime-based because appends move file mtimes without touching the dir. */\nconst listingCacheTtlMs = 10 * 1000;\n/** Summaries are keyed by session, and a busy project has hundreds — unlike the per-live-session\n * title cache, this needs a ceiling. Sized to hold a whole junk-heavy store: the listing scan runs\n * to exhaustion, and each entry is tiny (a path plus a few short strings), so a cap below the\n * store's size would evict-and-re-read the junk tail on every listing instead of paying its\n * windowed reads once. */\nconst summaryCacheLimit = 1000;\nconst defaultListLimit = 50;\n\n/**\n * Reads Claude Code transcript JSONL files at `~/.claude/projects/<encoded-cwd>/<sessionId>.jsonl`\n * to surface `custom-title`, `ai-title`, and `agent-name` entries as fallback session names.\n *\n * Maintains a per-session cache keyed by mtime + last-read byte offset, so repeated calls only\n * read the appended tail. Last occurrence per type wins.\n *\n * `resolve` is async, so a `forget` (or a newer `resolve`) for the same session can land between\n * its `await`s. A per-session generation counter is stamped at entry and re-checked before the\n * final cache write, so a stale `resolve` can't resurrect a forgotten entry or clobber a newer\n * resolve's result.\n */\nexport class ClaudeCodeTranscriptReader {\n\tprivate readonly _cache = new Map<string, CacheEntry>();\n\tprivate readonly _generations = new Map<string, number>();\n\tprivate readonly _listings = new Map<string, ListingCacheEntry>();\n\t/** Transcript file names per project directory — the readdir behind both the plain listing and\n\t * the by-id scan, which sweeps every project directory. */\n\tprivate readonly _names = new Map<string, NameCacheEntry>();\n\t/** Project directories under {@link getProjectsRoot}, re-scanned by every by-id recovery. */\n\tprivate _projectDirs: { dirs: string[]; resolvedAt: number } | undefined;\n\t/** Insertion-ordered LRU — re-inserted on hit, oldest key evicted past {@link summaryCacheLimit}. */\n\tprivate readonly _summaries = new Map<string, SummaryCacheEntry>();\n\t/** Resolved project path per transcript path, bounded like {@link _summaries}. */\n\tprivate readonly _transcriptCwds = new Map<string, string>();\n\tprivate _nextGen = 0;\n\t/** Bumped by {@link invalidateListings}; async listing scans capture it at entry and skip their\n\t * cache write when it moved, so a stale in-flight result can't re-seed a just-cleared cache. */\n\tprivate _listingGen = 0;\n\n\t/** Drops the directory-listing caches (entries, names, project dirs) so the next listing re-reads\n\t * the store. Called when a session ends or is removed: its transcript may have appeared inside\n\t * the TTL window, and a cached listing would hide the new file from the very fetch meant to\n\t * surface it. Summary and cwd caches are keyed by file identity and stay valid, so they're kept.\n\t * The generation bump makes any in-flight listing skip its own cache write — otherwise a scan\n\t * started before the invalidation would re-seed the caches with pre-end results, freshly\n\t * timestamped (same discipline as `resolve`'s per-session generations). */\n\tinvalidateListings(): void {\n\t\tthis._listingGen++;\n\t\tthis._listings.clear();\n\t\tthis._names.clear();\n\t\tthis._projectDirs = undefined;\n\t}\n\n\tasync resolve(sessionId: string, cwd: string | undefined): Promise<TranscriptTitles | undefined> {\n\t\tconst gen = ++this._nextGen;\n\t\tthis._generations.set(sessionId, gen);\n\n\t\tconst cached = this._cache.get(sessionId);\n\t\tconst path = cached?.path ?? (await this.locateTranscript(sessionId, cwd));\n\t\tif (path == null) return undefined;\n\n\t\tlet stats;\n\t\ttry {\n\t\t\tstats = await stat(path);\n\t\t} catch {\n\t\t\t// File disappeared; drop any stale cache entry so a future call retries discovery.\n\t\t\t// Skip the delete if a newer resolve/forget claimed the slot — their state is fresher.\n\t\t\tif (this._generations.get(sessionId) === gen) {\n\t\t\t\tthis._cache.delete(sessionId);\n\t\t\t\tthis._generations.delete(sessionId);\n\t\t\t}\n\t\t\treturn undefined;\n\t\t}\n\n\t\tif (stats.mtimeMs === cached?.mtimeMs && stats.size === cached?.size) {\n\t\t\treturn cached.titles;\n\t\t}\n\n\t\t// Truncation or rewrite: stat shrank below our read cursor or mtime moved backward → full re-scan.\n\t\tconst startFromScratch = cached == null || stats.size < cached.nextOffset || stats.mtimeMs < cached.mtimeMs;\n\t\tconst startOffset = startFromScratch ? 0 : cached.nextOffset;\n\t\tconst baseTitles: TranscriptTitles = startFromScratch ? {} : { ...cached.titles };\n\n\t\tconst { titles, consumedEnd } =\n\t\t\tstats.size > startOffset\n\t\t\t\t? await this.readRange(path, startOffset, stats.size, sessionId, baseTitles)\n\t\t\t\t: { titles: baseTitles, consumedEnd: startOffset };\n\n\t\t// If `forget` ran or a newer `resolve` started while we were awaiting, skip the cache write\n\t\t// — the entry we'd be writing reflects stale state.\n\t\tif (this._generations.get(sessionId) !== gen) return titles;\n\n\t\tconst entry: CacheEntry = {\n\t\t\tpath: path,\n\t\t\tmtimeMs: stats.mtimeMs,\n\t\t\tsize: stats.size,\n\t\t\tnextOffset: consumedEnd,\n\t\t\ttitles: titles,\n\t\t};\n\t\tthis._cache.set(sessionId, entry);\n\t\treturn titles;\n\t}\n\n\tforget(sessionId: string): void {\n\t\tthis._cache.delete(sessionId);\n\t\tthis._generations.delete(sessionId);\n\t}\n\n\t/**\n\t * One-shot read for an *ended* (terminal, static) session: extracts the transcript titles plus\n\t * the first and last user prompts. Unlike {@link resolve}, it bypasses the tail cache — a finished\n\t * transcript never grows, so it's read once on demand (when an ended row is opened) and the\n\t * result applied to the session. `last-prompt` entries carry the prompt as of that point; the first\n\t * one bearing a value is the session's opening prompt, the last its most recent. Returns `undefined`\n\t * when no transcript is found (archived/purged) — the caller keeps whatever durable-store fields it\n\t * already has.\n\t *\n\t * Streams the file line-by-line rather than buffering it whole: a finished transcript can run to\n\t * tens of MB, but this keeps only one line resident at a time. A *full* scan (not a head/tail\n\t * window) is required for correctness — when the first turn is large the opening `last-prompt` sits\n\t * past any fixed head window, and a windowed read would then mistake a later prompt for the first,\n\t * poisoning the derived session name.\n\t */\n\tasync resolveEndedDetails(sessionId: string, cwd: string | undefined): Promise<EndedTranscriptDetails | undefined> {\n\t\tconst path = await this.locateTranscript(sessionId, cwd);\n\t\tif (path == null) return undefined;\n\n\t\tlet stats;\n\t\ttry {\n\t\t\tstats = await stat(path);\n\t\t} catch (ex) {\n\t\t\t// A transcript that vanished is terminal; an I/O or permission failure must propagate so\n\t\t\t// the caller retries rather than caching this session as permanently unresolvable.\n\t\t\tif (!isMissingEntry(ex)) throw ex;\n\n\t\t\treturn undefined;\n\t\t}\n\t\tif (stats.size === 0) return undefined;\n\n\t\tconst titles: TranscriptTitles = {};\n\t\tlet firstPrompt: string | undefined;\n\t\tlet lastPrompt: string | undefined;\n\n\t\tconst rl = createInterface({ input: createReadStream(path, { encoding: 'utf8' }), crlfDelay: Infinity });\n\t\ttry {\n\t\t\tfor await (const line of rl) {\n\t\t\t\tapplyTitleLine(line, sessionId, titles);\n\t\t\t\tif (!line.includes('last-prompt')) continue;\n\n\t\t\t\tconst entry = parseSummaryLine(line, sessionId);\n\t\t\t\tif (entry?.type === 'last-prompt' && entry.lastPrompt != null && entry.lastPrompt.length > 0) {\n\t\t\t\t\tfirstPrompt ??= entry.lastPrompt;\n\t\t\t\t\tlastPrompt = entry.lastPrompt;\n\t\t\t\t}\n\t\t\t}\n\t\t} finally {\n\t\t\trl.close();\n\t\t}\n\n\t\treturn { titles: titles, firstPrompt: firstPrompt, lastPrompt: lastPrompt };\n\t}\n\n\t/**\n\t * Lists the transcripts of sessions whose working directory is `cwd`, most-recently-active first,\n\t * summarizing until `limit` summarizable transcripts are found — not just the first `limit` on\n\t * disk, since junk transcripts (dropped below) would otherwise starve the result.\n\t * `excludeSessionIds` is skipped before `limit` applies, and excluded entries are also dropped\n\t * from `total` — they're already shown elsewhere (e.g. as live sessions), so a caller's \"N of M\"\n\t * count must not double-count them.\n\t *\n\t * Claude homes a transcript under the directory encoding the cwd from which the session started.\n\t * That directory is authoritative for where `claude --resume <id>` can find the transcript, but\n\t * Claude does not reliably move it when the session later changes worktrees. Callers that have a\n\t * durable session-to-worktree association can recover those moved sessions with\n\t * {@link listSessionsByIds}; this method intentionally remains the cheap legacy directory listing.\n\t *\n\t * Discovery (readdir + stat) covers the whole directory and is cheap; summarizing is not, so it's\n\t * capped. Entries whose summary yields neither a title nor a prompt are dropped — those are aborted\n\t * or empty transcripts with nothing to show or search on.\n\t */\n\tasync listSessions(\n\t\tcwd: string,\n\t\toptions?: { limit?: number; excludeSessionIds?: ReadonlySet<string> },\n\t): Promise<TranscriptSessionListing> {\n\t\tconst dir = await this.resolveProjectDir(cwd);\n\t\tif (dir == null) return { sessions: [], total: 0 };\n\n\t\tconst entries = await this.listEntries(dir);\n\t\tif (entries.length === 0) return { sessions: [], total: 0 };\n\n\t\tconst limit = options?.limit ?? defaultListLimit;\n\t\tconst exclude = options?.excludeSessionIds;\n\t\t// Zero-byte transcripts can never summarize — skip the read entirely and keep them out of\n\t\t// `total`, which otherwise counts sessions no `limit` could ever surface.\n\t\tconst candidates = entries.filter(e => e.size > 0 && exclude?.has(e.sessionId) !== true);\n\t\tconst { sessions, exhausted } = await this.collectSessions(candidates, limit, async entry => {\n\t\t\tconst summary = await this.resolveSummary(entry);\n\t\t\treturn summary != null && hasSummaryContent(summary) ? summary : undefined;\n\t\t});\n\n\t\t// An exhausted scan proved the exact valid count; otherwise unscanned candidates keep\n\t\t// `total` an upper bound so paging knows more MAY exist.\n\t\treturn { sessions: sessions, total: exhausted ? sessions.length : candidates.length };\n\t}\n\n\t/**\n\t * Finds a known set of session ids across every Claude project directory except `excludeCwd`'s.\n\t * This is the recovery path for durable CLI records whose worktree changed after Claude chose the\n\t * transcript's project directory. The caller supplies the association; transcript contents alone\n\t * are deliberately not used to guess which current worktree owns an arbitrary old session.\n\t *\n\t * Each result carries the project path encoded by the directory that actually holds the transcript,\n\t * so resuming it does not repeat discovery from the wrong worktree. Duplicate ids are counted once,\n\t * with the newest transcript winning.\n\t */\n\tasync listSessionsByIds(\n\t\tsessionIds: ReadonlySet<string>,\n\t\toptions?: { limit?: number; excludeCwd?: string },\n\t): Promise<ResumableTranscriptSessionListing> {\n\t\tif (sessionIds.size === 0) return { sessions: [], total: 0 };\n\n\t\tconst excludeDir = await this.resolveProjectDir(options?.excludeCwd);\n\t\t// Same zero-byte drop as `listSessions` — see the rationale there.\n\t\tconst entries = (await this.listEntriesByIds(sessionIds, excludeDir)).filter(e => e.size > 0);\n\t\tif (entries.length === 0) return { sessions: [], total: 0 };\n\n\t\tconst limit = options?.limit ?? defaultListLimit;\n\t\tconst { sessions, exhausted } = await this.collectSessions(entries, limit, async entry => {\n\t\t\tconst summary = await this.resolveSummary(entry);\n\t\t\tif (summary == null || !hasSummaryContent(summary)) return undefined;\n\n\t\t\tconst cwd = await this.resolveTranscriptCwd(entry);\n\t\t\treturn cwd != null ? { ...summary, cwd: cwd } : undefined;\n\t\t});\n\n\t\t// Exhausted scans prove the exact count — see `listSessions`.\n\t\treturn { sessions: sessions, total: exhausted ? sessions.length : entries.length };\n\t}\n\n\t/** Discovers every transcript in `dir`, newest first. The directory also holds one sibling\n\t * subdirectory per session (`<uuid>/subagents`, `<uuid>/tool-results`), so entries must be filtered\n\t * to files — a busy project has roughly as many subdirectories as transcripts. */\n\tprivate async listEntries(dir: string): Promise<TranscriptSessionEntry[]> {\n\t\tconst cached = this._listings.get(dir);\n\t\tif (cached != null && Date.now() - cached.resolvedAt < listingCacheTtlMs) return cached.entries;\n\n\t\tconst gen = this._listingGen;\n\t\tlet names: string[];\n\t\ttry {\n\t\t\tnames = await this.listTranscriptNames(dir);\n\t\t} catch {\n\t\t\treturn [];\n\t\t}\n\n\t\tconst entries = (await this.statEntries(dir, names)).sort((a, b) => b.lastActivityMs - a.lastActivityMs);\n\n\t\tif (gen === this._listingGen) {\n\t\t\tthis._listings.set(dir, { entries: entries, resolvedAt: Date.now() });\n\t\t}\n\n\t\treturn entries;\n\t}\n\n\t/** Stats `names` in `dir`, dropping the ones that failed — a transcript can vanish (or be\n\t * archived) between the readdir and the stat. Unsorted; callers order as they need.\n\t * Protected (like {@link readSummary}) so tests can interleave against the listing flow. */\n\tprotected async statEntries(dir: string, names: string[]): Promise<TranscriptSessionEntry[]> {\n\t\tconst settled = await Promise.allSettled(\n\t\t\tnames.map(async (name): Promise<TranscriptSessionEntry> => {\n\t\t\t\tconst path = join(dir, name);\n\t\t\t\tconst stats = await stat(path);\n\t\t\t\treturn {\n\t\t\t\t\tsessionId: basename(name, '.jsonl'),\n\t\t\t\t\tpath: path,\n\t\t\t\t\tlastActivityMs: stats.mtimeMs,\n\t\t\t\t\tsize: stats.size,\n\t\t\t\t};\n\t\t\t}),\n\t\t);\n\n\t\treturn settled.filter(r => r.status === 'fulfilled').map(r => r.value);\n\t}\n\n\t/** The `.jsonl` file names in `dir`, cached for {@link listingCacheTtlMs}. Failures propagate\n\t * uncached: an unreadable directory means different things to different callers, and the next\n\t * call must be free to get a real answer. */\n\tprivate async listTranscriptNames(dir: string): Promise<string[]> {\n\t\tconst cached = this._names.get(dir);\n\t\tif (cached != null && Date.now() - cached.resolvedAt < listingCacheTtlMs) return cached.names;\n\n\t\tconst gen = this._listingGen;\n\t\tconst dirents = await readdir(dir, { withFileTypes: true });\n\t\tconst names = dirents.filter(d => d.isFile() && d.name.endsWith('.jsonl')).map(d => d.name);\n\t\tif (gen === this._listingGen) {\n\t\t\tthis._names.set(dir, { names: names, resolvedAt: Date.now() });\n\t\t}\n\n\t\treturn names;\n\t}\n\n\t/** Every project directory under the projects root, cached for {@link listingCacheTtlMs} — a\n\t * by-id recovery sweeps all of them, once per queried worktree. */\n\tprivate async listProjectDirs(): Promise<string[]> {\n\t\tconst cached = this._projectDirs;\n\t\tif (cached != null && Date.now() - cached.resolvedAt < listingCacheTtlMs) return cached.dirs;\n\n\t\tconst gen = this._listingGen;\n\t\tconst root = this.getProjectsRoot();\n\t\tconst dirents = await readdir(root, { withFileTypes: true });\n\t\tconst dirs = dirents.filter(d => d.isDirectory()).map(d => join(root, d.name));\n\t\tif (gen === this._listingGen) {\n\t\t\tthis._projectDirs = { dirs: dirs, resolvedAt: Date.now() };\n\t\t}\n\n\t\treturn dirs;\n\t}\n\n\t/** Scans project directory names once, then stats only requested transcript ids. */\n\tprivate async listEntriesByIds(\n\t\tsessionIds: ReadonlySet<string>,\n\t\texcludeDir: string | undefined,\n\t): Promise<TranscriptSessionEntry[]> {\n\t\tlet idsToFind = sessionIds;\n\t\tif (excludeDir != null) {\n\t\t\ttry {\n\t\t\t\tconst names = await this.listTranscriptNames(excludeDir);\n\t\t\t\tconst idsInExcludedDir = new Set(names.map(name => basename(name, '.jsonl')));\n\t\t\t\tidsToFind = new Set([...sessionIds].filter(id => !idsInExcludedDir.has(id)));\n\t\t\t\tif (idsToFind.size === 0) return [];\n\t\t\t} catch {\n\t\t\t\t// If the exact directory can't be inspected, don't risk returning a duplicate from a\n\t\t\t\t// stale transcript copy elsewhere. The ordinary listing still owns the exact path.\n\t\t\t\treturn [];\n\t\t\t}\n\t\t}\n\n\t\tlet dirs: string[];\n\t\ttry {\n\t\t\tdirs = await this.listProjectDirs();\n\t\t} catch (ex) {\n\t\t\tif (!isMissingEntry(ex)) throw ex;\n\n\t\t\treturn [];\n\t\t}\n\n\t\tconst settledDirs = await Promise.allSettled(\n\t\t\tdirs.map(async dir => {\n\t\t\t\tif (dir === excludeDir) return [];\n\n\t\t\t\tconst names = (await this.listTranscriptNames(dir)).filter(name =>\n\t\t\t\t\tidsToFind.has(basename(name, '.jsonl')),\n\t\t\t\t);\n\t\t\t\treturn this.statEntries(dir, names);\n\t\t\t}),\n\t\t);\n\n\t\tconst bySessionId = new Map<string, TranscriptSessionEntry>();\n\t\tfor (const settledDir of settledDirs) {\n\t\t\tif (settledDir.status !== 'fulfilled') continue;\n\n\t\t\tfor (const entry of settledDir.value) {\n\t\t\t\tconst existing = bySessionId.get(entry.sessionId);\n\t\t\t\tif (existing == null || entry.lastActivityMs > existing.lastActivityMs) {\n\t\t\t\t\tbySessionId.set(entry.sessionId, entry);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn [...bySessionId.values()].sort((a, b) => b.lastActivityMs - a.lastActivityMs);\n\t}\n\n\t/** `exhausted` reports whether every candidate was scanned — an exhausted scan has proven the\n\t * exact valid count, so callers can report it instead of an upper bound that would keep a\n\t * \"Show More\" affordance alive with nothing left to show. */\n\tprivate async collectSessions<T>(\n\t\tcandidates: readonly TranscriptSessionEntry[],\n\t\tlimit: number,\n\t\tresolve: (entry: TranscriptSessionEntry) => Promise<T | undefined>,\n\t): Promise<{ sessions: T[]; exhausted: boolean }> {\n\t\tconst sessions: T[] = [];\n\t\tconst pushResolved = (settled: PromiseSettledResult<T | undefined>[]): void => {\n\t\t\tfor (const result of settled) {\n\t\t\t\tif (result.status !== 'fulfilled' || result.value == null) continue;\n\n\t\t\t\tsessions.push(result.value);\n\t\t\t}\n\t\t};\n\n\t\t// limit <= 0 means \"no ceiling\" — resolve every candidate.\n\t\tif (limit <= 0) {\n\t\t\tpushResolved(await Promise.allSettled(candidates.map(resolve)));\n\t\t\treturn { sessions: sessions, exhausted: true };\n\t\t}\n\n\t\t// Scans until `limit` results or the candidates run out — a fixed scan ceiling would let a run\n\t\t// of junk transcripts hide every older real session behind it (and the picker then claims there\n\t\t// are none at all). The cost stays bounded: callers pre-drop zero-byte candidates read-free, and\n\t\t// a junk summary is windowed, tiny, and LRU-cached, so the sweep is paid in syscalls, once.\n\t\tlet cursor = 0;\n\t\twhile (sessions.length < limit && cursor < candidates.length) {\n\t\t\t// Batched by remaining need so a hit-rich head stops the scan early instead of resolving\n\t\t\t// every candidate up front.\n\t\t\tconst batch = candidates.slice(cursor, cursor + (limit - sessions.length));\n\t\t\tcursor += batch.length;\n\t\t\tpushResolved(await Promise.allSettled(batch.map(resolve)));\n\t\t}\n\n\t\treturn { sessions: sessions, exhausted: cursor >= candidates.length };\n\t}\n\n\t/** Summarizes one transcript, reusing the cached result while its mtime and size are unchanged. */\n\tprivate async resolveSummary(entry: TranscriptSessionEntry): Promise<TranscriptSessionSummary | undefined> {\n\t\tconst cached = this._summaries.get(entry.sessionId);\n\t\tif (\n\t\t\tcached != null &&\n\t\t\tcached.path === entry.path &&\n\t\t\tcached.mtimeMs === entry.lastActivityMs &&\n\t\t\tcached.size === entry.size\n\t\t) {\n\t\t\t// Refresh recency for the LRU.\n\t\t\tthis._summaries.delete(entry.sessionId);\n\t\t\tthis._summaries.set(entry.sessionId, cached);\n\t\t\treturn cached.summary;\n\t\t}\n\n\t\tlet summary: TranscriptSessionSummary;\n\t\ttry {\n\t\t\tsummary = await this.readSummary(entry);\n\t\t} catch {\n\t\t\treturn undefined;\n\t\t}\n\n\t\tthis._summaries.set(entry.sessionId, {\n\t\t\tpath: entry.path,\n\t\t\tmtimeMs: entry.lastActivityMs,\n\t\t\tsize: entry.size,\n\t\t\tsummary: summary,\n\t\t});\n\t\tif (this._summaries.size > summaryCacheLimit) {\n\t\t\tconst oldest = this._summaries.keys().next();\n\t\t\tif (!oldest.done) {\n\t\t\t\tthis._summaries.delete(oldest.value);\n\t\t\t}\n\t\t}\n\t\treturn summary;\n\t}\n\n\t/** Recovers the project path represented by the transcript's containing directory. A resolved\n\t * answer is cached: it's a line-scan of the whole transcript, and the directory holding a given\n\t * path never changes. A miss isn't cached — a `cwd` line can still land on append. */\n\tprivate async resolveTranscriptCwd(entry: TranscriptSessionEntry): Promise<string | undefined> {\n\t\tconst cached = this._transcriptCwds.get(entry.path);\n\t\tif (cached != null) return cached;\n\n\t\tconst projectDir = dirname(entry.path);\n\t\tconst projectDirName = basename(projectDir);\n\t\tconst matchesProjectDir = (cwd: string): boolean =>\n\t\t\tencodeProjectDirName(cwd).toLowerCase() === projectDirName.toLowerCase();\n\n\t\ttry {\n\t\t\tconst index = JSON.parse(await readFile(join(projectDir, 'sessions-index.json'), 'utf8')) as {\n\t\t\t\tentries?: { sessionId?: unknown; projectPath?: unknown }[];\n\t\t\t};\n\t\t\tif (Array.isArray(index.entries)) {\n\t\t\t\tconst indexed = index.entries.find(item => item?.sessionId === entry.sessionId);\n\t\t\t\tif (typeof indexed?.projectPath === 'string' && matchesProjectDir(indexed.projectPath)) {\n\t\t\t\t\tthis.rememberTranscriptCwd(entry.path, indexed.projectPath);\n\t\t\t\t\treturn indexed.projectPath;\n\t\t\t\t}\n\t\t\t}\n\t\t} catch {\n\t\t\t// The index is optional and can lag or be malformed. The transcript is authoritative below.\n\t\t}\n\n\t\tconst rl = createInterface({ input: createReadStream(entry.path, { encoding: 'utf8' }), crlfDelay: Infinity });\n\t\ttry {\n\t\t\tfor await (const line of rl) {\n\t\t\t\tif (!line.includes('\"cwd\"')) continue;\n\n\t\t\t\tlet parsed: { cwd?: unknown; sessionId?: unknown };\n\t\t\t\ttry {\n\t\t\t\t\tparsed = JSON.parse(line) as { cwd?: unknown; sessionId?: unknown };\n\t\t\t\t} catch {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif (parsed.sessionId != null && parsed.sessionId !== entry.sessionId) continue;\n\n\t\t\t\tif (typeof parsed.cwd === 'string' && matchesProjectDir(parsed.cwd)) {\n\t\t\t\t\tthis.rememberTranscriptCwd(entry.path, parsed.cwd);\n\t\t\t\t\treturn parsed.cwd;\n\t\t\t\t}\n\t\t\t}\n\t\t} finally {\n\t\t\trl.close();\n\t\t}\n\n\t\treturn undefined;\n\t}\n\n\tprivate rememberTranscriptCwd(path: string, cwd: string): void {\n\t\tthis._transcriptCwds.set(path, cwd);\n\t\tif (this._transcriptCwds.size > summaryCacheLimit) {\n\t\t\tconst oldest = this._transcriptCwds.keys().next();\n\t\t\tif (!oldest.done) {\n\t\t\t\tthis._transcriptCwds.delete(oldest.value);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Reads a summary from the first and last {@link summaryWindowSize} bytes of the transcript.\n\t *\n\t * Deliberately does not reuse `resolve` — that scans from the last-read offset and buffers the whole\n\t * range, which for a cold multi-MB transcript means loading the entire file. Titles are written early\n\t * and re-written as they're refined, while prompts land at the tail, so the two windows recover both\n\t * at a fraction of the bytes. The tail is parsed second so the newest value wins.\n\t */\n\tprotected async readSummary(entry: TranscriptSessionEntry): Promise<TranscriptSessionSummary> {\n\t\tconst titles: TranscriptTitles = {};\n\t\tlet lastPrompt: string | undefined;\n\n\t\tconst apply = (buffer: Buffer, dropPartialFirstLine: boolean): void => {\n\t\t\tconst text = buffer.toString('utf8');\n\t\t\tconst lines = text.split('\\n');\n\t\t\t// A window starting mid-file almost always opens mid-line; that fragment can't be parsed.\n\t\t\tif (dropPartialFirstLine) {\n\t\t\t\tlines.shift();\n\t\t\t}\n\t\t\tfor (const line of lines) {\n\t\t\t\tconst parsed = parseSummaryLine(line, entry.sessionId);\n\t\t\t\tif (parsed == null) continue;\n\n\t\t\t\tapplyTitleEntry(parsed, titles);\n\t\t\t\tif (parsed.lastPrompt != null && parsed.lastPrompt.length > 0) {\n\t\t\t\t\tlastPrompt = parsed.lastPrompt;\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\n\t\tconst handle = await open(entry.path, 'r');\n\t\ttry {\n\t\t\tif (entry.size <= summaryWindowSize * 2) {\n\t\t\t\tconst whole = Buffer.alloc(entry.size);\n\t\t\t\tawait handle.read(whole, 0, entry.size, 0);\n\t\t\t\tapply(whole, false);\n\t\t\t} else {\n\t\t\t\tconst head = Buffer.alloc(summaryWindowSize);\n\t\t\t\tawait handle.read(head, 0, summaryWindowSize, 0);\n\t\t\t\tapply(head, false);\n\n\t\t\t\tconst tail = Buffer.alloc(summaryWindowSize);\n\t\t\t\tawait handle.read(tail, 0, summaryWindowSize, entry.size - summaryWindowSize);\n\t\t\t\tapply(tail, true);\n\t\t\t}\n\t\t} finally {\n\t\t\tawait handle.close();\n\t\t}\n\n\t\treturn {\n\t\t\t...entry,\n\t\t\ttitles: titles,\n\t\t\tlastPrompt: lastPrompt,\n\t\t};\n\t}\n\n\tprotected getProjectsRoot(): string {\n\t\treturn join(homedir(), '.claude', 'projects');\n\t}\n\n\t/** Resolves the `~/.claude/projects` directory holding the sessions whose working directory is\n\t * `cwd`, or `undefined` when none exists. Falls back to a case-insensitive name match because on\n\t * Windows our\n\t * paths carry a lower-cased drive letter (`normalizePath`) while Claude encodes the OS-native\n\t * `C:\\...` — so the exact name never matches by case. */\n\tprotected async resolveProjectDir(cwd: string | undefined): Promise<string | undefined> {\n\t\tif (cwd == null || cwd.length === 0) return undefined;\n\n\t\tconst root = this.getProjectsRoot();\n\t\tconst encoded = encodeProjectDirName(cwd);\n\n\t\tconst exact = join(root, encoded);\n\t\tif (await directoryExists(exact)) return exact;\n\n\t\tlet dirs: string[];\n\t\ttry {\n\t\t\tdirs = await readdir(root);\n\t\t} catch (ex) {\n\t\t\t// Only a missing projects root means \"no transcript\". A transient failure propagates so the\n\t\t\t// caller retries instead of caching a permanent miss.\n\t\t\tif (!isMissingEntry(ex)) throw ex;\n\n\t\t\treturn undefined;\n\t\t}\n\n\t\tconst lowered = encoded.toLowerCase();\n\t\tconst match = dirs.find(d => d !== encoded && d.toLowerCase() === lowered);\n\t\treturn match != null ? join(root, match) : undefined;\n\t}\n\n\tprotected async locateTranscript(sessionId: string, cwd: string | undefined): Promise<string | undefined> {\n\t\tconst fileName = `${sessionId}.jsonl`;\n\n\t\tconst dir = await this.resolveProjectDir(cwd);\n\t\tif (dir != null) {\n\t\t\tconst candidate = join(dir, fileName);\n\t\t\tif (await fileExists(candidate)) return candidate;\n\t\t}\n\n\t\t// The recorded cwd drifts whenever the agent `cd`s, so it can encode to a directory the session\n\t\t// doesn't actually live in — scan every project for the file as a last resort.\n\t\tconst root = this.getProjectsRoot();\n\t\tlet dirs: string[];\n\t\ttry {\n\t\t\tdirs = await readdir(root);\n\t\t} catch (ex) {\n\t\t\tif (!isMissingEntry(ex)) throw ex;\n\n\t\t\treturn undefined;\n\t\t}\n\n\t\tconst candidates = await Promise.all(\n\t\t\tdirs.map(async d => {\n\t\t\t\tconst candidate = join(root, d, fileName);\n\t\t\t\treturn (await fileExists(candidate)) ? candidate : undefined;\n\t\t\t}),\n\t\t);\n\t\treturn candidates.find(c => c != null);\n\t}\n\n\t/**\n\t * Reads bytes `[start, end)` from `path` and applies any title entries it finds to a copy of\n\t * `baseTitles`. Returns the merged titles and the byte position immediately after the last\n\t * newline observed — partial trailing lines (writer hasn't flushed `\\n` yet) are intentionally\n\t * left unconsumed so they're re-read on the next pass.\n\t */\n\tprotected async readRange(\n\t\tpath: string,\n\t\tstart: number,\n\t\tend: number,\n\t\tsessionId: string,\n\t\tbaseTitles: TranscriptTitles,\n\t): Promise<{ titles: TranscriptTitles; consumedEnd: number }> {\n\t\tconst buffer = await readSlice(path, start, end);\n\t\tconst titles: TranscriptTitles = { ...baseTitles };\n\n\t\tlet lastNewlineEnd = start;\n\t\tlet lineStart = 0;\n\t\tfor (let i = 0; i < buffer.length; i++) {\n\t\t\tif (buffer[i] !== 0x0a) continue;\n\n\t\t\t// Slice the line (trim a trailing \\r for CRLF files), then advance the cursor past the \\n\n\t\t\t// regardless of whether the line yielded anything — we've fully observed those bytes.\n\t\t\tconst lineEnd = i > lineStart && buffer[i - 1] === 0x0d ? i - 1 : i;\n\t\t\tif (lineEnd > lineStart) {\n\t\t\t\tconst line = buffer.toString('utf8', lineStart, lineEnd);\n\t\t\t\tapplyTitleLine(line, sessionId, titles);\n\t\t\t}\n\t\t\tlineStart = i + 1;\n\t\t\tlastNewlineEnd = start + i + 1;\n\t\t}\n\n\t\treturn { titles: titles, consumedEnd: lastNewlineEnd };\n\t}\n}\n\nfunction applyTitleLine(line: string, sessionId: string, titles: TranscriptTitles): void {\n\tif (!isLikelyTitleLine(line)) return;\n\n\tlet entry: TitleEntry;\n\ttry {\n\t\tentry = JSON.parse(line) as TitleEntry;\n\t} catch {\n\t\treturn;\n\t}\n\tif (entry.sessionId != null && entry.sessionId !== sessionId) return;\n\n\tapplyTitleEntry(entry, titles);\n}\n\n/** Parses one transcript line for {@link ClaudeCodeTranscriptReader.readSummary}, rejecting entries\n * that belong to another session. Can't use the `-title` prefilter that {@link isLikelyTitleLine}\n * applies: `last-prompt` matches neither of its markers. */\nfunction parseSummaryLine(line: string, sessionId: string): TitleEntry | undefined {\n\tif (line.length === 0 || !line.includes('\"type\"')) return undefined;\n\n\tlet entry: TitleEntry;\n\ttry {\n\t\tentry = JSON.parse(line) as TitleEntry;\n\t} catch {\n\t\treturn undefined;\n\t}\n\tif (entry.sessionId != null && entry.sessionId !== sessionId) return undefined;\n\n\treturn entry;\n}\n\nfunction applyTitleEntry(entry: TitleEntry, titles: TranscriptTitles): void {\n\tswitch (entry.type) {\n\t\tcase 'custom-title':\n\t\t\tif (typeof entry.customTitle === 'string' && entry.customTitle.length > 0) {\n\t\t\t\ttitles.custom = entry.customTitle;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 'ai-title':\n\t\t\tif (typeof entry.aiTitle === 'string' && entry.aiTitle.length > 0) {\n\t\t\t\ttitles.ai = entry.aiTitle;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 'agent-name':\n\t\t\tif (typeof entry.agentName === 'string' && entry.agentName.length > 0) {\n\t\t\t\ttitles.agent = entry.agentName;\n\t\t\t}\n\t\t\tbreak;\n\t}\n}\n\n/** Encodes a working directory into its `~/.claude/projects` directory name. Claude replaces every\n * non-alphanumeric character — not just separators — preserving runs (`/home/e/.claude` →\n * `-home-e--claude`) and case. Separators-only would leave the dot in our own worktree convention\n * (`<repo>.worktrees/<name>`) intact and compute a directory that never exists. */\nexport function encodeProjectDirName(cwd: string): string {\n\treturn cwd.replace(/[^a-zA-Z0-9]/g, '-');\n}\n\nfunction isLikelyTitleLine(line: string): boolean {\n\tif (!line.includes('\"type\"')) return false;\n\treturn line.includes('-title') || line.includes('agent-name');\n}\n\n/** A transcript with no title and no prompt is an aborted or empty session — nothing to name it by,\n * nothing to search it on. */\nfunction hasSummaryContent(summary: TranscriptSessionSummary): boolean {\n\tconst { custom, ai, agent } = summary.titles;\n\treturn custom != null || ai != null || agent != null || summary.lastPrompt != null;\n}\n\nasync function fileExists(path: string): Promise<boolean> {\n\ttry {\n\t\tconst stats = await stat(path);\n\t\treturn stats.isFile();\n\t} catch (ex) {\n\t\t// See {@link isMissingEntry}: only a genuine absence is an answer.\n\t\tif (!isMissingEntry(ex)) throw ex;\n\n\t\treturn false;\n\t}\n}\n\nasync function directoryExists(path: string): Promise<boolean> {\n\ttry {\n\t\tconst stats = await stat(path);\n\t\treturn stats.isDirectory();\n\t} catch (ex) {\n\t\t// \"Not there\" is an answer; anything else (EACCES, EIO, a mount hiccup) is a transient\n\t\t// failure the caller must be able to tell apart — see {@link isMissingEntry}.\n\t\tif (isMissingEntry(ex)) return false;\n\n\t\tthrow ex;\n\t}\n}\n\n/** True for the \"path simply isn't there\" errno codes, as opposed to a transient I/O or permission\n * failure. Callers cache a genuine absence as terminal, so a transient failure must not look like\n * one — it would pin a session to its fallback name until the window reloads. */\nfunction isMissingEntry(ex: unknown): boolean {\n\tconst code = (ex as NodeJS.ErrnoException | undefined)?.code;\n\treturn code === 'ENOENT' || code === 'ENOTDIR';\n}\n\nfunction readSlice(path: string, start: number, end: number): Promise<Buffer> {\n\treturn new Promise((resolve, reject) => {\n\t\tconst chunks: Buffer[] = [];\n\t\tconst stream = createReadStream(path, { start: start, end: end - 1 });\n\t\tstream.on('data', chunk => chunks.push(chunk as Buffer));\n\t\tstream.on('end', () => resolve(Buffer.concat(chunks)));\n\t\tstream.on('error', reject);\n\t});\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"claudeCodeTranscript.js","names":["readdir"],"sources":["../../../../../plus/agents/src/providers/claudeCodeTranscript.ts"],"sourcesContent":["import { createReadStream } from 'fs';\nimport { open, readdir, readFile, stat } from 'fs/promises';\nimport { homedir } from 'os';\nimport { basename, dirname, join } from 'path';\nimport { createInterface } from 'readline';\n\nexport interface TranscriptTitles {\n\tcustom?: string;\n\tai?: string;\n\tagent?: string;\n}\n\n/** A transcript file discovered by {@link ClaudeCodeTranscriptReader.listSessions}, before its\n * contents are read — everything here comes from `readdir` + `stat`. */\nexport interface TranscriptSessionEntry {\n\treadonly sessionId: string;\n\treadonly path: string;\n\treadonly lastActivityMs: number;\n\treadonly size: number;\n}\n\n/** A {@link TranscriptSessionEntry} enriched with the fields recovered from the file's head/tail. */\nexport interface TranscriptSessionSummary extends TranscriptSessionEntry {\n\treadonly titles: TranscriptTitles;\n\treadonly lastPrompt?: string;\n}\n\nexport interface TranscriptSessionListing {\n\treadonly sessions: TranscriptSessionSummary[];\n\t/** Every transcript in the directory, not just the summarized slice — drives \"Showing N of M\". */\n\treadonly total: number;\n}\n\n/** A transcript found outside the queried cwd's project directory, plus the directory Claude\n * actually uses to resolve it for `--resume`. */\nexport interface ResumableTranscriptSessionSummary extends TranscriptSessionSummary {\n\treadonly cwd: string;\n}\n\nexport interface ResumableTranscriptSessionListing {\n\treadonly sessions: ResumableTranscriptSessionSummary[];\n\treadonly total: number;\n}\n\nexport interface EndedTranscriptDetails {\n\ttitles: TranscriptTitles;\n\tfirstPrompt?: string;\n\tlastPrompt?: string;\n}\n\ninterface CacheEntry {\n\tpath: string;\n\tmtimeMs: number;\n\tsize: number;\n\tnextOffset: number;\n\ttitles: TranscriptTitles;\n}\n\ninterface ListingCacheEntry {\n\tentries: TranscriptSessionEntry[];\n\tresolvedAt: number;\n}\n\ninterface NameCacheEntry {\n\tnames: string[];\n\tresolvedAt: number;\n}\n\ninterface SummaryCacheEntry {\n\tpath: string;\n\tmtimeMs: number;\n\tsize: number;\n\tsummary: TranscriptSessionSummary;\n}\n\ninterface TitleEntry {\n\ttype: string;\n\tsessionId?: string;\n\tcustomTitle?: string;\n\taiTitle?: string;\n\tagentName?: string;\n\tlastPrompt?: string;\n}\n\n/** Bytes read from each end of a transcript when summarizing. Titles and prompts cluster at both\n * extremes, and files run to tens of MB, so reading whole files is not an option. */\nconst summaryWindowSize = 64 * 1024;\n/** Listings are cheap (readdir + stat) but re-run per panel/sheet open; a short TTL absorbs bursts.\n * Time-based rather than dir-mtime-based because appends move file mtimes without touching the dir. */\nconst listingCacheTtlMs = 10 * 1000;\n/** Summaries are keyed by session, and a busy project has hundreds — unlike the per-live-session\n * title cache, this needs a ceiling. Sized to hold a whole junk-heavy store: the listing scan runs\n * to exhaustion, and each entry is tiny (a path plus a few short strings), so a cap below the\n * store's size would evict-and-re-read the junk tail on every listing instead of paying its\n * windowed reads once. */\nconst summaryCacheLimit = 1000;\nconst defaultListLimit = 50;\n\n/**\n * Reads Claude Code transcript JSONL files at `~/.claude/projects/<encoded-cwd>/<sessionId>.jsonl`\n * to surface `custom-title`, `ai-title`, and `agent-name` entries as fallback session names.\n *\n * Maintains a per-session cache keyed by mtime + last-read byte offset, so repeated calls only\n * read the appended tail. Last occurrence per type wins.\n *\n * `resolve` is async, so a `forget` (or a newer `resolve`) for the same session can land between\n * its `await`s. A per-session generation counter is stamped at entry and re-checked before the\n * final cache write, so a stale `resolve` can't resurrect a forgotten entry or clobber a newer\n * resolve's result.\n */\nexport class ClaudeCodeTranscriptReader {\n\tprivate readonly _cache = new Map<string, CacheEntry>();\n\tprivate readonly _generations = new Map<string, number>();\n\tprivate readonly _listings = new Map<string, ListingCacheEntry>();\n\t/** Transcript file names per project directory — the readdir behind both the plain listing and\n\t * the by-id scan, which sweeps every project directory. */\n\tprivate readonly _names = new Map<string, NameCacheEntry>();\n\t/** Project directories under {@link getProjectsRoot}, re-scanned by every by-id recovery. */\n\tprivate _projectDirs: { dirs: string[]; resolvedAt: number } | undefined;\n\t/** Insertion-ordered LRU — re-inserted on hit, oldest key evicted past {@link summaryCacheLimit}. */\n\tprivate readonly _summaries = new Map<string, SummaryCacheEntry>();\n\t/** Resolved project path per transcript path, bounded like {@link _summaries}. */\n\tprivate readonly _transcriptCwds = new Map<string, string>();\n\tprivate _nextGen = 0;\n\t/** Bumped by {@link invalidateListings}; async listing scans capture it at entry and skip their\n\t * cache write when it moved, so a stale in-flight result can't re-seed a just-cleared cache. */\n\tprivate _listingGen = 0;\n\n\t/** Drops the directory-listing caches (entries, names, project dirs) so the next listing re-reads\n\t * the store. Called when a session ends or is removed: its transcript may have appeared inside\n\t * the TTL window, and a cached listing would hide the new file from the very fetch meant to\n\t * surface it. Summary and cwd caches are keyed by file identity and stay valid, so they're kept.\n\t * The generation bump makes any in-flight listing skip its own cache write — otherwise a scan\n\t * started before the invalidation would re-seed the caches with pre-end results, freshly\n\t * timestamped (same discipline as `resolve`'s per-session generations). */\n\tinvalidateListings(): void {\n\t\tthis._listingGen++;\n\t\tthis._listings.clear();\n\t\tthis._names.clear();\n\t\tthis._projectDirs = undefined;\n\t}\n\n\tasync resolve(sessionId: string, cwd: string | undefined): Promise<TranscriptTitles | undefined> {\n\t\tconst gen = ++this._nextGen;\n\t\tthis._generations.set(sessionId, gen);\n\n\t\tconst cached = this._cache.get(sessionId);\n\t\tconst path = cached?.path ?? (await this.locateTranscript(sessionId, cwd));\n\t\tif (path == null) return undefined;\n\n\t\tlet stats;\n\t\ttry {\n\t\t\tstats = await stat(path);\n\t\t} catch {\n\t\t\t// File disappeared; drop any stale cache entry so a future call retries discovery.\n\t\t\t// Skip the delete if a newer resolve/forget claimed the slot — their state is fresher.\n\t\t\tif (this._generations.get(sessionId) === gen) {\n\t\t\t\tthis._cache.delete(sessionId);\n\t\t\t\tthis._generations.delete(sessionId);\n\t\t\t}\n\t\t\treturn undefined;\n\t\t}\n\n\t\tif (stats.mtimeMs === cached?.mtimeMs && stats.size === cached?.size) {\n\t\t\treturn cached.titles;\n\t\t}\n\n\t\t// Truncation or rewrite: stat shrank below our read cursor or mtime moved backward → full re-scan.\n\t\tconst startFromScratch = cached == null || stats.size < cached.nextOffset || stats.mtimeMs < cached.mtimeMs;\n\t\tconst startOffset = startFromScratch ? 0 : cached.nextOffset;\n\t\tconst baseTitles: TranscriptTitles = startFromScratch ? {} : { ...cached.titles };\n\n\t\tconst { titles, consumedEnd } =\n\t\t\tstats.size > startOffset\n\t\t\t\t? await this.readRange(path, startOffset, stats.size, sessionId, baseTitles)\n\t\t\t\t: { titles: baseTitles, consumedEnd: startOffset };\n\n\t\t// If `forget` ran or a newer `resolve` started while we were awaiting, skip the cache write\n\t\t// — the entry we'd be writing reflects stale state.\n\t\tif (this._generations.get(sessionId) !== gen) return titles;\n\n\t\tconst entry: CacheEntry = {\n\t\t\tpath: path,\n\t\t\tmtimeMs: stats.mtimeMs,\n\t\t\tsize: stats.size,\n\t\t\tnextOffset: consumedEnd,\n\t\t\ttitles: titles,\n\t\t};\n\t\tthis._cache.set(sessionId, entry);\n\t\treturn titles;\n\t}\n\n\tforget(sessionId: string): void {\n\t\tthis._cache.delete(sessionId);\n\t\tthis._generations.delete(sessionId);\n\t}\n\n\t/**\n\t * One-shot read for an *ended* (terminal, static) session: extracts the transcript titles plus\n\t * the first and last user prompts. Unlike {@link resolve}, it bypasses the tail cache — a finished\n\t * transcript never grows, so it's read once on demand (when an ended row is opened) and the\n\t * result applied to the session. `last-prompt` entries carry the prompt as of that point; the first\n\t * one bearing a value is the session's opening prompt, the last its most recent. Returns `undefined`\n\t * when no transcript is found (archived/purged) — the caller keeps whatever durable-store fields it\n\t * already has.\n\t *\n\t * Streams the file line-by-line rather than buffering it whole: a finished transcript can run to\n\t * tens of MB, but this keeps only one line resident at a time. A *full* scan (not a head/tail\n\t * window) is required for correctness — when the first turn is large the opening `last-prompt` sits\n\t * past any fixed head window, and a windowed read would then mistake a later prompt for the first,\n\t * poisoning the derived session name.\n\t */\n\tasync resolveEndedDetails(sessionId: string, cwd: string | undefined): Promise<EndedTranscriptDetails | undefined> {\n\t\tconst path = await this.locateTranscript(sessionId, cwd);\n\t\tif (path == null) return undefined;\n\n\t\tlet stats;\n\t\ttry {\n\t\t\tstats = await stat(path);\n\t\t} catch (ex) {\n\t\t\t// A transcript that vanished is terminal; an I/O or permission failure must propagate so\n\t\t\t// the caller retries rather than caching this session as permanently unresolvable.\n\t\t\tif (!isMissingEntry(ex)) throw ex;\n\n\t\t\treturn undefined;\n\t\t}\n\t\tif (stats.size === 0) return undefined;\n\n\t\tconst titles: TranscriptTitles = {};\n\t\tlet firstPrompt: string | undefined;\n\t\tlet lastPrompt: string | undefined;\n\n\t\tconst rl = createInterface({ input: createReadStream(path, { encoding: 'utf8' }), crlfDelay: Infinity });\n\t\ttry {\n\t\t\tfor await (const line of rl) {\n\t\t\t\tapplyTitleLine(line, sessionId, titles);\n\t\t\t\tif (!line.includes('last-prompt')) continue;\n\n\t\t\t\tconst entry = parseSummaryLine(line, sessionId);\n\t\t\t\tif (entry?.type === 'last-prompt' && entry.lastPrompt != null && entry.lastPrompt.length > 0) {\n\t\t\t\t\tfirstPrompt ??= entry.lastPrompt;\n\t\t\t\t\tlastPrompt = entry.lastPrompt;\n\t\t\t\t}\n\t\t\t}\n\t\t} finally {\n\t\t\trl.close();\n\t\t}\n\n\t\treturn { titles: titles, firstPrompt: firstPrompt, lastPrompt: lastPrompt };\n\t}\n\n\t/**\n\t * Lists the transcripts of sessions whose working directory is `cwd`, most-recently-active first,\n\t * summarizing until `limit` summarizable transcripts are found — not just the first `limit` on\n\t * disk, since junk transcripts (dropped below) would otherwise starve the result.\n\t * `excludeSessionIds` is skipped before `limit` applies, and excluded entries are also dropped\n\t * from `total` — they're already shown elsewhere (e.g. as live sessions), so a caller's \"N of M\"\n\t * count must not double-count them.\n\t *\n\t * Claude homes a transcript under the directory encoding the cwd from which the session started.\n\t * That directory is authoritative for where `claude --resume <id>` can find the transcript, but\n\t * Claude does not reliably move it when the session later changes worktrees. Callers that have a\n\t * durable session-to-worktree association can recover those moved sessions with\n\t * {@link listSessionsByIds}; this method intentionally remains the cheap legacy directory listing.\n\t *\n\t * Discovery (readdir + stat) covers the whole directory and is cheap; summarizing is not, so it's\n\t * capped. Entries whose summary yields neither a title nor a prompt are dropped — those are aborted\n\t * or empty transcripts with nothing to show or search on.\n\t */\n\tasync listSessions(\n\t\tcwd: string,\n\t\toptions?: { limit?: number; excludeSessionIds?: ReadonlySet<string> },\n\t): Promise<TranscriptSessionListing> {\n\t\tconst dir = await this.resolveProjectDir(cwd);\n\t\tif (dir == null) return { sessions: [], total: 0 };\n\n\t\tconst entries = await this.listEntries(dir);\n\t\tif (entries.length === 0) return { sessions: [], total: 0 };\n\n\t\tconst limit = options?.limit ?? defaultListLimit;\n\t\tconst exclude = options?.excludeSessionIds;\n\t\t// Zero-byte transcripts can never summarize — skip the read entirely and keep them out of\n\t\t// `total`, which otherwise counts sessions no `limit` could ever surface.\n\t\tconst candidates = entries.filter(e => e.size > 0 && exclude?.has(e.sessionId) !== true);\n\t\tconst { sessions, exhausted } = await this.collectSessions(candidates, limit, async entry => {\n\t\t\tconst summary = await this.resolveSummary(entry);\n\t\t\treturn summary != null && hasSummaryContent(summary) ? summary : undefined;\n\t\t});\n\n\t\t// An exhausted scan proved the exact valid count; otherwise unscanned candidates keep\n\t\t// `total` an upper bound so paging knows more MAY exist.\n\t\treturn { sessions: sessions, total: exhausted ? sessions.length : candidates.length };\n\t}\n\n\t/**\n\t * Finds a known set of session ids across every Claude project directory except `excludeCwd`'s.\n\t * This is the recovery path for durable CLI records whose worktree changed after Claude chose the\n\t * transcript's project directory. The caller supplies the association; transcript contents alone\n\t * are deliberately not used to guess which current worktree owns an arbitrary old session.\n\t *\n\t * Each result carries the project path encoded by the directory that actually holds the transcript,\n\t * so resuming it does not repeat discovery from the wrong worktree. Duplicate ids are counted once,\n\t * with the newest transcript winning.\n\t */\n\tasync listSessionsByIds(\n\t\tsessionIds: ReadonlySet<string>,\n\t\toptions?: { limit?: number; excludeCwd?: string },\n\t): Promise<ResumableTranscriptSessionListing> {\n\t\tif (sessionIds.size === 0) return { sessions: [], total: 0 };\n\n\t\tconst excludeDir = await this.resolveProjectDir(options?.excludeCwd);\n\t\t// Same zero-byte drop as `listSessions` — see the rationale there.\n\t\tconst entries = (await this.listEntriesByIds(sessionIds, excludeDir)).filter(e => e.size > 0);\n\t\tif (entries.length === 0) return { sessions: [], total: 0 };\n\n\t\tconst limit = options?.limit ?? defaultListLimit;\n\t\tconst { sessions, exhausted } = await this.collectSessions(entries, limit, async entry => {\n\t\t\tconst summary = await this.resolveSummary(entry);\n\t\t\tif (summary == null || !hasSummaryContent(summary)) return undefined;\n\n\t\t\tconst cwd = await this.resolveTranscriptCwd(entry);\n\t\t\treturn cwd != null ? { ...summary, cwd: cwd } : undefined;\n\t\t});\n\n\t\t// Exhausted scans prove the exact count — see `listSessions`.\n\t\treturn { sessions: sessions, total: exhausted ? sessions.length : entries.length };\n\t}\n\n\t/** Discovers every transcript in `dir`, newest first. The directory also holds one sibling\n\t * subdirectory per session (`<uuid>/subagents`, `<uuid>/tool-results`), so entries must be filtered\n\t * to files — a busy project has roughly as many subdirectories as transcripts. */\n\tprivate async listEntries(dir: string): Promise<TranscriptSessionEntry[]> {\n\t\tconst cached = this._listings.get(dir);\n\t\tif (cached != null && Date.now() - cached.resolvedAt < listingCacheTtlMs) return cached.entries;\n\n\t\tconst gen = this._listingGen;\n\t\tlet names: string[];\n\t\ttry {\n\t\t\tnames = await this.listTranscriptNames(dir);\n\t\t} catch {\n\t\t\treturn [];\n\t\t}\n\n\t\tconst entries = (await this.statEntries(dir, names)).sort((a, b) => b.lastActivityMs - a.lastActivityMs);\n\n\t\tif (gen === this._listingGen) {\n\t\t\tthis._listings.set(dir, { entries: entries, resolvedAt: Date.now() });\n\t\t}\n\n\t\treturn entries;\n\t}\n\n\t/** Stats `names` in `dir`, dropping the ones that failed — a transcript can vanish (or be\n\t * archived) between the readdir and the stat. Unsorted; callers order as they need.\n\t * Protected (like {@link readSummary}) so tests can interleave against the listing flow. */\n\tprotected async statEntries(dir: string, names: string[]): Promise<TranscriptSessionEntry[]> {\n\t\tconst settled = await Promise.allSettled(\n\t\t\tnames.map(async (name): Promise<TranscriptSessionEntry> => {\n\t\t\t\tconst path = join(dir, name);\n\t\t\t\tconst stats = await stat(path);\n\t\t\t\treturn {\n\t\t\t\t\tsessionId: basename(name, '.jsonl'),\n\t\t\t\t\tpath: path,\n\t\t\t\t\tlastActivityMs: stats.mtimeMs,\n\t\t\t\t\tsize: stats.size,\n\t\t\t\t};\n\t\t\t}),\n\t\t);\n\n\t\treturn settled.filter(r => r.status === 'fulfilled').map(r => r.value);\n\t}\n\n\t/** The `.jsonl` file names in `dir`, cached for {@link listingCacheTtlMs}. Failures propagate\n\t * uncached: an unreadable directory means different things to different callers, and the next\n\t * call must be free to get a real answer. */\n\tprivate async listTranscriptNames(dir: string): Promise<string[]> {\n\t\tconst cached = this._names.get(dir);\n\t\tif (cached != null && Date.now() - cached.resolvedAt < listingCacheTtlMs) return cached.names;\n\n\t\tconst gen = this._listingGen;\n\t\tconst dirents = await readdir(dir, { withFileTypes: true });\n\t\tconst names = dirents.filter(d => d.isFile() && d.name.endsWith('.jsonl')).map(d => d.name);\n\t\tif (gen === this._listingGen) {\n\t\t\tthis._names.set(dir, { names: names, resolvedAt: Date.now() });\n\t\t}\n\n\t\treturn names;\n\t}\n\n\t/** Every project directory under the projects root, cached for {@link listingCacheTtlMs} — a\n\t * by-id recovery sweeps all of them, once per queried worktree. */\n\tprivate async listProjectDirs(): Promise<string[]> {\n\t\tconst cached = this._projectDirs;\n\t\tif (cached != null && Date.now() - cached.resolvedAt < listingCacheTtlMs) return cached.dirs;\n\n\t\tconst gen = this._listingGen;\n\t\tconst root = this.getProjectsRoot();\n\t\tconst dirents = await readdir(root, { withFileTypes: true });\n\t\tconst dirs = dirents.filter(d => d.isDirectory()).map(d => join(root, d.name));\n\t\tif (gen === this._listingGen) {\n\t\t\tthis._projectDirs = { dirs: dirs, resolvedAt: Date.now() };\n\t\t}\n\n\t\treturn dirs;\n\t}\n\n\t/** Scans project directory names once, then stats only requested transcript ids. */\n\tprivate async listEntriesByIds(\n\t\tsessionIds: ReadonlySet<string>,\n\t\texcludeDir: string | undefined,\n\t): Promise<TranscriptSessionEntry[]> {\n\t\tlet idsToFind = sessionIds;\n\t\tif (excludeDir != null) {\n\t\t\ttry {\n\t\t\t\tconst names = await this.listTranscriptNames(excludeDir);\n\t\t\t\tconst idsInExcludedDir = new Set(names.map(name => basename(name, '.jsonl')));\n\t\t\t\tidsToFind = new Set([...sessionIds].filter(id => !idsInExcludedDir.has(id)));\n\t\t\t\tif (idsToFind.size === 0) return [];\n\t\t\t} catch {\n\t\t\t\t// If the exact directory can't be inspected, don't risk returning a duplicate from a\n\t\t\t\t// stale transcript copy elsewhere. The ordinary listing still owns the exact path.\n\t\t\t\treturn [];\n\t\t\t}\n\t\t}\n\n\t\tlet dirs: string[];\n\t\ttry {\n\t\t\tdirs = await this.listProjectDirs();\n\t\t} catch (ex) {\n\t\t\tif (!isMissingEntry(ex)) throw ex;\n\n\t\t\treturn [];\n\t\t}\n\n\t\tconst settledDirs = await Promise.allSettled(\n\t\t\tdirs.map(async dir => {\n\t\t\t\tif (dir === excludeDir) return [];\n\n\t\t\t\tconst names = (await this.listTranscriptNames(dir)).filter(name =>\n\t\t\t\t\tidsToFind.has(basename(name, '.jsonl')),\n\t\t\t\t);\n\t\t\t\treturn this.statEntries(dir, names);\n\t\t\t}),\n\t\t);\n\n\t\tconst bySessionId = new Map<string, TranscriptSessionEntry>();\n\t\tfor (const settledDir of settledDirs) {\n\t\t\tif (settledDir.status !== 'fulfilled') continue;\n\n\t\t\tfor (const entry of settledDir.value) {\n\t\t\t\tconst existing = bySessionId.get(entry.sessionId);\n\t\t\t\tif (existing == null || entry.lastActivityMs > existing.lastActivityMs) {\n\t\t\t\t\tbySessionId.set(entry.sessionId, entry);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn [...bySessionId.values()].sort((a, b) => b.lastActivityMs - a.lastActivityMs);\n\t}\n\n\t/** `exhausted` reports whether every candidate was scanned — an exhausted scan has proven the\n\t * exact valid count, so callers can report it instead of an upper bound that would keep a\n\t * \"Show More\" affordance alive with nothing left to show. */\n\tprivate async collectSessions<T>(\n\t\tcandidates: readonly TranscriptSessionEntry[],\n\t\tlimit: number,\n\t\tresolve: (entry: TranscriptSessionEntry) => Promise<T | undefined>,\n\t): Promise<{ sessions: T[]; exhausted: boolean }> {\n\t\tconst sessions: T[] = [];\n\t\tconst pushResolved = (settled: PromiseSettledResult<T | undefined>[]): void => {\n\t\t\tfor (const result of settled) {\n\t\t\t\tif (result.status !== 'fulfilled' || result.value == null) continue;\n\n\t\t\t\tsessions.push(result.value);\n\t\t\t}\n\t\t};\n\n\t\t// limit <= 0 means \"no ceiling\" — resolve every candidate.\n\t\tif (limit <= 0) {\n\t\t\tpushResolved(await Promise.allSettled(candidates.map(resolve)));\n\t\t\treturn { sessions: sessions, exhausted: true };\n\t\t}\n\n\t\t// Scans until `limit` results or the candidates run out — a fixed scan ceiling would let a run\n\t\t// of junk transcripts hide every older real session behind it (and the picker then claims there\n\t\t// are none at all). The cost stays bounded: callers pre-drop zero-byte candidates read-free, and\n\t\t// a junk summary is windowed, tiny, and LRU-cached, so the sweep is paid in syscalls, once.\n\t\tlet cursor = 0;\n\t\twhile (sessions.length < limit && cursor < candidates.length) {\n\t\t\t// Batched by remaining need so a hit-rich head stops the scan early instead of resolving\n\t\t\t// every candidate up front.\n\t\t\tconst batch = candidates.slice(cursor, cursor + (limit - sessions.length));\n\t\t\tcursor += batch.length;\n\t\t\tpushResolved(await Promise.allSettled(batch.map(resolve)));\n\t\t}\n\n\t\treturn { sessions: sessions, exhausted: cursor >= candidates.length };\n\t}\n\n\t/** Summarizes one transcript, reusing the cached result while its mtime and size are unchanged. */\n\tprivate async resolveSummary(entry: TranscriptSessionEntry): Promise<TranscriptSessionSummary | undefined> {\n\t\tconst cached = this._summaries.get(entry.sessionId);\n\t\tif (\n\t\t\tcached != null &&\n\t\t\tcached.path === entry.path &&\n\t\t\tcached.mtimeMs === entry.lastActivityMs &&\n\t\t\tcached.size === entry.size\n\t\t) {\n\t\t\t// Refresh recency for the LRU.\n\t\t\tthis._summaries.delete(entry.sessionId);\n\t\t\tthis._summaries.set(entry.sessionId, cached);\n\t\t\treturn cached.summary;\n\t\t}\n\n\t\tlet summary: TranscriptSessionSummary;\n\t\ttry {\n\t\t\tsummary = await this.readSummary(entry);\n\t\t} catch {\n\t\t\treturn undefined;\n\t\t}\n\n\t\tthis._summaries.set(entry.sessionId, {\n\t\t\tpath: entry.path,\n\t\t\tmtimeMs: entry.lastActivityMs,\n\t\t\tsize: entry.size,\n\t\t\tsummary: summary,\n\t\t});\n\t\tif (this._summaries.size > summaryCacheLimit) {\n\t\t\tconst oldest = this._summaries.keys().next();\n\t\t\tif (!oldest.done) {\n\t\t\t\tthis._summaries.delete(oldest.value);\n\t\t\t}\n\t\t}\n\t\treturn summary;\n\t}\n\n\t/** Recovers the project path represented by the transcript's containing directory. A resolved\n\t * answer is cached: it's a line-scan of the whole transcript, and the directory holding a given\n\t * path never changes. A miss isn't cached — a `cwd` line can still land on append. */\n\tprivate async resolveTranscriptCwd(entry: TranscriptSessionEntry): Promise<string | undefined> {\n\t\tconst cached = this._transcriptCwds.get(entry.path);\n\t\tif (cached != null) return cached;\n\n\t\tconst projectDir = dirname(entry.path);\n\t\tconst projectDirName = basename(projectDir);\n\t\tconst matchesProjectDir = (cwd: string): boolean =>\n\t\t\tencodeProjectDirName(cwd).toLowerCase() === projectDirName.toLowerCase();\n\n\t\ttry {\n\t\t\tconst index = JSON.parse(await readFile(join(projectDir, 'sessions-index.json'), 'utf8')) as {\n\t\t\t\tentries?: { sessionId?: unknown; projectPath?: unknown }[];\n\t\t\t};\n\t\t\tif (Array.isArray(index.entries)) {\n\t\t\t\tconst indexed = index.entries.find(item => item?.sessionId === entry.sessionId);\n\t\t\t\tif (typeof indexed?.projectPath === 'string' && matchesProjectDir(indexed.projectPath)) {\n\t\t\t\t\tthis.rememberTranscriptCwd(entry.path, indexed.projectPath);\n\t\t\t\t\treturn indexed.projectPath;\n\t\t\t\t}\n\t\t\t}\n\t\t} catch {\n\t\t\t// The index is optional and can lag or be malformed. The transcript is authoritative below.\n\t\t}\n\n\t\tconst rl = createInterface({ input: createReadStream(entry.path, { encoding: 'utf8' }), crlfDelay: Infinity });\n\t\ttry {\n\t\t\tfor await (const line of rl) {\n\t\t\t\tif (!line.includes('\"cwd\"')) continue;\n\n\t\t\t\tlet parsed: { cwd?: unknown; sessionId?: unknown };\n\t\t\t\ttry {\n\t\t\t\t\tparsed = JSON.parse(line) as { cwd?: unknown; sessionId?: unknown };\n\t\t\t\t} catch {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif (parsed.sessionId != null && parsed.sessionId !== entry.sessionId) continue;\n\n\t\t\t\tif (typeof parsed.cwd === 'string' && matchesProjectDir(parsed.cwd)) {\n\t\t\t\t\tthis.rememberTranscriptCwd(entry.path, parsed.cwd);\n\t\t\t\t\treturn parsed.cwd;\n\t\t\t\t}\n\t\t\t}\n\t\t} finally {\n\t\t\trl.close();\n\t\t}\n\n\t\treturn undefined;\n\t}\n\n\tprivate rememberTranscriptCwd(path: string, cwd: string): void {\n\t\tthis._transcriptCwds.set(path, cwd);\n\t\tif (this._transcriptCwds.size > summaryCacheLimit) {\n\t\t\tconst oldest = this._transcriptCwds.keys().next();\n\t\t\tif (!oldest.done) {\n\t\t\t\tthis._transcriptCwds.delete(oldest.value);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Reads a summary from the first and last {@link summaryWindowSize} bytes of the transcript.\n\t *\n\t * Deliberately does not reuse `resolve` — that scans from the last-read offset and buffers the whole\n\t * range, which for a cold multi-MB transcript means loading the entire file. Titles are written early\n\t * and re-written as they're refined, while prompts land at the tail, so the two windows recover both\n\t * at a fraction of the bytes. The tail is parsed second so the newest value wins.\n\t */\n\tprotected async readSummary(entry: TranscriptSessionEntry): Promise<TranscriptSessionSummary> {\n\t\tconst titles: TranscriptTitles = {};\n\t\tlet lastPrompt: string | undefined;\n\n\t\tconst apply = (buffer: Buffer, dropPartialFirstLine: boolean): void => {\n\t\t\tconst text = buffer.toString('utf8');\n\t\t\tconst lines = text.split('\\n');\n\t\t\t// A window starting mid-file almost always opens mid-line; that fragment can't be parsed.\n\t\t\tif (dropPartialFirstLine) {\n\t\t\t\tlines.shift();\n\t\t\t}\n\t\t\tfor (const line of lines) {\n\t\t\t\tconst parsed = parseSummaryLine(line, entry.sessionId);\n\t\t\t\tif (parsed == null) continue;\n\n\t\t\t\tapplyTitleEntry(parsed, titles);\n\t\t\t\tif (parsed.lastPrompt != null && parsed.lastPrompt.length > 0) {\n\t\t\t\t\tlastPrompt = parsed.lastPrompt;\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\n\t\tconst handle = await open(entry.path, 'r');\n\t\ttry {\n\t\t\tif (entry.size <= summaryWindowSize * 2) {\n\t\t\t\tconst whole = Buffer.alloc(entry.size);\n\t\t\t\tawait handle.read(whole, 0, entry.size, 0);\n\t\t\t\tapply(whole, false);\n\t\t\t} else {\n\t\t\t\tconst head = Buffer.alloc(summaryWindowSize);\n\t\t\t\tawait handle.read(head, 0, summaryWindowSize, 0);\n\t\t\t\tapply(head, false);\n\n\t\t\t\tconst tail = Buffer.alloc(summaryWindowSize);\n\t\t\t\tawait handle.read(tail, 0, summaryWindowSize, entry.size - summaryWindowSize);\n\t\t\t\tapply(tail, true);\n\t\t\t}\n\t\t} finally {\n\t\t\tawait handle.close();\n\t\t}\n\n\t\treturn {\n\t\t\t...entry,\n\t\t\ttitles: titles,\n\t\t\tlastPrompt: lastPrompt,\n\t\t};\n\t}\n\n\tprotected getProjectsRoot(): string {\n\t\treturn join(homedir(), '.claude', 'projects');\n\t}\n\n\t/** Resolves the `~/.claude/projects` directory holding the sessions whose working directory is\n\t * `cwd`, or `undefined` when none exists. Falls back to a case-insensitive name match because on\n\t * Windows our\n\t * paths carry a lower-cased drive letter (`normalizePath`) while Claude encodes the OS-native\n\t * `C:\\...` — so the exact name never matches by case. */\n\tprotected async resolveProjectDir(cwd: string | undefined): Promise<string | undefined> {\n\t\tif (cwd == null || cwd.length === 0) return undefined;\n\n\t\tconst root = this.getProjectsRoot();\n\t\tconst encoded = encodeProjectDirName(cwd);\n\n\t\tconst exact = join(root, encoded);\n\t\tif (await directoryExists(exact)) return exact;\n\n\t\tlet dirs: string[];\n\t\ttry {\n\t\t\tdirs = await readdir(root);\n\t\t} catch (ex) {\n\t\t\t// Only a missing projects root means \"no transcript\". A transient failure propagates so the\n\t\t\t// caller retries instead of caching a permanent miss.\n\t\t\tif (!isMissingEntry(ex)) throw ex;\n\n\t\t\treturn undefined;\n\t\t}\n\n\t\tconst lowered = encoded.toLowerCase();\n\t\tconst match = dirs.find(d => d !== encoded && d.toLowerCase() === lowered);\n\t\treturn match != null ? join(root, match) : undefined;\n\t}\n\n\tprotected async locateTranscript(sessionId: string, cwd: string | undefined): Promise<string | undefined> {\n\t\tconst fileName = `${sessionId}.jsonl`;\n\n\t\tconst dir = await this.resolveProjectDir(cwd);\n\t\tif (dir != null) {\n\t\t\tconst candidate = join(dir, fileName);\n\t\t\tif (await fileExists(candidate)) return candidate;\n\t\t}\n\n\t\t// The recorded cwd drifts whenever the agent `cd`s, so it can encode to a directory the session\n\t\t// doesn't actually live in — scan every project for the file as a last resort.\n\t\tconst root = this.getProjectsRoot();\n\t\tlet dirs: string[];\n\t\ttry {\n\t\t\tdirs = await readdir(root);\n\t\t} catch (ex) {\n\t\t\tif (!isMissingEntry(ex)) throw ex;\n\n\t\t\treturn undefined;\n\t\t}\n\n\t\tconst candidates = await Promise.all(\n\t\t\tdirs.map(async d => {\n\t\t\t\tconst candidate = join(root, d, fileName);\n\t\t\t\treturn (await fileExists(candidate)) ? candidate : undefined;\n\t\t\t}),\n\t\t);\n\t\treturn candidates.find(c => c != null);\n\t}\n\n\t/**\n\t * Reads bytes `[start, end)` from `path` and applies any title entries it finds to a copy of\n\t * `baseTitles`. Returns the merged titles and the byte position immediately after the last\n\t * newline observed — partial trailing lines (writer hasn't flushed `\\n` yet) are intentionally\n\t * left unconsumed so they're re-read on the next pass.\n\t */\n\tprotected async readRange(\n\t\tpath: string,\n\t\tstart: number,\n\t\tend: number,\n\t\tsessionId: string,\n\t\tbaseTitles: TranscriptTitles,\n\t): Promise<{ titles: TranscriptTitles; consumedEnd: number }> {\n\t\tconst buffer = await readSlice(path, start, end);\n\t\tconst titles: TranscriptTitles = { ...baseTitles };\n\n\t\tlet lastNewlineEnd = start;\n\t\tlet lineStart = 0;\n\t\tfor (let i = 0; i < buffer.length; i++) {\n\t\t\tif (buffer[i] !== 0x0a) continue;\n\n\t\t\t// Slice the line (trim a trailing \\r for CRLF files), then advance the cursor past the \\n\n\t\t\t// regardless of whether the line yielded anything — we've fully observed those bytes.\n\t\t\tconst lineEnd = i > lineStart && buffer[i - 1] === 0x0d ? i - 1 : i;\n\t\t\tif (lineEnd > lineStart) {\n\t\t\t\tconst line = buffer.toString('utf8', lineStart, lineEnd);\n\t\t\t\tapplyTitleLine(line, sessionId, titles);\n\t\t\t}\n\t\t\tlineStart = i + 1;\n\t\t\tlastNewlineEnd = start + i + 1;\n\t\t}\n\n\t\treturn { titles: titles, consumedEnd: lastNewlineEnd };\n\t}\n}\n\nfunction applyTitleLine(line: string, sessionId: string, titles: TranscriptTitles): void {\n\tif (!isLikelyTitleLine(line)) return;\n\n\tlet entry: TitleEntry;\n\ttry {\n\t\tentry = JSON.parse(line) as TitleEntry;\n\t} catch {\n\t\treturn;\n\t}\n\tif (entry.sessionId != null && entry.sessionId !== sessionId) return;\n\n\tapplyTitleEntry(entry, titles);\n}\n\n/** Parses one transcript line for {@link ClaudeCodeTranscriptReader.readSummary}, rejecting entries\n * that belong to another session. Can't use the `-title` prefilter that {@link isLikelyTitleLine}\n * applies: `last-prompt` matches neither of its markers. */\nfunction parseSummaryLine(line: string, sessionId: string): TitleEntry | undefined {\n\tif (line.length === 0 || !line.includes('\"type\"')) return undefined;\n\n\tlet entry: TitleEntry;\n\ttry {\n\t\tentry = JSON.parse(line) as TitleEntry;\n\t} catch {\n\t\treturn undefined;\n\t}\n\tif (entry.sessionId != null && entry.sessionId !== sessionId) return undefined;\n\n\treturn entry;\n}\n\nfunction applyTitleEntry(entry: TitleEntry, titles: TranscriptTitles): void {\n\tswitch (entry.type) {\n\t\tcase 'custom-title':\n\t\t\tif (typeof entry.customTitle === 'string' && entry.customTitle.length > 0) {\n\t\t\t\ttitles.custom = entry.customTitle;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 'ai-title':\n\t\t\tif (typeof entry.aiTitle === 'string' && entry.aiTitle.length > 0) {\n\t\t\t\ttitles.ai = entry.aiTitle;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 'agent-name':\n\t\t\tif (typeof entry.agentName === 'string' && entry.agentName.length > 0) {\n\t\t\t\ttitles.agent = entry.agentName;\n\t\t\t}\n\t\t\tbreak;\n\t}\n}\n\n/** Encodes a working directory into its `~/.claude/projects` directory name. Claude replaces every\n * non-alphanumeric character — not just separators — preserving runs (`/home/e/.claude` →\n * `-home-e--claude`) and case. Separators-only would leave the dot in our own worktree convention\n * (`<repo>.worktrees/<name>`) intact and compute a directory that never exists. */\nexport function encodeProjectDirName(cwd: string): string {\n\treturn cwd.replace(/[^a-zA-Z0-9]/g, '-');\n}\n\nfunction isLikelyTitleLine(line: string): boolean {\n\tif (!line.includes('\"type\"')) return false;\n\treturn line.includes('-title') || line.includes('agent-name');\n}\n\n/** A transcript with no title and no prompt is an aborted or empty session — nothing to name it by,\n * nothing to search it on. */\nfunction hasSummaryContent(summary: TranscriptSessionSummary): boolean {\n\tconst { custom, ai, agent } = summary.titles;\n\treturn custom != null || ai != null || agent != null || summary.lastPrompt != null;\n}\n\nasync function fileExists(path: string): Promise<boolean> {\n\ttry {\n\t\tconst stats = await stat(path);\n\t\treturn stats.isFile();\n\t} catch (ex) {\n\t\t// See {@link isMissingEntry}: only a genuine absence is an answer.\n\t\tif (!isMissingEntry(ex)) throw ex;\n\n\t\treturn false;\n\t}\n}\n\nasync function directoryExists(path: string): Promise<boolean> {\n\ttry {\n\t\tconst stats = await stat(path);\n\t\treturn stats.isDirectory();\n\t} catch (ex) {\n\t\t// \"Not there\" is an answer; anything else (EACCES, EIO, a mount hiccup) is a transient\n\t\t// failure the caller must be able to tell apart — see {@link isMissingEntry}.\n\t\tif (isMissingEntry(ex)) return false;\n\n\t\tthrow ex;\n\t}\n}\n\n/** True for the \"path simply isn't there\" errno codes, as opposed to a transient I/O or permission\n * failure. Callers cache a genuine absence as terminal, so a transient failure must not look like\n * one — it would pin a session to its fallback name until the window reloads. */\nfunction isMissingEntry(ex: unknown): boolean {\n\tconst code = (ex as NodeJS.ErrnoException | undefined)?.code;\n\treturn code === 'ENOENT' || code === 'ENOTDIR';\n}\n\nfunction readSlice(path: string, start: number, end: number): Promise<Buffer> {\n\treturn new Promise((resolve, reject) => {\n\t\tconst chunks: Buffer[] = [];\n\t\tconst stream = createReadStream(path, { start: start, end: end - 1 });\n\t\tstream.on('data', chunk => chunks.push(chunk as Buffer));\n\t\tstream.on('end', () => resolve(Buffer.concat(chunks)));\n\t\tstream.on('error', reject);\n\t});\n}\n"],"mappings":"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"}
@@ -1,8 +1,8 @@
1
1
  import { CancellationError, isCancellationError } from "../../../utils/cancellation.js";
2
2
  import { Logger as _logger, getLoggableName } from "../../../utils/logger.js";
3
3
  import { maybeStartScopedLogger } from "../../../utils/logger.scoped.js";
4
- import { uuid } from "../../../utils/crypto.js";
5
4
  import _usingCtx from "../../../_virtual/@oxc-project+runtime/helpers/esm/usingCtx.js";
5
+ import { uuid } from "../../../utils/crypto.js";
6
6
  import { AIError } from "../errors.js";
7
7
  import { getActionName, getReducedMaxInputTokens, getValidatedTemperature } from "../utils/ai.utils.js";
8
8
  import { isResponseFormatRejected, rememberResponseFormatRejection } from "./responseFormatCache.js";
@@ -9,8 +9,12 @@ export declare class RevisionGitSubProvider implements GitRevisionSubProvider {
9
9
  getEmptyTreeSha(_repoPath: string): Promise<string>;
10
10
  getRevisionContent(repoPath: string, path: string, rev: string): Promise<Uint8Array | undefined>;
11
11
  getTreeEntryForRevision(repoPath: string, path: string, rev: string): Promise<GitTreeEntry | undefined>;
12
- getTrackedFiles(repoPath: string): Promise<string[]>;
13
- getTreeForRevision(repoPath: string, rev: string): Promise<GitTreeEntry[]>;
12
+ getTrackedFiles(repoPath: string, options?: {
13
+ paths?: readonly string[];
14
+ }): Promise<string[]>;
15
+ getTreeForRevision(repoPath: string, rev: string, options?: {
16
+ paths?: readonly string[];
17
+ }): Promise<GitTreeEntry[]>;
14
18
  resolveRevision(repoPath: string, ref: string, pathOrUri?: string | Uri, _options?: {
15
19
  force?: boolean;
16
20
  }): Promise<ResolvedRevision>;
@@ -39,11 +39,13 @@ var RevisionGitSubProvider = class {
39
39
  type: (stats.type & FileType.Directory) !== 0 ? "tree" : "blob"
40
40
  };
41
41
  }
42
- async getTrackedFiles(repoPath) {
43
- return (await this.getTreeForRevision(repoPath, "HEAD")).filter((f) => f.type === "blob").map((f) => f.path);
42
+ async getTrackedFiles(repoPath, options) {
43
+ return (await this.getTreeForRevision(repoPath, "HEAD", options)).filter((f) => f.type === "blob").map((f) => f.path);
44
44
  }
45
- async getTreeForRevision(repoPath, rev) {
45
+ async getTreeForRevision(repoPath, rev, options) {
46
46
  if (repoPath == null) return [];
47
+ const paths = options?.paths;
48
+ if (paths != null && !paths.length) return [];
47
49
  if (rev === "HEAD") {
48
50
  const context = await this.provider.ensureRepositoryContext(repoPath);
49
51
  if (context == null) return [];
@@ -53,13 +55,16 @@ var RevisionGitSubProvider = class {
53
55
  const entries = await this.provider.context.fs.readDirectory(baseUri);
54
56
  if (entries == null) return [];
55
57
  const result = [];
56
- for (const [path, type] of entries) result.push({
57
- ref: rev,
58
- oid: "",
59
- path,
60
- size: 0,
61
- type: (type & FileType.Directory) !== 0 ? "tree" : "blob"
62
- });
58
+ for (const [path, type] of entries) {
59
+ if (paths != null && !paths.some((p) => p === path || p.startsWith(`${path}/`) || path.startsWith(`${p}/`))) continue;
60
+ result.push({
61
+ ref: rev,
62
+ oid: "",
63
+ path,
64
+ size: 0,
65
+ type: (type & FileType.Directory) !== 0 ? "tree" : "blob"
66
+ });
67
+ }
63
68
  return result;
64
69
  }
65
70
  async resolveRevision(repoPath, ref, pathOrUri, _options) {
@@ -1 +1 @@
1
- {"version":3,"file":"revision.js","names":[],"sources":["../../../../../../plus/git-github/src/providers/github/revision.ts"],"sourcesContent":["import { FileType } from '@gitlens/git/context.js';\nimport { deletedOrMissing } from '@gitlens/git/models/revision.js';\nimport type { GitTreeEntry } from '@gitlens/git/models/tree.js';\nimport type { GitRevisionSubProvider, ResolvedRevision } from '@gitlens/git/providers/revision.js';\nimport {\n\tisRevisionWithSuffix,\n\tisSha,\n\tisUncommitted,\n\tisUncommittedWithParentSuffix,\n\tstripOrigin,\n} from '@gitlens/git/utils/revision.utils.js';\nimport { gate } from '@gitlens/utils/decorators/gate.js';\nimport { debug } from '@gitlens/utils/decorators/log.js';\nimport type { Uri } from '@gitlens/utils/uri.js';\nimport { toTokenInfo } from '../../api/tokenUtils.js';\nimport type { GitHubGitProviderInternal } from '../githubProvider.js';\n\n/** GitHub repositories are always SHA-1 */\nconst sha1EmptyTreeSha = '4b825dc642cb6eb9a060e54bf8d69288fbee4904';\n\nexport class RevisionGitSubProvider implements GitRevisionSubProvider {\n\tconstructor(private readonly provider: GitHubGitProviderInternal) {}\n\n\tgetEmptyTreeSha(_repoPath: string): Promise<string> {\n\t\treturn Promise.resolve(sha1EmptyTreeSha);\n\t}\n\n\t@gate()\n\t@debug()\n\tgetRevisionContent(repoPath: string, path: string, rev: string): Promise<Uint8Array | undefined> {\n\t\tconst uri = rev\n\t\t\t? this.provider.createProviderUri(repoPath, rev, path)\n\t\t\t: this.provider.createVirtualUri(repoPath, rev, path);\n\n\t\treturn this.provider.context.fs.readFile(uri);\n\t}\n\n\t@gate()\n\t@debug()\n\tasync getTreeEntryForRevision(repoPath: string, path: string, rev: string): Promise<GitTreeEntry | undefined> {\n\t\tif (repoPath == null || !path) return undefined;\n\n\t\tif (path) {\n\t\t\tpath = this.provider.getRelativePath(path, repoPath);\n\t\t}\n\n\t\tif (rev === 'HEAD') {\n\t\t\tconst context = await this.provider.ensureRepositoryContext(repoPath);\n\t\t\tif (context == null) return undefined;\n\n\t\t\tconst revision = await context.metadata.getRevision();\n\t\t\trev = revision?.revision;\n\t\t}\n\n\t\tconst uri = rev\n\t\t\t? this.provider.createProviderUri(repoPath, rev, path)\n\t\t\t: this.provider.createVirtualUri(repoPath, rev, path);\n\n\t\tconst stats = await this.provider.context.fs.stat(uri);\n\t\tif (stats == null) return undefined;\n\n\t\treturn {\n\t\t\tref: rev,\n\t\t\toid: '',\n\t\t\tpath: this.provider.getRelativePath(uri, repoPath),\n\t\t\tsize: stats.size,\n\t\t\ttype: (stats.type & FileType.Directory) !== 0 ? 'tree' : 'blob',\n\t\t};\n\t}\n\n\t@debug()\n\tasync getTrackedFiles(repoPath: string): Promise<string[]> {\n\t\tconst tree = await this.getTreeForRevision(repoPath, 'HEAD');\n\t\treturn tree.filter(f => f.type === 'blob').map(f => f.path);\n\t}\n\n\t@gate()\n\t@debug()\n\tasync getTreeForRevision(repoPath: string, rev: string): Promise<GitTreeEntry[]> {\n\t\tif (repoPath == null) return [];\n\n\t\tif (rev === 'HEAD') {\n\t\t\tconst context = await this.provider.ensureRepositoryContext(repoPath);\n\t\t\tif (context == null) return [];\n\n\t\t\tconst revision = await context.metadata.getRevision();\n\t\t\trev = revision?.revision;\n\t\t}\n\n\t\tconst baseUri = rev\n\t\t\t? this.provider.createProviderUri(repoPath, rev)\n\t\t\t: this.provider.createVirtualUri(repoPath, rev);\n\n\t\tconst entries = await this.provider.context.fs.readDirectory(baseUri);\n\t\tif (entries == null) return [];\n\n\t\tconst result: GitTreeEntry[] = [];\n\t\tfor (const [path, type] of entries) {\n\t\t\tresult.push({\n\t\t\t\tref: rev,\n\t\t\t\toid: '',\n\t\t\t\tpath: path,\n\t\t\t\tsize: 0,\n\t\t\t\ttype: (type & FileType.Directory) !== 0 ? 'tree' : 'blob',\n\t\t\t});\n\t\t}\n\n\t\treturn result;\n\t}\n\n\t@debug()\n\tasync resolveRevision(\n\t\trepoPath: string,\n\t\tref: string,\n\t\tpathOrUri?: string | Uri,\n\t\t// Nothing to force here — every call resolves fresh from the GitHub API; there is no cache to bypass.\n\t\t_options?: { force?: boolean },\n\t): Promise<ResolvedRevision> {\n\t\tif (!ref || ref === deletedOrMissing) return { sha: ref, revision: ref };\n\n\t\tlet relativePath;\n\t\tif (pathOrUri == null) {\n\t\t\tif (isSha(ref)) return { sha: ref, revision: ref };\n\t\t\tif (ref.endsWith('^3')) return { sha: ref, revision: ref };\n\t\t} else {\n\t\t\tif (isUncommittedWithParentSuffix(ref)) {\n\t\t\t\tref = 'HEAD';\n\t\t\t}\n\t\t\tif (isUncommitted(ref)) return { sha: ref, revision: ref };\n\n\t\t\trelativePath = this.provider.getRelativePath(pathOrUri, repoPath);\n\t\t}\n\n\t\tconst context = await this.provider.ensureRepositoryContext(repoPath);\n\t\tif (context == null) return { sha: ref, revision: ref };\n\n\t\tconst { metadata, github, session } = context;\n\n\t\tconst sha = await github.resolveReference(\n\t\t\ttoTokenInfo(this.provider.authenticationProviderId, session),\n\t\t\tmetadata.repo.owner,\n\t\t\tmetadata.repo.name,\n\t\t\tstripOrigin(ref),\n\t\t\trelativePath,\n\t\t);\n\n\t\tif (sha == null) {\n\t\t\treturn { sha: relativePath ? deletedOrMissing : ref, revision: ref };\n\t\t}\n\t\t// If it looks like non-sha like then preserve it as the friendly name\n\t\treturn { sha: sha, revision: isRevisionWithSuffix(ref) ? sha : ref };\n\t}\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"revision.js","names":[],"sources":["../../../../../../plus/git-github/src/providers/github/revision.ts"],"sourcesContent":["import { FileType } from '@gitlens/git/context.js';\nimport { deletedOrMissing } from '@gitlens/git/models/revision.js';\nimport type { GitTreeEntry } from '@gitlens/git/models/tree.js';\nimport type { GitRevisionSubProvider, ResolvedRevision } from '@gitlens/git/providers/revision.js';\nimport {\n\tisRevisionWithSuffix,\n\tisSha,\n\tisUncommitted,\n\tisUncommittedWithParentSuffix,\n\tstripOrigin,\n} from '@gitlens/git/utils/revision.utils.js';\nimport { gate } from '@gitlens/utils/decorators/gate.js';\nimport { debug } from '@gitlens/utils/decorators/log.js';\nimport type { Uri } from '@gitlens/utils/uri.js';\nimport { toTokenInfo } from '../../api/tokenUtils.js';\nimport type { GitHubGitProviderInternal } from '../githubProvider.js';\n\n/** GitHub repositories are always SHA-1 */\nconst sha1EmptyTreeSha = '4b825dc642cb6eb9a060e54bf8d69288fbee4904';\n\nexport class RevisionGitSubProvider implements GitRevisionSubProvider {\n\tconstructor(private readonly provider: GitHubGitProviderInternal) {}\n\n\tgetEmptyTreeSha(_repoPath: string): Promise<string> {\n\t\treturn Promise.resolve(sha1EmptyTreeSha);\n\t}\n\n\t@gate()\n\t@debug()\n\tgetRevisionContent(repoPath: string, path: string, rev: string): Promise<Uint8Array | undefined> {\n\t\tconst uri = rev\n\t\t\t? this.provider.createProviderUri(repoPath, rev, path)\n\t\t\t: this.provider.createVirtualUri(repoPath, rev, path);\n\n\t\treturn this.provider.context.fs.readFile(uri);\n\t}\n\n\t@gate()\n\t@debug()\n\tasync getTreeEntryForRevision(repoPath: string, path: string, rev: string): Promise<GitTreeEntry | undefined> {\n\t\tif (repoPath == null || !path) return undefined;\n\n\t\tif (path) {\n\t\t\tpath = this.provider.getRelativePath(path, repoPath);\n\t\t}\n\n\t\tif (rev === 'HEAD') {\n\t\t\tconst context = await this.provider.ensureRepositoryContext(repoPath);\n\t\t\tif (context == null) return undefined;\n\n\t\t\tconst revision = await context.metadata.getRevision();\n\t\t\trev = revision?.revision;\n\t\t}\n\n\t\tconst uri = rev\n\t\t\t? this.provider.createProviderUri(repoPath, rev, path)\n\t\t\t: this.provider.createVirtualUri(repoPath, rev, path);\n\n\t\tconst stats = await this.provider.context.fs.stat(uri);\n\t\tif (stats == null) return undefined;\n\n\t\treturn {\n\t\t\tref: rev,\n\t\t\toid: '',\n\t\t\tpath: this.provider.getRelativePath(uri, repoPath),\n\t\t\tsize: stats.size,\n\t\t\ttype: (stats.type & FileType.Directory) !== 0 ? 'tree' : 'blob',\n\t\t};\n\t}\n\n\t@debug()\n\tasync getTrackedFiles(repoPath: string, options?: { paths?: readonly string[] }): Promise<string[]> {\n\t\tconst tree = await this.getTreeForRevision(repoPath, 'HEAD', options);\n\t\treturn tree.filter(f => f.type === 'blob').map(f => f.path);\n\t}\n\n\t@gate()\n\t@debug()\n\tasync getTreeForRevision(\n\t\trepoPath: string,\n\t\trev: string,\n\t\toptions?: { paths?: readonly string[] },\n\t): Promise<GitTreeEntry[]> {\n\t\tif (repoPath == null) return [];\n\n\t\tconst paths = options?.paths;\n\t\tif (paths != null && !paths.length) return [];\n\n\t\tif (rev === 'HEAD') {\n\t\t\tconst context = await this.provider.ensureRepositoryContext(repoPath);\n\t\t\tif (context == null) return [];\n\n\t\t\tconst revision = await context.metadata.getRevision();\n\t\t\trev = revision?.revision;\n\t\t}\n\n\t\tconst baseUri = rev\n\t\t\t? this.provider.createProviderUri(repoPath, rev)\n\t\t\t: this.provider.createVirtualUri(repoPath, rev);\n\n\t\tconst entries = await this.provider.context.fs.readDirectory(baseUri);\n\t\tif (entries == null) return [];\n\n\t\tconst result: GitTreeEntry[] = [];\n\t\tfor (const [path, type] of entries) {\n\t\t\t// Only the root is listed, so a scope keeps the entries on its way to it, and anything beneath it\n\t\t\tif (paths != null && !paths.some(p => p === path || p.startsWith(`${path}/`) || path.startsWith(`${p}/`))) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tresult.push({\n\t\t\t\tref: rev,\n\t\t\t\toid: '',\n\t\t\t\tpath: path,\n\t\t\t\tsize: 0,\n\t\t\t\ttype: (type & FileType.Directory) !== 0 ? 'tree' : 'blob',\n\t\t\t});\n\t\t}\n\n\t\treturn result;\n\t}\n\n\t@debug()\n\tasync resolveRevision(\n\t\trepoPath: string,\n\t\tref: string,\n\t\tpathOrUri?: string | Uri,\n\t\t// Nothing to force here — every call resolves fresh from the GitHub API; there is no cache to bypass.\n\t\t_options?: { force?: boolean },\n\t): Promise<ResolvedRevision> {\n\t\tif (!ref || ref === deletedOrMissing) return { sha: ref, revision: ref };\n\n\t\tlet relativePath;\n\t\tif (pathOrUri == null) {\n\t\t\tif (isSha(ref)) return { sha: ref, revision: ref };\n\t\t\tif (ref.endsWith('^3')) return { sha: ref, revision: ref };\n\t\t} else {\n\t\t\tif (isUncommittedWithParentSuffix(ref)) {\n\t\t\t\tref = 'HEAD';\n\t\t\t}\n\t\t\tif (isUncommitted(ref)) return { sha: ref, revision: ref };\n\n\t\t\trelativePath = this.provider.getRelativePath(pathOrUri, repoPath);\n\t\t}\n\n\t\tconst context = await this.provider.ensureRepositoryContext(repoPath);\n\t\tif (context == null) return { sha: ref, revision: ref };\n\n\t\tconst { metadata, github, session } = context;\n\n\t\tconst sha = await github.resolveReference(\n\t\t\ttoTokenInfo(this.provider.authenticationProviderId, session),\n\t\t\tmetadata.repo.owner,\n\t\t\tmetadata.repo.name,\n\t\t\tstripOrigin(ref),\n\t\t\trelativePath,\n\t\t);\n\n\t\tif (sha == null) {\n\t\t\treturn { sha: relativePath ? deletedOrMissing : ref, revision: ref };\n\t\t}\n\t\t// If it looks like non-sha like then preserve it as the friendly name\n\t\treturn { sha: sha, revision: isRevisionWithSuffix(ref) ? sha : ref };\n\t}\n}\n"],"mappings":"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"}
@@ -1,7 +1,7 @@
1
1
  import { debug } from "../../../../utils/decorators/log.js";
2
2
  import __decorate from "../../../../_virtual/@oxc-project+runtime/helpers/esm/decorate.js";
3
- import { gate } from "../../../../utils/decorators/gate.js";
4
3
  import { GitStatus } from "../../../../git/models/status.js";
4
+ import { gate } from "../../../../utils/decorators/gate.js";
5
5
  import { HeadType } from "../../context.js";
6
6
  //#region ../plus/git-github/src/providers/github/status.ts
7
7
  var StatusGitSubProvider = class {
@@ -5,9 +5,9 @@ import { trace } from "../../../../utils/decorators/log.js";
5
5
  import { PromiseCache } from "../../../../utils/promiseCache.js";
6
6
  import __decorate from "../../../../_virtual/@oxc-project+runtime/helpers/esm/decorate.js";
7
7
  import { AuthenticationError, RequestClientError, RequestNotFoundError } from "../../../../git/errors.js";
8
- import { sha256, uuid } from "../../../../utils/crypto.js";
9
8
  import { maybeStopWatch } from "../../../../utils/stopwatch.js";
10
9
  import { base64 } from "../../../../utils/base64.js";
10
+ import { sha256, uuid } from "../../../../utils/crypto.js";
11
11
  import { effectiveIssueSort } from "../../../../git/utils/issue.utils.js";
12
12
  import { ProviderFetchError, isRateLimitResponse, toRateLimitError } from "../../errors.js";
13
13
  import { decodePathSegment } from "../../utils/domain.utils.js";
@@ -11,6 +11,11 @@ export type AzureWorkItemStatesScope = {
11
11
  export declare function getAzureWorkItemTypesUrl(baseUrl: string, namespace: string, project: string): string;
12
12
  export declare function getAzureWorkItemTypeStatesPath(namespace: string, project: string, type: string): string;
13
13
  export declare function getAzureWorkItemTypeStatesUrl(baseUrl: string, namespace: string, project: string, type: string): string;
14
+ /**
15
+ * Workflow metadata per project, work item type and token, kept for 5 minutes. A failed read is kept for 30 seconds,
16
+ * so the pages and per-type reads of a listing fail fast with the same error instead of each asking a failing server
17
+ * again; the first read after that retries.
18
+ */
14
19
  export declare class AzureWorkItemStatesCache {
15
20
  private readonly types;
16
21
  private readonly states;
@@ -43,6 +43,11 @@ function scopeKey(scope, type) {
43
43
  scope.isPAT
44
44
  ]);
45
45
  }
46
+ /**
47
+ * Workflow metadata per project, work item type and token, kept for 5 minutes. A failed read is kept for 30 seconds,
48
+ * so the pages and per-type reads of a listing fail fast with the same error instead of each asking a failing server
49
+ * again; the first read after that retries.
50
+ */
46
51
  var AzureWorkItemStatesCache = class {
47
52
  types = new PromiseCache({
48
53
  createTTL: 3e5,
@@ -1 +1 @@
1
- {"version":3,"file":"workItemStateCategories.js","names":[],"sources":["../../../../../../plus/integrations/src/providers/azure/workItemStateCategories.ts"],"sourcesContent":["import type { AzureWorkItemTypeStates } from '@gitkraken/provider-apis';\nimport { PromiseCache } from '@gitlens/utils/promiseCache.js';\nimport type { AzureWorkItemStateCategory } from './models.js';\nimport { azureIssueStateCategory, encodeAzurePathSegment } from './models.js';\n\ntype WorkItemTypeStates = NonNullable<AzureWorkItemTypeStates['types'][number]['states']>;\n\nexport type AzureWorkItemStatesScope = {\n\tbaseUrl: string;\n\tnamespace: string;\n\tproject: string;\n\ttoken: string;\n\tisPAT: boolean;\n};\n\nexport function getAzureWorkItemTypesUrl(baseUrl: string, namespace: string, project: string): string {\n\treturn `${baseUrl.replace(/\\/$/, '')}/${encodeAzurePathSegment(namespace)}/${encodeAzurePathSegment(project)}/_apis/wit/workItemTypes?api-version=6.0`;\n}\n\n// Server 2020 serves this endpoint only as a preview; Services accepts the same version.\nexport function getAzureWorkItemTypeStatesPath(namespace: string, project: string, type: string): string {\n\treturn `${encodeAzurePathSegment(namespace)}/${encodeAzurePathSegment(project)}/_apis/wit/workItemTypes/${encodeAzurePathSegment(type)}/states?api-version=6.0-preview.1`;\n}\n\nexport function getAzureWorkItemTypeStatesUrl(\n\tbaseUrl: string,\n\tnamespace: string,\n\tproject: string,\n\ttype: string,\n): string {\n\treturn `${baseUrl.replace(/\\/$/, '')}/${getAzureWorkItemTypeStatesPath(namespace, project, type)}`;\n}\n\nfunction parseWorkItemTypes(body: unknown): string[] {\n\tconst types = (body as { value?: unknown } | null)?.value;\n\tif (!Array.isArray(types)) throw new Error('Azure DevOps returned no work item types');\n\n\tconst names: string[] = [];\n\tfor (const type of types as { name?: unknown }[]) {\n\t\tif (typeof type?.name !== 'string' || !type.name.trim()) {\n\t\t\tthrow new Error('Azure DevOps returned an invalid work item type');\n\t\t}\n\n\t\tnames.push(type.name);\n\t}\n\treturn [...new Set(names)];\n}\n\nfunction parseWorkItemStates(body: unknown): WorkItemTypeStates {\n\tconst states = (body as { value?: unknown } | null)?.value;\n\tif (!Array.isArray(states) || states.length === 0) throw new Error('Azure DevOps returned no work item states');\n\n\tconst parsed: { name: string; category: string }[] = [];\n\tfor (const state of states as { name?: unknown; category?: unknown }[]) {\n\t\tif (\n\t\t\ttypeof state?.name !== 'string' ||\n\t\t\t!state.name.trim() ||\n\t\t\ttypeof state.category !== 'string' ||\n\t\t\tazureIssueStateCategory(state.category as AzureWorkItemStateCategory) == null ||\n\t\t\tparsed.some(s => s.name === state.name && s.category !== state.category)\n\t\t) {\n\t\t\tthrow new Error('Azure DevOps returned an invalid work item state');\n\t\t}\n\n\t\tparsed.push({ name: state.name, category: state.category });\n\t}\n\treturn parsed;\n}\n\nfunction scopeKey(scope: AzureWorkItemStatesScope, type?: string): string {\n\treturn JSON.stringify([\n\t\tscope.baseUrl.replace(/\\/$/, ''),\n\t\tscope.namespace.toLowerCase(),\n\t\tscope.project.toLowerCase(),\n\t\ttype?.toLowerCase(),\n\t\tscope.token,\n\t\tscope.isPAT,\n\t]);\n}\n\nexport class AzureWorkItemStatesCache {\n\tprivate readonly types = new PromiseCache<string, PromiseSettledResult<string[]>>({\n\t\tcreateTTL: 5 * 60 * 1000,\n\t\tcapacity: 256,\n\t});\n\tprivate readonly states = new PromiseCache<string, PromiseSettledResult<WorkItemTypeStates>>({\n\t\tcreateTTL: 5 * 60 * 1000,\n\t\tcapacity: 256,\n\t});\n\n\tclear(): void {\n\t\tthis.types.clear();\n\t\tthis.states.clear();\n\t}\n\n\tgetTypes(scope: AzureWorkItemStatesScope, read: () => Promise<unknown>): Promise<string[]> {\n\t\treturn this.read(this.types, scopeKey(scope), async () => parseWorkItemTypes(await read()));\n\t}\n\n\tgetStates(\n\t\tscope: AzureWorkItemStatesScope,\n\t\ttype: string,\n\t\tread: () => Promise<unknown>,\n\t): Promise<WorkItemTypeStates> {\n\t\treturn this.read(this.states, scopeKey(scope, type), async () => parseWorkItemStates(await read()));\n\t}\n\n\tprivate async read<T>(\n\t\tcache: PromiseCache<string, PromiseSettledResult<T>>,\n\t\tkey: string,\n\t\tload: () => Promise<T>,\n\t): Promise<T> {\n\t\tconst result = await cache.getOrCreate(key, async () => ({ status: 'fulfilled', value: await load() }), {\n\t\t\t// Every caller must still see the failure; caching undefined would turn later pages into successes.\n\t\t\tonError: error =>\n\t\t\t\tcache.get(key) == null\n\t\t\t\t\t? undefined\n\t\t\t\t\t: { value: { status: 'rejected', reason: error }, createTTL: 30 * 1000, suppress: true },\n\t\t});\n\t\tif (result.status === 'rejected') throw result.reason;\n\n\t\treturn result.value;\n\t}\n}\n"],"mappings":";;;AAeA,SAAgB,yBAAyB,SAAiB,WAAmB,SAAyB;CACrG,OAAO,GAAG,QAAQ,QAAQ,OAAO,EAAE,EAAE,GAAG,uBAAuB,SAAS,EAAE,GAAG,uBAAuB,OAAO,EAAE;AAC9G;AAGA,SAAgB,+BAA+B,WAAmB,SAAiB,MAAsB;CACxG,OAAO,GAAG,uBAAuB,SAAS,EAAE,GAAG,uBAAuB,OAAO,EAAE,2BAA2B,uBAAuB,IAAI,EAAE;AACxI;AAEA,SAAgB,8BACf,SACA,WACA,SACA,MACS;CACT,OAAO,GAAG,QAAQ,QAAQ,OAAO,EAAE,EAAE,GAAG,+BAA+B,WAAW,SAAS,IAAI;AAChG;AAEA,SAAS,mBAAmB,MAAyB;CACpD,MAAM,QAAS,MAAqC;CACpD,IAAI,CAAC,MAAM,QAAQ,KAAK,GAAG,MAAM,IAAI,MAAM,0CAA0C;CAErF,MAAM,QAAkB,CAAC;CACzB,KAAK,MAAM,QAAQ,OAA+B;EACjD,IAAI,OAAO,MAAM,SAAS,YAAY,CAAC,KAAK,KAAK,KAAK,GACrD,MAAM,IAAI,MAAM,iDAAiD;EAGlE,MAAM,KAAK,KAAK,IAAI;CACrB;CACA,OAAO,CAAC,GAAG,IAAI,IAAI,KAAK,CAAC;AAC1B;AAEA,SAAS,oBAAoB,MAAmC;CAC/D,MAAM,SAAU,MAAqC;CACrD,IAAI,CAAC,MAAM,QAAQ,MAAM,KAAK,OAAO,WAAW,GAAG,MAAM,IAAI,MAAM,2CAA2C;CAE9G,MAAM,SAA+C,CAAC;CACtD,KAAK,MAAM,SAAS,QAAoD;EACvE,IACC,OAAO,OAAO,SAAS,YACvB,CAAC,MAAM,KAAK,KAAK,KACjB,OAAO,MAAM,aAAa,YAC1B,wBAAwB,MAAM,QAAsC,KAAK,QACzE,OAAO,MAAK,MAAK,EAAE,SAAS,MAAM,QAAQ,EAAE,aAAa,MAAM,QAAQ,GAEvE,MAAM,IAAI,MAAM,kDAAkD;EAGnE,OAAO,KAAK;GAAE,MAAM,MAAM;GAAM,UAAU,MAAM;EAAS,CAAC;CAC3D;CACA,OAAO;AACR;AAEA,SAAS,SAAS,OAAiC,MAAuB;CACzE,OAAO,KAAK,UAAU;EACrB,MAAM,QAAQ,QAAQ,OAAO,EAAE;EAC/B,MAAM,UAAU,YAAY;EAC5B,MAAM,QAAQ,YAAY;EAC1B,MAAM,YAAY;EAClB,MAAM;EACN,MAAM;CACP,CAAC;AACF;AAEA,IAAa,2BAAb,MAAsC;CACrC,QAAyB,IAAI,aAAqD;EACjF,WAAW;EACX,UAAU;CACX,CAAC;CACD,SAA0B,IAAI,aAA+D;EAC5F,WAAW;EACX,UAAU;CACX,CAAC;CAED,QAAc;EACb,KAAK,MAAM,MAAM;EACjB,KAAK,OAAO,MAAM;CACnB;CAEA,SAAS,OAAiC,MAAiD;EAC1F,OAAO,KAAK,KAAK,KAAK,OAAO,SAAS,KAAK,GAAG,YAAY,mBAAmB,MAAM,KAAK,CAAC,CAAC;CAC3F;CAEA,UACC,OACA,MACA,MAC8B;EAC9B,OAAO,KAAK,KAAK,KAAK,QAAQ,SAAS,OAAO,IAAI,GAAG,YAAY,oBAAoB,MAAM,KAAK,CAAC,CAAC;CACnG;CAEA,MAAc,KACb,OACA,KACA,MACa;EACb,MAAM,SAAS,MAAM,MAAM,YAAY,KAAK,aAAa;GAAE,QAAQ;GAAa,OAAO,MAAM,KAAK;EAAE,IAAI,EAEvG,UAAS,UACR,MAAM,IAAI,GAAG,KAAK,OACf,KAAA,IACA;GAAE,OAAO;IAAE,QAAQ;IAAY,QAAQ;GAAM;GAAG,WAAW;GAAW,UAAU;EAAK,EAC1F,CAAC;EACD,IAAI,OAAO,WAAW,YAAY,MAAM,OAAO;EAE/C,OAAO,OAAO;CACf;AACD"}
1
+ {"version":3,"file":"workItemStateCategories.js","names":[],"sources":["../../../../../../plus/integrations/src/providers/azure/workItemStateCategories.ts"],"sourcesContent":["import type { AzureWorkItemTypeStates } from '@gitkraken/provider-apis';\nimport { PromiseCache } from '@gitlens/utils/promiseCache.js';\nimport type { AzureWorkItemStateCategory } from './models.js';\nimport { azureIssueStateCategory, encodeAzurePathSegment } from './models.js';\n\ntype WorkItemTypeStates = NonNullable<AzureWorkItemTypeStates['types'][number]['states']>;\n\nexport type AzureWorkItemStatesScope = {\n\tbaseUrl: string;\n\tnamespace: string;\n\tproject: string;\n\ttoken: string;\n\tisPAT: boolean;\n};\n\nexport function getAzureWorkItemTypesUrl(baseUrl: string, namespace: string, project: string): string {\n\treturn `${baseUrl.replace(/\\/$/, '')}/${encodeAzurePathSegment(namespace)}/${encodeAzurePathSegment(project)}/_apis/wit/workItemTypes?api-version=6.0`;\n}\n\n// Server 2020 serves this endpoint only as a preview; Services accepts the same version.\nexport function getAzureWorkItemTypeStatesPath(namespace: string, project: string, type: string): string {\n\treturn `${encodeAzurePathSegment(namespace)}/${encodeAzurePathSegment(project)}/_apis/wit/workItemTypes/${encodeAzurePathSegment(type)}/states?api-version=6.0-preview.1`;\n}\n\nexport function getAzureWorkItemTypeStatesUrl(\n\tbaseUrl: string,\n\tnamespace: string,\n\tproject: string,\n\ttype: string,\n): string {\n\treturn `${baseUrl.replace(/\\/$/, '')}/${getAzureWorkItemTypeStatesPath(namespace, project, type)}`;\n}\n\nfunction parseWorkItemTypes(body: unknown): string[] {\n\tconst types = (body as { value?: unknown } | null)?.value;\n\tif (!Array.isArray(types)) throw new Error('Azure DevOps returned no work item types');\n\n\tconst names: string[] = [];\n\tfor (const type of types as { name?: unknown }[]) {\n\t\tif (typeof type?.name !== 'string' || !type.name.trim()) {\n\t\t\tthrow new Error('Azure DevOps returned an invalid work item type');\n\t\t}\n\n\t\tnames.push(type.name);\n\t}\n\treturn [...new Set(names)];\n}\n\nfunction parseWorkItemStates(body: unknown): WorkItemTypeStates {\n\tconst states = (body as { value?: unknown } | null)?.value;\n\tif (!Array.isArray(states) || states.length === 0) throw new Error('Azure DevOps returned no work item states');\n\n\tconst parsed: { name: string; category: string }[] = [];\n\tfor (const state of states as { name?: unknown; category?: unknown }[]) {\n\t\tif (\n\t\t\ttypeof state?.name !== 'string' ||\n\t\t\t!state.name.trim() ||\n\t\t\ttypeof state.category !== 'string' ||\n\t\t\tazureIssueStateCategory(state.category as AzureWorkItemStateCategory) == null ||\n\t\t\tparsed.some(s => s.name === state.name && s.category !== state.category)\n\t\t) {\n\t\t\tthrow new Error('Azure DevOps returned an invalid work item state');\n\t\t}\n\n\t\tparsed.push({ name: state.name, category: state.category });\n\t}\n\treturn parsed;\n}\n\nfunction scopeKey(scope: AzureWorkItemStatesScope, type?: string): string {\n\treturn JSON.stringify([\n\t\tscope.baseUrl.replace(/\\/$/, ''),\n\t\tscope.namespace.toLowerCase(),\n\t\tscope.project.toLowerCase(),\n\t\ttype?.toLowerCase(),\n\t\tscope.token,\n\t\tscope.isPAT,\n\t]);\n}\n\n/**\n * Workflow metadata per project, work item type and token, kept for 5 minutes. A failed read is kept for 30 seconds,\n * so the pages and per-type reads of a listing fail fast with the same error instead of each asking a failing server\n * again; the first read after that retries.\n */\nexport class AzureWorkItemStatesCache {\n\tprivate readonly types = new PromiseCache<string, PromiseSettledResult<string[]>>({\n\t\tcreateTTL: 5 * 60 * 1000,\n\t\tcapacity: 256,\n\t});\n\tprivate readonly states = new PromiseCache<string, PromiseSettledResult<WorkItemTypeStates>>({\n\t\tcreateTTL: 5 * 60 * 1000,\n\t\tcapacity: 256,\n\t});\n\n\tclear(): void {\n\t\tthis.types.clear();\n\t\tthis.states.clear();\n\t}\n\n\tgetTypes(scope: AzureWorkItemStatesScope, read: () => Promise<unknown>): Promise<string[]> {\n\t\treturn this.read(this.types, scopeKey(scope), async () => parseWorkItemTypes(await read()));\n\t}\n\n\tgetStates(\n\t\tscope: AzureWorkItemStatesScope,\n\t\ttype: string,\n\t\tread: () => Promise<unknown>,\n\t): Promise<WorkItemTypeStates> {\n\t\treturn this.read(this.states, scopeKey(scope, type), async () => parseWorkItemStates(await read()));\n\t}\n\n\tprivate async read<T>(\n\t\tcache: PromiseCache<string, PromiseSettledResult<T>>,\n\t\tkey: string,\n\t\tload: () => Promise<T>,\n\t): Promise<T> {\n\t\tconst result = await cache.getOrCreate(key, async () => ({ status: 'fulfilled', value: await load() }), {\n\t\t\t// Every caller must still see the failure; caching undefined would turn later pages into successes.\n\t\t\tonError: error =>\n\t\t\t\tcache.get(key) == null\n\t\t\t\t\t? undefined\n\t\t\t\t\t: { value: { status: 'rejected', reason: error }, createTTL: 30 * 1000, suppress: true },\n\t\t});\n\t\tif (result.status === 'rejected') throw result.reason;\n\n\t\treturn result.value;\n\t}\n}\n"],"mappings":";;;AAeA,SAAgB,yBAAyB,SAAiB,WAAmB,SAAyB;CACrG,OAAO,GAAG,QAAQ,QAAQ,OAAO,EAAE,EAAE,GAAG,uBAAuB,SAAS,EAAE,GAAG,uBAAuB,OAAO,EAAE;AAC9G;AAGA,SAAgB,+BAA+B,WAAmB,SAAiB,MAAsB;CACxG,OAAO,GAAG,uBAAuB,SAAS,EAAE,GAAG,uBAAuB,OAAO,EAAE,2BAA2B,uBAAuB,IAAI,EAAE;AACxI;AAEA,SAAgB,8BACf,SACA,WACA,SACA,MACS;CACT,OAAO,GAAG,QAAQ,QAAQ,OAAO,EAAE,EAAE,GAAG,+BAA+B,WAAW,SAAS,IAAI;AAChG;AAEA,SAAS,mBAAmB,MAAyB;CACpD,MAAM,QAAS,MAAqC;CACpD,IAAI,CAAC,MAAM,QAAQ,KAAK,GAAG,MAAM,IAAI,MAAM,0CAA0C;CAErF,MAAM,QAAkB,CAAC;CACzB,KAAK,MAAM,QAAQ,OAA+B;EACjD,IAAI,OAAO,MAAM,SAAS,YAAY,CAAC,KAAK,KAAK,KAAK,GACrD,MAAM,IAAI,MAAM,iDAAiD;EAGlE,MAAM,KAAK,KAAK,IAAI;CACrB;CACA,OAAO,CAAC,GAAG,IAAI,IAAI,KAAK,CAAC;AAC1B;AAEA,SAAS,oBAAoB,MAAmC;CAC/D,MAAM,SAAU,MAAqC;CACrD,IAAI,CAAC,MAAM,QAAQ,MAAM,KAAK,OAAO,WAAW,GAAG,MAAM,IAAI,MAAM,2CAA2C;CAE9G,MAAM,SAA+C,CAAC;CACtD,KAAK,MAAM,SAAS,QAAoD;EACvE,IACC,OAAO,OAAO,SAAS,YACvB,CAAC,MAAM,KAAK,KAAK,KACjB,OAAO,MAAM,aAAa,YAC1B,wBAAwB,MAAM,QAAsC,KAAK,QACzE,OAAO,MAAK,MAAK,EAAE,SAAS,MAAM,QAAQ,EAAE,aAAa,MAAM,QAAQ,GAEvE,MAAM,IAAI,MAAM,kDAAkD;EAGnE,OAAO,KAAK;GAAE,MAAM,MAAM;GAAM,UAAU,MAAM;EAAS,CAAC;CAC3D;CACA,OAAO;AACR;AAEA,SAAS,SAAS,OAAiC,MAAuB;CACzE,OAAO,KAAK,UAAU;EACrB,MAAM,QAAQ,QAAQ,OAAO,EAAE;EAC/B,MAAM,UAAU,YAAY;EAC5B,MAAM,QAAQ,YAAY;EAC1B,MAAM,YAAY;EAClB,MAAM;EACN,MAAM;CACP,CAAC;AACF;;;;;;AAOA,IAAa,2BAAb,MAAsC;CACrC,QAAyB,IAAI,aAAqD;EACjF,WAAW;EACX,UAAU;CACX,CAAC;CACD,SAA0B,IAAI,aAA+D;EAC5F,WAAW;EACX,UAAU;CACX,CAAC;CAED,QAAc;EACb,KAAK,MAAM,MAAM;EACjB,KAAK,OAAO,MAAM;CACnB;CAEA,SAAS,OAAiC,MAAiD;EAC1F,OAAO,KAAK,KAAK,KAAK,OAAO,SAAS,KAAK,GAAG,YAAY,mBAAmB,MAAM,KAAK,CAAC,CAAC;CAC3F;CAEA,UACC,OACA,MACA,MAC8B;EAC9B,OAAO,KAAK,KAAK,KAAK,QAAQ,SAAS,OAAO,IAAI,GAAG,YAAY,oBAAoB,MAAM,KAAK,CAAC,CAAC;CACnG;CAEA,MAAc,KACb,OACA,KACA,MACa;EACb,MAAM,SAAS,MAAM,MAAM,YAAY,KAAK,aAAa;GAAE,QAAQ;GAAa,OAAO,MAAM,KAAK;EAAE,IAAI,EAEvG,UAAS,UACR,MAAM,IAAI,GAAG,KAAK,OACf,KAAA,IACA;GAAE,OAAO;IAAE,QAAQ;IAAY,QAAQ;GAAM;GAAG,WAAW;GAAW,UAAU;EAAK,EAC1F,CAAC;EACD,IAAI,OAAO,WAAW,YAAY,MAAM,OAAO;EAE/C,OAAO,OAAO;CACf;AACD"}
@@ -2,10 +2,10 @@ import { getScopedLogger, maybeStartScopedLogger } from "../../logger.scoped.js"
2
2
  import { normalizePath } from "../../path.js";
3
3
  import { isWindows } from "./platform.js";
4
4
  import { join } from "path";
5
- import { stat } from "fs/promises";
6
- import * as process from "process";
7
5
  import { exec, execFile, spawn } from "child_process";
8
6
  import { access, constants } from "fs";
7
+ import { stat } from "fs/promises";
8
+ import * as process from "process";
9
9
  //#region ../utils/src/env/node/exec.ts
10
10
  var RunError = class extends Error {
11
11
  cmd;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@gitkraken/core-gitlens",
3
3
  "description": "GitLens core — shared Git / AI / GitHub primitives for internal GitKraken consumption",
4
- "version": "0.13.0",
4
+ "version": "0.14.0",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "author": {
7
7
  "name": "GitKraken",