hypercore-cli 1.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.
Files changed (84) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +110 -0
  3. package/dist/api-XGC7D5AW.js +162 -0
  4. package/dist/auth-DNQWYQKT.js +21 -0
  5. package/dist/background-2EGCAAQH.js +14 -0
  6. package/dist/backlog-Q2NZCLNY.js +24 -0
  7. package/dist/chunk-2CMSCWQW.js +162 -0
  8. package/dist/chunk-2LJ2DVEB.js +167 -0
  9. package/dist/chunk-3RPFCQKJ.js +288 -0
  10. package/dist/chunk-43OLRXM5.js +263 -0
  11. package/dist/chunk-4DVYJAJL.js +57 -0
  12. package/dist/chunk-6OL3GA3P.js +173 -0
  13. package/dist/chunk-AUHU7ALH.js +2023 -0
  14. package/dist/chunk-B6A2AKLN.js +139 -0
  15. package/dist/chunk-BE46C7JW.js +46 -0
  16. package/dist/chunk-CUVAUOXL.js +58 -0
  17. package/dist/chunk-GH7E2OJE.js +223 -0
  18. package/dist/chunk-GOOTEPBK.js +271 -0
  19. package/dist/chunk-GPPMJYSM.js +133 -0
  20. package/dist/chunk-GU2FZQ6A.js +69 -0
  21. package/dist/chunk-IOPKN5GD.js +190 -0
  22. package/dist/chunk-IXOIOGR5.js +1505 -0
  23. package/dist/chunk-KRPOPWGA.js +251 -0
  24. package/dist/chunk-MGLJ53QN.js +219 -0
  25. package/dist/chunk-MV4TTRYX.js +533 -0
  26. package/dist/chunk-OPZYEVYR.js +150 -0
  27. package/dist/chunk-QTSLP47C.js +166 -0
  28. package/dist/chunk-R3GPQC7I.js +393 -0
  29. package/dist/chunk-RKB2JOV2.js +43 -0
  30. package/dist/chunk-RNG3K465.js +80 -0
  31. package/dist/chunk-TGTYKBGC.js +86 -0
  32. package/dist/chunk-U5SGAIMM.js +681 -0
  33. package/dist/chunk-V5UHPPSY.js +140 -0
  34. package/dist/chunk-WHLVZCQY.js +245 -0
  35. package/dist/chunk-XDRCBMZZ.js +66 -0
  36. package/dist/chunk-XOS6HPEF.js +134 -0
  37. package/dist/chunk-ZSBHUGWR.js +262 -0
  38. package/dist/claude-NSQ442XD.js +12 -0
  39. package/dist/commands-CK3WFAGI.js +128 -0
  40. package/dist/commands-U63OEO5J.js +1044 -0
  41. package/dist/commands-ZE6GD3WC.js +232 -0
  42. package/dist/config-4EW42BSF.js +8 -0
  43. package/dist/config-loader-SXO674TF.js +24 -0
  44. package/dist/diagnose-AFW3ZTZ4.js +12 -0
  45. package/dist/display-IIUBEYWN.js +58 -0
  46. package/dist/extractor-QV53W2YJ.js +129 -0
  47. package/dist/history-WMSCHERZ.js +180 -0
  48. package/dist/index.d.ts +1 -0
  49. package/dist/index.js +406 -0
  50. package/dist/instance-registry-YSIJXSO7.js +15 -0
  51. package/dist/keybindings-JAAMLH3G.js +15 -0
  52. package/dist/loader-WHNTZTLP.js +58 -0
  53. package/dist/network-MM6YWPGO.js +279 -0
  54. package/dist/notify-HPTALZDC.js +14 -0
  55. package/dist/openai-compat-UQWJXBEK.js +12 -0
  56. package/dist/permissions-JUKXMNDH.js +10 -0
  57. package/dist/prompt-QV45TXRL.js +166 -0
  58. package/dist/quality-ST7PPNFR.js +16 -0
  59. package/dist/repl-RT3AHL7M.js +3375 -0
  60. package/dist/roadmap-5OBEKROY.js +17 -0
  61. package/dist/server-PORT7OEG.js +57 -0
  62. package/dist/session-4VUNDWLH.js +21 -0
  63. package/dist/skills-V4A35XKG.js +175 -0
  64. package/dist/store-Y4LU5QTO.js +25 -0
  65. package/dist/team-HO7Z4SIM.js +385 -0
  66. package/dist/telemetry-6R4EIE6O.js +30 -0
  67. package/dist/test-runner-ZQH5Y6OJ.js +619 -0
  68. package/dist/theme-3SYJ3UQA.js +14 -0
  69. package/dist/upgrade-7TGI3SXO.js +83 -0
  70. package/dist/verify-JUDKTPKZ.js +14 -0
  71. package/dist/web/static/app.js +562 -0
  72. package/dist/web/static/index.html +132 -0
  73. package/dist/web/static/mirror.css +1001 -0
  74. package/dist/web/static/mirror.html +184 -0
  75. package/dist/web/static/mirror.js +1125 -0
  76. package/dist/web/static/onboard.css +302 -0
  77. package/dist/web/static/onboard.html +140 -0
  78. package/dist/web/static/onboard.js +260 -0
  79. package/dist/web/static/style.css +602 -0
  80. package/dist/web/static/workspace.css +1568 -0
  81. package/dist/web/static/workspace.html +408 -0
  82. package/dist/web/static/workspace.js +1683 -0
  83. package/dist/web-Z5HSCQHW.js +39 -0
  84. package/package.json +67 -0
