wisp-agents 0.0.0-stage → 0.2.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 +21 -0
- package/README.md +191 -2
- package/bin/wisp.js +113 -0
- package/electron/icon.png +0 -0
- package/electron/icon.svg +13 -0
- package/electron/main.js +147 -0
- package/electron/preload.cjs +6 -0
- package/package.json +47 -4
- package/server/adapters/claude.js +230 -0
- package/server/adapters/index.js +36 -0
- package/server/index.js +364 -0
- package/server/mcp-ask.js +82 -0
- package/server/protocol.js +182 -0
- package/server/runner.js +283 -0
- package/server/store.js +132 -0
- package/server/terminal.js +73 -0
- package/server/welcome.js +168 -0
- package/web/css/app.css +893 -0
- package/web/favicon.svg +5 -0
- package/web/index.html +85 -0
- package/web/js/app.js +1452 -0
- package/web/js/blocks.js +557 -0
- package/web/js/diff.js +68 -0
- package/web/js/md.js +139 -0
package/web/js/md.js
ADDED
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
// Tiny markdown + syntax highlighting. Enough for agent output; no dependencies.
|
|
2
|
+
|
|
3
|
+
export const esc = (s) =>
|
|
4
|
+
String(s ?? '').replace(/[&<>"']/g, (c) => ({ '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' })[c]);
|
|
5
|
+
|
|
6
|
+
function inline(src) {
|
|
7
|
+
const codes = [];
|
|
8
|
+
let s = esc(src).replace(/`([^`]+)`/g, (_, c) => `\u0000${codes.push(c) - 1}\u0000`);
|
|
9
|
+
s = s
|
|
10
|
+
.replace(/\*\*([^*]+)\*\*/g, '<strong>$1</strong>')
|
|
11
|
+
.replace(/(^|[^*\w])\*([^*\s][^*]*)\*/g, '$1<em>$2</em>')
|
|
12
|
+
.replace(/(^|\W)_([^_\s][^_]*)_(?=\W|$)/g, '$1<em>$2</em>')
|
|
13
|
+
.replace(/~~([^~]+)~~/g, '<del>$1</del>')
|
|
14
|
+
.replace(/\[([^\]]+)\]\((https?:[^)\s]+)\)/g, '<a href="$2" target="_blank" rel="noreferrer">$1</a>')
|
|
15
|
+
.replace(/(^|\s)(https?:\/\/[^\s<]+)/g, '$1<a href="$2" target="_blank" rel="noreferrer">$2</a>');
|
|
16
|
+
return s.replace(/\u0000(\d+)\u0000/g, (_, i) => `<code>${codes[i]}</code>`);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function md(src = '') {
|
|
20
|
+
const lines = String(src).replace(/\r\n/g, '\n').split('\n');
|
|
21
|
+
const out = [];
|
|
22
|
+
let i = 0;
|
|
23
|
+
while (i < lines.length) {
|
|
24
|
+
const line = lines[i];
|
|
25
|
+
const fence = line.match(/^\s*```(\S*)/);
|
|
26
|
+
if (fence) {
|
|
27
|
+
const buf = [];
|
|
28
|
+
i++;
|
|
29
|
+
while (i < lines.length && !/^\s*```/.test(lines[i])) buf.push(lines[i++]);
|
|
30
|
+
i++;
|
|
31
|
+
out.push(`<pre class="code"><code>${highlight(buf.join('\n'), fence[1])}</code></pre>`);
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
const h = line.match(/^(#{1,4})\s+(.*)/);
|
|
35
|
+
if (h) {
|
|
36
|
+
out.push(`<h${h[1].length + 2}>${inline(h[2])}</h${h[1].length + 2}>`);
|
|
37
|
+
i++;
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
if (/^\s*([-*_])\s*\1\s*\1[\s\1]*$/.test(line)) {
|
|
41
|
+
out.push('<hr>');
|
|
42
|
+
i++;
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
if (/^\s*>/.test(line)) {
|
|
46
|
+
const buf = [];
|
|
47
|
+
while (i < lines.length && /^\s*>/.test(lines[i])) buf.push(lines[i++].replace(/^\s*>\s?/, ''));
|
|
48
|
+
out.push(`<blockquote>${md(buf.join('\n'))}</blockquote>`);
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
if (/^\s*\|.*\|\s*$/.test(line) && /^\s*\|?[\s:-]+\|[\s|:-]*$/.test(lines[i + 1] || '')) {
|
|
52
|
+
const row = (l) => l.trim().replace(/^\||\|$/g, '').split('|').map((c) => c.trim());
|
|
53
|
+
const head = row(line);
|
|
54
|
+
i += 2;
|
|
55
|
+
const body = [];
|
|
56
|
+
while (i < lines.length && /^\s*\|/.test(lines[i])) body.push(row(lines[i++]));
|
|
57
|
+
out.push(
|
|
58
|
+
`<div class="table-wrap"><table><thead><tr>${head.map((c) => `<th>${inline(c)}</th>`).join('')}</tr></thead><tbody>${body
|
|
59
|
+
.map((r) => `<tr>${r.map((c) => `<td>${inline(c)}</td>`).join('')}</tr>`)
|
|
60
|
+
.join('')}</tbody></table></div>`,
|
|
61
|
+
);
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
const li = line.match(/^(\s*)([-*+]|\d+[.)])\s+(.*)/);
|
|
65
|
+
if (li) {
|
|
66
|
+
const ordered = /\d/.test(li[2]);
|
|
67
|
+
const items = [];
|
|
68
|
+
while (i < lines.length) {
|
|
69
|
+
const m = lines[i].match(/^(\s*)([-*+]|\d+[.)])\s+(.*)/);
|
|
70
|
+
if (m) items.push(m[3]);
|
|
71
|
+
else if (/^\s{2,}\S/.test(lines[i]) && items.length) items[items.length - 1] += ' ' + lines[i].trim();
|
|
72
|
+
else break;
|
|
73
|
+
i++;
|
|
74
|
+
}
|
|
75
|
+
const tag = ordered ? 'ol' : 'ul';
|
|
76
|
+
out.push(
|
|
77
|
+
`<${tag}>${items
|
|
78
|
+
.map((t) => {
|
|
79
|
+
const task = t.match(/^\[( |x)\]\s+(.*)/i);
|
|
80
|
+
return task ? `<li class="task ${task[1] === ' ' ? '' : 'done'}">${inline(task[2])}</li>` : `<li>${inline(t)}</li>`;
|
|
81
|
+
})
|
|
82
|
+
.join('')}</${tag}>`,
|
|
83
|
+
);
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
if (!line.trim()) {
|
|
87
|
+
i++;
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
const buf = [];
|
|
91
|
+
while (i < lines.length && lines[i].trim() && !/^\s*(```|#{1,4}\s|>|[-*+]\s|\d+[.)]\s|\|)/.test(lines[i])) buf.push(lines[i++]);
|
|
92
|
+
if (!buf.length) buf.push(lines[i++]);
|
|
93
|
+
out.push(`<p>${inline(buf.join(' '))}</p>`);
|
|
94
|
+
}
|
|
95
|
+
return out.join('');
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export const inlineMd = inline;
|
|
99
|
+
|
|
100
|
+
// --- highlighting -----------------------------------------------------------
|
|
101
|
+
|
|
102
|
+
const KEYWORDS = new Set(
|
|
103
|
+
(
|
|
104
|
+
'abstract as async await break case catch class const continue def default defer del delete do elif else enum export extends ' +
|
|
105
|
+
'false final finally fn for from func function go if impl import in instanceof interface is let match mod module mut new nil ' +
|
|
106
|
+
'none null package pass private protected pub public raise return self select static struct super switch this throw throws ' +
|
|
107
|
+
'true try type typeof undefined use var void while with yield lambda not and or None True False readonly override'
|
|
108
|
+
).split(' '),
|
|
109
|
+
);
|
|
110
|
+
|
|
111
|
+
const TOKEN =
|
|
112
|
+
/(\/\/[^\n]*|#(?![\w{[])[^\n]*|\/\*[\s\S]*?\*\/|"(?:[^"\\\n]|\\.)*"|'(?:[^'\\\n]|\\.)*'|`(?:[^`\\]|\\.)*`|\b\d[\d_.]*(?:e[+-]?\d+)?[a-z]*\b|\b[A-Za-z_$][\w$]*\b|[^\sA-Za-z_$\d"'`/#]+|\s+|.)/g;
|
|
113
|
+
|
|
114
|
+
export function highlight(code, lang = '') {
|
|
115
|
+
lang = lang.toLowerCase();
|
|
116
|
+
if (['', 'text', 'txt', 'plain', 'output', 'log'].includes(lang)) return esc(code);
|
|
117
|
+
const shellish = ['sh', 'bash', 'zsh', 'shell', 'console', 'yaml', 'yml', 'toml', 'py', 'python', 'rb', 'ruby'].includes(lang);
|
|
118
|
+
let out = '';
|
|
119
|
+
for (const [tok] of code.matchAll(TOKEN)) {
|
|
120
|
+
let cls = '';
|
|
121
|
+
if (/^(\/\/|\/\*)/.test(tok) || (tok.startsWith('#') && shellish)) cls = 'c';
|
|
122
|
+
else if (tok.startsWith('#')) cls = '';
|
|
123
|
+
else if (/^["'`]/.test(tok)) cls = 's';
|
|
124
|
+
else if (/^\d/.test(tok)) cls = 'n';
|
|
125
|
+
else if (KEYWORDS.has(tok)) cls = 'k';
|
|
126
|
+
else if (/^[A-Z][\w$]*$/.test(tok)) cls = 't';
|
|
127
|
+
if (cls === '' && tok.startsWith('#') && !shellish) {
|
|
128
|
+
out += esc(tok);
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
out += cls ? `<span class="tk-${cls}">${esc(tok)}</span>` : esc(tok);
|
|
132
|
+
}
|
|
133
|
+
return out;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export function langOf(file = '') {
|
|
137
|
+
const ext = file.split('.').pop().toLowerCase();
|
|
138
|
+
return { mjs: 'js', cjs: 'js', jsx: 'js', tsx: 'ts', yml: 'yaml' }[ext] || ext;
|
|
139
|
+
}
|