stealthos-cli 0.1.0-alpha.3 → 0.1.0-alpha.5
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.
- package/ai/CONTRACT.md +110 -0
- package/ai/INDEX.md +203 -0
- package/ai/README.md +434 -0
- package/ai/ROUTER.md +288 -0
- package/ai/agents/README.md +103 -0
- package/ai/agents/architect.md +59 -0
- package/ai/agents/backend-engineer.md +62 -0
- package/ai/agents/founder.md +45 -0
- package/ai/agents/frontend-engineer.md +61 -0
- package/ai/agents/product-manager.md +56 -0
- package/ai/agents/qa-engineer.md +53 -0
- package/ai/agents/researcher.md +74 -0
- package/ai/agents/reviewer.md +73 -0
- package/ai/agents/security-engineer.md +59 -0
- package/ai/agents/sre-engineer.md +70 -0
- package/ai/agents/tech-lead.md +70 -0
- package/ai/architecture/README.md +35 -0
- package/ai/architecture/components.md +24 -0
- package/ai/architecture/containers.md +30 -0
- package/ai/architecture/event-flows.md +36 -0
- package/ai/architecture/sequence-diagrams.md +38 -0
- package/ai/architecture/system-context.md +46 -0
- package/ai/architecture/threat-modeling.md +40 -0
- package/ai/blueprints/README.md +67 -0
- package/ai/blueprints/_schema.json +40 -0
- package/ai/blueprints/ai-platform.json +28 -0
- package/ai/blueprints/crm.json +22 -0
- package/ai/blueprints/game.json +25 -0
- package/ai/blueprints/mobile.json +24 -0
- package/ai/blueprints/realtime.json +22 -0
- package/ai/blueprints/saas.json +25 -0
- package/ai/blueprints/telemetry.json +30 -0
- package/ai/blueprints/web.json +23 -0
- package/ai/bootstrap/discovery-questions.md +117 -0
- package/ai/bootstrap/dispatcher.md +85 -0
- package/ai/bootstrap/existing-project.md +191 -0
- package/ai/bootstrap/new-project.md +127 -0
- package/ai/bootstrap/tech-mapping.md +164 -0
- package/ai/clients/README.md +114 -0
- package/ai/clients/antigravity.md +125 -0
- package/ai/clients/claude-code.md +65 -0
- package/ai/clients/cline.md +69 -0
- package/ai/clients/codex-aider-cli.md +82 -0
- package/ai/clients/continue.md +67 -0
- package/ai/clients/copilot.md +49 -0
- package/ai/clients/cursor.md +81 -0
- package/ai/clients/snippets/mcp-absolute-paths.json +9 -0
- package/ai/clients/snippets/mcp-http.json +7 -0
- package/ai/clients/snippets/mcp-stdio.json +9 -0
- package/ai/clients/trae.md +69 -0
- package/ai/clients/windsurf.md +71 -0
- package/ai/core/pipeline/execution-engine.md +157 -0
- package/ai/engineering/README.md +32 -0
- package/ai/engineering/observability/incident-response.md +82 -0
- package/ai/evals/protocol-tests.md +150 -0
- package/ai/evolution/agent-evolution.md +161 -0
- package/ai/evolution/improvements.md +91 -0
- package/ai/evolution/learnings.md +49 -0
- package/ai/evolution/patterns-discovered.md +48 -0
- package/ai/execution/README.md +33 -0
- package/ai/execution/backlog.md +27 -0
- package/ai/execution/milestones.md +26 -0
- package/ai/execution/roadmap.md +30 -0
- package/ai/execution/sprint.md +42 -0
- package/ai/governance/README.md +34 -0
- package/ai/governance/architecture-principles.md +99 -0
- package/ai/governance/definition-of-done.md +88 -0
- package/ai/governance/definition-of-ready.md +69 -0
- package/ai/governance/engineering-principles.md +70 -0
- package/ai/governance/quality-gates.md +85 -0
- package/ai/governance/security-policies.md +84 -0
- package/ai/hooks/enforce-audit.ps1 +41 -0
- package/ai/hooks/enforce-audit.sh +39 -0
- package/ai/hooks/guard-edit.ps1 +182 -0
- package/ai/hooks/guard-edit.sh +161 -0
- package/ai/hooks/inject-os-reminder.ps1 +40 -0
- package/ai/hooks/inject-os-reminder.sh +16 -0
- package/ai/manifest.json +238 -0
- package/ai/memory/_detected-stack.json +33 -0
- package/ai/memory/_summary.md +49 -0
- package/ai/memory/archive/.gitkeep +3 -0
- package/ai/memory/completed-tasks.md +156 -0
- package/ai/memory/decisions.md +257 -0
- package/ai/memory/errors-and-solutions.md +41 -0
- package/ai/memory/known-issues.md +40 -0
- package/ai/memory/pending-tasks.md +37 -0
- package/ai/memory/project-context.md +67 -0
- package/ai/operating-system/architecture.md +54 -0
- package/ai/operating-system/coding-standards.md +84 -0
- package/ai/operating-system/folder-structure.md +126 -0
- package/ai/operating-system/performance-rules.md +86 -0
- package/ai/operating-system/quality-control.md +81 -0
- package/ai/operating-system/security-rules.md +91 -0
- package/ai/operating-system/workflow.md +86 -0
- package/ai/product/README.md +24 -0
- package/ai/product/business-rules.md +26 -0
- package/ai/product/personas.md +29 -0
- package/ai/product/user-journeys.md +30 -0
- package/ai/product/vision.md +35 -0
- package/ai/rules/behavior.md +45 -0
- package/ai/rules/do.md +47 -0
- package/ai/rules/dont.md +46 -0
- package/ai/rules/execution-flow.md +125 -0
- package/ai/rules/structural-constraints.md +59 -0
- package/ai/rules/structure-canon.md +116 -0
- package/ai/runtime.md +179 -0
- package/ai/scripts/detect-stack.ps1 +166 -0
- package/ai/scripts/detect-stack.sh +172 -0
- package/ai/scripts/init-ai-os.ps1 +215 -0
- package/ai/scripts/init-ai-os.sh +99 -0
- package/ai/scripts/lint-os.ps1 +99 -0
- package/ai/scripts/lint-os.sh +85 -0
- package/ai/scripts/start-os.ps1 +151 -0
- package/ai/scripts/start-os.sh +141 -0
- package/ai/server/README.md +105 -0
- package/ai/server/aios-server.mjs +2134 -0
- package/ai/server/package-lock.json +802 -0
- package/ai/server/package.json +31 -0
- package/ai/server/src/analyzer/graph-builder.ts +92 -0
- package/ai/server/src/analyzer/index.ts +191 -0
- package/ai/server/src/analyzer/module-mapper.ts +171 -0
- package/ai/server/src/analyzer/smell-detector.ts +54 -0
- package/ai/server/src/analyzer/stack-detector.ts +70 -0
- package/ai/server/src/index.ts +16 -0
- package/ai/server/src/packager/context-builder.ts +217 -0
- package/ai/server/src/packager/index.ts +3 -0
- package/ai/server/src/packager/memory-injector.ts +128 -0
- package/ai/server/src/packager/module-summarizer.ts +60 -0
- package/ai/server/src/packager/token-estimator.ts +26 -0
- package/ai/server/src/snapshot/index.ts +3 -0
- package/ai/server/src/snapshot/snapshot-creator.ts +206 -0
- package/ai/server/src/snapshot/snapshot-diff.ts +86 -0
- package/ai/server/src/snapshot/snapshot-restore.ts +14 -0
- package/ai/server/src/types.ts +94 -0
- package/ai/server/tsconfig.json +26 -0
- package/ai/skills/architecture-design.md +82 -0
- package/ai/skills/backend-engineering.md +57 -0
- package/ai/skills/database-design.md +76 -0
- package/ai/skills/frontend-engineering.md +63 -0
- package/ai/skills/performance.md +73 -0
- package/ai/skills/scalability.md +84 -0
- package/ai/skills/security.md +71 -0
- package/ai/skills/testing.md +77 -0
- package/ai/specs/ADR/ADR-0002-typescript-runtime.md +103 -0
- package/ai/specs/ADR/ADR-0004-runtime-orchestrator.md +94 -0
- package/ai/specs/ADR/ADR-0005-workflow-engine.md +105 -0
- package/ai/specs/ADR/ADR-0006-runtime-state.md +104 -0
- package/ai/specs/ADR/ADR-0007-state-compiler-drift-context-layers-artifact-index.md +82 -0
- package/ai/specs/ADR/ADR-0008-intent-runtime-discovery-branching.md +93 -0
- package/ai/specs/ADR/ADR-0009-confidence-system-maturity-tracking.md +113 -0
- package/ai/specs/ADR/ADR-0010-structural-architecture-standards.md +121 -0
- package/ai/specs/ADR/ADR-0011-mcp-prompts.md +86 -0
- package/ai/specs/ADR/ADR-0012-stealthos-hybrid-architecture.md +174 -0
- package/ai/specs/ADR/_TEMPLATE.md +60 -0
- package/ai/specs/BRD/_TEMPLATE.md +50 -0
- package/ai/specs/PRD/_TEMPLATE.md +72 -0
- package/ai/specs/README.md +43 -0
- package/ai/specs/RFC/RFC-0001-runtime-orchestrator.md +149 -0
- package/ai/specs/RFC/RFC-0002-runtime-orchestrator-extended.md +134 -0
- package/ai/specs/RFC/_TEMPLATE.md +61 -0
- package/ai/specs/RUNBOOKS/_TEMPLATE.md +68 -0
- package/ai/specs/SDD/_TEMPLATE.md +104 -0
- package/ai/specs/TASKS/_TEMPLATE.md +52 -0
- package/ai/tools/debugging.md +64 -0
- package/ai/tools/dependency-analysis.md +46 -0
- package/ai/tools/internet-research.md +42 -0
- package/ai/tools/mcp-discovery.md +44 -0
- package/ai/workflows/_schema.json +81 -0
- package/ai/workflows/init.json +148 -0
- package/ai/workflows/sync.json +71 -0
- package/ai/workflows/work.json +91 -0
- package/package.json +42 -36
- package/scripts/bundle-ai.mjs +58 -0
- package/src/cli.mjs +83 -79
- package/src/commands/install.mjs +35 -11
- package/src/commands/run.mjs +117 -0
- package/src/lib/resolve-source.mjs +27 -10
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
---
|
|
2
|
+
version: 1.0.0
|
|
3
|
+
updated: 2026-05-15
|
|
4
|
+
tier: conditional
|
|
5
|
+
tokens: ~700
|
|
6
|
+
load: architecture, design, refactor, review
|
|
7
|
+
triggers: princípio, principle, fundamentos, governança
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Engineering Principles
|
|
11
|
+
|
|
12
|
+
> Princípios que governam toda decisão técnica neste OS. Quando houver dúvida, voltar aqui.
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## P1 — Simplicidade vence sofisticação
|
|
17
|
+
|
|
18
|
+
Se há duas soluções, escolha a mais simples que funciona. Sofisticação só é justificada quando há requisito claro (escala, segurança, regulação). Anti-padrão: prematuramente introduzir microsserviços, ORM customizado, framework próprio.
|
|
19
|
+
|
|
20
|
+
## P2 — Causa raiz, não sintoma
|
|
21
|
+
|
|
22
|
+
Bug nunca é fechado com workaround silencioso. Identifique a causa, corrija, documente em `memory/errors-and-solutions.md`. Se urgência exige workaround, registre como `known-issue` com data de validade.
|
|
23
|
+
|
|
24
|
+
## P3 — Menor mudança que resolve
|
|
25
|
+
|
|
26
|
+
Refator oportunista é proibido em PR de feature/bug. Cada mudança tem 1 objetivo declarado.
|
|
27
|
+
|
|
28
|
+
## P4 — Reversibilidade
|
|
29
|
+
|
|
30
|
+
Toda decisão deve responder: "como desfazer se errado?". Migrações têm script de rollback; deploys têm canário/blue-green; ADRs têm seção "consequências negativas".
|
|
31
|
+
|
|
32
|
+
## P5 — Spec antes de código
|
|
33
|
+
|
|
34
|
+
Não escrever código sem PRD + SDD (para features) ou RFC (para mudanças cross-cutting). Bug-fix pequeno é exceção, mas deve atualizar o doc associado se a regra mudou.
|
|
35
|
+
|
|
36
|
+
## P6 — Memória explícita
|
|
37
|
+
|
|
38
|
+
Decisão tomada, registrar. Erro encontrado, registrar. Padrão emergente, registrar. O cérebro está no disco, não no chat.
|
|
39
|
+
|
|
40
|
+
## P7 — Custo é restrição
|
|
41
|
+
|
|
42
|
+
Toda escolha que adiciona custo (financeiro, complexidade, manutenção) é justificada em ADR. Default é não adicionar.
|
|
43
|
+
|
|
44
|
+
## P8 — Defesa em camadas
|
|
45
|
+
|
|
46
|
+
Validação no front + no back + no DB. Segurança no perímetro + na camada de aplicação + nos dados. Resiliência via timeouts + retries + circuit breakers + fallbacks.
|
|
47
|
+
|
|
48
|
+
## P9 — Observabilidade desde o dia 1
|
|
49
|
+
|
|
50
|
+
Sem logs estruturados + metrics + traces, qualquer sistema cresce no escuro. Logs com prefixo `[MODULE]`, métricas com nome em `snake_case`, traces propagados via `x-trace-id`.
|
|
51
|
+
|
|
52
|
+
## P10 — Não inventar
|
|
53
|
+
|
|
54
|
+
`rules/dont.md` regra 1. Se não tem certeza, leia o código, rode comando, ou pergunte. APIs/flags/versões nunca são chutadas.
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
|
|
58
|
+
## Princípios derivados (operacionais)
|
|
59
|
+
|
|
60
|
+
- **YAGNI** (You Aren't Gonna Need It) — derivado de P1.
|
|
61
|
+
- **Boy Scout** — derivado de P3 invertido: deixe **igual ou melhor**, mas dentro do escopo da PR.
|
|
62
|
+
- **Trunk-based** — branches curtas; ramos longos viram conflito e divergência.
|
|
63
|
+
- **Test first, when it pays** — TDD para regras críticas; smoke depois para UI.
|
|
64
|
+
- **Documentation as code** — markdown versionado >> wiki externo.
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
## Quando questionar um princípio
|
|
69
|
+
|
|
70
|
+
Quando 3+ casos reais mostrarem que o princípio prejudica mais que ajuda. Abrir entrada em `evolution/improvements.md`, validar, escalar para ADR de mudança.
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
---
|
|
2
|
+
version: 1.0.0
|
|
3
|
+
updated: 2026-05-15
|
|
4
|
+
tier: conditional
|
|
5
|
+
tokens: ~600
|
|
6
|
+
load: pipeline, gate, review, deploy
|
|
7
|
+
triggers: gate, qualidade, quality, checkpoint
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Quality Gates
|
|
11
|
+
|
|
12
|
+
> Pontos obrigatórios de validação no pipeline. O fluxo IDEIA → DEPLOY só avança quando o gate atual passa. Cada gate tem **owner** (humano OU agente) e **artefato de saída**.
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## Pipeline canônico (referência: `core/pipeline/execution-engine.md`)
|
|
17
|
+
|
|
18
|
+
```
|
|
19
|
+
G1 G2 G3 G4 G5 G6 G7 G8 G9 G10
|
|
20
|
+
IDEIA→PRD→ARCH→SDD→TASKS→IMPL→TEST→REVIEW→DEPLOY→OBSERVE→LEARN
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
| Gate | Nome | Owner | Artefato | Critério |
|
|
24
|
+
|---|---|---|---|---|
|
|
25
|
+
| **G1** | Discovery aprovado | `product-manager` agent + Founder | PRD draft | Problema, objetivo, KPIs, personas, escopo claros |
|
|
26
|
+
| **G2** | PRD aprovado | Founder | PRD final | Requisitos funcionais + não-funcionais + critérios de aceite |
|
|
27
|
+
| **G3** | Arquitetura aprovada | `architect` agent + Founder | SDD + ADRs | Decisões justificadas, alternativas listadas, riscos |
|
|
28
|
+
| **G4** | Tasks definidas | `tech-lead` agent | Backlog | Cada task atende DoR (`governance/definition-of-ready.md`) |
|
|
29
|
+
| **G5** | Implementação | `backend/frontend-engineer` | Código + testes unit | Build verde, types OK, testes unit passam |
|
|
30
|
+
| **G6** | QA aprovado | `qa-engineer` agent | Testes E2E + edge cases | Cobertura subiu ou manteve, edge cases cobertos |
|
|
31
|
+
| **G7** | Review aprovado | `reviewer` agent + humano | PR aprovada | Diff revisado, conformidade com `governance/*` |
|
|
32
|
+
| **G8** | Deploy autorizado | `sre-engineer` agent | Plano de deploy + rollback | Pré-deploy checklist OK, janela respeitada |
|
|
33
|
+
| **G9** | Observabilidade ativa | `sre-engineer` agent | Dashboards + alertas | Métrica de sucesso instrumentada, alertas no canal |
|
|
34
|
+
| **G10** | Aprendizado registrado | qualquer agent | Entrada em `evolution/` | Lessons learned, patterns ou anti-patterns |
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
## Como o agente sinaliza um gate
|
|
39
|
+
|
|
40
|
+
```
|
|
41
|
+
[GATE G3 — Arquitetura]
|
|
42
|
+
Status: ✅ aprovado | ⏸ aguardando aprovação humana | ❌ bloqueado
|
|
43
|
+
|
|
44
|
+
Artefato: .ai/specs/SDD/SDD-007-events-heatmap.md
|
|
45
|
+
Decisão pendente: ADR-0042 — Cassandra vs PostgreSQL para hot path
|
|
46
|
+
|
|
47
|
+
Próximo gate: G4 — Task Breakdown (owner: tech-lead)
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Gates parciais (não-todos os projetos precisam de todos)
|
|
51
|
+
|
|
52
|
+
| Cenário | Gates obrigatórios | Gates opcionais |
|
|
53
|
+
|---|---|---|
|
|
54
|
+
| Bug fix P0 | G5, G6, G7, G8, G9 | G1-G4 (skip) |
|
|
55
|
+
| Feature nova | G1-G10 (todos) | — |
|
|
56
|
+
| Refator interno | G3 (mini), G5, G6, G7 | G1, G2, G4, G8, G9 |
|
|
57
|
+
| Spike / pesquisa | G1, G10 | resto skip |
|
|
58
|
+
| Mudança de config | G7, G8, G9 | resto skip |
|
|
59
|
+
|
|
60
|
+
Skip de gate deve aparecer no Audit: `Gates skipped: G2, G4 (motivo: bug fix P0, escopo cirúrgico)`.
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
## Falha de gate
|
|
65
|
+
|
|
66
|
+
Quando um gate falha, o agente:
|
|
67
|
+
1. Para o pipeline.
|
|
68
|
+
2. Registra em `memory/known-issues.md` se for problema sistêmico.
|
|
69
|
+
3. Devolve para o gate anterior com diagnóstico:
|
|
70
|
+
```
|
|
71
|
+
[GATE G5] FALHOU
|
|
72
|
+
Causa: build vermelho em tsc server (1 erro em foo.ts:42)
|
|
73
|
+
Ação: voltar para G5 com fix; manter PR open.
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Repetir falha do mesmo gate em 3+ tasks → revisar o gate (talvez está mal calibrado) em `improvements.md`.
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## Anti-padrões
|
|
81
|
+
|
|
82
|
+
- Pular G3 (arquitetura) em feature complex → deriva inevitável.
|
|
83
|
+
- Pular G6 (QA) em bug crítico → bug volta.
|
|
84
|
+
- "Aprovar" gate sem artefato escrito — só vale com doc no `.ai/`.
|
|
85
|
+
- Founder fazer manual override em todos os gates → use override moderado, registre em `decisions.md`.
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
---
|
|
2
|
+
version: 1.0.0
|
|
3
|
+
updated: 2026-05-15
|
|
4
|
+
tier: conditional
|
|
5
|
+
tokens: ~500
|
|
6
|
+
load: security, auth, secret, vulnerability
|
|
7
|
+
triggers: segurança, security, auth, autenticação, senha, password, token, vuln, lgpd, gdpr
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Security Policies
|
|
11
|
+
|
|
12
|
+
> Políticas mínimas que se aplicam a todos os projetos sob este OS. Específicas por projeto vivem em `operating-system/security-rules.md`.
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## P-SEC-1 — Segredos fora do código
|
|
17
|
+
|
|
18
|
+
- **Nunca** commitar `.env`, chaves, tokens, dumps de credencial.
|
|
19
|
+
- `.env` SEMPRE no `.gitignore` e adicionado em template `*.example`.
|
|
20
|
+
- Secret manager (1Password, Vault, AWS SSM, Doppler) para produção.
|
|
21
|
+
- Rotação obrigatória após exposição acidental + log do incidente em `memory/errors-and-solutions.md`.
|
|
22
|
+
|
|
23
|
+
## P-SEC-2 — Autenticação e autorização
|
|
24
|
+
|
|
25
|
+
- Senhas hash com `bcrypt`/`argon2` (custo ≥10).
|
|
26
|
+
- JWT com expiração curta (≤1h) + refresh token rotativo.
|
|
27
|
+
- Default-deny: rota pública é a exceção, declarada.
|
|
28
|
+
- Princípio do menor privilégio em DB users, cloud roles, MCPs.
|
|
29
|
+
|
|
30
|
+
## P-SEC-3 — Validação de input
|
|
31
|
+
|
|
32
|
+
- **Validar no perímetro** (request body, query string, headers).
|
|
33
|
+
- Usar lib de schema (`zod`, `joi`, `valibot`) — não validar manualmente.
|
|
34
|
+
- Rejeitar antes de tocar lógica de negócio.
|
|
35
|
+
- Sanitizar antes de renderizar (XSS) e antes de SQL (SQL injection).
|
|
36
|
+
|
|
37
|
+
## P-SEC-4 — Logging seguro
|
|
38
|
+
|
|
39
|
+
- **Nunca** logar valores de variáveis sensíveis (senha, token, cartão, CPF parcial).
|
|
40
|
+
- Logs estruturados com filtro de PII no transporte.
|
|
41
|
+
- `[MODULE]` prefix obrigatório (ver `governance/engineering-principles.md` P9).
|
|
42
|
+
|
|
43
|
+
## P-SEC-5 — Dependências
|
|
44
|
+
|
|
45
|
+
- Lockfile commitado (`package-lock.json`, `pnpm-lock.yaml`, etc.).
|
|
46
|
+
- Audit semanal: `npm audit --production`, `pip-audit`, `cargo audit`.
|
|
47
|
+
- Dep major (semver-breaking) upgrade exige ADR.
|
|
48
|
+
|
|
49
|
+
## P-SEC-6 — Threat modeling
|
|
50
|
+
|
|
51
|
+
Para qualquer feature que toca dados sensíveis, criar entrada em `architecture/threat-modeling.md`:
|
|
52
|
+
- O que está sendo protegido?
|
|
53
|
+
- De quem (atores)?
|
|
54
|
+
- Como (vetores)?
|
|
55
|
+
- Mitigações?
|
|
56
|
+
|
|
57
|
+
Usar STRIDE mínimo (Spoofing, Tampering, Repudiation, Info Disclosure, DoS, Elevation of Privilege).
|
|
58
|
+
|
|
59
|
+
## P-SEC-7 — Compliance (quando aplicável)
|
|
60
|
+
|
|
61
|
+
- **LGPD/GDPR**: registrar bases legais, ter `/privacy` endpoint, suportar export/delete.
|
|
62
|
+
- **PCI-DSS**: nunca armazenar PAN não-criptografado; usar tokenização do provedor.
|
|
63
|
+
- **HIPAA**: BAA + criptografia at-rest e in-transit + auditoria de acesso.
|
|
64
|
+
|
|
65
|
+
Cada um desses requisitos vira ADR no projeto + entrada em `operating-system/security-rules.md`.
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
## Em PR de feature/bug
|
|
70
|
+
|
|
71
|
+
Reviewer (humano ou `security-engineer` agent) DEVE confirmar:
|
|
72
|
+
- [ ] Sem segredo no diff
|
|
73
|
+
- [ ] Inputs validados
|
|
74
|
+
- [ ] Outputs não vazam PII em logs
|
|
75
|
+
- [ ] Sem `eval`, `Function()`, `child_process.exec(unescaped)`
|
|
76
|
+
- [ ] Sem `dangerouslySetInnerHTML` sem sanitize
|
|
77
|
+
- [ ] Sem `cors({ origin: '*' })` em prod
|
|
78
|
+
|
|
79
|
+
## Em incidente
|
|
80
|
+
|
|
81
|
+
1. **Conter** — desabilitar feature flag, revogar token, isolar host.
|
|
82
|
+
2. **Comunicar** — registrar timestamp + impact em `memory/known-issues.md`.
|
|
83
|
+
3. **Investigar** — causa-raiz, em `memory/errors-and-solutions.md`.
|
|
84
|
+
4. **Aprender** — entrada em `evolution/learnings.md` se padrão emergir.
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# Stop hook
|
|
2
|
+
# Roda quando o agente termina seu turno.
|
|
3
|
+
# Verifica se transcript do turno contem [OS Audit] para tarefas nao-triviais.
|
|
4
|
+
|
|
5
|
+
$projectDir = $env:CLAUDE_PROJECT_DIR
|
|
6
|
+
if (-not $projectDir) { $projectDir = (Get-Location).Path }
|
|
7
|
+
|
|
8
|
+
$raw = [Console]::In.ReadToEnd()
|
|
9
|
+
try {
|
|
10
|
+
$payload = $raw | ConvertFrom-Json
|
|
11
|
+
} catch {
|
|
12
|
+
exit 0
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
$transcriptPath = $payload.transcript_path
|
|
16
|
+
if (-not $transcriptPath -or -not (Test-Path $transcriptPath)) {
|
|
17
|
+
exit 0
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
# Le ultimas linhas da transcript
|
|
21
|
+
$lines = Get-Content $transcriptPath -Tail 100 -ErrorAction SilentlyContinue
|
|
22
|
+
if (-not $lines) { exit 0 }
|
|
23
|
+
|
|
24
|
+
$transcriptText = $lines -join "`n"
|
|
25
|
+
|
|
26
|
+
# Heuristica: se houve tool calls de Edit/Write neste turno e nao ha [OS Audit] na resposta final
|
|
27
|
+
$hasEdits = $transcriptText -match '"tool_name"\s*:\s*"(Edit|Write|NotebookEdit)"'
|
|
28
|
+
$hasAudit = $transcriptText -match '\[OS Audit\]'
|
|
29
|
+
$hasTrivialMark = $transcriptText -match '\[OS Audit\] skipped: trivial'
|
|
30
|
+
|
|
31
|
+
if ($hasEdits -and -not $hasAudit -and -not $hasTrivialMark) {
|
|
32
|
+
# Pede ao agente para completar o audit antes de parar
|
|
33
|
+
$response = @{
|
|
34
|
+
decision = "block"
|
|
35
|
+
reason = "Protocolo nao cumprido: tarefa fez edicoes mas nao emitiu [OS Audit]. Adicione o bloco de audit no formato definido em .ai/CONTRACT.md antes de encerrar:`n[OS Audit]`n- Files touched: ...`n- Memory updated: ...`n- Validations: ...`n- Decisions: ...`n- Open items: ..."
|
|
36
|
+
} | ConvertTo-Json -Depth 5 -Compress
|
|
37
|
+
Write-Output $response
|
|
38
|
+
exit 0
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
exit 0
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Stop hook (Unix)
|
|
3
|
+
# Verifica se transcript contem [OS Audit] para tarefas com edicoes.
|
|
4
|
+
set -euo pipefail
|
|
5
|
+
|
|
6
|
+
PROJECT_DIR="${CLAUDE_PROJECT_DIR:-$(pwd)}"
|
|
7
|
+
PAYLOAD=$(cat)
|
|
8
|
+
|
|
9
|
+
TRANSCRIPT_PATH=""
|
|
10
|
+
if command -v python3 &>/dev/null; then
|
|
11
|
+
TRANSCRIPT_PATH=$(echo "$PAYLOAD" | python3 -c 'import sys,json
|
|
12
|
+
try:
|
|
13
|
+
d=json.load(sys.stdin); print(d.get("transcript_path",""))
|
|
14
|
+
except: pass' 2>/dev/null || echo "")
|
|
15
|
+
elif command -v node &>/dev/null; then
|
|
16
|
+
TRANSCRIPT_PATH=$(echo "$PAYLOAD" | node -e 'let s="";process.stdin.on("data",d=>s+=d).on("end",()=>{try{console.log(JSON.parse(s).transcript_path||"")}catch{}})' 2>/dev/null || echo "")
|
|
17
|
+
fi
|
|
18
|
+
|
|
19
|
+
[[ -z "$TRANSCRIPT_PATH" || ! -f "$TRANSCRIPT_PATH" ]] && exit 0
|
|
20
|
+
|
|
21
|
+
# Pega as ultimas 100 linhas
|
|
22
|
+
TAIL_TEXT=$(tail -n 100 "$TRANSCRIPT_PATH" 2>/dev/null || echo "")
|
|
23
|
+
[[ -z "$TAIL_TEXT" ]] && exit 0
|
|
24
|
+
|
|
25
|
+
HAS_EDITS=0
|
|
26
|
+
HAS_AUDIT=0
|
|
27
|
+
HAS_TRIVIAL=0
|
|
28
|
+
|
|
29
|
+
echo "$TAIL_TEXT" | grep -Eq '"tool_name"[[:space:]]*:[[:space:]]*"(Edit|Write|NotebookEdit)"' && HAS_EDITS=1
|
|
30
|
+
echo "$TAIL_TEXT" | grep -Fq '[OS Audit]' && HAS_AUDIT=1
|
|
31
|
+
echo "$TAIL_TEXT" | grep -Fq '[OS Audit] skipped: trivial' && HAS_TRIVIAL=1
|
|
32
|
+
|
|
33
|
+
if [[ $HAS_EDITS -eq 1 && $HAS_AUDIT -eq 0 && $HAS_TRIVIAL -eq 0 ]]; then
|
|
34
|
+
REASON='Protocolo nao cumprido: tarefa fez edicoes mas nao emitiu [OS Audit]. Adicione o bloco de audit conforme .ai/CONTRACT.md antes de encerrar:\n[OS Audit]\n- Files touched: ...\n- Memory updated: ...\n- Validations: ...\n- Decisions: ...\n- Open items: ...'
|
|
35
|
+
printf '{"decision":"block","reason":"%s"}\n' "$REASON"
|
|
36
|
+
exit 0
|
|
37
|
+
fi
|
|
38
|
+
|
|
39
|
+
exit 0
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
# PreToolUse hook (Windows / PowerShell) — matcher: Edit|Write|NotebookEdit|Bash
|
|
2
|
+
# Bloqueia mudancas em arquivos protegidos do AI OS sem confirmacao explicita.
|
|
3
|
+
# Para Bash, faz split em SUBCOMANDOS por separadores (&&, ||, ;, |, newline) e
|
|
4
|
+
# bloqueia apenas o subcomando que contem write verb + path protegido juntos.
|
|
5
|
+
# Override autorizado: criar .claude\.unlock (mera existencia = bypass total).
|
|
6
|
+
|
|
7
|
+
$projectDir = $env:CLAUDE_PROJECT_DIR
|
|
8
|
+
if (-not $projectDir) { $projectDir = (Get-Location).Path }
|
|
9
|
+
|
|
10
|
+
# Bypass via sentinela
|
|
11
|
+
$unlockPath = Join-Path $projectDir '.claude\.unlock'
|
|
12
|
+
if (Test-Path $unlockPath) { exit 0 }
|
|
13
|
+
|
|
14
|
+
# Ler payload do hook
|
|
15
|
+
$raw = [Console]::In.ReadToEnd()
|
|
16
|
+
try {
|
|
17
|
+
$payload = $raw | ConvertFrom-Json
|
|
18
|
+
} catch {
|
|
19
|
+
exit 0
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
$toolName = $payload.tool_name
|
|
23
|
+
|
|
24
|
+
# Paths PROTEGIDOS (substring match, normalizado para / lower-case)
|
|
25
|
+
$protectedSubstrings = @(
|
|
26
|
+
'.ai/rules/dont.md',
|
|
27
|
+
'.ai/contract.md',
|
|
28
|
+
'.ai/router.md',
|
|
29
|
+
'.ai/manifest.json',
|
|
30
|
+
'.ai/hooks/inject-os-reminder.ps1',
|
|
31
|
+
'.ai/hooks/inject-os-reminder.sh',
|
|
32
|
+
'.ai/hooks/guard-edit.ps1',
|
|
33
|
+
'.ai/hooks/guard-edit.sh',
|
|
34
|
+
'.ai/hooks/enforce-audit.ps1',
|
|
35
|
+
'.ai/hooks/enforce-audit.sh',
|
|
36
|
+
'.claude/settings.json'
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
# Verbos de escrita (regex); se houver match no subcomando + path protegido no mesmo, bloqueia.
|
|
40
|
+
$writeVerbPatterns = @(
|
|
41
|
+
'Set-Content', 'Out-File', 'Add-Content', 'Clear-Content',
|
|
42
|
+
'Tee-Object', 'New-Item', 'Remove-Item', 'Move-Item', 'Copy-Item',
|
|
43
|
+
'fs\.writeFile', 'fs\.appendFile', 'fs\.unlink', 'fs\.rm',
|
|
44
|
+
'fs\.rmdir', '\brm\s', '\bmv\s', '\bcp\s', '\btee\s',
|
|
45
|
+
'sed\s+-i', '\bawk\b.*>', '>\s*[^\s|&]', '>>\s*[^\s|&]'
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
function Test-ProtectedPath([string]$path) {
|
|
49
|
+
if (-not $path) { return $false }
|
|
50
|
+
$norm = ($path -replace '\\', '/').ToLower()
|
|
51
|
+
foreach ($p in $protectedSubstrings) {
|
|
52
|
+
if ($norm.Contains($p.ToLower())) { return $true }
|
|
53
|
+
}
|
|
54
|
+
return $false
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function Split-Subcommands([string]$cmd) {
|
|
58
|
+
# Divide o comando em subcomandos por separadores: && || ; | newline
|
|
59
|
+
# Nao quebra dentro de strings (heuristica simples — suficiente para uso geral).
|
|
60
|
+
$parts = [System.Collections.ArrayList]::new()
|
|
61
|
+
$buf = ''
|
|
62
|
+
$inSingle = $false
|
|
63
|
+
$inDouble = $false
|
|
64
|
+
$i = 0
|
|
65
|
+
while ($i -lt $cmd.Length) {
|
|
66
|
+
$c = $cmd[$i]
|
|
67
|
+
$next = if ($i + 1 -lt $cmd.Length) { $cmd[$i + 1] } else { $null }
|
|
68
|
+
if ($c -eq "'" -and -not $inDouble) { $inSingle = -not $inSingle; $buf += $c; $i++; continue }
|
|
69
|
+
if ($c -eq '"' -and -not $inSingle) { $inDouble = -not $inDouble; $buf += $c; $i++; continue }
|
|
70
|
+
if (-not $inSingle -and -not $inDouble) {
|
|
71
|
+
if (($c -eq '&' -and $next -eq '&') -or ($c -eq '|' -and $next -eq '|')) {
|
|
72
|
+
$null = $parts.Add($buf); $buf = ''; $i += 2; continue
|
|
73
|
+
}
|
|
74
|
+
if ($c -eq ';' -or $c -eq '|' -or $c -eq "`n") {
|
|
75
|
+
$null = $parts.Add($buf); $buf = ''; $i++; continue
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
$buf += $c
|
|
79
|
+
$i++
|
|
80
|
+
}
|
|
81
|
+
if ($buf.Length -gt 0) { $null = $parts.Add($buf) }
|
|
82
|
+
return ,$parts
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function Find-WriteToProtected([string]$subcmd) {
|
|
86
|
+
# Retorna o path protegido se este subcomando contem write verb + path; senao $null.
|
|
87
|
+
# Allowlist: subcomandos comecando com leitura (grep/cat/etc) NUNCA bloqueiam,
|
|
88
|
+
# mesmo que tenham strings como 'Set-Content' como argumento.
|
|
89
|
+
$trimmed = $subcmd.TrimStart()
|
|
90
|
+
$readOnlyPrefixes = @(
|
|
91
|
+
'grep', 'egrep', 'fgrep', 'rg', 'ag', 'cat', 'head', 'tail',
|
|
92
|
+
'less', 'more', 'ls', 'find', 'wc', 'stat', 'file', 'which',
|
|
93
|
+
'type', 'echo', 'printf',
|
|
94
|
+
'Get-Content', 'Get-ChildItem', 'Select-String', 'Test-Path',
|
|
95
|
+
'Resolve-Path', 'Get-Item', 'gci', 'gc', 'sls'
|
|
96
|
+
)
|
|
97
|
+
# Allowlist so vale se NAO houver redirect de escrita (> ou >>) no subcomando.
|
|
98
|
+
$hasRedirect = ($subcmd -match '>\s*[^\s|&]')
|
|
99
|
+
if (-not $hasRedirect) {
|
|
100
|
+
foreach ($pfx in $readOnlyPrefixes) {
|
|
101
|
+
if ($trimmed -match "^$([regex]::Escape($pfx))(\s|$)") { return $null }
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
$hasWriteVerb = $false
|
|
106
|
+
foreach ($v in $writeVerbPatterns) {
|
|
107
|
+
if ($subcmd -match $v) { $hasWriteVerb = $true; break }
|
|
108
|
+
}
|
|
109
|
+
if (-not $hasWriteVerb) { return $null }
|
|
110
|
+
$norm = ($subcmd -replace '\\', '/').ToLower()
|
|
111
|
+
foreach ($p in $protectedSubstrings) {
|
|
112
|
+
if ($norm.Contains($p.ToLower())) { return $p }
|
|
113
|
+
}
|
|
114
|
+
return $null
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function Deny-Edit([string]$target, [string]$source) {
|
|
118
|
+
$reason = @"
|
|
119
|
+
[OS GUARD] Arquivo protegido bloqueado: $target (via $source)
|
|
120
|
+
|
|
121
|
+
Este arquivo faz parte do nucleo do AI Operating System.
|
|
122
|
+
|
|
123
|
+
Para prosseguir, escolha UMA das opcoes:
|
|
124
|
+
1. Criar ADR em .ai/memory/decisions.md justificando a mudanca + pedir confirmacao explicita do usuario no formato 'Confirmo alteracao em <path> - motivo: <razao>'.
|
|
125
|
+
2. Bypass autorizado: o usuario cria o arquivo .claude/.unlock (basta existir). O guard fica inativo enquanto o arquivo existir.
|
|
126
|
+
|
|
127
|
+
Apos a mudanca, recomenda-se remover .claude/.unlock para restaurar a protecao.
|
|
128
|
+
"@
|
|
129
|
+
$response = @{
|
|
130
|
+
hookSpecificOutput = @{
|
|
131
|
+
hookEventName = 'PreToolUse'
|
|
132
|
+
permissionDecision = 'deny'
|
|
133
|
+
permissionDecisionReason = $reason
|
|
134
|
+
}
|
|
135
|
+
} | ConvertTo-Json -Depth 5 -Compress
|
|
136
|
+
Write-Output $response
|
|
137
|
+
exit 0
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
# Branch 1: Edit / Write / NotebookEdit — payload.tool_input.file_path
|
|
141
|
+
if ($toolName -in @('Edit', 'Write', 'NotebookEdit')) {
|
|
142
|
+
$filePath = $payload.tool_input.file_path
|
|
143
|
+
if ($filePath -and (Test-ProtectedPath $filePath)) {
|
|
144
|
+
Deny-Edit -target $filePath -source $toolName
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
# Branch 2: Bash — split em subcomandos, bloqueia apenas se write verb + path protegido coexistirem no MESMO subcomando.
|
|
149
|
+
if ($toolName -eq 'Bash') {
|
|
150
|
+
$cmd = $payload.tool_input.command
|
|
151
|
+
if ($cmd) {
|
|
152
|
+
$subs = Split-Subcommands $cmd
|
|
153
|
+
foreach ($s in $subs) {
|
|
154
|
+
$hit = Find-WriteToProtected $s
|
|
155
|
+
if ($hit) {
|
|
156
|
+
Deny-Edit -target $hit -source 'Bash'
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
# Aviso leve: editar codigo de producao sem ter consultado project-context.md
|
|
163
|
+
if ($toolName -in @('Edit', 'Write', 'NotebookEdit')) {
|
|
164
|
+
$filePath = $payload.tool_input.file_path
|
|
165
|
+
if ($filePath) {
|
|
166
|
+
$markerPath = Join-Path $projectDir '.claude\.os-context-loaded'
|
|
167
|
+
$contextPath = Join-Path $projectDir '.ai\memory\project-context.md'
|
|
168
|
+
$isProductionEdit = ($filePath -notmatch '\.ai[\\/]') -and ($filePath -notmatch '(?i)(test|spec|\.md)$')
|
|
169
|
+
if ($isProductionEdit -and (Test-Path $contextPath) -and (-not (Test-Path $markerPath))) {
|
|
170
|
+
$warning = @{
|
|
171
|
+
hookSpecificOutput = @{
|
|
172
|
+
hookEventName = 'PreToolUse'
|
|
173
|
+
additionalContext = '[OS WARNING] Voce esta editando codigo de producao mas project-context.md ainda nao foi consultado nesta sessao. Considere ler antes de prosseguir.'
|
|
174
|
+
}
|
|
175
|
+
} | ConvertTo-Json -Depth 5 -Compress
|
|
176
|
+
Write-Output $warning
|
|
177
|
+
exit 0
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
exit 0
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# PreToolUse hook (Unix) — matcher: Edit|Write|NotebookEdit|Bash
|
|
3
|
+
# Bloqueia mudancas em arquivos protegidos do AI OS sem confirmacao explicita.
|
|
4
|
+
# Para Bash, faz split em SUBCOMANDOS (&&, ||, ;, |, newline) e bloqueia
|
|
5
|
+
# apenas o subcomando que contem write verb + path protegido juntos.
|
|
6
|
+
# Override autorizado: criar .claude/.unlock (basta existir).
|
|
7
|
+
set -euo pipefail
|
|
8
|
+
|
|
9
|
+
PROJECT_DIR="${CLAUDE_PROJECT_DIR:-$(pwd)}"
|
|
10
|
+
|
|
11
|
+
# Bypass via sentinela
|
|
12
|
+
if [[ -f "$PROJECT_DIR/.claude/.unlock" ]]; then
|
|
13
|
+
exit 0
|
|
14
|
+
fi
|
|
15
|
+
|
|
16
|
+
PAYLOAD=$(cat)
|
|
17
|
+
|
|
18
|
+
# Extractor JSON: node primeiro (mais robusto), depois python3 real (nao stub MS Store), depois grep.
|
|
19
|
+
extract_field() {
|
|
20
|
+
local field="$1"
|
|
21
|
+
if command -v node &>/dev/null; then
|
|
22
|
+
echo "$PAYLOAD" | node -e "let s='';process.stdin.on('data',d=>s+=d).on('end',()=>{try{const o=JSON.parse(s);const f='$field';console.log(f==='tool_name'?o.tool_name||'':o.tool_input?.[f]||'')}catch{}})" 2>/dev/null && return 0
|
|
23
|
+
fi
|
|
24
|
+
if command -v python3 &>/dev/null && python3 -c "print('ok')" 2>/dev/null | grep -q '^ok$'; then
|
|
25
|
+
echo "$PAYLOAD" | python3 -c "import sys,json
|
|
26
|
+
try:
|
|
27
|
+
d=json.load(sys.stdin)
|
|
28
|
+
f='$field'
|
|
29
|
+
print(d.get('tool_name','') if f=='tool_name' else d.get('tool_input',{}).get(f,''))
|
|
30
|
+
except: pass" 2>/dev/null && return 0
|
|
31
|
+
fi
|
|
32
|
+
echo "$PAYLOAD" | grep -oE "\"$field\"[[:space:]]*:[[:space:]]*\"([^\"\\\\]|\\\\.)*\"" | sed 's/^"[^"]*"[[:space:]]*:[[:space:]]*"//; s/"$//' | head -1
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
TOOL_NAME=$(extract_field tool_name)
|
|
36
|
+
|
|
37
|
+
PROTECTED=(
|
|
38
|
+
'.ai/rules/dont.md'
|
|
39
|
+
'.ai/contract.md'
|
|
40
|
+
'.ai/router.md'
|
|
41
|
+
'.ai/manifest.json'
|
|
42
|
+
'.ai/hooks/inject-os-reminder.ps1'
|
|
43
|
+
'.ai/hooks/inject-os-reminder.sh'
|
|
44
|
+
'.ai/hooks/guard-edit.ps1'
|
|
45
|
+
'.ai/hooks/guard-edit.sh'
|
|
46
|
+
'.ai/hooks/enforce-audit.ps1'
|
|
47
|
+
'.ai/hooks/enforce-audit.sh'
|
|
48
|
+
'.claude/settings.json'
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
WRITE_VERBS='(Set-Content|Out-File|Add-Content|Clear-Content|Tee-Object|New-Item|Remove-Item|Move-Item|Copy-Item|fs\.writeFile|fs\.appendFile|fs\.unlink|fs\.rm|fs\.rmdir|\brm[[:space:]]|\bmv[[:space:]]|\bcp[[:space:]]|\btee[[:space:]]|sed[[:space:]]+-i|>[[:space:]]*[^[:space:]|&]|>>[[:space:]]*[^[:space:]|&])'
|
|
52
|
+
|
|
53
|
+
is_protected_path() {
|
|
54
|
+
local norm="${1//\\//}"
|
|
55
|
+
norm="$(echo "$norm" | tr '[:upper:]' '[:lower:]')"
|
|
56
|
+
for p in "${PROTECTED[@]}"; do
|
|
57
|
+
if [[ "$norm" == *"$p"* ]]; then
|
|
58
|
+
return 0
|
|
59
|
+
fi
|
|
60
|
+
done
|
|
61
|
+
return 1
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
# Retorna 0 (true) e ecoa o path se o subcomando tem write verb + path protegido.
|
|
65
|
+
# Allowlist: subcomandos comecando com leitura (grep/cat/etc) NUNCA bloqueiam.
|
|
66
|
+
READ_ONLY_PREFIXES='^[[:space:]]*(grep|egrep|fgrep|rg|ag|cat|head|tail|less|more|ls|find|wc|stat|file|which|type|echo|printf|Get-Content|Get-ChildItem|Select-String|Test-Path|Resolve-Path|Get-Item|gci|gc|sls)([[:space:]]|$)'
|
|
67
|
+
|
|
68
|
+
find_write_to_protected() {
|
|
69
|
+
local sub="$1"
|
|
70
|
+
# Se o subcomando NAO contem redirect de escrita E comeca com prefixo read-only, nao bloqueia.
|
|
71
|
+
if ! echo "$sub" | grep -Eq '>[[:space:]]*[^[:space:]|&]'; then
|
|
72
|
+
if echo "$sub" | grep -Eq "$READ_ONLY_PREFIXES"; then
|
|
73
|
+
return 1
|
|
74
|
+
fi
|
|
75
|
+
fi
|
|
76
|
+
if ! echo "$sub" | grep -Eq "$WRITE_VERBS"; then
|
|
77
|
+
return 1
|
|
78
|
+
fi
|
|
79
|
+
local norm
|
|
80
|
+
norm=$(echo "${sub//\\//}" | tr '[:upper:]' '[:lower:]')
|
|
81
|
+
for p in "${PROTECTED[@]}"; do
|
|
82
|
+
if [[ "$norm" == *"$p"* ]]; then
|
|
83
|
+
echo "$p"
|
|
84
|
+
return 0
|
|
85
|
+
fi
|
|
86
|
+
done
|
|
87
|
+
return 1
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
# Split em subcomandos: usa awk com FS multi-char.
|
|
91
|
+
# Separadores: && || ; | \n (heuristica: nao quebra dentro de aspas — suficiente para uso geral)
|
|
92
|
+
split_subcommands() {
|
|
93
|
+
local cmd="$1"
|
|
94
|
+
# Substitui separadores por \0 e divide.
|
|
95
|
+
echo "$cmd" | awk '
|
|
96
|
+
BEGIN { in_single=0; in_double=0; buf="" }
|
|
97
|
+
{
|
|
98
|
+
s = $0
|
|
99
|
+
for (i = 1; i <= length(s); i++) {
|
|
100
|
+
c = substr(s, i, 1)
|
|
101
|
+
n = (i < length(s)) ? substr(s, i+1, 1) : ""
|
|
102
|
+
if (c == "\047" && !in_double) { in_single = !in_single; buf = buf c; continue }
|
|
103
|
+
if (c == "\"" && !in_single) { in_double = !in_double; buf = buf c; continue }
|
|
104
|
+
if (!in_single && !in_double) {
|
|
105
|
+
if ((c == "&" && n == "&") || (c == "|" && n == "|")) {
|
|
106
|
+
print buf; buf = ""; i++; continue
|
|
107
|
+
}
|
|
108
|
+
if (c == ";" || c == "|") {
|
|
109
|
+
print buf; buf = ""; continue
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
buf = buf c
|
|
113
|
+
}
|
|
114
|
+
if (length(buf) > 0) { print buf; buf = "" }
|
|
115
|
+
}'
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
deny() {
|
|
119
|
+
local target="$1"
|
|
120
|
+
local source="$2"
|
|
121
|
+
local REASON="[OS GUARD] Arquivo protegido bloqueado: $target (via $source). Este arquivo faz parte do nucleo do AI Operating System. Para prosseguir: 1) Criar ADR em .ai/memory/decisions.md + confirmacao explicita do usuario no formato 'Confirmo alteracao em <path> - motivo: <razao>'. OU 2) Bypass autorizado: usuario cria .claude/.unlock (basta existir; remover apos a mudanca para restaurar protecao)."
|
|
122
|
+
printf '{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"deny","permissionDecisionReason":"%s"}}\n' "$REASON"
|
|
123
|
+
exit 0
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
case "$TOOL_NAME" in
|
|
127
|
+
Edit|Write|NotebookEdit)
|
|
128
|
+
FILE_PATH=$(extract_field file_path)
|
|
129
|
+
if [[ -n "$FILE_PATH" ]] && is_protected_path "$FILE_PATH"; then
|
|
130
|
+
deny "$FILE_PATH" "$TOOL_NAME"
|
|
131
|
+
fi
|
|
132
|
+
;;
|
|
133
|
+
Bash)
|
|
134
|
+
CMD=$(extract_field command)
|
|
135
|
+
if [[ -n "$CMD" ]]; then
|
|
136
|
+
while IFS= read -r sub; do
|
|
137
|
+
[[ -z "$sub" ]] && continue
|
|
138
|
+
hit=$(find_write_to_protected "$sub" || true)
|
|
139
|
+
if [[ -n "$hit" ]]; then
|
|
140
|
+
deny "$hit" "Bash"
|
|
141
|
+
fi
|
|
142
|
+
done < <(split_subcommands "$CMD")
|
|
143
|
+
fi
|
|
144
|
+
;;
|
|
145
|
+
esac
|
|
146
|
+
|
|
147
|
+
# Aviso leve para edicao de producao sem ter consultado project-context.md
|
|
148
|
+
if [[ "$TOOL_NAME" == "Edit" || "$TOOL_NAME" == "Write" || "$TOOL_NAME" == "NotebookEdit" ]]; then
|
|
149
|
+
FILE_PATH=$(extract_field file_path)
|
|
150
|
+
MARKER="$PROJECT_DIR/.claude/.os-context-loaded"
|
|
151
|
+
CONTEXT="$PROJECT_DIR/.ai/memory/project-context.md"
|
|
152
|
+
if [[ -n "$FILE_PATH" && -f "$CONTEXT" && ! -f "$MARKER" ]] && \
|
|
153
|
+
echo "$FILE_PATH" | grep -vqE '\.ai[\\/]' && \
|
|
154
|
+
echo "$FILE_PATH" | grep -vqiE '(test|spec|\.md)$'; then
|
|
155
|
+
WARN='[OS WARNING] Voce esta editando codigo de producao mas project-context.md ainda nao foi consultado nesta sessao. Considere ler antes de prosseguir.'
|
|
156
|
+
printf '{"hookSpecificOutput":{"hookEventName":"PreToolUse","additionalContext":"%s"}}\n' "$WARN"
|
|
157
|
+
exit 0
|
|
158
|
+
fi
|
|
159
|
+
fi
|
|
160
|
+
|
|
161
|
+
exit 0
|