@pi-claudian/auto-save-to-markdown 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -67,10 +67,12 @@ cost: 0.023401
67
67
  tokens: 18745
68
68
  tokens_input: 15230
69
69
  tokens_output: 3515
70
+ tokens_cache_read: 0
71
+ tokens_cache_write: 0
70
72
  messages: 8
71
73
  created: "2026-08-29T05:05:12.000Z"
72
74
  updated: "2026-08-29T05:42:10.000Z"
73
- cwd: "/Users/me/project"
75
+ project_root: "/Users/me/project"
74
76
  session_file: "~/.pi/agent/sessions/--Users-me-project-20260829-050500_ab12.jsonl"
75
77
  ---
76
78
 
@@ -103,6 +105,12 @@ kept in a collapsible `<details>` block) and summarizes each tool call and
103
105
  result in one line, so the file stays readable while still showing what the
104
106
  agent did.
105
107
 
108
+ `cost` and the token fields cover the whole saved branch and include cached
109
+ tokens (priced at the provider's cache rates), so the totals are comparable
110
+ with provider-side accounting (e.g. OpenRouter activity). Requests that never
111
+ landed in the session tree (failed retries, other sessions sharing the same
112
+ API key) are necessarily excluded.
113
+
106
114
  ## Branch behavior
107
115
 
108
116
  Pi sessions are trees: `/tree` navigates to an earlier point and a new prompt
package/README.zh.md CHANGED
@@ -57,10 +57,12 @@ cost: 0.023401
57
57
  tokens: 18745
58
58
  tokens_input: 15230
59
59
  tokens_output: 3515
60
+ tokens_cache_read: 0
61
+ tokens_cache_write: 0
60
62
  messages: 8
61
63
  created: "2026-08-29T05:05:12.000Z"
62
64
  updated: "2026-08-29T05:42:10.000Z"
63
- cwd: "/Users/me/project"
65
+ project_root: "/Users/me/project"
64
66
  session_file: "~/.pi/agent/sessions/--Users-me-project-20260829-050500_ab12.jsonl"
65
67
  ---
66
68
 
@@ -92,6 +94,10 @@ auth 重构之后登录页一直重定向死循环……
92
94
  `<details>` 块中),每个工具调用和结果各压缩成一行摘要,既可读又能看出
93
95
  agent 做了什么。
94
96
 
97
+ `cost` 和 token 字段统计整条已保存分支,且包含缓存 token(按供应商缓存
98
+ 价格计费),因此总计可与供应商侧账单(如 OpenRouter Activity)对照。未
99
+ 进入会话树的请求(失败重试、共用同一 API key 的其他会话)不在其中。
100
+
95
101
  ## 分支行为
96
102
 
97
103
  Pi 会话是树:`/tree` 导航到更早的位置后再提问就分出新的分支。每个
package/index.ts CHANGED
@@ -18,8 +18,8 @@
18
18
  * of the deepest message entry at file creation, and <time> is the local
19
19
  * file-creation timestamp (YYYYMMDD-HHmmss).
20
20
  * - Frontmatter: title, session id, tree (branch key), model, provider,
21
- * cumulative cost and tokens, message count, created/updated timestamps,
22
- * cwd and session file.
21
+ * cumulative cost and tokens (input, output, cache read/write), message
22
+ * count, created/updated timestamps, project root and session file.
23
23
  * - Branching: each file records exactly ONE branch (the root→leaf path
24
24
  * returned by sessionManager.getBranch()). State is persisted via
25
25
  * `pi.appendEntry()` custom entries, which are part of the session tree
@@ -114,10 +114,12 @@ interface BranchMeta {
114
114
  cost: number;
115
115
  tokensInput: number;
116
116
  tokensOutput: number;
117
+ tokensCacheRead: number;
118
+ tokensCacheWrite: number;
117
119
  messages: number;
118
120
  created: string;
119
121
  updated: string;
120
- cwd: string;
122
+ projectRoot: string;
121
123
  }
122
124
 
