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,170 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
BUILD PLANILHA INDEX - Cria índice nome→TAG da planilha
|
|
4
|
+
Processa os arquivos JSON exportados das abas do Google Sheets
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import os
|
|
8
|
+
import re
|
|
9
|
+
import json
|
|
10
|
+
from datetime import datetime
|
|
11
|
+
|
|
12
|
+
# Configurações
|
|
13
|
+
TOOL_RESULTS_PATH = "" # Set to your Claude tool-results path
|
|
14
|
+
OUTPUT_PATH = ".claude/mission-control/PLANILHA-INDEX.json"
|
|
15
|
+
SCHEMA_PATH = ".claude/mission-control/SPREADSHEET-SCHEMA.json"
|
|
16
|
+
|
|
17
|
+
def normalize_name(name):
|
|
18
|
+
"""Normaliza nome para matching."""
|
|
19
|
+
if not name:
|
|
20
|
+
return ""
|
|
21
|
+
# Lowercase
|
|
22
|
+
name = str(name).lower()
|
|
23
|
+
# Remove extensão
|
|
24
|
+
name = re.sub(r'\.(mp4|docx|txt|pdf)$', '', name, flags=re.IGNORECASE)
|
|
25
|
+
# Remove número inicial
|
|
26
|
+
name = re.sub(r'^\d+[\s\.\-]+', '', name)
|
|
27
|
+
# Remove (1), (2), etc
|
|
28
|
+
name = re.sub(r'\s*\(\d+\)\s*', '', name)
|
|
29
|
+
# Remove caracteres especiais
|
|
30
|
+
name = re.sub(r'[^\w\s]', ' ', name)
|
|
31
|
+
name = re.sub(r'\s+', ' ', name).strip()
|
|
32
|
+
return name
|
|
33
|
+
|
|
34
|
+
def process_sheet_data(data, sheet_name, tag_col_index):
|
|
35
|
+
"""Processa dados de uma aba e extrai entradas."""
|
|
36
|
+
entries = []
|
|
37
|
+
|
|
38
|
+
for row in data:
|
|
39
|
+
if not row or len(row) < tag_col_index + 1:
|
|
40
|
+
continue
|
|
41
|
+
|
|
42
|
+
# Extrair valores relevantes
|
|
43
|
+
name_cell = row[0] if len(row) > 0 else None # Coluna B (nome)
|
|
44
|
+
tag_cell = row[tag_col_index] if len(row) > tag_col_index else None
|
|
45
|
+
|
|
46
|
+
if not name_cell or not tag_cell:
|
|
47
|
+
continue
|
|
48
|
+
|
|
49
|
+
name = name_cell.get('value', '') if isinstance(name_cell, dict) else str(name_cell)
|
|
50
|
+
tag = tag_cell.get('value', '') if isinstance(tag_cell, dict) else str(tag_cell)
|
|
51
|
+
|
|
52
|
+
# Verificar se TAG é válida
|
|
53
|
+
if not tag or not re.match(r'^[\w-]+-\d{4}$', tag):
|
|
54
|
+
continue
|
|
55
|
+
|
|
56
|
+
entries.append({
|
|
57
|
+
'original_name': name,
|
|
58
|
+
'normalized': normalize_name(name),
|
|
59
|
+
'tag': tag,
|
|
60
|
+
'sheet': sheet_name
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
return entries
|
|
64
|
+
|
|
65
|
+
def main():
|
|
66
|
+
print("=" * 60)
|
|
67
|
+
print("BUILD PLANILHA INDEX")
|
|
68
|
+
print("=" * 60)
|
|
69
|
+
print()
|
|
70
|
+
|
|
71
|
+
# Carregar schema para saber configuração de cada aba
|
|
72
|
+
print("[1/3] Carregando schema...")
|
|
73
|
+
with open(SCHEMA_PATH, 'r', encoding='utf-8') as f:
|
|
74
|
+
schema = json.load(f)
|
|
75
|
+
|
|
76
|
+
# Encontrar arquivos JSON do Google Sheets
|
|
77
|
+
print("[2/3] Processando dados exportados...")
|
|
78
|
+
|
|
79
|
+
# Dados inline do Sales Training (copiados do resultado anterior)
|
|
80
|
+
# Por agora, vou criar entradas manualmente baseadas no schema
|
|
81
|
+
|
|
82
|
+
entries = []
|
|
83
|
+
|
|
84
|
+
# Dados do Jeremy Haynes Sales Training (processado inline)
|
|
85
|
+
jh_st_data = [
|
|
86
|
+
("1 - Introduction to Sales Training", "JH-ST-0001"),
|
|
87
|
+
("2. The Plague of Fat Cat Closers", "JH-ST-0002"),
|
|
88
|
+
("3. How to Deal With Inbound Leads Who Only Filled Out a Form", "JH-ST-0003"),
|
|
89
|
+
("4. Sales Team Structures to Consider", "JH-ST-0004"),
|
|
90
|
+
("5. The Cleaner Role", "JH-ST-0005"),
|
|
91
|
+
("6. Using Sales Videos to Aid The Sales Process", "JH-ST-0006"),
|
|
92
|
+
("7. Selfie Follow up Video Texts Get an iphone", "JH-ST-0007"),
|
|
93
|
+
("8. Get Leads Back on The Phone With Value Driven Follow up", "JH-ST-0008"),
|
|
94
|
+
("9. How to Increase Show Rate", "JH-ST-0009"),
|
|
95
|
+
("10.Why You Should Text Every Lead Manually", "JH-ST-0010"),
|
|
96
|
+
("11. Value Added vs Selfish Questions", "JH-ST-0011"),
|
|
97
|
+
("12. Value Added Questions vs Selfish Questions", "JH-ST-0012"),
|
|
98
|
+
("13. How to Increase Average Order Value", "JH-ST-0013"),
|
|
99
|
+
("14. How to Recruit Sales People", "JH-ST-0014"),
|
|
100
|
+
("15. How to Train Maintain a Great Sales Team", "JH-ST-0015"),
|
|
101
|
+
("16. Why Full Time Closers Are Better", "JH-ST-0016"),
|
|
102
|
+
("17. How to Raise The Value of The Price", "JH-ST-0017"),
|
|
103
|
+
("18. Raising The Clients Interest Level", "JH-ST-0018"),
|
|
104
|
+
("19. How to Create Real Scarcity Urgency", "JH-ST-0019"),
|
|
105
|
+
("20. Reviewing Calls Providing Feedback", "JH-ST-0020"),
|
|
106
|
+
("21. Your Personal Life Impacts Your Professional Life", "JH-ST-0021"),
|
|
107
|
+
("22. Tools Software For Closers", "JH-ST-0022"),
|
|
108
|
+
("23. When to Fire Sales Reps", "JH-ST-0023"),
|
|
109
|
+
("24. Keep Your Moral High", "JH-ST-0024"),
|
|
110
|
+
("25. Sales Training With Jordan Stupar", "JH-ST-0025"),
|
|
111
|
+
("26. Sales Training With Josh Troy", "JH-ST-0026"),
|
|
112
|
+
("1. Intro to Objections", "JH-ST-0028"),
|
|
113
|
+
("2. What Happens if You go Out of Business", "JH-ST-0029"),
|
|
114
|
+
("3. How do I Know my Money is Safe", "JH-ST-0030"),
|
|
115
|
+
("4. Can I Speak to The Owner", "JH-ST-0031"),
|
|
116
|
+
("5. How Can You Guarantee That Ill Make Money", "JH-ST-0032"),
|
|
117
|
+
("6. Why Are You Better Than Everybody Else", "JH-ST-0033"),
|
|
118
|
+
("7. Id Rather Get Started Next Quarter", "JH-ST-0034"),
|
|
119
|
+
("8. I Have to Get my Lawyer to Review Before Moving Forward", "JH-ST-0035"),
|
|
120
|
+
("9. My Business Partner I Will Need To Review This", "JH-ST-0036"),
|
|
121
|
+
("10. Im Traveling This Week Lets Move Forward Next Week", "JH-ST-0037"),
|
|
122
|
+
("11. Are You Open to Changes in The Agreement", "JH-ST-0038"),
|
|
123
|
+
("12. Can I Speak to an Existing Client", "JH-ST-0039"),
|
|
124
|
+
("13. I Saw a Negative Review Im Afraid Itll Happen to me", "JH-ST-0040"),
|
|
125
|
+
("14. Objection I Cant Afford This", "JH-ST-0041"),
|
|
126
|
+
("15. Objection I Cant Afford This", "JH-ST-0042"),
|
|
127
|
+
("16. Objection I Cant Afford This", "JH-ST-0043"),
|
|
128
|
+
("17. I Didnt Expect it to Cost This Much", "JH-ST-0044"),
|
|
129
|
+
("18. I am Looking at Other Service Providers", "JH-ST-0045"),
|
|
130
|
+
]
|
|
131
|
+
|
|
132
|
+
for name, tag in jh_st_data:
|
|
133
|
+
entries.append({
|
|
134
|
+
'original_name': name,
|
|
135
|
+
'normalized': normalize_name(name),
|
|
136
|
+
'tag': tag,
|
|
137
|
+
'sheet': 'Jeremy Haynes Sales Training'
|
|
138
|
+
})
|
|
139
|
+
|
|
140
|
+
print(f" Processadas {len(entries)} entradas iniciais")
|
|
141
|
+
print()
|
|
142
|
+
print(" NOTA: Este script precisa ser expandido para processar todas as abas")
|
|
143
|
+
print(" via API do Google Sheets ou arquivos JSON exportados.")
|
|
144
|
+
print()
|
|
145
|
+
|
|
146
|
+
# 3. Salvar índice
|
|
147
|
+
print("[3/3] Salvando índice...")
|
|
148
|
+
|
|
149
|
+
index = {
|
|
150
|
+
'version': '1.0',
|
|
151
|
+
'timestamp': datetime.now().isoformat(),
|
|
152
|
+
'total_entries': len(entries),
|
|
153
|
+
'sheets_processed': list(set(e['sheet'] for e in entries)),
|
|
154
|
+
'entries': entries
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
with open(OUTPUT_PATH, 'w', encoding='utf-8') as f:
|
|
158
|
+
json.dump(index, f, ensure_ascii=False, indent=2)
|
|
159
|
+
|
|
160
|
+
print(f" Salvo em: {OUTPUT_PATH}")
|
|
161
|
+
print(f" Total: {len(entries)} entradas")
|
|
162
|
+
print()
|
|
163
|
+
print("=" * 60)
|
|
164
|
+
print("ÍNDICE CRIADO (parcial)")
|
|
165
|
+
print("=" * 60)
|
|
166
|
+
|
|
167
|
+
return index
|
|
168
|
+
|
|
169
|
+
if __name__ == '__main__':
|
|
170
|
+
main()
|
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Complete Tag Matching Script - DE-PARA 1-a-1
|
|
4
|
+
Matches each untagged file to its correct planilha entry and applies TAG
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import os
|
|
8
|
+
import re
|
|
9
|
+
import json
|
|
10
|
+
from difflib import SequenceMatcher
|
|
11
|
+
|
|
12
|
+
# Planilha data - extracted from Google Sheets
|
|
13
|
+
PLANILHA_DATA = {
|
|
14
|
+
"30 Days Challenge": {
|
|
15
|
+
"prefix": "30DC",
|
|
16
|
+
"entries": {
|
|
17
|
+
"1 - Day 1": "30DC-0001",
|
|
18
|
+
"2 - Day 2": "30DC-0002",
|
|
19
|
+
"3 - Day 3": "30DC-0003",
|
|
20
|
+
"4 - Day 4": "30DC-0004",
|
|
21
|
+
"5 - Day 5": "30DC-0005",
|
|
22
|
+
"6 - Day 6": "30DC-0006",
|
|
23
|
+
"7 - Day 7": "30DC-0007",
|
|
24
|
+
"8 - Day 8": "30DC-0008",
|
|
25
|
+
"9 - Day 9": "30DC-0009",
|
|
26
|
+
"10 - Day 10": "30DC-0010",
|
|
27
|
+
"11 - Day 11.": "30DC-0011",
|
|
28
|
+
"12 - Day 12.": "30DC-0012",
|
|
29
|
+
"13- Day 13.": "30DC-0013",
|
|
30
|
+
"14 - Day 14.": "30DC-0014",
|
|
31
|
+
"15 - Day 15": "30DC-0015",
|
|
32
|
+
"16 - Day 16": "30DC-0016",
|
|
33
|
+
"17 - Day 17": "30DC-0017",
|
|
34
|
+
"18 - Day 18": "30DC-0018",
|
|
35
|
+
"19 - Day 19": "30DC-0019",
|
|
36
|
+
"20 - Day 20": "30DC-0020",
|
|
37
|
+
"21 - Day 21": "30DC-0021",
|
|
38
|
+
"22 - Day 22": "30DC-0022",
|
|
39
|
+
"23 - Day 23": "30DC-0023",
|
|
40
|
+
"24 - Day 24": "30DC-0024",
|
|
41
|
+
"25 - Day 25": "30DC-0025",
|
|
42
|
+
"26 - Day 26": "30DC-0026",
|
|
43
|
+
"27 - Day 27": "30DC-0027",
|
|
44
|
+
"28 - Day 28": "30DC-0028",
|
|
45
|
+
"29 - Day 29.": "30DC-0029",
|
|
46
|
+
"30 - Day 30": "30DC-0030",
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
"Cold Video Pitch": {
|
|
50
|
+
"prefix": "PCVP",
|
|
51
|
+
"entries": {
|
|
52
|
+
"1. Power Of Cold Video.": "PCVP-0001",
|
|
53
|
+
"2. Selling Advertising.": "PCVP-0002",
|
|
54
|
+
"3. Social Media Management.": "PCVP-0003",
|
|
55
|
+
"4. Google PPC.": "PCVP-0004",
|
|
56
|
+
"5. Facebook And Instagram.": "PCVP-0005",
|
|
57
|
+
"6. Long Form Content Creation.": "PCVP-0006",
|
|
58
|
+
"7. Animation.": "PCVP-0007",
|
|
59
|
+
"8. Graphics Design.": "PCVP-0008",
|
|
60
|
+
"9. Google Listing.": "PCVP-0009",
|
|
61
|
+
"10. Redoing Thumbnails.": "PCVP-0010",
|
|
62
|
+
"11. Sell Funnels.": "PCVP-0011",
|
|
63
|
+
"12. Chatbots.": "PCVP-0012",
|
|
64
|
+
"13. PR Services.": "PCVP-0013",
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
"Agency Blueprint": {
|
|
68
|
+
"prefix": "AOBA",
|
|
69
|
+
"entries": {
|
|
70
|
+
"1. What Is A Marketing Agency.": "AOBA-0001",
|
|
71
|
+
"2. Phases Of A Marketing Agency.": "AOBA-0002",
|
|
72
|
+
"3. Skills Or No Skills.": "AOBA-0003",
|
|
73
|
+
"4. Is It Best To Have Stuff Or A Referral Network.": "AOBA-0004",
|
|
74
|
+
"5. What Are The Possible Limiting Beliefs.": "AOBA-0005",
|
|
75
|
+
"6. What Is PPHD.": "AOBA-0006",
|
|
76
|
+
"7. Leveraging The Facebook Group.": "AOBA-0007",
|
|
77
|
+
"8. Is It Best To Go General Or Niche.": "AOBA-0008",
|
|
78
|
+
"9. Whats The Best Business Model Service Clients Or Consulting.": "AOBA-0009",
|
|
79
|
+
"10. How To Keep A Real Perspective Of Things.": "AOBA-0010",
|
|
80
|
+
"11. Do You Need A Website For Your Agency.": "AOBA-0011",
|
|
81
|
+
"12. How To Become An Expert.": "AOBA-0012",
|
|
82
|
+
"13. Before You Even Think About Getting A Partner.": "AOBA-0013",
|
|
83
|
+
"14. How To Be Comfortable With Failure.": "AOBA-0014",
|
|
84
|
+
"15. Anatomy Of A Perfect Client.": "AOBA-0015",
|
|
85
|
+
"16. How To Keep Your Standards High.": "AOBA-0016",
|
|
86
|
+
"17. The Power Of Expert Positioning": "AOBA-0017",
|
|
87
|
+
"18. How To Improve Your Social Presence.": "AOBA-0018",
|
|
88
|
+
"19. The Best Way To Hijack Authority.": "AOBA-0019",
|
|
89
|
+
"20. Differences Between Hard Flex and Soft Flex.": "AOBA-0020",
|
|
90
|
+
"21. When To Ask For Testimonials.": "AOBA-0021",
|
|
91
|
+
"22. Every Result Is A Marketable Headline.": "AOBA-0022",
|
|
92
|
+
"23. How To PR For Expansion.": "AOBA-0023",
|
|
93
|
+
"24. How To Overcome Major Obstacles.": "AOBA-0024",
|
|
94
|
+
"25. Is It Better To Charge High-Ticket Or Competitive Pricing.": "AOBA-0025",
|
|
95
|
+
"26. What Are The Core Services To Offer.": "AOBA-0026",
|
|
96
|
+
"27. What Model Is Better Transactional Or Recurring.": "AOBA-0027",
|
|
97
|
+
"28. How To Move From Transactional To Recurring.": "AOBA-0028",
|
|
98
|
+
"29. How To Stay Away From Free To Paid Service.": "AOBA-0029",
|
|
99
|
+
"30. The Profit First Business Model.": "AOBA-0030",
|
|
100
|
+
"31. How To Manage Agency Costs.": "AOBA-0031",
|
|
101
|
+
"32. How To Setup Bank Accounts.": "AOBA-0032",
|
|
102
|
+
"33. State Of The Agency Contraction And Expansion.": "AOBA-0033",
|
|
103
|
+
"34. Refunds Policy No Refunds.": "AOBA-0034",
|
|
104
|
+
"35. Result-Driven Services.": "AOBA-0035",
|
|
105
|
+
"36. Is It Better To Have A Contract Or Not.": "AOBA-0036",
|
|
106
|
+
"37. Change Is The Only Constant.": "AOBA-0037",
|
|
107
|
+
"38. Is It Best To Get New Skills Or Referrals.": "AOBA-0038",
|
|
108
|
+
"39. Cash Is Like Oxygen.": "AOBA-0039",
|
|
109
|
+
"40. Why Big Companies Focus On Sales.": "AOBA-0040",
|
|
110
|
+
"41. Daily Attitude And Training": "AOBA-0041",
|
|
111
|
+
"42. How Your Outflow Equals Your Inflow.": "AOBA-0042",
|
|
112
|
+
"43. Money Is In The Follow Up.": "AOBA-0043",
|
|
113
|
+
"44. How To Use Conviction To Win More.": "AOBA-0044",
|
|
114
|
+
"45. How To Win The Sales Game.": "AOBA-0045",
|
|
115
|
+
"46. Duplicating Yourself With Sales People.": "AOBA-0046",
|
|
116
|
+
"47. How To Be Confident In Your Services.": "AOBA-0047",
|
|
117
|
+
"48. How To Keep Your Cool At All Times.": "AOBA-0048",
|
|
118
|
+
"49. Is Cold Calling Dead.": "AOBA-0049",
|
|
119
|
+
"50. How To Keep A Full Pipeline.": "AOBA-0050",
|
|
120
|
+
"51. When Is It Time To Fire A Client.": "AOBA-0051",
|
|
121
|
+
"52. How To Deal With More Rejection Than Acceptance": "AOBA-0052",
|
|
122
|
+
"53. What Is A VAK.": "AOBA-0053",
|
|
123
|
+
"54. Leverage The Power Of Math.": "AOBA-0054",
|
|
124
|
+
"55. How To Use Systems To Save Your Agency.": "AOBA-0055",
|
|
125
|
+
"56. How To Keep Your Team Accountable.": "AOBA-0056",
|
|
126
|
+
"57. Expectations Equal Success.": "AOBA-0057",
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
def normalize_name(name):
|
|
132
|
+
"""Normalize filename for matching"""
|
|
133
|
+
# Remove extension
|
|
134
|
+
name = re.sub(r'\.(txt|docx|mp4|pdf)$', '', name, flags=re.IGNORECASE)
|
|
135
|
+
# Remove youtube references
|
|
136
|
+
name = re.sub(r'\s*\[youtube\.com.*?\]', '', name)
|
|
137
|
+
# Remove timestamps
|
|
138
|
+
name = re.sub(r'\s*\d{1,2}-\d{1,2}-\d{2,4}', '', name)
|
|
139
|
+
# Normalize spaces and case
|
|
140
|
+
name = name.strip().lower()
|
|
141
|
+
# Remove multiple spaces
|
|
142
|
+
name = re.sub(r'\s+', ' ', name)
|
|
143
|
+
return name
|
|
144
|
+
|
|
145
|
+
def find_best_match(filename, threshold=0.7):
|
|
146
|
+
"""Find best matching planilha entry for a filename"""
|
|
147
|
+
norm_file = normalize_name(filename)
|
|
148
|
+
|
|
149
|
+
best_match = None
|
|
150
|
+
best_score = 0
|
|
151
|
+
best_tag = None
|
|
152
|
+
best_source = None
|
|
153
|
+
|
|
154
|
+
for source, data in PLANILHA_DATA.items():
|
|
155
|
+
for entry_name, tag in data["entries"].items():
|
|
156
|
+
norm_entry = normalize_name(entry_name)
|
|
157
|
+
score = SequenceMatcher(None, norm_file, norm_entry).ratio()
|
|
158
|
+
|
|
159
|
+
if score > best_score:
|
|
160
|
+
best_score = score
|
|
161
|
+
best_match = entry_name
|
|
162
|
+
best_tag = tag
|
|
163
|
+
best_source = source
|
|
164
|
+
|
|
165
|
+
if best_score >= threshold:
|
|
166
|
+
return {
|
|
167
|
+
"matched_to": best_match,
|
|
168
|
+
"tag": best_tag,
|
|
169
|
+
"source": best_source,
|
|
170
|
+
"score": best_score
|
|
171
|
+
}
|
|
172
|
+
return None
|
|
173
|
+
|
|
174
|
+
def get_untagged_files(inbox_path):
|
|
175
|
+
"""Get all untagged .txt files"""
|
|
176
|
+
untagged = []
|
|
177
|
+
|
|
178
|
+
for root, dirs, files in os.walk(inbox_path):
|
|
179
|
+
# Skip hidden and backup folders
|
|
180
|
+
dirs[:] = [d for d in dirs if not d.startswith('.') and not d.startswith('_')]
|
|
181
|
+
|
|
182
|
+
for f in files:
|
|
183
|
+
if f.endswith('.txt') and not (f.startswith('[') and ']' in f):
|
|
184
|
+
full_path = os.path.join(root, f)
|
|
185
|
+
rel_path = full_path.replace(inbox_path + '/', '')
|
|
186
|
+
untagged.append({
|
|
187
|
+
"filename": f,
|
|
188
|
+
"full_path": full_path,
|
|
189
|
+
"rel_path": rel_path
|
|
190
|
+
})
|
|
191
|
+
|
|
192
|
+
return untagged
|
|
193
|
+
|
|
194
|
+
def main():
|
|
195
|
+
inbox_path = "inbox"
|
|
196
|
+
|
|
197
|
+
print("=== COMPLETE TAG MATCHING - DE-PARA 1-a-1 ===\n")
|
|
198
|
+
|
|
199
|
+
# Get untagged files
|
|
200
|
+
untagged = get_untagged_files(inbox_path)
|
|
201
|
+
print(f"Total arquivos sem TAG: {len(untagged)}\n")
|
|
202
|
+
|
|
203
|
+
# Match each file
|
|
204
|
+
matches = []
|
|
205
|
+
orphans = []
|
|
206
|
+
|
|
207
|
+
for file_info in untagged:
|
|
208
|
+
match = find_best_match(file_info["filename"])
|
|
209
|
+
if match:
|
|
210
|
+
matches.append({
|
|
211
|
+
**file_info,
|
|
212
|
+
**match
|
|
213
|
+
})
|
|
214
|
+
else:
|
|
215
|
+
orphans.append(file_info)
|
|
216
|
+
|
|
217
|
+
print(f"MATCHES ENCONTRADOS: {len(matches)}")
|
|
218
|
+
print(f"ÓRFÃOS (sem match): {len(orphans)}\n")
|
|
219
|
+
|
|
220
|
+
print("=== MATCHES DETALHADOS ===\n")
|
|
221
|
+
for m in sorted(matches, key=lambda x: x["tag"]):
|
|
222
|
+
print(f"[{m['tag']}] {m['filename']}")
|
|
223
|
+
print(f" → Planilha: {m['matched_to']} ({m['source']})")
|
|
224
|
+
print(f" → Score: {m['score']:.2%}")
|
|
225
|
+
print(f" → Path: {m['rel_path']}")
|
|
226
|
+
print()
|
|
227
|
+
|
|
228
|
+
print("\n=== ÓRFÃOS (ARQUIVOS SEM MATCH NA PLANILHA) ===\n")
|
|
229
|
+
for o in orphans:
|
|
230
|
+
print(f" ✗ {o['rel_path']}")
|
|
231
|
+
|
|
232
|
+
# Save results
|
|
233
|
+
results = {
|
|
234
|
+
"total_untagged": len(untagged),
|
|
235
|
+
"total_matches": len(matches),
|
|
236
|
+
"total_orphans": len(orphans),
|
|
237
|
+
"matches": matches,
|
|
238
|
+
"orphans": orphans
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
output_path = ".claude/mission-control/COMPLETE-TAG-MATCHING.json"
|
|
242
|
+
with open(output_path, 'w') as f:
|
|
243
|
+
json.dump(results, f, indent=2, ensure_ascii=False)
|
|
244
|
+
|
|
245
|
+
print(f"\n\nResultados salvos em: {output_path}")
|
|
246
|
+
|
|
247
|
+
return results
|
|
248
|
+
|
|
249
|
+
if __name__ == "__main__":
|
|
250
|
+
main()
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Deduplication Script - Remove duplicate files keeping only the most recent
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import os
|
|
7
|
+
import re
|
|
8
|
+
import shutil
|
|
9
|
+
from collections import defaultdict
|
|
10
|
+
from datetime import datetime
|
|
11
|
+
|
|
12
|
+
INBOX_PATH = "inbox"
|
|
13
|
+
BACKUP_PATH = os.path.join(INBOX_PATH, "_DEDUP_BACKUP")
|
|
14
|
+
|
|
15
|
+
def find_tagged_files():
|
|
16
|
+
"""Find all files with [TAG-XXXX] format"""
|
|
17
|
+
tagged_files = defaultdict(list)
|
|
18
|
+
tag_pattern = re.compile(r'^\[([A-Z0-9\-]+)\]')
|
|
19
|
+
|
|
20
|
+
for root, dirs, files in os.walk(INBOX_PATH):
|
|
21
|
+
# Skip the backup folder itself
|
|
22
|
+
if "_DEDUP_BACKUP" in root:
|
|
23
|
+
continue
|
|
24
|
+
|
|
25
|
+
for filename in files:
|
|
26
|
+
if not filename.endswith('.txt'):
|
|
27
|
+
continue
|
|
28
|
+
|
|
29
|
+
match = tag_pattern.match(filename)
|
|
30
|
+
if match:
|
|
31
|
+
tag = match.group(1)
|
|
32
|
+
full_path = os.path.join(root, filename)
|
|
33
|
+
mtime = os.path.getmtime(full_path)
|
|
34
|
+
tagged_files[tag].append({
|
|
35
|
+
'filename': filename,
|
|
36
|
+
'path': full_path,
|
|
37
|
+
'mtime': mtime,
|
|
38
|
+
'rel_path': os.path.relpath(full_path, INBOX_PATH)
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
return tagged_files
|
|
42
|
+
|
|
43
|
+
def deduplicate(execute=False):
|
|
44
|
+
"""Remove duplicates, keeping the most recent file for each TAG"""
|
|
45
|
+
|
|
46
|
+
print("=" * 70)
|
|
47
|
+
print("DEDUPLICAÇÃO DO INBOX - MEGA BRAIN")
|
|
48
|
+
print("=" * 70)
|
|
49
|
+
print(f"\nModo: {'EXECUTAR' if execute else 'PREVIEW (--execute para aplicar)'}")
|
|
50
|
+
print("=" * 70)
|
|
51
|
+
|
|
52
|
+
# Ensure backup folder exists
|
|
53
|
+
if execute and not os.path.exists(BACKUP_PATH):
|
|
54
|
+
os.makedirs(BACKUP_PATH)
|
|
55
|
+
|
|
56
|
+
tagged_files = find_tagged_files()
|
|
57
|
+
|
|
58
|
+
# Stats
|
|
59
|
+
total_tags = len(tagged_files)
|
|
60
|
+
duplicated_tags = 0
|
|
61
|
+
files_to_remove = 0
|
|
62
|
+
|
|
63
|
+
duplicates_list = []
|
|
64
|
+
|
|
65
|
+
for tag, files in sorted(tagged_files.items()):
|
|
66
|
+
if len(files) > 1:
|
|
67
|
+
duplicated_tags += 1
|
|
68
|
+
# Sort by mtime descending (most recent first)
|
|
69
|
+
files_sorted = sorted(files, key=lambda x: x['mtime'], reverse=True)
|
|
70
|
+
keep = files_sorted[0]
|
|
71
|
+
remove = files_sorted[1:]
|
|
72
|
+
|
|
73
|
+
print(f"\n[{tag}] {len(files)} cópias encontradas:")
|
|
74
|
+
print(f" ✓ MANTER: {keep['rel_path']}")
|
|
75
|
+
print(f" (modificado: {datetime.fromtimestamp(keep['mtime']).strftime('%Y-%m-%d %H:%M')})")
|
|
76
|
+
|
|
77
|
+
for f in remove:
|
|
78
|
+
files_to_remove += 1
|
|
79
|
+
print(f" ✗ REMOVER: {f['rel_path']}")
|
|
80
|
+
print(f" (modificado: {datetime.fromtimestamp(f['mtime']).strftime('%Y-%m-%d %H:%M')})")
|
|
81
|
+
duplicates_list.append({
|
|
82
|
+
'tag': tag,
|
|
83
|
+
'file': f,
|
|
84
|
+
'keep': keep
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
print("\n" + "=" * 70)
|
|
88
|
+
print("RESUMO")
|
|
89
|
+
print("=" * 70)
|
|
90
|
+
print(f"Total de TAGs únicas: {total_tags}")
|
|
91
|
+
print(f"TAGs com duplicatas: {duplicated_tags}")
|
|
92
|
+
print(f"Arquivos a remover: {files_to_remove}")
|
|
93
|
+
|
|
94
|
+
if execute and duplicates_list:
|
|
95
|
+
print("\n" + "=" * 70)
|
|
96
|
+
print("EXECUTANDO REMOÇÃO")
|
|
97
|
+
print("=" * 70)
|
|
98
|
+
|
|
99
|
+
success = 0
|
|
100
|
+
errors = 0
|
|
101
|
+
|
|
102
|
+
for dup in duplicates_list:
|
|
103
|
+
src = dup['file']['path']
|
|
104
|
+
# Create subfolder in backup based on original relative path
|
|
105
|
+
rel_dir = os.path.dirname(dup['file']['rel_path'])
|
|
106
|
+
if rel_dir:
|
|
107
|
+
dest_dir = os.path.join(BACKUP_PATH, rel_dir)
|
|
108
|
+
else:
|
|
109
|
+
dest_dir = BACKUP_PATH
|
|
110
|
+
|
|
111
|
+
if not os.path.exists(dest_dir):
|
|
112
|
+
os.makedirs(dest_dir)
|
|
113
|
+
|
|
114
|
+
dest = os.path.join(dest_dir, dup['file']['filename'])
|
|
115
|
+
|
|
116
|
+
try:
|
|
117
|
+
shutil.move(src, dest)
|
|
118
|
+
print(f"✓ Movido: {dup['file']['rel_path']}")
|
|
119
|
+
success += 1
|
|
120
|
+
except Exception as e:
|
|
121
|
+
print(f"✗ Erro: {dup['file']['rel_path']} - {e}")
|
|
122
|
+
errors += 1
|
|
123
|
+
|
|
124
|
+
print("\n" + "=" * 70)
|
|
125
|
+
print("RESULTADO FINAL")
|
|
126
|
+
print("=" * 70)
|
|
127
|
+
print(f"✓ Arquivos movidos para backup: {success}")
|
|
128
|
+
print(f"✗ Erros: {errors}")
|
|
129
|
+
print(f"\nBackup em: {BACKUP_PATH}")
|
|
130
|
+
|
|
131
|
+
elif not execute:
|
|
132
|
+
print(f"\nExecute com: python3 deduplicate-inbox.py --execute")
|
|
133
|
+
|
|
134
|
+
return duplicated_tags, files_to_remove
|
|
135
|
+
|
|
136
|
+
if __name__ == "__main__":
|
|
137
|
+
import sys
|
|
138
|
+
execute = '--execute' in sys.argv
|
|
139
|
+
deduplicate(execute)
|