pi-auto-save-to-markdown 0.7.3 → 0.7.4

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
@@ -92,10 +92,10 @@ changes never touch the filename, and manually renamed files are left alone.
92
92
  ---
93
93
  title: "Fix login redirect loop"
94
94
  agent: "pi"
95
- format_version: "1.2"
95
+ format_version: "1.3"
96
96
  session_id: "d0a4f541-976d-4d1b-8e1c-30a1f2b3c4d5"
97
97
  session_key: "c2088d77"
98
- last_entry_id: "019be3a2-1f4d-7c8a-9b01-d23e45f6a7b8"
98
+ branch_last_entry_id: "019be3a2-1f4d-7c8a-9b01-d23e45f6a7b8"
99
99
  model: "z-ai/glm-5.3"
100
100
  provider: "openrouter"
101
101
  cost: 0.023401
@@ -105,8 +105,8 @@ tokens_output: 3515
105
105
  tokens_cache_read: 0
106
106
  tokens_cache_write: 0
107
107
  messages: 8
108
- created: "2026-08-29T05:05:12.000Z"
109
- updated: "2026-08-29T05:42:10.000Z"
108
+ created: "2026-08-29T13:05:12+08:00"
109
+ updated: "2026-08-29T13:42:10+08:00"
110
110
  project_root: "/Users/me/project"
111
111
  session_file: "~/.pi/agent/sessions/--Users-me-project-20260829-050500_ab12.jsonl"
112
112
  ---
package/README.zh.md CHANGED
@@ -66,10 +66,10 @@ PI_SAVE_CONVERSATION_DIR=notes/ai pi
66
66
  ---
67
67
  title: "修复登录重定向死循环"
68
68
  agent: "pi"
69
- format_version: "1.2"
69
+ format_version: "1.3"
70
70
  session_id: "d0a4f541-976d-4d1b-8e1c-30a1f2b3c4d5"
71
71
  session_key: "c2088d77"
72
- last_entry_id: "019be3a2-1f4d-7c8a-9b01-d23e45f6a7b8"
72
+ branch_last_entry_id: "019be3a2-1f4d-7c8a-9b01-d23e45f6a7b8"
73
73
  model: "z-ai/glm-5.3"
74
74
  provider: "openrouter"
75
75
  cost: 0.023401
@@ -79,8 +79,8 @@ tokens_output: 3515
79
79
  tokens_cache_read: 0
80
80
  tokens_cache_write: 0
81
81
  messages: 8
82
- created: "2026-08-29T05:05:12.000Z"
83
- updated: "2026-08-29T05:42:10.000Z"
82
+ created: "2026-08-29T13:05:12+08:00"
83
+ updated: "2026-08-29T13:42:10+08:00"
84
84
  project_root: "/Users/me/project"
85
85
  session_file: "~/.pi/agent/sessions/--Users-me-project-20260829-050500_ab12.jsonl"
86
86
  ---
package/index.ts CHANGED
@@ -25,11 +25,14 @@
25
25
  * plugins for other runtimes would write their own value), format_version
26
26
  * (the document format of the last write — additive frontmatter fields
27
27
  * never bump it; see FORMAT_VERSION), session id, session key (the
28
- * filename key), last entry id (the id of the deepest message entry on the
29
- * saved branch the file's position in the session jsonl tree at the last
30
- * write), model, provider, cumulative cost and tokens (input, output,
31
- * cache read/write), message count, created/updated timestamps, project
32
- * root and session file.
28
+ * filename key), branch last entry id (field `branch_last_entry_id` the
29
+ * id of the deepest message entry on the saved branch: the file's position
30
+ * in the session jsonl tree at the last write, scoped to this file's
31
+ * branch, not the session-wide last entry), model, provider, cumulative
32
+ * cost and tokens (input, output,
33
+ * cache read/write), message count, created/updated timestamps (tz-aware
34
+ * ISO 8601 in the local timezone with its numeric UTC offset, e.g.
35
+ * "2026-08-29T13:05:12+08:00"), project root and session file.
33
36
  * - Body format: every message block opens with a setext-H1 info header
34
37
  * (`User <span …>YYYY-MM-DD HH:MM:SS</span>` /
35
38
  * `Assistant <span …>YYYY-MM-DD HH:MM:SS · model</span>`, where the span
@@ -196,7 +199,7 @@ const SAVE_STATE_SCHEMA = "1.2";
196
199
  * the frontmatter and the document heading); additive frontmatter fields do
197
200
  * NOT bump it — they are invisible to any within-major parser.
198
201
  */
199
- const FORMAT_VERSION = "1.2";
202
+ const FORMAT_VERSION = "1.3";
200
203
 
