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,501 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
DUAL-LOCATION ENFORCEMENT HOOK - REGRA #8
|
|
4
|
+
==========================================
|
|
5
|
+
Garante que todo batch log existe em AMBOS os locais:
|
|
6
|
+
- /logs/batches/BATCH-XXX.md
|
|
7
|
+
- /.claude/mission-control/batch-logs/BATCH-XXX.json
|
|
8
|
+
|
|
9
|
+
Autor: JARVIS
|
|
10
|
+
Data: 2026-01-11
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
import os
|
|
14
|
+
import re
|
|
15
|
+
import json
|
|
16
|
+
import sys
|
|
17
|
+
from pathlib import Path
|
|
18
|
+
from datetime import datetime
|
|
19
|
+
from typing import Dict, Optional, List, Tuple
|
|
20
|
+
|
|
21
|
+
# Caminhos
|
|
22
|
+
BASE_PATH = Path(os.environ.get('CLAUDE_PROJECT_DIR', '.'))
|
|
23
|
+
LOGS_MD = BASE_PATH / "logs" / "batches"
|
|
24
|
+
LOGS_JSON = BASE_PATH / ".claude" / "mission-control" / "batch-logs"
|
|
25
|
+
ENFORCEMENT_LOG = BASE_PATH / "logs" / "enforcement.jsonl"
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def ensure_directories():
|
|
29
|
+
"""Garante que os diretórios existem."""
|
|
30
|
+
LOGS_MD.mkdir(parents=True, exist_ok=True)
|
|
31
|
+
LOGS_JSON.mkdir(parents=True, exist_ok=True)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def log_enforcement(action: str, batch_id: str, details: dict):
|
|
35
|
+
"""Registra ação de enforcement no log."""
|
|
36
|
+
entry = {
|
|
37
|
+
"timestamp": datetime.now().isoformat(),
|
|
38
|
+
"action": action,
|
|
39
|
+
"batch_id": batch_id,
|
|
40
|
+
"details": details
|
|
41
|
+
}
|
|
42
|
+
with open(ENFORCEMENT_LOG, "a", encoding="utf-8") as f:
|
|
43
|
+
f.write(json.dumps(entry, ensure_ascii=False) + "\n")
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def extract_batch_id_from_filename(filename: str) -> Optional[str]:
|
|
47
|
+
"""
|
|
48
|
+
Extrai o ID do batch do nome do arquivo.
|
|
49
|
+
Suporta múltiplos formatos:
|
|
50
|
+
- BATCH-050.md
|
|
51
|
+
- BATCH-033-CG.json
|
|
52
|
+
- BATCH-001-JEREMY-HAYNES-SOPS-20260104.md
|
|
53
|
+
"""
|
|
54
|
+
# Tenta extrair número do batch
|
|
55
|
+
match = re.search(r'BATCH[_-]?(\d+)', filename, re.IGNORECASE)
|
|
56
|
+
if match:
|
|
57
|
+
return match.group(1).zfill(3) # Padroniza para 3 dígitos
|
|
58
|
+
return None
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def extract_metadata_from_md(md_path: Path) -> dict:
|
|
62
|
+
"""
|
|
63
|
+
Extrai metadados do arquivo .md para criar .json.
|
|
64
|
+
Faz parsing do formato visual do batch log.
|
|
65
|
+
"""
|
|
66
|
+
content = md_path.read_text(encoding="utf-8")
|
|
67
|
+
|
|
68
|
+
metadata = {
|
|
69
|
+
"batch_id": "",
|
|
70
|
+
"source": "",
|
|
71
|
+
"timestamp": datetime.now().isoformat(),
|
|
72
|
+
"status": "COMPLETE",
|
|
73
|
+
"files_processed": 0,
|
|
74
|
+
"files": [],
|
|
75
|
+
"extraction_summary": {
|
|
76
|
+
"filosofias": 0,
|
|
77
|
+
"frameworks": 0,
|
|
78
|
+
"heuristicas": 0,
|
|
79
|
+
"metodologias": 0,
|
|
80
|
+
"modelos_mentais": 0
|
|
81
|
+
},
|
|
82
|
+
"key_frameworks": [],
|
|
83
|
+
"key_heuristicas": [],
|
|
84
|
+
"key_filosofias": [],
|
|
85
|
+
"key_metodologias": [],
|
|
86
|
+
"auto_generated": True,
|
|
87
|
+
"generated_from": str(md_path.name),
|
|
88
|
+
"generated_at": datetime.now().isoformat()
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
# Extrai batch_id do nome do arquivo
|
|
92
|
+
batch_match = re.search(r'BATCH[_-]?(\d+)', md_path.name, re.IGNORECASE)
|
|
93
|
+
if batch_match:
|
|
94
|
+
metadata["batch_id"] = f"BATCH-{batch_match.group(1).zfill(3)}"
|
|
95
|
+
|
|
96
|
+
# Extrai SOURCE
|
|
97
|
+
source_patterns = [
|
|
98
|
+
r'SOURCE\s+([A-Z][A-Z\s\(\)\.]+)',
|
|
99
|
+
r'FONTE\s+([A-Z][A-Z\s\(\)\.]+)',
|
|
100
|
+
r'Source:\s*([^\n]+)',
|
|
101
|
+
r'\|\s*SOURCE\s*\|\s*([^\|]+)',
|
|
102
|
+
]
|
|
103
|
+
for pattern in source_patterns:
|
|
104
|
+
match = re.search(pattern, content, re.IGNORECASE)
|
|
105
|
+
if match:
|
|
106
|
+
metadata["source"] = match.group(1).strip()
|
|
107
|
+
break
|
|
108
|
+
|
|
109
|
+
# Se nao encontrou, tenta extrair do nome do arquivo dinamicamente
|
|
110
|
+
if not metadata["source"]:
|
|
111
|
+
# Extract source hint from filename by removing BATCH-XXX prefix and extension
|
|
112
|
+
name_upper = md_path.stem.upper()
|
|
113
|
+
# Remove BATCH-NNN- prefix
|
|
114
|
+
source_hint = re.sub(r'^BATCH[_-]?\d+[_-]?', '', name_upper).strip('-_ ')
|
|
115
|
+
if source_hint:
|
|
116
|
+
# Convert dash/underscore separated name to readable form
|
|
117
|
+
metadata["source"] = source_hint.replace('-', ' ').replace('_', ' ').strip()
|
|
118
|
+
|
|
119
|
+
# Extrai metricas
|
|
120
|
+
metrics_patterns = {
|
|
121
|
+
"filosofias": r'Filosofias\s*[:\|]?\s*(\d+)',
|
|
122
|
+
"frameworks": r'Frameworks?\s*[:\|]?\s*(\d+)',
|
|
123
|
+
"heuristicas": r'Heur[ií]sticas?\s*[:\|]?\s*(\d+)',
|
|
124
|
+
"metodologias": r'Metodologias?\s*[:\|]?\s*(\d+)',
|
|
125
|
+
"modelos_mentais": r'Modelos?\s*Mentais?\s*[:\|]?\s*(\d+)'
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
for key, pattern in metrics_patterns.items():
|
|
129
|
+
match = re.search(pattern, content, re.IGNORECASE)
|
|
130
|
+
if match:
|
|
131
|
+
metadata["extraction_summary"][key] = int(match.group(1))
|
|
132
|
+
|
|
133
|
+
# Extrai arquivos processados da tabela
|
|
134
|
+
table_pattern = r'\|\s*\d+\s*\|\s*([^\|]+)\s*\|'
|
|
135
|
+
files = re.findall(table_pattern, content)
|
|
136
|
+
metadata["files"] = [f.strip() for f in files if f.strip() and "Arquivo" not in f]
|
|
137
|
+
metadata["files_processed"] = len(metadata["files"])
|
|
138
|
+
|
|
139
|
+
# Extrai filosofias destaque
|
|
140
|
+
filosofias_section = re.search(r'PHILOSOPHIES.*?```(.*?)```', content, re.DOTALL | re.IGNORECASE)
|
|
141
|
+
if filosofias_section:
|
|
142
|
+
filosofias = re.findall(r'"([^"]+)"', filosofias_section.group(1))
|
|
143
|
+
metadata["key_filosofias"] = filosofias[:12] # Limita a 12
|
|
144
|
+
|
|
145
|
+
# Extrai frameworks
|
|
146
|
+
frameworks_section = re.search(r'KEY FRAMEWORKS(.*?)(?:##|FILOSOFIAS|HEURISTICAS|```\s*\n\s*##)', content, re.DOTALL | re.IGNORECASE)
|
|
147
|
+
if frameworks_section:
|
|
148
|
+
# Busca nomes de frameworks entre boxes ASCII
|
|
149
|
+
framework_names = re.findall(r'[\u2500-\u257F]\s*([A-Z][A-Za-z0-9\s\(\)\-]+?)\s*[\u2500-\u257F]', frameworks_section.group(1))
|
|
150
|
+
if not framework_names:
|
|
151
|
+
framework_names = re.findall(r'([A-Z][A-Z\s\-]+(?:Framework|Method|System|Model|Process))', frameworks_section.group(1), re.IGNORECASE)
|
|
152
|
+
metadata["key_frameworks"] = list(set(framework_names))[:12]
|
|
153
|
+
|
|
154
|
+
# Extrai heuristicas
|
|
155
|
+
heuristicas_section = re.search(r'HEURISTICS.*?```(.*?)```', content, re.DOTALL | re.IGNORECASE)
|
|
156
|
+
if heuristicas_section:
|
|
157
|
+
heuristicas = re.findall(r'"([^"]+)"', heuristicas_section.group(1))
|
|
158
|
+
metadata["key_heuristicas"] = [{"heuristica": h, "rating": 4} for h in heuristicas[:17]]
|
|
159
|
+
|
|
160
|
+
return metadata
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def create_json_from_md(batch_id: str, md_path: Path) -> bool:
|
|
164
|
+
"""Cria arquivo .json a partir do .md existente."""
|
|
165
|
+
try:
|
|
166
|
+
metadata = extract_metadata_from_md(md_path)
|
|
167
|
+
|
|
168
|
+
# Determina o nome do arquivo JSON dynamically from source name
|
|
169
|
+
source_code = ""
|
|
170
|
+
source_upper = metadata["source"].upper().strip()
|
|
171
|
+
if source_upper:
|
|
172
|
+
# Generate short code from initials of source name words
|
|
173
|
+
words = source_upper.replace('(', '').replace(')', '').replace('.', '').split()
|
|
174
|
+
if len(words) == 1:
|
|
175
|
+
source_code = f"-{words[0][:3]}"
|
|
176
|
+
else:
|
|
177
|
+
source_code = f"-{''.join(w[0] for w in words if w)}"
|
|
178
|
+
|
|
179
|
+
json_filename = f"BATCH-{batch_id}{source_code}.json"
|
|
180
|
+
json_path = LOGS_JSON / json_filename
|
|
181
|
+
|
|
182
|
+
with open(json_path, "w", encoding="utf-8") as f:
|
|
183
|
+
json.dump(metadata, f, indent=2, ensure_ascii=False)
|
|
184
|
+
|
|
185
|
+
log_enforcement("CREATE_JSON", batch_id, {
|
|
186
|
+
"source_md": str(md_path.name),
|
|
187
|
+
"created_json": json_filename,
|
|
188
|
+
"files_processed": metadata["files_processed"]
|
|
189
|
+
})
|
|
190
|
+
|
|
191
|
+
return True
|
|
192
|
+
|
|
193
|
+
except Exception as e:
|
|
194
|
+
log_enforcement("ERROR_CREATE_JSON", batch_id, {
|
|
195
|
+
"source_md": str(md_path.name),
|
|
196
|
+
"error": str(e)
|
|
197
|
+
})
|
|
198
|
+
return False
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
def create_md_from_json(batch_id: str, json_path: Path) -> bool:
|
|
202
|
+
"""Cria arquivo .md minimo a partir do .json existente."""
|
|
203
|
+
try:
|
|
204
|
+
with open(json_path, "r", encoding="utf-8") as f:
|
|
205
|
+
data = json.load(f)
|
|
206
|
+
|
|
207
|
+
# Gera markdown minimo
|
|
208
|
+
source = data.get("source", "UNKNOWN")
|
|
209
|
+
files_processed = data.get("files_processed", 0)
|
|
210
|
+
timestamp = data.get("timestamp", datetime.now().isoformat())
|
|
211
|
+
|
|
212
|
+
extraction = data.get("extraction_summary", {})
|
|
213
|
+
filosofias = extraction.get("filosofias", 0)
|
|
214
|
+
frameworks = extraction.get("frameworks", 0)
|
|
215
|
+
heuristicas = extraction.get("heuristicas", 0)
|
|
216
|
+
metodologias = extraction.get("metodologias", 0)
|
|
217
|
+
modelos = extraction.get("modelos_mentais", 0)
|
|
218
|
+
total = filosofias + frameworks + heuristicas + metodologias + modelos
|
|
219
|
+
|
|
220
|
+
md_content = f"""# BATCH-{batch_id}
|
|
221
|
+
|
|
222
|
+
> **AUTO-GENERATED from JSON** - {datetime.now().strftime('%Y-%m-%d %H:%M')}
|
|
223
|
+
> Original JSON: {json_path.name}
|
|
224
|
+
|
|
225
|
+
## BATCH SUMMARY
|
|
226
|
+
|
|
227
|
+
| Campo | Valor |
|
|
228
|
+
|-------|-------|
|
|
229
|
+
| SOURCE | {source} |
|
|
230
|
+
| ARQUIVOS | {files_processed} |
|
|
231
|
+
| TIMESTAMP | {timestamp} |
|
|
232
|
+
| STATUS | {data.get('status', 'COMPLETE')} |
|
|
233
|
+
|
|
234
|
+
## METRICAS
|
|
235
|
+
|
|
236
|
+
| Camada | Quantidade |
|
|
237
|
+
|--------|------------|
|
|
238
|
+
| Filosofias | {filosofias} |
|
|
239
|
+
| Frameworks | {frameworks} |
|
|
240
|
+
| Heuristicas | {heuristicas} |
|
|
241
|
+
| Metodologias | {metodologias} |
|
|
242
|
+
| Modelos Mentais | {modelos} |
|
|
243
|
+
| **TOTAL** | **{total}** |
|
|
244
|
+
|
|
245
|
+
## ARQUIVOS PROCESSADOS
|
|
246
|
+
|
|
247
|
+
"""
|
|
248
|
+
files = data.get("files", [])
|
|
249
|
+
for i, f in enumerate(files, 1):
|
|
250
|
+
md_content += f"| {i} | {f} |\n"
|
|
251
|
+
|
|
252
|
+
if data.get("key_frameworks"):
|
|
253
|
+
md_content += "\n## KEY FRAMEWORKS\n\n"
|
|
254
|
+
for fw in data["key_frameworks"]:
|
|
255
|
+
md_content += f"- {fw}\n"
|
|
256
|
+
|
|
257
|
+
if data.get("key_filosofias"):
|
|
258
|
+
md_content += "\n## FILOSOFIAS DESTAQUE\n\n"
|
|
259
|
+
for fil in data["key_filosofias"]:
|
|
260
|
+
md_content += f"- \"{fil}\"\n"
|
|
261
|
+
|
|
262
|
+
md_content += f"""
|
|
263
|
+
---
|
|
264
|
+
|
|
265
|
+
```
|
|
266
|
+
AUTO-GENERATED BY DUAL-LOCATION ENFORCEMENT HOOK
|
|
267
|
+
JARVIS v3.33 | {datetime.now().strftime('%Y-%m-%d %H:%M')}
|
|
268
|
+
```
|
|
269
|
+
"""
|
|
270
|
+
|
|
271
|
+
md_filename = f"BATCH-{batch_id}.md"
|
|
272
|
+
md_path = LOGS_MD / md_filename
|
|
273
|
+
|
|
274
|
+
with open(md_path, "w", encoding="utf-8") as f:
|
|
275
|
+
f.write(md_content)
|
|
276
|
+
|
|
277
|
+
log_enforcement("CREATE_MD", batch_id, {
|
|
278
|
+
"source_json": str(json_path.name),
|
|
279
|
+
"created_md": md_filename
|
|
280
|
+
})
|
|
281
|
+
|
|
282
|
+
return True
|
|
283
|
+
|
|
284
|
+
except Exception as e:
|
|
285
|
+
log_enforcement("ERROR_CREATE_MD", batch_id, {
|
|
286
|
+
"source_json": str(json_path.name),
|
|
287
|
+
"error": str(e)
|
|
288
|
+
})
|
|
289
|
+
return False
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
def get_all_batches() -> Dict[str, Dict[str, Optional[Path]]]:
|
|
293
|
+
"""
|
|
294
|
+
Coleta todos os batches de ambos os locais.
|
|
295
|
+
Retorna dict: batch_id -> {"md": Path or None, "json": Path or None}
|
|
296
|
+
"""
|
|
297
|
+
batches = {}
|
|
298
|
+
|
|
299
|
+
# Coleta .md files
|
|
300
|
+
if LOGS_MD.exists():
|
|
301
|
+
for md_file in LOGS_MD.glob("BATCH-*.md"):
|
|
302
|
+
batch_id = extract_batch_id_from_filename(md_file.name)
|
|
303
|
+
if batch_id:
|
|
304
|
+
if batch_id not in batches:
|
|
305
|
+
batches[batch_id] = {"md": None, "json": None}
|
|
306
|
+
batches[batch_id]["md"] = md_file
|
|
307
|
+
|
|
308
|
+
# Coleta .json files
|
|
309
|
+
if LOGS_JSON.exists():
|
|
310
|
+
for json_file in LOGS_JSON.glob("BATCH-*.json"):
|
|
311
|
+
batch_id = extract_batch_id_from_filename(json_file.name)
|
|
312
|
+
if batch_id:
|
|
313
|
+
if batch_id not in batches:
|
|
314
|
+
batches[batch_id] = {"md": None, "json": None}
|
|
315
|
+
batches[batch_id]["json"] = json_file
|
|
316
|
+
|
|
317
|
+
return batches
|
|
318
|
+
|
|
319
|
+
|
|
320
|
+
def enforce_dual_location(batch_id: str) -> Tuple[bool, str]:
|
|
321
|
+
"""
|
|
322
|
+
Garante que batch existe em ambos locais.
|
|
323
|
+
Retorna (success, message)
|
|
324
|
+
"""
|
|
325
|
+
# Busca arquivos existentes para este batch
|
|
326
|
+
md_files = list(LOGS_MD.glob(f"BATCH-{batch_id}*.md")) + list(LOGS_MD.glob(f"BATCH-{batch_id.lstrip('0')}*.md"))
|
|
327
|
+
json_files = list(LOGS_JSON.glob(f"BATCH-{batch_id}*.json")) + list(LOGS_JSON.glob(f"BATCH-{batch_id.lstrip('0')}*.json"))
|
|
328
|
+
|
|
329
|
+
md_exists = len(md_files) > 0
|
|
330
|
+
json_exists = len(json_files) > 0
|
|
331
|
+
|
|
332
|
+
if md_exists and json_exists:
|
|
333
|
+
return True, f"BATCH-{batch_id}: Dual-location OK"
|
|
334
|
+
|
|
335
|
+
if md_exists and not json_exists:
|
|
336
|
+
success = create_json_from_md(batch_id, md_files[0])
|
|
337
|
+
if success:
|
|
338
|
+
return True, f"BATCH-{batch_id}: JSON criado a partir de MD"
|
|
339
|
+
return False, f"BATCH-{batch_id}: ERRO ao criar JSON"
|
|
340
|
+
|
|
341
|
+
if json_exists and not md_exists:
|
|
342
|
+
success = create_md_from_json(batch_id, json_files[0])
|
|
343
|
+
if success:
|
|
344
|
+
return True, f"BATCH-{batch_id}: MD criado a partir de JSON"
|
|
345
|
+
return False, f"BATCH-{batch_id}: ERRO ao criar MD"
|
|
346
|
+
|
|
347
|
+
return False, f"BATCH-{batch_id}: Nao encontrado em nenhum local"
|
|
348
|
+
|
|
349
|
+
|
|
350
|
+
def enforce_single_batch(batch_id: str) -> dict:
|
|
351
|
+
"""
|
|
352
|
+
Enforça dual-location para um batch especifico.
|
|
353
|
+
Usado como hook apos criar batch.
|
|
354
|
+
"""
|
|
355
|
+
ensure_directories()
|
|
356
|
+
success, message = enforce_dual_location(batch_id)
|
|
357
|
+
|
|
358
|
+
result = {
|
|
359
|
+
"batch_id": batch_id,
|
|
360
|
+
"success": success,
|
|
361
|
+
"message": message,
|
|
362
|
+
"timestamp": datetime.now().isoformat()
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
return result
|
|
366
|
+
|
|
367
|
+
|
|
368
|
+
def main():
|
|
369
|
+
"""
|
|
370
|
+
Verifica TODOS os batches e enforça dual-location.
|
|
371
|
+
Gera relatorio completo.
|
|
372
|
+
"""
|
|
373
|
+
ensure_directories()
|
|
374
|
+
|
|
375
|
+
print("=" * 70)
|
|
376
|
+
print(" DUAL-LOCATION ENFORCEMENT HOOK - REGRA #8")
|
|
377
|
+
print(" Verificando todos os batches...")
|
|
378
|
+
print("=" * 70)
|
|
379
|
+
print()
|
|
380
|
+
|
|
381
|
+
batches = get_all_batches()
|
|
382
|
+
|
|
383
|
+
stats = {
|
|
384
|
+
"total": len(batches),
|
|
385
|
+
"complete": 0,
|
|
386
|
+
"md_only": 0,
|
|
387
|
+
"json_only": 0,
|
|
388
|
+
"created_json": 0,
|
|
389
|
+
"created_md": 0,
|
|
390
|
+
"errors": 0
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
# Ordena por numero do batch
|
|
394
|
+
sorted_batches = sorted(batches.items(), key=lambda x: int(x[0]))
|
|
395
|
+
|
|
396
|
+
for batch_id, paths in sorted_batches:
|
|
397
|
+
md_exists = paths["md"] is not None
|
|
398
|
+
json_exists = paths["json"] is not None
|
|
399
|
+
|
|
400
|
+
if md_exists and json_exists:
|
|
401
|
+
stats["complete"] += 1
|
|
402
|
+
status = "OK"
|
|
403
|
+
elif md_exists and not json_exists:
|
|
404
|
+
stats["md_only"] += 1
|
|
405
|
+
# Tenta criar JSON
|
|
406
|
+
success = create_json_from_md(batch_id, paths["md"])
|
|
407
|
+
if success:
|
|
408
|
+
stats["created_json"] += 1
|
|
409
|
+
status = "CREATED JSON"
|
|
410
|
+
else:
|
|
411
|
+
stats["errors"] += 1
|
|
412
|
+
status = "ERROR"
|
|
413
|
+
elif json_exists and not md_exists:
|
|
414
|
+
stats["json_only"] += 1
|
|
415
|
+
# Tenta criar MD
|
|
416
|
+
success = create_md_from_json(batch_id, paths["json"])
|
|
417
|
+
if success:
|
|
418
|
+
stats["created_md"] += 1
|
|
419
|
+
status = "CREATED MD"
|
|
420
|
+
else:
|
|
421
|
+
stats["errors"] += 1
|
|
422
|
+
status = "ERROR"
|
|
423
|
+
else:
|
|
424
|
+
status = "MISSING"
|
|
425
|
+
stats["errors"] += 1
|
|
426
|
+
|
|
427
|
+
# Output visual
|
|
428
|
+
md_icon = "+" if md_exists or status == "CREATED MD" else "X"
|
|
429
|
+
json_icon = "+" if json_exists or status == "CREATED JSON" else "X"
|
|
430
|
+
print(f" BATCH-{batch_id}: [MD:{md_icon}] [JSON:{json_icon}] -> {status}")
|
|
431
|
+
|
|
432
|
+
# Sumario
|
|
433
|
+
print()
|
|
434
|
+
print("=" * 70)
|
|
435
|
+
print(" SUMARIO")
|
|
436
|
+
print("=" * 70)
|
|
437
|
+
print(f" Total de batches: {stats['total']}")
|
|
438
|
+
print(f" Ja completos: {stats['complete']}")
|
|
439
|
+
print(f" Apenas MD: {stats['md_only']}")
|
|
440
|
+
print(f" Apenas JSON: {stats['json_only']}")
|
|
441
|
+
print(f" JSONs criados: {stats['created_json']}")
|
|
442
|
+
print(f" MDs criados: {stats['created_md']}")
|
|
443
|
+
print(f" Erros: {stats['errors']}")
|
|
444
|
+
print()
|
|
445
|
+
|
|
446
|
+
final_complete = stats['complete'] + stats['created_json'] + stats['created_md']
|
|
447
|
+
compliance = (final_complete / stats['total'] * 100) if stats['total'] > 0 else 0
|
|
448
|
+
|
|
449
|
+
print(f" COMPLIANCE FINAL: {final_complete}/{stats['total']} ({compliance:.1f}%)")
|
|
450
|
+
print()
|
|
451
|
+
print(f" Log salvo em: {ENFORCEMENT_LOG}")
|
|
452
|
+
print("=" * 70)
|
|
453
|
+
|
|
454
|
+
# Log final
|
|
455
|
+
log_enforcement("FULL_SCAN", "ALL", {
|
|
456
|
+
"stats": stats,
|
|
457
|
+
"compliance": compliance
|
|
458
|
+
})
|
|
459
|
+
|
|
460
|
+
return stats
|
|
461
|
+
|
|
462
|
+
|
|
463
|
+
def hook_main():
|
|
464
|
+
"""
|
|
465
|
+
Hook entry point for Claude Code PostToolUse event.
|
|
466
|
+
Reads JSON from stdin, outputs JSON to stdout.
|
|
467
|
+
Triggers dual-location enforcement when a batch file is written.
|
|
468
|
+
"""
|
|
469
|
+
try:
|
|
470
|
+
input_data = sys.stdin.read()
|
|
471
|
+
hook_input = json.loads(input_data) if input_data else {}
|
|
472
|
+
|
|
473
|
+
tool_input = hook_input.get('tool_input', {})
|
|
474
|
+
file_path = tool_input.get('file_path', '')
|
|
475
|
+
|
|
476
|
+
# Only trigger for batch files
|
|
477
|
+
if 'BATCH' not in file_path.upper():
|
|
478
|
+
print(json.dumps({'continue': True}))
|
|
479
|
+
return
|
|
480
|
+
|
|
481
|
+
# Extract batch ID from path
|
|
482
|
+
batch_id = extract_batch_id_from_filename(Path(file_path).name)
|
|
483
|
+
if not batch_id:
|
|
484
|
+
print(json.dumps({'continue': True}))
|
|
485
|
+
return
|
|
486
|
+
|
|
487
|
+
ensure_directories()
|
|
488
|
+
success, message = enforce_dual_location(batch_id)
|
|
489
|
+
|
|
490
|
+
feedback = f"[REGRA #8] {message}" if success else None
|
|
491
|
+
print(json.dumps({'continue': True, 'feedback': feedback}))
|
|
492
|
+
|
|
493
|
+
except Exception:
|
|
494
|
+
print(json.dumps({'continue': True}))
|
|
495
|
+
|
|
496
|
+
|
|
497
|
+
if __name__ == "__main__":
|
|
498
|
+
if len(sys.argv) > 1 and sys.argv[1] == '--full':
|
|
499
|
+
main()
|
|
500
|
+
else:
|
|
501
|
+
hook_main()
|