@co0ontty/wand 0.3.0 → 0.4.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.
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Shared PTY text processing utilities.
3
+ * Used by both claude-pty-bridge.ts and message-parser.ts to ensure
4
+ * consistent ANSI stripping and noise filtering.
5
+ */
6
+ /** Strip ANSI escape sequences and control characters from raw PTY output. */
7
+ export declare function stripAnsi(text: string): string;
8
+ /** Lines considered as UI noise that should be excluded from chat view. */
9
+ export declare function isNoiseLine(line: string): boolean;
10
+ /** Append text to a windowed buffer, trimming from start if over max size. */
11
+ export declare function appendWindow(buffer: string, chunk: string, maxSize: number): string;
12
+ /** Normalize prompt text for permission detection (strip ANSI, collapse whitespace). */
13
+ export declare function normalizePromptText(value: string): string;
@@ -0,0 +1,84 @@
1
+ /**
2
+ * Shared PTY text processing utilities.
3
+ * Used by both claude-pty-bridge.ts and message-parser.ts to ensure
4
+ * consistent ANSI stripping and noise filtering.
5
+ */
6
+ /** Strip ANSI escape sequences and control characters from raw PTY output. */
7
+ export function stripAnsi(text) {
8
+ return text
9
+ .replace(/\x1b\[[0-9;?]*[a-zA-Z]/g, "") // CSI sequences
10
+ .replace(/\x1b\][^\x07]*(\x07|\x1b\\)/g, "") // OSC sequences
11
+ .replace(/\x1b[><=ePX^_]/g, "") // Single-char escapes
12
+ // eslint-disable-next-line no-control-regex
13
+ .replace(/[\x00-\x08\x0b\x0c\x0e-\x1f]/g, "") // Control chars (keep \t \n \r)
14
+ .replace(/\r\n?/g, "\n");
15
+ }
16
+ /** Lines considered as UI noise that should be excluded from chat view. */
17
+ export function isNoiseLine(line) {
18
+ if (!line)
19
+ return false;
20
+ if (line.startsWith("────"))
21
+ return true;
22
+ if (line === "❯")
23
+ return true;
24
+ if (line.includes("esc to interrupt"))
25
+ return true;
26
+ if (line.includes("Claude Code v"))
27
+ return true;
28
+ if (/^Sonnet\b/.test(line))
29
+ return true;
30
+ if (line.includes("Failed to install Anthropic"))
31
+ return true;
32
+ if (line.includes("Claude Code has switched"))
33
+ return true;
34
+ if (line.includes("? for shortcuts"))
35
+ return true;
36
+ if (line.includes("Claude is waiting"))
37
+ return true;
38
+ if (line.includes("[wand]"))
39
+ return true;
40
+ if (line.startsWith("0;") || line.startsWith("9;"))
41
+ return true;
42
+ if (line.includes("ctrl+g"))
43
+ return true;
44
+ if (line.includes("/effort"))
45
+ return true;
46
+ if (/^Using .* for .* session/.test(line))
47
+ return true;
48
+ if (line.startsWith("Press ") && line.includes(" for"))
49
+ return true;
50
+ if (line.startsWith("type ") && line.includes(" to "))
51
+ return true;
52
+ if (line.includes("auto mode is unavailable"))
53
+ return true;
54
+ if (/MCP server.*failed/i.test(line))
55
+ return true;
56
+ if (line.includes("Germinating") || line.includes("Doodling") || line.includes("Brewing"))
57
+ return true;
58
+ if (line.includes("Permissions") && line.includes("mode"))
59
+ return true;
60
+ if (line.startsWith("●") && line.includes("·"))
61
+ return true;
62
+ if (line.startsWith("[>") || line.startsWith("[<"))
63
+ return true;
64
+ if (line.includes("Captured Claude session ID"))
65
+ return true;
66
+ if (line.includes("/effort"))
67
+ return true;
68
+ return false;
69
+ }
70
+ /** Append text to a windowed buffer, trimming from start if over max size. */
71
+ export function appendWindow(buffer, chunk, maxSize) {
72
+ const next = buffer + chunk;
73
+ return next.length > maxSize ? next.slice(-maxSize) : next;
74
+ }
75
+ /** Normalize prompt text for permission detection (strip ANSI, collapse whitespace). */
76
+ export function normalizePromptText(value) {
77
+ return value
78
+ .replace(/\u001b\[(\d+)C/g, (_match, count) => " ".repeat(Number(count) || 1))
79
+ .replace(/\u001b\[[0-9;?]*[ -/]*[@-~]/g, "")
80
+ .replace(/\r/g, "\n")
81
+ .replace(/[ \t]+/g, " ")
82
+ .replace(/\n+/g, "\n")
83
+ .trim();
84
+ }
package/dist/pwa.d.ts ADDED
@@ -0,0 +1,5 @@
1
+ /**
2
+ * PWA manifest and Service Worker generation.
3
+ */
4
+ export declare function generatePwaManifest(): string;
5
+ export declare function generateServiceWorker(): string;
package/dist/pwa.js ADDED
@@ -0,0 +1,118 @@
1
+ /**
2
+ * PWA manifest and Service Worker generation.
3
+ */
4
+ export function generatePwaManifest() {
5
+ return JSON.stringify({
6
+ id: "/wand",
7
+ scope: "/",
8
+ lang: "zh-CN",
9
+ dir: "ltr",
10
+ name: "Wand Console",
11
+ short_name: "Wand",
12
+ description: "Local CLI Console for Vibe Coding",
13
+ start_url: "/",
14
+ display: "standalone",
15
+ display_override: ["window-controls-overlay", "standalone"],
16
+ background_color: "#f6f1e8",
17
+ theme_color: "#c5653d",
18
+ orientation: "any",
19
+ prefer_related_applications: false,
20
+ icons: [
21
+ { src: "/icon.svg", sizes: "any", type: "image/svg+xml", purpose: "any" },
22
+ { src: "/icon.svg", sizes: "any", type: "image/svg+xml", purpose: "maskable" },
23
+ ],
24
+ categories: ["developer tools", "productivity"],
25
+ shortcuts: [
26
+ {
27
+ name: "New Session",
28
+ url: "/?action=new",
29
+ description: "Start a new CLI session",
30
+ icons: [{ src: "/icon.svg", sizes: "any", type: "image/svg+xml" }],
31
+ },
32
+ ],
33
+ });
34
+ }
35
+ export function generateServiceWorker() {
36
+ return `
37
+ const STATIC_CACHE = 'wand-static-v6';
38
+ const RUNTIME_CACHE = 'wand-runtime-v6';
39
+ const APP_SHELL = '/';
40
+ const STATIC_ASSETS = [
41
+ '/manifest.json',
42
+ '/icon.svg',
43
+ '/icon-192.png',
44
+ '/icon-512.png',
45
+ '/vendor/xterm/css/xterm.css',
46
+ '/vendor/xterm/lib/xterm.js',
47
+ '/vendor/xterm-addon-fit/lib/addon-fit.js'
48
+ ];
49
+
50
+ self.addEventListener('install', (event) => {
51
+ event.waitUntil(caches.open(STATIC_CACHE).then((cache) => cache.addAll(STATIC_ASSETS)));
52
+ self.skipWaiting();
53
+ });
54
+
55
+ self.addEventListener('activate', (event) => {
56
+ event.waitUntil(
57
+ caches.keys().then((keys) => Promise.all(
58
+ keys
59
+ .filter((key) => key !== STATIC_CACHE && key !== RUNTIME_CACHE)
60
+ .map((key) => caches.delete(key))
61
+ ))
62
+ );
63
+ self.clients.claim();
64
+ });
65
+
66
+ async function cacheFirst(request) {
67
+ const cached = await caches.match(request);
68
+ if (cached) return cached;
69
+
70
+ const response = await fetch(request);
71
+ if (response.ok && request.method === 'GET') {
72
+ const clone = response.clone();
73
+ const cache = await caches.open(RUNTIME_CACHE);
74
+ cache.put(request, clone);
75
+ }
76
+ return response;
77
+ }
78
+
79
+ self.addEventListener('fetch', (event) => {
80
+ const request = event.request;
81
+ const url = new URL(request.url);
82
+
83
+ if (request.method !== 'GET') {
84
+ return;
85
+ }
86
+
87
+ if (url.pathname.startsWith('/api/')) {
88
+ event.respondWith(
89
+ fetch(request).catch(() => new Response(JSON.stringify({ error: 'Offline' }), {
90
+ status: 503,
91
+ headers: { 'Content-Type': 'application/json' }
92
+ }))
93
+ );
94
+ return;
95
+ }
96
+
97
+ if (request.mode === 'navigate') {
98
+ event.respondWith(
99
+ fetch(request)
100
+ .then((response) => {
101
+ const clone = response.clone();
102
+ caches.open(RUNTIME_CACHE).then((cache) => cache.put(APP_SHELL, clone));
103
+ return response;
104
+ })
105
+ .catch(async () => (await caches.match(APP_SHELL)) || Response.error())
106
+ );
107
+ return;
108
+ }
109
+
110
+ event.respondWith(
111
+ cacheFirst(request).catch(async () => {
112
+ const cached = await caches.match(request);
113
+ return cached || (await caches.match(APP_SHELL)) || Response.error();
114
+ })
115
+ );
116
+ });
117
+ `;
118
+ }