phase 4 planning

This commit is contained in:
Lucas Berger
2026-01-30 15:29:24 -05:00
parent 0f1ceba947
commit 0ee37acfec
2 changed files with 451 additions and 0 deletions
@@ -0,0 +1,198 @@
---
phase: 04-logs-intelligence
plan: 01
type: execute
wave: 1
depends_on: []
files_modified: [n8n-workflow.json]
autonomous: true
user_setup:
- service: anthropic
why: "Claude API for natural language understanding"
env_vars:
- name: ANTHROPIC_API_KEY
source: "Anthropic Console -> API Keys -> Create Key"
dashboard_config: []
must_haves:
truths:
- "User can request logs for a container by name"
- "User can specify number of log lines (default 50)"
- "Logs are returned in readable format in Telegram"
artifacts:
- path: "n8n-workflow.json"
provides: "Logs command routing and Docker API integration"
contains: "logs"
key_links:
- from: "Switch node"
to: "Docker logs API call"
via: "logs command pattern match"
pattern: "logs|show logs"
- from: "Docker API response"
to: "Telegram reply"
via: "log formatting code node"
---
<objective>
Implement container log retrieval via Telegram commands.
Purpose: Delivers REQ-07 (view logs with configurable line count) - users can troubleshoot containers directly from their phone by viewing recent logs.
Output: Extended n8n workflow with logs command routing, Docker logs API call, and formatted log response.
</objective>
<execution_context>
@/home/luc/.claude/get-shit-done/workflows/execute-plan.md
@/home/luc/.claude/get-shit-done/templates/summary.md
</execution_context>
<context>
@.planning/PROJECT.md
@.planning/ROADMAP.md
@.planning/STATE.md
@.planning/phases/04-logs-intelligence/04-RESEARCH.md
@.planning/phases/02-docker-integration/02-02-SUMMARY.md
@n8n-workflow.json
</context>
<tasks>
<task type="auto">
<name>Task 1: Add logs command routing to workflow</name>
<files>n8n-workflow.json</files>
<action>
Extend the main Switch node to detect logs commands. Pattern: "logs <container>" or "show logs <container>" with optional line count.
Add new route in Switch node:
- Condition: message matches /^(show\s+)?logs\s+/i
- Route to new "Parse Logs Command" Code node
Create "Parse Logs Command" Code node:
- Extract container name from message
- Extract line count if specified (e.g., "logs plex 100"), default to 50
- Return: { container: string, lines: number }
Example inputs to handle:
- "logs plex" -> { container: "plex", lines: 50 }
- "show logs sonarr" -> { container: "sonarr", lines: 50 }
- "logs nginx 100" -> { container: "nginx", lines: 100 }
- "show logs radarr last 200" -> { container: "radarr", lines: 200 }
</action>
<verify>
In n8n workflow editor, send message "logs test" - should route to Parse Logs Command node (visible in execution view). Check output includes container and lines fields.
</verify>
<done>Logs commands route to dedicated branch with parsed container name and line count.</done>
</task>
<task type="auto">
<name>Task 2: Implement Docker logs API call with formatting</name>
<files>n8n-workflow.json</files>
<action>
After Parse Logs Command, add container matching (reuse existing fuzzy match pattern from actions branch).
Create "Build Logs Command" Code node:
- Input: matched container ID and requested line count
- Build curl command:
```
curl -s --unix-socket /var/run/docker.sock "http://localhost/v1.53/containers/CONTAINER_ID/logs?stdout=1&stderr=1&tail=LINES&timestamps=1"
```
- Note: Docker logs API returns binary stream with 8-byte header per line. First byte indicates stream (1=stdout, 2=stderr).
Create "Execute Logs" Execute Command node:
- Run the curl command
Create "Format Logs" Code node:
- Parse Docker log stream format (strip 8-byte headers from each line)
- Format for Telegram:
- Truncate if > 4000 chars (Telegram message limit)
- Add header: "Logs for <container> (last N lines):"
- Use monospace formatting with <pre> tags (HTML parse mode already enabled)
- Handle empty logs gracefully: "No logs available for <container>"
- Handle errors: "Could not retrieve logs for <container>: <error>"
Wire to Telegram Send Message node for reply.
</action>
<verify>
Test via Telegram:
1. "logs n8n" - should return recent n8n container logs in monospace format
2. "logs n8n 10" - should return only 10 lines
3. "logs nonexistent" - should return friendly "no container found" message
</verify>
<done>Users can retrieve container logs via Telegram with configurable line count, formatted for readability.</done>
</task>
<task type="auto">
<name>Task 3: Handle Docker log stream binary format</name>
<files>n8n-workflow.json</files>
<action>
The Docker logs API returns a multiplexed stream with 8-byte headers. Each frame:
- Byte 0: stream type (1=stdout, 2=stderr)
- Bytes 1-3: reserved (zeros)
- Bytes 4-7: frame size (big-endian uint32)
- Remaining bytes: log content
Update "Format Logs" Code node to properly decode this:
```javascript
// Docker logs binary stream decoder
const rawOutput = $input.item.json.stdout || '';
// Docker API with timestamps returns text lines when using tail parameter
// But may have 8-byte binary headers we need to strip
const lines = rawOutput.split('\n')
.filter(line => line.length > 0)
.map(line => {
// Check if line starts with binary header (non-printable chars in first 8 bytes)
if (line.length > 8 && line.charCodeAt(0) <= 2) {
return line.substring(8);
}
return line;
})
.join('\n');
// Truncate for Telegram (4096 char limit, leave room for header)
const maxLen = 3800;
const truncated = lines.length > maxLen
? lines.substring(0, maxLen) + '\n... (truncated)'
: lines;
return {
formatted: truncated,
lineCount: lines.split('\n').length
};
```
Add error indicator prefix for stderr lines if desired (optional enhancement).
</action>
<verify>
Test with a container known to have logs:
1. Send "logs n8n 20" via Telegram
2. Verify output is readable text (no garbled binary characters)
3. Verify timestamps appear if container outputs them
</verify>
<done>Docker log binary stream format properly decoded into readable text.</done>
</task>
</tasks>
<verification>
After all tasks:
1. Send "logs n8n" - returns formatted logs
2. Send "logs plex 100" - returns up to 100 lines
3. Send "show logs sonarr" - alternative syntax works
4. Send "logs nonexistent" - friendly error message
5. Logs display in monospace (preformatted) in Telegram
</verification>
<success_criteria>
- REQ-07 delivered: Users can view container logs with configurable line count
- Default 50 lines when not specified
- Multiple syntax variations supported (logs X, show logs X)
- Binary stream format properly decoded
- Output formatted for Telegram readability (monospace, truncated if needed)
</success_criteria>
<output>
After completion, create `.planning/phases/04-logs-intelligence/04-01-SUMMARY.md`
</output>
@@ -0,0 +1,253 @@
---
phase: 04-logs-intelligence
plan: 02
type: execute
wave: 1
depends_on: []
files_modified: [n8n-workflow.json]
autonomous: false
user_setup:
- service: anthropic
why: "Claude API for natural language understanding"
env_vars:
- name: ANTHROPIC_API_KEY
source: "Anthropic Console -> API Keys -> Create Key"
dashboard_config: []
must_haves:
truths:
- "Messages are parsed by Claude for intent detection"
- "Recognized intents route to appropriate handlers"
- "Unknown intents receive helpful response"
artifacts:
- path: "n8n-workflow.json"
provides: "Claude API integration and intent routing"
contains: "anthropic"
key_links:
- from: "Telegram message"
to: "Claude API"
via: "HTTP Request node"
pattern: "api.anthropic.com"
- from: "Claude response"
to: "Switch node"
via: "intent parsing code node"
pattern: "action.*view_logs|query_stats"
---
<objective>
Integrate Claude API for natural language understanding and intent-first message routing.
Purpose: Enables conversational interaction (REQ-08) by understanding user intent before execution. Users can speak naturally instead of memorizing command syntax.
Output: Extended n8n workflow with Claude API integration, intent parsing, and routing logic.
</objective>
<execution_context>
@/home/luc/.claude/get-shit-done/workflows/execute-plan.md
@/home/luc/.claude/get-shit-done/templates/summary.md
</execution_context>
<context>
@.planning/PROJECT.md
@.planning/ROADMAP.md
@.planning/STATE.md
@.planning/phases/04-logs-intelligence/04-RESEARCH.md
@n8n-workflow.json
</context>
<tasks>
<task type="auto">
<name>Task 1: Add Claude API credential and HTTP Request node</name>
<files>n8n-workflow.json</files>
<action>
Create Claude API integration in the workflow.
1. Add "Claude Intent Parser" HTTP Request node:
- Method: POST
- URL: https://api.anthropic.com/v1/messages
- Authentication: Header Auth
- Header: x-api-key = {{$credentials.anthropicApi.apiKey}} (or use Generic Credential)
- Header: anthropic-version = 2023-06-01
- Header: content-type = application/json
- Body (JSON):
```json
{
"model": "claude-sonnet-4-5-20250929",
"max_tokens": 256,
"system": [
{
"type": "text",
"text": "You are a Docker container management assistant. Parse user requests and return ONLY valid JSON.\n\nValid actions:\n- view_logs: User wants to see container logs\n- query_stats: User asks about resource usage (memory, CPU)\n- container_action: User wants to start/stop/restart/update a container\n- container_status: User asks about container status\n- list_containers: User wants to see all containers\n- unknown: Cannot determine intent\n\nRespond with JSON: {\"action\": \"<action>\", \"container\": \"<name or null>\", \"parameters\": {}}\\n\\nExamples:\\n- \"show me plex logs\" -> {\"action\": \"view_logs\", \"container\": \"plex\", \"parameters\": {\"lines\": 50}}\\n- \"what's using the most memory?\" -> {\"action\": \"query_stats\", \"container\": null, \"parameters\": {\"metric\": \"memory\", \"sort\": \"desc\"}}\\n- \"restart nginx\" -> {\"action\": \"container_action\", \"container\": \"nginx\", \"parameters\": {\"action\": \"restart\"}}\\n- \"how's sonarr doing?\" -> {\"action\": \"container_status\", \"container\": \"sonarr\", \"parameters\": {}}\\n- \"hello\" -> {\"action\": \"unknown\", \"container\": null, \"parameters\": {\"message\": \"I can help with Docker containers. Try: 'show logs', 'restart plex', or 'what's using memory?'\"}}",
"cache_control": {"type": "ephemeral"}
}
],
"messages": [
{"role": "user", "content": "{{$json.message}}"}
]
}
```
- Options: Timeout 30000ms, Retry on fail (3 attempts)
Note: n8n may need a "Header Auth" credential type created with the API key, OR use the body to include auth. Check n8n's HTTP Request node documentation for Anthropic compatibility.
Alternative if Header Auth credential doesn't work well:
- Use "None" authentication
- Add x-api-key header manually via "Send Headers" option
- Value: Use expression referencing an n8n credential or environment variable
</action>
<verify>
1. In n8n, navigate to Credentials and ensure Anthropic/Header credential is configured
2. Test the HTTP Request node manually with a simple message like "hello"
3. Check response contains valid JSON with action field
</verify>
<done>Claude API HTTP Request node configured and able to receive intent parsing responses.</done>
</task>
<task type="auto">
<name>Task 2: Create intent parsing and validation logic</name>
<files>n8n-workflow.json</files>
<action>
After Claude HTTP Request node, add "Parse Intent" Code node to validate and extract the structured intent.
```javascript
// Parse and validate Claude's intent response
const response = $input.item.json;
// Claude response structure: { content: [{ type: "text", text: "..." }] }
let intentText = '';
try {
intentText = response.content[0].text;
} catch (e) {
return {
action: 'error',
error: 'Invalid Claude response structure',
raw: JSON.stringify(response)
};
}
// Parse JSON from Claude's response
let intent;
try {
// Claude might wrap JSON in markdown code blocks, strip them
const cleaned = intentText.replace(/```json\n?/g, '').replace(/```\n?/g, '').trim();
intent = JSON.parse(cleaned);
} catch (e) {
return {
action: 'error',
error: 'Could not parse intent JSON',
raw: intentText
};
}
// Validate required fields
const validActions = ['view_logs', 'query_stats', 'container_action', 'container_status', 'list_containers', 'unknown'];
if (!intent.action || !validActions.includes(intent.action)) {
return {
action: 'unknown',
error: 'Invalid or missing action',
parameters: { message: 'I didn\'t understand that. Try: "show logs plex" or "restart nginx"' }
};
}
// Normalize container name if present
if (intent.container) {
intent.container = intent.container.toLowerCase().trim();
}
// Set defaults for parameters
intent.parameters = intent.parameters || {};
return intent;
```
</action>
<verify>
Test Parse Intent node with various Claude responses:
1. Valid JSON response -> extracts action and container
2. Malformed JSON -> returns error action with helpful message
3. Missing action field -> returns unknown with guidance
</verify>
<done>Intent parsing extracts and validates Claude's response into usable workflow data.</done>
</task>
<task type="auto">
<name>Task 3: Wire intent router to existing and new handlers</name>
<files>n8n-workflow.json</files>
<action>
Restructure workflow to use intent-first routing:
1. After auth check (existing), route ALL messages through Claude Intent Parser first (not just unknown messages).
2. Add "Intent Router" Switch node after Parse Intent:
- Condition 1: action == "view_logs" -> Route to logs handler (from 04-01)
- Condition 2: action == "query_stats" -> Route to stats handler (placeholder for 04-03)
- Condition 3: action == "container_action" -> Route to existing actions branch
- Condition 4: action == "container_status" -> Route to existing status branch
- Condition 5: action == "list_containers" -> Route to existing status summary
- Condition 6: action == "unknown" -> Route to Unknown Intent handler
- Condition 7: action == "error" -> Route to Error handler
3. Create "Unknown Intent" handler:
- Telegram Send Message with helpful text from intent.parameters.message
- Default: "I can help manage your Docker containers. Try:\n- 'show logs plex'\n- 'restart sonarr'\n- 'what containers are running?'\n- 'what's using the most memory?'"
4. Create "Stats Placeholder" handler (will be replaced in 04-03):
- Returns: "Stats queries coming soon! For now, try 'status' to see running containers."
5. Update existing action/status handlers to use intent.container instead of re-parsing message.
Important: Keep existing command-based routing as fallback for when Claude API is unavailable (rate limit, error). Add error handling around Claude call that falls back to pattern matching.
</action>
<verify>
Test complete flow via Telegram:
1. "show me nginx logs" -> routes to view_logs handler
2. "restart plex" -> routes to container_action handler
3. "how's sonarr?" -> routes to container_status handler
4. "what's running" -> routes to list_containers handler
5. "hello there" -> routes to unknown with helpful message
6. "what's eating memory?" -> routes to stats placeholder
</verify>
<done>All messages routed through Claude intent parsing with appropriate handler dispatch.</done>
</task>
</tasks>
<checkpoint type="human-verify" gate="blocking">
<what-built>Claude API integration with intent-first message routing</what-built>
<how-to-verify>
1. Open Telegram chat with the bot
2. Send: "show me the logs for n8n"
- Expected: Should return n8n container logs (if 04-01 complete) or route correctly
3. Send: "restart plex" (or any container you can safely restart)
- Expected: Should trigger restart via existing action handler
4. Send: "hey how are you"
- Expected: Should return helpful message about available commands
5. Send: "what's using the most CPU?"
- Expected: Should return "Stats queries coming soon!" placeholder
6. Check n8n execution log to verify Claude API was called and intent parsed
</how-to-verify>
<resume-signal>Type "approved" if conversational routing works, or describe issues</resume-signal>
</checkpoint>
<verification>
After all tasks:
1. Claude API credential configured in n8n
2. HTTP Request node successfully calls Claude Messages API
3. Intent parsing extracts action, container, and parameters
4. Switch node routes to correct handlers based on intent
5. Unknown intents receive helpful guidance
6. Existing command-based functionality still works
</verification>
<success_criteria>
- Claude Sonnet 4.5 used for intent parsing with prompt caching
- Natural language queries parsed into structured intents
- All existing functionality (status, actions) accessible via conversation
- Graceful fallback/error handling when Claude unavailable
- Unknown intents guide users toward valid commands
</success_criteria>
<output>
After completion, create `.planning/phases/04-logs-intelligence/04-02-SUMMARY.md`
</output>