@zhushanwen/pi-session-reader 0.2.4 → 0.3.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zhushanwen/pi-session-reader",
3
- "version": "0.2.4",
3
+ "version": "0.3.0",
4
4
  "type": "module",
5
5
  "main": "index.ts",
6
6
  "xyz-agent": {
@@ -93,7 +93,7 @@ describe('renderOutline', () => {
93
93
  })
94
94
 
95
95
  it('2a. 降级:单行 toolSummary 过长超 perTurnBudget → 砍 toolSummary,保 userBrief 骨架', () => {
96
- // L1 行不含 assistantBrief(design §3.5 算法1 step2);budget=40, 3 turns → perTurnCharBudget≈53
96
+ // 预算紧张降级后 L1 行不含 assistantBrief(预算充足 level 0 行含);budget=40, 3 turns → perTurnCharBudget≈53
97
97
  // 行 = head + userBrief(10) + toolSummary(20 个工具名≈63 chars) → ~80 > 53 → 砍 toolSummary
98
98
  const toolCalls = Array.from({ length: 20 }, (_, k) => ({ name: `t${String(k).padStart(2, '0')}` }))
99
99
  const turns = [0, 1, 2].map((i) =>
@@ -183,7 +183,7 @@ describe('renderOutline', () => {
183
183
  // stats 完整性
184
184
  expect(result.stats.totalTurns).toBe(32)
185
185
  // totalEntries 近似(leaf+branch+orphan)不含 session header(segmentTurns 规则1 跳过);
186
- // 准确值由 M2 工具层用 ParseResult.totalEntries 覆盖。M1 验量级。
186
+ // 工具层仅覆盖 stats.totalBytes skippedLines(ParseResulttotalEntries 字段)。M1 验量级。
187
187
  expect(result.stats.totalEntries).toBeGreaterThan(1000)
188
188
  // 5.6MB 全量解析在并发/高负载下可能超 vitest 默认 5s,显式放宽
189
189
  }, 60000)
@@ -22,7 +22,7 @@ import {
22
22
  * 测试框架 vitest(禁止 node:test/tsx)。直接调 handleSessionRead(纯逻辑,agentDir 注入),
23
23
  * 传真实 `/Users/zhushanwen/.pi/agent` 作 agentDir——用本机真实历史 session 数据,无需 mock。
24
24
  *
25
- * 覆盖 7 action 主路径 + F1(find 零匹配)/F4(turn 越界)/F5(缺参)/resolveSessionId 片段等价。
25
+ * 覆盖 9 action 主路径 + F1(find 零匹配)/F4(turn 越界)/F5(缺参)/resolveSessionId 片段等价。
26
26
  *
27
27
  * 真实数据用例全部带 skipIf 守卫(CI 无本机 ~/.pi/agent → skip,不硬失败);
28
28
  * renderExtractItems F9 截断是纯 fixture,无条件跑。
@@ -782,7 +782,7 @@ describe('resolveSessionId ② sa-id 形态(w2 TC7-TC10 + CQ3)', () => {
782
782
  await handleSessionRead({ action: 'outline', session: 'sa-nonexist-9999' }, dir)
783
783
  } catch (e) {
784
784
  const msg = (e as Error).message
785
- expect(msg).toContain('可能仍在运行')
785
+ expect(msg).toContain('可能尚未落盘')
786
786
  expect(msg).toContain('action:"family"')
787
787
  expect(msg).toContain('👉')
788
788
  }
@@ -173,7 +173,6 @@ function collectRelatedRecords(
173
173
  // buildExecutionTree(IF3 核心)
174
174
  // ============================================================
175
175
 
176
- /** 建树过程的共享可变状态(避免递归函数参数爆炸,封装为 context)。 */
177
176
  /** 建树过程的共享可变状态(避免递归函数参数爆炸,封装为 context)。 */
178
177
  interface BuildContext {
179
178
  rootSessionId: string
@@ -11,7 +11,7 @@ import type { Entry } from './parser.js'
11
11
  * - SessionRef.mtime/sizeBytes:从 fileStats 取(M1 key=sessionId),取不到为 0
12
12
  * - SubagentRef.sessionId:identity entry 不含 subagent session 的 id → 占位用 entry.id
13
13
  * - SubagentRef.cwd:identity entry(custom 类型)无 cwd → 占位空串
14
- * - fileStats 的 key:M1 用 sessionId(M2 改用真实文件路径),cleanedUp 逻辑 M1/M2 通用
14
+ * - fileStats 的 key:sessionId(M2 实现沿用此 key,cleanedUp sessionId 命中),逻辑通用
15
15
  *
16
16
  * 隔代关联规则(design §3.3 D-7 Q1)见 resolveFamily 注释。
17
17
  */
@@ -24,7 +24,6 @@ export interface SessionRef {
24
24
  cwd: string
25
25
  /** fork 文件指向来源的路径(来自 header 的 parentSession,原始文件路径字符串) */
26
26
  parentSession?: string
27
- name?: string
28
27
  }
29
28
 
30
29
  export interface SubagentRef extends SessionRef {
@@ -194,7 +193,7 @@ export function buildFamilyIndex(
194
193
  mtime: stat?.mtime ?? 0,
195
194
  sizeBytes: stat?.size ?? 0,
196
195
  cwd: '', // identity entry 无 cwd;M2 从 subagent 文件 header 补
197
- // M1: fileStats key=sessionId;M2 改用 subagent 真实文件路径(SubagentRef.fileName)查 fileStats
196
+ // fileStats key=sessionId(buildFamilyFromFs header.id 写入),cleanedUp 按 sessionId 命中
198
197
  cleanedUp: !fileStats.has(ident.id),
199
198
  // U4 富字段:从 identity data 读(manifest 主/P-fallback 由 buildFamilyFromFs 组装时决定)
200
199
  task: ident.data.task,
@@ -285,7 +285,7 @@ function formatHHMM(timestamp?: string): string {
285
285
  return m ? `${m[1]}:${m[2]}` : ''
286
286
  }
287
287
 
288
- function formatBytesMarker(bytes: number): string {
288
+ export function formatBytesMarker(bytes: number): string {
289
289
  if (bytes <= 0) return ''
290
290
  if (bytes < BYTES_PER_KB) return `[${bytes}B omitted]`
291
291
  return `[${Math.round(bytes / BYTES_PER_KB)}KB omitted]`
@@ -23,7 +23,8 @@ import { resolveWorkflows } from './workflows.js'
23
23
  * 是 records/<sa-id>.json manifest(subagent 创建时写入,持久存在)。故 manifest 是孤儿/
24
24
  * cleanedUp 的来源(design §3.3 D-7 "records/*.json manifest 作孤儿补充")。
25
25
  * - workflows:M1 resolveFamily 恒返回 []。M2 在此单独读目标 session 的 workflow-state-link
26
- * custom entry → link.data.path(wf-state 文件绝对路径)→ 读该文件最后一行(最新快照)取 calls。
26
+ * custom entry → link.data.path(wf-state 文件绝对路径)→ 从文件尾向头找首个非空且
27
+ * JSON.parse 成功的行(最新快照,容错尾半截 JSON)取 calls。
27
28
  *
28
29
  * @throws sessionId 不在任意 main session header → Error(M3 tool-adapter 层转 F1 恢复指引)
29
30
  */
@@ -87,7 +88,7 @@ export async function buildFamilyFromFs(sessionId: string, agentDir: string): Pr
87
88
  // manifest 主(TC-u4-manifest-enrich):透 task/slug/model/status/sessionFile 全字段
88
89
  data = {
89
90
  rootSessionId: manifest.rootSessionId,
90
- slug: manifest.slug ?? '', // slug 兼容旧 manifest(缺→空串兑底,m0 契约)
91
+ slug: manifest.slug ?? '', // slug 兼容旧 manifest(缺→空串兜底,m0 契约)
91
92
  task: manifest.task,
92
93
  agent: manifest.agentName, // 同语义异名:manifest.agentName ↔ identity.data.agent
93
94
  model: manifest.model,
@@ -163,7 +164,7 @@ export async function buildFamilyFromFs(sessionId: string, agentDir: string): Pr
163
164
  const family = resolveFamily(sessionId, index)
164
165
 
165
166
  // ---- 6. 补 M1 占位字段(fileName / subagent cwd)+ workflows ----
166
- enrichRefs(family, sessionIdToPath, pathToRef)
167
+ enrichRefs(family, pathToRef)
167
168
  family.workflows = await resolveWorkflows(sessionId, sessionIdToPath, pathToRef)
168
169
 
169
170
  return family
@@ -330,11 +331,6 @@ function isRecordManifest(v: unknown): v is RecordManifest {
330
331
  )
331
332
  }
332
333
 
333
- /**
334
- * 扫描 subagents/<cwdSlug>/records/*.json —— subagent 注册清单。
335
- * 每个 manifest 在 subagent 创建时写入,持久存在即使 .jsonl 被 GC。坏 manifest(缺必填字段
336
- * /JSON 损坏)跳过,不中断扫描。
337
- */
338
334
  /** 读单个 manifest 文件并校验;坏 manifest(JSON 损坏/缺必填字段)返回 undefined。 */
339
335
  async function tryReadManifest(path: string): Promise<RecordManifest | undefined> {
340
336
  try {
@@ -345,6 +341,11 @@ async function tryReadManifest(path: string): Promise<RecordManifest | undefined
345
341
  }
346
342
  }
347
343
 
344
+ /**
345
+ * 扫描 subagents/<cwdSlug>/records/*.json —— subagent 注册清单。
346
+ * 每个 manifest 在 subagent 创建时写入,持久存在即使 .jsonl 被 GC。坏 manifest(缺必填字段
347
+ * /JSON 损坏)跳过,不中断扫描。
348
+ */
348
349
  export async function listRecordManifests(agentDir: string): Promise<RecordManifest[]> {
349
350
  const root = join(agentDir, 'subagents')
350
351
  const out: RecordManifest[] = []
@@ -395,11 +396,7 @@ export function extractSessionIdFromFilename(name: string): string {
395
396
  *(identity 无 cwd)。此处用已扫描的真实文件信息补全:alive 的 ref 补 fileName + cwd;
396
397
  * cleanedUp 孤儿(无文件)保持占位。
397
398
  */
398
- function enrichRefs(
399
- family: Family,
400
- sessionIdToPath: Map<string, string>,
401
- pathToRef: Map<string, SessionRef>,
402
- ): void {
399
+ function enrichRefs(family: Family, pathToRef: Map<string, SessionRef>): void {
403
400
  // sessionId → 完整 ref(含真实 fileName/cwd),由 pathToRef 反建
404
401
  const bySid = new Map<string, SessionRef>()
405
402
  for (const ref of pathToRef.values()) bySid.set(ref.sessionId, ref)
@@ -426,6 +423,4 @@ function enrichRefs(
426
423
  cwd: full.cwd || s.cwd,
427
424
  } as SubagentRef
428
425
  })
429
- // sessionIdToPath 仅用于类型完整性占位引用,避免未用警告(实际路径信息已在 pathToRef)
430
- void sessionIdToPath
431
426
  }
package/src/index.ts CHANGED
@@ -15,8 +15,6 @@ import { createSessionCommand } from './tui/session-command.js'
15
15
  * (错误直接 throw 给 pi——pi-agent-core agent-loop 只对 execute throw 置
16
16
  * isError:true,返回值里的 isError 字段被丢弃;W4 修复,锚点
17
17
  * agent-loop.js:453-483/525-547,pi 自带 bash 工具同范式)
18
- *
19
- * M4 将在此 addAutocompleteProvider(TUI # 补全,ctx.mode === 'tui' 时)。
20
18
  */
21
19
 
22
20
  // ---- TypeBox 参数 schema(design §3.4 14 字段)----
@@ -32,7 +30,7 @@ const SessionReadSchema = Type.Object({
32
30
  session: Type.Optional(
33
31
  Type.String({
34
32
  description:
35
- 'Session id, uuid fragment (e.g. e6c96), subagent record id (sa-xxx, precise lookup), or absolute .jsonl path (~ or ~/ allowed). Required for family/outline/expand/detail/search/export/workflow. # prefix auto-stripped.',
33
+ 'Session id, uuid fragment (e.g. e6c96), subagent record id (sa-xxx, precise lookup), or absolute .jsonl path (~ or ~/ allowed). Required for family/outline/expand/detail/search/export/extract/workflow. # prefix auto-stripped.',
36
34
  }),
37
35
  ),
38
36
  query: Type.Optional(
@@ -43,7 +41,7 @@ const SessionReadSchema = Type.Object({
43
41
  ),
44
42
  turns: Type.Optional(
45
43
  Type.String({
46
- description: 'detail action: turn range, "T013-T015" or "T013".',
44
+ description: 'detail/extract action: turn range, "T013-T015" or "T013".',
47
45
  }),
48
46
  ),
49
47
  turn: Type.Optional(
@@ -74,7 +72,8 @@ const SessionReadSchema = Type.Object({
74
72
  ),
75
73
  allBranches: Type.Optional(
76
74
  Type.Boolean({
77
- description: 'outline/family: include abandoned side-branches. Default false.',
75
+ description:
76
+ "outline (and export's outline section): include abandoned side-branches. Not supported by family. Default false.",
78
77
  }),
79
78
  ),
80
79
  granularity: Type.Optional(
@@ -87,7 +86,8 @@ const SessionReadSchema = Type.Object({
87
86
  ),
88
87
  source: Type.Optional(
89
88
  StringEnum(['main', 'subagent'], {
90
- description: 'find action: filter by source. "main" = sessions/, "subagent" = subagents/. Default both (merged).',
89
+ description:
90
+ 'find and session-resolving actions: filter by source. "main" = sessions/, "subagent" = subagents/. Default both (merged).',
91
91
  }),
92
92
  ),
93
93
  limit: Type.Optional(
@@ -123,8 +123,8 @@ const SessionReadSchema = Type.Object({
123
123
  // ---- guidelines(注入 LLM,design §3.4)----
124
124
 
125
125
  const guidelines = [
126
- 'Progressive reading: outline (~500 token overview) → expand (one turn) → detail (full text). Default omits toolResult/thinking noise.',
127
- 'find first to locate a session by uuid fragment or name. TUI #references are uuid fragments.',
126
+ 'Progressive reading: outline (~1500 token overview) → expand (one turn) → detail (full text). Default omits toolResult/thinking noise.',
127
+ 'find first to locate a session by uuid fragment or name. TUI #references are full uuids.',
128
128
  'outline before detail. Never read raw .jsonl files—use this tool.',
129
129
  'family traces fork parents/children, subagent sessions, and workflow runs.',
130
130
  'extract what=<type> to pull user messages / commands / files / commits / tool results across turns (optional tool= filter for commands/tool-results).',
@@ -134,7 +134,7 @@ const guidelines = [
134
134
 
135
135
  // ---- 工具 description(design §3.4,照搬措辞)----
136
136
 
137
- const description = `Read pi session files (conversation history) by semantic structure instead of raw bytes. Use when you need to review another session, trace a fork/subagent/workflow family, or locate a past decision. Nine actions: find (locate by name/uuid fragment), family (fork/subagent/workflow relations), outline (turn-level overview, ~500 token), expand (single-turn entry list), detail (full text of turns), search (full-text grep across a session), export (materialize to file), extract (pull user messages / commands / files / commits / tool results by type), workflow (workflow run overview: status/budget/steps, step call sessionId jumps to outline/detail). Progressive reading: outline → expand → detail. Do NOT use for the current session (use get_messages) or to edit sessions (pi has /resume /fork).`
137
+ const description = `Read pi session files (conversation history) by semantic structure instead of raw bytes. Use when you need to review another session, trace a fork/subagent/workflow family, or locate a past decision. Nine actions: find (locate by name/uuid fragment), family (fork/subagent/workflow relations), outline (turn-level overview, ~1500 token), expand (single-turn entry list), detail (full text of turns), search (full-text grep across a session), export (materialize to file), extract (pull user messages / commands / files / commits / tool results by type), workflow (workflow run overview: status/budget/steps, step call sessionId jumps to outline/detail). Progressive reading: outline → expand → detail. Do NOT use for the current session (the host provides current-session access) or to edit sessions (pi has /resume /fork).`
138
138
 
139
139
  /**
140
140
  * 已注册过 TUI provider/command 的 pi 实例集合。
@@ -4,7 +4,7 @@
4
4
  * 分层约定(同 scheduler/cw-tool):本文件零 pi 依赖——agentDir 作参数注入,
5
5
  * 不调用 getAgentDir(),可完全单测;pi 注册与 getAgentDir() 调用在 index.ts。
6
6
  *
7
- * 按 action 分发到 8 条路径,串联 M1 core(parser/tree/turns/render)+ M2 discovery
7
+ * 按 action 分发到 9 条路径,串联 M1 core(parser/tree/turns/render)+ M2 discovery
8
8
  *(find/subagents)。content 给 LLM 读(人类可读摘要),details 供程序化消费/测试断言。
9
9
  *
10
10
  * 错误规格 F1-F6:handler 抛 Error(message 含 👉 恢复指引),index.ts 的 execute 闭包
@@ -28,6 +28,7 @@ import {
28
28
  renderOutline,
29
29
  renderExpand,
30
30
  renderDetail,
31
+ formatBytesMarker,
31
32
  type OutlineOptions,
32
33
  type OutlineResult,
33
34
  type EntryBrief,
@@ -126,15 +127,6 @@ function shortCwd(cwd: string): string {
126
127
  return parts.slice(-SHORT_CWD_SEGMENTS).join('/')
127
128
  }
128
129
 
129
- /** bytes→KB 换算基数(formatOmitted 的 KB 显示)。 */
130
- const BYTES_PER_KB = 1024
131
-
132
- function formatOmitted(bytes: number): string {
133
- if (bytes <= 0) return ''
134
- if (bytes < BYTES_PER_KB) return `[${bytes}B omitted]`
135
- return `[${Math.round(bytes / BYTES_PER_KB)}KB omitted]`
136
- }
137
-
138
130
  /** F5 必填参数校验。 */
139
131
  function requireStr(
140
132
  val: string | undefined,
@@ -347,7 +339,7 @@ function formatSessionGc(record: RecordManifest): string {
347
339
  /** ES2(SA_ID_NO_MATCH):sa-id 无精确匹配(可能仍在运行 / 片段输入)。 */
348
340
  function formatSaIdNotFound(saId: string): string {
349
341
  return (
350
- `subagent "${saId}" 无匹配 record(可能仍在运行——终态 record completed/failed 后才写)。` +
342
+ `subagent "${saId}" 无匹配 record(若刚启动,record 可能尚未落盘)。` +
351
343
  `\n👉 用 session_read { action:"family" } 查活跃/已完成的 subagent;` +
352
344
  `若是片段输入,请用完整 sa- id 或 action:"find" 重试。`
353
345
  )
@@ -439,7 +431,7 @@ function formatOutlineText(r: OutlineResult): string {
439
431
  if (b.toolSummary) parts.push(b.toolSummary)
440
432
  // v2 O1:补 assistant 结论行(→ )让 outline 单独可决策
441
433
  if (b.assistantBrief) parts.push('→ ' + b.assistantBrief)
442
- const om = formatOmitted(b.omittedBytes)
434
+ const om = formatBytesMarker(b.omittedBytes)
443
435
  if (om) parts.push(om)
444
436
  if (b.branch) parts.push('[旁支]')
445
437
  return parts.join(' · ')
@@ -449,7 +441,7 @@ function formatOutlineText(r: OutlineResult): string {
449
441
  `${r.stats.totalTurns} turns · ${r.stats.totalEntries} entries · ~${r.tokenEstimate} tokens${
450
442
  r.stats.skippedLines > 0 ? ` · ${r.stats.skippedLines} skipped lines` : ''
451
443
  }`,
452
- r.truncated ? `[还有 ${r.truncated} 轮未显示,用 detail 或调大 budget]` : '',
444
+ r.truncated ? `[还有 ${r.truncated} 轮未显示,用 detail turns 参数看指定 turn 范围]` : '',
453
445
  ]
454
446
  .filter(Boolean)
455
447
  .join('\n')
@@ -460,7 +452,7 @@ function formatExpandText(turn: string, entries: EntryBrief[]): string {
460
452
  const lines = entries.map(
461
453
  (e) =>
462
454
  ` [${e.index}] ${e.type}${e.role ? '/' + e.role : ''} ${e.brief}${
463
- e.omittedBytes > 0 ? ' ' + formatOmitted(e.omittedBytes) : ''
455
+ e.omittedBytes > 0 ? ' ' + formatBytesMarker(e.omittedBytes) : ''
464
456
  }`,
465
457
  )
466
458
  return `${turn}\n${lines.join('\n')}`
@@ -681,7 +673,7 @@ async function doFamily(params: SessionReadParams, agentDir: string): Promise<To
681
673
  tree = await buildExecutionTree(resolved.sessionId, agentDir, resolved.fileName)
682
674
  } catch (e) {
683
675
  throw err(
684
- `构建执行树失败:${resolved.sessionId}(${e instanceof Error ? e.message : String(e)})。👉 检查 session 或用 find 重新定位,或改用 recursive:false 看 flat family 兑底。`,
676
+ `构建执行树失败:${resolved.sessionId}(${e instanceof Error ? e.message : String(e)})。👉 检查 session 或用 find 重新定位,或改用 recursive:false 看 flat family 兜底。`,
685
677
  )
686
678
  }
687
679
  return {
@@ -702,7 +694,7 @@ async function doFamily(params: SessionReadParams, agentDir: string): Promise<To
702
694
  return { content: [{ type: 'text', text: formatFamilyText(family) }], details: family }
703
695
  }
704
696
 
705
- /** outline:turn 级全貌 TOC(design §3.4 outline,~500 token)。 */
697
+ /** outline:turn 级全貌 TOC(design §3.4 outline,~1500 token;render budget 硬编码 2000)。 */
706
698
  async function doOutline(params: SessionReadParams, agentDir: string): Promise<ToolResult> {
707
699
  const resolved = await resolveSessionId(params.session, 'outline', agentDir, params.source)
708
700
  if (resolved.kind === 'multi') return disambiguate(resolved.query, resolved.candidates)
@@ -1385,8 +1377,9 @@ interface SkippedRun {
1385
1377
  * session 会抛「session not found in family index」)——resolveSessionId 已把 session 解析到
1386
1378
  * 真实文件(kind==='ok' 保证文件存在,三形态:绝对路径/sa-id 均 existsSync 校验,片段匹配
1387
1379
  * 来自实际 fs 扫描),直接用 resolved.fileName 构造单条目 sessionIdToPath 调 resolveWorkflows
1388
- *(与 buildFamilyFromFs 步骤 6 的 workflow 腿同源)。pathToRef 仅含目标 session,call 引用
1389
- * 走 sessionRefFromPath 文件名最小回退(sessionId+fileName,足够 LLM 跳 outline/detail 深读)。
1380
+ *(与 buildFamilyFromFs 步骤 6 的 workflow 腿同源)。pathToRef 传空 Map(单条目链路无其他
1381
+ * 文件可反查),call 引用 100% 走 sessionRefFromPath 文件名最小回退(sessionId+fileName
1382
+ * 足够 LLM 跳 outline/detail 深读)。
1390
1383
  *
1391
1384
  * 错误契约(C2):workflow 概览探索语义,三类错误均返回 ToolResult 不抛错。
1392
1385
  * step 的 call sessionId/sessionFile 是 LLM 跳 outline/detail 的入口(m0 resolveSessionId
@@ -1468,7 +1461,7 @@ async function doWorkflow(params: SessionReadParams, agentDir: string): Promise<
1468
1461
  if (requestedRunId !== undefined) details.requestedRunId = requestedRunId
1469
1462
  if (skippedRuns.length > 0) details.skippedRuns = skippedRuns
1470
1463
 
1471
- // 全部 run 都跳过的兑底提示(ES-wf-snapshot-read-fail 末段)
1464
+ // 全部 run 都跳过的兜底提示(ES-wf-snapshot-read-fail 末段)
1472
1465
  let text: string
1473
1466
  if (runs.length === 0) {
1474
1467
  text =
@@ -47,8 +47,6 @@ const AGE_NUM_DIGITS = 2
47
47
  export interface AutocompleteCandidate {
48
48
  /** 显示文本(满宽 label)。`${age} ${预览/name}`,如 "01m 看看 pi-session-reader..." */
49
49
  label: string
50
- /** 副信息(次列)。本 provider 不设(undefined)——触发 SelectList 满宽 label 分支 */
51
- description?: string
52
50
  /** 插入编辑器,如 "#019e6c96-aaaa-bbbb-cccc-dddddddddddd"(design D-3:完整 uuid,非名称;不显示给用户看) */
53
51
  insertText: string
54
52
  }
@@ -203,7 +201,6 @@ export function createHashAutocompleteProvider(
203
201
  const items: AutocompleteItem[] = candidates.map((c) => ({
204
202
  value: c.insertText,
205
203
  label: c.label,
206
- description: c.description,
207
204
  }))
208
205
  // prefix = 光标前匹配到的整段(# 及片段),applyCompletion 据此定位替换区间
209
206
  return { items, prefix: `#${fragment}` }
@@ -36,7 +36,7 @@ export function createSessionCommand(
36
36
  handler(args: string, ctx: ExtensionCommandContext): Promise<void>
37
37
  } {
38
38
  return {
39
- description: 'Pick a session and insert a #uuid-fragment reference into the editor.',
39
+ description: 'Pick a session and insert a #uuid reference into the editor.',
40
40
  async getArgumentCompletions(argumentPrefix) {
41
41
  const trimmed = argumentPrefix.trim()
42
42
  const all = await SessionManager.listAll(getCwdSessionDir())