@cad0p/pi-timestamps 0.1.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 cad0p
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,42 @@
1
+ # pi-timestamps
2
+
3
+ Inline UTC timestamps for every message in [pi](https://pi.dev) coding agent.
4
+
5
+ ```
6
+ user message 2026-06-05T21:44:02Z
7
+ assistant response 2026-06-05T21:44:02Z
8
+ ```
9
+
10
+ ## Features
11
+
12
+ - **Inline** — timestamps appended directly to each message
13
+ - **UTC ISO format** — `2026-06-05T21:44:02Z`
14
+ - **Dim-styled** — uses ANSI dim (`\x1b[2m`) for visual parity with thinking blocks; works with any theme
15
+ - **LLM-safe** — timestamps are stripped at the `context` event, so the model never sees them
16
+ - **Zero config** — install and it works
17
+ - **No dependencies** — pure TypeScript, no runtime deps
18
+
19
+ ## Install
20
+
21
+ ```bash
22
+ pi install npm:@cad0p/pi-timestamps
23
+ ```
24
+
25
+ Or try without installing:
26
+
27
+ ```bash
28
+ pi -e npm:@cad0p/pi-timestamps
29
+ ```
30
+
31
+ ## How it works
32
+
33
+ 1. At `message_end`, appends ` 2026-06-05T21:44:02Z` (dimmed) to user/assistant messages
34
+ 2. At `context` event (before LLM call), strips the timestamp from a deep copy — the session retains timestamps for history/replay; the LLM sees clean messages
35
+
36
+ ## Visual style
37
+
38
+ Timestamps render with terminal's native "dim" attribute (SGR 2), matching pi's thinking block style. No hardcoded colors — works with light/dark themes automatically.
39
+
40
+ ## License
41
+
42
+ MIT
@@ -0,0 +1,91 @@
1
+ /**
2
+ * message-timestamps
3
+ *
4
+ * Appends a dim-styled UTC ISO timestamp to every user/assistant message.
5
+ * Uses ANSI dim escape codes for visual parity with thinking blocks.
6
+ * Strips at `context` so the LLM never sees them.
7
+ */
8
+
9
+ import type {
10
+ AssistantMessage,
11
+ Message as PiAiMessage,
12
+ TextContent,
13
+ UserMessage,
14
+ } from '@earendil-works/pi-ai';
15
+ import type { ExtensionAPI } from '@earendil-works/pi-coding-agent';
16
+
17
+ const DIM = '\x1b[2m';
18
+ const RESET = '\x1b[0m';
19
+ // Build regex from constants to avoid control chars in regex literal
20
+ const TIMESTAMP_SUFFIX_REGEX = new RegExp(
21
+ ` ${DIM.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}Z${RESET.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}$`,
22
+ );
23
+
24
+ export function fmtUtc(ts: number): string {
25
+ return new Date(ts).toISOString().replace(/\.\d{3}Z$/, 'Z');
26
+ }
27
+
28
+ // biome-ignore lint/suspicious/noExplicitAny: content type varies by role
29
+ export function appendTimestamp(content: PiAiMessage['content'], suffix: string): any {
30
+ const styled = `${DIM}${suffix}${RESET}`;
31
+ if (typeof content === 'string') {
32
+ return `${content} ${styled}`;
33
+ }
34
+ return [...content, { type: 'text', text: ` ${styled}` }];
35
+ }
36
+
37
+ // biome-ignore lint/suspicious/noExplicitAny: content type varies by role
38
+ export function stripTimestamp(content: PiAiMessage['content']): any {
39
+ if (typeof content === 'string') {
40
+ return content.replace(TIMESTAMP_SUFFIX_REGEX, '');
41
+ }
42
+ const arr = content as TextContent[];
43
+ if (arr.length > 0 && arr[arr.length - 1].type === 'text') {
44
+ const text = arr[arr.length - 1].text;
45
+ if (TIMESTAMP_SUFFIX_REGEX.test(text)) {
46
+ return arr.slice(0, -1) as PiAiMessage['content'];
47
+ }
48
+ }
49
+ return content;
50
+ }
51
+
52
+ function getTimestamp(msg: UserMessage | AssistantMessage): number {
53
+ return msg.timestamp ?? Date.now();
54
+ }
55
+
56
+ // biome-ignore lint/suspicious/noExplicitAny: pi event types are opaque
57
+ type MessageEndEvent = { message: any };
58
+
59
+ export { DIM, RESET, TIMESTAMP_SUFFIX_REGEX };
60
+
61
+ export default function (pi: ExtensionAPI) {
62
+ // biome-ignore lint/suspicious/noExplicitAny: pi event types are opaque
63
+ pi.on('message_end' as any, async (event: MessageEndEvent, _ctx: any) => {
64
+ const msg = event.message;
65
+
66
+ if (msg.role !== 'user' && msg.role !== 'assistant') {
67
+ return;
68
+ }
69
+
70
+ const ts = getTimestamp(msg as UserMessage | AssistantMessage);
71
+ const suffix = fmtUtc(ts);
72
+
73
+ return {
74
+ message: {
75
+ ...msg,
76
+ // biome-ignore lint/suspicious/noExplicitAny: msg content is opaque
77
+ content: appendTimestamp((msg as any).content, suffix),
78
+ },
79
+ };
80
+ });
81
+
82
+ // biome-ignore lint/suspicious/noExplicitAny: pi event types are opaque
83
+ pi.on('context' as any, async (event: { messages: any[] }, _ctx: any) => {
84
+ const cleanMessages = event.messages.map((m) => ({
85
+ ...m,
86
+ // biome-ignore lint/suspicious/noExplicitAny: message content is opaque
87
+ content: stripTimestamp((m as any).content),
88
+ }));
89
+ return { messages: cleanMessages };
90
+ });
91
+ }
package/package.json ADDED
@@ -0,0 +1,51 @@
1
+ {
2
+ "name": "@cad0p/pi-timestamps",
3
+ "version": "0.1.0",
4
+ "description": "Inline UTC timestamps for every message in pi coding agent — dim-styled, LLM-safe, zero-config",
5
+ "publishConfig": {
6
+ "access": "public",
7
+ "registry": "https://registry.npmjs.org"
8
+ },
9
+ "keywords": [
10
+ "pi",
11
+ "pi-package",
12
+ "pi-extension",
13
+ "timestamps",
14
+ "chat-history",
15
+ "message-metadata"
16
+ ],
17
+ "repository": {
18
+ "type": "git",
19
+ "url": "git+https://github.com/cad0p/pi-timestamps.git"
20
+ },
21
+ "homepage": "https://github.com/cad0p/pi-timestamps#readme",
22
+ "bugs": {
23
+ "url": "https://github.com/cad0p/pi-timestamps/issues"
24
+ },
25
+ "files": [
26
+ "extensions/message-timestamps/index.ts",
27
+ "README.md",
28
+ "LICENSE"
29
+ ],
30
+ "pi": {
31
+ "extensions": [
32
+ "extensions/message-timestamps"
33
+ ]
34
+ },
35
+ "scripts": {
36
+ "lint": "bunx biome check extensions/",
37
+ "lint:fix": "bunx biome check --write extensions/",
38
+ "typecheck": "bunx tsc --noEmit",
39
+ "test": "bun test"
40
+ },
41
+ "devDependencies": {
42
+ "@biomejs/biome": "^2.4.16",
43
+ "@types/bun": "^1.3.0",
44
+ "typescript": "^5.8.0"
45
+ },
46
+ "peerDependencies": {
47
+ "@earendil-works/pi-coding-agent": ">=0.74.0",
48
+ "@earendil-works/pi-tui": ">=0.74.0"
49
+ },
50
+ "license": "MIT"
51
+ }