mega-brain-ai 1.1.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.
Potentially problematic release.
This version of mega-brain-ai might be problematic. Click here for more details.
- package/.claude/CLAUDE.md +155 -0
- package/.claude/commands/agents.md +161 -0
- package/.claude/commands/ask.md +117 -0
- package/.claude/commands/benchmark.md +224 -0
- package/.claude/commands/chat.md +343 -0
- package/.claude/commands/compare.md +116 -0
- package/.claude/commands/conclave.md +194 -0
- package/.claude/commands/config.md +133 -0
- package/.claude/commands/council.md +194 -0
- package/.claude/commands/create-agent.md +452 -0
- package/.claude/commands/debate.md +157 -0
- package/.claude/commands/documentation/create-architecture-documentation.md +175 -0
- package/.claude/commands/dossiers.md +180 -0
- package/.claude/commands/evolve.md +223 -0
- package/.claude/commands/extract-dna.md +170 -0
- package/.claude/commands/extract-knowledge.md +507 -0
- package/.claude/commands/inbox.md +296 -0
- package/.claude/commands/ingest-empresa.md +191 -0
- package/.claude/commands/ingest.md +182 -0
- package/.claude/commands/jarvis-briefing.md +67 -0
- package/.claude/commands/jarvis-control.md +169 -0
- package/.claude/commands/jarvis-full.md +181 -0
- package/.claude/commands/jarvis.md +212 -0
- package/.claude/commands/ler-drive.md +212 -0
- package/.claude/commands/log.md +158 -0
- package/.claude/commands/loop.md +133 -0
- package/.claude/commands/loops.md +73 -0
- package/.claude/commands/mission-autopilot.md +538 -0
- package/.claude/commands/mission.md +353 -0
- package/.claude/commands/process-inbox.md +148 -0
- package/.claude/commands/process-jarvis.md +3036 -0
- package/.claude/commands/process-video.md +131 -0
- package/.claude/commands/rag-search.md +78 -0
- package/.claude/commands/resume.md +33 -0
- package/.claude/commands/save.md +38 -0
- package/.claude/commands/scan-inbox.md +125 -0
- package/.claude/commands/setup.md +99 -0
- package/.claude/commands/system-digest.md +243 -0
- package/.claude/commands/verify.md +182 -0
- package/.claude/commands/view-dna.md +169 -0
- package/.claude/hooks/agent_doctor.py +433 -0
- package/.claude/hooks/agent_memory_persister.py +203 -0
- package/.claude/hooks/auto_formatter.py +158 -0
- package/.claude/hooks/checkpoint_writer.py +244 -0
- package/.claude/hooks/claude_md_guard.py +146 -0
- package/.claude/hooks/creation_validator.py +357 -0
- package/.claude/hooks/enforce_dual_location.py +501 -0
- package/.claude/hooks/enforce_plan_mode.py +220 -0
- package/.claude/hooks/inbox_age_alert.py +367 -0
- package/.claude/hooks/jarvis_briefing.py +506 -0
- package/.claude/hooks/ledger_updater.py +301 -0
- package/.claude/hooks/memory_hints_injector.py +251 -0
- package/.claude/hooks/memory_updater.py +202 -0
- package/.claude/hooks/multi_agent_hook.py +464 -0
- package/.claude/hooks/notification_system.py +120 -0
- package/.claude/hooks/pattern_analyzer.py +526 -0
- package/.claude/hooks/pending_tracker.py +188 -0
- package/.claude/hooks/post_batch_cascading.py +1740 -0
- package/.claude/hooks/post_output_validator.py +358 -0
- package/.claude/hooks/post_tool_use.py +120 -0
- package/.claude/hooks/post_write_validator.py +200 -0
- package/.claude/hooks/quality_watchdog.py +394 -0
- package/.claude/hooks/ralph_wiggum.py +277 -0
- package/.claude/hooks/session-source-sync.py +218 -0
- package/.claude/hooks/session_autosave_v2.py +1135 -0
- package/.claude/hooks/session_end.py +203 -0
- package/.claude/hooks/session_start.py +939 -0
- package/.claude/hooks/skill_indexer.py +48 -0
- package/.claude/hooks/skill_router.py +358 -0
- package/.claude/hooks/stop_hook_completeness.py +178 -0
- package/.claude/hooks/subagent_tracker.py +163 -0
- package/.claude/hooks/token_checkpoint.py +584 -0
- package/.claude/hooks/user_prompt_submit.py +125 -0
- package/.claude/rules/ANTHROPIC-STANDARDS.md +384 -0
- package/.claude/rules/CLAUDE-LITE.md +201 -0
- package/.claude/rules/RULE-GROUP-1.md +320 -0
- package/.claude/rules/RULE-GROUP-2.md +307 -0
- package/.claude/rules/RULE-GROUP-3.md +248 -0
- package/.claude/rules/RULE-GROUP-4.md +427 -0
- package/.claude/rules/RULE-GROUP-5.md +388 -0
- package/.claude/rules/RULE-GROUP-6.md +387 -0
- package/.claude/rules/logging.md +53 -0
- package/.claude/rules/mcp-governance.md +128 -0
- package/.claude/rules/pipeline.md +60 -0
- package/.claude/rules/state-management.md +93 -0
- package/.claude/scripts/apply-tags.py +77 -0
- package/.claude/scripts/batch-extract-transcriptions.py +132 -0
- package/.claude/scripts/build-complete-index.py +250 -0
- package/.claude/scripts/build-planilha-index.py +170 -0
- package/.claude/scripts/complete-tag-matching.py +250 -0
- package/.claude/scripts/deduplicate-inbox.py +139 -0
- package/.claude/scripts/docx-xml-extractor.py +141 -0
- package/.claude/scripts/extract-docx-text.py +58 -0
- package/.claude/scripts/extract-single-transcription.py +74 -0
- package/.claude/scripts/extract_docx_from_gdrive.py +77 -0
- package/.claude/scripts/organized-downloader.py +246 -0
- package/.claude/scripts/planilha-tagger.py +187 -0
- package/.claude/scripts/revert-tags.py +70 -0
- package/.claude/scripts/source-sync.py +265 -0
- package/.claude/scripts/tag-inbox-files.py +276 -0
- package/.claude/scripts/tag-inbox-v2.py +253 -0
- package/.claude/scripts/test-extraction.py +35 -0
- package/.claude/scripts/test-full-extraction.py +74 -0
- package/.claude/skills/00-SKILL-CREATOR/SKILL.md +186 -0
- package/.claude/skills/01-SKILL-DOCS-MEGABRAIN/SKILL.md +251 -0
- package/.claude/skills/02-SKILL-PYTHON-MEGABRAIN/SKILL.md +323 -0
- package/.claude/skills/03-SKILL-AGENT-CREATION/SKILL.md +374 -0
- package/.claude/skills/04-SKILL-KNOWLEDGE-EXTRACTION/SKILL.md +318 -0
- package/.claude/skills/05-SKILL-PIPELINE-JARVIS/SKILL.md +430 -0
- package/.claude/skills/06-SKILL-BRAINSTORMING/SKILL.md +72 -0
- package/.claude/skills/07-SKILL-DISPATCHING-PARALLEL-AGENTS/SKILL.md +193 -0
- package/.claude/skills/08-SKILL-EXECUTING-PLANS/SKILL.md +114 -0
- package/.claude/skills/09-SKILL-WRITING-PLANS/SKILL.md +184 -0
- package/.claude/skills/10-SKILL-VERIFICATION-BEFORE-COMPLETION/SKILL.md +130 -0
- package/.claude/skills/11-SKILL-USING-SUPERPOWERS/SKILL.md +105 -0
- package/.claude/skills/DETECTION-PROTOCOL.md +217 -0
- package/.claude/skills/README.md +240 -0
- package/.claude/skills/SKILL-REGISTRY.md +284 -0
- package/.claude/skills/SKILL-SUGGESTIONS.md +114 -0
- package/.claude/skills/_TEMPLATES/SKILL-WRITER-GUIDE.md +385 -0
- package/.claude/skills/chronicler/SKILL.md +146 -0
- package/.claude/skills/chronicler/chronicler_core.py +468 -0
- package/.claude/skills/code-review/SKILL.md +160 -0
- package/.claude/skills/council/SKILL.md +210 -0
- package/.claude/skills/executor/SKILL.md +161 -0
- package/.claude/skills/fase-2-5-tagging/SKILL.md +182 -0
- package/.claude/skills/feature-dev/SKILL.md +154 -0
- package/.claude/skills/finance-agent/SKILL.md +137 -0
- package/.claude/skills/frontend-design/SKILL.md +165 -0
- package/.claude/skills/gdrive-transcription-downloader/SKILL.md +249 -0
- package/.claude/skills/gemini-fallback/SKILL.md +67 -0
- package/.claude/skills/gemini-fallback/gemini_fetch.py +0 -0
- package/.claude/skills/gha/SKILL.md +96 -0
- package/.claude/skills/gha/gha_diagnostic.py +227 -0
- package/.claude/skills/github-workflow/SKILL.md +190 -0
- package/.claude/skills/hookify/SKILL.md +134 -0
- package/.claude/skills/hybrid-source-reading/SKILL.md +265 -0
- package/.claude/skills/jarvis/SKILL.md +546 -0
- package/.claude/skills/jarvis-briefing/SKILL.md +340 -0
- package/.claude/skills/ler-planilha/SKILL.md +281 -0
- package/.claude/skills/plugin-dev/SKILL.md +176 -0
- package/.claude/skills/pr-review-toolkit/SKILL.md +178 -0
- package/.claude/skills/resume/SKILL.md +61 -0
- package/.claude/skills/save/SKILL.md +87 -0
- package/.claude/skills/skill-writer/SKILL.md +153 -0
- package/.claude/skills/skill-writer/examples.md +191 -0
- package/.claude/skills/skill-writer/troubleshooting.md +205 -0
- package/.claude/skills/smart-download-tagger/SKILL.md +148 -0
- package/.claude/skills/source-sync/SKILL.md +240 -0
- package/.claude/skills/sync-docs/SKILL.md +193 -0
- package/.claude/skills/sync-docs/config.json +37 -0
- package/.claude/skills/sync-docs/gdrive_sync.py +358 -0
- package/.claude/skills/sync-docs/reauth.py +71 -0
- package/.claude/skills/talent-agent/SKILL.md +183 -0
- package/.claude/skills/verify/SKILL.md +154 -0
- package/.claude/skills/verify/verify_runner.py +0 -0
- package/.claude/skills/verify-6-levels/SKILL.md +234 -0
- package/.claude/templates/BATCH-LOG-TEMPLATE.md +221 -0
- package/.claudeignore +9 -0
- package/.gitattributes +4 -0
- package/.github/layer1-allowlist.txt +80 -0
- package/.github/layer2-manifest.txt +40 -0
- package/.gitignore +219 -0
- package/README.md +1210 -0
- package/agents/_templates/INDEX.md +741 -0
- package/agents/_templates/TEMPLATE-AGENT-MD-ULTRA-ROBUSTO-V3.md +2399 -0
- package/agents/boardroom/CHECKLIST-MASTER.md +281 -0
- package/agents/boardroom/INTEGRATION-GUIDE.md +406 -0
- package/agents/boardroom/README.md +238 -0
- package/agents/boardroom/config/BOARDROOM-CONFIG.md +186 -0
- package/agents/boardroom/config/TTS-INTEGRATION.md +258 -0
- package/agents/boardroom/config/VOICE-PROFILES.md +624 -0
- package/agents/boardroom/config/voice_mapping.json +128 -0
- package/agents/boardroom/scripts/audio_generator.py +375 -0
- package/agents/boardroom/scripts/audio_generator_edge.py +353 -0
- package/agents/boardroom/scripts/jarvis_boardroom_hook.py +415 -0
- package/agents/boardroom/scripts/notebooklm_generator.py +578 -0
- package/agents/boardroom/templates/EPISODE-TEMPLATE.md +367 -0
- package/agents/boardroom/templates/scene-templates/SCENE-AGENT-DEBATE.md +252 -0
- package/agents/boardroom/templates/scene-templates/SCENE-COUNCIL.md +270 -0
- package/agents/boardroom/templates/scene-templates/SCENE-DNA-CONSULTATION.md +126 -0
- package/agents/boardroom/templates/scene-templates/SCENE-QUESTION.md +174 -0
- package/agents/boardroom/workflows/WORKFLOW-AUDIO-GENERATION.md +421 -0
- package/agents/constitution/BASE-CONSTITUTION.md +254 -0
- package/agents/council/CRITIC.md +197 -0
- package/agents/council/DEVILS-ADVOCATE.md +274 -0
- package/agents/council/SYNTHESIZER.md +293 -0
- package/agents/council/advogado-do-diabo/AGENT.md +489 -0
- package/agents/council/advogado-do-diabo/SOUL.md +100 -0
- package/agents/council/critico-metodologico/AGENT.md +670 -0
- package/agents/council/critico-metodologico/SOUL.md +107 -0
- package/agents/council/sintetizador/AGENT.md +558 -0
- package/agents/council/sintetizador/SOUL.md +94 -0
- package/agents/persons/_example/AGENT-EXAMPLE.md +42 -0
- package/agents/persons/_example/DNA-EXAMPLE.yaml +61 -0
- package/agents/protocols/AGENT-COGNITION-PROTOCOL.md +779 -0
- package/agents/protocols/AGENT-INTEGRITY-PROTOCOL.md +692 -0
- package/agents/protocols/BATCH-VISUAL-PROTOCOL.md +841 -0
- package/agents/protocols/DNA-CONFIG-TEMPLATE.yaml +181 -0
- package/agents/protocols/DNA-EXTRACTION-PROTOCOL.md +370 -0
- package/agents/protocols/EPISTEMIC-PROTOCOL.md +333 -0
- package/agents/protocols/LOG-STRUCTURE-PROTOCOL.md +65 -0
- package/agents/protocols/MEMORY-PROTOCOL.md +567 -0
- package/agents/protocols/NARRATIVE-SYNTHESIS-PROTOCOL.md +278 -0
- package/agents/protocols/PHASE-4-VERIFICATION-CHECKPOINT.md +146 -0
- package/agents/protocols/SOUL-TEMPLATE.md +416 -0
- package/agents/protocols/TEMPLATE-EVOLUTION-PROTOCOL.md +544 -0
- package/agents/protocols/VISUAL-DIFF-PROTOCOL.md +159 -0
- package/agents/sua-empresa/README.md +44 -0
- package/agents/sua-empresa/_example/jds/EXAMPLE-JD.md +42 -0
- package/agents/sua-empresa/_example/org/EXAMPLE-ORG.md +32 -0
- package/agents/sua-empresa/_example/roles/EXAMPLE-ROLE.md +38 -0
- package/bin/cli.js +2 -0
- package/bin/lib/ascii-art.js +234 -0
- package/bin/lib/installer.js +402 -0
- package/bin/lib/setup-wizard.js +95 -0
- package/bin/lib/validate-email.js +109 -0
- package/bin/mega-brain.js +97 -0
- package/bin/push.js +342 -0
- package/bin/templates/env.example +38 -0
- package/inbox/.gitkeep +0 -0
- package/integrations/README.md +46 -0
- package/integrations/mcps/MCP-REGISTRY.md +56 -0
- package/integrations/mcps/excalidraw/CONFIG.md +56 -0
- package/integrations/mcps/gdrive/CONFIG.md +38 -0
- package/knowledge/dna/.gitkeep +0 -0
- package/knowledge/dossiers/persons/.gitkeep +0 -0
- package/knowledge/dossiers/persons/DOSSIER-EXAMPLE.md +49 -0
- package/knowledge/dossiers/system/.gitkeep +0 -0
- package/knowledge/dossiers/themes/.gitkeep +0 -0
- package/knowledge/playbooks/.gitkeep +0 -0
- package/knowledge/playbooks/PLAYBOOK-EXAMPLE.md +50 -0
- package/knowledge/sources/.gitkeep +0 -0
- package/logs/.gitkeep +0 -0
- package/package.json +128 -0
- package/processing/canonical/.gitkeep +0 -0
- package/processing/chunks/.gitkeep +0 -0
- package/processing/insights/.gitkeep +0 -0
- package/processing/narratives/.gitkeep +0 -0
- package/reference/CONSELHO.md +337 -0
- package/reference/CONTEXT7_README.md +28 -0
- package/reference/JARVIS-LOGGING-PROTOCOL.md +380 -0
- package/reference/QUICK-START.md +197 -0
- package/reference/README-RALPH-CASCATEAMENTO.md +207 -0
- package/reference/TEMPLATE-MASTER.md +727 -0
- package/reference/prds/prd-jarvis-mega-brain-v3.md +1305 -0
- package/reference/templates/phase5/IMPLEMENTATION-GUIDE.md +355 -0
- package/reference/templates/phase5/MOGA-BRAIN-PHASE5-TEMPLATES.md +1284 -0
- package/reference/templates/phase5/README.md +165 -0
- package/reference/workflow-claude-code-boris-cherny-continuous-claude.md +2232 -0
- package/system/database/001_moneyclub_buyers.sql +160 -0
- package/system/database/002_premium_token.sql +97 -0
- package/system/database/apply-migration.mjs +129 -0
- package/system/docs/MEGA-BRAIN-DEMO-COMPLETA.md +1226 -0
- package/system/docs/MEGA-BRAIN-MANIFESTO-COMPLETO.md +1054 -0
- package/system/docs/MOGA-BRAIN-EXPLICACAO-COMPLETA.md +791 -0
- package/system/docs/STRATEGIC-INTEGRATION-GUIDE.md +725 -0
- package/system/docs/architecture/01-system-context.md +136 -0
- package/system/docs/architecture/02-components.md +225 -0
- package/system/docs/architecture/03-data-flow.md +235 -0
- package/system/docs/architecture/04-integrations.md +283 -0
- package/system/docs/architecture/README.md +71 -0
- package/system/docs/architecture/diagrams/component-diagram.mmd +50 -0
- package/system/docs/architecture/diagrams/data-flow.mmd +39 -0
- package/system/docs/architecture/diagrams/system-overview.mmd +68 -0
- package/system/protocols/AGENT-AUTHORITY.md +217 -0
- package/system/protocols/CONSTITUICAO-BASE.md +115 -0
- package/system/protocols/CONSTITUTION.md +231 -0
- package/system/protocols/GOVERNANCE-MAP.md +123 -0
- package/system/protocols/HOOK-SECURITY-THREAT-MODEL.md +152 -0
- package/system/protocols/ORQUESTRACAO-PROTOCOL.md +215 -0
- package/system/protocols/_archive/CHUNKING-PROTOCOL.md +207 -0
- package/system/protocols/_archive/ENTITY-RESOLUTION-PROTOCOL.md +269 -0
- package/system/protocols/_archive/INSIGHT-EXTRACTION-PROTOCOL.md +257 -0
- package/system/protocols/_archive/NARRATIVE-SYNTHESIS-PROTOCOL.md +290 -0
- package/system/protocols/agents/AGENT-INTERACTION.md +315 -0
- package/system/protocols/agents/CORTEX-PROTOCOL.md +520 -0
- package/system/protocols/agents/EPISTEMIC-PROTOCOL.md +465 -0
- package/system/protocols/agents/MEMORY-PROTOCOL.md +366 -0
- package/system/protocols/agents/WAR-ROOM.md +355 -0
- package/system/protocols/company/COMPANY-DOCUMENT-PROTOCOL.md +793 -0
- package/system/protocols/company/COMPANY-ENRICHMENT-PROTOCOL.md +679 -0
- package/system/protocols/conclave/CONCLAVE-LOG-TEMPLATE-v2.md +309 -0
- package/system/protocols/conclave/CONCLAVE-PROTOCOL.md +518 -0
- package/system/protocols/conclave/DEBATE-DYNAMICS-CONFIG.yaml +322 -0
- package/system/protocols/conclave/DEBATE-DYNAMICS-PROTOCOL.md +613 -0
- package/system/protocols/conclave/DEBATE-PROTOCOL.md +323 -0
- package/system/protocols/council/COUNCIL-LOG-TEMPLATE-v2.md +309 -0
- package/system/protocols/council/COUNCIL-PROTOCOL.md +518 -0
- package/system/protocols/council/DEBATE-DYNAMICS-CONFIG.yaml +322 -0
- package/system/protocols/council/DEBATE-DYNAMICS-PROTOCOL.md +613 -0
- package/system/protocols/council/DEBATE-PROTOCOL.md +323 -0
- package/system/protocols/dna/DNA-EXTRACTION-PROTOCOL.md +1214 -0
- package/system/protocols/dna/ENRICHMENT-PROTOCOL.md +408 -0
- package/system/protocols/dna/REASONING-MODEL-PROTOCOL.md +331 -0
- package/system/protocols/pipeline/DOSSIER-COMPILATION-PROTOCOL.md +790 -0
- package/system/protocols/pipeline/NARRATIVE-METABOLISM-PROTOCOL.md +292 -0
- package/system/protocols/pipeline/PIPELINE-JARVIS-v2.1.md +606 -0
- package/system/protocols/pipeline/PROMPT-1.1-CHUNKING.md +154 -0
- package/system/protocols/pipeline/PROMPT-1.2-ENTITY-RESOLUTION.md +186 -0
- package/system/protocols/pipeline/PROMPT-2.1-DNA-TAGS-INCREMENT.md +208 -0
- package/system/protocols/pipeline/PROMPT-2.1-INSIGHT-EXTRACTION.md +191 -0
- package/system/protocols/pipeline/PROMPT-3.1-NARRATIVE-SYNTHESIS.md +331 -0
- package/system/protocols/pipeline/SOURCES-COMPILATION-PROTOCOL.md +340 -0
- package/system/protocols/system/AUTO-LOG-PROTOCOL.md +369 -0
- package/system/protocols/system/CHECKPOINT-ENFORCEMENT.md +176 -0
- package/system/protocols/system/ENFORCEMENT.md +435 -0
- package/system/protocols/system/LOG-TEMPLATES.md +1068 -0
|
@@ -0,0 +1,584 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
JARVIS Token Checkpoint System v1.0
|
|
4
|
+
Sistema de checkpoint baseado em estimativa de uso de tokens.
|
|
5
|
+
|
|
6
|
+
Como Claude Code não expõe contagem direta de tokens aos hooks,
|
|
7
|
+
usamos heurísticas baseadas em:
|
|
8
|
+
- Número de ações/tool calls
|
|
9
|
+
- Tamanho do conteúdo processado
|
|
10
|
+
- Turnos de conversa
|
|
11
|
+
|
|
12
|
+
Triggers de checkpoint:
|
|
13
|
+
- 50% do limite estimado (WARNING)
|
|
14
|
+
- 75% do limite estimado (CHECKPOINT)
|
|
15
|
+
- 90% do limite estimado (CRITICAL)
|
|
16
|
+
|
|
17
|
+
Integra com:
|
|
18
|
+
- STATE.json (estado JARVIS)
|
|
19
|
+
- Session autosave (persistência)
|
|
20
|
+
- HANDOFF system (continuidade)
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
import json
|
|
24
|
+
import sys
|
|
25
|
+
import os
|
|
26
|
+
from datetime import datetime
|
|
27
|
+
from pathlib import Path
|
|
28
|
+
from dataclasses import dataclass, field, asdict
|
|
29
|
+
from typing import Dict, List, Optional, Any
|
|
30
|
+
from enum import Enum
|
|
31
|
+
import threading
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
# =============================================================================
|
|
35
|
+
# CONFIGURATION
|
|
36
|
+
# =============================================================================
|
|
37
|
+
|
|
38
|
+
class Config:
|
|
39
|
+
"""Configuração do sistema de checkpoint."""
|
|
40
|
+
PROJECT_DIR = Path(os.environ.get('CLAUDE_PROJECT_DIR', '.'))
|
|
41
|
+
JARVIS_DIR = PROJECT_DIR / ".claude" / "jarvis"
|
|
42
|
+
MISSION_CONTROL_DIR = PROJECT_DIR / ".claude" / "mission-control"
|
|
43
|
+
LOGS_DIR = PROJECT_DIR / "logs"
|
|
44
|
+
|
|
45
|
+
# Estimativas de tokens por tipo de ação
|
|
46
|
+
TOKENS_PER_TOOL_CALL = 500 # Média estimada por tool call
|
|
47
|
+
TOKENS_PER_CHAR_INPUT = 0.25 # ~4 chars por token
|
|
48
|
+
TOKENS_PER_CHAR_OUTPUT = 0.25
|
|
49
|
+
|
|
50
|
+
# Limites de contexto (conservador para Opus)
|
|
51
|
+
CONTEXT_LIMIT = 200000 # Limite estimado de contexto
|
|
52
|
+
WARNING_THRESHOLD = 0.50 # 50% - warning
|
|
53
|
+
CHECKPOINT_THRESHOLD = 0.75 # 75% - checkpoint automático
|
|
54
|
+
CRITICAL_THRESHOLD = 0.90 # 90% - checkpoint crítico
|
|
55
|
+
|
|
56
|
+
# Checkpoint por ações (fallback)
|
|
57
|
+
MAX_ACTIONS_BETWEEN_CHECKPOINTS = 50
|
|
58
|
+
MAX_FILES_BETWEEN_CHECKPOINTS = 20
|
|
59
|
+
|
|
60
|
+
# Arquivos de estado
|
|
61
|
+
STATE_FILE = JARVIS_DIR / "STATE.json"
|
|
62
|
+
CHECKPOINT_FILE = JARVIS_DIR / "TOKEN-CHECKPOINT.json"
|
|
63
|
+
CHECKPOINT_LOG = LOGS_DIR / "token_checkpoints.jsonl"
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
class CheckpointLevel(Enum):
|
|
67
|
+
"""Níveis de checkpoint."""
|
|
68
|
+
NORMAL = "normal"
|
|
69
|
+
WARNING = "warning"
|
|
70
|
+
CHECKPOINT = "checkpoint"
|
|
71
|
+
CRITICAL = "critical"
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
# =============================================================================
|
|
75
|
+
# DATA STRUCTURES
|
|
76
|
+
# =============================================================================
|
|
77
|
+
|
|
78
|
+
@dataclass
|
|
79
|
+
class TokenEstimate:
|
|
80
|
+
"""Estimativa de uso de tokens."""
|
|
81
|
+
input_tokens: int = 0
|
|
82
|
+
output_tokens: int = 0
|
|
83
|
+
tool_call_tokens: int = 0
|
|
84
|
+
total_estimated: int = 0
|
|
85
|
+
percent_of_limit: float = 0.0
|
|
86
|
+
level: str = "normal"
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
@dataclass
|
|
90
|
+
class ActionRecord:
|
|
91
|
+
"""Registro de uma ação para estimativa."""
|
|
92
|
+
timestamp: str
|
|
93
|
+
action_type: str
|
|
94
|
+
input_chars: int = 0
|
|
95
|
+
output_chars: int = 0
|
|
96
|
+
is_tool_call: bool = False
|
|
97
|
+
estimated_tokens: int = 0
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
@dataclass
|
|
101
|
+
class CheckpointData:
|
|
102
|
+
"""Dados de um checkpoint."""
|
|
103
|
+
checkpoint_id: str
|
|
104
|
+
session_id: str
|
|
105
|
+
created_at: str
|
|
106
|
+
level: str
|
|
107
|
+
token_estimate: TokenEstimate
|
|
108
|
+
actions_since_last: int
|
|
109
|
+
files_modified_since_last: int
|
|
110
|
+
summary: str
|
|
111
|
+
state_snapshot: Dict[str, Any] = field(default_factory=dict)
|
|
112
|
+
continuation_hint: str = ""
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
@dataclass
|
|
116
|
+
class TokenCheckpointState:
|
|
117
|
+
"""Estado completo do sistema de checkpoint."""
|
|
118
|
+
session_id: str
|
|
119
|
+
started_at: str
|
|
120
|
+
last_checkpoint_at: Optional[str] = None
|
|
121
|
+
checkpoint_count: int = 0
|
|
122
|
+
current_estimate: TokenEstimate = field(default_factory=TokenEstimate)
|
|
123
|
+
actions: List[ActionRecord] = field(default_factory=list)
|
|
124
|
+
files_modified: List[str] = field(default_factory=list)
|
|
125
|
+
checkpoints: List[str] = field(default_factory=list) # IDs dos checkpoints
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
# =============================================================================
|
|
129
|
+
# TOKEN CHECKPOINT MANAGER
|
|
130
|
+
# =============================================================================
|
|
131
|
+
|
|
132
|
+
class TokenCheckpointManager:
|
|
133
|
+
"""Gerenciador singleton de checkpoints de token."""
|
|
134
|
+
|
|
135
|
+
_instance = None
|
|
136
|
+
_lock = threading.Lock()
|
|
137
|
+
|
|
138
|
+
def __new__(cls):
|
|
139
|
+
with cls._lock:
|
|
140
|
+
if cls._instance is None:
|
|
141
|
+
cls._instance = super().__new__(cls)
|
|
142
|
+
cls._instance._initialized = False
|
|
143
|
+
return cls._instance
|
|
144
|
+
|
|
145
|
+
def __init__(self):
|
|
146
|
+
if self._initialized:
|
|
147
|
+
return
|
|
148
|
+
|
|
149
|
+
self._initialized = True
|
|
150
|
+
self.state = self._load_or_create_state()
|
|
151
|
+
self._ensure_directories()
|
|
152
|
+
|
|
153
|
+
def _ensure_directories(self):
|
|
154
|
+
"""Garante que diretórios existem."""
|
|
155
|
+
Config.JARVIS_DIR.mkdir(parents=True, exist_ok=True)
|
|
156
|
+
Config.MISSION_CONTROL_DIR.mkdir(parents=True, exist_ok=True)
|
|
157
|
+
Config.LOGS_DIR.mkdir(parents=True, exist_ok=True)
|
|
158
|
+
|
|
159
|
+
def _generate_session_id(self) -> str:
|
|
160
|
+
"""Gera ID de sessão."""
|
|
161
|
+
return f"TOKEN-{datetime.now().strftime('%Y%m%d-%H%M%S')}"
|
|
162
|
+
|
|
163
|
+
def _generate_checkpoint_id(self) -> str:
|
|
164
|
+
"""Gera ID de checkpoint."""
|
|
165
|
+
return f"CKPT-{datetime.now().strftime('%Y%m%d-%H%M%S')}-{self.state.checkpoint_count + 1:03d}"
|
|
166
|
+
|
|
167
|
+
def _load_or_create_state(self) -> TokenCheckpointState:
|
|
168
|
+
"""Carrega estado existente ou cria novo."""
|
|
169
|
+
if Config.CHECKPOINT_FILE.exists():
|
|
170
|
+
try:
|
|
171
|
+
with open(Config.CHECKPOINT_FILE, 'r', encoding='utf-8') as f:
|
|
172
|
+
data = json.load(f)
|
|
173
|
+
|
|
174
|
+
# Reconstruir objetos
|
|
175
|
+
state = TokenCheckpointState(
|
|
176
|
+
session_id=data.get('session_id', self._generate_session_id()),
|
|
177
|
+
started_at=data.get('started_at', datetime.now().isoformat()),
|
|
178
|
+
last_checkpoint_at=data.get('last_checkpoint_at'),
|
|
179
|
+
checkpoint_count=data.get('checkpoint_count', 0),
|
|
180
|
+
checkpoints=data.get('checkpoints', []),
|
|
181
|
+
files_modified=data.get('files_modified', [])
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
# Reconstruir token estimate
|
|
185
|
+
if 'current_estimate' in data:
|
|
186
|
+
est = data['current_estimate']
|
|
187
|
+
state.current_estimate = TokenEstimate(
|
|
188
|
+
input_tokens=est.get('input_tokens', 0),
|
|
189
|
+
output_tokens=est.get('output_tokens', 0),
|
|
190
|
+
tool_call_tokens=est.get('tool_call_tokens', 0),
|
|
191
|
+
total_estimated=est.get('total_estimated', 0),
|
|
192
|
+
percent_of_limit=est.get('percent_of_limit', 0.0),
|
|
193
|
+
level=est.get('level', 'normal')
|
|
194
|
+
)
|
|
195
|
+
|
|
196
|
+
# Reconstruir actions
|
|
197
|
+
if 'actions' in data:
|
|
198
|
+
state.actions = [
|
|
199
|
+
ActionRecord(**a) for a in data['actions'][-100:] # Manter últimas 100
|
|
200
|
+
]
|
|
201
|
+
|
|
202
|
+
return state
|
|
203
|
+
|
|
204
|
+
except Exception:
|
|
205
|
+
pass
|
|
206
|
+
|
|
207
|
+
return TokenCheckpointState(
|
|
208
|
+
session_id=self._generate_session_id(),
|
|
209
|
+
started_at=datetime.now().isoformat()
|
|
210
|
+
)
|
|
211
|
+
|
|
212
|
+
def _save_state(self):
|
|
213
|
+
"""Salva estado atual."""
|
|
214
|
+
data = {
|
|
215
|
+
'session_id': self.state.session_id,
|
|
216
|
+
'started_at': self.state.started_at,
|
|
217
|
+
'last_checkpoint_at': self.state.last_checkpoint_at,
|
|
218
|
+
'checkpoint_count': self.state.checkpoint_count,
|
|
219
|
+
'checkpoints': self.state.checkpoints,
|
|
220
|
+
'files_modified': self.state.files_modified[-50:], # Limitar
|
|
221
|
+
'current_estimate': asdict(self.state.current_estimate),
|
|
222
|
+
'actions': [asdict(a) for a in self.state.actions[-100:]],
|
|
223
|
+
'updated_at': datetime.now().isoformat()
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
with open(Config.CHECKPOINT_FILE, 'w', encoding='utf-8') as f:
|
|
227
|
+
json.dump(data, f, indent=2, ensure_ascii=False)
|
|
228
|
+
|
|
229
|
+
def _calculate_level(self, percent: float) -> str:
|
|
230
|
+
"""Determina nível baseado na porcentagem."""
|
|
231
|
+
if percent >= Config.CRITICAL_THRESHOLD:
|
|
232
|
+
return CheckpointLevel.CRITICAL.value
|
|
233
|
+
elif percent >= Config.CHECKPOINT_THRESHOLD:
|
|
234
|
+
return CheckpointLevel.CHECKPOINT.value
|
|
235
|
+
elif percent >= Config.WARNING_THRESHOLD:
|
|
236
|
+
return CheckpointLevel.WARNING.value
|
|
237
|
+
return CheckpointLevel.NORMAL.value
|
|
238
|
+
|
|
239
|
+
def _update_estimate(self):
|
|
240
|
+
"""Atualiza estimativa total de tokens."""
|
|
241
|
+
est = self.state.current_estimate
|
|
242
|
+
est.total_estimated = est.input_tokens + est.output_tokens + est.tool_call_tokens
|
|
243
|
+
est.percent_of_limit = est.total_estimated / Config.CONTEXT_LIMIT
|
|
244
|
+
est.level = self._calculate_level(est.percent_of_limit)
|
|
245
|
+
|
|
246
|
+
def record_action(
|
|
247
|
+
self,
|
|
248
|
+
action_type: str,
|
|
249
|
+
input_chars: int = 0,
|
|
250
|
+
output_chars: int = 0,
|
|
251
|
+
is_tool_call: bool = False
|
|
252
|
+
) -> Dict[str, Any]:
|
|
253
|
+
"""
|
|
254
|
+
Registra uma ação e atualiza estimativa.
|
|
255
|
+
|
|
256
|
+
Returns:
|
|
257
|
+
Dict com status e se checkpoint é necessário
|
|
258
|
+
"""
|
|
259
|
+
# Calcular tokens estimados
|
|
260
|
+
input_tokens = int(input_chars * Config.TOKENS_PER_CHAR_INPUT)
|
|
261
|
+
output_tokens = int(output_chars * Config.TOKENS_PER_CHAR_OUTPUT)
|
|
262
|
+
tool_tokens = Config.TOKENS_PER_TOOL_CALL if is_tool_call else 0
|
|
263
|
+
total = input_tokens + output_tokens + tool_tokens
|
|
264
|
+
|
|
265
|
+
# Criar registro
|
|
266
|
+
record = ActionRecord(
|
|
267
|
+
timestamp=datetime.now().isoformat(),
|
|
268
|
+
action_type=action_type,
|
|
269
|
+
input_chars=input_chars,
|
|
270
|
+
output_chars=output_chars,
|
|
271
|
+
is_tool_call=is_tool_call,
|
|
272
|
+
estimated_tokens=total
|
|
273
|
+
)
|
|
274
|
+
|
|
275
|
+
# Atualizar estado
|
|
276
|
+
self.state.actions.append(record)
|
|
277
|
+
self.state.current_estimate.input_tokens += input_tokens
|
|
278
|
+
self.state.current_estimate.output_tokens += output_tokens
|
|
279
|
+
self.state.current_estimate.tool_call_tokens += tool_tokens
|
|
280
|
+
|
|
281
|
+
# Recalcular
|
|
282
|
+
self._update_estimate()
|
|
283
|
+
|
|
284
|
+
# Verificar se precisa checkpoint
|
|
285
|
+
needs_checkpoint = self._check_checkpoint_needed()
|
|
286
|
+
|
|
287
|
+
# Salvar estado
|
|
288
|
+
self._save_state()
|
|
289
|
+
|
|
290
|
+
return {
|
|
291
|
+
'recorded': True,
|
|
292
|
+
'estimated_tokens': total,
|
|
293
|
+
'total_estimated': self.state.current_estimate.total_estimated,
|
|
294
|
+
'percent_of_limit': self.state.current_estimate.percent_of_limit,
|
|
295
|
+
'level': self.state.current_estimate.level,
|
|
296
|
+
'needs_checkpoint': needs_checkpoint
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
def record_file_modified(self, file_path: str):
|
|
300
|
+
"""Registra arquivo modificado."""
|
|
301
|
+
if file_path not in self.state.files_modified:
|
|
302
|
+
self.state.files_modified.append(file_path)
|
|
303
|
+
self._save_state()
|
|
304
|
+
|
|
305
|
+
def _check_checkpoint_needed(self) -> bool:
|
|
306
|
+
"""Verifica se checkpoint é necessário."""
|
|
307
|
+
est = self.state.current_estimate
|
|
308
|
+
|
|
309
|
+
# Por porcentagem de contexto
|
|
310
|
+
if est.percent_of_limit >= Config.CHECKPOINT_THRESHOLD:
|
|
311
|
+
return True
|
|
312
|
+
|
|
313
|
+
# Por número de ações desde último checkpoint
|
|
314
|
+
actions_since = len([
|
|
315
|
+
a for a in self.state.actions
|
|
316
|
+
if not self.state.last_checkpoint_at or
|
|
317
|
+
a.timestamp > self.state.last_checkpoint_at
|
|
318
|
+
])
|
|
319
|
+
if actions_since >= Config.MAX_ACTIONS_BETWEEN_CHECKPOINTS:
|
|
320
|
+
return True
|
|
321
|
+
|
|
322
|
+
# Por número de arquivos
|
|
323
|
+
files_since = len([
|
|
324
|
+
f for i, f in enumerate(self.state.files_modified)
|
|
325
|
+
if i >= len(self.state.files_modified) - Config.MAX_FILES_BETWEEN_CHECKPOINTS
|
|
326
|
+
])
|
|
327
|
+
if files_since >= Config.MAX_FILES_BETWEEN_CHECKPOINTS:
|
|
328
|
+
return True
|
|
329
|
+
|
|
330
|
+
return False
|
|
331
|
+
|
|
332
|
+
def create_checkpoint(self, summary: str = "") -> CheckpointData:
|
|
333
|
+
"""
|
|
334
|
+
Cria um checkpoint.
|
|
335
|
+
|
|
336
|
+
Args:
|
|
337
|
+
summary: Resumo do trabalho até agora
|
|
338
|
+
|
|
339
|
+
Returns:
|
|
340
|
+
CheckpointData com informações do checkpoint
|
|
341
|
+
"""
|
|
342
|
+
checkpoint_id = self._generate_checkpoint_id()
|
|
343
|
+
|
|
344
|
+
# Carregar estado JARVIS para snapshot
|
|
345
|
+
state_snapshot = {}
|
|
346
|
+
if Config.STATE_FILE.exists():
|
|
347
|
+
try:
|
|
348
|
+
with open(Config.STATE_FILE, 'r', encoding='utf-8') as f:
|
|
349
|
+
state_snapshot = json.load(f)
|
|
350
|
+
except Exception:
|
|
351
|
+
pass
|
|
352
|
+
|
|
353
|
+
# Calcular ações desde último checkpoint
|
|
354
|
+
actions_since = len([
|
|
355
|
+
a for a in self.state.actions
|
|
356
|
+
if not self.state.last_checkpoint_at or
|
|
357
|
+
a.timestamp > self.state.last_checkpoint_at
|
|
358
|
+
])
|
|
359
|
+
|
|
360
|
+
# Criar checkpoint
|
|
361
|
+
checkpoint = CheckpointData(
|
|
362
|
+
checkpoint_id=checkpoint_id,
|
|
363
|
+
session_id=self.state.session_id,
|
|
364
|
+
created_at=datetime.now().isoformat(),
|
|
365
|
+
level=self.state.current_estimate.level,
|
|
366
|
+
token_estimate=self.state.current_estimate,
|
|
367
|
+
actions_since_last=actions_since,
|
|
368
|
+
files_modified_since_last=len(self.state.files_modified),
|
|
369
|
+
summary=summary or f"Checkpoint automático - {self.state.current_estimate.percent_of_limit:.1%} do contexto",
|
|
370
|
+
state_snapshot=state_snapshot,
|
|
371
|
+
continuation_hint=self._generate_continuation_hint()
|
|
372
|
+
)
|
|
373
|
+
|
|
374
|
+
# Atualizar estado
|
|
375
|
+
self.state.checkpoints.append(checkpoint_id)
|
|
376
|
+
self.state.checkpoint_count += 1
|
|
377
|
+
self.state.last_checkpoint_at = checkpoint.created_at
|
|
378
|
+
|
|
379
|
+
# Logar checkpoint
|
|
380
|
+
self._log_checkpoint(checkpoint)
|
|
381
|
+
|
|
382
|
+
# Salvar estado
|
|
383
|
+
self._save_state()
|
|
384
|
+
|
|
385
|
+
return checkpoint
|
|
386
|
+
|
|
387
|
+
def _generate_continuation_hint(self) -> str:
|
|
388
|
+
"""Gera dica de continuação para próxima sessão."""
|
|
389
|
+
recent_actions = self.state.actions[-10:]
|
|
390
|
+
action_types = [a.action_type for a in recent_actions]
|
|
391
|
+
|
|
392
|
+
if 'batch_process' in action_types:
|
|
393
|
+
return "Continuar processamento de batch"
|
|
394
|
+
elif 'file_write' in action_types or 'file_edit' in action_types:
|
|
395
|
+
return "Continuar modificações de arquivos"
|
|
396
|
+
elif 'search' in action_types:
|
|
397
|
+
return "Continuar pesquisa/análise"
|
|
398
|
+
|
|
399
|
+
return "Verificar estado e continuar trabalho"
|
|
400
|
+
|
|
401
|
+
def _log_checkpoint(self, checkpoint: CheckpointData):
|
|
402
|
+
"""Loga checkpoint em arquivo."""
|
|
403
|
+
log_entry = {
|
|
404
|
+
'timestamp': checkpoint.created_at,
|
|
405
|
+
'checkpoint_id': checkpoint.checkpoint_id,
|
|
406
|
+
'session_id': checkpoint.session_id,
|
|
407
|
+
'level': checkpoint.level,
|
|
408
|
+
'percent_of_limit': checkpoint.token_estimate.percent_of_limit,
|
|
409
|
+
'total_tokens': checkpoint.token_estimate.total_estimated,
|
|
410
|
+
'actions_since_last': checkpoint.actions_since_last,
|
|
411
|
+
'summary': checkpoint.summary[:200]
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
with open(Config.CHECKPOINT_LOG, 'a', encoding='utf-8') as f:
|
|
415
|
+
f.write(json.dumps(log_entry, ensure_ascii=False) + '\n')
|
|
416
|
+
|
|
417
|
+
def get_status(self) -> Dict[str, Any]:
|
|
418
|
+
"""Retorna status atual do sistema."""
|
|
419
|
+
return {
|
|
420
|
+
'session_id': self.state.session_id,
|
|
421
|
+
'started_at': self.state.started_at,
|
|
422
|
+
'checkpoint_count': self.state.checkpoint_count,
|
|
423
|
+
'last_checkpoint_at': self.state.last_checkpoint_at,
|
|
424
|
+
'token_estimate': {
|
|
425
|
+
'total': self.state.current_estimate.total_estimated,
|
|
426
|
+
'percent': self.state.current_estimate.percent_of_limit,
|
|
427
|
+
'level': self.state.current_estimate.level
|
|
428
|
+
},
|
|
429
|
+
'actions_count': len(self.state.actions),
|
|
430
|
+
'files_modified': len(self.state.files_modified),
|
|
431
|
+
'needs_checkpoint': self._check_checkpoint_needed()
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
def reset_session(self):
|
|
435
|
+
"""Reseta para nova sessão."""
|
|
436
|
+
self.state = TokenCheckpointState(
|
|
437
|
+
session_id=self._generate_session_id(),
|
|
438
|
+
started_at=datetime.now().isoformat()
|
|
439
|
+
)
|
|
440
|
+
self._save_state()
|
|
441
|
+
|
|
442
|
+
|
|
443
|
+
# =============================================================================
|
|
444
|
+
# GLOBAL API
|
|
445
|
+
# =============================================================================
|
|
446
|
+
|
|
447
|
+
_manager: Optional[TokenCheckpointManager] = None
|
|
448
|
+
|
|
449
|
+
|
|
450
|
+
def get_manager() -> TokenCheckpointManager:
|
|
451
|
+
"""Obtém instância do manager."""
|
|
452
|
+
global _manager
|
|
453
|
+
if _manager is None:
|
|
454
|
+
_manager = TokenCheckpointManager()
|
|
455
|
+
return _manager
|
|
456
|
+
|
|
457
|
+
|
|
458
|
+
def record_action(
|
|
459
|
+
action_type: str,
|
|
460
|
+
input_chars: int = 0,
|
|
461
|
+
output_chars: int = 0,
|
|
462
|
+
is_tool_call: bool = False
|
|
463
|
+
) -> Dict[str, Any]:
|
|
464
|
+
"""Registra uma ação."""
|
|
465
|
+
return get_manager().record_action(action_type, input_chars, output_chars, is_tool_call)
|
|
466
|
+
|
|
467
|
+
|
|
468
|
+
def record_file(file_path: str):
|
|
469
|
+
"""Registra arquivo modificado."""
|
|
470
|
+
get_manager().record_file_modified(file_path)
|
|
471
|
+
|
|
472
|
+
|
|
473
|
+
def create_checkpoint(summary: str = "") -> Dict[str, Any]:
|
|
474
|
+
"""Cria checkpoint."""
|
|
475
|
+
checkpoint = get_manager().create_checkpoint(summary)
|
|
476
|
+
return asdict(checkpoint)
|
|
477
|
+
|
|
478
|
+
|
|
479
|
+
def get_status() -> Dict[str, Any]:
|
|
480
|
+
"""Obtém status atual."""
|
|
481
|
+
return get_manager().get_status()
|
|
482
|
+
|
|
483
|
+
|
|
484
|
+
def needs_checkpoint() -> bool:
|
|
485
|
+
"""Verifica se checkpoint é necessário."""
|
|
486
|
+
return get_manager()._check_checkpoint_needed()
|
|
487
|
+
|
|
488
|
+
|
|
489
|
+
def reset_session():
|
|
490
|
+
"""Reseta sessão."""
|
|
491
|
+
get_manager().reset_session()
|
|
492
|
+
|
|
493
|
+
|
|
494
|
+
# =============================================================================
|
|
495
|
+
# HOOK INTEGRATION
|
|
496
|
+
# =============================================================================
|
|
497
|
+
|
|
498
|
+
def process_hook_input(hook_input: Dict[str, Any]) -> Dict[str, Any]:
|
|
499
|
+
"""
|
|
500
|
+
Processa input de hook e registra ação.
|
|
501
|
+
|
|
502
|
+
Chamado por outros hooks para registrar ações automaticamente.
|
|
503
|
+
"""
|
|
504
|
+
action_type = hook_input.get('tool_name', 'unknown')
|
|
505
|
+
input_content = hook_input.get('tool_input', {})
|
|
506
|
+
output_content = hook_input.get('tool_result', '')
|
|
507
|
+
|
|
508
|
+
# Estimar tamanho
|
|
509
|
+
input_chars = len(json.dumps(input_content)) if input_content else 0
|
|
510
|
+
output_chars = len(str(output_content)) if output_content else 0
|
|
511
|
+
|
|
512
|
+
result = record_action(
|
|
513
|
+
action_type=action_type,
|
|
514
|
+
input_chars=input_chars,
|
|
515
|
+
output_chars=output_chars,
|
|
516
|
+
is_tool_call=True
|
|
517
|
+
)
|
|
518
|
+
|
|
519
|
+
# Se arquivo foi modificado, registrar
|
|
520
|
+
if action_type in ['Write', 'Edit', 'NotebookEdit']:
|
|
521
|
+
file_path = input_content.get('file_path', '')
|
|
522
|
+
if file_path:
|
|
523
|
+
record_file(file_path)
|
|
524
|
+
|
|
525
|
+
return result
|
|
526
|
+
|
|
527
|
+
|
|
528
|
+
# =============================================================================
|
|
529
|
+
# CLI
|
|
530
|
+
# =============================================================================
|
|
531
|
+
|
|
532
|
+
def main():
|
|
533
|
+
"""CLI para teste e debug."""
|
|
534
|
+
import argparse
|
|
535
|
+
|
|
536
|
+
parser = argparse.ArgumentParser(description='JARVIS Token Checkpoint System')
|
|
537
|
+
parser.add_argument('command', choices=['status', 'checkpoint', 'reset', 'test', 'record'])
|
|
538
|
+
parser.add_argument('--summary', '-s', help='Resumo para checkpoint')
|
|
539
|
+
parser.add_argument('tool_name', nargs='?', default='unknown', help='Nome da tool (para record)')
|
|
540
|
+
|
|
541
|
+
args = parser.parse_args()
|
|
542
|
+
|
|
543
|
+
if args.command == 'status':
|
|
544
|
+
status = get_status()
|
|
545
|
+
print(json.dumps(status, indent=2))
|
|
546
|
+
|
|
547
|
+
elif args.command == 'checkpoint':
|
|
548
|
+
checkpoint = create_checkpoint(args.summary or '')
|
|
549
|
+
print(f"Checkpoint criado: {checkpoint['checkpoint_id']}")
|
|
550
|
+
print(json.dumps(checkpoint, indent=2, default=str))
|
|
551
|
+
|
|
552
|
+
elif args.command == 'reset':
|
|
553
|
+
reset_session()
|
|
554
|
+
print("Sessão resetada")
|
|
555
|
+
|
|
556
|
+
elif args.command == 'test':
|
|
557
|
+
# Simular algumas ações
|
|
558
|
+
for i in range(5):
|
|
559
|
+
result = record_action(
|
|
560
|
+
action_type=f'test_action_{i}',
|
|
561
|
+
input_chars=1000,
|
|
562
|
+
output_chars=2000,
|
|
563
|
+
is_tool_call=True
|
|
564
|
+
)
|
|
565
|
+
print(f"Action {i}: {result['percent_of_limit']:.2%}")
|
|
566
|
+
|
|
567
|
+
status = get_status()
|
|
568
|
+
print(f"\nStatus final:")
|
|
569
|
+
print(json.dumps(status, indent=2))
|
|
570
|
+
|
|
571
|
+
elif args.command == 'record':
|
|
572
|
+
# Registrar ação de tool (chamado pelos hooks)
|
|
573
|
+
result = record_action(
|
|
574
|
+
action_type=args.tool_name,
|
|
575
|
+
input_chars=500, # Estimativa média
|
|
576
|
+
output_chars=1000, # Estimativa média
|
|
577
|
+
is_tool_call=True
|
|
578
|
+
)
|
|
579
|
+
# Output silencioso para não poluir - apenas JSON para debug
|
|
580
|
+
print(json.dumps({'recorded': True, 'tool': args.tool_name, 'percent': result['percent_of_limit']}))
|
|
581
|
+
|
|
582
|
+
|
|
583
|
+
if __name__ == '__main__':
|
|
584
|
+
main()
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
JARVIS User Prompt Submit Hook
|
|
4
|
+
Executado quando o usuário envia uma mensagem.
|
|
5
|
+
|
|
6
|
+
Responsabilidades:
|
|
7
|
+
1. Registrar prompts para análise
|
|
8
|
+
2. Detectar intenções especiais
|
|
9
|
+
3. Injetar contexto básico (status, greeting)
|
|
10
|
+
|
|
11
|
+
NOTE: Skill routing (REGRA #27) e quality watchdog são hooks separados
|
|
12
|
+
registrados em settings.json. Este hook foca apenas em intents básicos.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
import json
|
|
16
|
+
import sys
|
|
17
|
+
import os
|
|
18
|
+
from datetime import datetime
|
|
19
|
+
from pathlib import Path
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def get_project_dir():
|
|
23
|
+
"""Obtém o diretório do projeto."""
|
|
24
|
+
return os.environ.get('CLAUDE_PROJECT_DIR', os.getcwd())
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def detect_special_intents(prompt):
|
|
28
|
+
"""Detecta intenções especiais no prompt."""
|
|
29
|
+
prompt_lower = prompt.lower()
|
|
30
|
+
|
|
31
|
+
intents = []
|
|
32
|
+
|
|
33
|
+
if any(word in prompt_lower for word in ['status', 'onde estamos', 'onde paramos']):
|
|
34
|
+
intents.append('status_request')
|
|
35
|
+
|
|
36
|
+
if any(word in prompt_lower for word in ['ajuda', 'help', 'como fazer']):
|
|
37
|
+
intents.append('help_request')
|
|
38
|
+
|
|
39
|
+
if any(word in prompt_lower for word in ['bom dia', 'boa tarde', 'boa noite', 'olá', 'oi']):
|
|
40
|
+
intents.append('greeting')
|
|
41
|
+
|
|
42
|
+
if any(word in prompt_lower for word in ['crie', 'criar', 'faça', 'fazer', 'gerar']):
|
|
43
|
+
intents.append('creation_request')
|
|
44
|
+
|
|
45
|
+
if any(word in prompt_lower for word in ['analise', 'verifique', 'cheque', 'revise']):
|
|
46
|
+
intents.append('analysis_request')
|
|
47
|
+
|
|
48
|
+
if any(phrase in prompt_lower for phrase in ['war room', 'debate sobre', 'múltiplas perspectivas', 'prós e contras']):
|
|
49
|
+
intents.append('war_room_request')
|
|
50
|
+
|
|
51
|
+
if any(phrase in prompt_lower for phrase in ['use os agentes', 'consulte especialistas', 'análise profunda']):
|
|
52
|
+
intents.append('force_agents')
|
|
53
|
+
|
|
54
|
+
return intents
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def load_context_for_intents(intents):
|
|
58
|
+
"""Carrega contexto relevante baseado nas intenções."""
|
|
59
|
+
project_dir = get_project_dir()
|
|
60
|
+
context_parts = []
|
|
61
|
+
|
|
62
|
+
if 'status_request' in intents:
|
|
63
|
+
state_path = Path(project_dir) / '.claude' / 'jarvis' / 'STATE.json'
|
|
64
|
+
if state_path.exists():
|
|
65
|
+
try:
|
|
66
|
+
with open(state_path, 'r', encoding='utf-8') as f:
|
|
67
|
+
state = json.load(f)
|
|
68
|
+
phase = state.get('current_state', {}).get('phase_name', 'IDLE')
|
|
69
|
+
pct = state.get('current_state', {}).get('percent_complete', 0)
|
|
70
|
+
context_parts.append(
|
|
71
|
+
f"[JARVIS Context] Estado: Fase {phase} | Progresso: {pct}%"
|
|
72
|
+
)
|
|
73
|
+
except Exception:
|
|
74
|
+
pass
|
|
75
|
+
|
|
76
|
+
return context_parts
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def main():
|
|
80
|
+
"""Hook entry point - reads JSON from stdin, outputs JSON to stdout."""
|
|
81
|
+
try:
|
|
82
|
+
input_data = sys.stdin.read()
|
|
83
|
+
hook_input = json.loads(input_data) if input_data else {}
|
|
84
|
+
|
|
85
|
+
prompt = hook_input.get('prompt', '')
|
|
86
|
+
session_id = hook_input.get('session_id', 'unknown')
|
|
87
|
+
|
|
88
|
+
# Detectar intenções
|
|
89
|
+
intents = detect_special_intents(prompt)
|
|
90
|
+
|
|
91
|
+
# Carregar contexto básico
|
|
92
|
+
context_parts = load_context_for_intents(intents)
|
|
93
|
+
|
|
94
|
+
# Registrar prompt para análise posterior
|
|
95
|
+
project_dir = get_project_dir()
|
|
96
|
+
prompts_path = Path(project_dir) / 'logs' / 'prompts.jsonl'
|
|
97
|
+
prompts_path.parent.mkdir(parents=True, exist_ok=True)
|
|
98
|
+
|
|
99
|
+
prompt_log = {
|
|
100
|
+
'timestamp': datetime.now().isoformat(),
|
|
101
|
+
'session_id': session_id,
|
|
102
|
+
'prompt_length': len(prompt),
|
|
103
|
+
'intents': intents
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
try:
|
|
107
|
+
with open(prompts_path, 'a', encoding='utf-8') as f:
|
|
108
|
+
f.write(json.dumps(prompt_log) + '\n')
|
|
109
|
+
except Exception:
|
|
110
|
+
pass
|
|
111
|
+
|
|
112
|
+
# Output
|
|
113
|
+
output = {
|
|
114
|
+
'continue': True,
|
|
115
|
+
'feedback': '\n'.join(context_parts) if context_parts else None
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
print(json.dumps(output))
|
|
119
|
+
|
|
120
|
+
except Exception:
|
|
121
|
+
print(json.dumps({'continue': True, 'feedback': None}))
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
if __name__ == '__main__':
|
|
125
|
+
main()
|