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,394 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Quality Watchdog - Layer 1 do META-AGENT System v1.0
|
|
4
|
+
|
|
5
|
+
FUNÇÃO: Detecta agentes em prompts e injeta MANDATORY_SECTIONS no contexto.
|
|
6
|
+
Faz parte do sistema de enforcement de qualidade (warn, não block).
|
|
7
|
+
|
|
8
|
+
REGRA #28: META-AGENT QUALITY AWARENESS
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
import json
|
|
12
|
+
import os
|
|
13
|
+
import re
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
from datetime import datetime
|
|
16
|
+
from typing import Dict, Optional
|
|
17
|
+
|
|
18
|
+
PROJECT_ROOT = Path(os.environ.get('CLAUDE_PROJECT_DIR', '.'))
|
|
19
|
+
AGENTS_PATH = PROJECT_ROOT / "agents"
|
|
20
|
+
JARVIS_SUBAGENTS = PROJECT_ROOT / ".claude" / "jarvis" / "sub-agents"
|
|
21
|
+
LOGS_PATH = PROJECT_ROOT / "logs"
|
|
22
|
+
QUALITY_GAPS_LOG = LOGS_PATH / "quality_gaps.jsonl"
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
# ═══════════════════════════════════════════════════════════════════════════
|
|
26
|
+
# AGENT DETECTION
|
|
27
|
+
# ═══════════════════════════════════════════════════════════════════════════
|
|
28
|
+
|
|
29
|
+
# Keywords para sub-agents JARVIS
|
|
30
|
+
SUBAGENT_KEYWORDS = {
|
|
31
|
+
"chronicler": ["log bonito", "log visual", "chronicler", "formatar log", "status formatado", "resumo visual"],
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
# Dynamic caches (populated on first use)
|
|
35
|
+
_PERSON_KEYWORDS_CACHE = None
|
|
36
|
+
_CARGO_KEYWORDS_CACHE = None
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def _scan_agents_directory(subdir: str) -> Dict:
|
|
40
|
+
"""
|
|
41
|
+
Dynamically scan agents/{subdir}/ to build keyword map.
|
|
42
|
+
Each subdirectory name becomes an agent key, and keywords are
|
|
43
|
+
generated from the directory name parts.
|
|
44
|
+
"""
|
|
45
|
+
keywords = {}
|
|
46
|
+
scan_path = AGENTS_PATH / subdir
|
|
47
|
+
if not scan_path.exists():
|
|
48
|
+
return keywords
|
|
49
|
+
|
|
50
|
+
for entry in scan_path.iterdir():
|
|
51
|
+
if entry.is_dir():
|
|
52
|
+
agent_name = entry.name.lower()
|
|
53
|
+
# Generate keywords from directory name
|
|
54
|
+
parts = agent_name.replace('-', ' ').replace('_', ' ').split()
|
|
55
|
+
kws = [agent_name.replace('-', ' ')] # full name as keyword
|
|
56
|
+
# Add individual meaningful parts (skip very short ones)
|
|
57
|
+
for part in parts:
|
|
58
|
+
if len(part) >= 3:
|
|
59
|
+
kws.append(part)
|
|
60
|
+
# Also add the hyphenated form
|
|
61
|
+
if '-' in agent_name or '_' in agent_name:
|
|
62
|
+
kws.append(agent_name)
|
|
63
|
+
keywords[agent_name] = list(set(kws))
|
|
64
|
+
|
|
65
|
+
return keywords
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def _get_person_keywords() -> Dict:
|
|
69
|
+
"""Lazily load person keywords from agents/persons/ directory."""
|
|
70
|
+
global _PERSON_KEYWORDS_CACHE
|
|
71
|
+
if _PERSON_KEYWORDS_CACHE is None:
|
|
72
|
+
_PERSON_KEYWORDS_CACHE = _scan_agents_directory("persons")
|
|
73
|
+
return _PERSON_KEYWORDS_CACHE
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def _get_cargo_keywords() -> Dict:
|
|
77
|
+
"""Lazily load cargo keywords from agents/cargo/ directory (recursive)."""
|
|
78
|
+
global _CARGO_KEYWORDS_CACHE
|
|
79
|
+
if _CARGO_KEYWORDS_CACHE is None:
|
|
80
|
+
_CARGO_KEYWORDS_CACHE = {}
|
|
81
|
+
cargo_path = AGENTS_PATH / "cargo"
|
|
82
|
+
if cargo_path.exists():
|
|
83
|
+
# Scan all subdirectories recursively for agent folders containing AGENT.md
|
|
84
|
+
for agent_md in cargo_path.rglob("AGENT.md"):
|
|
85
|
+
agent_dir = agent_md.parent
|
|
86
|
+
agent_name = agent_dir.name.lower()
|
|
87
|
+
parts = agent_name.replace('-', ' ').replace('_', ' ').split()
|
|
88
|
+
kws = [agent_name.replace('-', ' ')]
|
|
89
|
+
for part in parts:
|
|
90
|
+
if len(part) >= 3:
|
|
91
|
+
kws.append(part)
|
|
92
|
+
_CARGO_KEYWORDS_CACHE[agent_name] = list(set(kws))
|
|
93
|
+
return _CARGO_KEYWORDS_CACHE
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def detect_agent_in_prompt(prompt: str) -> Dict:
|
|
97
|
+
"""
|
|
98
|
+
Detecta qual agente está sendo requisitado no prompt.
|
|
99
|
+
|
|
100
|
+
Returns:
|
|
101
|
+
dict: {type: "subagent"|"person"|"cargo"|None, name: str|None}
|
|
102
|
+
"""
|
|
103
|
+
prompt_lower = prompt.lower()
|
|
104
|
+
|
|
105
|
+
# Detecta sub-agent JARVIS (prioridade mais alta)
|
|
106
|
+
for agent, keywords in SUBAGENT_KEYWORDS.items():
|
|
107
|
+
if any(kw in prompt_lower for kw in keywords):
|
|
108
|
+
return {"type": "subagent", "name": agent}
|
|
109
|
+
|
|
110
|
+
# Detecta PERSON agent (dynamically from agents/persons/ directory)
|
|
111
|
+
for agent, keywords in _get_person_keywords().items():
|
|
112
|
+
if any(kw in prompt_lower for kw in keywords):
|
|
113
|
+
return {"type": "person", "name": agent}
|
|
114
|
+
|
|
115
|
+
# Detecta CARGO agent (dynamically from agents/cargo/ directory)
|
|
116
|
+
for agent, keywords in _get_cargo_keywords().items():
|
|
117
|
+
if any(kw in prompt_lower for kw in keywords):
|
|
118
|
+
return {"type": "cargo", "name": agent}
|
|
119
|
+
|
|
120
|
+
return {"type": None, "name": None}
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
# ═══════════════════════════════════════════════════════════════════════════
|
|
124
|
+
# MANDATORY SECTIONS EXTRACTION
|
|
125
|
+
# ═══════════════════════════════════════════════════════════════════════════
|
|
126
|
+
|
|
127
|
+
def extract_mandatory_sections(agent_path: Path) -> Dict:
|
|
128
|
+
"""
|
|
129
|
+
Extrai MANDATORY_SECTIONS do header do AGENT.md.
|
|
130
|
+
|
|
131
|
+
Prioridade:
|
|
132
|
+
1. Bloco formal <!-- MANDATORY --> ... <!-- End MANDATORY -->
|
|
133
|
+
2. Seção ## ⚠️ MANDATORY OUTPUT SECTIONS
|
|
134
|
+
3. Fallback: primeiras 50 linhas
|
|
135
|
+
"""
|
|
136
|
+
agent_md = agent_path / "AGENT.md"
|
|
137
|
+
if not agent_md.exists():
|
|
138
|
+
return {"found": False, "content": "", "lines": 0}
|
|
139
|
+
|
|
140
|
+
try:
|
|
141
|
+
content = agent_md.read_text(encoding='utf-8')
|
|
142
|
+
except Exception as e:
|
|
143
|
+
return {"found": False, "content": "", "lines": 0, "error": str(e)}
|
|
144
|
+
|
|
145
|
+
# Método 1: Bloco formal com comentários HTML
|
|
146
|
+
match = re.search(
|
|
147
|
+
r'<!-- MANDATORY -->(.+?)<!-- End MANDATORY -->',
|
|
148
|
+
content,
|
|
149
|
+
re.DOTALL | re.IGNORECASE
|
|
150
|
+
)
|
|
151
|
+
if match:
|
|
152
|
+
return {
|
|
153
|
+
"found": True,
|
|
154
|
+
"method": "html_comments",
|
|
155
|
+
"content": match.group(1).strip(),
|
|
156
|
+
"lines": len(match.group(1).split('\n'))
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
# Método 2: Seção com header markdown
|
|
160
|
+
match = re.search(
|
|
161
|
+
r'## ⚠️ MANDATORY OUTPUT SECTIONS.*?(?=## [^⚠️]|$)',
|
|
162
|
+
content,
|
|
163
|
+
re.DOTALL | re.IGNORECASE
|
|
164
|
+
)
|
|
165
|
+
if match:
|
|
166
|
+
return {
|
|
167
|
+
"found": True,
|
|
168
|
+
"method": "markdown_header",
|
|
169
|
+
"content": match.group(0).strip(),
|
|
170
|
+
"lines": len(match.group(0).split('\n'))
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
# Método 3: Fallback - primeiras 50 linhas (para agents sem MANDATORY formal)
|
|
174
|
+
lines = content.split('\n')[:50]
|
|
175
|
+
return {
|
|
176
|
+
"found": False,
|
|
177
|
+
"method": "fallback_header",
|
|
178
|
+
"content": '\n'.join(lines),
|
|
179
|
+
"lines": 50
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
def resolve_agent_path(agent_info: Dict) -> Optional[Path]:
|
|
184
|
+
"""Resolve o caminho do agente baseado no tipo."""
|
|
185
|
+
if not agent_info.get("name"):
|
|
186
|
+
return None
|
|
187
|
+
|
|
188
|
+
agent_name = agent_info["name"].upper()
|
|
189
|
+
|
|
190
|
+
if agent_info["type"] == "subagent":
|
|
191
|
+
return JARVIS_SUBAGENTS / agent_name
|
|
192
|
+
elif agent_info["type"] == "person":
|
|
193
|
+
return AGENTS_PATH / "persons" / agent_name
|
|
194
|
+
elif agent_info["type"] == "cargo":
|
|
195
|
+
# Cargo agents têm estrutura diferente
|
|
196
|
+
cargo_paths = {
|
|
197
|
+
"CRO": AGENTS_PATH / "cargo" / "C-LEVEL" / "CRO",
|
|
198
|
+
"CFO": AGENTS_PATH / "cargo" / "C-LEVEL" / "CFO",
|
|
199
|
+
"SALES-MANAGER": AGENTS_PATH / "cargo" / "SALES" / "SALES-MANAGER",
|
|
200
|
+
"closer": AGENTS_PATH / "cargo" / "SALES" / "closer",
|
|
201
|
+
"SDR": AGENTS_PATH / "cargo" / "SALES" / "SDR",
|
|
202
|
+
}
|
|
203
|
+
return cargo_paths.get(agent_name)
|
|
204
|
+
|
|
205
|
+
return None
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
# ═══════════════════════════════════════════════════════════════════════════
|
|
209
|
+
# CONTEXT INJECTION
|
|
210
|
+
# ═══════════════════════════════════════════════════════════════════════════
|
|
211
|
+
|
|
212
|
+
def inject_quality_context(prompt: str) -> str:
|
|
213
|
+
"""
|
|
214
|
+
Injeta contexto de qualidade se agente detectado.
|
|
215
|
+
|
|
216
|
+
Retorna string vazia se nenhum agente detectado.
|
|
217
|
+
"""
|
|
218
|
+
agent_info = detect_agent_in_prompt(prompt)
|
|
219
|
+
|
|
220
|
+
if not agent_info.get("name"):
|
|
221
|
+
return ""
|
|
222
|
+
|
|
223
|
+
agent_path = resolve_agent_path(agent_info)
|
|
224
|
+
if not agent_path or not agent_path.exists():
|
|
225
|
+
return ""
|
|
226
|
+
|
|
227
|
+
mandatory = extract_mandatory_sections(agent_path)
|
|
228
|
+
|
|
229
|
+
# Sempre injeta contexto se agente detectado
|
|
230
|
+
context = f"""
|
|
231
|
+
[QUALITY WATCHDOG ACTIVATED]
|
|
232
|
+
Agent detected: {agent_info['name']}
|
|
233
|
+
Type: {agent_info['type']}
|
|
234
|
+
Path: {agent_path}
|
|
235
|
+
|
|
236
|
+
"""
|
|
237
|
+
|
|
238
|
+
if mandatory.get("found"):
|
|
239
|
+
context += f"""--- MANDATORY SECTIONS (MUST INCLUDE IN OUTPUT) ---
|
|
240
|
+
{mandatory['content']}
|
|
241
|
+
--- END MANDATORY SECTIONS ---
|
|
242
|
+
|
|
243
|
+
⚠️ OUTPUT WILL BE VALIDATED AGAINST THESE REQUIREMENTS
|
|
244
|
+
⚠️ Quality scoring active - minimum recommended: 70/100
|
|
245
|
+
"""
|
|
246
|
+
else:
|
|
247
|
+
context += f"""--- AGENT HEADER (First 50 lines) ---
|
|
248
|
+
{mandatory['content']}
|
|
249
|
+
--- END HEADER ---
|
|
250
|
+
|
|
251
|
+
ℹ️ No formal MANDATORY_SECTIONS found. Using header as reference.
|
|
252
|
+
"""
|
|
253
|
+
|
|
254
|
+
return context
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
# ═══════════════════════════════════════════════════════════════════════════
|
|
258
|
+
# LOGGING
|
|
259
|
+
# ═══════════════════════════════════════════════════════════════════════════
|
|
260
|
+
|
|
261
|
+
def log_quality_gap(agent: str, score: int, missing: list, agent_type: str = "unknown"):
|
|
262
|
+
"""
|
|
263
|
+
Loga gap de qualidade para análise do DOCTOR (Layer 2).
|
|
264
|
+
|
|
265
|
+
Não bloqueia - apenas registra para análise posterior.
|
|
266
|
+
"""
|
|
267
|
+
# Garantir que diretório existe
|
|
268
|
+
QUALITY_GAPS_LOG.parent.mkdir(parents=True, exist_ok=True)
|
|
269
|
+
|
|
270
|
+
entry = {
|
|
271
|
+
"timestamp": datetime.now().isoformat(),
|
|
272
|
+
"agent": agent,
|
|
273
|
+
"agent_type": agent_type,
|
|
274
|
+
"score": score,
|
|
275
|
+
"missing_sections": missing,
|
|
276
|
+
"status": "gap_detected",
|
|
277
|
+
"action_taken": "logged_for_review"
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
try:
|
|
281
|
+
with open(QUALITY_GAPS_LOG, "a", encoding='utf-8') as f:
|
|
282
|
+
f.write(json.dumps(entry, ensure_ascii=False) + "\n")
|
|
283
|
+
except Exception as e:
|
|
284
|
+
# Falha silenciosa - não deve interromper fluxo
|
|
285
|
+
pass
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
def log_watchdog_activation(agent_info: Dict, mandatory_found: bool):
|
|
289
|
+
"""Loga ativação do watchdog para auditoria."""
|
|
290
|
+
log_file = LOGS_PATH / "watchdog_activations.jsonl"
|
|
291
|
+
log_file.parent.mkdir(parents=True, exist_ok=True)
|
|
292
|
+
|
|
293
|
+
entry = {
|
|
294
|
+
"timestamp": datetime.now().isoformat(),
|
|
295
|
+
"agent": agent_info.get("name"),
|
|
296
|
+
"type": agent_info.get("type"),
|
|
297
|
+
"mandatory_found": mandatory_found
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
try:
|
|
301
|
+
with open(log_file, "a", encoding='utf-8') as f:
|
|
302
|
+
f.write(json.dumps(entry, ensure_ascii=False) + "\n")
|
|
303
|
+
except Exception:
|
|
304
|
+
pass
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
# ═══════════════════════════════════════════════════════════════════════════
|
|
308
|
+
# MAIN INTERFACE
|
|
309
|
+
# ═══════════════════════════════════════════════════════════════════════════
|
|
310
|
+
|
|
311
|
+
def process_prompt(prompt: str) -> Dict:
|
|
312
|
+
"""
|
|
313
|
+
Processa prompt e retorna contexto de qualidade se aplicável.
|
|
314
|
+
|
|
315
|
+
Interface principal para integração com user_prompt_submit.py
|
|
316
|
+
"""
|
|
317
|
+
agent_info = detect_agent_in_prompt(prompt)
|
|
318
|
+
|
|
319
|
+
result = {
|
|
320
|
+
"agent_detected": agent_info.get("name") is not None,
|
|
321
|
+
"agent_info": agent_info,
|
|
322
|
+
"context": "",
|
|
323
|
+
"mandatory_found": False
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
if agent_info.get("name"):
|
|
327
|
+
context = inject_quality_context(prompt)
|
|
328
|
+
result["context"] = context
|
|
329
|
+
result["mandatory_found"] = "MANDATORY SECTIONS" in context
|
|
330
|
+
|
|
331
|
+
# Log activation
|
|
332
|
+
log_watchdog_activation(agent_info, result["mandatory_found"])
|
|
333
|
+
|
|
334
|
+
return result
|
|
335
|
+
|
|
336
|
+
|
|
337
|
+
# ═══════════════════════════════════════════════════════════════════════════
|
|
338
|
+
# CLI INTERFACE
|
|
339
|
+
# ═══════════════════════════════════════════════════════════════════════════
|
|
340
|
+
|
|
341
|
+
def main():
|
|
342
|
+
"""
|
|
343
|
+
Hook entry point for Claude Code UserPromptSubmit event.
|
|
344
|
+
Reads JSON from stdin, outputs JSON to stdout.
|
|
345
|
+
"""
|
|
346
|
+
import sys
|
|
347
|
+
|
|
348
|
+
try:
|
|
349
|
+
input_data = sys.stdin.read()
|
|
350
|
+
hook_input = json.loads(input_data) if input_data else {}
|
|
351
|
+
|
|
352
|
+
prompt = hook_input.get('prompt', '')
|
|
353
|
+
if not prompt:
|
|
354
|
+
print(json.dumps({'continue': True}))
|
|
355
|
+
return
|
|
356
|
+
|
|
357
|
+
result = process_prompt(prompt)
|
|
358
|
+
|
|
359
|
+
feedback = result.get('context') if result.get('agent_detected') else None
|
|
360
|
+
|
|
361
|
+
print(json.dumps({
|
|
362
|
+
'continue': True,
|
|
363
|
+
'feedback': feedback if feedback else None
|
|
364
|
+
}))
|
|
365
|
+
|
|
366
|
+
except Exception:
|
|
367
|
+
print(json.dumps({'continue': True}))
|
|
368
|
+
|
|
369
|
+
|
|
370
|
+
def cli_test():
|
|
371
|
+
"""CLI test mode - run directly for debugging."""
|
|
372
|
+
import sys
|
|
373
|
+
|
|
374
|
+
if len(sys.argv) > 1:
|
|
375
|
+
test_prompt = " ".join(sys.argv[1:])
|
|
376
|
+
else:
|
|
377
|
+
test_prompt = "me dá um log bonito do conteúdo do Alex Hormozi"
|
|
378
|
+
|
|
379
|
+
print(f"Testing prompt: {test_prompt}\n")
|
|
380
|
+
|
|
381
|
+
result = process_prompt(test_prompt)
|
|
382
|
+
|
|
383
|
+
print(f"Agent detected: {result['agent_detected']}")
|
|
384
|
+
print(f"Agent info: {result['agent_info']}")
|
|
385
|
+
print(f"Mandatory found: {result['mandatory_found']}")
|
|
386
|
+
print(f"\nContext to inject:\n{result['context'][:500]}...")
|
|
387
|
+
|
|
388
|
+
|
|
389
|
+
if __name__ == "__main__":
|
|
390
|
+
import sys
|
|
391
|
+
if len(sys.argv) > 1 and sys.argv[1] == '--test':
|
|
392
|
+
cli_test()
|
|
393
|
+
else:
|
|
394
|
+
main()
|
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
RALPH WIGGUM - COMPLETION PROMISE HOOK
|
|
4
|
+
======================================
|
|
5
|
+
Baseado no plugin original de Boris Cherny.
|
|
6
|
+
|
|
7
|
+
O "completion promise" é um pattern onde Claude deve outputtar uma string
|
|
8
|
+
específica para sinalizar que a tarefa está REALMENTE completa.
|
|
9
|
+
|
|
10
|
+
Funcionamento:
|
|
11
|
+
1. Claude recebe instrução para usar <promise>DONE</promise> quando terminar
|
|
12
|
+
2. Este hook verifica se o completion promise foi dado
|
|
13
|
+
3. Se não foi, re-prompta Claude para continuar ou confirmar
|
|
14
|
+
|
|
15
|
+
Token: <promise>DONE</promise>
|
|
16
|
+
|
|
17
|
+
Referência: https://github.com/chernyshov/ralph-wiggum
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
import sys
|
|
21
|
+
import os
|
|
22
|
+
import json
|
|
23
|
+
import re
|
|
24
|
+
from pathlib import Path
|
|
25
|
+
from datetime import datetime
|
|
26
|
+
|
|
27
|
+
PROJECT_ROOT = Path(os.environ.get('CLAUDE_PROJECT_DIR', '.'))
|
|
28
|
+
STATE_FILE = PROJECT_ROOT / '.claude' / 'ralph_wiggum_state.json'
|
|
29
|
+
LOG_FILE = PROJECT_ROOT / 'logs' / 'ralph_wiggum.jsonl'
|
|
30
|
+
|
|
31
|
+
# Completion promise token
|
|
32
|
+
PROMISE_TOKEN = '<promise>DONE</promise>'
|
|
33
|
+
PROMISE_PATTERN = re.compile(r'<promise>DONE</promise>', re.IGNORECASE)
|
|
34
|
+
|
|
35
|
+
# Tasks that require completion promise
|
|
36
|
+
COMPLEX_TASK_KEYWORDS = [
|
|
37
|
+
'processar', 'pipeline', 'batch', 'criar agente',
|
|
38
|
+
'implementar', 'refatorar', 'migrar', 'varredura',
|
|
39
|
+
'fase 4', 'fase 5', 'dossier', 'playbook'
|
|
40
|
+
]
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def ensure_dirs():
|
|
44
|
+
"""Ensure log directory exists"""
|
|
45
|
+
LOG_FILE.parent.mkdir(parents=True, exist_ok=True)
|
|
46
|
+
STATE_FILE.parent.mkdir(parents=True, exist_ok=True)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def load_state() -> dict:
|
|
50
|
+
"""Load Ralph Wiggum state"""
|
|
51
|
+
if STATE_FILE.exists():
|
|
52
|
+
try:
|
|
53
|
+
return json.loads(STATE_FILE.read_text())
|
|
54
|
+
except:
|
|
55
|
+
pass
|
|
56
|
+
return {
|
|
57
|
+
'active_task': None,
|
|
58
|
+
'promise_required': False,
|
|
59
|
+
'promise_received': False,
|
|
60
|
+
'reprompt_count': 0,
|
|
61
|
+
'max_reprompts': 3
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def save_state(state: dict):
|
|
66
|
+
"""Save Ralph Wiggum state"""
|
|
67
|
+
ensure_dirs()
|
|
68
|
+
STATE_FILE.write_text(json.dumps(state, indent=2))
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def log_event(event_type: str, details: dict):
|
|
72
|
+
"""Log Ralph Wiggum events"""
|
|
73
|
+
ensure_dirs()
|
|
74
|
+
entry = {
|
|
75
|
+
'timestamp': datetime.now().isoformat(),
|
|
76
|
+
'event': event_type,
|
|
77
|
+
**details
|
|
78
|
+
}
|
|
79
|
+
with open(LOG_FILE, 'a') as f:
|
|
80
|
+
f.write(json.dumps(entry) + '\n')
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def is_complex_task(task_description: str) -> bool:
|
|
84
|
+
"""Determine if task requires completion promise"""
|
|
85
|
+
if not task_description:
|
|
86
|
+
return False
|
|
87
|
+
task_lower = task_description.lower()
|
|
88
|
+
return any(keyword in task_lower for keyword in COMPLEX_TASK_KEYWORDS)
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def check_promise_in_output() -> bool:
|
|
92
|
+
"""Check if completion promise was given in recent output"""
|
|
93
|
+
# Check LEDGER for promise
|
|
94
|
+
ledger_path = PROJECT_ROOT / '.claude' / 'LEDGER.md'
|
|
95
|
+
if ledger_path.exists():
|
|
96
|
+
content = ledger_path.read_text()
|
|
97
|
+
if PROMISE_PATTERN.search(content):
|
|
98
|
+
return True
|
|
99
|
+
|
|
100
|
+
# Check recent session log
|
|
101
|
+
sessions_dir = PROJECT_ROOT / '.claude' / 'sessions'
|
|
102
|
+
if sessions_dir.exists():
|
|
103
|
+
session_files = sorted(sessions_dir.glob('SESSION-*.md'), reverse=True)
|
|
104
|
+
if session_files:
|
|
105
|
+
recent = session_files[0].read_text()
|
|
106
|
+
if PROMISE_PATTERN.search(recent):
|
|
107
|
+
return True
|
|
108
|
+
|
|
109
|
+
return False
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def start_task(task_description: str):
|
|
113
|
+
"""Call when a complex task starts"""
|
|
114
|
+
state = load_state()
|
|
115
|
+
|
|
116
|
+
if is_complex_task(task_description):
|
|
117
|
+
state['active_task'] = task_description
|
|
118
|
+
state['promise_required'] = True
|
|
119
|
+
state['promise_received'] = False
|
|
120
|
+
state['reprompt_count'] = 0
|
|
121
|
+
save_state(state)
|
|
122
|
+
log_event('task_started', {'task': task_description, 'promise_required': True})
|
|
123
|
+
|
|
124
|
+
# Inject instruction about completion promise
|
|
125
|
+
print(f"""
|
|
126
|
+
╔══════════════════════════════════════════════════════════════════════════════╗
|
|
127
|
+
║ 🎯 RALPH WIGGUM ACTIVE ║
|
|
128
|
+
╠══════════════════════════════════════════════════════════════════════════════╣
|
|
129
|
+
║ ║
|
|
130
|
+
║ Tarefa complexa detectada: {task_description[:50]:<50} ║
|
|
131
|
+
║ ║
|
|
132
|
+
║ Quando COMPLETAR a tarefa, inclua na sua resposta: ║
|
|
133
|
+
║ ║
|
|
134
|
+
║ <promise>DONE</promise> ║
|
|
135
|
+
║ ║
|
|
136
|
+
║ Isso confirma que a tarefa está REALMENTE completa. ║
|
|
137
|
+
║ ║
|
|
138
|
+
╚══════════════════════════════════════════════════════════════════════════════╝
|
|
139
|
+
""")
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def check_completion() -> bool:
|
|
143
|
+
"""Check if task is complete (has promise)"""
|
|
144
|
+
state = load_state()
|
|
145
|
+
|
|
146
|
+
if not state.get('promise_required'):
|
|
147
|
+
return True
|
|
148
|
+
|
|
149
|
+
if state.get('promise_received'):
|
|
150
|
+
return True
|
|
151
|
+
|
|
152
|
+
# Check if promise was given
|
|
153
|
+
if check_promise_in_output():
|
|
154
|
+
state['promise_received'] = True
|
|
155
|
+
save_state(state)
|
|
156
|
+
log_event('promise_received', {'task': state.get('active_task')})
|
|
157
|
+
return True
|
|
158
|
+
|
|
159
|
+
return False
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def reprompt():
|
|
163
|
+
"""Generate reprompt message if task incomplete"""
|
|
164
|
+
state = load_state()
|
|
165
|
+
|
|
166
|
+
if state.get('reprompt_count', 0) >= state.get('max_reprompts', 3):
|
|
167
|
+
# Max reprompts reached, allow exit
|
|
168
|
+
log_event('max_reprompts_reached', {'task': state.get('active_task')})
|
|
169
|
+
clear_task()
|
|
170
|
+
return None
|
|
171
|
+
|
|
172
|
+
state['reprompt_count'] = state.get('reprompt_count', 0) + 1
|
|
173
|
+
save_state(state)
|
|
174
|
+
|
|
175
|
+
task = state.get('active_task', 'current task')
|
|
176
|
+
count = state['reprompt_count']
|
|
177
|
+
max_count = state.get('max_reprompts', 3)
|
|
178
|
+
|
|
179
|
+
log_event('reprompt_generated', {'task': task, 'count': count})
|
|
180
|
+
|
|
181
|
+
return f"""
|
|
182
|
+
╔══════════════════════════════════════════════════════════════════════════════╗
|
|
183
|
+
║ ⚠️ RALPH WIGGUM: Completion promise not detected ({count}/{max_count}) ║
|
|
184
|
+
╠══════════════════════════════════════════════════════════════════════════════╣
|
|
185
|
+
║ ║
|
|
186
|
+
║ Task: {task[:66]:<66} ║
|
|
187
|
+
║ ║
|
|
188
|
+
║ Você ainda não confirmou que a tarefa está completa. ║
|
|
189
|
+
║ ║
|
|
190
|
+
║ Se a tarefa está COMPLETA, responda com: ║
|
|
191
|
+
║ <promise>DONE</promise> ║
|
|
192
|
+
║ ║
|
|
193
|
+
║ Se há mais trabalho, continue de onde parou. ║
|
|
194
|
+
║ ║
|
|
195
|
+
╚══════════════════════════════════════════════════════════════════════════════╝
|
|
196
|
+
"""
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
def clear_task():
|
|
200
|
+
"""Clear the current task"""
|
|
201
|
+
state = load_state()
|
|
202
|
+
if state.get('active_task'):
|
|
203
|
+
log_event('task_cleared', {'task': state.get('active_task')})
|
|
204
|
+
state['active_task'] = None
|
|
205
|
+
state['promise_required'] = False
|
|
206
|
+
state['promise_received'] = False
|
|
207
|
+
state['reprompt_count'] = 0
|
|
208
|
+
save_state(state)
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
def main():
|
|
212
|
+
"""Main Ralph Wiggum logic for Stop hook"""
|
|
213
|
+
ensure_dirs()
|
|
214
|
+
state = load_state()
|
|
215
|
+
|
|
216
|
+
# If no task active, allow exit
|
|
217
|
+
if not state.get('active_task'):
|
|
218
|
+
sys.exit(0)
|
|
219
|
+
|
|
220
|
+
# If promise not required, allow exit
|
|
221
|
+
if not state.get('promise_required'):
|
|
222
|
+
sys.exit(0)
|
|
223
|
+
|
|
224
|
+
# Check if promise was given
|
|
225
|
+
if check_completion():
|
|
226
|
+
print("\n✅ Ralph Wiggum: Completion promise received. Task complete.\n")
|
|
227
|
+
clear_task()
|
|
228
|
+
sys.exit(0)
|
|
229
|
+
|
|
230
|
+
# Generate reprompt
|
|
231
|
+
reprompt_msg = reprompt()
|
|
232
|
+
|
|
233
|
+
if reprompt_msg:
|
|
234
|
+
print(reprompt_msg)
|
|
235
|
+
else:
|
|
236
|
+
# Max reprompts reached
|
|
237
|
+
print("\n⚠️ Ralph Wiggum: Max reprompts reached. Allowing exit.\n")
|
|
238
|
+
|
|
239
|
+
sys.exit(0)
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
# CLI interface for manual control
|
|
243
|
+
if __name__ == '__main__':
|
|
244
|
+
if len(sys.argv) > 1:
|
|
245
|
+
cmd = sys.argv[1]
|
|
246
|
+
|
|
247
|
+
if cmd == 'start' and len(sys.argv) > 2:
|
|
248
|
+
task = ' '.join(sys.argv[2:])
|
|
249
|
+
start_task(task)
|
|
250
|
+
|
|
251
|
+
elif cmd == 'check':
|
|
252
|
+
if check_completion():
|
|
253
|
+
print("✅ Task complete (promise received)")
|
|
254
|
+
else:
|
|
255
|
+
print("⏳ Task incomplete (no promise)")
|
|
256
|
+
|
|
257
|
+
elif cmd == 'clear':
|
|
258
|
+
clear_task()
|
|
259
|
+
print("🗑️ Task cleared")
|
|
260
|
+
|
|
261
|
+
elif cmd == 'status':
|
|
262
|
+
state = load_state()
|
|
263
|
+
print(json.dumps(state, indent=2))
|
|
264
|
+
|
|
265
|
+
else:
|
|
266
|
+
print("""
|
|
267
|
+
Ralph Wiggum - Completion Promise Hook
|
|
268
|
+
|
|
269
|
+
Usage:
|
|
270
|
+
ralph_wiggum.py # Stop hook mode
|
|
271
|
+
ralph_wiggum.py start TASK # Start tracking complex task
|
|
272
|
+
ralph_wiggum.py check # Check if promise received
|
|
273
|
+
ralph_wiggum.py clear # Clear current task
|
|
274
|
+
ralph_wiggum.py status # Show current state
|
|
275
|
+
""")
|
|
276
|
+
else:
|
|
277
|
+
main()
|