@luanpdd/kit-mcp 1.29.0 → 1.30.1
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/LICENSE +21 -21
- package/README.md +168 -168
- package/gates/agent-no-recursive-dispatch.md +82 -82
- package/kit/COMANDOS.md +138 -138
- package/kit/README.md +76 -76
- package/kit/agents/advisor-researcher.md +106 -106
- package/kit/agents/assumptions-analyzer.md +107 -107
- package/kit/agents/audit-log-implementer.md +313 -313
- package/kit/agents/auditor-consistencia-isolamento.md +413 -413
- package/kit/agents/b2b-saas-architect.md +156 -156
- package/kit/agents/cascading-failures-auditor.md +298 -298
- package/kit/agents/codebase-mapper.md +768 -768
- package/kit/agents/crm-pipeline-implementer.md +256 -256
- package/kit/agents/debugger.md +813 -813
- package/kit/agents/detector-tenant-quente.md +337 -337
- package/kit/agents/evolution-go-integrator.md +200 -200
- package/kit/agents/example-reviewer.md +21 -21
- package/kit/agents/executor.md +564 -564
- package/kit/agents/integration-checker.md +200 -200
- package/kit/agents/invite-flow-implementer.md +189 -189
- package/kit/agents/legacy-characterizer.md +368 -368
- package/kit/agents/lgpd-compliance-auditor.md +295 -295
- package/kit/agents/multi-tenant-isolation-auditor.md +253 -253
- package/kit/agents/multi-tenant-rls-writer.md +340 -340
- package/kit/agents/nyquist-auditor.md +178 -178
- package/kit/agents/observability-coverage-auditor.md +315 -315
- package/kit/agents/org-onboarding-implementer.md +223 -223
- package/kit/agents/payload-capture-instrumenter.md +273 -273
- package/kit/agents/phase-researcher.md +696 -696
- package/kit/agents/plan-checker.md +272 -272
- package/kit/agents/planner.md +922 -922
- package/kit/agents/project-researcher.md +652 -652
- package/kit/agents/refactor-safety-auditor.md +404 -404
- package/kit/agents/research-synthesizer.md +245 -245
- package/kit/agents/roadmapper.md +677 -677
- package/kit/agents/seam-finder.md +359 -359
- package/kit/agents/shotgun-surgery-detector.md +349 -349
- package/kit/agents/supabase-branching-architect.md +562 -562
- package/kit/agents/supabase-cicd-pipeline-implementer.md +777 -777
- package/kit/agents/supabase-column-privileges-writer.md +399 -399
- package/kit/agents/supabase-edge-fn-tester.md +287 -0
- package/kit/agents/supabase-edge-fn-writer.md +239 -210
- package/kit/agents/supabase-migration-writer.md +385 -385
- package/kit/agents/supabase-rbac-implementer.md +392 -392
- package/kit/agents/supabase-realtime-implementer.md +363 -267
- package/kit/agents/supabase-rls-hardener.md +521 -521
- package/kit/agents/supabase-rls-writer.md +323 -323
- package/kit/agents/supabase-roles-implementer.md +355 -355
- package/kit/agents/super-admin-implementer.md +281 -281
- package/kit/agents/ui-auditor.md +437 -437
- package/kit/agents/ui-checker.md +302 -302
- package/kit/agents/ui-researcher.md +355 -355
- package/kit/agents/user-profiler.md +175 -175
- package/kit/agents/validador-evolucao-schema.md +335 -335
- package/kit/agents/verifier.md +728 -728
- package/kit/commands/adicionar-backlog.md +75 -75
- package/kit/commands/adicionar-fase.md +42 -42
- package/kit/commands/adicionar-tarefa.md +45 -45
- package/kit/commands/adicionar-testes.md +41 -41
- package/kit/commands/ajuda.md +21 -21
- package/kit/commands/atualizar.md +37 -37
- package/kit/commands/auditar-cascading.md +111 -111
- package/kit/commands/auditar-marco.md +179 -179
- package/kit/commands/auditar-observabilidade-cobertura.md +183 -183
- package/kit/commands/auditar-refactor.md +219 -219
- package/kit/commands/auditar-release.md +109 -109
- package/kit/commands/auditar-uat.md +23 -23
- package/kit/commands/autonomo.md +40 -40
- package/kit/commands/branch-pr.md +24 -24
- package/kit/commands/burn-rate-status.md +408 -408
- package/kit/commands/capturar-payloads.md +193 -193
- package/kit/commands/caracterizar.md +212 -212
- package/kit/commands/concluir-marco.md +247 -247
- package/kit/commands/configuracoes.md +36 -36
- package/kit/commands/dados-distribuidos.md +188 -188
- package/kit/commands/definir-perfil.md +10 -10
- package/kit/commands/depurar.md +190 -190
- package/kit/commands/detectar-duplicacao.md +197 -197
- package/kit/commands/discutir-fase.md +131 -131
- package/kit/commands/encontrar-seams.md +136 -136
- package/kit/commands/entrar-discord.md +17 -17
- package/kit/commands/estatisticas.md +18 -18
- package/kit/commands/example-greeting.md +33 -33
- package/kit/commands/executar-fase.md +58 -58
- package/kit/commands/expresso.md +56 -56
- package/kit/commands/fase-ui.md +34 -34
- package/kit/commands/fazer.md +57 -57
- package/kit/commands/fio.md +125 -125
- package/kit/commands/fluxos-trabalho.md +64 -64
- package/kit/commands/forense.md +176 -176
- package/kit/commands/gerenciador.md +38 -38
- package/kit/commands/inserir-fase.md +31 -31
- package/kit/commands/legacy.md +263 -263
- package/kit/commands/limpeza.md +17 -17
- package/kit/commands/listar-hipoteses-fase.md +45 -45
- package/kit/commands/listar-workspaces.md +18 -18
- package/kit/commands/load-shedding.md +117 -117
- package/kit/commands/mapear-codebase.md +70 -70
- package/kit/commands/multi-tenant.md +163 -163
- package/kit/commands/nota.md +33 -33
- package/kit/commands/novo-marco.md +43 -43
- package/kit/commands/novo-projeto.md +41 -41
- package/kit/commands/novo-workspace.md +43 -43
- package/kit/commands/pausar-trabalho.md +37 -37
- package/kit/commands/perfil-usuario.md +45 -45
- package/kit/commands/pesquisar-fase.md +195 -195
- package/kit/commands/planejar-fase.md +67 -67
- package/kit/commands/planejar-lacunas.md +33 -33
- package/kit/commands/plantar-ideia.md +25 -25
- package/kit/commands/progresso.md +24 -24
- package/kit/commands/proximo.md +30 -30
- package/kit/commands/publicar.md +490 -490
- package/kit/commands/rapido.md +35 -35
- package/kit/commands/reaplicar-patches.md +124 -124
- package/kit/commands/refactor-seguro.md +321 -321
- package/kit/commands/relatorio-sessao.md +19 -19
- package/kit/commands/remover-fase.md +31 -31
- package/kit/commands/remover-workspace.md +26 -26
- package/kit/commands/resumo-marco.md +50 -50
- package/kit/commands/retomar-trabalho.md +40 -40
- package/kit/commands/revisar-backlog.md +60 -60
- package/kit/commands/revisar-ui.md +32 -32
- package/kit/commands/revisar.md +37 -37
- package/kit/commands/saude.md +21 -21
- package/kit/commands/setup-notion.md +93 -93
- package/kit/commands/storytelling.md +179 -179
- package/kit/commands/supabase.md +30 -7
- package/kit/commands/sync-main.md +68 -68
- package/kit/commands/validar-fase.md +35 -35
- package/kit/commands/verificar-tarefas.md +44 -44
- package/kit/commands/verificar-trabalho.md +64 -64
- package/kit/file-manifest.json +15 -8
- package/kit/framework/bin/lib/commands.cjs +959 -959
- package/kit/framework/bin/lib/config.cjs +442 -442
- package/kit/framework/bin/lib/core.cjs +1230 -1230
- package/kit/framework/bin/lib/frontmatter.cjs +336 -336
- package/kit/framework/bin/lib/init.cjs +1442 -1442
- package/kit/framework/bin/lib/milestone.cjs +252 -252
- package/kit/framework/bin/lib/model-profiles.cjs +68 -68
- package/kit/framework/bin/lib/phase.cjs +888 -888
- package/kit/framework/bin/lib/profile-output.cjs +952 -952
- package/kit/framework/bin/lib/profile-pipeline.cjs +539 -539
- package/kit/framework/bin/lib/roadmap.cjs +329 -329
- package/kit/framework/bin/lib/security.cjs +382 -382
- package/kit/framework/bin/lib/state.cjs +1031 -1031
- package/kit/framework/bin/lib/template.cjs +222 -222
- package/kit/framework/bin/lib/uat.cjs +282 -282
- package/kit/framework/bin/lib/verify.cjs +888 -888
- package/kit/framework/bin/lib/workstream.cjs +491 -491
- package/kit/framework/bin/tools.cjs +918 -918
- package/kit/framework/commands/workstreams.md +63 -63
- package/kit/framework/references/checkpoints.md +778 -778
- package/kit/framework/references/continuation-format.md +249 -249
- package/kit/framework/references/decimal-phase-calculation.md +64 -64
- package/kit/framework/references/git-integration.md +295 -295
- package/kit/framework/references/git-planning-commit.md +38 -38
- package/kit/framework/references/model-profile-resolution.md +36 -36
- package/kit/framework/references/model-profiles.md +139 -139
- package/kit/framework/references/phase-argument-parsing.md +61 -61
- package/kit/framework/references/planning-config.md +202 -202
- package/kit/framework/references/questioning.md +162 -162
- package/kit/framework/references/tdd.md +263 -263
- package/kit/framework/references/ui-brand.md +160 -160
- package/kit/framework/references/user-profiling.md +657 -657
- package/kit/framework/references/verification-patterns.md +612 -612
- package/kit/framework/references/workstream-flag.md +58 -58
- package/kit/framework/templates/DEBUG.md +164 -164
- package/kit/framework/templates/UAT.md +265 -265
- package/kit/framework/templates/UI-SPEC.md +100 -100
- package/kit/framework/templates/VALIDATION.md +76 -76
- package/kit/framework/templates/claude-md.md +122 -122
- package/kit/framework/templates/codebase/architecture.md +185 -185
- package/kit/framework/templates/codebase/concerns.md +205 -205
- package/kit/framework/templates/codebase/conventions.md +204 -204
- package/kit/framework/templates/codebase/integrations.md +192 -192
- package/kit/framework/templates/codebase/stack.md +158 -158
- package/kit/framework/templates/codebase/structure.md +199 -199
- package/kit/framework/templates/codebase/testing.md +301 -301
- package/kit/framework/templates/config.json +44 -44
- package/kit/framework/templates/context.md +352 -352
- package/kit/framework/templates/continue-here.md +78 -78
- package/kit/framework/templates/copilot-instructions.md +7 -7
- package/kit/framework/templates/debug-subagent-prompt.md +91 -91
- package/kit/framework/templates/dev-preferences.md +20 -20
- package/kit/framework/templates/discovery.md +146 -146
- package/kit/framework/templates/discussion-log.md +63 -63
- package/kit/framework/templates/milestone-archive.md +123 -123
- package/kit/framework/templates/milestone.md +115 -115
- package/kit/framework/templates/phase-prompt.md +610 -610
- package/kit/framework/templates/planner-subagent-prompt.md +117 -117
- package/kit/framework/templates/project.md +186 -186
- package/kit/framework/templates/requirements.md +231 -231
- package/kit/framework/templates/research-project/ARCHITECTURE.md +204 -204
- package/kit/framework/templates/research-project/FEATURES.md +147 -147
- package/kit/framework/templates/research-project/PITFALLS.md +200 -200
- package/kit/framework/templates/research-project/STACK.md +120 -120
- package/kit/framework/templates/research-project/SUMMARY.md +170 -170
- package/kit/framework/templates/research.md +419 -419
- package/kit/framework/templates/retrospective.md +54 -54
- package/kit/framework/templates/roadmap.md +202 -202
- package/kit/framework/templates/state.md +176 -176
- package/kit/framework/templates/summary-complex.md +59 -59
- package/kit/framework/templates/summary-minimal.md +41 -41
- package/kit/framework/templates/summary-standard.md +48 -48
- package/kit/framework/templates/summary.md +209 -209
- package/kit/framework/templates/user-profile.md +146 -146
- package/kit/framework/templates/user-setup.md +256 -256
- package/kit/framework/templates/verification-report.md +258 -258
- package/kit/framework/workflows/add-phase.md +112 -112
- package/kit/framework/workflows/add-tests.md +351 -351
- package/kit/framework/workflows/add-todo.md +158 -158
- package/kit/framework/workflows/audit-milestone.md +340 -340
- package/kit/framework/workflows/audit-uat.md +109 -109
- package/kit/framework/workflows/autonomous.md +891 -891
- package/kit/framework/workflows/check-todos.md +177 -177
- package/kit/framework/workflows/cleanup.md +152 -152
- package/kit/framework/workflows/complete-milestone.md +696 -696
- package/kit/framework/workflows/diagnose-issues.md +231 -231
- package/kit/framework/workflows/discovery-phase.md +289 -289
- package/kit/framework/workflows/discuss-phase-assumptions.md +653 -653
- package/kit/framework/workflows/discuss-phase.md +784 -784
- package/kit/framework/workflows/do.md +104 -104
- package/kit/framework/workflows/execute-phase.md +838 -838
- package/kit/framework/workflows/execute-plan.md +510 -510
- package/kit/framework/workflows/fast.md +102 -102
- package/kit/framework/workflows/forensics.md +265 -265
- package/kit/framework/workflows/health.md +181 -181
- package/kit/framework/workflows/help.md +619 -619
- package/kit/framework/workflows/insert-phase.md +130 -130
- package/kit/framework/workflows/list-phase-assumptions.md +178 -178
- package/kit/framework/workflows/list-workspaces.md +56 -56
- package/kit/framework/workflows/manager.md +362 -362
- package/kit/framework/workflows/map-codebase.md +377 -377
- package/kit/framework/workflows/milestone-summary.md +223 -223
- package/kit/framework/workflows/new-milestone.md +486 -486
- package/kit/framework/workflows/new-project.md +1159 -1159
- package/kit/framework/workflows/new-workspace.md +237 -237
- package/kit/framework/workflows/next.md +97 -97
- package/kit/framework/workflows/node-repair.md +92 -92
- package/kit/framework/workflows/note.md +156 -156
- package/kit/framework/workflows/pause-work.md +176 -176
- package/kit/framework/workflows/plan-milestone-gaps.md +273 -273
- package/kit/framework/workflows/plan-phase.md +765 -765
- package/kit/framework/workflows/plant-seed.md +169 -169
- package/kit/framework/workflows/pr-branch.md +129 -129
- package/kit/framework/workflows/profile-user.md +450 -450
- package/kit/framework/workflows/progress.md +507 -507
- package/kit/framework/workflows/quick.md +757 -757
- package/kit/framework/workflows/remove-phase.md +155 -155
- package/kit/framework/workflows/remove-workspace.md +90 -90
- package/kit/framework/workflows/research-phase.md +82 -82
- package/kit/framework/workflows/resume-project.md +326 -326
- package/kit/framework/workflows/review.md +228 -228
- package/kit/framework/workflows/session-report.md +146 -146
- package/kit/framework/workflows/settings.md +283 -283
- package/kit/framework/workflows/ship.md +228 -228
- package/kit/framework/workflows/stats.md +60 -60
- package/kit/framework/workflows/transition.md +671 -671
- package/kit/framework/workflows/ui-phase.md +302 -302
- package/kit/framework/workflows/ui-review.md +165 -165
- package/kit/framework/workflows/update.md +323 -323
- package/kit/framework/workflows/validate-phase.md +174 -174
- package/kit/framework/workflows/verify-phase.md +252 -252
- package/kit/framework/workflows/verify-work.md +637 -637
- package/kit/hooks/check-update.js +118 -118
- package/kit/hooks/context-monitor.js +163 -163
- package/kit/hooks/kit-attribution-reminder.cjs +98 -0
- package/kit/hooks/prompt-guard.js +103 -103
- package/kit/hooks/statusline.js +125 -125
- package/kit/hooks/workflow-guard.js +101 -101
- package/kit/settings.json +45 -45
- package/kit/skills/_shared-supabase/glossary.md +17 -0
- package/kit/skills/ai-prompt-characterization/SKILL.md +335 -335
- package/kit/skills/armadilhas-sistemas-distribuidos/SKILL.md +447 -447
- package/kit/skills/audit-log-multi-tenant/SKILL.md +340 -340
- package/kit/skills/b2b-saas-architecture/SKILL.md +300 -300
- package/kit/skills/consistencia-leitura-replica/SKILL.md +385 -385
- package/kit/skills/crm-lead-pipeline-patterns/SKILL.md +343 -343
- package/kit/skills/escolha-modelo-consistencia/SKILL.md +494 -494
- package/kit/skills/evolucao-schema-compativel/SKILL.md +448 -448
- package/kit/skills/evolution-go-whatsapp-integration/SKILL.md +322 -322
- package/kit/skills/example-skill/SKILL.md +42 -42
- package/kit/skills/legacy-api-only-applications/SKILL.md +358 -358
- package/kit/skills/legacy-characterization-tests/SKILL.md +330 -330
- package/kit/skills/legacy-effect-analysis/SKILL.md +331 -331
- package/kit/skills/legacy-extract-class/SKILL.md +203 -203
- package/kit/skills/legacy-programming-by-difference/SKILL.md +252 -252
- package/kit/skills/legacy-seams-and-test-harness/SKILL.md +460 -460
- package/kit/skills/legacy-shotgun-surgery/SKILL.md +286 -286
- package/kit/skills/legacy-sprout-wrap-techniques/SKILL.md +434 -434
- package/kit/skills/legacy-storytelling-naked-crc/SKILL.md +270 -270
- package/kit/skills/lgpd-multi-tenant-compliance/SKILL.md +340 -340
- package/kit/skills/member-invite-flow/SKILL.md +305 -305
- package/kit/skills/member-management-react-shadcn/SKILL.md +328 -328
- package/kit/skills/multi-tenant-performance-scaling/SKILL.md +316 -316
- package/kit/skills/multi-tenant-rls-hierarchy/SKILL.md +342 -342
- package/kit/skills/org-onboarding-flow/SKILL.md +257 -257
- package/kit/skills/org-switcher-react-pattern/SKILL.md +349 -349
- package/kit/skills/permission-gate-react-pattern/SKILL.md +271 -271
- package/kit/skills/postgres-isolamento-concorrencia/SKILL.md +552 -552
- package/kit/skills/pre-refactor-characterization/SKILL.md +421 -421
- package/kit/skills/rbac-permissions-matrix-supabase/SKILL.md +338 -338
- package/kit/skills/streams-eventos-cdc/SKILL.md +711 -711
- package/kit/skills/supabase-branching-workflow/SKILL.md +544 -544
- package/kit/skills/supabase-ci-cd-github-actions/SKILL.md +880 -880
- package/kit/skills/supabase-column-level-security/SKILL.md +426 -426
- package/kit/skills/supabase-config-toml-remotes/SKILL.md +807 -807
- package/kit/skills/supabase-custom-claims-rbac/SKILL.md +472 -472
- package/kit/skills/supabase-edge-functions/SKILL.md +229 -141
- package/kit/skills/supabase-edge-functions-auth/SKILL.md +309 -0
- package/kit/skills/supabase-edge-functions-limits/SKILL.md +302 -0
- package/kit/skills/supabase-edge-functions-mcp-server/SKILL.md +279 -0
- package/kit/skills/supabase-edge-functions-testing/SKILL.md +277 -0
- package/kit/skills/supabase-edge-runtime-builtins/SKILL.md +357 -0
- package/kit/skills/supabase-migration-repair/SKILL.md +823 -823
- package/kit/skills/supabase-migrations/SKILL.md +297 -297
- package/kit/skills/supabase-pgtap-testing/SKILL.md +1053 -1053
- package/kit/skills/supabase-postgres-roles/SKILL.md +392 -392
- package/kit/skills/supabase-realtime/SKILL.md +460 -236
- package/kit/skills/supabase-rls-defense-in-depth/SKILL.md +418 -418
- package/kit/skills/supabase-rls-policies/SKILL.md +635 -635
- package/kit/skills/super-admin-platform-pattern/SKILL.md +326 -326
- package/kit/skills/tenant-quente-mitigacao/SKILL.md +605 -605
- package/kit/skills/whatsapp-conversation-state-machine/SKILL.md +287 -287
- package/package.json +1 -1
- package/src/core/kit.js +216 -216
- package/src/core/reflect.js +247 -247
- package/src/core/reverse-sync.js +372 -372
- package/src/core/sync.js +418 -418
- package/src/core/watch.js +121 -121
- package/src/mcp-server/index.js +715 -693
|
@@ -1,118 +1,118 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
// hook-version: 1.30.0
|
|
3
|
-
// SEC-13-05: flush-before-exit category = E (parent returns after spawn unref; child uses sync fs writes) — no fix needed
|
|
4
|
-
// Check for framework updates in background, write result to cache
|
|
5
|
-
// Called by SessionStart hook - runs once per session
|
|
6
|
-
|
|
7
|
-
const fs = require('fs');
|
|
8
|
-
const path = require('path');
|
|
9
|
-
const os = require('os');
|
|
10
|
-
const { spawn } = require('child_process');
|
|
11
|
-
|
|
12
|
-
const homeDir = os.homedir();
|
|
13
|
-
const cwd = process.cwd();
|
|
14
|
-
|
|
15
|
-
// Detect runtime config directory (supports Claude, OpenCode, Gemini)
|
|
16
|
-
// Respects CLAUDE_CONFIG_DIR for custom config directory setups
|
|
17
|
-
function detectConfigDir(baseDir) {
|
|
18
|
-
// Check env override first (supports multi-account setups)
|
|
19
|
-
const envDir = process.env.CLAUDE_CONFIG_DIR;
|
|
20
|
-
if (envDir && fs.existsSync(path.join(envDir, 'framework', 'VERSION'))) {
|
|
21
|
-
return envDir;
|
|
22
|
-
}
|
|
23
|
-
for (const dir of ['.config/opencode', '.opencode', '.gemini', '.claude']) {
|
|
24
|
-
if (fs.existsSync(path.join(baseDir, dir, 'framework', 'VERSION'))) {
|
|
25
|
-
return path.join(baseDir, dir);
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
return envDir || path.join(baseDir, '.claude');
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
const globalConfigDir = detectConfigDir(homeDir);
|
|
32
|
-
const projectConfigDir = detectConfigDir(cwd);
|
|
33
|
-
const cacheDir = path.join(globalConfigDir, 'cache');
|
|
34
|
-
const cacheFile = path.join(cacheDir, 'update-check.json');
|
|
35
|
-
|
|
36
|
-
// VERSION file locations (check project first, then global)
|
|
37
|
-
const projectVersionFile = path.join(projectConfigDir, 'framework', 'VERSION');
|
|
38
|
-
const globalVersionFile = path.join(globalConfigDir, 'framework', 'VERSION');
|
|
39
|
-
|
|
40
|
-
// Ensure cache directory exists
|
|
41
|
-
if (!fs.existsSync(cacheDir)) {
|
|
42
|
-
fs.mkdirSync(cacheDir, { recursive: true });
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
// Run check in background (spawn background process, windowsHide prevents console flash)
|
|
46
|
-
// SEC-13-05: parent process retorna imediatamente após child.unref() — não
|
|
47
|
-
// há buffered I/O no parent. Child usa fs.writeFileSync (sync), sem race.
|
|
48
|
-
// Categoria E na taxonomia da Phase 80.
|
|
49
|
-
const child = spawn(process.execPath, ['-e', `
|
|
50
|
-
const fs = require('fs');
|
|
51
|
-
const path = require('path');
|
|
52
|
-
const { execSync } = require('child_process');
|
|
53
|
-
|
|
54
|
-
const cacheFile = ${JSON.stringify(cacheFile)};
|
|
55
|
-
const projectVersionFile = ${JSON.stringify(projectVersionFile)};
|
|
56
|
-
const globalVersionFile = ${JSON.stringify(globalVersionFile)};
|
|
57
|
-
|
|
58
|
-
// Check project directory first (local install), then global
|
|
59
|
-
let installed = '0.0.0';
|
|
60
|
-
let configDir = '';
|
|
61
|
-
try {
|
|
62
|
-
if (fs.existsSync(projectVersionFile)) {
|
|
63
|
-
installed = fs.readFileSync(projectVersionFile, 'utf8').trim();
|
|
64
|
-
configDir = path.dirname(path.dirname(projectVersionFile));
|
|
65
|
-
} else if (fs.existsSync(globalVersionFile)) {
|
|
66
|
-
installed = fs.readFileSync(globalVersionFile, 'utf8').trim();
|
|
67
|
-
configDir = path.dirname(path.dirname(globalVersionFile));
|
|
68
|
-
}
|
|
69
|
-
} catch (e) {}
|
|
70
|
-
|
|
71
|
-
// Check for stale hooks — compare hook version headers against installed VERSION
|
|
72
|
-
// Hooks live inside framework/hooks/, not configDir/hooks/
|
|
73
|
-
let staleHooks = [];
|
|
74
|
-
if (configDir) {
|
|
75
|
-
const hooksDir = path.join(configDir, 'framework', 'hooks');
|
|
76
|
-
try {
|
|
77
|
-
if (fs.existsSync(hooksDir)) {
|
|
78
|
-
const hookFiles = fs.readdirSync(hooksDir).filter(f => f.startsWith('framework-') && f.endsWith('.js'));
|
|
79
|
-
for (const hookFile of hookFiles) {
|
|
80
|
-
try {
|
|
81
|
-
const content = fs.readFileSync(path.join(hooksDir, hookFile), 'utf8');
|
|
82
|
-
const versionMatch = content.match(/\\/\\/ hook-version:\\s*(.+)/);
|
|
83
|
-
if (versionMatch) {
|
|
84
|
-
const hookVersion = versionMatch[1].trim();
|
|
85
|
-
if (hookVersion !== installed && !hookVersion.includes('{{')) {
|
|
86
|
-
staleHooks.push({ file: hookFile, hookVersion, installedVersion: installed });
|
|
87
|
-
}
|
|
88
|
-
} else {
|
|
89
|
-
// No version header at all — definitely stale (pre-version-tracking)
|
|
90
|
-
staleHooks.push({ file: hookFile, hookVersion: 'unknown', installedVersion: installed });
|
|
91
|
-
}
|
|
92
|
-
} catch (e) {}
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
} catch (e) {}
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
let latest = null;
|
|
99
|
-
try {
|
|
100
|
-
latest = execSync('npm view framework-cc version', { encoding: 'utf8', timeout: 10000, windowsHide: true }).trim();
|
|
101
|
-
} catch (e) {}
|
|
102
|
-
|
|
103
|
-
const result = {
|
|
104
|
-
update_available: latest && installed !== latest,
|
|
105
|
-
installed,
|
|
106
|
-
latest: latest || 'unknown',
|
|
107
|
-
checked: Math.floor(Date.now() / 1000),
|
|
108
|
-
stale_hooks: staleHooks.length > 0 ? staleHooks : undefined
|
|
109
|
-
};
|
|
110
|
-
|
|
111
|
-
fs.writeFileSync(cacheFile, JSON.stringify(result));
|
|
112
|
-
`], {
|
|
113
|
-
stdio: 'ignore',
|
|
114
|
-
windowsHide: true,
|
|
115
|
-
detached: true // Required on Windows for proper process detachment
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
child.unref();
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// hook-version: 1.30.0
|
|
3
|
+
// SEC-13-05: flush-before-exit category = E (parent returns after spawn unref; child uses sync fs writes) — no fix needed
|
|
4
|
+
// Check for framework updates in background, write result to cache
|
|
5
|
+
// Called by SessionStart hook - runs once per session
|
|
6
|
+
|
|
7
|
+
const fs = require('fs');
|
|
8
|
+
const path = require('path');
|
|
9
|
+
const os = require('os');
|
|
10
|
+
const { spawn } = require('child_process');
|
|
11
|
+
|
|
12
|
+
const homeDir = os.homedir();
|
|
13
|
+
const cwd = process.cwd();
|
|
14
|
+
|
|
15
|
+
// Detect runtime config directory (supports Claude, OpenCode, Gemini)
|
|
16
|
+
// Respects CLAUDE_CONFIG_DIR for custom config directory setups
|
|
17
|
+
function detectConfigDir(baseDir) {
|
|
18
|
+
// Check env override first (supports multi-account setups)
|
|
19
|
+
const envDir = process.env.CLAUDE_CONFIG_DIR;
|
|
20
|
+
if (envDir && fs.existsSync(path.join(envDir, 'framework', 'VERSION'))) {
|
|
21
|
+
return envDir;
|
|
22
|
+
}
|
|
23
|
+
for (const dir of ['.config/opencode', '.opencode', '.gemini', '.claude']) {
|
|
24
|
+
if (fs.existsSync(path.join(baseDir, dir, 'framework', 'VERSION'))) {
|
|
25
|
+
return path.join(baseDir, dir);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return envDir || path.join(baseDir, '.claude');
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const globalConfigDir = detectConfigDir(homeDir);
|
|
32
|
+
const projectConfigDir = detectConfigDir(cwd);
|
|
33
|
+
const cacheDir = path.join(globalConfigDir, 'cache');
|
|
34
|
+
const cacheFile = path.join(cacheDir, 'update-check.json');
|
|
35
|
+
|
|
36
|
+
// VERSION file locations (check project first, then global)
|
|
37
|
+
const projectVersionFile = path.join(projectConfigDir, 'framework', 'VERSION');
|
|
38
|
+
const globalVersionFile = path.join(globalConfigDir, 'framework', 'VERSION');
|
|
39
|
+
|
|
40
|
+
// Ensure cache directory exists
|
|
41
|
+
if (!fs.existsSync(cacheDir)) {
|
|
42
|
+
fs.mkdirSync(cacheDir, { recursive: true });
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Run check in background (spawn background process, windowsHide prevents console flash)
|
|
46
|
+
// SEC-13-05: parent process retorna imediatamente após child.unref() — não
|
|
47
|
+
// há buffered I/O no parent. Child usa fs.writeFileSync (sync), sem race.
|
|
48
|
+
// Categoria E na taxonomia da Phase 80.
|
|
49
|
+
const child = spawn(process.execPath, ['-e', `
|
|
50
|
+
const fs = require('fs');
|
|
51
|
+
const path = require('path');
|
|
52
|
+
const { execSync } = require('child_process');
|
|
53
|
+
|
|
54
|
+
const cacheFile = ${JSON.stringify(cacheFile)};
|
|
55
|
+
const projectVersionFile = ${JSON.stringify(projectVersionFile)};
|
|
56
|
+
const globalVersionFile = ${JSON.stringify(globalVersionFile)};
|
|
57
|
+
|
|
58
|
+
// Check project directory first (local install), then global
|
|
59
|
+
let installed = '0.0.0';
|
|
60
|
+
let configDir = '';
|
|
61
|
+
try {
|
|
62
|
+
if (fs.existsSync(projectVersionFile)) {
|
|
63
|
+
installed = fs.readFileSync(projectVersionFile, 'utf8').trim();
|
|
64
|
+
configDir = path.dirname(path.dirname(projectVersionFile));
|
|
65
|
+
} else if (fs.existsSync(globalVersionFile)) {
|
|
66
|
+
installed = fs.readFileSync(globalVersionFile, 'utf8').trim();
|
|
67
|
+
configDir = path.dirname(path.dirname(globalVersionFile));
|
|
68
|
+
}
|
|
69
|
+
} catch (e) {}
|
|
70
|
+
|
|
71
|
+
// Check for stale hooks — compare hook version headers against installed VERSION
|
|
72
|
+
// Hooks live inside framework/hooks/, not configDir/hooks/
|
|
73
|
+
let staleHooks = [];
|
|
74
|
+
if (configDir) {
|
|
75
|
+
const hooksDir = path.join(configDir, 'framework', 'hooks');
|
|
76
|
+
try {
|
|
77
|
+
if (fs.existsSync(hooksDir)) {
|
|
78
|
+
const hookFiles = fs.readdirSync(hooksDir).filter(f => f.startsWith('framework-') && f.endsWith('.js'));
|
|
79
|
+
for (const hookFile of hookFiles) {
|
|
80
|
+
try {
|
|
81
|
+
const content = fs.readFileSync(path.join(hooksDir, hookFile), 'utf8');
|
|
82
|
+
const versionMatch = content.match(/\\/\\/ hook-version:\\s*(.+)/);
|
|
83
|
+
if (versionMatch) {
|
|
84
|
+
const hookVersion = versionMatch[1].trim();
|
|
85
|
+
if (hookVersion !== installed && !hookVersion.includes('{{')) {
|
|
86
|
+
staleHooks.push({ file: hookFile, hookVersion, installedVersion: installed });
|
|
87
|
+
}
|
|
88
|
+
} else {
|
|
89
|
+
// No version header at all — definitely stale (pre-version-tracking)
|
|
90
|
+
staleHooks.push({ file: hookFile, hookVersion: 'unknown', installedVersion: installed });
|
|
91
|
+
}
|
|
92
|
+
} catch (e) {}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
} catch (e) {}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
let latest = null;
|
|
99
|
+
try {
|
|
100
|
+
latest = execSync('npm view framework-cc version', { encoding: 'utf8', timeout: 10000, windowsHide: true }).trim();
|
|
101
|
+
} catch (e) {}
|
|
102
|
+
|
|
103
|
+
const result = {
|
|
104
|
+
update_available: latest && installed !== latest,
|
|
105
|
+
installed,
|
|
106
|
+
latest: latest || 'unknown',
|
|
107
|
+
checked: Math.floor(Date.now() / 1000),
|
|
108
|
+
stale_hooks: staleHooks.length > 0 ? staleHooks : undefined
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
fs.writeFileSync(cacheFile, JSON.stringify(result));
|
|
112
|
+
`], {
|
|
113
|
+
stdio: 'ignore',
|
|
114
|
+
windowsHide: true,
|
|
115
|
+
detached: true // Required on Windows for proper process detachment
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
child.unref();
|
|
@@ -1,163 +1,163 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
// hook-version: 1.30.1
|
|
3
|
-
// SEC-13-05: flush-before-exit category = A (stdout.write + immediate exit)
|
|
4
|
-
// Fix applied: process.stdout.write(payload, () => process.exit(0)) on warning path.
|
|
5
|
-
// Context Monitor - PostToolUse/AfterTool hook (Gemini uses AfterTool)
|
|
6
|
-
// Reads context metrics from the statusline bridge file and injects
|
|
7
|
-
// warnings when context usage is high. This makes the AGENT aware of
|
|
8
|
-
// context limits (the statusline only shows the user).
|
|
9
|
-
//
|
|
10
|
-
// How it works:
|
|
11
|
-
// 1. The statusline hook writes metrics to /tmp/claude-ctx-{session_id}.json
|
|
12
|
-
// 2. This hook reads those metrics after each tool use
|
|
13
|
-
// 3. When remaining context drops below thresholds, it injects a warning
|
|
14
|
-
// as additionalContext, which the agent sees in its conversation
|
|
15
|
-
//
|
|
16
|
-
// Thresholds:
|
|
17
|
-
// WARNING (remaining <= 35%): Agent should wrap up current task
|
|
18
|
-
// CRITICAL (remaining <= 25%): Agent should stop immediately and save state
|
|
19
|
-
//
|
|
20
|
-
// Debounce: 5 tool uses between warnings to avoid spam
|
|
21
|
-
// Severity escalation bypasses debounce (WARNING -> CRITICAL fires immediately)
|
|
22
|
-
|
|
23
|
-
const fs = require('fs');
|
|
24
|
-
const os = require('os');
|
|
25
|
-
const path = require('path');
|
|
26
|
-
|
|
27
|
-
const WARNING_THRESHOLD = 35; // remaining_percentage <= 35%
|
|
28
|
-
const CRITICAL_THRESHOLD = 25; // remaining_percentage <= 25%
|
|
29
|
-
const STALE_SECONDS = 60; // ignore metrics older than 60s
|
|
30
|
-
const DEBOUNCE_CALLS = 5; // min tool uses between warnings
|
|
31
|
-
|
|
32
|
-
let input = '';
|
|
33
|
-
// Timeout guard: if stdin doesn't close within 10s (e.g. pipe issues on
|
|
34
|
-
// Windows/Git Bash, or slow Claude Code piping during large outputs),
|
|
35
|
-
// exit silently instead of hanging until Claude Code kills the process
|
|
36
|
-
// and reports "hook error". See #775, #1162.
|
|
37
|
-
const stdinTimeout = setTimeout(() => process.exit(0), 10000);
|
|
38
|
-
process.stdin.setEncoding('utf8');
|
|
39
|
-
process.stdin.on('data', chunk => input += chunk);
|
|
40
|
-
process.stdin.on('end', () => {
|
|
41
|
-
clearTimeout(stdinTimeout);
|
|
42
|
-
try {
|
|
43
|
-
const data = JSON.parse(input);
|
|
44
|
-
const sessionId = data.session_id;
|
|
45
|
-
|
|
46
|
-
if (!sessionId) {
|
|
47
|
-
process.exit(0);
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
// Check if context warnings are disabled via config
|
|
51
|
-
const cwd = data.cwd || process.cwd();
|
|
52
|
-
const configPath = path.join(cwd, '.planning', 'config.json');
|
|
53
|
-
if (fs.existsSync(configPath)) {
|
|
54
|
-
try {
|
|
55
|
-
const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
56
|
-
if (config.hooks?.context_warnings === false) {
|
|
57
|
-
process.exit(0);
|
|
58
|
-
}
|
|
59
|
-
} catch (e) {
|
|
60
|
-
// Ignore config parse errors
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
const tmpDir = os.tmpdir();
|
|
65
|
-
const metricsPath = path.join(tmpDir, `claude-ctx-${sessionId}.json`);
|
|
66
|
-
|
|
67
|
-
// If no metrics file, this is a subagent or fresh session -- exit silently
|
|
68
|
-
if (!fs.existsSync(metricsPath)) {
|
|
69
|
-
process.exit(0);
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
const metrics = JSON.parse(fs.readFileSync(metricsPath, 'utf8'));
|
|
73
|
-
const now = Math.floor(Date.now() / 1000);
|
|
74
|
-
|
|
75
|
-
// Ignore stale metrics
|
|
76
|
-
if (metrics.timestamp && (now - metrics.timestamp) > STALE_SECONDS) {
|
|
77
|
-
process.exit(0);
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
const remaining = metrics.remaining_percentage;
|
|
81
|
-
const usedPct = metrics.used_pct;
|
|
82
|
-
|
|
83
|
-
// No warning needed
|
|
84
|
-
if (remaining > WARNING_THRESHOLD) {
|
|
85
|
-
process.exit(0);
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
// Debounce: check if we warned recently
|
|
89
|
-
const warnPath = path.join(tmpDir, `claude-ctx-${sessionId}-warned.json`);
|
|
90
|
-
let warnData = { callsSinceWarn: 0, lastLevel: null };
|
|
91
|
-
let firstWarn = true;
|
|
92
|
-
|
|
93
|
-
if (fs.existsSync(warnPath)) {
|
|
94
|
-
try {
|
|
95
|
-
warnData = JSON.parse(fs.readFileSync(warnPath, 'utf8'));
|
|
96
|
-
firstWarn = false;
|
|
97
|
-
} catch (e) {
|
|
98
|
-
// Corrupted file, reset
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
warnData.callsSinceWarn = (warnData.callsSinceWarn || 0) + 1;
|
|
103
|
-
|
|
104
|
-
const isCritical = remaining <= CRITICAL_THRESHOLD;
|
|
105
|
-
const currentLevel = isCritical ? 'critical' : 'warning';
|
|
106
|
-
|
|
107
|
-
// Emit immediately on first warning, then debounce subsequent ones
|
|
108
|
-
// Severity escalation (WARNING -> CRITICAL) bypasses debounce
|
|
109
|
-
const severityEscalated = currentLevel === 'critical' && warnData.lastLevel === 'warning';
|
|
110
|
-
if (!firstWarn && warnData.callsSinceWarn < DEBOUNCE_CALLS && !severityEscalated) {
|
|
111
|
-
// Update counter and exit without warning
|
|
112
|
-
fs.writeFileSync(warnPath, JSON.stringify(warnData));
|
|
113
|
-
process.exit(0);
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
// Reset debounce counter
|
|
117
|
-
warnData.callsSinceWarn = 0;
|
|
118
|
-
warnData.lastLevel = currentLevel;
|
|
119
|
-
fs.writeFileSync(warnPath, JSON.stringify(warnData));
|
|
120
|
-
|
|
121
|
-
// Detect if framework is active (has .planning/STATE.md in working directory)
|
|
122
|
-
const isFrameworkActive = fs.existsSync(path.join(cwd, '.planning', 'STATE.md'));
|
|
123
|
-
|
|
124
|
-
// Build advisory warning message (never use imperative commands that
|
|
125
|
-
// override user preferences — see #884)
|
|
126
|
-
let message;
|
|
127
|
-
if (isCritical) {
|
|
128
|
-
message = isFrameworkActive
|
|
129
|
-
? `CONTEXTO CRÍTICO: Uso em ${usedPct}%. Restando: ${remaining}%. ` +
|
|
130
|
-
'O contexto está quase esgotado. NÃO inicie novo trabalho complexo nem escreva arquivos de handoff — ' +
|
|
131
|
-
'o estado do framework já está rastreado no STATE.md. Informe o usuário para que possa executar ' +
|
|
132
|
-
'/pause-work no próximo ponto de parada natural.'
|
|
133
|
-
: `CONTEXTO CRÍTICO: Uso em ${usedPct}%. Restando: ${remaining}%. ` +
|
|
134
|
-
'O contexto está quase esgotado. Informe o usuário que o contexto está baixo e pergunte como ' +
|
|
135
|
-
'deseja prosseguir. NÃO salve estado nem escreva arquivos de handoff de forma autônoma sem que o usuário solicite.';
|
|
136
|
-
} else {
|
|
137
|
-
message = isFrameworkActive
|
|
138
|
-
? `AVISO DE CONTEXTO: Uso em ${usedPct}%. Restando: ${remaining}%. ` +
|
|
139
|
-
'O contexto está ficando limitado. Evite iniciar novo trabalho complexo. Se não estiver entre ' +
|
|
140
|
-
'passos definidos do plano, informe o usuário para que possa se preparar para pausar.'
|
|
141
|
-
: `AVISO DE CONTEXTO: Uso em ${usedPct}%. Restando: ${remaining}%. ` +
|
|
142
|
-
'Esteja ciente de que o contexto está ficando limitado. Evite exploração desnecessária ou ' +
|
|
143
|
-
'iniciar novo trabalho complexo.';
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
const output = {
|
|
147
|
-
hookSpecificOutput: {
|
|
148
|
-
hookEventName: process.env.GEMINI_API_KEY ? "AfterTool" : "PostToolUse",
|
|
149
|
-
additionalContext: message
|
|
150
|
-
}
|
|
151
|
-
};
|
|
152
|
-
|
|
153
|
-
// SEC-13-05: aguardar flush do stdout antes do exit. Sem callback, em
|
|
154
|
-
// pipes lentos (CI/Windows/Git Bash) o JSON pode ser dropado quando o
|
|
155
|
-
// process termina antes do kernel drenar o buffer.
|
|
156
|
-
process.stdout.write(JSON.stringify(output), () => {
|
|
157
|
-
process.exit(0);
|
|
158
|
-
});
|
|
159
|
-
} catch (e) {
|
|
160
|
-
// Silent fail -- never block tool execution
|
|
161
|
-
process.exit(0);
|
|
162
|
-
}
|
|
163
|
-
});
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// hook-version: 1.30.1
|
|
3
|
+
// SEC-13-05: flush-before-exit category = A (stdout.write + immediate exit)
|
|
4
|
+
// Fix applied: process.stdout.write(payload, () => process.exit(0)) on warning path.
|
|
5
|
+
// Context Monitor - PostToolUse/AfterTool hook (Gemini uses AfterTool)
|
|
6
|
+
// Reads context metrics from the statusline bridge file and injects
|
|
7
|
+
// warnings when context usage is high. This makes the AGENT aware of
|
|
8
|
+
// context limits (the statusline only shows the user).
|
|
9
|
+
//
|
|
10
|
+
// How it works:
|
|
11
|
+
// 1. The statusline hook writes metrics to /tmp/claude-ctx-{session_id}.json
|
|
12
|
+
// 2. This hook reads those metrics after each tool use
|
|
13
|
+
// 3. When remaining context drops below thresholds, it injects a warning
|
|
14
|
+
// as additionalContext, which the agent sees in its conversation
|
|
15
|
+
//
|
|
16
|
+
// Thresholds:
|
|
17
|
+
// WARNING (remaining <= 35%): Agent should wrap up current task
|
|
18
|
+
// CRITICAL (remaining <= 25%): Agent should stop immediately and save state
|
|
19
|
+
//
|
|
20
|
+
// Debounce: 5 tool uses between warnings to avoid spam
|
|
21
|
+
// Severity escalation bypasses debounce (WARNING -> CRITICAL fires immediately)
|
|
22
|
+
|
|
23
|
+
const fs = require('fs');
|
|
24
|
+
const os = require('os');
|
|
25
|
+
const path = require('path');
|
|
26
|
+
|
|
27
|
+
const WARNING_THRESHOLD = 35; // remaining_percentage <= 35%
|
|
28
|
+
const CRITICAL_THRESHOLD = 25; // remaining_percentage <= 25%
|
|
29
|
+
const STALE_SECONDS = 60; // ignore metrics older than 60s
|
|
30
|
+
const DEBOUNCE_CALLS = 5; // min tool uses between warnings
|
|
31
|
+
|
|
32
|
+
let input = '';
|
|
33
|
+
// Timeout guard: if stdin doesn't close within 10s (e.g. pipe issues on
|
|
34
|
+
// Windows/Git Bash, or slow Claude Code piping during large outputs),
|
|
35
|
+
// exit silently instead of hanging until Claude Code kills the process
|
|
36
|
+
// and reports "hook error". See #775, #1162.
|
|
37
|
+
const stdinTimeout = setTimeout(() => process.exit(0), 10000);
|
|
38
|
+
process.stdin.setEncoding('utf8');
|
|
39
|
+
process.stdin.on('data', chunk => input += chunk);
|
|
40
|
+
process.stdin.on('end', () => {
|
|
41
|
+
clearTimeout(stdinTimeout);
|
|
42
|
+
try {
|
|
43
|
+
const data = JSON.parse(input);
|
|
44
|
+
const sessionId = data.session_id;
|
|
45
|
+
|
|
46
|
+
if (!sessionId) {
|
|
47
|
+
process.exit(0);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Check if context warnings are disabled via config
|
|
51
|
+
const cwd = data.cwd || process.cwd();
|
|
52
|
+
const configPath = path.join(cwd, '.planning', 'config.json');
|
|
53
|
+
if (fs.existsSync(configPath)) {
|
|
54
|
+
try {
|
|
55
|
+
const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
56
|
+
if (config.hooks?.context_warnings === false) {
|
|
57
|
+
process.exit(0);
|
|
58
|
+
}
|
|
59
|
+
} catch (e) {
|
|
60
|
+
// Ignore config parse errors
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const tmpDir = os.tmpdir();
|
|
65
|
+
const metricsPath = path.join(tmpDir, `claude-ctx-${sessionId}.json`);
|
|
66
|
+
|
|
67
|
+
// If no metrics file, this is a subagent or fresh session -- exit silently
|
|
68
|
+
if (!fs.existsSync(metricsPath)) {
|
|
69
|
+
process.exit(0);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const metrics = JSON.parse(fs.readFileSync(metricsPath, 'utf8'));
|
|
73
|
+
const now = Math.floor(Date.now() / 1000);
|
|
74
|
+
|
|
75
|
+
// Ignore stale metrics
|
|
76
|
+
if (metrics.timestamp && (now - metrics.timestamp) > STALE_SECONDS) {
|
|
77
|
+
process.exit(0);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const remaining = metrics.remaining_percentage;
|
|
81
|
+
const usedPct = metrics.used_pct;
|
|
82
|
+
|
|
83
|
+
// No warning needed
|
|
84
|
+
if (remaining > WARNING_THRESHOLD) {
|
|
85
|
+
process.exit(0);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Debounce: check if we warned recently
|
|
89
|
+
const warnPath = path.join(tmpDir, `claude-ctx-${sessionId}-warned.json`);
|
|
90
|
+
let warnData = { callsSinceWarn: 0, lastLevel: null };
|
|
91
|
+
let firstWarn = true;
|
|
92
|
+
|
|
93
|
+
if (fs.existsSync(warnPath)) {
|
|
94
|
+
try {
|
|
95
|
+
warnData = JSON.parse(fs.readFileSync(warnPath, 'utf8'));
|
|
96
|
+
firstWarn = false;
|
|
97
|
+
} catch (e) {
|
|
98
|
+
// Corrupted file, reset
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
warnData.callsSinceWarn = (warnData.callsSinceWarn || 0) + 1;
|
|
103
|
+
|
|
104
|
+
const isCritical = remaining <= CRITICAL_THRESHOLD;
|
|
105
|
+
const currentLevel = isCritical ? 'critical' : 'warning';
|
|
106
|
+
|
|
107
|
+
// Emit immediately on first warning, then debounce subsequent ones
|
|
108
|
+
// Severity escalation (WARNING -> CRITICAL) bypasses debounce
|
|
109
|
+
const severityEscalated = currentLevel === 'critical' && warnData.lastLevel === 'warning';
|
|
110
|
+
if (!firstWarn && warnData.callsSinceWarn < DEBOUNCE_CALLS && !severityEscalated) {
|
|
111
|
+
// Update counter and exit without warning
|
|
112
|
+
fs.writeFileSync(warnPath, JSON.stringify(warnData));
|
|
113
|
+
process.exit(0);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Reset debounce counter
|
|
117
|
+
warnData.callsSinceWarn = 0;
|
|
118
|
+
warnData.lastLevel = currentLevel;
|
|
119
|
+
fs.writeFileSync(warnPath, JSON.stringify(warnData));
|
|
120
|
+
|
|
121
|
+
// Detect if framework is active (has .planning/STATE.md in working directory)
|
|
122
|
+
const isFrameworkActive = fs.existsSync(path.join(cwd, '.planning', 'STATE.md'));
|
|
123
|
+
|
|
124
|
+
// Build advisory warning message (never use imperative commands that
|
|
125
|
+
// override user preferences — see #884)
|
|
126
|
+
let message;
|
|
127
|
+
if (isCritical) {
|
|
128
|
+
message = isFrameworkActive
|
|
129
|
+
? `CONTEXTO CRÍTICO: Uso em ${usedPct}%. Restando: ${remaining}%. ` +
|
|
130
|
+
'O contexto está quase esgotado. NÃO inicie novo trabalho complexo nem escreva arquivos de handoff — ' +
|
|
131
|
+
'o estado do framework já está rastreado no STATE.md. Informe o usuário para que possa executar ' +
|
|
132
|
+
'/pause-work no próximo ponto de parada natural.'
|
|
133
|
+
: `CONTEXTO CRÍTICO: Uso em ${usedPct}%. Restando: ${remaining}%. ` +
|
|
134
|
+
'O contexto está quase esgotado. Informe o usuário que o contexto está baixo e pergunte como ' +
|
|
135
|
+
'deseja prosseguir. NÃO salve estado nem escreva arquivos de handoff de forma autônoma sem que o usuário solicite.';
|
|
136
|
+
} else {
|
|
137
|
+
message = isFrameworkActive
|
|
138
|
+
? `AVISO DE CONTEXTO: Uso em ${usedPct}%. Restando: ${remaining}%. ` +
|
|
139
|
+
'O contexto está ficando limitado. Evite iniciar novo trabalho complexo. Se não estiver entre ' +
|
|
140
|
+
'passos definidos do plano, informe o usuário para que possa se preparar para pausar.'
|
|
141
|
+
: `AVISO DE CONTEXTO: Uso em ${usedPct}%. Restando: ${remaining}%. ` +
|
|
142
|
+
'Esteja ciente de que o contexto está ficando limitado. Evite exploração desnecessária ou ' +
|
|
143
|
+
'iniciar novo trabalho complexo.';
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const output = {
|
|
147
|
+
hookSpecificOutput: {
|
|
148
|
+
hookEventName: process.env.GEMINI_API_KEY ? "AfterTool" : "PostToolUse",
|
|
149
|
+
additionalContext: message
|
|
150
|
+
}
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
// SEC-13-05: aguardar flush do stdout antes do exit. Sem callback, em
|
|
154
|
+
// pipes lentos (CI/Windows/Git Bash) o JSON pode ser dropado quando o
|
|
155
|
+
// process termina antes do kernel drenar o buffer.
|
|
156
|
+
process.stdout.write(JSON.stringify(output), () => {
|
|
157
|
+
process.exit(0);
|
|
158
|
+
});
|
|
159
|
+
} catch (e) {
|
|
160
|
+
// Silent fail -- never block tool execution
|
|
161
|
+
process.exit(0);
|
|
162
|
+
}
|
|
163
|
+
});
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// hook-version: 1.30.1
|
|
3
|
+
// kit-mcp · Kit Attribution Reminder (UserPromptSubmit)
|
|
4
|
+
//
|
|
5
|
+
// Injeta `additionalContext` no envelope do prompt do usuário com diretiva
|
|
6
|
+
// canônica v1.30.1: sempre que Claude usar comandos/agents/skills do kit-mcp
|
|
7
|
+
// no turno corrente, deve abrir o sidecar (auto-spawn já cuida disso na 1ª
|
|
8
|
+
// tool call) e listar os recursos usados em bloco final do output.
|
|
9
|
+
//
|
|
10
|
+
// Objetivo: dar feedback visual + textual de uso real do kit-mcp para o
|
|
11
|
+
// usuário detectar bugs no fluxo precocemente.
|
|
12
|
+
//
|
|
13
|
+
// Pipeline: UserPromptSubmit hook → reads stdin envelope → writes JSON com
|
|
14
|
+
// `{ continue: true, hookSpecificOutput: { hookEventName: "UserPromptSubmit",
|
|
15
|
+
// additionalContext: "<diretiva>" } }` → exit 0.
|
|
16
|
+
//
|
|
17
|
+
// SOFT failure: qualquer erro → exit 0 sem bloquear. Skip se
|
|
18
|
+
// KIT_MCP_NO_ATTRIBUTION=1.
|
|
19
|
+
//
|
|
20
|
+
// Module format: CommonJS (.cjs) — compatível com qualquer setup Node.
|
|
21
|
+
//
|
|
22
|
+
// Enable em settings.json (project ou user):
|
|
23
|
+
// {
|
|
24
|
+
// "hooks": {
|
|
25
|
+
// "UserPromptSubmit": [{
|
|
26
|
+
// "matcher": "*",
|
|
27
|
+
// "hooks": [{
|
|
28
|
+
// "type": "command",
|
|
29
|
+
// "command": "node /abs/path/to/kit-attribution-reminder.cjs"
|
|
30
|
+
// }]
|
|
31
|
+
// }]
|
|
32
|
+
// }
|
|
33
|
+
// }
|
|
34
|
+
|
|
35
|
+
'use strict';
|
|
36
|
+
|
|
37
|
+
// Opt-out via env var
|
|
38
|
+
if (process.env.KIT_MCP_NO_ATTRIBUTION === '1' || process.env.KIT_MCP_NO_ATTRIBUTION === 'true') {
|
|
39
|
+
process.exit(0);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const DIRECTIVE = [
|
|
43
|
+
'🔧 KIT-MCP ATTRIBUTION — directive ativa neste turno (v1.30.1):',
|
|
44
|
+
'',
|
|
45
|
+
'Se ao responder este turno você usar QUALQUER recurso do kit-mcp (qualquer',
|
|
46
|
+
'subagent_type listado em /agents kit-mcp, qualquer skill cujo nome começa',
|
|
47
|
+
'com supabase-/legacy-/cascading-/etc. do kit, ou qualquer /slash-command',
|
|
48
|
+
'do kit), SEMPRE faça duas coisas:',
|
|
49
|
+
'',
|
|
50
|
+
'1. **Sidecar visual:** o kit-mcp abre o sidecar automaticamente na primeira',
|
|
51
|
+
' tool call do MCP server (browser tab em 127.0.0.1:<porta>). Você NÃO',
|
|
52
|
+
' precisa abrir manualmente — é fire-and-forget no MCP. Apenas confirme',
|
|
53
|
+
' que foi disparado e mencione se o usuário precisar conferir.',
|
|
54
|
+
'',
|
|
55
|
+
'2. **Bloco de atribuição no final do output** — adicione ao final da sua',
|
|
56
|
+
' resposta um bloco markdown listando TODOS os recursos kit-mcp usados:',
|
|
57
|
+
'',
|
|
58
|
+
' ```',
|
|
59
|
+
' ---',
|
|
60
|
+
' 🔧 **Kit-mcp resources usados neste turno:**',
|
|
61
|
+
' - Commands: /<command> (motivo: ...)',
|
|
62
|
+
' - Agents: <agent-name> (via Task — motivo: ...)',
|
|
63
|
+
' - Skills: <skill-name> (auto-trigger / consultada — motivo: ...)',
|
|
64
|
+
' - Sidecar: http://127.0.0.1:<porta>/ (opened on first MCP tool call)',
|
|
65
|
+
' ```',
|
|
66
|
+
'',
|
|
67
|
+
' Omita seções vazias. Se NÃO usou nenhum recurso kit-mcp neste turno,',
|
|
68
|
+
' **não adicione o bloco** — silêncio é melhor que ruído.',
|
|
69
|
+
'',
|
|
70
|
+
'Disable temporário: env KIT_MCP_NO_ATTRIBUTION=1.',
|
|
71
|
+
'',
|
|
72
|
+
].join('\n');
|
|
73
|
+
|
|
74
|
+
let input = '';
|
|
75
|
+
const stdinTimeout = setTimeout(() => process.exit(0), 3000);
|
|
76
|
+
process.stdin.setEncoding('utf8');
|
|
77
|
+
process.stdin.on('data', (chunk) => { input += chunk; });
|
|
78
|
+
process.stdin.on('end', () => {
|
|
79
|
+
clearTimeout(stdinTimeout);
|
|
80
|
+
try {
|
|
81
|
+
// Aceita envelope vazio; só precisamos retornar o JSON com additionalContext.
|
|
82
|
+
JSON.parse(input || '{}');
|
|
83
|
+
} catch {
|
|
84
|
+
// Envelope inválido — não bloquear; sair sem inject.
|
|
85
|
+
process.exit(0);
|
|
86
|
+
}
|
|
87
|
+
const payload = JSON.stringify({
|
|
88
|
+
continue: true,
|
|
89
|
+
hookSpecificOutput: {
|
|
90
|
+
hookEventName: 'UserPromptSubmit',
|
|
91
|
+
additionalContext: DIRECTIVE,
|
|
92
|
+
},
|
|
93
|
+
});
|
|
94
|
+
// SEC-13-05 (category A): flush antes de exit
|
|
95
|
+
process.stdout.write(payload, () => process.exit(0));
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
process.stdin.on('error', () => process.exit(0));
|