okam 0.2.0__py3-none-any.whl

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.
okam/__init__.py ADDED
@@ -0,0 +1 @@
1
+ __version__ = "0.2.0"
okam/cli.py ADDED
@@ -0,0 +1,392 @@
1
+ import os
2
+ import sys
3
+ import argparse
4
+ from datetime import datetime
5
+
6
+ from okam import __version__
7
+ from okam.manager import (
8
+ get_wiki_files,
9
+ validate_file,
10
+ load_markdown_file,
11
+ save_markdown_file,
12
+ )
13
+ from okam.skill_creator import (
14
+ create_new_skill,
15
+ print_colored,
16
+ ask_question,
17
+ COLOR_GREEN,
18
+ COLOR_YELLOW,
19
+ COLOR_RED,
20
+ COLOR_BLUE,
21
+ COLOR_BOLD,
22
+ )
23
+ from okam.hooks import (
24
+ install_hooks,
25
+ uninstall_hooks,
26
+ get_hooks_status,
27
+ HOOK_NAMES,
28
+ )
29
+
30
+
31
+ def get_default_wiki_dir():
32
+ """Retorna o diretório default do wiki."""
33
+ # Se ./knowledge/wiki existir no diretório atual, usa ele
34
+ if os.path.exists(os.path.join("knowledge", "wiki")):
35
+ return os.path.abspath(os.path.join("knowledge", "wiki"))
36
+ # Caso contrário, tenta ./wiki
37
+ if os.path.exists("wiki"):
38
+ return os.path.abspath("wiki")
39
+ # Fallback para o valor default
40
+ return os.path.abspath(os.path.join("knowledge", "wiki"))
41
+
42
+
43
+ def cmd_init(wiki_dir):
44
+ """Gera seed pages com frontmatter OKF válido."""
45
+ print_colored(f"Inicializando base de conhecimento OKF em: {wiki_dir}...", COLOR_BLUE)
46
+ os.makedirs(wiki_dir, exist_ok=True)
47
+ today = datetime.now().strftime("%Y-%m-%d")
48
+
49
+ seeds = {
50
+ "index.md": {
51
+ "frontmatter": {
52
+ "title": "Índice do Wiki",
53
+ "description": "Catálogo central de conhecimento persistente.",
54
+ "type": "index",
55
+ "resource": "workspace",
56
+ "timestamp": today,
57
+ "tags": ["hub/wiki", "tipo/indice"],
58
+ "parent": "root"
59
+ },
60
+ "body": """
61
+ # Índice do Wiki
62
+
63
+ Bem-vindo ao catálogo central de conhecimento persistente.
64
+
65
+ ## 00. Visão Geral
66
+
67
+ - [[getting-started]]: Guia de primeiros passos.
68
+ - [[architecture]]: Visão geral da arquitetura.
69
+
70
+ ## 01. Conceitos
71
+
72
+ *(Adicione links para páginas de conceitos aqui)*
73
+
74
+ ## 02. Entidades
75
+
76
+ *(Adicione links para páginas de entidades aqui)*
77
+ """
78
+ },
79
+ "getting-started.md": {
80
+ "frontmatter": {
81
+ "title": "Primeiros Passos",
82
+ "description": "Guia rápido para começar a usar o Wiki de Conhecimento.",
83
+ "type": "runbook",
84
+ "resource": "workspace",
85
+ "timestamp": today,
86
+ "tags": ["tipo/guia", "onboarding"],
87
+ "parent": "[[index]]"
88
+ },
89
+ "body": """
90
+ # Primeiros Passos
91
+
92
+ Este guia ajuda você a configurar e começar a usar o Wiki de Conhecimento.
93
+
94
+ ## 1. Estrutura
95
+
96
+ - `wiki/`: Páginas de síntese (você está aqui).
97
+ - `raw-sources/`: Fontes brutas e imutáveis.
98
+ - `scripts/`: Ferramentas de validação.
99
+
100
+ ## 2. Criar uma Página
101
+
102
+ Cada página do wiki deve ter frontmatter YAML no formato OKF:
103
+
104
+ ```yaml
105
+ ---
106
+ title: "Título da Página"
107
+ description: "Descrição breve do conteúdo."
108
+ type: concept
109
+ resource: workspace
110
+ timestamp: 2026-01-01
111
+ tags:
112
+ - conceito/exemplo
113
+ parent: "[[index]]"
114
+ ---
115
+ ```
116
+
117
+ ## 3. Validar
118
+
119
+ ```bash
120
+ okam validate
121
+ ```
122
+ """
123
+ },
124
+ "architecture.md": {
125
+ "frontmatter": {
126
+ "title": "Arquitetura do Conhecimento",
127
+ "description": "Visão geral da arquitetura de 3 camadas: Raw Sources, Wiki e Schema.",
128
+ "type": "architecture",
129
+ "resource": "workspace",
130
+ "timestamp": today,
131
+ "tags": ["conceito/arquitetura", "conceito/okf"],
132
+ "parent": "[[index]]"
133
+ },
134
+ "body": """
135
+ # Arquitetura do Conhecimento
136
+
137
+ O sistema de conhecimento opera em 3 camadas:
138
+
139
+ ## 1. Raw Sources (Fontes Brutas)
140
+
141
+ Documentos imutáveis: PDFs, transcrições, logs, clippings.
142
+ Servem como fonte de verdade original.
143
+
144
+ ## 2. Wiki (Síntese)
145
+
146
+ Páginas de síntese geradas e mantidas por IA.
147
+ Seguem o formato OKF para garantir interoperabilidade.
148
+
149
+ ## 3. Schema (Governança)
150
+
151
+ Regras definidas em `AGENTS.md` e `.agents/rules/`.
152
+ Controlam como o conhecimento é criado, validado e mantido.
153
+ """
154
+ }
155
+ }
156
+
157
+ created = 0
158
+ for filename, content in seeds.items():
159
+ filepath = os.path.join(wiki_dir, filename)
160
+ if os.path.exists(filepath):
161
+ print_colored(f" [EXISTE] {filename} — pulando", COLOR_YELLOW)
162
+ continue
163
+
164
+ frontmatter = content["frontmatter"]
165
+ body = content["body"]
166
+ save_markdown_file(filepath, frontmatter, body)
167
+ print_colored(f" [CRIADO] {filename}", COLOR_GREEN)
168
+ created += 1
169
+
170
+ print_colored(f"\n✅ {created} seed page(s) criada(s) em {wiki_dir}", COLOR_GREEN + COLOR_BOLD)
171
+
172
+ # Oferecer instalação de hooks
173
+ try:
174
+ resp = ask_question(
175
+ "Deseja instalar os Git hooks de governança?",
176
+ default="sim"
177
+ ).lower()
178
+ if resp in ['s', 'sim', 'y', 'yes']:
179
+ print_colored("\nInstalando hooks de governança...", COLOR_BLUE)
180
+ installed, skipped, errors = install_hooks()
181
+ if errors == 0:
182
+ print_colored(
183
+ f"✅ {installed} hook(s) instalado(s).",
184
+ COLOR_GREEN + COLOR_BOLD
185
+ )
186
+ else:
187
+ print_colored(
188
+ f"⚠ {installed} instalado(s), {errors} erro(s).",
189
+ COLOR_YELLOW
190
+ )
191
+ except (KeyboardInterrupt, EOFError):
192
+ pass # Non-interactive ou cancelado — pular silenciosamente
193
+
194
+
195
+ def cmd_validate(wiki_dir):
196
+ """Valida todos os arquivos .md no wiki_dir contra o padrão OKF."""
197
+ files = get_wiki_files(wiki_dir)
198
+
199
+ if not files:
200
+ print_colored(f"Nenhum arquivo .md encontrado em: {wiki_dir}", COLOR_YELLOW)
201
+ return True
202
+
203
+ all_ok = True
204
+ print_colored(f"Validando conformidade OKF em: {wiki_dir}...\n", COLOR_BLUE)
205
+
206
+ for filepath in files:
207
+ filename = os.path.basename(filepath)
208
+ is_ok, errors = validate_file(filepath)
209
+ if is_ok:
210
+ print_colored(f" [OK] {filename}", COLOR_GREEN)
211
+ else:
212
+ all_ok = False
213
+ print_colored(f" [FALHA] {filename}", COLOR_RED)
214
+ for err in errors:
215
+ print(f" - {err}")
216
+
217
+ if all_ok:
218
+ print_colored("\n✅ Todos os arquivos estão em conformidade com o OKF!", COLOR_GREEN + COLOR_BOLD)
219
+ return True
220
+ else:
221
+ print_colored("\n❌ Validação falhou com erros.", COLOR_RED + COLOR_BOLD)
222
+ return False
223
+
224
+
225
+ def cmd_index(wiki_dir):
226
+ """Gera índice markdown da base OKF e exibe no stdout."""
227
+ files = get_wiki_files(wiki_dir)
228
+
229
+ print("# Índice de Conhecimento OKF\n")
230
+ print("| Arquivo | Título | Tipo | Recurso | Data | Descrição |")
231
+ print("| --- | --- | --- | --- | --- | --- |")
232
+
233
+ for filepath in files:
234
+ filename = os.path.basename(filepath)
235
+ try:
236
+ frontmatter, _ = load_markdown_file(filepath)
237
+ if not frontmatter:
238
+ continue
239
+ except Exception:
240
+ continue
241
+
242
+ title = frontmatter.get('title', filename)
243
+ ftype = frontmatter.get('type', '-')
244
+ resource = frontmatter.get('resource', '-')
245
+ timestamp = frontmatter.get('timestamp', '-')
246
+ description = frontmatter.get('description', '-')
247
+
248
+ print(f"| {filename} | {title} | {ftype} | {resource} | {timestamp} | {description} |")
249
+
250
+ def cmd_hooks(args):
251
+ """Gerencia Git hooks de governança do Okam."""
252
+ action = getattr(args, "hooks_action", None)
253
+
254
+ if action is None:
255
+ print_colored("Uso: okam hooks {install|uninstall|status}", COLOR_YELLOW)
256
+ print_colored(" install — Instala hooks de governança no repositório", COLOR_BLUE)
257
+ print_colored(" uninstall — Remove hooks do Okam e restaura backups", COLOR_BLUE)
258
+ print_colored(" status — Mostra o status de cada hook", COLOR_BLUE)
259
+ sys.exit(0)
260
+
261
+ if action == "install":
262
+ print_colored("⬡ Instalando hooks de governança...\n", COLOR_BLUE + COLOR_BOLD)
263
+ skip = []
264
+ if getattr(args, "skip_commit_msg", False):
265
+ skip.append("commit-msg")
266
+ installed, skipped, errors = install_hooks(skip_hooks=skip)
267
+ print()
268
+ if errors == 0:
269
+ print_colored(
270
+ f"✅ {installed} hook(s) instalado(s), {skipped} pulado(s).",
271
+ COLOR_GREEN + COLOR_BOLD,
272
+ )
273
+ else:
274
+ print_colored(
275
+ f"⚠ {installed} instalado(s), {errors} erro(s).",
276
+ COLOR_RED + COLOR_BOLD,
277
+ )
278
+ sys.exit(1)
279
+
280
+ elif action == "uninstall":
281
+ print_colored("⬡ Removendo hooks de governança...\n", COLOR_BLUE + COLOR_BOLD)
282
+ removed, restored, not_found = uninstall_hooks()
283
+ print()
284
+ total = removed + restored
285
+ if total > 0:
286
+ print_colored(
287
+ f"✅ {total} hook(s) removido(s) ({restored} backup(s) restaurado(s)).",
288
+ COLOR_GREEN + COLOR_BOLD,
289
+ )
290
+ else:
291
+ print_colored("Nenhum hook do Okam encontrado para remover.", COLOR_YELLOW)
292
+
293
+ elif action == "status":
294
+ print_colored("⬡ Status dos hooks de governança:\n", COLOR_BLUE + COLOR_BOLD)
295
+ status = get_hooks_status()
296
+ for hook_name, state in status.items():
297
+ if "Okam" in state:
298
+ color = COLOR_GREEN
299
+ icon = "✓"
300
+ elif "externo" in state:
301
+ color = COLOR_YELLOW
302
+ icon = "~"
303
+ else:
304
+ color = COLOR_RED
305
+ icon = "✗"
306
+ print_colored(f" {icon} {hook_name}: {state}", color)
307
+ print()
308
+
309
+
310
+ def main():
311
+ # Configura encoding UTF-8 para Windows se executado diretamente
312
+ if sys.platform.startswith('win'):
313
+ import io
314
+ sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
315
+ sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding='utf-8')
316
+
317
+ parser = argparse.ArgumentParser(
318
+ description="⬡ Okam CLI — Framework de governança de IA e memória persistente",
319
+ formatter_class=argparse.RawDescriptionHelpFormatter
320
+ )
321
+ parser.add_argument(
322
+ '--version', '-v',
323
+ action='version',
324
+ version=f"okam version {__version__}"
325
+ )
326
+
327
+ subparsers = parser.add_subparsers(dest="command", help="Comando a ser executado")
328
+
329
+ # Comando init
330
+ p_init = subparsers.add_parser("init", help="Inicializa wiki com seed pages OKF")
331
+ p_init.add_argument("--wiki-dir", help="Diretório destino do wiki (default: autodetect ./knowledge/wiki)")
332
+
333
+ # Comando validate
334
+ p_val = subparsers.add_parser("validate", help="Valida conformidade OKF de arquivos Markdown")
335
+ p_val.add_argument("--wiki-dir", help="Diretório do wiki a validar (default: autodetect ./knowledge/wiki)")
336
+
337
+ # Comando index
338
+ p_idx = subparsers.add_parser("index", help="Gera tabela Markdown do índice de conhecimento")
339
+ p_idx.add_argument("--wiki-dir", help="Diretório do wiki (default: autodetect ./knowledge/wiki)")
340
+
341
+ # Adiciona compatibilidade para dump-index diretamente
342
+ p_dump = subparsers.add_parser("dump-index", help="Alias para o comando 'index'")
343
+ p_dump.add_argument("--wiki-dir", help="Diretório do wiki (default: autodetect ./knowledge/wiki)")
344
+
345
+ # Comando new-skill
346
+ subparsers.add_parser("new-skill", help="Cria interativamente uma nova skill na pasta .agents/skills")
347
+
348
+ # Comando hooks
349
+ p_hooks = subparsers.add_parser("hooks", help="Gerencia Git hooks de governança")
350
+ p_hooks_sub = p_hooks.add_subparsers(dest="hooks_action", help="Ação dos hooks")
351
+
352
+ p_hooks_install = p_hooks_sub.add_parser("install", help="Instala hooks de governança no repositório")
353
+ p_hooks_install.add_argument(
354
+ "--skip-commit-msg",
355
+ action="store_true",
356
+ help="Não instalar o hook de Conventional Commits"
357
+ )
358
+
359
+ p_hooks_sub.add_parser("uninstall", help="Remove hooks do Okam e restaura backups")
360
+ p_hooks_sub.add_parser("status", help="Mostra o status de cada hook de governança")
361
+
362
+ args = parser.parse_args()
363
+
364
+ if not args.command:
365
+ parser.print_help()
366
+ sys.exit(0)
367
+
368
+ # Roteamento dos comandos
369
+ wiki_dir = None
370
+ if hasattr(args, "wiki_dir") and args.wiki_dir:
371
+ wiki_dir = os.path.abspath(args.wiki_dir)
372
+ else:
373
+ wiki_dir = get_default_wiki_dir()
374
+
375
+ if args.command == "init":
376
+ cmd_init(wiki_dir)
377
+ elif args.command == "validate":
378
+ success = cmd_validate(wiki_dir)
379
+ sys.exit(0 if success else 1)
380
+ elif args.command in ["index", "dump-index"]:
381
+ if not os.path.exists(wiki_dir):
382
+ print_colored(f"Erro: Diretório do wiki não encontrado em {wiki_dir}", COLOR_RED)
383
+ sys.exit(1)
384
+ cmd_index(wiki_dir)
385
+ elif args.command == "new-skill":
386
+ create_new_skill()
387
+ elif args.command == "hooks":
388
+ cmd_hooks(args)
389
+
390
+
391
+ if __name__ == "__main__":
392
+ main()
okam/hooks/commit-msg ADDED
@@ -0,0 +1,58 @@
1
+ #!/bin/sh
2
+ # ⬡ Okam — Commit-msg Hook
3
+ # Enforces Conventional Commits format on commit messages.
4
+ # Part of the Okam governance framework. Install via: okam hooks install
5
+
6
+ # ── Colors ──────────────────────────────────────────────────────────────────
7
+ RED='\033[0;31m'
8
+ GREEN='\033[0;32m'
9
+ YELLOW='\033[0;33m'
10
+ BOLD='\033[1m'
11
+ RESET='\033[0m'
12
+
13
+ COMMIT_MSG_FILE="$1"
14
+
15
+ if [ ! -f "$COMMIT_MSG_FILE" ]; then
16
+ printf "${RED}⬡ Okam commit-msg: Arquivo de mensagem não encontrado.${RESET}\n"
17
+ exit 1
18
+ fi
19
+
20
+ # Read the first line (subject) of the commit message
21
+ FIRST_LINE=$(head -n1 "$COMMIT_MSG_FILE")
22
+
23
+ # Skip merge commits and fixup/squash commits
24
+ case "$FIRST_LINE" in
25
+ Merge*|Revert*|fixup!*|squash!*|amend!*)
26
+ exit 0
27
+ ;;
28
+ esac
29
+
30
+ # Conventional Commits regex:
31
+ # <type>[optional scope][optional !]: <description>
32
+ # Types: feat, fix, docs, style, refactor, perf, test, build, ci, chore, revert
33
+ PATTERN='^(feat|fix|docs|style|refactor|perf|test|build|ci|chore|revert)(\([a-zA-Z0-9._-]+\))?(!)?: .{1,}'
34
+
35
+ if ! echo "$FIRST_LINE" | grep -qE "$PATTERN"; then
36
+ printf "\n${RED}${BOLD}⬡ Okam commit-msg: Mensagem fora do padrão Conventional Commits.${RESET}\n"
37
+ printf "${RED} Recebido: ${FIRST_LINE}${RESET}\n"
38
+ printf "\n${YELLOW} Formato esperado: <tipo>[escopo opcional]: <descrição>${RESET}\n"
39
+ printf "${YELLOW} Tipos válidos: feat, fix, docs, style, refactor, perf, test, build, ci, chore, revert${RESET}\n"
40
+ printf "\n${YELLOW} Exemplos:${RESET}\n"
41
+ printf "${GREEN} feat: adiciona autenticação OAuth${RESET}\n"
42
+ printf "${GREEN} fix(api): corrige timeout no endpoint de login${RESET}\n"
43
+ printf "${GREEN} docs: atualiza README com instruções de deploy${RESET}\n"
44
+ printf "${GREEN} feat!: breaking change na API de pagamentos${RESET}\n"
45
+ printf "\n${YELLOW} Para bypass emergencial: git commit --no-verify${RESET}\n"
46
+ exit 1
47
+ fi
48
+
49
+ # Validate subject line length (soft warning at 72, hard fail at 100)
50
+ SUBJECT_LEN=$(printf '%s' "$FIRST_LINE" | wc -c)
51
+ if [ "$SUBJECT_LEN" -gt 100 ]; then
52
+ printf "${RED}${BOLD}⬡ Okam commit-msg: Linha de assunto muito longa (${SUBJECT_LEN} chars, max 100).${RESET}\n"
53
+ exit 1
54
+ elif [ "$SUBJECT_LEN" -gt 72 ]; then
55
+ printf "${YELLOW}⬡ Okam commit-msg: Linha de assunto longa (${SUBJECT_LEN} chars, recomendado ≤72).${RESET}\n"
56
+ fi
57
+
58
+ exit 0
okam/hooks/pre-commit ADDED
@@ -0,0 +1,114 @@
1
+ #!/bin/sh
2
+ # ⬡ Okam — Pre-commit Hook
3
+ # Validates OKF compliance for staged wiki files and detects leaked secrets.
4
+ # Part of the Okam governance framework. Install via: okam hooks install
5
+
6
+ set -e
7
+
8
+ # ── Colors ──────────────────────────────────────────────────────────────────
9
+ RED='\033[0;31m'
10
+ GREEN='\033[0;32m'
11
+ YELLOW='\033[0;33m'
12
+ BLUE='\033[0;34m'
13
+ BOLD='\033[1m'
14
+ RESET='\033[0m'
15
+
16
+ ERRORS=0
17
+
18
+ # ── 1. OKF Validation ──────────────────────────────────────────────────────
19
+ # Find staged .md files in wiki directories
20
+ WIKI_FILES=$(git diff --cached --name-only --diff-filter=ACM | grep -E '(knowledge/wiki|wiki)/.*\.md$' || true)
21
+
22
+ if [ -n "$WIKI_FILES" ]; then
23
+ printf "${BLUE}${BOLD}⬡ Okam pre-commit: Validando conformidade OKF...${RESET}\n"
24
+
25
+ # Check if okam CLI is available
26
+ if command -v okam >/dev/null 2>&1; then
27
+ for file in $WIKI_FILES; do
28
+ # Validate the staged version by using the working copy
29
+ # (okam validate works on files in the filesystem)
30
+ WIKI_DIR=$(dirname "$file")
31
+ RESULT=$(okam validate --wiki-dir "$WIKI_DIR" 2>&1) || true
32
+
33
+ BASENAME=$(basename "$file")
34
+ if echo "$RESULT" | grep -q "\[FALHA\] $BASENAME"; then
35
+ printf "${RED} ✗ ${BASENAME}${RESET}\n"
36
+ echo "$RESULT" | grep -A5 "\[FALHA\] $BASENAME" | grep "^ -" || true
37
+ ERRORS=$((ERRORS + 1))
38
+ else
39
+ printf "${GREEN} ✓ ${BASENAME}${RESET}\n"
40
+ fi
41
+ done
42
+ else
43
+ printf "${YELLOW} ⚠ okam CLI não encontrado. Pulando validação OKF.${RESET}\n"
44
+ printf "${YELLOW} Instale com: pip install -e . (na raiz do projeto Okam)${RESET}\n"
45
+ fi
46
+ fi
47
+
48
+ # ── 2. Secret Detection ────────────────────────────────────────────────────
49
+ STAGED_FILES=$(git diff --cached --name-only --diff-filter=ACM || true)
50
+
51
+ if [ -n "$STAGED_FILES" ]; then
52
+ # Check for .env files being committed
53
+ ENV_FILES=$(echo "$STAGED_FILES" | grep -E '\.env($|\.)' | grep -v '\.env\.example$' || true)
54
+ if [ -n "$ENV_FILES" ]; then
55
+ printf "\n${RED}${BOLD}⬡ Okam pre-commit: Arquivos .env detectados!${RESET}\n"
56
+ for f in $ENV_FILES; do
57
+ printf "${RED} ✗ ${f}${RESET}\n"
58
+ done
59
+ printf "${YELLOW} → Adicione ao .gitignore ou use .env.example como template.${RESET}\n"
60
+ ERRORS=$((ERRORS + 1))
61
+ fi
62
+
63
+ # Scan staged content for secret patterns
64
+ SECRET_FOUND=0
65
+ for file in $STAGED_FILES; do
66
+ # Skip binary files and known safe extensions
67
+ case "$file" in
68
+ *.png|*.jpg|*.jpeg|*.gif|*.ico|*.woff|*.woff2|*.ttf|*.eot|*.svg|*.mp4|*.zip|*.tar|*.gz)
69
+ continue
70
+ ;;
71
+ esac
72
+
73
+ # Get staged content (not working copy)
74
+ CONTENT=$(git show ":$file" 2>/dev/null) || continue
75
+
76
+ # AWS Access Key IDs
77
+ if echo "$CONTENT" | grep -qE 'AKIA[0-9A-Z]{16}'; then
78
+ printf "${RED} ✗ Possível AWS Key em: ${file}${RESET}\n"
79
+ SECRET_FOUND=1
80
+ fi
81
+
82
+ # OpenAI / Anthropic / Generic API keys
83
+ if echo "$CONTENT" | grep -qE '(sk-[a-zA-Z0-9]{20,}|ghp_[a-zA-Z0-9]{36}|glpat-[a-zA-Z0-9\-]{20,}|xoxb-[0-9]{10,})'; then
84
+ printf "${RED} ✗ Possível API key/token em: ${file}${RESET}\n"
85
+ SECRET_FOUND=1
86
+ fi
87
+
88
+ # Generic secret assignments (password, secret, token with long values)
89
+ if echo "$CONTENT" | grep -qiE '(password|secret|token|api_key|apikey|private_key)\s*[=:]\s*["\x27][^\s"'\'']{12,}'; then
90
+ printf "${RED} ✗ Possível segredo hardcoded em: ${file}${RESET}\n"
91
+ SECRET_FOUND=1
92
+ fi
93
+ done
94
+
95
+ if [ "$SECRET_FOUND" -eq 1 ]; then
96
+ printf "\n${RED}${BOLD}⬡ Okam pre-commit: Segredos detectados!${RESET}\n"
97
+ printf "${YELLOW} → Remova os segredos e use variáveis de ambiente.${RESET}\n"
98
+ printf "${YELLOW} → Para scan mais robusto, considere: https://github.com/gitleaks/gitleaks${RESET}\n"
99
+ ERRORS=$((ERRORS + 1))
100
+ fi
101
+ fi
102
+
103
+ # ── Result ──────────────────────────────────────────────────────────────────
104
+ if [ "$ERRORS" -gt 0 ]; then
105
+ printf "\n${RED}${BOLD}⬡ Okam: Commit bloqueado — corrija os erros acima.${RESET}\n"
106
+ printf "${YELLOW} Para bypass emergencial: git commit --no-verify${RESET}\n"
107
+ exit 1
108
+ fi
109
+
110
+ if [ -n "$WIKI_FILES" ]; then
111
+ printf "${GREEN}${BOLD}⬡ Okam: Todas as verificações passaram.${RESET}\n"
112
+ fi
113
+
114
+ exit 0
okam/hooks/pre-push ADDED
@@ -0,0 +1,36 @@
1
+ #!/bin/sh
2
+ # ⬡ Okam — Pre-push Hook
3
+ # Runs full OKF validation before pushing to remote.
4
+ # Part of the Okam governance framework. Install via: okam hooks install
5
+
6
+ set -e
7
+
8
+ # ── Colors ──────────────────────────────────────────────────────────────────
9
+ RED='\033[0;31m'
10
+ GREEN='\033[0;32m'
11
+ YELLOW='\033[0;33m'
12
+ BLUE='\033[0;34m'
13
+ BOLD='\033[1m'
14
+ RESET='\033[0m'
15
+
16
+ printf "${BLUE}${BOLD}⬡ Okam pre-push: Executando validação OKF completa...${RESET}\n"
17
+
18
+ # Check if okam CLI is available
19
+ if ! command -v okam >/dev/null 2>&1; then
20
+ printf "${YELLOW} ⚠ okam CLI não encontrado. Pulando validação OKF.${RESET}\n"
21
+ printf "${YELLOW} Instale com: pip install -e . (na raiz do projeto Okam)${RESET}\n"
22
+ exit 0
23
+ fi
24
+
25
+ # Run full validation
26
+ if okam validate >/dev/null 2>&1; then
27
+ printf "${GREEN}${BOLD}⬡ Okam pre-push: Validação OKF completa — tudo OK.${RESET}\n"
28
+ exit 0
29
+ else
30
+ printf "\n${RED}${BOLD}⬡ Okam pre-push: Validação OKF falhou!${RESET}\n"
31
+ printf "${YELLOW} Detalhes:${RESET}\n"
32
+ okam validate 2>&1 | grep -E '\[(FALHA|OK)\]' || okam validate 2>&1
33
+ printf "\n${YELLOW} Corrija os erros antes de enviar ao remote.${RESET}\n"
34
+ printf "${YELLOW} Para bypass emergencial: git push --no-verify${RESET}\n"
35
+ exit 1
36
+ fi
okam/hooks.py ADDED
@@ -0,0 +1,213 @@
1
+ """
2
+ Okam Git Hooks Manager — Instala, desinstala e verifica hooks de governança.
3
+
4
+ Hooks portáveis (POSIX shell) que rodam validação OKF, detecção de segredos
5
+ e Conventional Commits antes de commits e pushes.
6
+ """
7
+
8
+ import os
9
+ import shutil
10
+ import stat
11
+ import sys
12
+
13
+ from okam.skill_creator import (
14
+ print_colored,
15
+ find_workspace_root,
16
+ COLOR_GREEN,
17
+ COLOR_YELLOW,
18
+ COLOR_RED,
19
+ COLOR_BLUE,
20
+ COLOR_BOLD,
21
+ )
22
+
23
+ # Hooks que o Okam gerencia
24
+ HOOK_NAMES = ["pre-commit", "commit-msg", "pre-push"]
25
+
26
+ # Marcador para identificar hooks instalados pelo Okam
27
+ OKAM_MARKER = "# ⬡ Okam"
28
+
29
+
30
+ def _get_hooks_source_dir():
31
+ """Retorna o diretório onde os hook scripts do Okam estão armazenados."""
32
+ # Tenta encontrar a pasta hooks/ relativa ao pacote instalado
33
+ # Primeiro: relativo ao workspace root (dev mode / pip install -e .)
34
+ workspace = find_workspace_root()
35
+ hooks_dir = os.path.join(workspace, "hooks")
36
+ if os.path.isdir(hooks_dir):
37
+ return hooks_dir
38
+
39
+ # Fallback: relativo ao pacote Python instalado
40
+ package_dir = os.path.dirname(os.path.abspath(__file__))
41
+ hooks_dir = os.path.join(package_dir, "hooks")
42
+ if os.path.isdir(hooks_dir):
43
+ return hooks_dir
44
+
45
+ return None
46
+
47
+
48
+ def _get_git_hooks_dir(repo_root=None):
49
+ """Retorna o caminho para .git/hooks/ do repositório."""
50
+ if repo_root is None:
51
+ repo_root = find_workspace_root()
52
+
53
+ git_dir = os.path.join(repo_root, ".git")
54
+
55
+ # Suporta git worktrees onde .git é um arquivo apontando para o gitdir
56
+ if os.path.isfile(git_dir):
57
+ with open(git_dir, "r", encoding="utf-8") as f:
58
+ content = f.read().strip()
59
+ if content.startswith("gitdir:"):
60
+ git_dir = content[7:].strip()
61
+ if not os.path.isabs(git_dir):
62
+ git_dir = os.path.normpath(os.path.join(repo_root, git_dir))
63
+
64
+ hooks_dir = os.path.join(git_dir, "hooks")
65
+ return hooks_dir
66
+
67
+
68
+ def _is_okam_hook(filepath):
69
+ """Verifica se um hook foi instalado pelo Okam (contém o marcador)."""
70
+ if not os.path.exists(filepath):
71
+ return False
72
+ try:
73
+ with open(filepath, "r", encoding="utf-8") as f:
74
+ content = f.read(512) # Ler só o início
75
+ return OKAM_MARKER in content
76
+ except (OSError, UnicodeDecodeError):
77
+ return False
78
+
79
+
80
+ def install_hooks(repo_root=None, skip_hooks=None):
81
+ """
82
+ Instala hooks de governança do Okam no repositório.
83
+
84
+ Args:
85
+ repo_root: Raiz do repositório Git. Se None, autodetecta.
86
+ skip_hooks: Lista de nomes de hooks para pular (ex: ['commit-msg']).
87
+
88
+ Returns:
89
+ Tuple (installed, skipped, errors) com contagens.
90
+ """
91
+ if skip_hooks is None:
92
+ skip_hooks = []
93
+
94
+ source_dir = _get_hooks_source_dir()
95
+ if source_dir is None:
96
+ print_colored(
97
+ "Erro: Pasta 'hooks/' não encontrada. Verifique a instalação do Okam.",
98
+ COLOR_RED,
99
+ )
100
+ return 0, 0, 1
101
+
102
+ git_hooks_dir = _get_git_hooks_dir(repo_root)
103
+ os.makedirs(git_hooks_dir, exist_ok=True)
104
+
105
+ installed = 0
106
+ skipped = 0
107
+ errors = 0
108
+
109
+ for hook_name in HOOK_NAMES:
110
+ if hook_name in skip_hooks:
111
+ print_colored(f" [PULADO] {hook_name} (--skip-{hook_name})", COLOR_YELLOW)
112
+ skipped += 1
113
+ continue
114
+
115
+ source_path = os.path.join(source_dir, hook_name)
116
+ target_path = os.path.join(git_hooks_dir, hook_name)
117
+
118
+ if not os.path.exists(source_path):
119
+ print_colored(
120
+ f" [ERRO] Script fonte não encontrado: {source_path}", COLOR_RED
121
+ )
122
+ errors += 1
123
+ continue
124
+
125
+ # Se já existe um hook que NÃO é do Okam, fazer backup
126
+ if os.path.exists(target_path) and not _is_okam_hook(target_path):
127
+ backup_path = target_path + ".bak"
128
+ shutil.copy2(target_path, backup_path)
129
+ print_colored(
130
+ f" [BACKUP] {hook_name} existente salvo em {hook_name}.bak",
131
+ COLOR_YELLOW,
132
+ )
133
+
134
+ # Copiar o hook
135
+ shutil.copy2(source_path, target_path)
136
+
137
+ # Setar permissão de execução (no-op funcional no Windows)
138
+ try:
139
+ current = os.stat(target_path).st_mode
140
+ os.chmod(target_path, current | stat.S_IEXEC | stat.S_IXGRP | stat.S_IXOTH)
141
+ except OSError:
142
+ pass # Windows pode não suportar chmod, mas Git for Windows não precisa
143
+
144
+ print_colored(f" [INSTALADO] {hook_name}", COLOR_GREEN)
145
+ installed += 1
146
+
147
+ return installed, skipped, errors
148
+
149
+
150
+ def uninstall_hooks(repo_root=None):
151
+ """
152
+ Remove hooks do Okam e restaura backups se existirem.
153
+
154
+ Returns:
155
+ Tuple (removed, restored, not_found) com contagens.
156
+ """
157
+ git_hooks_dir = _get_git_hooks_dir(repo_root)
158
+
159
+ removed = 0
160
+ restored = 0
161
+ not_found = 0
162
+
163
+ for hook_name in HOOK_NAMES:
164
+ target_path = os.path.join(git_hooks_dir, hook_name)
165
+ backup_path = target_path + ".bak"
166
+
167
+ if not os.path.exists(target_path):
168
+ print_colored(f" [NÃO ENCONTRADO] {hook_name}", COLOR_YELLOW)
169
+ not_found += 1
170
+ continue
171
+
172
+ if not _is_okam_hook(target_path):
173
+ print_colored(
174
+ f" [PULADO] {hook_name} — não foi instalado pelo Okam", COLOR_YELLOW
175
+ )
176
+ continue
177
+
178
+ os.remove(target_path)
179
+
180
+ if os.path.exists(backup_path):
181
+ shutil.move(backup_path, target_path)
182
+ print_colored(
183
+ f" [RESTAURADO] {hook_name} (backup restaurado)", COLOR_GREEN
184
+ )
185
+ restored += 1
186
+ else:
187
+ print_colored(f" [REMOVIDO] {hook_name}", COLOR_GREEN)
188
+ removed += 1
189
+
190
+ return removed, restored, not_found
191
+
192
+
193
+ def get_hooks_status(repo_root=None):
194
+ """
195
+ Verifica o status de cada hook de governança.
196
+
197
+ Returns:
198
+ Dict {hook_name: status_string}
199
+ """
200
+ git_hooks_dir = _get_git_hooks_dir(repo_root)
201
+ status = {}
202
+
203
+ for hook_name in HOOK_NAMES:
204
+ target_path = os.path.join(git_hooks_dir, hook_name)
205
+
206
+ if not os.path.exists(target_path):
207
+ status[hook_name] = "não instalado"
208
+ elif _is_okam_hook(target_path):
209
+ status[hook_name] = "instalado (Okam)"
210
+ else:
211
+ status[hook_name] = "instalado (externo)"
212
+
213
+ return status
okam/manager.py ADDED
@@ -0,0 +1,155 @@
1
+ import os
2
+ import re
3
+ import sys
4
+ from datetime import datetime
5
+
6
+ VALID_TYPES = {"index", "concept", "architecture", "runbook", "entity", "benchmark"}
7
+
8
+
9
+ def parse_simple_yaml(yaml_str):
10
+ """Parser minimalista de YAML para frontmatter. Sem dependências externas."""
11
+ lines = yaml_str.splitlines()
12
+ data = {}
13
+ current_key = None
14
+ for line in lines:
15
+ stripped = line.strip()
16
+ if not stripped or stripped.startswith('#'):
17
+ continue
18
+
19
+ match = re.match(r'^([a-zA-Z0-9_\-]+)\s*:\s*(.*)$', line)
20
+ if match:
21
+ current_key = match.group(1)
22
+ val = match.group(2).strip()
23
+ if val.startswith('[') and val.endswith(']'):
24
+ items = [x.strip() for x in val[1:-1].split(',')]
25
+ cleaned_items = []
26
+ for x in items:
27
+ if (x.startswith('"') and x.endswith('"')) or (x.startswith("'") and x.endswith("'")):
28
+ x = x[1:-1]
29
+ cleaned_items.append(x)
30
+ data[current_key] = cleaned_items
31
+ elif val == "":
32
+ data[current_key] = None
33
+ else:
34
+ if (val.startswith('"') and val.endswith('"')) or (val.startswith("'") and val.endswith("'")):
35
+ val = val[1:-1]
36
+ data[current_key] = val
37
+ elif line.startswith(' - ') and current_key:
38
+ val = line[4:].strip()
39
+ if (val.startswith('"') and val.endswith('"')) or (val.startswith("'") and val.endswith("'")):
40
+ val = val[1:-1]
41
+ if not isinstance(data.get(current_key), list):
42
+ data[current_key] = []
43
+ data[current_key].append(val)
44
+ return data
45
+
46
+
47
+ def serialize_simple_yaml(data):
48
+ """Serializa dicionário para YAML simples."""
49
+ lines = []
50
+ for key, val in data.items():
51
+ if val is None:
52
+ lines.append(f"{key}:")
53
+ elif isinstance(val, list):
54
+ if len(val) == 0:
55
+ lines.append(f"{key}: []")
56
+ else:
57
+ lines.append(f"{key}:")
58
+ for item in val:
59
+ lines.append(f" - {item}")
60
+ elif isinstance(val, str):
61
+ if any(c in val for c in [':', '#', '[', ']', '{', '}', ',', '"', "'"]) or val.strip() != val:
62
+ escaped = val.replace('"', '\\"')
63
+ lines.append(f'{key}: "{escaped}"')
64
+ else:
65
+ lines.append(f"{key}: {val}")
66
+ else:
67
+ lines.append(f"{key}: {val}")
68
+ return "\n".join(lines)
69
+
70
+
71
+ def load_markdown_file(filepath):
72
+ """Carrega um arquivo Markdown e separa frontmatter do corpo."""
73
+ with open(filepath, 'r', encoding='utf-8') as f:
74
+ content = f.read()
75
+
76
+ frontmatter = {}
77
+ body = content
78
+ match = re.match(r'^---\s*\n(.*?)\n---\s*\n', content, re.DOTALL)
79
+ if match:
80
+ yaml_content = match.group(1)
81
+ try:
82
+ frontmatter = parse_simple_yaml(yaml_content)
83
+ body = content[match.end():]
84
+ except Exception as e:
85
+ raise ValueError(f"Erro ao parsear YAML em {os.path.basename(filepath)}: {e}")
86
+
87
+ return frontmatter, body
88
+
89
+
90
+ def save_markdown_file(filepath, frontmatter, body):
91
+ """Salva arquivo Markdown com frontmatter YAML ordenado."""
92
+ ordered_keys = ['title', 'description', 'type', 'resource', 'timestamp', 'tags', 'parent']
93
+ ordered_fm = {}
94
+
95
+ for key in ordered_keys:
96
+ if key in frontmatter:
97
+ ordered_fm[key] = frontmatter[key]
98
+
99
+ for key, val in frontmatter.items():
100
+ if key not in ordered_keys:
101
+ ordered_fm[key] = val
102
+
103
+ yaml_str = serialize_simple_yaml(ordered_fm).strip()
104
+
105
+ with open(filepath, 'w', encoding='utf-8') as f:
106
+ f.write(f"---\n{yaml_str}\n---\n{body}")
107
+
108
+
109
+ def extract_h1(body):
110
+ """Extrai o primeiro heading H1 do corpo do documento."""
111
+ match = re.search(r'^#\s+(.+)$', body, re.MULTILINE)
112
+ return match.group(1).strip() if match else ""
113
+
114
+
115
+ def validate_file(filepath):
116
+ """Valida um arquivo contra o padrão OKF. Retorna (is_valid, errors)."""
117
+ try:
118
+ frontmatter, body = load_markdown_file(filepath)
119
+ except Exception as e:
120
+ return False, [str(e)]
121
+
122
+ if frontmatter is None:
123
+ return False, ["Erro de parse no YAML"]
124
+
125
+ errors = []
126
+
127
+ required_keys = ['title', 'description', 'type', 'resource', 'timestamp', 'tags', 'parent']
128
+ for key in required_keys:
129
+ if key not in frontmatter or frontmatter[key] is None:
130
+ errors.append(f"Chave obrigatória ausente: '{key}'")
131
+
132
+ if 'type' in frontmatter and frontmatter['type'] not in VALID_TYPES:
133
+ errors.append(f"Tipo inválido '{frontmatter['type']}'. Deve ser: {', '.join(VALID_TYPES)}")
134
+
135
+ if 'timestamp' in frontmatter:
136
+ ts = str(frontmatter['timestamp'])
137
+ if not re.match(r'^\d{4}-\d{2}-\d{2}$', ts):
138
+ errors.append(f"Formato de timestamp inválido '{ts}'. Deve ser YYYY-MM-DD")
139
+
140
+ if 'title' in frontmatter and body:
141
+ expected_title = extract_h1(body)
142
+ if expected_title and frontmatter['title'] != expected_title:
143
+ errors.append(f"Título no frontmatter ('{frontmatter['title']}') difere do H1 ('{expected_title}')")
144
+
145
+ return len(errors) == 0, errors
146
+
147
+
148
+ def get_wiki_files(wiki_dir):
149
+ """Retorna todos os arquivos .md de um wiki_dir."""
150
+ files = []
151
+ for root, _, filenames in os.walk(wiki_dir):
152
+ for f in filenames:
153
+ if f.endswith('.md'):
154
+ files.append(os.path.join(root, f))
155
+ return sorted(files)
okam/skill_creator.py ADDED
@@ -0,0 +1,166 @@
1
+ import os
2
+ import re
3
+ import sys
4
+
5
+ # Cores ANSI
6
+ COLOR_GREEN = "\033[92m"
7
+ COLOR_BLUE = "\033[94m"
8
+ COLOR_YELLOW = "\033[93m"
9
+ COLOR_RED = "\033[91m"
10
+ COLOR_BOLD = "\033[1m"
11
+ COLOR_RESET = "\033[0m"
12
+
13
+
14
+ def print_colored(text, color):
15
+ """Imprime texto colorido se o terminal suportar."""
16
+ # Ativa suporte a ANSI no Windows 10+
17
+ if sys.platform.startswith('win'):
18
+ os.system('')
19
+ sys.stdout.write(f"{color}{text}{COLOR_RESET}\n")
20
+ sys.stdout.flush()
21
+
22
+
23
+ def find_workspace_root():
24
+ """Sobe a árvore de diretórios a partir do atual para achar a raiz do workspace."""
25
+ current = os.path.abspath(os.getcwd())
26
+ while True:
27
+ # Indicadores de raiz do workspace okam/Juliano
28
+ indicators = ['.git', '.agents', 'AGENTS.md', 'Dashboard_Central.md']
29
+ for ind in indicators:
30
+ if os.path.exists(os.path.join(current, ind)):
31
+ return current
32
+
33
+ parent = os.path.dirname(current)
34
+ if parent == current:
35
+ # Chegou na raiz do sistema de arquivos
36
+ break
37
+ current = parent
38
+
39
+ return os.path.abspath(os.getcwd())
40
+
41
+
42
+ def ask_question(prompt_text, default=None, validator=None):
43
+ """Faz uma pergunta ao usuário no terminal e retorna a resposta validada."""
44
+ while True:
45
+ display = f"{COLOR_BOLD}{prompt_text}{COLOR_RESET}"
46
+ if default is not None:
47
+ display += f" [{COLOR_BLUE}{default}{COLOR_RESET}]"
48
+ display += ": "
49
+
50
+ try:
51
+ resp = input(display).strip()
52
+ except (KeyboardInterrupt, EOFError):
53
+ print_colored("\nOperação cancelada pelo usuário.", COLOR_RED)
54
+ sys.exit(1)
55
+
56
+ if not resp and default is not None:
57
+ resp = default
58
+
59
+ if not resp:
60
+ print_colored("A resposta não pode ser vazia.", COLOR_YELLOW)
61
+ continue
62
+
63
+ if validator:
64
+ is_valid, err_msg = validator(resp)
65
+ if not is_valid:
66
+ print_colored(err_msg, COLOR_RED)
67
+ continue
68
+
69
+ return resp
70
+
71
+
72
+ def validate_skill_name(name):
73
+ """Valida se o nome da skill é seguro e segue o padrão kebab-case."""
74
+ if not re.match(r'^[a-z0-9\-]+$', name):
75
+ return False, "O nome da skill deve conter apenas letras minúsculas, números e hifens (ex: minha-nova-skill)."
76
+ return True, ""
77
+
78
+
79
+ def create_new_skill():
80
+ """Fluxo interativo de criação de uma nova skill."""
81
+ print_colored("\n⬡ Criando Nova Skill para o Agente ⬡\n", COLOR_BLUE + COLOR_BOLD)
82
+
83
+ workspace_root = find_workspace_root()
84
+ agents_dir = os.path.join(workspace_root, ".agents")
85
+ skills_dir = os.path.join(agents_dir, "skills")
86
+
87
+ if not os.path.exists(agents_dir):
88
+ print_colored(f"Aviso: Diretório de governança '.agents' não encontrado na raiz: {workspace_root}", COLOR_YELLOW)
89
+ # Pergunta se quer criar a pasta .agents
90
+ confirm = ask_question("Deseja criar a pasta '.agents' na raiz deste diretório?", "sim").lower()
91
+ if confirm in ['s', 'sim', 'y', 'yes']:
92
+ os.makedirs(skills_dir, exist_ok=True)
93
+ else:
94
+ print_colored("Cancelado. Não é possível prosseguir sem a pasta de destino.", COLOR_RED)
95
+ sys.exit(1)
96
+ else:
97
+ os.makedirs(skills_dir, exist_ok=True)
98
+
99
+ skill_name = ask_question("Nome da Skill (kebab-case)", validator=validate_skill_name)
100
+
101
+ target_skill_dir = os.path.join(skills_dir, skill_name)
102
+ if os.path.exists(target_skill_dir):
103
+ print_colored(f"Erro: A skill '{skill_name}' já existe em: {target_skill_dir}", COLOR_RED)
104
+ sys.exit(1)
105
+
106
+ description = ask_question("Descrição da Skill (uma frase clara do que ela faz)")
107
+ version = ask_question("Versão inicial", default="1.0")
108
+
109
+ priority = ask_question(
110
+ "Prioridade (CRITICAL, HIGH, NORMAL, LOW)",
111
+ default="NORMAL",
112
+ validator=lambda x: (x.upper() in ["CRITICAL", "HIGH", "NORMAL", "LOW"], "Prioridade inválida. Escolha entre: CRITICAL, HIGH, NORMAL, LOW.")
113
+ ).upper()
114
+
115
+ create_dirs_prompt = ask_question("Criar subpastas de apoio (scripts, examples, resources, references)? (sim/não)", default="não").lower()
116
+ create_subdirs = create_dirs_prompt in ['s', 'sim', 'y', 'yes']
117
+
118
+ # Criando diretórios e arquivos
119
+ os.makedirs(target_skill_dir, exist_ok=True)
120
+
121
+ skill_md_path = os.path.join(target_skill_dir, "SKILL.md")
122
+
123
+ # Capitaliza as palavras do nome da skill para o título
124
+ title_display = " ".join(word.capitalize() for word in skill_name.split("-"))
125
+
126
+ skill_content = f"""---
127
+ name: {skill_name}
128
+ description: {description}
129
+ version: "{version}"
130
+ priority: {priority}
131
+ ---
132
+
133
+ # {title_display}
134
+
135
+ ## Objetivo
136
+
137
+ Descreva detalhadamente o objetivo e o escopo operacional desta skill.
138
+
139
+ ## Diretrizes de Uso
140
+
141
+ - Regra 1: Defina como o agente deve agir ao executar esta skill.
142
+ - Regra 2: Seja cirúrgico e prefira simplicidade.
143
+
144
+ ## Exemplos / Referências
145
+
146
+ Adicione exemplos de prompt ou referências úteis de código aqui.
147
+ """
148
+
149
+ with open(skill_md_path, 'w', encoding='utf-8') as f:
150
+ f.write(skill_content)
151
+
152
+ print_colored(f"\n[CRIADO] {os.path.relpath(skill_md_path, workspace_root)}", COLOR_GREEN)
153
+
154
+ if create_subdirs:
155
+ subdirs = ["scripts", "examples", "resources", "references"]
156
+ for sd in subdirs:
157
+ sd_path = os.path.join(target_skill_dir, sd)
158
+ os.makedirs(sd_path, exist_ok=True)
159
+ # Cria um gitkeep para que as pastas vazias possam ser commitadas
160
+ gitkeep_path = os.path.join(sd_path, ".gitkeep")
161
+ with open(gitkeep_path, 'w') as f:
162
+ pass
163
+ print_colored(f"[CRIADO] {os.path.relpath(sd_path, workspace_root)}/", COLOR_GREEN)
164
+
165
+ print_colored(f"\n✅ Skill '{skill_name}' criada com sucesso na governança do workspace!", COLOR_GREEN + COLOR_BOLD)
166
+ print_colored(f"Caminho: {target_skill_dir}\n", COLOR_BLUE)
@@ -0,0 +1,134 @@
1
+ Metadata-Version: 2.4
2
+ Name: okam
3
+ Version: 0.2.0
4
+ Summary: Framework de governança de IA, memória persistente e gestão de conhecimento
5
+ Author: Juliano Ceconi
6
+ License: MIT
7
+ License-File: LICENSE
8
+ Classifier: License :: OSI Approved :: MIT License
9
+ Classifier: Operating System :: OS Independent
10
+ Classifier: Programming Language :: Python :: 3
11
+ Requires-Python: >=3.8
12
+ Description-Content-Type: text/markdown
13
+
14
+ <div align="center">
15
+
16
+ # ⬡ Okam
17
+
18
+ **Pare de Redescobrir. Comece a Governar.**
19
+
20
+ Framework open-source para governança de IA, memória persistente e gestão de conhecimento.
21
+
22
+ [![MIT License](https://img.shields.io/badge/Licença-MIT-blue.svg)](./LICENSE)
23
+ [![Python 3.8+](https://img.shields.io/badge/Python-3.8+-green.svg)](https://python.org)
24
+ [![PRs Welcome](https://img.shields.io/badge/PRs-Welcome-brightgreen.svg)](https://github.com/juliano-ceconi/okam/pulls)
25
+
26
+ </div>
27
+
28
+ ---
29
+
30
+ ## O Que É Isso?
31
+
32
+ **Okam** é um framework que resolve um problema simples: **seus agentes de IA não têm memória.**
33
+
34
+ Toda vez que você abre uma sessão com seu copilot, ele começa do zero. Sem contexto das decisões passadas, sem governança, sem reutilização de conhecimento.
35
+
36
+ Okam resolve isso com:
37
+
38
+ - 🏛️ **Governança** — Regras claras para seus agentes (AGENTS.md + pipeline de 4 fases)
39
+ - 🧩 **Skills** — Capacidades modulares reutilizáveis entre projetos
40
+ - 📚 **Wiki de Conhecimento** — Memória persistente no formato OKF (não é RAG)
41
+ - 🔍 **Pipeline de Metadados** — Extrai a "alma" dos seus projetos automaticamente
42
+
43
+ ## Quick Start (5 minutos)
44
+
45
+ ```bash
46
+ # 1. Clone e instale localmente em modo de desenvolvimento
47
+ git clone https://github.com/juliano-ceconi/okam.git
48
+ cd okam
49
+ pip install -e .
50
+
51
+ # 2. Inicialize o wiki com seed pages
52
+ okam init
53
+
54
+ # 3. Customize o AGENTS.md com as regras do seu projeto
55
+ # (edite AGENTS.md com suas preferências)
56
+
57
+ # 4. Valide a conformidade
58
+ okam validate
59
+
60
+ # 5. Crie uma nova skill interativamente
61
+ okam new-skill
62
+ ```
63
+
64
+ *Nota: O script legado `python knowledge/scripts/okf_manager.py` continua funcionando como um wrapper de compatibilidade.*
65
+
66
+ Para o guia completo, veja o [QUICKSTART.md](./QUICKSTART.md).
67
+
68
+ ## Conceitos Core
69
+
70
+ | Conceito | O Que É | Doc |
71
+ |:---|:---|:---|
72
+ | **LLM Wiki** | Memória persistente vs RAG tradicional | [docs/concepts/llm-wiki.md](./docs/concepts/llm-wiki.md) |
73
+ | **OKF** | Formato padronizado de metadados para conhecimento | [docs/concepts/okf-format.md](./docs/concepts/okf-format.md) |
74
+ | **Deep Metadata** | Pipeline de 4 fases para extrair contexto | [docs/concepts/deep-metadata.md](./docs/concepts/deep-metadata.md) |
75
+ | **Guided Tours** | Padrão TOUR.md para onboarding de agentes | [docs/concepts/guided-tours.md](./docs/concepts/guided-tours.md) |
76
+ | **Git Hooks** | Governança automatizada via hooks pre-commit/push | [docs/concepts/git-hooks.md](./docs/concepts/git-hooks.md) |
77
+
78
+ ## Estrutura do Projeto
79
+
80
+ ```
81
+ okam/
82
+ ├── .agents/
83
+ │ ├── rules/ # Padrões de governança
84
+ │ ├── skills/ # Capacidades modulares
85
+ │ │ ├── deep-metadata-analysis/
86
+ │ │ ├── knowledge-wiki/
87
+ │ │ └── memory-maintenance/
88
+ │ └── workflows/ # Pipelines de execução
89
+ ├── hooks/ # Git hooks portáveis (POSIX sh)
90
+ │ ├── pre-commit # Validação OKF + detecção de segredos
91
+ │ ├── commit-msg # Conventional Commits
92
+ │ └── pre-push # Validação OKF completa
93
+ ├── knowledge/
94
+ │ ├── wiki/ # Páginas de síntese (OKF)
95
+ │ ├── raw-sources/ # Fontes brutas
96
+ │ └── scripts/ # Validador OKF
97
+ ├── templates/ # Templates reutilizáveis
98
+ ├── docs/
99
+ │ ├── concepts/ # Documentação de conceitos
100
+ │ └── diagrams/ # Diagramas Mermaid
101
+ ├── landing/ # Landing page
102
+ ├── AGENTS.md # Governança central
103
+ ├── QUICKSTART.md # Guia rápido
104
+ └── LICENSE # MIT
105
+ ```
106
+
107
+ ## Construído Com
108
+
109
+ - **Python** — Validador OKF (zero dependências, stdlib only)
110
+ - **Markdown** — Documentação e Wiki
111
+ - **YAML** — Metadados estruturados
112
+
113
+ ## Contribuindo
114
+
115
+ 1. Fork o repositório
116
+ 2. Crie uma branch para sua feature (`git checkout -b feat/minha-feature`)
117
+ 3. Instale os hooks de governança (`okam hooks install`)
118
+ 4. Commit suas mudanças (`git commit -m 'feat: adiciona minha feature'`)
119
+ 5. Push para a branch (`git push origin feat/minha-feature`)
120
+ 6. Abra um Pull Request
121
+
122
+ ## Licença
123
+
124
+ Este projeto está sob a licença MIT. Veja [LICENSE](./LICENSE) para mais detalhes.
125
+
126
+ ---
127
+
128
+ <div align="center">
129
+
130
+ **⬡ Feito por devs, para devs.**
131
+
132
+ [Landing Page](./landing/index.html) · [Quick Start](./QUICKSTART.md) · [Documentação](./docs/)
133
+
134
+ </div>
@@ -0,0 +1,13 @@
1
+ okam/__init__.py,sha256=Zn1KFblwuFHiDRdRAiRnDBRkbPttWh44jKa5zG2ov0E,22
2
+ okam/cli.py,sha256=5-bRTTUKqVr8TQCod2zmpFPE0VSV13bTi3h978SE5o8,12922
3
+ okam/hooks.py,sha256=0WFBJvaWnydLNOd-Y1UCbWzkxuKTFHWaJNz2o6DvC-k,6366
4
+ okam/manager.py,sha256=FPf4SwBPn4TFriHUfWYiHAOVC7bp_R3JO2zbgT0qamI,5549
5
+ okam/skill_creator.py,sha256=Fg9Mbl_zL_me1bn9mHa7oUZHixFVvW1Xy7ByqiF9x1Y,6028
6
+ okam/hooks/commit-msg,sha256=K6JPe8SpywxD-JprJO_GiwZRxyMOKu-dVlg60liwIYw,2490
7
+ okam/hooks/pre-commit,sha256=ga_MH6QrcyPE-IU-abElVs6xfazmcq64_gq-HqtNj18,4967
8
+ okam/hooks/pre-push,sha256=2VFHSF1gMdAHI2V52EXwM-X68oSTobJ5Co7tGz-CtbA,1402
9
+ okam-0.2.0.dist-info/METADATA,sha256=oXeRW9CREduCo9AYTjvh9UHfP0_orGvyQZK5ME8AabI,4801
10
+ okam-0.2.0.dist-info/WHEEL,sha256=mffPy8wBnZQn2VnJUU5jE99KsxaSfiyMHV9Yt0aLVxs,87
11
+ okam-0.2.0.dist-info/entry_points.txt,sha256=J464ar3O_pbCuxaKrFOUL1H2Z0OqmxwQiq20L63nDI8,39
12
+ okam-0.2.0.dist-info/licenses/LICENSE,sha256=X_SoMbZ6BaV_Mq5UcrmXbHumMxVqOPpF0-kXZ_DRqbs,1074
13
+ okam-0.2.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.30.1
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ okam = okam.cli:main
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Okam Contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.