@molecule/api-agent-transcript-markdown-chat 1.0.0 → 1.0.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 ADDED
@@ -0,0 +1,167 @@
1
+ <!--
2
+ AUTO-GENERATED — DO NOT EDIT THIS FILE.
3
+ Generated by `mlcl sync-docs` from the package's src/index.ts JSDoc + mlcl/registry.json.
4
+ Edits here are overwritten on the next commit (molecule's pre-commit hook regenerates).
5
+ To change this document, edit the module-level JSDoc in src/index.ts.
6
+ Generated: 2026-09-29T16:49:58.176Z
7
+ -->
8
+
9
+ # @molecule/api-agent-transcript-markdown-chat
10
+
11
+ > **Auto-generated, AI-first package reference** for the [molecule.dev](https://molecule.dev) ecosystem.
12
+ > It is written to be read by coding agents as much as by people, and is generated from this
13
+ > package's source — edit `src/index.ts` JSDoc, not this file.
14
+
15
+ Generic Markdown chat reader for `@molecule/api-agent-transcript`.
16
+
17
+ The reader of last resort: a plain Markdown or text chat whose turns start
18
+ with a speaker marker — a heading (`## User` / `## Assistant`), a bold
19
+ label (`**User:** …`) or a plain label (`User: …`). Use it for chats saved
20
+ by hand, copied out of a chat web app, or written by a harness that has no
21
+ reader of its own.
22
+
23
+ ## Quick Start
24
+
25
+ ```typescript
26
+ import { readTranscript, setProvider } from '@molecule/api-agent-transcript'
27
+ import { provider } from '@molecule/api-agent-transcript-markdown-chat'
28
+
29
+ setProvider(provider)
30
+ const session = readTranscript({
31
+ text: '**User:** Add a README.\n\n**Assistant:** Added README.md with a short overview.',
32
+ fileName: 'chat.md',
33
+ })
34
+ // session.turns → [{ role: 'user', … }, { role: 'assistant', … }]
35
+ ```
36
+
37
+ ## Type
38
+
39
+ `provider`
40
+
41
+ ## Installation
42
+
43
+ ```bash
44
+ npm install @molecule/api-agent-transcript-markdown-chat @molecule/api-agent-transcript
45
+ ```
46
+
47
+ ## API
48
+
49
+ ### Functions
50
+
51
+ #### `chatStyleOf(text)`
52
+
53
+ The marker style a text uses: the style of its first marker, provided the
54
+ text has at least one user and one assistant marker in that style.
55
+
56
+ ```typescript
57
+ function chatStyleOf(text: string): Style | null
58
+ ```
59
+
60
+ - `text` — The text.
61
+
62
+ **Returns:** The style, or `null` when the text is not a chat.
63
+
64
+ #### `looksLikeMarkdownChat(text)`
65
+
66
+ Whether a text is a Markdown / plain-text chat.
67
+
68
+ ```typescript
69
+ function looksLikeMarkdownChat(text: string): boolean
70
+ ```
71
+
72
+ - `text` — The file's text.
73
+
74
+ **Returns:** True when it has user and assistant markers in one style.
75
+
76
+ #### `readMarkdownChat(text)`
77
+
78
+ Read a Markdown / plain-text chat.
79
+
80
+ ```typescript
81
+ function readMarkdownChat(text: string): AgentSession
82
+ ```
83
+
84
+ - `text` — The chat's text.
85
+
86
+ **Returns:** The normalized session.
87
+
88
+ ### Constants
89
+
90
+ #### `ASSISTANT_NAMES`
91
+
92
+ Names that mark the assistant.
93
+
94
+ ```typescript
95
+ const ASSISTANT_NAMES: readonly [
96
+ 'assistant',
97
+ 'ai',
98
+ 'bot',
99
+ 'model',
100
+ 'agent',
101
+ 'claude',
102
+ 'chatgpt',
103
+ 'gpt',
104
+ 'gemini',
105
+ 'copilot',
106
+ 'cursor',
107
+ ]
108
+ ```
109
+
110
+ #### `provider`
111
+
112
+ Reads a plain Markdown or text chat with User / Assistant speaker markers.
113
+ Compose it LAST: every harness-specific reader should get the first look.
114
+
115
+ ```typescript
116
+ const provider: AgentTranscriptReader
117
+ ```
118
+
119
+ #### `USER_NAMES`
120
+
121
+ Names that mark the person.
122
+
123
+ ```typescript
124
+ const USER_NAMES: readonly ['user', 'human', 'you', 'me']
125
+ ```
126
+
127
+ ## Core Interface
128
+
129
+ Implements `@molecule/api-agent-transcript` interface.
130
+
131
+ ## Bond Wiring
132
+
133
+ Setup function to register this provider with the core interface:
134
+
135
+ ```typescript
136
+ import { setProvider } from '@molecule/api-agent-transcript'
137
+ import { provider } from '@molecule/api-agent-transcript-markdown-chat'
138
+
139
+ export function setupAgentTranscriptMarkdownChat(): void {
140
+ setProvider(provider)
141
+ }
142
+ ```
143
+
144
+ ## Injection Notes
145
+
146
+ ### Requirements
147
+
148
+ Peer dependencies:
149
+
150
+ - `@molecule/api-agent-transcript` ^1.0.0
151
+
152
+ ### Runtime Dependencies
153
+
154
+ - `@molecule/api-agent-transcript`
155
+
156
+ - **Compose it last.** `@molecule/api-agent-transcript-autodetect` tries
157
+ every harness reader first; this one only sees files none of them claimed.
158
+ - It still refuses anything without at least one user marker AND one
159
+ assistant marker, so a document that merely mentions "User:" is not a chat.
160
+ - User names: User, Human, You, Me. Assistant names: Assistant, AI, Bot,
161
+ Model, Agent, Claude, ChatGPT, GPT, Gemini, Copilot, Cursor. Case does not
162
+ matter; a trailing colon is optional for headings.
163
+ - Only the style of the file's first marker splits turns, and markers
164
+ inside fenced code blocks are ignored — so a `Model:` line in a reply
165
+ does not break a chat written with `##` headings.
166
+ - A chat names no model, no times and no file writes; those fields stay
167
+ empty. `harness` is `Markdown chat`.
package/dist/chat.d.ts ADDED
@@ -0,0 +1,47 @@
1
+ /**
2
+ * A plain Markdown or text chat: turns introduced by a speaker marker in one
3
+ * of three styles — a heading (`## User`), a bold label (`**User:**`, text
4
+ * may follow on the same line) or a plain label at the start of a line
5
+ * (`User: …`).
6
+ *
7
+ * This is the reader of last resort, for chats saved by hand, by a harness
8
+ * with no reader of its own, or by a chat web app's copy button. It never
9
+ * guesses at a format: the file must hold at least one user marker AND one
10
+ * assistant marker, and only the style of the file's first marker splits
11
+ * turns, so a `Model:` line or `## Assistant notes` heading inside a reply
12
+ * cannot start a new turn in a file that uses another style. Markers inside
13
+ * fenced code blocks are ignored.
14
+ *
15
+ * @module
16
+ */
17
+ import type { AgentSession } from '@molecule/api-agent-transcript';
18
+ /** Names that mark the person. */
19
+ export declare const USER_NAMES: readonly ["user", "human", "you", "me"];
20
+ /** Names that mark the assistant. */
21
+ export declare const ASSISTANT_NAMES: readonly ["assistant", "ai", "bot", "model", "agent", "claude", "chatgpt", "gpt", "gemini", "copilot", "cursor"];
22
+ type Style = 'heading' | 'bold' | 'plain';
23
+ /**
24
+ * The marker style a text uses: the style of its first marker, provided the
25
+ * text has at least one user and one assistant marker in that style.
26
+ *
27
+ * @param text - The text.
28
+ * @returns The style, or `null` when the text is not a chat.
29
+ */
30
+ export declare function chatStyleOf(text: string): Style | null;
31
+ /**
32
+ * Whether a text is a Markdown / plain-text chat.
33
+ *
34
+ * @param text - The file's text.
35
+ * @returns True when it has user and assistant markers in one style.
36
+ */
37
+ export declare function looksLikeMarkdownChat(text: string): boolean;
38
+ /**
39
+ * Read a Markdown / plain-text chat.
40
+ *
41
+ * @param text - The chat's text.
42
+ * @returns The normalized session.
43
+ * @throws {Error} When the text is not a chat.
44
+ */
45
+ export declare function readMarkdownChat(text: string): AgentSession;
46
+ export {};
47
+ //# sourceMappingURL=chat.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chat.d.ts","sourceRoot":"","sources":["../src/chat.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EAAE,YAAY,EAA4B,MAAM,gCAAgC,CAAA;AAE5F,kCAAkC;AAClC,eAAO,MAAM,UAAU,yCAA0C,CAAA;AAEjE,qCAAqC;AACrC,eAAO,MAAM,eAAe,kHAYlB,CAAA;AAEV,KAAK,KAAK,GAAG,SAAS,GAAG,MAAM,GAAG,OAAO,CAAA;AA2DzC;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,KAAK,GAAG,IAAI,CAMtD;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAE3D;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,CA4B3D"}
package/dist/chat.js ADDED
@@ -0,0 +1,148 @@
1
+ /**
2
+ * A plain Markdown or text chat: turns introduced by a speaker marker in one
3
+ * of three styles — a heading (`## User`), a bold label (`**User:**`, text
4
+ * may follow on the same line) or a plain label at the start of a line
5
+ * (`User: …`).
6
+ *
7
+ * This is the reader of last resort, for chats saved by hand, by a harness
8
+ * with no reader of its own, or by a chat web app's copy button. It never
9
+ * guesses at a format: the file must hold at least one user marker AND one
10
+ * assistant marker, and only the style of the file's first marker splits
11
+ * turns, so a `Model:` line or `## Assistant notes` heading inside a reply
12
+ * cannot start a new turn in a file that uses another style. Markers inside
13
+ * fenced code blocks are ignored.
14
+ *
15
+ * @module
16
+ */
17
+ /** Names that mark the person. */
18
+ export const USER_NAMES = ['user', 'human', 'you', 'me'];
19
+ /** Names that mark the assistant. */
20
+ export const ASSISTANT_NAMES = [
21
+ 'assistant',
22
+ 'ai',
23
+ 'bot',
24
+ 'model',
25
+ 'agent',
26
+ 'claude',
27
+ 'chatgpt',
28
+ 'gpt',
29
+ 'gemini',
30
+ 'copilot',
31
+ 'cursor',
32
+ ];
33
+ const NAME = `(${[...USER_NAMES, ...ASSISTANT_NAMES].join('|')})`;
34
+ const PATTERNS = {
35
+ heading: new RegExp(`^#{1,4}\\s+${NAME}\\s*:?\\s*$`, 'i'),
36
+ bold: new RegExp(`^\\*\\*${NAME}(?::\\*\\*|\\*\\*:?)\\s*(.*)$`, 'i'),
37
+ plain: new RegExp(`^${NAME}:\\s*(.*)$`, 'i'),
38
+ };
39
+ const STYLES = ['heading', 'bold', 'plain'];
40
+ /**
41
+ * The speaker marker on a line, if any.
42
+ *
43
+ * @param line - The line.
44
+ * @param only - Only this style counts, when given.
45
+ * @returns The marker, or `null`.
46
+ */
47
+ function markerOf(line, only) {
48
+ for (const style of only ? [only] : STYLES) {
49
+ const m = PATTERNS[style].exec(line);
50
+ if (!m)
51
+ continue;
52
+ const name = m[1].toLowerCase();
53
+ const role = USER_NAMES.includes(name)
54
+ ? 'user'
55
+ : 'assistant';
56
+ return { style, role, rest: (m[2] ?? '').trim() };
57
+ }
58
+ return null;
59
+ }
60
+ /**
61
+ * The lines outside fenced code blocks, with their fence state.
62
+ *
63
+ * @param text - The text.
64
+ * @returns Each line and whether it is inside a fence.
65
+ */
66
+ function withFences(text) {
67
+ let fence = null;
68
+ return text
69
+ .replace(/\r\n?/g, '\n')
70
+ .split('\n')
71
+ .map((line) => {
72
+ const f = /^\s*(`{3,}|~{3,})/.exec(line);
73
+ const wasFenced = fence !== null;
74
+ if (f) {
75
+ if (fence === null)
76
+ fence = f[1][0];
77
+ else if (f[1][0] === fence)
78
+ fence = null;
79
+ }
80
+ return { line, fenced: wasFenced || !!f };
81
+ });
82
+ }
83
+ /**
84
+ * The marker style a text uses: the style of its first marker, provided the
85
+ * text has at least one user and one assistant marker in that style.
86
+ *
87
+ * @param text - The text.
88
+ * @returns The style, or `null` when the text is not a chat.
89
+ */
90
+ export function chatStyleOf(text) {
91
+ const lines = withFences(text).filter((l) => !l.fenced);
92
+ const first = lines.map((l) => markerOf(l.line)).find(Boolean);
93
+ if (!first)
94
+ return null;
95
+ const roles = new Set(lines.map((l) => markerOf(l.line, first.style)?.role).filter(Boolean));
96
+ return roles.has('user') && roles.has('assistant') ? first.style : null;
97
+ }
98
+ /**
99
+ * Whether a text is a Markdown / plain-text chat.
100
+ *
101
+ * @param text - The file's text.
102
+ * @returns True when it has user and assistant markers in one style.
103
+ */
104
+ export function looksLikeMarkdownChat(text) {
105
+ return chatStyleOf(text) !== null;
106
+ }
107
+ /**
108
+ * Read a Markdown / plain-text chat.
109
+ *
110
+ * @param text - The chat's text.
111
+ * @returns The normalized session.
112
+ * @throws {Error} When the text is not a chat.
113
+ */
114
+ export function readMarkdownChat(text) {
115
+ const style = chatStyleOf(text);
116
+ if (!style)
117
+ throw new Error('Not a Markdown chat: no user and assistant speaker markers.');
118
+ const turns = [];
119
+ let current = null;
120
+ const close = () => {
121
+ if (!current)
122
+ return;
123
+ const body = current.body.join('\n').trim();
124
+ if (body) {
125
+ const last = turns[turns.length - 1];
126
+ if (last && last.role === current.role)
127
+ last.text = `${last.text}\n\n${body}`;
128
+ else
129
+ turns.push({ role: current.role, text: body, files: [] });
130
+ }
131
+ current = null;
132
+ };
133
+ for (const { line, fenced } of withFences(text)) {
134
+ const m = fenced ? null : markerOf(line, style);
135
+ if (m) {
136
+ close();
137
+ current = { role: m.role, body: m.rest ? [m.rest] : [] };
138
+ }
139
+ else if (current) {
140
+ current.body.push(line);
141
+ }
142
+ }
143
+ close();
144
+ // A `---` rule between turns belongs to the file's layout, not the message.
145
+ for (const t of turns)
146
+ t.text = t.text.replace(/\n+-{3,}\s*$/, '').trim();
147
+ return { format: 'markdown-chat', harness: 'Markdown chat', turns };
148
+ }
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Generic Markdown chat reader for `@molecule/api-agent-transcript`.
3
+ *
4
+ * The reader of last resort: a plain Markdown or text chat whose turns start
5
+ * with a speaker marker — a heading (`## User` / `## Assistant`), a bold
6
+ * label (`**User:** …`) or a plain label (`User: …`). Use it for chats saved
7
+ * by hand, copied out of a chat web app, or written by a harness that has no
8
+ * reader of its own.
9
+ *
10
+ * @example
11
+ * ```typescript
12
+ * import { readTranscript, setProvider } from '@molecule/api-agent-transcript'
13
+ * import { provider } from '@molecule/api-agent-transcript-markdown-chat'
14
+ *
15
+ * setProvider(provider)
16
+ * const session = readTranscript({
17
+ * text: '**User:** Add a README.\n\n**Assistant:** Added README.md with a short overview.',
18
+ * fileName: 'chat.md',
19
+ * })
20
+ * // session.turns → [{ role: 'user', … }, { role: 'assistant', … }]
21
+ * ```
22
+ *
23
+ * @remarks
24
+ * - **Compose it last.** `@molecule/api-agent-transcript-autodetect` tries
25
+ * every harness reader first; this one only sees files none of them claimed.
26
+ * - It still refuses anything without at least one user marker AND one
27
+ * assistant marker, so a document that merely mentions "User:" is not a chat.
28
+ * - User names: User, Human, You, Me. Assistant names: Assistant, AI, Bot,
29
+ * Model, Agent, Claude, ChatGPT, GPT, Gemini, Copilot, Cursor. Case does not
30
+ * matter; a trailing colon is optional for headings.
31
+ * - Only the style of the file's first marker splits turns, and markers
32
+ * inside fenced code blocks are ignored — so a `Model:` line in a reply
33
+ * does not break a chat written with `##` headings.
34
+ * - A chat names no model, no times and no file writes; those fields stay
35
+ * empty. `harness` is `Markdown chat`.
36
+ *
37
+ * @module
38
+ */
39
+ export * from './browser-guard.js';
40
+ export * from './chat.js';
41
+ export * from './provider.js';
42
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AAEH,cAAc,oBAAoB,CAAA;AAClC,cAAc,WAAW,CAAA;AACzB,cAAc,eAAe,CAAA"}
package/dist/index.js ADDED
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Generic Markdown chat reader for `@molecule/api-agent-transcript`.
3
+ *
4
+ * The reader of last resort: a plain Markdown or text chat whose turns start
5
+ * with a speaker marker — a heading (`## User` / `## Assistant`), a bold
6
+ * label (`**User:** …`) or a plain label (`User: …`). Use it for chats saved
7
+ * by hand, copied out of a chat web app, or written by a harness that has no
8
+ * reader of its own.
9
+ *
10
+ * @example
11
+ * ```typescript
12
+ * import { readTranscript, setProvider } from '@molecule/api-agent-transcript'
13
+ * import { provider } from '@molecule/api-agent-transcript-markdown-chat'
14
+ *
15
+ * setProvider(provider)
16
+ * const session = readTranscript({
17
+ * text: '**User:** Add a README.\n\n**Assistant:** Added README.md with a short overview.',
18
+ * fileName: 'chat.md',
19
+ * })
20
+ * // session.turns → [{ role: 'user', … }, { role: 'assistant', … }]
21
+ * ```
22
+ *
23
+ * @remarks
24
+ * - **Compose it last.** `@molecule/api-agent-transcript-autodetect` tries
25
+ * every harness reader first; this one only sees files none of them claimed.
26
+ * - It still refuses anything without at least one user marker AND one
27
+ * assistant marker, so a document that merely mentions "User:" is not a chat.
28
+ * - User names: User, Human, You, Me. Assistant names: Assistant, AI, Bot,
29
+ * Model, Agent, Claude, ChatGPT, GPT, Gemini, Copilot, Cursor. Case does not
30
+ * matter; a trailing colon is optional for headings.
31
+ * - Only the style of the file's first marker splits turns, and markers
32
+ * inside fenced code blocks are ignored — so a `Model:` line in a reply
33
+ * does not break a chat written with `##` headings.
34
+ * - A chat names no model, no times and no file writes; those fields stay
35
+ * empty. `harness` is `Markdown chat`.
36
+ *
37
+ * @module
38
+ */
39
+ export * from './browser-guard.js';
40
+ export * from './chat.js';
41
+ export * from './provider.js';
@@ -0,0 +1,12 @@
1
+ /**
2
+ * The generic Markdown / plain-text chat reader.
3
+ *
4
+ * @module
5
+ */
6
+ import type { AgentTranscriptReader } from '@molecule/api-agent-transcript';
7
+ /**
8
+ * Reads a plain Markdown or text chat with User / Assistant speaker markers.
9
+ * Compose it LAST: every harness-specific reader should get the first look.
10
+ */
11
+ export declare const provider: AgentTranscriptReader;
12
+ //# sourceMappingURL=provider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../src/provider.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAEV,qBAAqB,EAEtB,MAAM,gCAAgC,CAAA;AAIvC;;;GAGG;AACH,eAAO,MAAM,QAAQ,EAAE,qBActB,CAAA"}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * The generic Markdown / plain-text chat reader.
3
+ *
4
+ * @module
5
+ */
6
+ import { looksLikeMarkdownChat, readMarkdownChat } from './chat.js';
7
+ /**
8
+ * Reads a plain Markdown or text chat with User / Assistant speaker markers.
9
+ * Compose it LAST: every harness-specific reader should get the first look.
10
+ */
11
+ export const provider = {
12
+ format: 'markdown-chat',
13
+ label: 'Markdown chat',
14
+ detect(input) {
15
+ return looksLikeMarkdownChat(input.text);
16
+ },
17
+ read(input) {
18
+ if (!looksLikeMarkdownChat(input.text)) {
19
+ throw new Error(`Not a Markdown chat${input.fileName ? ` (${input.fileName})` : ''}: expected User and Assistant speaker markers (\`## User\`, \`**User:**\` or \`User:\`).`);
20
+ }
21
+ return readMarkdownChat(input.text);
22
+ },
23
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@molecule/api-agent-transcript-markdown-chat",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "description": "A last-resort transcript reader for plain Markdown or text chats with clear User / Assistant speaker markers",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",