github-search-cli 9.7.1 → 9.8.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/CHANGELOG.md +80 -0
- package/README.md +79 -24
- package/completions/ghfind.bash +5 -1
- package/completions/ghfind.fish +6 -1
- package/completions/ghfind.zsh +14 -1
- package/dist/cli.js +1506 -264
- package/package.json +7 -2
- package/release-notes/9.8.0.md +55 -0
- package/release-notes/9.8.1.md +74 -0
- package/src/agent-skill.ts +14 -0
- package/src/cli.ts +413 -1
- package/src/help.ts +2 -1
- package/src/image-loader.ts +277 -0
- package/src/image-render.ts +209 -0
- package/src/markdown-images.ts +123 -0
- package/src/markdown-view.ts +316 -0
- package/src/mcp-server.ts +60 -0
- package/src/png.ts +260 -0
- package/src/readme.ts +54 -0
- package/src/skill-finder.ts +445 -0
- package/src/tui.ts +72 -50
- package/src/markdown-render.ts +0 -211
package/src/markdown-render.ts
DELETED
|
@@ -1,211 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Minimal markdown-to-terminal-text renderer.
|
|
3
|
-
*
|
|
4
|
-
* Renders headings, code blocks, lists, tables, blockquotes, horizontal rules,
|
|
5
|
-
* bold/italic, and links into a plain-text representation suitable for a
|
|
6
|
-
* TextRenderable in the TUI.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
interface RenderOptions {
|
|
10
|
-
/** Terminal width for wrapping and table layout. Default: 80. */
|
|
11
|
-
width?: number;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
/** Render markdown text to terminal-friendly plain text. */
|
|
15
|
-
export function renderMarkdown(md: string, opts: RenderOptions = {}): string {
|
|
16
|
-
const width = opts.width ?? 80;
|
|
17
|
-
const lines = md.split("\n");
|
|
18
|
-
const out: string[] = [];
|
|
19
|
-
let inCodeBlock = false;
|
|
20
|
-
let codeLang = "";
|
|
21
|
-
let codeLines: string[] = [];
|
|
22
|
-
let inTable = false;
|
|
23
|
-
let tableRows: string[][] = [];
|
|
24
|
-
|
|
25
|
-
function flushCode() {
|
|
26
|
-
if (codeLines.length === 0) return;
|
|
27
|
-
const maxLen = Math.max(...codeLines.map((l) => l.length), codeLang.length);
|
|
28
|
-
const barW = Math.min(maxLen + 4, width - 2);
|
|
29
|
-
out.push(`┌${"─".repeat(barW)}┐`);
|
|
30
|
-
if (codeLang) out.push(`│ ${codeLang.padEnd(barW - 1)}│`);
|
|
31
|
-
for (const cl of codeLines) {
|
|
32
|
-
out.push(`│ ${cl.padEnd(barW - 1)}│`);
|
|
33
|
-
}
|
|
34
|
-
out.push(`└${"─".repeat(barW)}┘`);
|
|
35
|
-
out.push("");
|
|
36
|
-
codeLines = [];
|
|
37
|
-
codeLang = "";
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
function flushTable() {
|
|
41
|
-
if (tableRows.length === 0) return;
|
|
42
|
-
const dataRows = tableRows.filter(
|
|
43
|
-
(r) => !r.every((c) => /^[\s\-:|]+$/.test(c)),
|
|
44
|
-
);
|
|
45
|
-
if (dataRows.length === 0) {
|
|
46
|
-
tableRows = [];
|
|
47
|
-
return;
|
|
48
|
-
}
|
|
49
|
-
const colCount = Math.max(...dataRows.map((r) => r.length));
|
|
50
|
-
const colW: number[] = [];
|
|
51
|
-
for (let ci = 0; ci < colCount; ci++) {
|
|
52
|
-
colW.push(Math.max(3, ...dataRows.map((r) => (r[ci] ?? "").length)));
|
|
53
|
-
}
|
|
54
|
-
const totalW = colW.reduce((a, b) => a + b, 0) + colCount * 3 - 1;
|
|
55
|
-
if (totalW > width) {
|
|
56
|
-
for (const row of dataRows) {
|
|
57
|
-
out.push(` ${row.map((c) => c.trim()).join(" │ ")}`);
|
|
58
|
-
}
|
|
59
|
-
} else {
|
|
60
|
-
const sep = `┌${colW.map((w) => "─".repeat(w + 2)).join("┬")}┐`;
|
|
61
|
-
const mid = `├${colW.map((w) => "─".repeat(w + 2)).join("┼")}┤`;
|
|
62
|
-
const end = `└${colW.map((w) => "─".repeat(w + 2)).join("┴")}┘`;
|
|
63
|
-
out.push(sep);
|
|
64
|
-
dataRows.forEach((row, i) => {
|
|
65
|
-
const cells = colW.map((w, ci) => (row[ci] ?? "").trim().padEnd(w));
|
|
66
|
-
out.push(`│ ${cells.map((c) => ` ${c} `).join("│")}│`);
|
|
67
|
-
if (i === 0) out.push(mid);
|
|
68
|
-
});
|
|
69
|
-
out.push(end);
|
|
70
|
-
}
|
|
71
|
-
out.push("");
|
|
72
|
-
tableRows = [];
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
function renderInline(line: string): string {
|
|
76
|
-
// Bold
|
|
77
|
-
line = line.replace(/\*\*(.+?)\*\*/g, "$1");
|
|
78
|
-
// Italic (no asterisks)
|
|
79
|
-
line = line.replace(/\*([^*]+?)\*/g, "$1");
|
|
80
|
-
// Inline code
|
|
81
|
-
line = line.replace(/`([^`]+)`/g, "⟨$1⟩");
|
|
82
|
-
// Links [text](url)
|
|
83
|
-
line = line.replace(/\[([^\]]+)\]\(([^)]+)\)/g, "$1 ($2)");
|
|
84
|
-
// Images
|
|
85
|
-
line = line.replace(/!\[([^\]]*)\]\([^)]+\)/g, "🖼 $1");
|
|
86
|
-
return line;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
function wrapLine(line: string, maxW: number): string[] {
|
|
90
|
-
if (line.length <= maxW) return [line];
|
|
91
|
-
const parts: string[] = [];
|
|
92
|
-
let remaining = line;
|
|
93
|
-
while (remaining.length > maxW) {
|
|
94
|
-
let breakAt = maxW;
|
|
95
|
-
const spaceIdx = remaining.lastIndexOf(" ", maxW);
|
|
96
|
-
if (spaceIdx > maxW / 2) breakAt = spaceIdx;
|
|
97
|
-
parts.push(remaining.slice(0, breakAt));
|
|
98
|
-
remaining = remaining.slice(breakAt).replace(/^ /, "");
|
|
99
|
-
}
|
|
100
|
-
if (remaining) parts.push(remaining);
|
|
101
|
-
return parts;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
for (let i = 0; i < lines.length; i++) {
|
|
105
|
-
const line = lines[i];
|
|
106
|
-
|
|
107
|
-
// Code block boundaries
|
|
108
|
-
if (/^```/.test(line)) {
|
|
109
|
-
if (inCodeBlock) {
|
|
110
|
-
flushCode();
|
|
111
|
-
inCodeBlock = false;
|
|
112
|
-
} else {
|
|
113
|
-
inCodeBlock = true;
|
|
114
|
-
codeLang = line.slice(3).trim();
|
|
115
|
-
}
|
|
116
|
-
continue;
|
|
117
|
-
}
|
|
118
|
-
if (inCodeBlock) {
|
|
119
|
-
codeLines.push(line);
|
|
120
|
-
continue;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
// Table row
|
|
124
|
-
if (line.includes("|")) {
|
|
125
|
-
const cells = line
|
|
126
|
-
.split("|")
|
|
127
|
-
.map((c) => c.trim())
|
|
128
|
-
.filter((_, idx, arr) => idx > 0 && idx < arr.length - 1);
|
|
129
|
-
if (cells.length >= 2) {
|
|
130
|
-
inTable = true;
|
|
131
|
-
tableRows.push(cells);
|
|
132
|
-
continue;
|
|
133
|
-
}
|
|
134
|
-
} else if (inTable) {
|
|
135
|
-
flushTable();
|
|
136
|
-
inTable = false;
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
// Horizontal rule
|
|
140
|
-
if (/^(\s*[-*_]){3,}\s*$/.test(line)) {
|
|
141
|
-
out.push("─".repeat(Math.min(width, 40)));
|
|
142
|
-
out.push("");
|
|
143
|
-
continue;
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
// Headings
|
|
147
|
-
const headingMatch = line.match(/^(#{1,6})\s+(.+)/);
|
|
148
|
-
if (headingMatch) {
|
|
149
|
-
const level = headingMatch[1].length;
|
|
150
|
-
const text = renderInline(headingMatch[2]);
|
|
151
|
-
const prefix = "█ ".repeat(level);
|
|
152
|
-
out.push("");
|
|
153
|
-
out.push(prefix + text);
|
|
154
|
-
out.push("");
|
|
155
|
-
continue;
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
// Blockquote
|
|
159
|
-
const bqMatch = line.match(/^>\s?(.*)/);
|
|
160
|
-
if (bqMatch) {
|
|
161
|
-
const inner = renderInline(bqMatch[1]);
|
|
162
|
-
for (const wl of wrapLine(inner, width - 4)) {
|
|
163
|
-
out.push(`│ ${wl}`);
|
|
164
|
-
}
|
|
165
|
-
continue;
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
// Unordered list
|
|
169
|
-
const ulMatch = line.match(/^(\s*)[-*+]\s+(.*)/);
|
|
170
|
-
if (ulMatch) {
|
|
171
|
-
const indent = ulMatch[1].length;
|
|
172
|
-
const depth = Math.floor(indent / 2);
|
|
173
|
-
const prefix = `${" ".repeat(depth)}• `;
|
|
174
|
-
const inner = renderInline(ulMatch[2]);
|
|
175
|
-
for (const wl of wrapLine(inner, width - prefix.length)) {
|
|
176
|
-
out.push(prefix + wl);
|
|
177
|
-
}
|
|
178
|
-
continue;
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
// Ordered list
|
|
182
|
-
const olMatch = line.match(/^(\s*)\d+\.\s+(.*)/);
|
|
183
|
-
if (olMatch) {
|
|
184
|
-
const indent = olMatch[1].length;
|
|
185
|
-
const depth = Math.floor(indent / 2);
|
|
186
|
-
const prefix = `${" ".repeat(depth)}◦ `;
|
|
187
|
-
const inner = renderInline(olMatch[2]);
|
|
188
|
-
for (const wl of wrapLine(inner, width - prefix.length)) {
|
|
189
|
-
out.push(prefix + wl);
|
|
190
|
-
}
|
|
191
|
-
continue;
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
// Empty line
|
|
195
|
-
if (line.trim() === "") {
|
|
196
|
-
out.push("");
|
|
197
|
-
continue;
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
// Paragraph
|
|
201
|
-
const rendered = renderInline(line);
|
|
202
|
-
for (const wl of wrapLine(rendered, width)) {
|
|
203
|
-
out.push(wl);
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
if (inCodeBlock) flushCode();
|
|
208
|
-
if (inTable) flushTable();
|
|
209
|
-
|
|
210
|
-
return out.join("\n");
|
|
211
|
-
}
|