feat: ClawTap v0.1.0 — initial release

Multi-adapter mobile UI for AI coding assistants.
Supports Claude Code, Codex CLI, and Gemini CLI through one interface.

Features:
- Real-time bidirectional sync via tmux + WebSocket
- Cross-AI review (send one AI's output to another for review)
- Multi-review tabs with minimize/expand
- Push notifications (PWA) with smart session-aware filtering
- Three-channel event system (hooks, file watcher, pane monitor)
- Voice input, image paste, draft persistence
- Terminal-native design (JetBrains Mono, dark theme, pixel art claw)
- CLI with --adapter flag on every command
- Zero-overhead fire-and-forget hooks
This commit is contained in:
kuannnn
2026-03-18 10:24:45 +08:00
commit 42861ea7fa
151 changed files with 33897 additions and 0 deletions
+62
View File
@@ -0,0 +1,62 @@
/** A content block within a Codex message */
export interface CodexContentBlock {
type: 'input_text' | 'output_text' | 'input_image' | string;
text?: string;
image?: { url: string };
[key: string]: unknown;
}
/** Standard normalized content block (matches claw-tap frontend format) */
export interface NormalizedBlock {
type: string;
text?: string;
[key: string]: unknown;
}
const SYSTEM_PATTERNS: RegExp[] = [
/<permissions instructions>/i,
/<environment_context>/i,
/AGENTS\.md/,
];
/**
* Convert a Codex content block to the standard format used by claw-tap.
*
* - `input_text` → `{ type: 'text', text }`
* - `output_text` → `{ type: 'text', text }`
* - Unknown types are passed through as-is.
*/
export function normalizeContentBlock(block: CodexContentBlock): NormalizedBlock {
if (block.type === 'input_text' || block.type === 'output_text') {
return { type: 'text', text: block.text };
}
return { ...block } as NormalizedBlock;
}
/**
* Extract readable text from an array of Codex content blocks.
* Concatenates all text-bearing blocks (input_text / output_text) with newlines.
*/
export function extractText(content: CodexContentBlock[]): string {
return content
.filter((b) => b.type === 'input_text' || b.type === 'output_text')
.map((b) => b.text ?? '')
.join('\n');
}
/**
* Return true if this message should be filtered out as a system message.
*
* Matches:
* - `role === 'developer'`
* - Text containing `<permissions instructions>`, `<environment_context>`, or `AGENTS.md`
*/
export function isSystemMessage(role: string, content: CodexContentBlock[]): boolean {
if (role === 'developer') return true;
const text = extractText(content);
for (const pattern of SYSTEM_PATTERNS) {
if (pattern.test(text)) return true;
}
return false;
}