@luanpdd/kit-mcp 1.32.0 → 1.34.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -21
- package/README.md +168 -168
- package/gates/agent-no-recursive-dispatch.md +84 -84
- package/kit/COMANDOS.md +138 -138
- package/kit/COMPATIBILITY.md +70 -70
- package/kit/README.md +76 -76
- package/kit/agents/advisor-researcher.md +109 -109
- package/kit/agents/ai-mutation-tester.md +289 -289
- package/kit/agents/assumptions-analyzer.md +110 -110
- package/kit/agents/audit-log-implementer.md +314 -314
- package/kit/agents/auditor-consistencia-isolamento.md +414 -414
- package/kit/agents/b2b-saas-architect.md +157 -157
- package/kit/agents/burn-rate-forecaster.md +153 -153
- package/kit/agents/cascading-failures-auditor.md +299 -299
- package/kit/agents/codebase-mapper.md +769 -769
- package/kit/agents/crm-pipeline-implementer.md +257 -257
- package/kit/agents/debugger.md +814 -814
- package/kit/agents/designer-ui.md +216 -0
- package/kit/agents/detector-tenant-quente.md +338 -338
- package/kit/agents/evolution-go-integrator.md +201 -201
- package/kit/agents/example-reviewer.md +22 -22
- package/kit/agents/executor.md +565 -565
- package/kit/agents/golden-signals-instrumenter.md +232 -232
- package/kit/agents/incident-investigator.md +238 -238
- package/kit/agents/integration-checker.md +203 -203
- package/kit/agents/invite-flow-implementer.md +190 -190
- package/kit/agents/legacy-characterizer.md +369 -369
- package/kit/agents/lgpd-compliance-auditor.md +296 -296
- package/kit/agents/load-shedding-instrumenter.md +290 -290
- package/kit/agents/multi-tenant-isolation-auditor.md +254 -254
- package/kit/agents/multi-tenant-rls-writer.md +341 -341
- package/kit/agents/nyquist-auditor.md +181 -181
- package/kit/agents/observability-coverage-auditor.md +316 -316
- package/kit/agents/observability-instrumenter.md +191 -191
- package/kit/agents/omm-auditor.md +291 -291
- package/kit/agents/org-onboarding-implementer.md +224 -224
- package/kit/agents/payload-capture-instrumenter.md +274 -274
- package/kit/agents/phase-researcher.md +697 -697
- package/kit/agents/plan-checker.md +275 -275
- package/kit/agents/planner.md +923 -923
- package/kit/agents/postmortem-writer.md +273 -273
- package/kit/agents/project-researcher.md +653 -653
- package/kit/agents/prr-conductor.md +287 -287
- package/kit/agents/refactor-safety-auditor.md +405 -405
- package/kit/agents/release-pipeline-auditor.md +364 -364
- package/kit/agents/research-synthesizer.md +246 -246
- package/kit/agents/roadmapper.md +678 -678
- package/kit/agents/schema-checker.md +160 -160
- package/kit/agents/seam-finder.md +360 -360
- package/kit/agents/shotgun-surgery-detector.md +350 -350
- package/kit/agents/slo-engineer.md +217 -217
- package/kit/agents/storytelling-analyst.md +300 -300
- package/kit/agents/supabase-architect.md +249 -249
- package/kit/agents/supabase-auth-bootstrapper.md +400 -400
- package/kit/agents/supabase-auth-hook-writer.md +418 -418
- package/kit/agents/supabase-branching-architect.md +563 -563
- package/kit/agents/supabase-cicd-pipeline-implementer.md +778 -778
- package/kit/agents/supabase-column-privileges-writer.md +400 -400
- package/kit/agents/supabase-edge-fn-tester.md +288 -288
- package/kit/agents/supabase-edge-fn-writer.md +341 -341
- package/kit/agents/supabase-mfa-implementer.md +439 -439
- package/kit/agents/supabase-migration-writer.md +386 -386
- package/kit/agents/supabase-oauth-server-implementer.md +507 -507
- package/kit/agents/supabase-rbac-implementer.md +393 -393
- package/kit/agents/supabase-realtime-implementer.md +364 -364
- package/kit/agents/supabase-rls-hardener.md +522 -522
- package/kit/agents/supabase-rls-writer.md +324 -324
- package/kit/agents/supabase-roles-implementer.md +356 -356
- package/kit/agents/supabase-social-auth-implementer.md +451 -451
- package/kit/agents/supabase-sso-saml-architect.md +549 -549
- package/kit/agents/supabase-storage-implementer.md +407 -407
- package/kit/agents/super-admin-implementer.md +282 -282
- package/kit/agents/toil-auditor.md +268 -268
- package/kit/agents/ui-auditor.md +438 -438
- package/kit/agents/ui-checker.md +305 -305
- package/kit/agents/ui-researcher.md +356 -356
- package/kit/agents/user-profiler.md +176 -176
- package/kit/agents/validador-evolucao-schema.md +336 -336
- package/kit/agents/verifier.md +729 -729
- 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-workflow.md +121 -0
- 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 +238 -238
- 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 +13 -3
- 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 +92 -92
- package/kit/hooks/kit-router.cjs +137 -137
- 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/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-auth-hardening/SKILL.md +674 -674
- package/kit/skills/supabase-auth-hooks/SKILL.md +875 -875
- package/kit/skills/supabase-auth-methods/SKILL.md +486 -486
- package/kit/skills/supabase-auth-sessions/SKILL.md +579 -579
- package/kit/skills/supabase-auth-ssr/SKILL.md +306 -306
- 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 +330 -330
- package/kit/skills/supabase-edge-functions-auth/SKILL.md +309 -309
- package/kit/skills/supabase-edge-functions-limits/SKILL.md +302 -302
- package/kit/skills/supabase-edge-functions-mcp-server/SKILL.md +279 -279
- package/kit/skills/supabase-edge-functions-testing/SKILL.md +277 -277
- package/kit/skills/supabase-edge-runtime-builtins/SKILL.md +357 -357
- package/kit/skills/supabase-enterprise-sso-saml/SKILL.md +545 -545
- package/kit/skills/supabase-jwt-signing-keys/SKILL.md +399 -399
- package/kit/skills/supabase-mfa/SKILL.md +488 -488
- package/kit/skills/supabase-migration-repair/SKILL.md +823 -823
- package/kit/skills/supabase-migrations/SKILL.md +297 -297
- package/kit/skills/supabase-oauth-server/SKILL.md +537 -537
- 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 -460
- 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/supabase-social-oauth/SKILL.md +480 -480
- package/kit/skills/supabase-third-party-auth/SKILL.md +450 -450
- package/kit/skills/super-admin-platform-pattern/SKILL.md +326 -326
- package/kit/skills/tenant-quente-mitigacao/SKILL.md +605 -605
- package/kit/skills/ui-anti-padroes-ia/SKILL.md +261 -0
- package/kit/skills/ui-contexto-produto/SKILL.md +248 -0
- package/kit/skills/ui-cor-estrategia/SKILL.md +213 -0
- package/kit/skills/ui-critica-auditoria/SKILL.md +260 -0
- package/kit/skills/ui-motion-funcional/SKILL.md +264 -0
- package/kit/skills/ui-ritmo-espacial/SKILL.md +259 -0
- package/kit/skills/ui-tipografia/SKILL.md +211 -0
- package/kit/skills/whatsapp-conversation-state-machine/SKILL.md +287 -287
- package/kit/workflows/auditar-observabilidade-cobertura.workflow.js +250 -0
- package/package.json +65 -63
- package/src/core/kit.js +333 -216
- package/src/core/reflect.js +247 -247
- package/src/core/registry.js +123 -112
- package/src/core/reverse-sync.js +448 -372
- package/src/core/sync.js +477 -437
- package/src/core/watch.js +121 -121
- package/src/mcp-server/index.js +794 -794
|
@@ -1,282 +1,282 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* UAT Audit — Cross-phase UAT/VERIFICATION scanner
|
|
3
|
-
*
|
|
4
|
-
* Reads all *-UAT.md and *-VERIFICATION.md files across all phases.
|
|
5
|
-
* Extracts non-passing items. Returns structured JSON for workflow consumption.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
const fs = require('fs');
|
|
9
|
-
const path = require('path');
|
|
10
|
-
const { output, error, getMilestonePhaseFilter, planningDir, toPosixPath } = require('./core.cjs');
|
|
11
|
-
const { extractFrontmatter } = require('./frontmatter.cjs');
|
|
12
|
-
const { requireSafePath, sanitizeForDisplay } = require('./security.cjs');
|
|
13
|
-
|
|
14
|
-
function cmdAuditUat(cwd, raw) {
|
|
15
|
-
const phasesDir = path.join(planningDir(cwd), 'phases');
|
|
16
|
-
if (!fs.existsSync(phasesDir)) {
|
|
17
|
-
error('No phases directory found in planning directory');
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
const isDirInMilestone = getMilestonePhaseFilter(cwd);
|
|
21
|
-
const results = [];
|
|
22
|
-
|
|
23
|
-
// Scan all phase directories
|
|
24
|
-
const dirs = fs.readdirSync(phasesDir, { withFileTypes: true })
|
|
25
|
-
.filter(e => e.isDirectory())
|
|
26
|
-
.map(e => e.name)
|
|
27
|
-
.filter(isDirInMilestone)
|
|
28
|
-
.sort();
|
|
29
|
-
|
|
30
|
-
for (const dir of dirs) {
|
|
31
|
-
const phaseMatch = dir.match(/^(\d+[A-Z]?(?:\.\d+)*)/i);
|
|
32
|
-
const phaseNum = phaseMatch ? phaseMatch[1] : dir;
|
|
33
|
-
const phaseDir = path.join(phasesDir, dir);
|
|
34
|
-
const files = fs.readdirSync(phaseDir);
|
|
35
|
-
|
|
36
|
-
// Process UAT files
|
|
37
|
-
for (const file of files.filter(f => f.includes('-UAT') && f.endsWith('.md'))) {
|
|
38
|
-
const content = fs.readFileSync(path.join(phaseDir, file), 'utf-8');
|
|
39
|
-
const items = parseUatItems(content);
|
|
40
|
-
if (items.length > 0) {
|
|
41
|
-
results.push({
|
|
42
|
-
phase: phaseNum,
|
|
43
|
-
phase_dir: dir,
|
|
44
|
-
file,
|
|
45
|
-
file_path: toPosixPath(path.relative(cwd, path.join(phaseDir, file))),
|
|
46
|
-
type: 'uat',
|
|
47
|
-
status: (extractFrontmatter(content).status || 'unknown'),
|
|
48
|
-
items,
|
|
49
|
-
});
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
// Process VERIFICATION files
|
|
54
|
-
for (const file of files.filter(f => f.includes('-VERIFICATION') && f.endsWith('.md'))) {
|
|
55
|
-
const content = fs.readFileSync(path.join(phaseDir, file), 'utf-8');
|
|
56
|
-
const status = extractFrontmatter(content).status || 'unknown';
|
|
57
|
-
if (status === 'human_needed' || status === 'gaps_found') {
|
|
58
|
-
const items = parseVerificationItems(content, status);
|
|
59
|
-
if (items.length > 0) {
|
|
60
|
-
results.push({
|
|
61
|
-
phase: phaseNum,
|
|
62
|
-
phase_dir: dir,
|
|
63
|
-
file,
|
|
64
|
-
file_path: toPosixPath(path.relative(cwd, path.join(phaseDir, file))),
|
|
65
|
-
type: 'verification',
|
|
66
|
-
status,
|
|
67
|
-
items,
|
|
68
|
-
});
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
// Compute summary
|
|
75
|
-
const summary = {
|
|
76
|
-
total_files: results.length,
|
|
77
|
-
total_items: results.reduce((sum, r) => sum + r.items.length, 0),
|
|
78
|
-
by_category: {},
|
|
79
|
-
by_phase: {},
|
|
80
|
-
};
|
|
81
|
-
|
|
82
|
-
for (const r of results) {
|
|
83
|
-
if (!summary.by_phase[r.phase]) summary.by_phase[r.phase] = 0;
|
|
84
|
-
for (const item of r.items) {
|
|
85
|
-
summary.by_phase[r.phase]++;
|
|
86
|
-
const cat = item.category || 'unknown';
|
|
87
|
-
summary.by_category[cat] = (summary.by_category[cat] || 0) + 1;
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
output({ results, summary }, raw);
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
function cmdRenderCheckpoint(cwd, options = {}, raw) {
|
|
95
|
-
const filePath = options.file;
|
|
96
|
-
if (!filePath) {
|
|
97
|
-
error('UAT file required: use uat render-checkpoint --file <path>');
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
const resolvedPath = requireSafePath(filePath, cwd, 'UAT file', { allowAbsolute: true });
|
|
101
|
-
if (!fs.existsSync(resolvedPath)) {
|
|
102
|
-
error(`UAT file not found: ${filePath}`);
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
const content = fs.readFileSync(resolvedPath, 'utf-8');
|
|
106
|
-
const currentTest = parseCurrentTest(content);
|
|
107
|
-
|
|
108
|
-
if (currentTest.complete) {
|
|
109
|
-
error('UAT session is already complete; no pending checkpoint to render');
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
const checkpoint = buildCheckpoint(currentTest);
|
|
113
|
-
output({
|
|
114
|
-
file_path: toPosixPath(path.relative(cwd, resolvedPath)),
|
|
115
|
-
test_number: currentTest.number,
|
|
116
|
-
test_name: currentTest.name,
|
|
117
|
-
checkpoint,
|
|
118
|
-
}, raw, checkpoint);
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
function parseCurrentTest(content) {
|
|
122
|
-
const currentTestMatch = content.match(/##\s*Current Test\s*(?:\n<!--[\s\S]*?-->)?\n([\s\S]*?)(?=\n##\s|$)/i);
|
|
123
|
-
if (!currentTestMatch) {
|
|
124
|
-
error('UAT file is missing a Current Test section');
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
const section = currentTestMatch[1].trimEnd();
|
|
128
|
-
if (!section.trim()) {
|
|
129
|
-
error('Current Test section is empty');
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
if (/\[testing complete\]/i.test(section)) {
|
|
133
|
-
return { complete: true };
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
const numberMatch = section.match(/^number:\s*(\d+)\s*$/m);
|
|
137
|
-
const nameMatch = section.match(/^name:\s*(.+)\s*$/m);
|
|
138
|
-
const expectedBlockMatch = section.match(/^expected:\s*\|\n([\s\S]*?)(?=^\w[\w-]*:\s)/m)
|
|
139
|
-
|| section.match(/^expected:\s*\|\n([\s\S]+)/m);
|
|
140
|
-
const expectedInlineMatch = section.match(/^expected:\s*(.+)\s*$/m);
|
|
141
|
-
|
|
142
|
-
if (!numberMatch || !nameMatch || (!expectedBlockMatch && !expectedInlineMatch)) {
|
|
143
|
-
error('Current Test section is malformed');
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
let expected;
|
|
147
|
-
if (expectedBlockMatch) {
|
|
148
|
-
expected = expectedBlockMatch[1]
|
|
149
|
-
.split('\n')
|
|
150
|
-
.map(line => line.replace(/^ {2}/, ''))
|
|
151
|
-
.join('\n')
|
|
152
|
-
.trim();
|
|
153
|
-
} else {
|
|
154
|
-
expected = expectedInlineMatch[1].trim();
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
return {
|
|
158
|
-
complete: false,
|
|
159
|
-
number: parseInt(numberMatch[1], 10),
|
|
160
|
-
name: sanitizeForDisplay(nameMatch[1].trim()),
|
|
161
|
-
expected: sanitizeForDisplay(expected),
|
|
162
|
-
};
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
function buildCheckpoint(currentTest) {
|
|
166
|
-
return [
|
|
167
|
-
'╔══════════════════════════════════════════════════════════════╗',
|
|
168
|
-
'║ CHECKPOINT: Verification Required ║',
|
|
169
|
-
'╚══════════════════════════════════════════════════════════════╝',
|
|
170
|
-
'',
|
|
171
|
-
`**Test ${currentTest.number}: ${currentTest.name}**`,
|
|
172
|
-
'',
|
|
173
|
-
currentTest.expected,
|
|
174
|
-
'',
|
|
175
|
-
'──────────────────────────────────────────────────────────────',
|
|
176
|
-
'Type `pass` or describe what\'s wrong.',
|
|
177
|
-
'──────────────────────────────────────────────────────────────',
|
|
178
|
-
].join('\n');
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
function parseUatItems(content) {
|
|
182
|
-
const items = [];
|
|
183
|
-
// Match test blocks: ### N. Name\nexpected: ...\nresult: ...\n
|
|
184
|
-
const testPattern = /###\s*(\d+)\.\s*([^\n]+)\nexpected:\s*([^\n]+)\nresult:\s*(\w+)(?:\n(?:reported|reason|blocked_by):\s*[^\n]*)?/g;
|
|
185
|
-
let match;
|
|
186
|
-
while ((match = testPattern.exec(content)) !== null) {
|
|
187
|
-
const [, num, name, expected, result] = match;
|
|
188
|
-
if (result === 'pending' || result === 'skipped' || result === 'blocked') {
|
|
189
|
-
// Extract optional fields — limit to current test block (up to next ### or EOF)
|
|
190
|
-
const afterMatch = content.slice(match.index);
|
|
191
|
-
const nextHeading = afterMatch.indexOf('\n###', 1);
|
|
192
|
-
const blockText = nextHeading > 0 ? afterMatch.slice(0, nextHeading) : afterMatch;
|
|
193
|
-
const reasonMatch = blockText.match(/reason:\s*(.+)/);
|
|
194
|
-
const blockedByMatch = blockText.match(/blocked_by:\s*(.+)/);
|
|
195
|
-
|
|
196
|
-
const item = {
|
|
197
|
-
test: parseInt(num, 10),
|
|
198
|
-
name: name.trim(),
|
|
199
|
-
expected: expected.trim(),
|
|
200
|
-
result,
|
|
201
|
-
category: categorizeItem(result, reasonMatch?.[1], blockedByMatch?.[1]),
|
|
202
|
-
};
|
|
203
|
-
if (reasonMatch) item.reason = reasonMatch[1].trim();
|
|
204
|
-
if (blockedByMatch) item.blocked_by = blockedByMatch[1].trim();
|
|
205
|
-
items.push(item);
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
return items;
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
function parseVerificationItems(content, status) {
|
|
212
|
-
const items = [];
|
|
213
|
-
if (status === 'human_needed') {
|
|
214
|
-
// Extract from human_verification section — look for numbered items or table rows
|
|
215
|
-
const hvSection = content.match(/##\s*Human Verification.*?\n([\s\S]*?)(?=\n##\s|\n---\s|$)/i);
|
|
216
|
-
if (hvSection) {
|
|
217
|
-
const lines = hvSection[1].split('\n');
|
|
218
|
-
for (const line of lines) {
|
|
219
|
-
// Match table rows: | N | description | ... |
|
|
220
|
-
const tableMatch = line.match(/\|\s*(\d+)\s*\|\s*([^|]+)/);
|
|
221
|
-
// Match bullet items: - description
|
|
222
|
-
const bulletMatch = line.match(/^[-*]\s+(.+)/);
|
|
223
|
-
// Match numbered items: 1. description
|
|
224
|
-
const numberedMatch = line.match(/^(\d+)\.\s+(.+)/);
|
|
225
|
-
|
|
226
|
-
if (tableMatch) {
|
|
227
|
-
items.push({
|
|
228
|
-
test: parseInt(tableMatch[1], 10),
|
|
229
|
-
name: tableMatch[2].trim(),
|
|
230
|
-
result: 'human_needed',
|
|
231
|
-
category: 'human_uat',
|
|
232
|
-
});
|
|
233
|
-
} else if (numberedMatch) {
|
|
234
|
-
items.push({
|
|
235
|
-
test: parseInt(numberedMatch[1], 10),
|
|
236
|
-
name: numberedMatch[2].trim(),
|
|
237
|
-
result: 'human_needed',
|
|
238
|
-
category: 'human_uat',
|
|
239
|
-
});
|
|
240
|
-
} else if (bulletMatch && bulletMatch[1].length > 10) {
|
|
241
|
-
items.push({
|
|
242
|
-
name: bulletMatch[1].trim(),
|
|
243
|
-
result: 'human_needed',
|
|
244
|
-
category: 'human_uat',
|
|
245
|
-
});
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
// gaps_found items are already handled by plan-phase --gaps pipeline
|
|
251
|
-
return items;
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
function categorizeItem(result, reason, blockedBy) {
|
|
255
|
-
if (result === 'blocked' || blockedBy) {
|
|
256
|
-
if (blockedBy) {
|
|
257
|
-
if (/server/i.test(blockedBy)) return 'server_blocked';
|
|
258
|
-
if (/device|physical/i.test(blockedBy)) return 'device_needed';
|
|
259
|
-
if (/build|release|preview/i.test(blockedBy)) return 'build_needed';
|
|
260
|
-
if (/third.party|twilio|stripe/i.test(blockedBy)) return 'third_party';
|
|
261
|
-
}
|
|
262
|
-
return 'blocked';
|
|
263
|
-
}
|
|
264
|
-
if (result === 'skipped') {
|
|
265
|
-
if (reason) {
|
|
266
|
-
if (/server|not running|not available/i.test(reason)) return 'server_blocked';
|
|
267
|
-
if (/simulator|physical|device/i.test(reason)) return 'device_needed';
|
|
268
|
-
if (/build|release|preview/i.test(reason)) return 'build_needed';
|
|
269
|
-
}
|
|
270
|
-
return 'skipped_unresolved';
|
|
271
|
-
}
|
|
272
|
-
if (result === 'pending') return 'pending';
|
|
273
|
-
if (result === 'human_needed') return 'human_uat';
|
|
274
|
-
return 'unknown';
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
module.exports = {
|
|
278
|
-
cmdAuditUat,
|
|
279
|
-
cmdRenderCheckpoint,
|
|
280
|
-
parseCurrentTest,
|
|
281
|
-
buildCheckpoint,
|
|
282
|
-
};
|
|
1
|
+
/**
|
|
2
|
+
* UAT Audit — Cross-phase UAT/VERIFICATION scanner
|
|
3
|
+
*
|
|
4
|
+
* Reads all *-UAT.md and *-VERIFICATION.md files across all phases.
|
|
5
|
+
* Extracts non-passing items. Returns structured JSON for workflow consumption.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const fs = require('fs');
|
|
9
|
+
const path = require('path');
|
|
10
|
+
const { output, error, getMilestonePhaseFilter, planningDir, toPosixPath } = require('./core.cjs');
|
|
11
|
+
const { extractFrontmatter } = require('./frontmatter.cjs');
|
|
12
|
+
const { requireSafePath, sanitizeForDisplay } = require('./security.cjs');
|
|
13
|
+
|
|
14
|
+
function cmdAuditUat(cwd, raw) {
|
|
15
|
+
const phasesDir = path.join(planningDir(cwd), 'phases');
|
|
16
|
+
if (!fs.existsSync(phasesDir)) {
|
|
17
|
+
error('No phases directory found in planning directory');
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const isDirInMilestone = getMilestonePhaseFilter(cwd);
|
|
21
|
+
const results = [];
|
|
22
|
+
|
|
23
|
+
// Scan all phase directories
|
|
24
|
+
const dirs = fs.readdirSync(phasesDir, { withFileTypes: true })
|
|
25
|
+
.filter(e => e.isDirectory())
|
|
26
|
+
.map(e => e.name)
|
|
27
|
+
.filter(isDirInMilestone)
|
|
28
|
+
.sort();
|
|
29
|
+
|
|
30
|
+
for (const dir of dirs) {
|
|
31
|
+
const phaseMatch = dir.match(/^(\d+[A-Z]?(?:\.\d+)*)/i);
|
|
32
|
+
const phaseNum = phaseMatch ? phaseMatch[1] : dir;
|
|
33
|
+
const phaseDir = path.join(phasesDir, dir);
|
|
34
|
+
const files = fs.readdirSync(phaseDir);
|
|
35
|
+
|
|
36
|
+
// Process UAT files
|
|
37
|
+
for (const file of files.filter(f => f.includes('-UAT') && f.endsWith('.md'))) {
|
|
38
|
+
const content = fs.readFileSync(path.join(phaseDir, file), 'utf-8');
|
|
39
|
+
const items = parseUatItems(content);
|
|
40
|
+
if (items.length > 0) {
|
|
41
|
+
results.push({
|
|
42
|
+
phase: phaseNum,
|
|
43
|
+
phase_dir: dir,
|
|
44
|
+
file,
|
|
45
|
+
file_path: toPosixPath(path.relative(cwd, path.join(phaseDir, file))),
|
|
46
|
+
type: 'uat',
|
|
47
|
+
status: (extractFrontmatter(content).status || 'unknown'),
|
|
48
|
+
items,
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Process VERIFICATION files
|
|
54
|
+
for (const file of files.filter(f => f.includes('-VERIFICATION') && f.endsWith('.md'))) {
|
|
55
|
+
const content = fs.readFileSync(path.join(phaseDir, file), 'utf-8');
|
|
56
|
+
const status = extractFrontmatter(content).status || 'unknown';
|
|
57
|
+
if (status === 'human_needed' || status === 'gaps_found') {
|
|
58
|
+
const items = parseVerificationItems(content, status);
|
|
59
|
+
if (items.length > 0) {
|
|
60
|
+
results.push({
|
|
61
|
+
phase: phaseNum,
|
|
62
|
+
phase_dir: dir,
|
|
63
|
+
file,
|
|
64
|
+
file_path: toPosixPath(path.relative(cwd, path.join(phaseDir, file))),
|
|
65
|
+
type: 'verification',
|
|
66
|
+
status,
|
|
67
|
+
items,
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Compute summary
|
|
75
|
+
const summary = {
|
|
76
|
+
total_files: results.length,
|
|
77
|
+
total_items: results.reduce((sum, r) => sum + r.items.length, 0),
|
|
78
|
+
by_category: {},
|
|
79
|
+
by_phase: {},
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
for (const r of results) {
|
|
83
|
+
if (!summary.by_phase[r.phase]) summary.by_phase[r.phase] = 0;
|
|
84
|
+
for (const item of r.items) {
|
|
85
|
+
summary.by_phase[r.phase]++;
|
|
86
|
+
const cat = item.category || 'unknown';
|
|
87
|
+
summary.by_category[cat] = (summary.by_category[cat] || 0) + 1;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
output({ results, summary }, raw);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function cmdRenderCheckpoint(cwd, options = {}, raw) {
|
|
95
|
+
const filePath = options.file;
|
|
96
|
+
if (!filePath) {
|
|
97
|
+
error('UAT file required: use uat render-checkpoint --file <path>');
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const resolvedPath = requireSafePath(filePath, cwd, 'UAT file', { allowAbsolute: true });
|
|
101
|
+
if (!fs.existsSync(resolvedPath)) {
|
|
102
|
+
error(`UAT file not found: ${filePath}`);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const content = fs.readFileSync(resolvedPath, 'utf-8');
|
|
106
|
+
const currentTest = parseCurrentTest(content);
|
|
107
|
+
|
|
108
|
+
if (currentTest.complete) {
|
|
109
|
+
error('UAT session is already complete; no pending checkpoint to render');
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const checkpoint = buildCheckpoint(currentTest);
|
|
113
|
+
output({
|
|
114
|
+
file_path: toPosixPath(path.relative(cwd, resolvedPath)),
|
|
115
|
+
test_number: currentTest.number,
|
|
116
|
+
test_name: currentTest.name,
|
|
117
|
+
checkpoint,
|
|
118
|
+
}, raw, checkpoint);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function parseCurrentTest(content) {
|
|
122
|
+
const currentTestMatch = content.match(/##\s*Current Test\s*(?:\n<!--[\s\S]*?-->)?\n([\s\S]*?)(?=\n##\s|$)/i);
|
|
123
|
+
if (!currentTestMatch) {
|
|
124
|
+
error('UAT file is missing a Current Test section');
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const section = currentTestMatch[1].trimEnd();
|
|
128
|
+
if (!section.trim()) {
|
|
129
|
+
error('Current Test section is empty');
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (/\[testing complete\]/i.test(section)) {
|
|
133
|
+
return { complete: true };
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const numberMatch = section.match(/^number:\s*(\d+)\s*$/m);
|
|
137
|
+
const nameMatch = section.match(/^name:\s*(.+)\s*$/m);
|
|
138
|
+
const expectedBlockMatch = section.match(/^expected:\s*\|\n([\s\S]*?)(?=^\w[\w-]*:\s)/m)
|
|
139
|
+
|| section.match(/^expected:\s*\|\n([\s\S]+)/m);
|
|
140
|
+
const expectedInlineMatch = section.match(/^expected:\s*(.+)\s*$/m);
|
|
141
|
+
|
|
142
|
+
if (!numberMatch || !nameMatch || (!expectedBlockMatch && !expectedInlineMatch)) {
|
|
143
|
+
error('Current Test section is malformed');
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
let expected;
|
|
147
|
+
if (expectedBlockMatch) {
|
|
148
|
+
expected = expectedBlockMatch[1]
|
|
149
|
+
.split('\n')
|
|
150
|
+
.map(line => line.replace(/^ {2}/, ''))
|
|
151
|
+
.join('\n')
|
|
152
|
+
.trim();
|
|
153
|
+
} else {
|
|
154
|
+
expected = expectedInlineMatch[1].trim();
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
return {
|
|
158
|
+
complete: false,
|
|
159
|
+
number: parseInt(numberMatch[1], 10),
|
|
160
|
+
name: sanitizeForDisplay(nameMatch[1].trim()),
|
|
161
|
+
expected: sanitizeForDisplay(expected),
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function buildCheckpoint(currentTest) {
|
|
166
|
+
return [
|
|
167
|
+
'╔══════════════════════════════════════════════════════════════╗',
|
|
168
|
+
'║ CHECKPOINT: Verification Required ║',
|
|
169
|
+
'╚══════════════════════════════════════════════════════════════╝',
|
|
170
|
+
'',
|
|
171
|
+
`**Test ${currentTest.number}: ${currentTest.name}**`,
|
|
172
|
+
'',
|
|
173
|
+
currentTest.expected,
|
|
174
|
+
'',
|
|
175
|
+
'──────────────────────────────────────────────────────────────',
|
|
176
|
+
'Type `pass` or describe what\'s wrong.',
|
|
177
|
+
'──────────────────────────────────────────────────────────────',
|
|
178
|
+
].join('\n');
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function parseUatItems(content) {
|
|
182
|
+
const items = [];
|
|
183
|
+
// Match test blocks: ### N. Name\nexpected: ...\nresult: ...\n
|
|
184
|
+
const testPattern = /###\s*(\d+)\.\s*([^\n]+)\nexpected:\s*([^\n]+)\nresult:\s*(\w+)(?:\n(?:reported|reason|blocked_by):\s*[^\n]*)?/g;
|
|
185
|
+
let match;
|
|
186
|
+
while ((match = testPattern.exec(content)) !== null) {
|
|
187
|
+
const [, num, name, expected, result] = match;
|
|
188
|
+
if (result === 'pending' || result === 'skipped' || result === 'blocked') {
|
|
189
|
+
// Extract optional fields — limit to current test block (up to next ### or EOF)
|
|
190
|
+
const afterMatch = content.slice(match.index);
|
|
191
|
+
const nextHeading = afterMatch.indexOf('\n###', 1);
|
|
192
|
+
const blockText = nextHeading > 0 ? afterMatch.slice(0, nextHeading) : afterMatch;
|
|
193
|
+
const reasonMatch = blockText.match(/reason:\s*(.+)/);
|
|
194
|
+
const blockedByMatch = blockText.match(/blocked_by:\s*(.+)/);
|
|
195
|
+
|
|
196
|
+
const item = {
|
|
197
|
+
test: parseInt(num, 10),
|
|
198
|
+
name: name.trim(),
|
|
199
|
+
expected: expected.trim(),
|
|
200
|
+
result,
|
|
201
|
+
category: categorizeItem(result, reasonMatch?.[1], blockedByMatch?.[1]),
|
|
202
|
+
};
|
|
203
|
+
if (reasonMatch) item.reason = reasonMatch[1].trim();
|
|
204
|
+
if (blockedByMatch) item.blocked_by = blockedByMatch[1].trim();
|
|
205
|
+
items.push(item);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
return items;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
function parseVerificationItems(content, status) {
|
|
212
|
+
const items = [];
|
|
213
|
+
if (status === 'human_needed') {
|
|
214
|
+
// Extract from human_verification section — look for numbered items or table rows
|
|
215
|
+
const hvSection = content.match(/##\s*Human Verification.*?\n([\s\S]*?)(?=\n##\s|\n---\s|$)/i);
|
|
216
|
+
if (hvSection) {
|
|
217
|
+
const lines = hvSection[1].split('\n');
|
|
218
|
+
for (const line of lines) {
|
|
219
|
+
// Match table rows: | N | description | ... |
|
|
220
|
+
const tableMatch = line.match(/\|\s*(\d+)\s*\|\s*([^|]+)/);
|
|
221
|
+
// Match bullet items: - description
|
|
222
|
+
const bulletMatch = line.match(/^[-*]\s+(.+)/);
|
|
223
|
+
// Match numbered items: 1. description
|
|
224
|
+
const numberedMatch = line.match(/^(\d+)\.\s+(.+)/);
|
|
225
|
+
|
|
226
|
+
if (tableMatch) {
|
|
227
|
+
items.push({
|
|
228
|
+
test: parseInt(tableMatch[1], 10),
|
|
229
|
+
name: tableMatch[2].trim(),
|
|
230
|
+
result: 'human_needed',
|
|
231
|
+
category: 'human_uat',
|
|
232
|
+
});
|
|
233
|
+
} else if (numberedMatch) {
|
|
234
|
+
items.push({
|
|
235
|
+
test: parseInt(numberedMatch[1], 10),
|
|
236
|
+
name: numberedMatch[2].trim(),
|
|
237
|
+
result: 'human_needed',
|
|
238
|
+
category: 'human_uat',
|
|
239
|
+
});
|
|
240
|
+
} else if (bulletMatch && bulletMatch[1].length > 10) {
|
|
241
|
+
items.push({
|
|
242
|
+
name: bulletMatch[1].trim(),
|
|
243
|
+
result: 'human_needed',
|
|
244
|
+
category: 'human_uat',
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
// gaps_found items are already handled by plan-phase --gaps pipeline
|
|
251
|
+
return items;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
function categorizeItem(result, reason, blockedBy) {
|
|
255
|
+
if (result === 'blocked' || blockedBy) {
|
|
256
|
+
if (blockedBy) {
|
|
257
|
+
if (/server/i.test(blockedBy)) return 'server_blocked';
|
|
258
|
+
if (/device|physical/i.test(blockedBy)) return 'device_needed';
|
|
259
|
+
if (/build|release|preview/i.test(blockedBy)) return 'build_needed';
|
|
260
|
+
if (/third.party|twilio|stripe/i.test(blockedBy)) return 'third_party';
|
|
261
|
+
}
|
|
262
|
+
return 'blocked';
|
|
263
|
+
}
|
|
264
|
+
if (result === 'skipped') {
|
|
265
|
+
if (reason) {
|
|
266
|
+
if (/server|not running|not available/i.test(reason)) return 'server_blocked';
|
|
267
|
+
if (/simulator|physical|device/i.test(reason)) return 'device_needed';
|
|
268
|
+
if (/build|release|preview/i.test(reason)) return 'build_needed';
|
|
269
|
+
}
|
|
270
|
+
return 'skipped_unresolved';
|
|
271
|
+
}
|
|
272
|
+
if (result === 'pending') return 'pending';
|
|
273
|
+
if (result === 'human_needed') return 'human_uat';
|
|
274
|
+
return 'unknown';
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
module.exports = {
|
|
278
|
+
cmdAuditUat,
|
|
279
|
+
cmdRenderCheckpoint,
|
|
280
|
+
parseCurrentTest,
|
|
281
|
+
buildCheckpoint,
|
|
282
|
+
};
|