Merge pull request 'ai-enhancement' (#23) from ai-enhancement into main

Reviewed-on: mstoeck3/cc24-hub#23
This commit is contained in:
Mario Stöckl 2025-07-26 21:29:50 +00:00
commit 0327f9cea8
5 changed files with 1547 additions and 536 deletions

File diff suppressed because it is too large Load Diff

View File

@ -878,23 +878,33 @@ tools:
- reporting - reporting
- data-processing - data-processing
- scripting - scripting
- name: "Android Logical Imaging" - name: Android Logical Imaging
icon: "📋" icon: 📋
type: "method" type: method
description: "Es gibt immer wieder auch Fälle, wo man nicht allermodernste Mobilgeräte knacken muss - der Großvater, der im Nachlass ein altes Samsung mit wichtigen Daten hinterlassen hat. description: >-
Es muss in diesn Fällen nicht der teure Hersteller aus Israel sein. Die Androis-ADB-Shell bietet genug Möglichkeiten, auch ohne viel Geld auszugeben an das logische Dateisystem zu gelangen. Es gibt immer wieder auch Fälle, wo man nicht allermodernste Mobilgeräte
Die Erfolgsaussichten sinken jedoch massiv bei neuren Geräten." knacken muss - der Großvater, der im Nachlass ein altes Samsung mit
domains: ["mobile-forensics"] wichtigen Daten hinterlassen hat. Es muss in diesn Fällen nicht der teure
phases: ["data-collection"] Hersteller aus Israel sein. Die Androis-ADB-Shell bietet genug
Möglichkeiten, auch ohne viel Geld auszugeben an das logische Dateisystem
zu gelangen. Die Erfolgsaussichten sinken jedoch massiv bei neuren
Geräten.
domains:
- mobile-forensics
phases:
- data-collection
platforms: [] platforms: []
skillLevel: "advanced" skillLevel: advanced
accessType: null accessType: null
url: "https://claude.ai/public/artifacts/66785e1f-62bb-4eb9-9269-b08648161742" url: https://claude.ai/public/artifacts/66785e1f-62bb-4eb9-9269-b08648161742
projectUrl: null projectUrl: null
license: null license: null
knowledgebase: true knowledgebase: true
related_concepts: null related_concepts: null
tags: ["imaging", "filesystem", "hardware-interface"] tags:
- imaging
- filesystem
- hardware-interface
- name: Microsoft Office 365 - name: Microsoft Office 365
type: software type: software
description: >- description: >-
@ -2081,6 +2091,29 @@ tools:
- evidence-preservation - evidence-preservation
- malware-identification - malware-identification
- chain-of-custody - chain-of-custody
- name: MSAB XRY
type: software
description: >-
Die Smartphone-Extraktionssoftware der Firma MSAB positioniert sich als
Konkurrenz zum Marktführer Cellebrite. MSAB wirbt mit dem Imaging selbst
neuester Android- und IOS-Geräte.
skillLevel: beginner
url: https://www.msab.com/product/xry-extract/
icon: 📦
domains:
- mobile-forensics
phases:
- data-collection
tags:
- law-enforcement
- fast
- SIM
- image-recognition
platforms:
- Windows
accessType: download
license: Proprietary
knowledgebase: false
domains: domains:
- id: incident-response - id: incident-response
name: Incident Response & Breach-Untersuchung name: Incident Response & Breach-Untersuchung

View File

@ -0,0 +1,186 @@
// src/pages/api/ai/enhance-input.ts
import type { APIRoute } from 'astro';
import { withAPIAuth } from '../../../utils/auth.js';
import { apiError, apiServerError, createAuthErrorResponse } from '../../../utils/api.js';
import { enqueueApiCall } from '../../../utils/rateLimitedQueue.js';
export const prerender = false;
function getEnv(key: string): string {
const value = process.env[key];
if (!value) {
throw new Error(`Missing environment variable: ${key}`);
}
return value;
}
const AI_MODEL = getEnv('AI_MODEL');
const rateLimitStore = new Map<string, { count: number; resetTime: number }>();
const RATE_LIMIT_WINDOW = 60 * 1000; // 1 minute
const RATE_LIMIT_MAX = 5; // 5 enhancement requests per minute per user
function sanitizeInput(input: string): string {
return input
.replace(/```[\s\S]*?```/g, '[CODE_BLOCK_REMOVED]')
.replace(/\<\/?[^>]+(>|$)/g, '')
.replace(/\b(system|assistant|user)\s*[:]/gi, '[ROLE_REMOVED]')
.replace(/\b(ignore|forget|disregard)\s+(previous|all|your)\s+(instructions?|context|rules?)/gi, '[INSTRUCTION_REMOVED]')
.trim()
.slice(0, 1000); // Shorter limit for enhancement
}
function checkRateLimit(userId: string): boolean {
const now = Date.now();
const userLimit = rateLimitStore.get(userId);
if (!userLimit || now > userLimit.resetTime) {
rateLimitStore.set(userId, { count: 1, resetTime: now + RATE_LIMIT_WINDOW });
return true;
}
if (userLimit.count >= RATE_LIMIT_MAX) {
return false;
}
userLimit.count++;
return true;
}
function cleanupExpiredRateLimits() {
const now = Date.now();
for (const [userId, limit] of rateLimitStore.entries()) {
if (now > limit.resetTime) {
rateLimitStore.delete(userId);
}
}
}
// Clean up expired limits every 5 minutes
setInterval(cleanupExpiredRateLimits, 5 * 60 * 1000);
function createEnhancementPrompt(input: string): string {
return `
Du bist eine KI für digitale Forensik. Der Nutzer beschreibt ein forensisches Szenario. Analysiere die Eingabe.
Wenn die Beschreibung unvollständig oder vage ist, stelle bis zu drei präzise Rückfragen im JSON-Array-Format, um wichtige Details zu klären (z.B. Vorfalltyp, System, Ziel, Datenquellen, Zeit, Beteiligte, rechtlicher Rahmen).
Wenn die Eingabe bereits klar, spezifisch und vollständig ist, gib stattdessen nur eine leere Liste [] zurück.
Antwortformat strikt:
\`\`\`json
[
"Frage 1?",
"Frage 2?",
"Frage 3?"
]
\`\`\`
Nutzer-Eingabe:
${input}
`.trim();
}
export const POST: APIRoute = async ({ request }) => {
try {
const authResult = await withAPIAuth(request, 'ai');
if (!authResult.authenticated) {
return createAuthErrorResponse();
}
const userId = authResult.userId;
if (!checkRateLimit(userId)) {
return apiError.rateLimit('Enhancement rate limit exceeded');
}
const body = await request.json();
const { input } = body;
if (!input || typeof input !== 'string' || input.length < 20) {
return apiError.badRequest('Input too short for enhancement');
}
const sanitizedInput = sanitizeInput(input);
if (sanitizedInput.length < 20) {
return apiError.badRequest('Input too short after sanitization');
}
const systemPrompt = createEnhancementPrompt(sanitizedInput);
const taskId = `enhance_${userId}_${Date.now()}_${Math.random().toString(36).substr(2, 4)}`;
const aiResponse = await enqueueApiCall(() =>
fetch(process.env.AI_API_ENDPOINT + '/v1/chat/completions', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${process.env.AI_API_KEY}`
},
body: JSON.stringify({
model: AI_MODEL,
messages: [
{
role: 'user',
content: systemPrompt
}
],
max_tokens: 200,
temperature: 0.7
})
}), taskId);
if (!aiResponse.ok) {
console.error('AI enhancement error:', await aiResponse.text());
return apiServerError.unavailable('Enhancement service unavailable');
}
const aiData = await aiResponse.json();
const aiContent = aiData.choices?.[0]?.message?.content;
if (!aiContent) {
return apiServerError.unavailable('No enhancement response');
}
let questions;
try {
const cleanedContent = aiContent
.replace(/^```json\s*/i, '')
.replace(/\s*```\s*$/, '')
.trim();
questions = JSON.parse(cleanedContent);
if (!Array.isArray(questions) || questions.length === 0) {
throw new Error('Invalid questions format');
}
// Validate and clean questions
questions = questions
.filter(q => typeof q === 'string' && q.length > 5 && q.length < 120)
.slice(0, 3);
if (questions.length === 0) {
throw new Error('No valid questions found');
}
} catch (error) {
console.error('Failed to parse enhancement response:', aiContent);
return apiServerError.unavailable('Invalid enhancement response format');
}
console.log(`[AI Enhancement] User: ${userId}, Questions: ${questions.length}, Input length: ${sanitizedInput.length}`);
return new Response(JSON.stringify({
success: true,
questions,
taskId
}), {
status: 200,
headers: { 'Content-Type': 'application/json' }
});
} catch (error) {
console.error('Enhancement error:', error);
return apiServerError.internal('Enhancement processing failed');
}
};

View File

@ -155,6 +155,11 @@ WICHTIGE REGELN:
8. WICHTIG: Erwähne relevante Hintergrundwissen-Konzepte wenn Tools verwendet werden, die related_concepts haben 8. WICHTIG: Erwähne relevante Hintergrundwissen-Konzepte wenn Tools verwendet werden, die related_concepts haben
9. Konzepte sind NICHT Tools - empfehle sie nicht als actionable Schritte, sondern als Wissensbasis 9. Konzepte sind NICHT Tools - empfehle sie nicht als actionable Schritte, sondern als Wissensbasis
ENHANCED CONTEXTUAL ANALYSIS:
10. Analysiere das Szenario detailliert und identifiziere Schlüsselelemente, Bedrohungen und forensische Herausforderungen
11. Entwickle einen strategischen Untersuchungsansatz basierend auf dem spezifischen Szenario
12. Identifiziere zeitkritische oder besonders wichtige Faktoren für diesen Fall
SOFTWARE/METHODEN-AUSWAHL NACH PHASE: SOFTWARE/METHODEN-AUSWAHL NACH PHASE:
${phaseDescriptions} ${phaseDescriptions}
@ -163,16 +168,18 @@ ${domainAgnosticDescriptions}
ANTWORT-FORMAT (strict JSON): ANTWORT-FORMAT (strict JSON):
{ {
"scenario_analysis": "Detaillierte Analyse des Szenarios auf Deutsch/English", "scenario_analysis": "Detaillierte Analyse des Szenarios: Erkannte Schlüsselelemente, Art des Vorfalls, betroffene Systeme, potentielle Bedrohungen und forensische Herausforderungen",
"investigation_approach": "Strategischer Untersuchungsansatz für dieses spezifische Szenario: Prioritäten, Reihenfolge der Phasen, besondere Überlegungen",
"critical_considerations": "Zeitkritische Faktoren, wichtige Sicherheitsaspekte oder besondere Vorsichtsmaßnahmen für diesen Fall",
"recommended_tools": [ "recommended_tools": [
{ {
"name": "EXAKTER Name aus der Tools-Database", "name": "EXAKTER Name aus der Tools-Database",
"priority": "high|medium|low", "priority": "high|medium|low",
"phase": "${validPhases}", "phase": "${validPhases}",
"justification": "Warum diese Methode für diese Phase und Szenario geeignet ist" "justification": "Warum diese Methode für diese Phase und dieses spezifische Szenario geeignet ist - mit Bezug zu den erkannten Schlüsselelementen"
} }
], ],
"workflow_suggestion": "Vorgeschlagener Untersuchungsablauf", "workflow_suggestion": "Vorgeschlagener Untersuchungsablauf mit konkreten Schritten für dieses Szenario",
"background_knowledge": [ "background_knowledge": [
{ {
"concept_name": "EXAKTER Name aus der Konzepte-Database", "concept_name": "EXAKTER Name aus der Konzepte-Database",
@ -230,15 +237,22 @@ WICHTIGE REGELN:
10. WICHTIG: Erwähne relevante Hintergrundwissen-Konzepte wenn Tools verwendet werden, die related_concepts haben 10. WICHTIG: Erwähne relevante Hintergrundwissen-Konzepte wenn Tools verwendet werden, die related_concepts haben
11. Konzepte sind NICHT Tools - empfehle sie nicht als actionable Schritte, sondern als Wissensbasis 11. Konzepte sind NICHT Tools - empfehle sie nicht als actionable Schritte, sondern als Wissensbasis
ENHANCED CONTEXTUAL ANALYSIS:
12. Analysiere das Problem detailliert und identifiziere technische Anforderungen, Herausforderungen und Erfolgsfaktoren
13. Entwickle einen strategischen Lösungsansatz basierend auf dem spezifischen Problem
14. Identifiziere wichtige Voraussetzungen oder Warnungen für die Anwendung
ANTWORT-FORMAT (strict JSON): ANTWORT-FORMAT (strict JSON):
{ {
"problem_analysis": "Detaillierte Analyse des Problems/der Anforderung", "problem_analysis": "Detaillierte Analyse des Problems: Erkannte technische Anforderungen, Herausforderungen, benötigte Fähigkeiten und Erfolgsfaktoren",
"investigation_approach": "Strategischer Lösungsansatz für dieses spezifische Problem: Herangehensweise, Prioritäten, optimale Anwendungsreihenfolge",
"critical_considerations": "Wichtige Voraussetzungen, potentielle Fallstricke oder Warnungen für die Anwendung der empfohlenen Lösungen",
"recommended_tools": [ "recommended_tools": [
{ {
"name": "EXAKTER Name aus der Tools-Database", "name": "EXAKTER Name aus der Tools-Database",
"rank": 1, "rank": 1,
"suitability_score": "high|medium|low", "suitability_score": "high|medium|low",
"detailed_explanation": "Detaillierte Erklärung, warum dieses Tool/diese Methode das Problem löst", "detailed_explanation": "Detaillierte Erklärung, warum dieses Tool/diese Methode das spezifische Problem löst - mit Bezug zu den erkannten Anforderungen",
"implementation_approach": "Konkrete Schritte/Ansatz zur Anwendung für dieses spezifische Problem", "implementation_approach": "Konkrete Schritte/Ansatz zur Anwendung für dieses spezifische Problem",
"pros": ["Spezifische Vorteile für diesen Anwendungsfall", "Weitere Vorteile"], "pros": ["Spezifische Vorteile für diesen Anwendungsfall", "Weitere Vorteile"],
"cons": ["Potentielle Nachteile oder Limitationen", "Weitere Einschränkungen"], "cons": ["Potentielle Nachteile oder Limitationen", "Weitere Einschränkungen"],
@ -348,6 +362,10 @@ export const POST: APIRoute = async ({ request }) => {
if (mode === 'workflow') { if (mode === 'workflow') {
validatedRecommendation = { validatedRecommendation = {
...recommendation, ...recommendation,
// Ensure all new fields are included with fallbacks
scenario_analysis: recommendation.scenario_analysis || recommendation.problem_analysis || '',
investigation_approach: recommendation.investigation_approach || '',
critical_considerations: recommendation.critical_considerations || '',
recommended_tools: recommendation.recommended_tools?.filter((tool: any) => { recommended_tools: recommendation.recommended_tools?.filter((tool: any) => {
if (!validToolNames.has(tool.name)) { if (!validToolNames.has(tool.name)) {
console.warn(`AI recommended unknown tool: ${tool.name}`); console.warn(`AI recommended unknown tool: ${tool.name}`);
@ -366,6 +384,10 @@ export const POST: APIRoute = async ({ request }) => {
} else { } else {
validatedRecommendation = { validatedRecommendation = {
...recommendation, ...recommendation,
// Ensure all new fields are included with fallbacks
problem_analysis: recommendation.problem_analysis || recommendation.scenario_analysis || '',
investigation_approach: recommendation.investigation_approach || '',
critical_considerations: recommendation.critical_considerations || '',
recommended_tools: recommendation.recommended_tools?.filter((tool: any) => { recommended_tools: recommendation.recommended_tools?.filter((tool: any) => {
if (!validToolNames.has(tool.name)) { if (!validToolNames.has(tool.name)) {
console.warn(`AI recommended unknown tool: ${tool.name}`); console.warn(`AI recommended unknown tool: ${tool.name}`);

View File

@ -967,6 +967,23 @@ input[type="checkbox"] {
transition: var(--transition-fast); transition: var(--transition-fast);
} }
.ai-input-layout {
display: flex;
gap: 1.5rem;
align-items: flex-start;
margin-bottom: 1rem;
}
.ai-textarea-section {
flex: 1;
min-width: 0;
}
.ai-suggestions-section {
flex: 0 0 320px;
min-height: 120px;
}
.ai-input-container textarea:focus { .ai-input-container textarea:focus {
outline: none; outline: none;
border-color: var(--color-primary); border-color: var(--color-primary);
@ -1789,6 +1806,33 @@ footer {
.form-grid.two-columns { .form-grid.two-columns {
grid-template-columns: 1fr; grid-template-columns: 1fr;
} }
.hint-card {
padding: 1rem;
}
.hint-description {
font-size: 0.6875rem;
}
.hint-trigger {
flex-direction: column;
gap: 0.25rem;
}
.ai-input-layout {
gap: 0.75rem;
flex-direction: column;
}
.ai-suggestions-section {
flex: 0 0 auto;
width: 100%;
max-width: none;
}
.ai-textarea-section {
flex: 1;
min-width: 0;
width: 100%;
min-height: 100px;
}
} }
@media (width <= 640px) { @media (width <= 640px) {
@ -1922,4 +1966,444 @@ footer {
.kb-entry .flex.gap-2 { .kb-entry .flex.gap-2 {
gap: 0.5rem; gap: 0.5rem;
} }
.prompting-card {
padding: 0.75rem;
} }
.suggestion-item {
padding: 0.375rem 0.5rem;
font-size: 0.75rem;
}
.position-badge {
width: 28px;
height: 28px;
font-size: 0.875rem;
}
.queue-status-card {
padding: 1rem;
}
.hint-card {
padding: 0.75rem;
}
.hint-title {
font-size: 0.8125rem;
}
.hint-description {
font-size: 0.625rem;
}
}
/* Smart Prompting Styles - Simplified */
.smart-prompting-container {
height: 100%;
animation: smartPromptSlideIn 0.4s cubic-bezier(0.4, 0, 0.2, 1);
}
.prompting-card {
background-color: var(--color-bg-tertiary);
border: 1px solid var(--color-border);
border-radius: 0.5rem;
padding: 1rem;
height: 100%;
min-height: 120px;
display: flex;
flex-direction: column;
opacity: 0.85;
transition: var(--transition-fast);
}
.prompting-card:hover {
opacity: 1;
border-color: var(--color-accent);
}
.prompting-status {
display: flex;
align-items: center;
gap: 0.5rem;
margin-bottom: 0.75rem;
padding-bottom: 0.75rem;
border-bottom: 1px solid var(--color-border);
}
.status-icon {
font-size: 1rem;
flex-shrink: 0;
}
.status-text {
font-size: 0.8125rem;
font-weight: 500;
color: var(--color-text);
flex: 1;
}
.prompting-spinner {
flex-shrink: 0;
animation: spin 1s linear infinite;
}
/* Smart Prompting Hint */
.smart-prompting-hint {
height: 100%;
min-height: 120px;
display: flex;
align-items: center;
animation: hintFadeIn 0.3s ease-in-out;
}
.hint-card {
background: linear-gradient(135deg, var(--color-bg-secondary) 0%, var(--color-bg-tertiary) 100%);
border: 1px dashed var(--color-border);
border-radius: 0.5rem;
padding: 1.25rem;
width: 100%;
text-align: center;
opacity: 0.7;
transition: var(--transition-medium);
}
.hint-card:hover {
opacity: 0.9;
border-color: var(--color-accent);
transform: translateY(-1px);
}
.hint-icon {
margin-bottom: 0.75rem;
opacity: 0.8;
}
.hint-content {
display: flex;
flex-direction: column;
gap: 0.5rem;
}
.hint-title {
margin: 0;
font-size: 0.875rem;
font-weight: 600;
color: var(--color-text);
letter-spacing: 0.025em;
}
.hint-description {
margin: 0;
font-size: 0.75rem;
line-height: 1.5;
color: var(--color-text-secondary);
}
.hint-trigger {
display: flex;
align-items: center;
justify-content: center;
gap: 0.5rem;
margin-top: 0.25rem;
padding-top: 0.75rem;
border-top: 1px solid var(--color-border);
}
.hint-label {
font-size: 0.6875rem;
color: var(--color-text-secondary);
text-transform: uppercase;
letter-spacing: 0.05em;
font-weight: 500;
}
.hint-value {
font-size: 0.75rem;
font-weight: 600;
color: var(--color-accent);
background-color: var(--color-bg);
padding: 0.125rem 0.5rem;
border-radius: 1rem;
border: 1px solid var(--color-accent);
}
/* Hide hint when smart prompting is active */
.smart-prompting-container[style*="block"] ~ .smart-prompting-hint,
.smart-prompting-container:not([style*="none"]) ~ .smart-prompting-hint {
display: none;
}
@keyframes hintFadeIn {
from {
opacity: 0;
transform: translateY(10px);
}
to {
opacity: 0.7;
transform: translateY(0);
}
}
.suggested-questions {
flex: 1;
display: flex;
flex-direction: column;
}
.suggestions-header {
margin-bottom: 0.75rem;
}
.suggestions-label {
font-size: 0.75rem;
color: var(--color-text-secondary);
font-weight: 500;
text-transform: uppercase;
letter-spacing: 0.025em;
}
.questions-list {
flex: 1;
display: flex;
flex-direction: column;
gap: 0.5rem;
margin-bottom: 0.75rem;
}
.suggestion-item {
background-color: var(--color-bg);
border: 1px solid transparent;
border-radius: 0.375rem;
padding: 0.5rem 0.75rem;
font-size: 0.8125rem;
line-height: 1.4;
color: var(--color-text-secondary);
border-left: 2px solid var(--color-accent);
transition: var(--transition-fast);
position: relative;
}
.suggestion-item::before {
content: counter(suggestion-counter);
counter-increment: suggestion-counter;
position: absolute;
left: -8px;
top: -6px;
background-color: var(--color-accent);
color: white;
width: 16px;
height: 16px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 0.625rem;
font-weight: 600;
}
.questions-list {
counter-reset: suggestion-counter;
}
.suggestion-number {
color: var(--color-accent);
font-weight: 600;
margin-right: 0.5rem;
}
.dismiss-button {
align-self: flex-end;
background: none;
border: none;
color: var(--color-text-secondary);
cursor: pointer;
padding: 0.25rem;
border-radius: 0.25rem;
transition: var(--transition-fast);
opacity: 0.7;
}
.dismiss-button:hover {
background-color: var(--color-bg-secondary);
color: var(--color-text);
opacity: 1;
}
/* Queue Status - Improved Design */
.queue-status-card {
max-width: 400px;
margin: 1.5rem auto 0;
background: linear-gradient(135deg, var(--color-bg-secondary) 0%, var(--color-bg-tertiary) 100%);
border: 1px solid var(--color-border);
border-radius: 0.75rem;
padding: 1.25rem;
box-shadow: var(--shadow-sm);
}
.queue-header {
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 1rem;
}
.queue-position-display {
display: flex;
align-items: center;
gap: 0.75rem;
}
.position-badge {
width: 32px;
height: 32px;
background: linear-gradient(135deg, var(--color-primary) 0%, var(--color-accent) 100%);
color: white;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-weight: 700;
font-size: 1rem;
box-shadow: var(--shadow-sm);
}
.position-label {
font-weight: 600;
color: var(--color-text);
font-size: 0.9375rem;
}
.queue-details {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 1rem;
margin-bottom: 1.25rem;
}
.queue-stat {
text-align: center;
padding: 0.75rem;
background-color: var(--color-bg);
border-radius: 0.5rem;
border: 1px solid var(--color-border);
}
.stat-label {
display: block;
font-size: 0.75rem;
color: var(--color-text-secondary);
margin-bottom: 0.25rem;
font-weight: 500;
}
.stat-value {
font-size: 1.125rem;
font-weight: 700;
color: var(--color-primary);
}
.stat-unit {
font-size: 0.75rem;
color: var(--color-text-secondary);
margin-left: 0.25rem;
}
.queue-progress-container {
position: relative;
}
.queue-progress-track {
background-color: var(--color-bg);
border-radius: 8px;
height: 6px;
overflow: hidden;
border: 1px solid var(--color-border);
margin-bottom: 0.75rem;
}
.queue-progress-fill {
height: 100%;
background: linear-gradient(90deg, var(--color-primary) 0%, var(--color-accent) 100%);
width: 0%;
transition: width 0.3s ease;
border-radius: 8px;
}
.task-id-display {
text-align: center;
padding: 0.5rem;
background-color: var(--color-bg);
border-radius: 0.375rem;
border: 1px solid var(--color-border);
}
.task-label {
font-size: 0.6875rem;
color: var(--color-text-secondary);
margin-right: 0.5rem;
text-transform: uppercase;
letter-spacing: 0.025em;
font-weight: 500;
}
.task-id {
font-family: 'SF Mono', 'Monaco', 'Menlo', 'Consolas', monospace;
font-size: 0.6875rem;
color: var(--color-text);
background-color: var(--color-bg-secondary);
padding: 0.125rem 0.375rem;
border-radius: 0.25rem;
border: 1px solid var(--color-border);
}
@keyframes smartPromptSlideIn {
from {
opacity: 0;
transform: translateX(20px);
max-height: 0;
}
to {
opacity: 0.85;
transform: translateX(0);
max-height: 300px;
}
}
/* Enhanced contextual analysis cards */
.contextual-analysis-card {
margin-bottom: 2rem;
border-left: 4px solid;
transition: var(--transition-fast);
}
.contextual-analysis-card:hover {
transform: translateY(-1px);
box-shadow: var(--shadow-md);
}
.contextual-analysis-card.scenario {
border-left-color: var(--color-primary);
}
.contextual-analysis-card.approach {
border-left-color: var(--color-accent);
}
.contextual-analysis-card.critical {
border-left-color: var(--color-warning);
}
.analysis-header {
display: flex;
align-items: center;
gap: 0.5rem;
margin-bottom: 1rem;
font-size: 1rem;
font-weight: 600;
}
.analysis-header.scenario { color: var(--color-primary); }
.analysis-header.approach { color: var(--color-accent); }
.analysis-header.critical { color: var(--color-warning); }