aios-core 4.0.2 → 4.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.
Files changed (111) hide show
  1. package/.aios-core/cli/commands/migrate/analyze.js +6 -6
  2. package/.aios-core/cli/commands/migrate/backup.js +2 -2
  3. package/.aios-core/cli/commands/migrate/execute.js +4 -4
  4. package/.aios-core/cli/commands/migrate/index.js +5 -5
  5. package/.aios-core/cli/commands/migrate/rollback.js +6 -6
  6. package/.aios-core/cli/commands/migrate/update-imports.js +2 -2
  7. package/.aios-core/cli/commands/migrate/validate.js +2 -2
  8. package/.aios-core/cli/commands/pro/index.js +52 -0
  9. package/.aios-core/cli/index.js +1 -1
  10. package/.aios-core/core/ids/registry-updater.js +29 -3
  11. package/.aios-core/core/migration/migration-config.yaml +2 -2
  12. package/.aios-core/core/migration/module-mapping.yaml +2 -2
  13. package/.aios-core/core/registry/README.md +2 -2
  14. package/.aios-core/core/synapse/context/context-builder.js +34 -0
  15. package/.aios-core/core/synapse/diagnostics/collectors/consistency-collector.js +168 -0
  16. package/.aios-core/core/synapse/diagnostics/collectors/hook-collector.js +129 -0
  17. package/.aios-core/core/synapse/diagnostics/collectors/manifest-collector.js +82 -0
  18. package/.aios-core/core/synapse/diagnostics/collectors/output-analyzer.js +134 -0
  19. package/.aios-core/core/synapse/diagnostics/collectors/pipeline-collector.js +75 -0
  20. package/.aios-core/core/synapse/diagnostics/collectors/quality-collector.js +252 -0
  21. package/.aios-core/core/synapse/diagnostics/collectors/relevance-matrix.js +174 -0
  22. package/.aios-core/core/synapse/diagnostics/collectors/safe-read-json.js +31 -0
  23. package/.aios-core/core/synapse/diagnostics/collectors/session-collector.js +102 -0
  24. package/.aios-core/core/synapse/diagnostics/collectors/timing-collector.js +126 -0
  25. package/.aios-core/core/synapse/diagnostics/collectors/uap-collector.js +83 -0
  26. package/.aios-core/core/synapse/diagnostics/report-formatter.js +484 -0
  27. package/.aios-core/core/synapse/diagnostics/synapse-diagnostics.js +95 -0
  28. package/.aios-core/core/synapse/engine.js +73 -20
  29. package/.aios-core/core/synapse/runtime/hook-runtime.js +60 -0
  30. package/.aios-core/core-config.yaml +6 -0
  31. package/.aios-core/data/agent-config-requirements.yaml +2 -2
  32. package/.aios-core/data/aios-kb.md +4 -4
  33. package/.aios-core/data/entity-registry.yaml +5 -5
  34. package/.aios-core/development/agents/architect.md +10 -10
  35. package/.aios-core/development/agents/devops.md +93 -50
  36. package/.aios-core/development/agents/qa.md +94 -40
  37. package/.aios-core/development/agents/ux-design-expert.md +25 -25
  38. package/.aios-core/development/scripts/activation-runtime.js +63 -0
  39. package/.aios-core/development/scripts/generate-greeting.js +9 -8
  40. package/.aios-core/development/scripts/unified-activation-pipeline.js +102 -2
  41. package/.aios-core/development/tasks/{db-expansion-pack-integration.md → db-squad-integration.md} +5 -5
  42. package/.aios-core/development/tasks/{integrate-expansion-pack.md → integrate-squad.md} +2 -2
  43. package/.aios-core/development/tasks/next.md +3 -3
  44. package/.aios-core/development/tasks/pr-automation.md +2 -2
  45. package/.aios-core/development/tasks/publish-npm.md +257 -0
  46. package/.aios-core/development/tasks/release-management.md +4 -4
  47. package/.aios-core/development/tasks/setup-github.md +1 -1
  48. package/.aios-core/development/tasks/squad-creator-migrate.md +1 -1
  49. package/.aios-core/development/tasks/squad-creator-sync-ide-command.md +14 -14
  50. package/.aios-core/development/tasks/update-aios.md +1 -1
  51. package/.aios-core/docs/standards/AIOS-COLOR-PALETTE-QUICK-REFERENCE.md +1 -1
  52. package/.aios-core/docs/standards/AIOS-COLOR-PALETTE-V2.1.md +5 -5
  53. package/.aios-core/docs/standards/AIOS-LIVRO-DE-OURO-V2.1-COMPLETE.md +21 -21
  54. package/.aios-core/docs/standards/AIOS-LIVRO-DE-OURO-V2.2-SUMMARY.md +25 -25
  55. package/.aios-core/docs/standards/OPEN-SOURCE-VS-SERVICE-DIFFERENCES.md +4 -4
  56. package/.aios-core/docs/standards/QUALITY-GATES-SPECIFICATION.md +3 -3
  57. package/.aios-core/docs/standards/STANDARDS-INDEX.md +13 -13
  58. package/.aios-core/docs/standards/STORY-TEMPLATE-V2-SPECIFICATION.md +1 -1
  59. package/.aios-core/framework-config.yaml +4 -0
  60. package/.aios-core/infrastructure/scripts/codex-skills-sync/index.js +182 -0
  61. package/.aios-core/infrastructure/scripts/codex-skills-sync/validate.js +172 -0
  62. package/.aios-core/infrastructure/scripts/ide-sync/README.md +14 -0
  63. package/.aios-core/infrastructure/scripts/ide-sync/index.js +6 -0
  64. package/.aios-core/infrastructure/scripts/tool-resolver.js +4 -4
  65. package/.aios-core/infrastructure/scripts/validate-paths.js +142 -0
  66. package/.aios-core/infrastructure/templates/aios-sync.yaml.template +11 -11
  67. package/.aios-core/infrastructure/templates/github-workflows/README.md +1 -1
  68. package/.aios-core/install-manifest.yaml +190 -106
  69. package/.aios-core/local-config.yaml.template +2 -0
  70. package/.aios-core/product/README.md +2 -2
  71. package/.aios-core/product/data/integration-patterns.md +1 -1
  72. package/.aios-core/product/templates/ide-rules/cline-rules.md +1 -1
  73. package/.aios-core/product/templates/ide-rules/codex-rules.md +65 -0
  74. package/.aios-core/product/templates/ide-rules/copilot-rules.md +1 -1
  75. package/.aios-core/product/templates/ide-rules/roo-rules.md +1 -1
  76. package/.aios-core/user-guide.md +15 -14
  77. package/.aios-core/workflow-intelligence/engine/output-formatter.js +1 -1
  78. package/.claude/hooks/enforce-architecture-first.py +196 -0
  79. package/.claude/hooks/install-hooks.sh +41 -0
  80. package/.claude/hooks/mind-clone-governance.py +192 -0
  81. package/.claude/hooks/pre-commit-mmos-guard.sh +99 -0
  82. package/.claude/hooks/pre-commit-version-check.sh +156 -0
  83. package/.claude/hooks/read-protection.py +151 -0
  84. package/.claude/hooks/slug-validation.py +176 -0
  85. package/.claude/hooks/sql-governance.py +182 -0
  86. package/.claude/hooks/synapse-engine.js +9 -20
  87. package/.claude/hooks/write-path-validation.py +194 -0
  88. package/README.md +44 -14
  89. package/bin/aios-init.js +255 -184
  90. package/bin/aios-minimal.js +2 -2
  91. package/bin/aios.js +19 -19
  92. package/package.json +7 -4
  93. package/packages/aios-pro-cli/bin/aios-pro.js +75 -2
  94. package/packages/aios-pro-cli/package.json +5 -1
  95. package/packages/aios-pro-cli/src/recover.js +100 -0
  96. package/packages/installer/src/__tests__/performance-benchmark.js +382 -0
  97. package/packages/installer/src/config/ide-configs.js +12 -1
  98. package/packages/installer/src/config/templates/core-config-template.js +2 -2
  99. package/packages/installer/src/installer/aios-core-installer.js +2 -2
  100. package/packages/installer/src/installer/file-hasher.js +97 -0
  101. package/packages/installer/src/installer/post-install-validator.js +41 -1
  102. package/packages/installer/src/pro/pro-scaffolder.js +335 -0
  103. package/packages/installer/src/utils/aios-colors.js +2 -2
  104. package/packages/installer/src/wizard/feedback.js +1 -1
  105. package/packages/installer/src/wizard/ide-config-generator.js +2 -2
  106. package/packages/installer/src/wizard/index.js +58 -19
  107. package/packages/installer/src/wizard/pro-setup.js +547 -0
  108. package/packages/installer/src/wizard/questions.js +20 -14
  109. package/packages/installer/src/wizard/validators.js +1 -1
  110. package/scripts/package-synapse.js +323 -0
  111. package/scripts/validate-package-completeness.js +317 -0
