@agenticmail/cli 0.8.14 → 0.8.16

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.
Files changed (2) hide show
  1. package/dist/cli.js +194 -12
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -90,23 +90,198 @@ function formatEmailDate(input, now = /* @__PURE__ */ new Date()) {
90
90
  return `${rel} \u2014 ${abs}`;
91
91
  }
92
92
 
93
- // src/ui/email-card.ts
93
+ // src/ui/markdown.ts
94
94
  var ESC = "\x1B[";
95
95
  var RESET = `${ESC}0m`;
96
+ var BOLD_OFF = `${ESC}22m`;
97
+ var ITALIC_OFF = `${ESC}23m`;
98
+ var STRIKE_OFF = `${ESC}29m`;
99
+ var FG_OFF = `${ESC}39m`;
100
+ var BG_OFF = `${ESC}49m`;
101
+ var md = {
102
+ bold: (s) => `${ESC}1m${s}${BOLD_OFF}`,
103
+ italic: (s) => `${ESC}3m${s}${ITALIC_OFF}`,
104
+ // Bold + italic combo for ***text*** — apply both, reset both targeted.
105
+ boldItalic: (s) => `${ESC}1m${ESC}3m${s}${ITALIC_OFF}${BOLD_OFF}`,
106
+ strike: (s) => `${ESC}9m${s}${STRIKE_OFF}`,
107
+ dim: (s) => `${ESC}2m${s}${ESC}22m`,
108
+ underline: (s) => `${ESC}4m${s}${ESC}24m`,
109
+ // Inline code: subtle dark background + orange foreground, surrounded
110
+ // by thin-space padding so it visually pops as a token.
111
+ code: (s) => `${ESC}48;5;236m${ESC}38;5;208m ${s} ${BG_OFF}${FG_OFF}`,
112
+ // Block code: cyan foreground, no background — keeps multi-line
113
+ // indentation legible and doesn't paint the whole screen on long
114
+ // snippets the way a background would.
115
+ blockCode: (s) => `${ESC}38;5;39m${s}${FG_OFF}`,
116
+ // Headings: brand pink, bold. We use the same `38;5;205` we use for
117
+ // the project logo so the whole shell stays on one accent color.
118
+ heading: (s) => `${ESC}1m${ESC}38;5;205m${s}${FG_OFF}${BOLD_OFF}`,
119
+ // Highlight: yellow background. Used for ==text== (GFM-ish).
120
+ highlight: (s) => `${ESC}48;5;226m${ESC}30m${s}${BG_OFF}${FG_OFF}`,
121
+ // Link URL: dim parens. The visible text stays at normal weight so
122
+ // the eye still reads it as link copy.
123
+ link: (text, url) => `${text} ${md.dim("(" + url + ")")}`
124
+ };
125
+ function decodeEntities(s) {
126
+ return s.replace(/&nbsp;/g, " ").replace(/&amp;/g, "&").replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&quot;/g, '"').replace(/&#39;/g, "'").replace(/&hellip;/g, "\u2026").replace(/&mdash;/g, "\u2014").replace(/&ndash;/g, "\u2013");
127
+ }
128
+ function renderInlineMarkdown(text) {
129
+ let masked = decodeEntities(text);
130
+ const codeSpans = [];
131
+ masked = masked.replace(/`([^`\n]+)`/g, (_match, code) => {
132
+ codeSpans.push(code);
133
+ return `\0CODE${codeSpans.length - 1}\0`;
134
+ });
135
+ masked = masked.replace(/\*\*\*([^*\n]+)\*\*\*/g, (_m, s) => md.boldItalic(s));
136
+ masked = masked.replace(/___([^_\n]+)___/g, (_m, s) => md.boldItalic(s));
137
+ masked = masked.replace(/\*\*([^*\n]+)\*\*/g, (_m, s) => md.bold(s));
138
+ masked = masked.replace(/__([^_\n]+)__/g, (_m, s) => md.bold(s));
139
+ masked = masked.replace(/(^|[^\w*])\*([^*\n]+)\*(?!\w)/g, (_m, lead, s) => `${lead}${md.italic(s)}`);
140
+ masked = masked.replace(/(^|[^\w_])_([^_\n]+)_(?!\w)/g, (_m, lead, s) => `${lead}${md.italic(s)}`);
141
+ masked = masked.replace(/~~([^~\n]+)~~/g, (_m, s) => md.strike(s));
142
+ masked = masked.replace(/==([^=\n]+)==/g, (_m, s) => md.highlight(s));
143
+ masked = masked.replace(/!\[([^\]]*)\]\((https?:\/\/[^)\s]+)\)/g, (_m, alt, u) => {
144
+ const label = alt ? `\u{1F5BC} ${alt}` : "\u{1F5BC} image";
145
+ return `[${label}] ${md.dim("(" + u + ")")}`;
146
+ });
147
+ masked = masked.replace(/\[([^\]]+)\]\((https?:\/\/[^)\s]+)\)/g, (_m, t, u) => md.link(t, u));
148
+ masked = masked.replace(/<(https?:\/\/[^>\s]+)>/g, (_m, u) => md.dim(u));
149
+ masked = masked.replace(/\u0000CODE(\d+)\u0000/g, (_m, idx) => md.code(codeSpans[Number(idx)] ?? ""));
150
+ return masked;
151
+ }
152
+ var TABLE_SEPARATOR_RE = /^\s*\|?(\s*:?-{3,}:?\s*\|)+(\s*:?-{3,}:?\s*)?\s*$/;
153
+ var TABLE_ROW_RE = /^\s*\|.*\|\s*$/;
154
+ function createMarkdownLineRenderer() {
155
+ let inCodeBlock = false;
156
+ let inIndentedCode = false;
157
+ return {
158
+ renderLine(line) {
159
+ const fence = line.match(/^\s*```([\w+-]*)\s*$/);
160
+ if (fence) {
161
+ inCodeBlock = !inCodeBlock;
162
+ return md.dim(fence[1] ? `\u25BE ${fence[1]}` : "\u25BE code");
163
+ }
164
+ if (inCodeBlock) {
165
+ return md.blockCode(line);
166
+ }
167
+ if (/^( {4,}|\t)/.test(line) && !/^(?:\s{4,}|\t)(?:[-*+]|\d+[.)])\s/.test(line)) {
168
+ inIndentedCode = true;
169
+ return md.blockCode(line.replace(/^( {4}|\t)/, ""));
170
+ }
171
+ if (inIndentedCode && line.trim() !== "") {
172
+ inIndentedCode = false;
173
+ }
174
+ if (TABLE_SEPARATOR_RE.test(line) && line.includes("|")) {
175
+ const rendered = line.replace(/-{3,}/g, (m) => "\u2500".repeat(m.length)).replace(/\|/g, md.dim("\u2502")).replace(/:/g, md.dim(":"));
176
+ return rendered;
177
+ }
178
+ if (TABLE_ROW_RE.test(line)) {
179
+ const cells = line.trim().slice(1, -1).split("|").map((c3) => renderInlineMarkdown(c3.trim()));
180
+ return `${md.dim("\u2502")} ${cells.join(` ${md.dim("\u2502")} `)} ${md.dim("\u2502")}`;
181
+ }
182
+ if (/^\s*(?:-{3,}|_{3,}|\*{3,})\s*$/.test(line)) {
183
+ return md.dim("\u2500".repeat(40));
184
+ }
185
+ const heading2 = line.match(/^(#{1,6})\s+(.+?)\s*#*\s*$/);
186
+ if (heading2) {
187
+ const level = heading2[1].length;
188
+ const text = renderInlineMarkdown(heading2[2]);
189
+ if (level === 1) return md.heading(`\u258C ${text}`);
190
+ if (level === 2) return md.heading(`\u258C ${text}`);
191
+ return md.heading(text);
192
+ }
193
+ const task = line.match(/^(\s*)([-*+])\s+\[([ xX])\]\s+(.*)$/);
194
+ if (task) {
195
+ const indent = task[1];
196
+ const checked = task[3] !== " ";
197
+ const content = renderInlineMarkdown(task[4]);
198
+ const box = checked ? md.dim("\u2611") : "\u2610";
199
+ return checked ? `${indent}${box} ${md.dim(md.strike(content))}` : `${indent}${box} ${content}`;
200
+ }
201
+ const bullet = line.match(/^(\s*)([-*+])\s+(.*)$/);
202
+ if (bullet) {
203
+ const indent = bullet[1];
204
+ const content = renderInlineMarkdown(bullet[3]);
205
+ return `${indent}${md.dim("\u2022")} ${content}`;
206
+ }
207
+ const numbered = line.match(/^(\s*)(\d+)[.)]\s+(.*)$/);
208
+ if (numbered) {
209
+ const indent = numbered[1];
210
+ const num = numbered[2];
211
+ const content = renderInlineMarkdown(numbered[3]);
212
+ return `${indent}${md.dim(num + ".")} ${content}`;
213
+ }
214
+ return renderInlineMarkdown(line);
215
+ }
216
+ };
217
+ }
218
+
219
+ // src/ui/email-card.ts
220
+ var ESC2 = "\x1B[";
221
+ var RESET2 = `${ESC2}0m`;
96
222
  var ansi = {
97
- reset: RESET,
98
- bold: (s) => `${ESC}1m${s}${RESET}`,
99
- dim: (s) => `${ESC}90m${s}${RESET}`,
100
- cyan: (s) => `${ESC}36m${s}${RESET}`,
101
- magenta: (s) => `${ESC}35m${s}${RESET}`,
102
- yellow: (s) => `${ESC}33m${s}${RESET}`,
103
- green: (s) => `${ESC}32m${s}${RESET}`,
104
- red: (s) => `${ESC}31m${s}${RESET}`,
223
+ reset: RESET2,
224
+ bold: (s) => `${ESC2}1m${s}${RESET2}`,
225
+ italic: (s) => `${ESC2}3m${s}${RESET2}`,
226
+ dim: (s) => `${ESC2}90m${s}${RESET2}`,
227
+ cyan: (s) => `${ESC2}36m${s}${RESET2}`,
228
+ magenta: (s) => `${ESC2}35m${s}${RESET2}`,
229
+ yellow: (s) => `${ESC2}33m${s}${RESET2}`,
230
+ green: (s) => `${ESC2}32m${s}${RESET2}`,
231
+ red: (s) => `${ESC2}31m${s}${RESET2}`,
105
232
  // The brand pink we use for the project logo. `38;5;205` is xterm-256
106
233
  // hot pink — close to the bow in the logo and readable on both dark
107
234
  // and light terminals.
108
- pink: (s) => `${ESC}38;5;205m${s}${RESET}`
235
+ pink: (s) => `${ESC2}38;5;205m${s}${RESET2}`
109
236
  };
