Skip to content

Commit 1ab55ab

Browse files
CSCSoftwareclaude
andcommitted
feat: add consume pattern, Viewer improvements, and Log Hub docs (v1.16.1)
- Log Hub: consume parameter removes entries after query (poll pattern) - Viewer: Clear button in Logs tab, WebSocket auto-reconnect - Fix: data:null no longer shows "null" text in Viewer and MCP output - Docs: LogHub Developer Guide with code examples for C#, Python, JS, C++, PowerShell - README: new Log Hub section with architecture diagram and quick start Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 02a2578 commit 1ab55ab

10 files changed

Lines changed: 235 additions & 7 deletions

File tree

.claude/CLAUDE.md

Lines changed: 104 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
MCP Server für persistentes Code-Indexing. Ermöglicht Claude Code schnelle, präzise Suchen statt Grep/Glob.
44

5-
**Version:** 1.16.0 | **Sprachen:** 11 | **Repo:** https://github.com/CSCSoftware/AiDex
5+
**Version:** 1.16.1 | **Sprachen:** 11 | **Repo:** https://github.com/CSCSoftware/AiDex
66

77
## Build & Run
88

@@ -276,6 +276,109 @@ node build/index.js init <path> # Indexieren
276276
- **Arrow Functions:** Werden als Methods erkannt (gewollt, etwas Noise)
277277
- **Keyword-Filter:** Pro Sprache in `src/parser/languages/`
278278

279+
## LogHub Developer Guide
280+
281+
### Übersicht
282+
283+
LogHub ist ein universeller Log-Empfänger. Jedes Programm kann per HTTP POST Logs senden — keine Library, kein SDK nötig. Die KI kann die Logs abfragen, der User sieht sie live im AiDex Viewer.
284+
285+
### Setup (durch die KI)
286+
287+
```
288+
1. aidex_log({ action: "init" }) # Server starten (Port 3335)
289+
2. aidex_viewer({ path: "." }) # Viewer öffnen → Logs-Tab zeigt Live-Stream
290+
3. Logging in das Programm einbauen (siehe unten)
291+
4. aidex_log({ action: "query", since: "5m" }) # KI fragt Logs ab
292+
5. aidex_log({ action: "free" }) # Am Ende: Server stoppen
293+
```
294+
295+
### HTTP API
296+
297+
| Endpoint | Method | Body | Beschreibung |
298+
|----------|--------|------|--------------|
299+
| `/log` | POST | `{ level, source, message, data? }` | Einzelner Log-Eintrag |
300+
| `/logs` | POST | `[{ level, source, message, data? }, ...]` | Batch (mehrere auf einmal) |
301+
| `/health` | GET || Status + Buffer-Auslastung |
302+
303+
**Felder:**
304+
- `level`: `"debug"`, `"info"`, `"warn"`, `"error"` (default: `"info"`)
305+
- `source`: Name der App/Komponente (z.B. `"MyApp"`, `"Parser"`)
306+
- `message`: Log-Text (required)
307+
- `data`: Beliebiges JSON-Objekt für strukturierte Daten (optional)
308+
- `timestamp`: Unix-Timestamp in ms (optional, sonst Server-Zeit)
309+
310+
### Code-Beispiele für verschiedene Sprachen
311+
312+
**C# (.NET)**
313+
```csharp
314+
using var http = new HttpClient();
315+
http.PostAsJsonAsync("http://localhost:3335/log", new {
316+
level = "info",
317+
source = "MyApp",
318+
message = "Player spawned",
319+
data = new { x = 10, y = 20 }
320+
});
321+
```
322+
323+
**C# (Minimal-Helper)**
324+
```csharp
325+
// Einmal initialisieren
326+
static readonly HttpClient _log = new();
327+
static void Log(string msg, string level = "info", object? data = null) {
328+
var body = new { level, source = "MyApp", message = msg, data };
329+
_ = _log.PostAsJsonAsync("http://localhost:3335/log", body);
330+
}
331+
332+
// Verwenden
333+
Log("Game started");
334+
Log("Error loading level", "error", new { levelId = 5 });
335+
```
336+
337+
**Python**
338+
```python
339+
import requests
340+
requests.post("http://localhost:3335/log", json={
341+
"level": "info",
342+
"source": "MyScript",
343+
"message": "Processing complete",
344+
"data": {"items": 42}
345+
})
346+
```
347+
348+
**JavaScript / Node.js**
349+
```javascript
350+
fetch("http://localhost:3335/log", {
351+
method: "POST",
352+
headers: { "Content-Type": "application/json" },
353+
body: JSON.stringify({
354+
level: "info",
355+
source: "MyApp",
356+
message: "Server started",
357+
})
358+
});
359+
```
360+
361+
**C / C++ (curl)**
362+
```c
363+
// Mit libcurl oder shell:
364+
// curl -X POST http://localhost:3335/log -H "Content-Type: application/json" -d '{"level":"info","source":"MyApp","message":"Init done"}'
365+
```
366+
367+
**PowerShell**
368+
```powershell
369+
Invoke-RestMethod -Uri "http://localhost:3335/log" -Method POST -ContentType "application/json" -Body '{"level":"info","source":"MyApp","message":"Task done"}'
370+
```
371+
372+
### Tipps für die KI
373+
374+
- **Viewer immer mit anbieten**`aidex_viewer({ path: "." })` öffnet den Browser, Logs-Tab zeigt Live-Stream via WebSocket
375+
- **Source sinnvoll wählen** — ermöglicht Filtern per `aidex_log({ action: "query", source: "MyApp" })`
376+
- **Level nutzen**`error` für Fehler, `warn` für Warnungen, `debug` für Verbose
377+
- **Batch für Performance** — bei vielen Logs pro Sekunde `/logs` statt `/log` verwenden
378+
- **Consume-Pattern**`aidex_log({ action: "query", consume: true })` holt Logs und entfernt sie aus dem Buffer (Poll-Muster)
379+
- **Fire & Forget** — Logs asynchron senden (kein await nötig), damit die App nicht blockiert
380+
- **Kein Error-Handling nötig** — wenn LogHub nicht läuft, schlägt der POST still fehl
381+
279382
## Dokumentation
280383