@@ -0,0 +1,139 @@
1
+ import {
2
+ HYPERCORE_DIR
3
+ } from "./chunk-V5UHPPSY.js";
4
+
5
+ // src/core/hooks.ts
6
+ import { existsSync } from "fs";
7
+ import { readFile } from "fs/promises";
8
+ import { join } from "path";
9
+ import { spawn } from "child_process";
10
+ var HookManager = class {
11
+ hooks = [];
12
+ loaded = false;
13
+ /** 从配置文件加载钩子 */
14
+ async load() {
15
+ this.hooks = [];
16
+ const systemPath = join(HYPERCORE_DIR, "hooks.json");
17
+ await this.loadFromFile(systemPath);
18
+ const projectPath = join(process.cwd(), ".hypercore", "hooks.json");
19
+ if (projectPath !== systemPath) {
20
+ await this.loadFromFile(projectPath);
21
+ }
22
+ this.loaded = true;
23
+ }
24
+ async loadFromFile(filePath) {
25
+ if (!existsSync(filePath)) return;
26
+ try {
27
+ const content = await readFile(filePath, "utf-8");
28
+ const config = JSON.parse(content);
29
+ if (Array.isArray(config.hooks)) {
30
+ for (const hook of config.hooks) {
31
+ if (hook.event && hook.command) {
32
+ this.hooks.push(hook);
33
+ }
34
+ }
35
+ }
36
+ } catch {
37
+ }
38
+ }
39
+ /** 触发钩子,返回拦截结果 */
40
+ async trigger(event, context = {}) {
41
+ if (!this.loaded) await this.load();
42
+ const matching = this.hooks.filter((h) => {
43
+ if (h.event !== event) return false;
44
+ if (h.match?.toolName && context.toolName !== h.match.toolName) return false;
45
+ if (h.match?.model && context.model !== h.match.model) return false;
46
+ return true;
47
+ });
48
+ if (matching.length === 0) return { intercepted: false };
49
+ const fullContext = {
50
+ event,
51
+ ...context
52
+ };
53
+ const blocking = matching.filter((h) => h.blocking);
54
+ const nonBlocking = matching.filter((h) => !h.blocking);
55
+ for (const hook of nonBlocking) {
56
+ this.executeHook(hook, fullContext).catch(() => {
57
+ });
58
+ }
59
+ for (const hook of blocking) {
60
+ const result = await this.executeHook(hook, fullContext);
61
+ if (hook.intercept && event === "onToolCall" && result.exitCode !== 0) {
62
+ return {
63
+ intercepted: true,
64
+ reason: result.stdout || `Hook "${hook.name || hook.command}" \u62E6\u622A\u4E86\u64CD\u4F5C (exit code: ${result.exitCode})`
65
+ };
66
+ }
67
+ }
68
+ return { intercepted: false };
69
+ }
70
+ async executeHook(hook, context) {
71
+ const timeout = hook.timeout || 1e4;
72
+ const env = {
73
+ ...process.env,
74
+ HYPERCORE_EVENT: context.event,
75
+ HYPERCORE_SESSION_ID: context.sessionId || "",
76
+ HYPERCORE_MODEL: context.model || "",
77
+ HYPERCORE_CWD: process.cwd()
78
+ };
79
+ if (context.toolName) env.HYPERCORE_TOOL_NAME = context.toolName;
80
+ if (context.toolInput) env.HYPERCORE_TOOL_INPUT = context.toolInput.slice(0, 1e3);
81
+ if (context.toolResult) env.HYPERCORE_TOOL_RESULT = context.toolResult.slice(0, 1e3);
82
+ if (context.userPrompt) env.HYPERCORE_USER_PROMPT = context.userPrompt.slice(0, 500);
83
+ if (context.error) env.HYPERCORE_ERROR = context.error.slice(0, 500);
84
+ return await new Promise((resolve) => {
85
+ const child = spawn("sh", ["-c", hook.command], {
86
+ cwd: process.cwd(),
87
+ env,
88
+ stdio: ["ignore", "pipe", "pipe"]
89
+ });
90
+ let stdout = "";
91
+ let stderr = "";
92
+ let settled = false;
93
+ const finish = (exitCode, output) => {
94
+ if (settled) return;
95
+ settled = true;
96
+ resolve({ exitCode, stdout: output.trim() });
97
+ };
98
+ const timer = setTimeout(() => {
99
+ child.kill("SIGTERM");
100
+ setTimeout(() => child.kill("SIGKILL"), 1e3);
101
+ const output = stdout || stderr || `Hook timeout after ${timeout}ms`;
102
+ finish(124, output);
103
+ }, timeout);
104
+ child.stdout?.on("data", (chunk) => {
105
+ stdout += chunk.toString("utf-8");
106
+ });
107
+ child.stderr?.on("data", (chunk) => {
108
+ stderr += chunk.toString("utf-8");
109
+ });
110
+ child.on("error", (err) => {
111
+ clearTimeout(timer);
112
+ finish(1, stderr || stdout || err.message);
113
+ });
114
+ child.on("close", (code) => {
115
+ clearTimeout(timer);
116
+ const output = stdout || stderr;
117
+ finish(code ?? 1, output);
118
+ });
119
+ });
120
+ }
121
+ /** 获取已加载的钩子数量 */
122
+ get count() {
123
+ return this.hooks.length;
124
+ }
125
+ /** 获取按事件分组的钩子列表 */
126
+ listByEvent() {
127
+ const grouped = {};
128
+ for (const hook of this.hooks) {
129
+ if (!grouped[hook.event]) grouped[hook.event] = [];
130
+ grouped[hook.event].push(hook);
131
+ }
132
+ return grouped;
133
+ }
134
+ };
135
+ var hookManager = new HookManager();
136
+
137
+ export {
138
+ hookManager
139
+ };
@@ -0,0 +1,46 @@
1
+ // src/ui/text-utils.ts
2
+ function isCJK(char) {
3
+ const code = char.codePointAt(0);
4
+ if (!code) return false;
5
+ return code >= 11904 && code <= 40959 || // CJK 部首/汉字
6
+ code >= 44032 && code <= 55215 || // 韩文音节
7
+ code >= 63744 && code <= 64255 || // CJK 兼容汉字
8
+ code >= 65072 && code <= 65103 || // CJK 兼容形式
9
+ code >= 65280 && code <= 65376 || // 全角 ASCII
10
+ code >= 65504 && code <= 65510 || // 全角符号
11
+ code >= 126976 && code <= 130047 || // 表情/符号
12
+ code >= 131072 && code <= 195103;
13
+ }
14
+ function displayWidth(str) {
15
+ const clean = str.replace(/\x1b\[[0-9;]*m/g, "");
16
+ let width = 0;
17
+ for (const char of clean) {
18
+ width += isCJK(char) ? 2 : 1;
19
+ }
20
+ return width;
21
+ }
22
+ function padToWidth(str, targetWidth) {
23
+ const currentWidth = displayWidth(str);
24
+ const padding = targetWidth - currentWidth;
25
+ return padding > 0 ? str + " ".repeat(padding) : str;
26
+ }
27
+ function truncateToWidth(str, maxWidth) {
28
+ let width = 0;
29
+ let result = "";
30
+ for (const char of str) {
31
+ const charWidth = isCJK(char) ? 2 : 1;
32
+ if (width + charWidth > maxWidth - 1) {
33
+ result += "\u2026";
34
+ break;
35
+ }
36
+ result += char;
37
+ width += charWidth;
38
+ }
39
+ return result;
40
+ }
41
+
42
+ export {
43
+ displayWidth,
44
+ padToWidth,
45
+ truncateToWidth
46
+ };
@@ -0,0 +1,58 @@
1
+ import {
2
+ HYPERCORE_DIR
3
+ } from "./chunk-V5UHPPSY.js";
4
+
5
+ // src/ui/keybindings.ts
6
+ import { existsSync } from "fs";
7
+ import { readFile } from "fs/promises";
8
+ import { join } from "path";
9
+ var DEFAULT_BINDINGS = {
10
+ "ctrl+t": "todo",
11
+ "ctrl+n": "new",
12
+ "ctrl+k": "palette"
13
+ };
14
+ var loadedBindings = null;
15
+ async function loadKeyBindings() {
16
+ const merged = { ...DEFAULT_BINDINGS };
17
+ const filePath = join(HYPERCORE_DIR, "keybindings.json");
18
+ if (existsSync(filePath)) {
19
+ try {
20
+ const content = await readFile(filePath, "utf-8");
21
+ const userBindings = JSON.parse(content);
22
+ Object.assign(merged, userBindings);
23
+ } catch {
24
+ }
25
+ }
26
+ loadedBindings = merged;
27
+ return merged;
28
+ }
29
+ function getKeyBindings() {
30
+ return loadedBindings || DEFAULT_BINDINGS;
31
+ }
32
+ function keypressToCombo(key) {
33
+ if (!key.name) return null;
34
+ const parts = [];
35
+ if (key.ctrl) parts.push("ctrl");
36
+ if (key.meta) parts.push("alt");
37
+ if (key.shift) parts.push("shift");
38
+ parts.push(key.name);
39
+ return parts.join("+");
40
+ }
41
+ function matchKeyBinding(key) {
42
+ const combo = keypressToCombo(key);
43
+ if (!combo) return null;
44
+ const bindings = getKeyBindings();
45
+ return bindings[combo] || null;
46
+ }
47
+ function listKeyBindings() {
48
+ const bindings = getKeyBindings();
49
+ return Object.entries(bindings).map(([combo, command]) => ({ combo, command }));
50
+ }
51
+
52
+ export {
53
+ loadKeyBindings,
54
+ getKeyBindings,
55
+ keypressToCombo,
56
+ matchKeyBinding,
57
+ listKeyBindings
58
+ };
@@ -0,0 +1,223 @@
1
+ import {
2
+ displayWidth,
3
+ padToWidth,
4
+ truncateToWidth
5
+ } from "./chunk-BE46C7JW.js";
6
+
7
+ // src/ui/markdown.ts
8
+ import chalk from "chalk";
9
+ import { Marked } from "marked";
10
+ import { markedTerminal } from "marked-terminal";
11
+ var INDENT = " ";
12
+ function renderTable(token) {
13
+ const termWidth = process.stdout.columns || 120;
14
+ const headers = token.header.map((h) => h.text.trim());
15
+ const rows = token.rows.map((r) => r.map((c) => c.text.trim()));
16
+ const colCount = headers.length;
17
+ const colWidths = headers.map((h) => displayWidth(h));
18
+ for (const row of rows) {
19
+ for (let i = 0; i < colCount; i++) {
20
+ const cellText = row[i] || "";
21
+ const w = displayWidth(cellText);
22
+ if (w > colWidths[i]) colWidths[i] = w;
23
+ }
24
+ }
25
+ const paddedWidths = colWidths.map((w) => w + 2);
26
+ const borderOverhead = colCount + 1;
27
+ const totalWidth = paddedWidths.reduce((a, b) => a + b, 0) + borderOverhead;
28
+ if (totalWidth > termWidth) {
29
+ const available = termWidth - borderOverhead - 4;
30
+ const ratio = available / paddedWidths.reduce((a, b) => a + b, 0);
31
+ for (let i = 0; i < colCount; i++) {
32
+ paddedWidths[i] = Math.max(4, Math.floor(paddedWidths[i] * ratio));
33
+ }
34
+ }
35
+ const contentWidths = paddedWidths.map((w) => w - 2);
36
+ const topLine = INDENT + "\u250C" + paddedWidths.map((w) => "\u2500".repeat(w)).join("\u252C") + "\u2510";
37
+ const midLine = INDENT + "\u251C" + paddedWidths.map((w) => "\u2500".repeat(w)).join("\u253C") + "\u2524";
38
+ const bottomLine = INDENT + "\u2514" + paddedWidths.map((w) => "\u2500".repeat(w)).join("\u2534") + "\u2518";
39
+ const headerCells = headers.map((h, i) => {
40
+ const text = displayWidth(h) > contentWidths[i] ? truncateToWidth(h, contentWidths[i]) : h;
41
+ return " " + padToWidth(text, contentWidths[i]) + " ";
42
+ });
43
+ const headerRow = INDENT + "\u2502" + headerCells.map((c) => chalk.bold(c)).join("\u2502") + "\u2502";
44
+ const dataRows = rows.map((row) => {
45
+ const cells = row.map((cell, i) => {
46
+ const cellText = cell || "";
47
+ const text = displayWidth(cellText) > contentWidths[i] ? truncateToWidth(cellText, contentWidths[i]) : cellText;
48
+ return " " + padToWidth(text, contentWidths[i]) + " ";
49
+ });
50
+ return INDENT + "\u2502" + cells.join("\u2502") + "\u2502";
51
+ });
52
+ return ["", topLine, headerRow, midLine, ...dataRows, bottomLine, ""].join("\n") + "\n";
53
+ }
54
+ function highlightCode(code, lang) {
55
+ const l = lang.toLowerCase();
56
+ if (l === "json" || l === "jsonc") {
57
+ return code.replace(/"([^"]*)"(?=\s*:)/g, chalk.cyan('"$1"')).replace(/:\s*"([^"]*)"/g, ": " + chalk.green('"$1"')).replace(/:\s*(\d+\.?\d*)/g, ": " + chalk.yellow("$1")).replace(/:\s*(true|false|null)/g, ": " + chalk.magenta("$1"));
58
+ }
59
+ if (l === "bash" || l === "sh" || l === "shell" || l === "zsh") {
60
+ return code.replace(/(#.*)$/gm, chalk.gray("$1")).replace(/\b(if|then|else|fi|for|do|done|while|case|esac|function|return|export|source|alias|echo|cd|ls|rm|cp|mv|mkdir|chmod|grep|sed|awk|cat|git|npm|node|docker)\b/g, chalk.blue("$1")).replace(/"([^"]*)"/g, chalk.green('"$1"')).replace(/'([^']*)'/g, chalk.green("'$1'")).replace(/\$\w+/g, chalk.cyan("$&"));
61
+ }
62
+ if (l === "css" || l === "scss" || l === "less") {
63
+ return code.replace(/(\/\*[\s\S]*?\*\/)/g, chalk.gray("$1")).replace(/(#[0-9a-fA-F]{3,8})\b/g, chalk.yellow("$1")).replace(/(\d+\.?\d*)(px|em|rem|%|vh|vw|s|ms)/g, chalk.yellow("$1$2")).replace(/\b(var|calc|rgba?|hsla?)\b/g, chalk.blue("$1"));
64
+ }
65
+ const isJSTS = ["js", "javascript", "ts", "typescript", "jsx", "tsx"].includes(l);
66
+ const isPython = ["python", "py"].includes(l);
67
+ const isGo = l === "go";
68
+ const isRust = l === "rust" || l === "rs";
69
+ if (isJSTS || isPython || isGo || isRust || !l) {
70
+ let result = code;
71
+ result = result.replace(/(\/\/.*)$/gm, chalk.gray("$1"));
72
+ result = result.replace(/(#.*)$/gm, chalk.gray("$1"));
73
+ result = result.replace(/"([^"\\]*(\\.[^"\\]*)*)"/g, chalk.green('"$1"'));
74
+ result = result.replace(/'([^'\\]*(\\.[^'\\]*)*)'/g, chalk.green("'$1'"));
75
+ result = result.replace(/`([^`]*)`/g, chalk.green("`$1`"));
76
+ let keywords;
77
+ if (isJSTS) {
78
+ keywords = ["import", "export", "from", "const", "let", "var", "function", "class", "interface", "type", "enum", "return", "if", "else", "for", "while", "switch", "case", "break", "continue", "async", "await", "new", "this", "extends", "implements", "typeof", "instanceof", "default", "try", "catch", "finally", "throw"];
79
+ } else if (isPython) {
80
+ keywords = ["import", "from", "def", "class", "return", "if", "elif", "else", "for", "while", "try", "except", "finally", "with", "as", "in", "not", "and", "or", "is", "None", "True", "False", "lambda", "yield", "async", "await", "raise", "pass", "self"];
81
+ } else if (isGo) {
82
+ keywords = ["package", "import", "func", "return", "if", "else", "for", "range", "switch", "case", "default", "var", "const", "type", "struct", "interface", "map", "chan", "go", "defer", "select", "nil", "true", "false"];
83
+ } else if (isRust) {
84
+ keywords = ["use", "fn", "let", "mut", "const", "struct", "enum", "impl", "trait", "pub", "mod", "return", "if", "else", "for", "while", "loop", "match", "self", "Self", "true", "false", "None", "Some", "Ok", "Err", "async", "await", "move", "where"];
85
+ } else {
86
+ keywords = ["import", "export", "function", "class", "return", "if", "else", "for", "while", "const", "let", "var"];
87
+ }
88
+ const kwPattern = new RegExp(`\\b(${keywords.join("|")})\\b`, "g");
89
+ result = result.replace(kwPattern, chalk.blue("$1"));
90
+ result = result.replace(/\b(\d+\.?\d*)\b/g, chalk.yellow("$1"));
91
+ return result;
92
+ }
93
+ return code;
94
+ }
95
+ var cjkTableAndCodeExtension = {
96
+ renderer: {
97
+ // 标题渲染:去除 # 前缀,纯样式输出
98
+ heading(token) {
99
+ const text = token.text;
100
+ if (token.depth === 1) {
101
+ return "\n" + chalk.bold.underline(text) + "\n\n";
102
+ }
103
+ if (token.depth === 2) {
104
+ return "\n" + chalk.bold(text) + "\n\n";
105
+ }
106
+ return "\n" + chalk.bold(text) + "\n\n";
107
+ },
108
+ table(token) {
109
+ return renderTable(token);
110
+ },
111
+ code(token) {
112
+ const lang = token.lang || "";
113
+ const highlighted = highlightCode(token.text, lang);
114
+ const termW = process.stdout.columns || 80;
115
+ const codeW = Math.min(termW - 6, 76);
116
+ const langLabel = lang ? chalk.dim.italic(` ${lang}`) : "";
117
+ const topBorder = INDENT + chalk.dim("\u250C" + "\u2500".repeat(codeW) + "\u2510") + langLabel;
118
+ const botBorder = INDENT + chalk.dim("\u2514" + "\u2500".repeat(codeW) + "\u2518");
119
+ const codeLines = highlighted.split("\n").map((line) => {
120
+ return INDENT + chalk.dim("\u2502") + chalk.bgHex("#1a1a2e")(" " + line) + " ";
121
+ });
122
+ return "\n" + topBorder + "\n" + codeLines.join("\n") + "\n" + botBorder + "\n";
123
+ }
124
+ }
125
+ };
126
+ var marked = new Marked(
127
+ // 先注册 marked-terminal 基础渲染
128
+ markedTerminal({
129
+ code: chalk.yellow,
130
+ codespan: chalk.cyan,
131
+ strong: chalk.bold,
132
+ em: chalk.italic,
133
+ link: chalk.blue.underline,
134
+ // 标题:粗体 + 下划线(一级)/ 粗体(其他)
135
+ firstHeading: chalk.bold.underline,
136
+ heading: chalk.bold,
137
+ blockquote: chalk.gray.italic,
138
+ listitem: chalk.reset,
139
+ // 渲染宽度
140
+ width: (process.stdout.columns || 80) - 4
141
+ }),
142
+ // 后注册自定义扩展(优先级更高)
143
+ cjkTableAndCodeExtension
144
+ );
145
+ function preprocess(text) {
146
+ const normalized = normalizeSpacing(text);
147
+ return normalized.replace(/^(\s*)\* /gm, "$1- ");
148
+ }
149
+ function isListLine(line) {
150
+ const t = line.trim();
151
+ return /^[-+*]\s+/.test(t) || /^\d+\.\s+/.test(t);
152
+ }
153
+ function normalizeSpacing(text) {
154
+ const lines = text.replace(/\r\n/g, "\n").split("\n");
155
+ const out = [];
156
+ let inCodeFence = false;
157
+ let blankCount = 0;
158
+ for (let i = 0; i < lines.length; i++) {
159
+ const current = lines[i].replace(/\s+$/g, "");
160
+ const trimmed = current.trim();
161
+ if (/^```/.test(trimmed)) {
162
+ inCodeFence = !inCodeFence;
163
+ blankCount = 0;
164
+ out.push(current);
165
+ continue;
166
+ }
167
+ if (!inCodeFence && trimmed === "") {
168
+ const prev = out.length > 0 ? out[out.length - 1] : "";
169
+ const next = i + 1 < lines.length ? lines[i + 1].trim() : "";
170
+ if (isListLine(prev) && isListLine(next)) {
171
+ continue;
172
+ }
173
+ blankCount += 1;
174
+ if (blankCount > 1) continue;
175
+ out.push("");
176
+ continue;
177
+ }
178
+ blankCount = 0;
179
+ out.push(current);
180
+ }
181
+ return out.join("\n").trim();
182
+ }
183
+ function addIndent(rendered) {
184
+ const lines = rendered.split("\n");
185
+ const out = [];
186
+ let prevBlank = false;
187
+ for (const line of lines) {
188
+ const trimmed = line.trim();
189
+ const isBlank = trimmed === "";
190
+ if (isBlank) {
191
+ if (!prevBlank) out.push("");
192
+ prevBlank = true;
193
+ continue;
194
+ }
195
+ prevBlank = false;
196
+ if (/^ /.test(line)) {
197
+ out.push(line);
198
+ } else {
199
+ out.push(INDENT + line);
200
+ }
201
+ }
202
+ return out.join("\n");
203
+ }
204
+ function renderMarkdown(text) {
205
+ try {
206
+ let rendered = marked.parse(preprocess(text));
207
+ rendered = rendered.replace(/^(\s*)(\*) /gm, "$1- ");
208
+ rendered = rendered.replace(/\*\*(.+?)\*\*/g, (_m, content) => chalk.bold(content));
209
+ rendered = rendered.replace(/(?<!\*)\*([^*\s][^*]*?)\*(?!\*)/g, (_m, content) => chalk.italic(content));
210
+ rendered = rendered.replace(/^(\x1b\[0m)+/gm, "");
211
+ rendered = rendered.replace(/^\s*(\x1b\[[0-9;]*m\s*)+$/gm, "");
212
+ rendered = rendered.replace(/(\x1b\[0m){2,}/g, "\x1B[0m");
213
+ rendered = rendered.replace(/\n{3,}/g, "\n\n");
214
+ rendered = addIndent(rendered);
215
+ return rendered;
216
+ } catch {
217
+ return text;
218
+ }
219
+ }
220
+
221
+ export {
222
+ renderMarkdown
223
+ };