123
125
  export default function (pi: ExtensionAPI) {
@@ -221,23 +223,32 @@ export default function (pi: ExtensionAPI) {
221
223
  // ---------- markdown rendering ----------
222
224
 
223
225
  function renderAssistant(m: AssistantMessage, t: string): string {
224
- const texts: string[] = [];
226
+ // Render blocks in their original chronological order: thinking always
227
+ // precedes the text it produced, instead of being grouped after the fact.
228
+ const header = `## Assistant · ${t}${m.model ? ` · ${m.model}` : ""}`;
229
+ const parts: string[] = [];
225
230
  const thinkings: string[] = [];
231
+ const flushThinking = () => {
232
+ if (thinkings.length) {
233
+ parts.push(
234
+ `<details>\n<summary>Thinking</summary>\n\n${thinkings.join("\n\n")}\n\n</details>`,
235
+ );
236
+ thinkings.length = 0;
237
+ }
238
+ };
226
239
  const calls: string[] = [];
227
240
  for (const b of m.content) {
228
- if (b.type === "text") texts.push(b.text);
229
- else if (b.type === "thinking") thinkings.push(b.thinking);
230
- else if (b.type === "toolCall") calls.push(`- \`${b.name}\` — ${previewArgs(b.arguments)}`);
231
- }
232
-
233
- const header = `## Assistant · ${t}${m.model ? ` · ${m.model}` : ""}`;
234
- const parts: string[] = [];
235
- if (texts.length) parts.push(texts.join("\n\n"));
236
- if (thinkings.length) {
237
- parts.push(
238
- `<details>\n<summary>Thinking</summary>\n\n${thinkings.join("\n\n")}\n\n</details>`,
239
- );
241
+ if (b.type === "text") {
242
+ flushThinking();
243
+ parts.push(b.text);
244
+ } else if (b.type === "thinking") {
245
+ thinkings.push(b.thinking);
246
+ } else if (b.type === "toolCall") {
247
+ flushThinking();
248
+ calls.push(`- \`${b.name}\` — ${previewArgs(b.arguments)}`);
249
+ }
240
250
  }
251
+ flushThinking();
241
252
  if (calls.length) parts.push(`**Tool calls**\n\n${calls.join("\n")}`);
242
253
  if (m.errorMessage) parts.push(`> Error: ${m.errorMessage.replace(/\s+/g, " ").trim()}`);
243
254
  if (parts.length === 0) parts.push("_(empty response)_");
@@ -292,13 +303,19 @@ export default function (pi: ExtensionAPI) {
292
303
  if (meta.model) lines.push(`model: ${yamlQuote(meta.model)}`);
293
304
  if (meta.provider) lines.push(`provider: ${yamlQuote(meta.provider)}`);
294
305
  lines.push(`cost: ${meta.cost.toFixed(6)}`);
295
- lines.push(`tokens: ${meta.tokensInput + meta.tokensOutput}`);
306
+ // `tokens` counts everything billed, including cached tokens, so it is
307
+ // comparable with provider-side token totals (e.g. OpenRouter activity).
308
+ lines.push(
309
+ `tokens: ${meta.tokensInput + meta.tokensOutput + meta.tokensCacheRead + meta.tokensCacheWrite}`,
310
+ );
296
311
  lines.push(`tokens_input: ${meta.tokensInput}`);
297
312
  lines.push(`tokens_output: ${meta.tokensOutput}`);
313
+ lines.push(`tokens_cache_read: ${meta.tokensCacheRead}`);
314
+ lines.push(`tokens_cache_write: ${meta.tokensCacheWrite}`);
298
315
  lines.push(`messages: ${meta.messages}`);
299
316
  lines.push(`created: ${yamlQuote(meta.created)}`);
300
317
  lines.push(`updated: ${yamlQuote(meta.updated)}`);
301
- lines.push(`cwd: ${yamlQuote(meta.cwd)}`);
318
+ lines.push(`project_root: ${yamlQuote(meta.projectRoot)}`);
302
319
  if (meta.sessionFile) lines.push(`session_file: ${yamlQuote(meta.sessionFile)}`);
303
320
  lines.push("---");
304
321
  return lines.join("\n");
@@ -323,18 +340,21 @@ export default function (pi: ExtensionAPI) {
323
340
  let cost = 0;
324
341
  let tokensInput = 0;
325
342
  let tokensOutput = 0;
343
+ let tokensCacheRead = 0;
344
+ let tokensCacheWrite = 0;
326
345
  for (const e of pathMessages) {
327
346
  const m = e.message;
347
+ const usage = m.role === "assistant" ? m.usage : m.role === "toolResult" ? m.usage : null;
328
348
  if (m.role === "assistant") {
329
349
  model = m.model;
330
350
  provider = m.provider;
331
- cost += m.usage?.cost?.total ?? 0;
332
- tokensInput += m.usage?.input ?? 0;
333
- tokensOutput += m.usage?.output ?? 0;
334
- } else if (m.role === "toolResult" && m.usage) {
335
- cost += m.usage.cost?.total ?? 0;
336
- tokensInput += m.usage.input ?? 0;
337
- tokensOutput += m.usage.output ?? 0;
351
+ }
352
+ if (usage) {
353
+ cost += usage.cost?.total ?? 0;
354
+ tokensInput += usage.input ?? 0;
355
+ tokensOutput += usage.output ?? 0;
356
+ tokensCacheRead += usage.cacheRead ?? 0;
357
+ tokensCacheWrite += usage.cacheWrite ?? 0;
338
358
  }
339
359
  }
340
360
  const now = new Date().toISOString();
@@ -348,10 +368,12 @@ export default function (pi: ExtensionAPI) {
348
368
  cost,
349
369
  tokensInput,
350
370
  tokensOutput,
371
+ tokensCacheRead,
372
+ tokensCacheWrite,
351
373
  messages: pathMessages.length,
352
374
  created: created ?? now,
353
375
  updated: now,
354
- cwd: ctx.cwd,
376
+ projectRoot: ctx.cwd,
355
377
  };
356
378
  }
357
379
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-claudian/auto-save-to-markdown",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Pi extension that automatically saves each completed conversation turn as a markdown file with YAML frontmatter, one file per session-tree branch.",
5
5
  "type": "module",
6
6
  "license": "MIT",