201
204
  /**
202
205
  * Package version of this extension, read best-effort from the adjacent
@@ -248,8 +251,8 @@ interface SaveState {
248
251
  /**
249
252
  * Id of the session tree leaf at save time (getLeafId()) — may be a custom
250
253
  * state entry rather than a message; used to rank continuation candidates
251
- * on the current path. Distinct from frontmatter `last_entry_id`, which is
252
- * the deepest message entry (an id actually present in the file).
254
+ * on the current path. Distinct from frontmatter `branch_last_entry_id`,
255
+ * which is the deepest message entry (an id actually present in the file).
253
256
  */
254
257
  lastSavedEntryId: string | null;
255
258
  file: string;
@@ -388,10 +391,11 @@ interface BranchMeta {
388
391
  sessionKey: string;
389
392
  /**
390
393
  * Id of the deepest message entry on the saved branch at the last write
391
- * (field name `last_entry_id`) — the file's exact position in the
392
- * session jsonl tree. Updated on every append, like `updated`.
394
+ * (field name `branch_last_entry_id`) — the file's exact position in the
395
+ * session jsonl tree, scoped to this file's branch rather than the
396
+ * session-wide last entry. Updated on every append, like `updated`.
393
397
  */
394
- lastEntryId: string;
398
+ branchLastEntryId: string;
395
399
  model: string | null;
396
400
  provider: string | null;
397
401
  cost: number;
@@ -1097,6 +1101,24 @@ export default function (pi: ExtensionAPI) {
1097
1101
  return `"${safe.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
1098
1102
  }
1099
1103
 
1104
+ /**
1105
+ * ISO 8601 timestamp in the machine's local timezone, with the numeric
1106
+ * UTC offset appended (e.g. "2026-08-29T13:05:12+08:00"): tz-aware, so the
1107
+ * value reads as local wall-clock time without assuming the reader's
1108
+ * timezone. Legacy files written with UTC "Z" values parse identically.
1109
+ */
1110
+ function localIsoTimestamp(date: Date): string {
1111
+ const pad = (n: number) => String(n).padStart(2, "0");
1112
+ const offsetMin = -date.getTimezoneOffset();
1113
+ const sign = offsetMin >= 0 ? "+" : "-";
1114
+ const abs = Math.abs(offsetMin);
1115
+ return (
1116
+ `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}` +
1117
+ `T${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}` +
1118
+ `${sign}${pad(Math.floor(abs / 60))}:${pad(abs % 60)}`
1119
+ );
1120
+ }
1121
+
1100
1122
  function frontmatter(meta: BranchMeta): string {
1101
1123
  const lines: string[] = ["---"];
1102
1124
  lines.push(`title: ${yamlQuote(meta.title)}`);
@@ -1104,7 +1126,7 @@ export default function (pi: ExtensionAPI) {
1104
1126
  lines.push(`format_version: ${yamlQuote(FORMAT_VERSION)}`);
1105
1127
  if (meta.sessionId) lines.push(`session_id: ${yamlQuote(meta.sessionId)}`);
1106
1128
  lines.push(`session_key: ${yamlQuote(meta.sessionKey)}`);
1107
- lines.push(`last_entry_id: ${yamlQuote(meta.lastEntryId)}`);
1129
+ lines.push(`branch_last_entry_id: ${yamlQuote(meta.branchLastEntryId)}`);
1108
1130
  if (meta.model) lines.push(`model: ${yamlQuote(meta.model)}`);
1109
1131
  if (meta.provider) lines.push(`provider: ${yamlQuote(meta.provider)}`);
1110
1132
  lines.push(`cost: ${meta.cost.toFixed(6)}`);
@@ -1177,14 +1199,14 @@ export default function (pi: ExtensionAPI) {
1177
1199
  tokensCacheWrite += usage.cacheWrite ?? 0;
1178
1200
  }
1179
1201
  }
1180
- const now = new Date().toISOString();
1202
+ const now = localIsoTimestamp(new Date());
1181
1203
  return {
1182
1204
  title: displayTitle(ctx, firstUserText(pathMessages)),
1183
1205
  agent: agent ?? AGENT,
1184
1206
  sessionId: ctx.session.getSessionId(),
1185
1207
  sessionFile: ctx.session.getSessionFile() ?? null,
1186
1208
  sessionKey,
1187
- lastEntryId: pathMessages[pathMessages.length - 1].id,
1209
+ branchLastEntryId: pathMessages[pathMessages.length - 1].id,
1188
1210
  model,
1189
1211
  provider,
1190
1212
  cost,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-auto-save-to-markdown",
3
- "version": "0.7.3",
3
+ "version": "0.7.4",
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",