281384
| Datei | Inhalt |

CHANGELOG.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,17 @@ All notable changes to AiDex will be documented in this file.
44

55
## [Unreleased]
66

7+
## [1.16.1] - 2026-03-20
8+
9+
### Added
10+
- **Log Hub consume pattern**: New `consume` parameter on `aidex_log` query — returned entries are removed from the buffer, ideal for polling without duplicates
11+
- **Viewer: Clear Logs button**: "Clear" button in the Logs tab to reset the log display
12+
- **Viewer: WebSocket auto-reconnect**: Viewer automatically reconnects when WebSocket connection drops (2s retry)
13+
- **LogHub Developer Guide**: Added comprehensive integration guide to project CLAUDE.md with code examples for C#, Python, JavaScript, C/C++, PowerShell
14+
15+
### Fixed
16+
- **Log entry `data: null` display**: Entries with `data: null` no longer show "null" text in Viewer and MCP output
17+
718
## [1.16.0] - 2026-03-20
819

920
### Added

MCP-API-REFERENCE.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -910,6 +910,7 @@ Universal log receiver — any program (C#, Python, Node, etc.) can send logs vi
910910
| `source` | string | - | Filter by source name (query) |
911911
| `contains` | string | - | Filter by message substring (query) |
912912
| `limit` | number | - | Max entries to return (default: `50`, used with query) |
913+
| `consume` | boolean | - | If true, returned entries are removed from buffer — ideal for polling without duplicates (default: `false`, used with query) |
913914
| `message` | string | for write | Log message text |
914915
| `data` | string | - | Optional JSON data (write) |
915916

@@ -920,7 +921,7 @@ Universal log receiver — any program (C#, Python, Node, etc.) can send logs vi
920921
| `init` | Start HTTP server and ring buffer. Optional: `persist` + `path` for SQLite storage |
921922
| `free` | Stop server, free all resources, release port |
922923
| `status` | Show stats: entries, buffer usage, sources, level counts, port |
923-
| `query` | Search logs with filters (since, level, source, contains, limit). Newest first |
924+
| `query` | Search logs with filters (since, level, source, contains, limit, consume). Newest first |
924925
| `clear` | Clear the ring buffer (keep server running) |
925926
| `write` | Inject a log entry as source "claude" |
926927

@@ -948,6 +949,9 @@ Body limit: 64KB. CORS enabled. Levels: `debug`, `info`, `warn`, `error`.
948949
// Query by source
949950
{ "action": "query", "source": "MyApp", "contains": "connection" }
950951

952+
// Poll and consume (entries removed from buffer after reading)
953+
{ "action": "query", "consume": true }
954+
951955
// LLM writes a log entry
952956
{ "action": "write", "level": "info", "message": "Starting debug session" }
953957

README.md

Lines changed: 68 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ aidex_task({ path: ".", action: "create", title: "Fix edge case in parser", prio
6767

6868
## Table of Contents
6969

70-
- [What's Inside](#whats-inside--29-tools-in-one-server)
70+
- [What's Inside](#whats-inside--30-tools-in-one-server)
7171
- [The Problem](#the-problem)
7272
- [The Solution](#the-solution)
7373
- [Why Not Just Grep?](#why-not-just-grep)
@@ -82,6 +82,7 @@ aidex_task({ path: ".", action: "create", title: "Fix edge case in parser", prio
8282
- [Task Backlog](#task-backlog)
8383
- [Global Search](#global-search)
8484
- [AI Guidelines](#ai-guidelines)
85+
- [Log Hub](#log-hub--universal-logging)
8586
- [Screenshots — LLM-Optimized](#screenshots--llm-optimized)
8687
- [Interactive Viewer](#interactive-viewer)
8788
- [CLI Usage](#cli-usage)
@@ -563,6 +564,70 @@ aidex_global_guideline({ action: "delete", key: "old-rule" }) # Remove
563564

564565
Guidelines are stored in `~/.aidex/global.db` — available across all your projects without `aidex_init`. Ask your AI: *"Load the review guideline and apply it to this file."*
565566

567+
## Log Hub — Universal Logging
568+
569+
Turn any program into a log source for your AI assistant. Your app sends logs via HTTP POST, the AI queries them via MCP, and you see them live in the Viewer — zero dependencies, zero setup in your code.
570+
571+
### How it works
572+
573+
```
574+
Your Program ──HTTP POST──→ AiDex Log Hub (port 3335) ──→ Ring Buffer
575+
│ │
576+
│ WebSocket │ MCP query
577+
↓ ↓
578+
Viewer (Logs tab) AI Assistant
579+
(you see live) (queries & analyzes)
580+
```
581+
582+
### Quick start
583+
584+
1. AI starts the Log Hub: `aidex_log({ action: "init" })`
585+
2. AI opens the Viewer: `aidex_viewer({ path: "." })` — Logs tab shows live stream
586+
3. Add one line to your program:
587+
588+
```csharp
589+
// C#
590+
await new HttpClient().PostAsJsonAsync("http://localhost:3335/log",
591+
new { level = "info", source = "MyApp", message = "Player spawned", data = new { x = 10, y = 20 } });
592+
```
593+
594+
```python
595+
# Python
596+
requests.post("http://localhost:3335/log", json={"level": "info", "source": "MyApp", "message": "Done"})
597+
```
598+
599+
```javascript
600+
// JavaScript
601+
fetch("http://localhost:3335/log", {
602+
method: "POST", headers: {"Content-Type": "application/json"},
603+
body: JSON.stringify({level: "info", source: "MyApp", message: "Started"})
604+
});
605+
```
606+
607+
```powershell
608+
# PowerShell
609+
Invoke-RestMethod -Uri http://localhost:3335/log -Method POST -ContentType "application/json" -Body '{"level":"info","source":"Script","message":"Done"}'
610+
```
611+
612+
### HTTP API
613+
614+
| Endpoint | Method | Body | Description |
615+
|----------|--------|------|-------------|
616+
| `/log` | POST | `{ level, source, message, data? }` | Single log entry |
617+
| `/logs` | POST | `[{ ... }, ...]` | Batch (multiple at once) |
618+
| `/health` | GET || Status + buffer usage |
619+
620+
**Fields:** `level` (`debug`/`info`/`warn`/`error`), `source` (app name), `message` (text, required), `data` (optional JSON), `timestamp` (optional, ms)
621+
622+
### Features
623+
624+
- **Ring Buffer**: Fixed-size in-memory FIFO (default 10,000 entries) — oldest entries overwritten
625+
- **Zero-cost**: No server, no buffer, no resources until `init` is called
626+
- **Persistence**: Optional SQLite storage with 7-day auto-cleanup (`persist: true`)
627+
- **Consume pattern**: `query` with `consume: true` removes returned entries — ideal for polling
628+
- **Viewer integration**: Logs tab with WebSocket live-stream, level/source/text filters, auto-scroll
629+
- **Fire & forget**: Just POST and go — if the server isn't running, the POST silently fails
630+
566631
## Screenshots — LLM-Optimized
567632

568633
Take screenshots and **reduce them up to 95%** for LLM context. A typical screenshot goes from ~100 KB to ~5 KB — that's thousands of tokens saved per image.
@@ -633,6 +698,8 @@ Opens `http://localhost:3333` with:
633698
- **File signatures** - Click any file to see its types and methods
634699
- **Live reload** - Changes detected automatically while you code
635700
- **Git status icons** - See which files are modified, staged, or untracked
701+
- **Logs tab** - Live log stream from Log Hub with filters (level, source, text search)
702+
- **Tasks tab** - View and manage your task backlog
636703

637704
![AiDex Viewer - Signatures](docs/aidex-viewer.png)
638705

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "aidex-mcp",
3-
"version": "1.16.0",
3+
"version": "1.16.1",
44
"mcpName": "io.github.CSCSoftware/aidex",
55
"description": "MCP Server for persistent code indexing. Gives AI assistants (Claude, Gemini, Copilot, Cursor) instant access to your codebase. 50x less context than grep.",
66
"main": "build/index.js",

src/commands/log.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ export async function log(params: LogParams): Promise<LogResult> {
9999
source: params.source,
100100
contains: params.contains,
101101
limit: params.limit ?? 50,
102+
consume: params.consume ?? false,
102103
});
103104

104105
return { success: true, action, entries };

src/loghub/log-buffer.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,9 +53,11 @@ export class LogBuffer {
5353
source?: string;
5454
contains?: string;
5555
limit?: number;
56+
consume?: boolean;
5657
}): LogEntry[] {
5758
const limit = opts?.limit ?? 50;
5859
const results: LogEntry[] = [];
60+
const matchedIndices: number[] = [];
5961

6062
// Iterate backwards from most recent
6163
for (let i = 0; i < this.count && results.length < limit; i++) {
@@ -70,6 +72,16 @@ export class LogBuffer {
7072
if (opts?.contains && !entry.message.toLowerCase().includes(opts.contains.toLowerCase())) continue;
7173

7274
results.push(entry);
75+
if (opts?.consume) matchedIndices.push(idx);
76+
}
77+
78+
// Remove consumed entries from buffer
79+
if (opts?.consume && matchedIndices.length > 0) {
80+
for (const idx of matchedIndices) {
81+
this.buffer[idx] = null;
82+
}
83+
this.count -= matchedIndices.length;
84+
if (this.count < 0) this.count = 0;
7385
}
7486

7587
return results;

src/loghub/log-types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ export interface LogParams {
3737
source?: string;
3838
contains?: string;
3939
limit?: number;
40+
consume?: boolean; // If true, returned entries are removed from the buffer (poll pattern)
4041
// write
4142
message?: string;
4243
data?: string;

src/server/tools.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -803,6 +803,10 @@ export function registerTools(): Tool[] {
803803
type: 'number',
804804
description: 'Max entries to return (default: 50, used with query)',
805805
},
806+
consume: {
807+
type: 'boolean',
808+
description: 'If true, returned entries are removed from the buffer — ideal for polling without duplicates (default: false, used with query)',
809+
},
806810
message: {
807811
type: 'string',
808812
description: 'Log message text (required for write)',
@@ -2516,6 +2520,7 @@ async function handleLog(args: Record<string, unknown>): Promise<{ content: Arra
25162520
source: args.source as string | undefined,
25172521
contains: args.contains as string | undefined,
25182522
limit: args.limit as number | undefined,
2523+
consume: args.consume as boolean | undefined,
25192524
message: args.message as string | undefined,
25202525
data: args.data as string | undefined,
25212526
});
@@ -2574,7 +2579,7 @@ async function handleLog(args: Record<string, unknown>): Promise<{ content: Arra
25742579
const time = new Date(e.timestamp).toISOString().slice(11, 23);
25752580
const icon = levelIcon[e.level] || '';
25762581
msg += `${icon} \`${time}\` **[${e.source}]** ${e.message}`;
2577-
if (e.data) msg += ` \`${e.data}\``;
2582+
if (e.data && e.data !== 'null') msg += ` \`${e.data}\``;
25782583
msg += '\n';
25792584
}
25802585
return { content: [{ type: 'text', text: msg.trimEnd() }] };

0 commit comments

Comments
 (0)