237
+ var ON_WROTE_RE = /^((?:>\s*)*)On (\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?Z), (.+?) wrote:$/;
238
+ function splitQuotePrefix(line) {
239
+ let i = 0;
240
+ let depth = 0;
241
+ while (i < line.length) {
242
+ if (line[i] === ">") {
243
+ depth++;
244
+ i++;
245
+ while (i < line.length && line[i] === " ") i++;
246
+ } else {
247
+ break;
248
+ }
249
+ }
250
+ return { depth, content: line.slice(i) };
251
+ }
252
+ var QUOTE_COLORS = [
253
+ ansi.cyan,
254
+ ansi.magenta,
255
+ ansi.yellow
256
+ ];
257
+ function renderQuoteStripe(depth) {
258
+ if (depth <= 0) return "";
259
+ const bars = [];
260
+ for (let i = 0; i < depth; i++) {
261
+ const color = QUOTE_COLORS[i] ?? ansi.dim;
262
+ bars.push(color("\u258E"));
263
+ }
264
+ return bars.join("") + " ";
265
+ }
266
+ function renderBodyLine(line, now, mdRenderer) {
267
+ if (line.trim() === "") return line;
268
+ const m = line.match(ON_WROTE_RE);
269
+ if (m) {
270
+ const prefixRaw = m[1] ?? "";
271
+ const iso = m[2];
272
+ const sender = m[3];
273
+ const parsed = new Date(iso);
274
+ const dateStr = Number.isNaN(parsed.getTime()) ? iso : formatEmailDate(parsed, now);
275
+ const depth2 = (prefixRaw.match(/>/g) ?? []).length;
276
+ const stripe = renderQuoteStripe(depth2);
277
+ const inner = `On ${dateStr}, ${sender} wrote:`;
278
+ return `${stripe}${ansi.dim(ansi.italic(inner))}`;
279
+ }
280
+ const { depth, content } = splitQuotePrefix(line);
281
+ const rendered = mdRenderer.renderLine(content);
282
+ if (depth === 0) return rendered;
283
+ return `${renderQuoteStripe(depth)}${rendered}`;
284
+ }
110
285
  function formatAddress(a) {
111
286
  if (!a) return "";
112
287
  if (a.name && a.address) return `${a.name} <${a.address}>`;
@@ -175,8 +350,9 @@ function renderEmailCard(msg, opts = {}) {
175
350
  let body = msg.text ?? "";
176
351
  if (!body && msg.html) body = stripHtmlForTerminal(msg.html);
177
352
  if (body) {
353
+ const mdRenderer = createMarkdownLineRenderer();
178
354
  for (const line of body.split("\n")) {
179
- out.push(` ${line}`);
355
+ out.push(` ${renderBodyLine(line, now, mdRenderer)}`);
180
356
  }
181
357
  } else {
182
358
  out.push(` ${ansi.dim("(no body content)")}`);
@@ -207,8 +383,14 @@ var c = {
207
383
  red: (s) => `\x1B[31m${s}\x1B[0m`,
208
384
  yellow: (s) => `\x1B[33m${s}\x1B[0m`,
209
385
  cyan: (s) => `\x1B[36m${s}\x1B[0m`,
386
+ blue: (s) => `\x1B[34m${s}\x1B[0m`,
387
+ magenta: (s) => `\x1B[35m${s}\x1B[0m`,
210
388
  dim: (s) => `\x1B[90m${s}\x1B[0m`,
211
- bold: (s) => `\x1B[1m${s}\x1B[0m`
389
+ bold: (s) => `\x1B[1m${s}\x1B[0m`,
390
+ // Brand pink — xterm-256 hot-pink background (205) with white text (97).
391
+ // Mirrors the pinkBg helper in cli.ts; the shell uses it for the "🎀
392
+ // AgenticMail is running" welcome banner and any future brand marks.
393
+ pinkBg: (s) => `\x1B[48;5;205m\x1B[97m${s}\x1B[0m`
212
394
  };
213
395
  var dotColors = [
214
396
  (s) => `\x1B[35m${s}\x1B[0m`,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agenticmail/cli",
3
- "version": "0.8.14",
3
+ "version": "0.8.16",
4
4
  "description": "Email and SMS infrastructure for AI agents — the first platform to give agents real email addresses and phone numbers",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",