@@ -0,0 +1,99 @@
1
+ #!/bin/bash
2
+ # MMOS Architecture Guard - Pre-commit Hook
3
+ # Version: 1.0
4
+ # Purpose: Prevent architectural violations in file placement
5
+
6
+ set -e
7
+
8
+ echo "🛡️ Running MMOS Architecture Guard..."
9
+
10
+ # Colors
11
+ RED='\033[0;31m'
12
+ GREEN='\033[0;32m'
13
+ YELLOW='\033[1;33m'
14
+ NC='\033[0m' # No Color
15
+
16
+ violations=0
17
+
18
+ # Get staged files
19
+ staged_files=$(git diff --cached --name-only --diff-filter=ACM)
20
+
21
+ # Rule 1: No mind-specific folders in docs/mmos/
22
+ echo "Checking Rule 1: No mind-specific folders in docs/mmos/..."
23
+
24
+ if echo "$staged_files" | grep -qE "docs/mmos/(validations|migrations)/[a-z_-]+"; then
25
+ echo -e "${RED}❌ VIOLATION: Mind-specific folders detected in docs/mmos/${NC}"
26
+ echo ""
27
+ echo "Found:"
28
+ echo "$staged_files" | grep -E "docs/mmos/(validations|migrations)/[a-z_-]+" | sed 's/^/ - /'
29
+ echo ""
30
+ echo -e "${YELLOW}Fix: Move to outputs/minds/{slug}/docs/ instead${NC}"
31
+ echo ""
32
+ echo "Examples:"
33
+ echo " ❌ docs/mmos/validations/pedro-valerio-checklist.md"
34
+ echo " ✅ outputs/minds/pedro_valerio/docs/validation-checklist.md"
35
+ echo ""
36
+ violations=$((violations + 1))
37
+ fi
38
+
39
+ # Rule 2: No output files in expansion pack
40
+ echo "Checking Rule 2: No output files in expansion pack..."
41
+
42
+ if echo "$staged_files" | grep -qE "expansion-packs/mmos/(benchmarks|outputs|results)/"; then
43
+ echo -e "${RED}❌ VIOLATION: Output files in expansion pack${NC}"
44
+ echo ""
45
+ echo "Found:"
46
+ echo "$staged_files" | grep -E "expansion-packs/mmos/(benchmarks|outputs|results)/" | sed 's/^/ - /'
47
+ echo ""
48
+ echo -e "${YELLOW}Fix: Move to docs/mmos/qa/benchmarks/ or appropriate output location${NC}"
49
+ echo ""
50
+ violations=$((violations + 1))
51
+ fi
52
+
53
+ # Rule 3: Check for common naming violations
54
+ echo "Checking Rule 3: Mind-specific files should be in outputs/minds/..."
55
+
56
+ # Check for files with mind names in docs/mmos/ (excluding allowed folders)
57
+ for mind_name in $(ls outputs/minds/ 2>/dev/null | grep -v "^README" || true); do
58
+ if echo "$staged_files" | grep -qE "docs/mmos/.*${mind_name}"; then
59
+ # Exclude allowed locations (reports can mention minds)
60
+ if ! echo "$staged_files" | grep -qE "docs/mmos/(reports|architecture)/"; then
61
+ echo -e "${YELLOW}⚠️ WARNING: File containing mind name '$mind_name' in docs/mmos/${NC}"
62
+ echo "$staged_files" | grep -E "docs/mmos/.*${mind_name}" | sed 's/^/ - /'
63
+ echo ""
64
+ echo "Verify this is system-level documentation, not mind-specific"
65
+ echo ""
66
+ fi
67
+ fi
68
+ done
69
+
70
+ # Rule 4: Verify outputs/minds/{slug}/ structure
71
+ echo "Checking Rule 4: outputs/minds/{slug}/ structure..."
72
+
73
+ if echo "$staged_files" | grep -qE "outputs/minds/[^/]+/[^/]+\.(md|yaml|json)$"; then
74
+ echo -e "${YELLOW}⚠️ WARNING: Files in outputs/minds/{slug}/ root detected${NC}"
75
+ echo ""
76
+ echo "Found:"
77
+ echo "$staged_files" | grep -E "outputs/minds/[^/]+/[^/]+\.(md|yaml|json)$" | sed 's/^/ - /'
78
+ echo ""
79
+ echo "Pipeline outputs should be in subfolders:"
80
+ echo " - analysis/, synthesis/, implementation/, system_prompts/, kb/"
81
+ echo " - Process docs should be in docs/"
82
+ echo " - Logs should be in logs/"
83
+ echo ""
84
+ fi
85
+
86
+ # Final verdict
87
+ echo ""
88
+ if [ $violations -gt 0 ]; then
89
+ echo -e "${RED}❌ COMMIT REJECTED: $violations architectural violation(s) found${NC}"
90
+ echo ""
91
+ echo "Review:"
92
+ echo " - docs/mmos/ARCHITECTURE_RULES.md"
93
+ echo " - .aios-core/checklists/mmos-architecture-guard.md"
94
+ echo ""
95
+ exit 1
96
+ else
97
+ echo -e "${GREEN}✅ Architecture guard passed${NC}"
98
+ exit 0
99
+ fi
@@ -0,0 +1,156 @@
1
+ #!/bin/bash
2
+ #
3
+ # Pre-Commit Hook: Version Metadata Validation
4
+ #
5
+ # Ensures all tasks and agents have version metadata in frontmatter.
6
+ # This prevents silent version mismatches (e.g., v1.0 agent + v2.0 task).
7
+ #
8
+ # Usage:
9
+ # Run manually: .aios-core/hooks/pre-commit-version-check.sh
10
+ # Install: .aios-core/hooks/install-hooks.sh
11
+ #
12
+ # Story: STORY-3.10 (Version Alignment & Compatibility Checks)
13
+
14
+ set -e
15
+
16
+ echo "🔍 Validating task and agent version metadata..."
17
+
18
+ # Colors for output
19
+ RED='\033[0;31m'
20
+ GREEN='\033[0;32m'
21
+ YELLOW='\033[1;33m'
22
+ NC='\033[0m' # No Color
23
+
24
+ # Track validation status
25
+ ERRORS=0
26
+
27
+ # Function to check if file has required version fields
28
+ check_task_version() {
29
+ local file="$1"
30
+ local missing_fields=()
31
+
32
+ if ! grep -q "^task_name:" "$file"; then
33
+ missing_fields+=("task_name")
34
+ fi
35
+
36
+ if ! grep -q "^task_version:" "$file"; then
37
+ missing_fields+=("task_version")
38
+ fi
39
+
40
+ if ! grep -q "^required_agent_version:" "$file"; then
41
+ missing_fields+=("required_agent_version")
42
+ fi
43
+
44
+ if ! grep -q "^description:" "$file"; then
45
+ missing_fields+=("description")
46
+ fi
47
+
48
+ if ! grep -q "^last_updated:" "$file"; then
49
+ missing_fields+=("last_updated")
50
+ fi
51
+
52
+ if [ ${#missing_fields[@]} -gt 0 ]; then
53
+ echo -e "${RED}❌ ERROR: $file missing version metadata${NC}"
54
+ for field in "${missing_fields[@]}"; do
55
+ echo -e "${RED} - Missing field: '$field'${NC}"
56
+ done
57
+ echo ""
58
+ ERRORS=$((ERRORS + 1))
59
+ return 1
60
+ fi
61
+
62
+ return 0
63
+ }
64
+
65
+ check_agent_version() {
66
+ local file="$1"
67
+ local missing_fields=()
68
+
69
+ if ! grep -q "^agent_name:" "$file"; then
70
+ missing_fields+=("agent_name")
71
+ fi
72
+
73
+ if ! grep -q "^agent_version:" "$file"; then
74
+ missing_fields+=("agent_version")
75
+ fi
76
+
77
+ if ! grep -q "^compatible_task_versions:" "$file"; then
78
+ missing_fields+=("compatible_task_versions")
79
+ fi
80
+
81
+ if ! grep -q "^description:" "$file"; then
82
+ missing_fields+=("description")
83
+ fi
84
+
85
+ if ! grep -q "^last_updated:" "$file"; then
86
+ missing_fields+=("last_updated")
87
+ fi
88
+
89
+ if [ ${#missing_fields[@]} -gt 0 ]; then
90
+ echo -e "${RED}❌ ERROR: $file missing version metadata${NC}"
91
+ for field in "${missing_fields[@]}"; do
92
+ echo -e "${RED} - Missing field: '$field'${NC}"
93
+ done
94
+ echo ""
95
+ ERRORS=$((ERRORS + 1))
96
+ return 1
97
+ fi
98
+
99
+ return 0
100
+ }
101
+
102
+ # Check all tasks in expansion-packs/creator-os/tasks/
103
+ echo "Checking CreatorOS tasks..."
104
+ TASK_COUNT=0
105
+ for task in expansion-packs/creator-os/tasks/*.md; do
106
+ # Skip backup files
107
+ if [[ "$task" == *"-backup.md" ]]; then
108
+ echo -e "${YELLOW}⏭️ Skipping backup: $task${NC}"
109
+ continue
110
+ fi
111
+
112
+ if [ -f "$task" ]; then
113
+ if check_task_version "$task"; then
114
+ echo -e "${GREEN}✅ $task${NC}"
115
+ fi
116
+ TASK_COUNT=$((TASK_COUNT + 1))
117
+ fi
118
+ done
119
+
120
+ # Check all agents in expansion-packs/creator-os/agents/
121
+ echo ""
122
+ echo "Checking CreatorOS agents..."
123
+ AGENT_COUNT=0
124
+ for agent in expansion-packs/creator-os/agents/*.md; do
125
+ if [ -f "$agent" ]; then
126
+ if check_agent_version "$agent"; then
127
+ echo -e "${GREEN}✅ $agent${NC}"
128
+ fi
129
+ AGENT_COUNT=$((AGENT_COUNT + 1))
130
+ fi
131
+ done
132
+
133
+ echo ""
134
+ echo "────────────────────────────────────────────────────────────"
135
+ echo "Summary:"
136
+ echo " - Tasks checked: $TASK_COUNT"
137
+ echo " - Agents checked: $AGENT_COUNT"
138
+ echo " - Errors found: $ERRORS"
139
+ echo "────────────────────────────────────────────────────────────"
140
+
141
+ if [ $ERRORS -gt 0 ]; then
142
+ echo ""
143
+ echo -e "${RED}❌ Version validation FAILED${NC}"
144
+ echo ""
145
+ echo "How to fix:"
146
+ echo "1. Add YAML frontmatter to each flagged file"
147
+ echo "2. Include all required fields (task_name, task_version, etc.)"
148
+ echo "3. See Story 3.10 for examples"
149
+ echo ""
150
+ exit 1
151
+ fi
152
+
153
+ echo ""
154
+ echo -e "${GREEN}✅ Version validation PASSED${NC}"
155
+ echo ""
156
+ exit 0
@@ -0,0 +1,151 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Hook: Read Protection
4
+
5
+ REGRA: Arquivos protegidos DEVEM ser lidos completamente (sem limit/offset).
6
+
7
+ Este hook intercepta chamadas Read e bloqueia leitura parcial em arquivos
8
+ que requerem contexto completo para edição segura.
9
+
10
+ Exit Codes:
11
+ - 0: Permitido
12
+ - 2: Bloqueado (arquivo protegido com limit/offset)
13
+ """
14
+
15
+ import json
16
+ import sys
17
+ import os
18
+ import fnmatch
19
+ from pathlib import Path
20
+
21
+ # =============================================================================
22
+ # CONFIGURAÇÃO: Arquivos que DEVEM ser lidos completamente
23
+ # =============================================================================
24
+
25
+ PROTECTED_PATTERNS = [
26
+ # Configuração do Claude
27
+ ".claude/CLAUDE.md",
28
+ ".claude/rules/*.md",
29
+ ".claude/settings*.json",
30
+
31
+ # Definições de agentes
32
+ ".aios-core/development/agents/*.md",
33
+ ".aios-upstream/.aios-core/development/agents/*.md",
34
+
35
+ # Documentação crítica
36
+ "docs/mmos/ARCHITECTURE_RULES.md",
37
+ "supabase/docs/SCHEMA.md",
38
+
39
+ # Configuração do projeto
40
+ "package.json",
41
+ "tsconfig.json",
42
+ "tsconfig.*.json",
43
+
44
+ # Arquivos de código críticos
45
+ "app/components/ui/icons/icon-map.ts",
46
+
47
+ # Migrations (sempre ler completo antes de editar)
48
+ "supabase/migrations/*.sql",
49
+ ]
50
+
51
+ # =============================================================================
52
+ # LÓGICA DO HOOK
53
+ # =============================================================================
54
+
55
+ def get_project_root():
56
+ """Obtém o root do projeto via variável de ambiente ou cwd."""
57
+ return os.environ.get("CLAUDE_PROJECT_DIR", os.getcwd())
58
+
59
+ def normalize_path(file_path: str, project_root: str) -> str:
60
+ """Normaliza path para relativo ao projeto."""
61
+ if file_path.startswith(project_root):
62
+ return file_path[len(project_root):].lstrip("/")
63
+ if file_path.startswith("/"):
64
+ # Path absoluto fora do projeto
65
+ return file_path
66
+ return file_path
67
+
68
+ def matches_protected_pattern(relative_path: str) -> bool:
69
+ """Verifica se o path corresponde a algum padrão protegido."""
70
+ for pattern in PROTECTED_PATTERNS:
71
+ if fnmatch.fnmatch(relative_path, pattern):
72
+ return True
73
+ # Também verificar se é substring (para paths com wildcards)
74
+ if "*" not in pattern and pattern in relative_path:
75
+ return True
76
+ return False
77
+
78
+ def has_partial_read_params(tool_input: dict) -> tuple[bool, str]:
79
+ """Verifica se a chamada Read tem parâmetros de leitura parcial."""
80
+ limit = tool_input.get("limit")
81
+ offset = tool_input.get("offset")
82
+
83
+ reasons = []
84
+ if limit is not None:
85
+ reasons.append(f"limit={limit}")
86
+ if offset is not None and offset != 0:
87
+ reasons.append(f"offset={offset}")
88
+
89
+ return len(reasons) > 0, ", ".join(reasons)
90
+
91
+ def main():
92
+ # Ler input do stdin
93
+ try:
94
+ input_data = json.load(sys.stdin)
95
+ except json.JSONDecodeError:
96
+ # Se não conseguir parsear, permitir (fail-open)
97
+ sys.exit(0)
98
+
99
+ tool_name = input_data.get("tool_name", "")
100
+ tool_input = input_data.get("tool_input", {})
101
+
102
+ # Só processar Read
103
+ if tool_name != "Read":
104
+ sys.exit(0)
105
+
106
+ file_path = tool_input.get("file_path", "")
107
+ if not file_path:
108
+ sys.exit(0)
109
+
110
+ # Normalizar path
111
+ project_root = get_project_root()
112
+ relative_path = normalize_path(file_path, project_root)
113
+
114
+ # Verificar se é arquivo protegido
115
+ if not matches_protected_pattern(relative_path):
116
+ sys.exit(0)
117
+
118
+ # Verificar se tem parâmetros de leitura parcial
119
+ is_partial, partial_reason = has_partial_read_params(tool_input)
120
+
121
+ if not is_partial:
122
+ # Leitura completa, permitir
123
+ sys.exit(0)
124
+
125
+ # BLOQUEAR: Tentando ler arquivo protegido parcialmente
126
+ error_message = f"""
127
+ ╔══════════════════════════════════════════════════════════════════════════════╗
128
+ ║ 🛑 READ PROTECTION: Arquivo protegido deve ser lido completamente ║
129
+ ╠══════════════════════════════════════════════════════════════════════════════╣
130
+ ║ ║
131
+ ║ Arquivo: {relative_path[:58]:<58} ║
132
+ ║ Problema: {partial_reason:<57} ║
133
+ ║ ║
134
+ ║ REGRA: Este arquivo está na lista de arquivos protegidos. ║
135
+ ║ Arquivos protegidos DEVEM ser lidos completamente para evitar: ║
136
+ ║ - Edições com contexto incompleto ║
137
+ ║ - Duplicações acidentais ║
138
+ ║ - Breaking changes não intencionais ║
139
+ ║ ║
140
+ ║ SOLUÇÃO: Remova os parâmetros 'limit' e 'offset' da chamada Read. ║
141
+ ║ ║
142
+ ║ ✅ Read(file_path="{relative_path[:40]}")
143
+ ║ ❌ Read(file_path="...", limit=100) ║
144
+ ║ ║
145
+ ╚══════════════════════════════════════════════════════════════════════════════╝
146
+ """
147
+ print(error_message, file=sys.stderr)
148
+ sys.exit(2)
149
+
150
+ if __name__ == "__main__":
151
+ main()
@@ -0,0 +1,176 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Hook: Slug Validation
4
+
5
+ REGRA: Todos os slugs DEVEM usar snake_case (underscores, não hyphens).
6
+
7
+ Este hook intercepta comandos SQL que inserem/atualizam slugs e valida
8
+ o formato antes de permitir a operação.
9
+
10
+ Pattern válido: ^[a-z0-9]+(_[a-z0-9]+)*$
11
+ Exemplos válidos: jose_carlos_amorim, alan_nicolas, elon_musk
12
+ Exemplos inválidos: jose-carlos-amorim, JoseAmorim, ELON_MUSK
13
+
14
+ Exit Codes:
15
+ - 0: Permitido
16
+ - 2: Bloqueado (slug com formato inválido)
17
+ """
18
+
19
+ import json
20
+ import sys
21
+ import re
22
+
23
+ # =============================================================================
24
+ # CONFIGURAÇÃO
25
+ # =============================================================================
26
+
27
+ # Pattern válido para slugs
28
+ SLUG_PATTERN = re.compile(r"^[a-z0-9]+(_[a-z0-9]+)*$")
29
+
30
+ # Tabelas que têm coluna slug
31
+ TABLES_WITH_SLUG = [
32
+ "minds",
33
+ "contents",
34
+ "content_projects",
35
+ "tools",
36
+ "drivers",
37
+ "mapping_systems",
38
+ "frameworks",
39
+ ]
40
+
41
+ # =============================================================================
42
+ # LÓGICA DO HOOK
43
+ # =============================================================================
44
+
45
+ def extract_slug_values(command: str) -> list[tuple[str, str]]:
46
+ """
47
+ Extrai valores de slug de comandos SQL INSERT/UPDATE.
48
+
49
+ Returns:
50
+ Lista de tuplas (table, slug_value)
51
+ """
52
+ found = []
53
+ command_upper = command.upper()
54
+
55
+ for table in TABLES_WITH_SLUG:
56
+ table_upper = table.upper()
57
+
58
+ # Detectar INSERT INTO table (..., slug, ...) VALUES (..., 'value', ...)
59
+ insert_pattern = rf"INSERT\s+INTO\s+{table}\s*\([^)]*\bslug\b[^)]*\)\s*VALUES\s*\(([^)]+)\)"
60
+ insert_match = re.search(insert_pattern, command, re.IGNORECASE)
61
+ if insert_match:
62
+ values_str = insert_match.group(1)
63
+ # Extrair valores entre aspas
64
+ slug_values = re.findall(r"'([^']+)'", values_str)
65
+ for sv in slug_values:
66
+ # Verificar se parece um slug (não é UUID, não é número)
67
+ if not re.match(r"^[0-9a-f-]{36}$", sv) and not sv.isdigit():
68
+ found.append((table, sv))
69
+
70
+ # Detectar UPDATE table SET slug = 'value'
71
+ update_pattern = rf"UPDATE\s+{table}\s+.*SET\s+.*\bslug\s*=\s*'([^']+)'"
72
+ update_match = re.search(update_pattern, command, re.IGNORECASE)
73
+ if update_match:
74
+ found.append((table, update_match.group(1)))
75
+
76
+ return found
77
+
78
+ def validate_slug(slug: str) -> tuple[bool, str]:
79
+ """
80
+ Valida se o slug está no formato correto.
81
+
82
+ Returns:
83
+ (is_valid, error_message)
84
+ """
85
+ if SLUG_PATTERN.match(slug):
86
+ return True, ""
87
+
88
+ errors = []
89
+
90
+ if "-" in slug:
91
+ errors.append("contém hyphens (use underscores)")
92
+ if any(c.isupper() for c in slug):
93
+ errors.append("contém maiúsculas (use lowercase)")
94
+ if slug.startswith("_") or slug.endswith("_"):
95
+ errors.append("começa ou termina com underscore")
96
+ if "__" in slug:
97
+ errors.append("contém underscores duplos")
98
+ if not errors:
99
+ errors.append("formato inválido")
100
+
101
+ return False, ", ".join(errors)
102
+
103
+ def main():
104
+ # Ler input do stdin
105
+ try:
106
+ input_data = json.load(sys.stdin)
107
+ except json.JSONDecodeError:
108
+ sys.exit(0)
109
+
110
+ tool_name = input_data.get("tool_name", "")
111
+ tool_input = input_data.get("tool_input", {})
112
+
113
+ # Só processar Bash
114
+ if tool_name != "Bash":
115
+ sys.exit(0)
116
+
117
+ command = tool_input.get("command", "")
118
+ if not command:
119
+ sys.exit(0)
120
+
121
+ # Extrair slugs do comando
122
+ slug_values = extract_slug_values(command)
123
+
124
+ if not slug_values:
125
+ sys.exit(0)
126
+
127
+ # Validar cada slug
128
+ invalid_slugs = []
129
+ for table, slug in slug_values:
130
+ is_valid, error = validate_slug(slug)
131
+ if not is_valid:
132
+ invalid_slugs.append((table, slug, error))
133
+
134
+ if not invalid_slugs:
135
+ sys.exit(0)
136
+
137
+ # BLOQUEAR: Slug inválido detectado
138
+ slug_errors = "\n".join([
139
+ f"║ • {table}.slug = '{slug[:30]}' → {error[:30]:<30} ║"
140
+ for table, slug, error in invalid_slugs[:5]
141
+ ])
142
+
143
+ # Sugerir correção
144
+ suggestions = "\n".join([
145
+ f"║ • '{slug}' → '{slug.lower().replace('-', '_')}'{'':>30} ║"
146
+ for _, slug, _ in invalid_slugs[:5]
147
+ ])
148
+
149
+ error_message = f"""
150
+ ╔══════════════════════════════════════════════════════════════════════════════╗
151
+ ║ 🛑 SLUG VALIDATION: Formato de slug inválido ║
152
+ ╠══════════════════════════════════════════════════════════════════════════════╣
153
+ ║ ║
154
+ ║ Slugs inválidos detectados: ║
155
+ {slug_errors}
156
+ ║ ║
157
+ ║ REGRA: Todos os slugs DEVEM usar snake_case ║
158
+ ║ ║
159
+ ║ Pattern válido: ^[a-z0-9]+(_[a-z0-9]+)*$ ║
160
+ ║ ✅ jose_carlos_amorim ║
161
+ ║ ✅ alan_nicolas ║
162
+ ║ ❌ jose-carlos-amorim (hyphen) ║
163
+ ║ ❌ JoseAmorim (camelCase) ║
164
+ ║ ║
165
+ ║ Sugestões de correção: ║
166
+ {suggestions}
167
+ ║ ║
168
+ ║ POR QUÊ: Slugs inconsistentes causam falhas silenciosas no frontend. ║
169
+ ║ ║
170
+ ╚══════════════════════════════════════════════════════════════════════════════╝
171
+ """
172
+ print(error_message, file=sys.stderr)
173
+ sys.exit(2)
174
+
175
+ if __name__ == "__main__":
176
+ main()