claude-flow-novice 2.6.0 → 2.8.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/.claude/adaptive-context/cfn-v3-reflection.json +21 -0
- package/.claude/agents/AGENT_LIFECYCLE.md +495 -0
- package/.claude/agents/CLAUDE.md +1002 -995
- package/.claude/agents/accessibility-advocate.md +457 -0
- package/.claude/agents/agent-principles/README.md +226 -226
- package/.claude/agents/agent-principles/agent-type-guidelines.md +10 -0
- package/.claude/agents/agent-principles/format-selection.md +10 -0
- package/.claude/agents/agent-principles/phase4-template-optimization.md +502 -494
- package/.claude/agents/agent-principles/prompt-engineering.md +8 -0
- package/.claude/agents/agent-principles/quality-metrics.md +8 -0
- package/.claude/agents/analysis/code-analyzer.md +7 -17
- package/.claude/agents/analysis/code-review/analyze-code-quality.md +2 -104
- package/.claude/agents/analysis/perf-analyzer.md +2 -196
- package/.claude/agents/context/context-curator.md +78 -84
- package/.claude/agents/context/context-reflector.md +27 -81
- package/.claude/agents/coordinators/README.md +42 -0
- package/.claude/agents/coordinators/cfn-v3-coordinator.md +440 -0
- package/.claude/agents/{product-owner-team → coordinators}/cto-agent.md +154 -187
- package/.claude/agents/coordinators/multi-sprint-coordinator.md +50 -0
- package/.claude/agents/{product-owner-team → coordinators}/product-owner-agent.md +6 -39
- package/.claude/agents/{cfn-loop → coordinators}/product-owner.md +72 -17
- package/.claude/agents/core-agents/reviewer.md +114 -135
- package/.claude/agents/custom/agent-builder.md +637 -637
- package/.claude/agents/developers/README.md +69 -0
- package/.claude/agents/developers/backend-dev.md +77 -0
- package/.claude/agents/{core-agents → developers}/coder.md +131 -26
- package/.claude/agents/developers/react-frontend-engineer.md +121 -0
- package/.claude/agents/{frontend → developers}/state-architect.md +1 -0
- package/.claude/agents/{frontend → developers}/ui-designer.md +1 -0
- package/.claude/agents/development/backend/dev-backend-api.md +0 -29
- package/.claude/agents/development/npm-package-specialist.md +355 -347
- package/.claude/agents/documentation/api-docs/docs-api-openapi.md +8 -0
- package/.claude/agents/documentation/api-docs.md +8 -0
- package/.claude/agents/github/github-commit-agent.md +125 -117
- package/.claude/agents/goal/goal-planner.md +8 -0
- package/.claude/agents/infrastructure/README.md +100 -0
- package/.claude/agents/{specialized → infrastructure}/devops-engineer.md +131 -150
- package/.claude/agents/planners/README.md +94 -0
- package/.claude/agents/{core-agents → planners}/analyst.md +1 -22
- package/.claude/agents/{planning-team → planners}/api-designer-persona.md +8 -0
- package/.claude/agents/{core-agents → planners}/architect.md +7 -20
- package/.claude/agents/{core-agents → planners}/planner.md +0 -21
- package/.claude/agents/{planning-team → planners}/security-architect-persona.md +8 -28
- package/.claude/agents/{planning-team → planners}/system-architect-persona.md +6 -38
- package/.claude/agents/{architecture → planners}/system-architect.md +12 -17
- package/.claude/agents/product-owner-team/accessibility-advocate-persona.md +132 -161
- package/.claude/agents/product-owner-team/power-user-persona.md +149 -182
- package/.claude/agents/retrospective-analyst.md +84 -0
- package/.claude/agents/reviewers/README.md +58 -0
- package/.claude/agents/{analysis → reviewers}/code-quality-validator.md +8 -17
- package/.claude/agents/reviewers/reviewer.md +181 -0
- package/.claude/agents/sparc/architecture.md +6 -25
- package/.claude/agents/sparc/pseudocode.md +6 -0
- package/.claude/agents/sparc/refinement.md +6 -0
- package/.claude/agents/sparc/specification.md +1 -0
- package/.claude/agents/specialists/README.md +60 -0
- package/.claude/agents/{core-agents → specialists}/base-template-generator.md +8 -21
- package/.claude/agents/{specialized → specialists}/cli-agent-optimizer.md +1 -1
- package/.claude/agents/{specialized → specialists}/code-booster.md +1 -0
- package/.claude/agents/{consensus → specialists}/consensus-builder.md +1 -17
- package/.claude/agents/{specialized/mobile → specialists}/mobile-dev.md +0 -20
- package/.claude/agents/{core-agents → specialists}/performance-benchmarker.md +134 -148
- package/.claude/agents/{specialized → specialists}/rust-developer.md +1 -20
- package/.claude/agents/{specialized → specialists}/rust-enterprise-developer.md +1 -20
- package/.claude/agents/{specialized → specialists}/rust-mvp-developer.md +1 -20
- package/.claude/agents/{core-agents → specialists}/security-manager.md +68 -88
- package/.claude/agents/{security → specialists}/security-specialist-existing.md +6 -57
- package/.claude/agents/{security → specialists}/security-specialist.md +6 -30
- package/.claude/agents/{specialized/mobile → specialists}/spec-mobile-react-native.md +2 -21
- package/.claude/agents/testers/README.md +94 -0
- package/.claude/agents/{testing → testers}/e2e/playwright-agent.md +1 -20
- package/.claude/agents/{testing → testers}/interaction-tester.md +1 -20
- package/.claude/agents/{testing → testers}/playwright-tester.md +1 -1
- package/.claude/agents/testers/tester.md +139 -0
- package/.claude/agents/testers/unit/tdd-london-swarm.md +49 -0
- package/.claude/agents/testers/validation/production-validator.md +33 -0
- package/.claude/agents-ignore/cfn-loop-coordinator.md +157 -0
- package/.claude/agents-ignore/cfn-loop-coordinator.md.backup +156 -0
- package/.claude/agents-ignore/coordinator.md.backup +182 -0
- package/.claude/agents-ignore/cost-savings-cfn-loop-coordinator.md +760 -0
- package/.claude/agents-ignore/cost-savings-coordinator.md +173 -0
- package/.claude/artifacts/ace-reflections/REFLECT-001-summary.json +39 -0
- package/.claude/artifacts/ace-reflections/sprint-7_$(date -u +/"%Y%m%d_%H%M%S/").json" +47 -0
- package/.claude/commands/CFN_COORDINATOR_PARAMETERS.md +10 -10
- package/.claude/commands/cfn-loop-epic.md +3 -3
- package/.claude/commands/cfn-loop-single.md +3 -3
- package/.claude/commands/cfn-loop-sprints.md +1 -1
- package/.claude/commands/cfn-loop.md +3 -3
- package/.claude/commands/cfn-mode.md +20 -0
- package/.claude/commands/write-plan.md +104 -0
- package/.claude/data/cfn-loop.db +0 -0
- package/.claude/data/cfn_loop_logs.db +0 -0
- package/.claude/hooks/BACKUP_USAGE.md +243 -0
- package/.claude/hooks/post-edit-cfn-retrospective.sh +79 -0
- package/.claude/hooks/post-edit.sh +21 -0
- package/.claude/hooks/pre-edit-backup.sh +71 -0
- package/.claude/hooks/restore-from-backup.sh +37 -0
- package/.claude/prompts/cfn-loop-context.md +115 -0
- package/.claude/prompts/loop-specific/loop2.md +50 -0
- package/.claude/prompts/loop-specific/loop3.md +43 -0
- package/.claude/prompts/loop-specific/loop4.md +54 -0
- package/.claude/root-claude-distribute/CLAUDE.md +76 -2
- package/.claude/skills/ace-system/sprint-7-lessons.json +46 -0
- package/.claude/skills/ace-system/store-reflection.sh +33 -136
- package/.claude/skills/agent-discovery/SKILL.md +40 -0
- package/.claude/skills/agent-discovery/agents-registry-clean.json +0 -0
- package/.claude/skills/agent-discovery/agents-registry-fixed.json +19 -0
- package/.claude/skills/agent-discovery/agents-registry.json +718 -0
- package/.claude/skills/agent-discovery/discover-agents.py +175 -0
- package/.claude/skills/agent-discovery/discover-agents.sh +87 -0
- package/.claude/skills/agent-discovery/invoke-registry.sh +11 -0
- package/.claude/skills/agent-discovery/temp_script.py +0 -0
- package/.claude/skills/agent-output-processing/SKILL.md +359 -0
- package/.claude/skills/agent-selector/SKILL.md +90 -0
- package/.claude/skills/agent-selector/select-agents.sh +96 -0
- package/.claude/skills/agent-spawning/agent-selection-guide.md +1 -1
- package/.claude/skills/agent-swap/SKILL.md +36 -0
- package/.claude/skills/agent-swap/recommend-swap.sh +60 -0
- package/.claude/skills/api-validation/test-endpoints.sh +54 -0
- package/.claude/skills/automatic-memory-persistence/SKILL.md +73 -0
- package/.claude/skills/automatic-memory-persistence/persist-agent-output.sh +49 -0
- package/.claude/skills/automatic-memory-persistence/query-agent-history.sh +35 -0
- package/.claude/skills/automatic-memory-persistence/test-memory-persistence.sh +235 -0
- package/.claude/skills/cfn-loop-orchestration/README.md +41 -0
- package/.claude/skills/cfn-loop-orchestration/SKILL.md +299 -0
- package/.claude/skills/cfn-loop-orchestration/helpers/auto-tune-timeouts.sh +228 -0
- package/.claude/skills/cfn-loop-orchestration/helpers/consensus.sh +84 -0
- package/.claude/skills/cfn-loop-orchestration/helpers/deliverable-verifier.sh +71 -0
- package/.claude/skills/cfn-loop-orchestration/helpers/gate-check.sh +90 -0
- package/.claude/skills/cfn-loop-orchestration/helpers/iteration-manager.sh +87 -0
- package/.claude/skills/cfn-loop-orchestration/helpers/timeout-calculator.sh +51 -0
- package/.claude/skills/cfn-loop-orchestration/inject-loop-context.sh +41 -0
- package/.claude/skills/cfn-loop-orchestration/monitor-execution.sh +156 -0
- package/.claude/skills/cfn-loop-orchestration/orchestrate.sh +840 -0
- package/.claude/skills/cfn-loop-orchestration/security_utils.sh +99 -0
- package/.claude/skills/cfn-loop-orchestration/test-cfn-orchestration.sh +281 -0
- package/.claude/skills/cfn-loop-orchestration/test-edge-cases.sh +188 -0
- package/.claude/skills/cfn-loop-validation/SKILL.md +307 -217
- package/.claude/skills/complexity-estimator/SKILL.md +96 -0
- package/.claude/skills/complexity-estimator/estimate-complexity.sh +144 -0
- package/.claude/skills/context-pruner/SKILL.md +75 -0
- package/.claude/skills/context-pruner/prune-context.sh +73 -0
- package/.claude/skills/defense-in-depth/SKILL.md +133 -0
- package/.claude/skills/dependency-extractor/SKILL.md +35 -0
- package/.claude/skills/dependency-extractor/extract-dependencies.sh +66 -0
- package/.claude/skills/epic-decomposer/SKILL.md +44 -0
- package/.claude/skills/epic-decomposer/decompose-epic.sh +104 -0
- package/.claude/skills/improvement-recommender/SKILL.md +33 -0
- package/.claude/skills/improvement-recommender/recommend-improvements.sh +92 -0
- package/.claude/skills/intervention-detector/SKILL.md +39 -0
- package/.claude/skills/intervention-detector/detect-intervention.sh +111 -0
- package/.claude/skills/intervention-orchestrator/SKILL.md +43 -0
- package/.claude/skills/intervention-orchestrator/execute-intervention.sh +59 -0
- package/.claude/skills/loop2-output-processing/SKILL.md +163 -0
- package/.claude/skills/loop2-output-processing/execute-and-extract.sh +77 -0
- package/.claude/skills/loop2-output-processing/execute-and-extract.sh.backup +36 -0
- package/.claude/skills/loop2-output-processing/parse-feedback.sh +147 -0
- package/.claude/skills/loop2-output-processing/process-validator-output.sh +275 -0
- package/.claude/skills/loop2-output-processing/test-bug27-fix.sh +200 -0
- package/.claude/skills/loop2-output-processing/test-loop2-processing.sh +113 -0
- package/.claude/skills/loop3-output-processing/AGENT_COMPLETION_PROTOCOL.md +206 -0
- package/.claude/skills/loop3-output-processing/SKILL.md +421 -0
- package/.claude/skills/loop3-output-processing/calculate-confidence.sh +28 -0
- package/.claude/skills/loop3-output-processing/execute-and-extract.sh +85 -0
- package/.claude/skills/loop3-output-processing/parse-confidence.sh +31 -0
- package/.claude/skills/loop3-output-processing/test-agent-timeout.sh +327 -0
- package/.claude/skills/loop3-output-processing/test-loop3-processing.sh +155 -0
- package/.claude/skills/loop3-output-processing/verify-deliverables.sh +42 -0
- package/.claude/skills/pattern-extraction/SKILL.md +30 -0
- package/.claude/skills/pattern-extraction/extract-patterns.sh +80 -0
- package/.claude/skills/playbook/SKILL.md +113 -0
- package/.claude/skills/playbook/init-playbook.sh +54 -0
- package/.claude/skills/playbook/playbook.db +0 -0
- package/.claude/skills/playbook/query-playbook.sh +79 -0
- package/.claude/skills/playbook/update-playbook.sh +69 -0
- package/.claude/skills/playbook-auto-update/SKILL.md +29 -0
- package/.claude/skills/playbook-auto-update/auto-update-playbook.sh +86 -0
- package/.claude/skills/product-owner-decision/SKILL.md +332 -0
- package/.claude/skills/product-owner-decision/execute-decision.sh +176 -0
- package/.claude/skills/product-owner-decision/parse-decision.sh +66 -0
- package/.claude/skills/product-owner-decision/validate-deliverables.sh +82 -0
- package/.claude/skills/redis-coordination/LOGGING.md +260 -0
- package/.claude/skills/redis-coordination/README.md +30 -29
- package/.claude/skills/redis-coordination/SKILL.md +685 -83
- package/.claude/skills/redis-coordination/analyze-task-complexity.sh +277 -0
- package/.claude/skills/redis-coordination/cfn-loop-exec.sh +468 -0
- package/.claude/skills/redis-coordination/collect-confidence-scores.sh +179 -0
- package/.claude/skills/redis-coordination/collect-results.sh +75 -0
- package/.claude/skills/redis-coordination/data/cfn-loop.db +0 -0
- package/.claude/skills/redis-coordination/{test-orchestrator.sh → demos/test-orchestrator.sh} +25 -0
- package/.claude/skills/redis-coordination/execute-product-owner-decision.sh +258 -0
- package/.claude/skills/redis-coordination/get-agent-timeout.sh +176 -176
- package/.claude/skills/redis-coordination/invoke-waiting-mode.sh +93 -227
- package/.claude/skills/redis-coordination/invoke-waiting-mode.sh.backup-p7 +423 -0
- package/.claude/skills/redis-coordination/log-event.sh +109 -0
- package/.claude/skills/redis-coordination/monitor-cfn-violations.sh +391 -0
- package/.claude/skills/redis-coordination/orchestrate-cfn-loop-v3.sh +141 -0
- package/.claude/skills/redis-coordination/orchestrate-cfn-loop.sh +31 -993
- package/.claude/skills/redis-coordination/orchestrate-cfn-loop.sh.backup +38 -0
- package/.claude/skills/redis-coordination/orchestrate-cfn-loop.sh.backup-1761167675 +1672 -0
- package/.claude/skills/redis-coordination/orchestrate-cfn-loop.sh.backup-p5 +1604 -0
- package/.claude/skills/redis-coordination/orchestrate-cfn-loop.sh.backup-phase1 +1550 -0
- package/.claude/skills/redis-coordination/orchestrate-cfn-loop.sh.backup-phase2 +1621 -0
- package/.claude/skills/redis-coordination/orchestrate-cfn-loop.sh.backup-phase3 +1621 -0
- package/.claude/skills/redis-coordination/orchestrate-cfn-loop.sh.bak +0 -0
- package/.claude/skills/redis-coordination/orchestrate-cfn-loop.sh.broken +1627 -0
- package/.claude/skills/redis-coordination/orchestrate-cfn-loop.sh.corrupted +80 -0
- package/.claude/skills/redis-coordination/orchestrate-cfn-loop.sh.deprecated +1864 -0
- package/.claude/skills/redis-coordination/query-logs.sh +103 -0
- package/.claude/skills/redis-coordination/retrieve-context.sh +58 -0
- package/.claude/skills/redis-coordination/select-specialist-agent.sh +371 -0
- package/.claude/skills/redis-coordination/semantic-match-tfidf.py +252 -0
- package/.claude/skills/redis-coordination/send-heartbeat.sh +164 -72
- package/.claude/skills/redis-coordination/signal.sh +38 -0
- package/.claude/skills/redis-coordination/store-context.sh +86 -0
- package/.claude/skills/redis-coordination/test-context-injection.sh +354 -0
- package/.claude/skills/redis-coordination/test-timeout-enforcement.sh +513 -0
- package/.claude/skills/redis-coordination/tests/convert-line-endings.sh +15 -0
- package/.claude/skills/redis-coordination/tests/dlq-functionality-test.sh +101 -101
- package/.claude/skills/redis-coordination/tests/edge-cases-test.sh +98 -98
- package/.claude/skills/redis-coordination/tests/integration-test.sh +169 -169
- package/.claude/skills/redis-coordination/tests/retry-mechanism-test.sh +81 -81
- package/.claude/skills/redis-coordination/tests/run-test-suite.sh +91 -91
- package/.claude/skills/redis-coordination/tests/run-tests.sh +4 -0
- package/.claude/skills/redis-coordination/tests/test-primitives.sh +166 -0
- package/.claude/skills/redis-coordination/tests/test-utils.sh +53 -121
- package/.claude/skills/redis-coordination/tests/test_coordination_primitives.sh.deprecated +20 -0
- package/.claude/skills/redis-coordination/tests/test_utils.sh +49 -0
- package/.claude/skills/redis-coordination/v2_modularization/core_orchestration.sh +76 -0
- package/.claude/skills/redis-coordination/validate-parameters.sh +492 -0
- package/.claude/skills/retrospective-report/SKILL.md +31 -0
- package/.claude/skills/retrospective-report/generate-report.sh +101 -0
- package/.claude/skills/run-all-skill-tests.sh +124 -0
- package/.claude/skills/scope-simplifier/SKILL.md +37 -0
- package/.claude/skills/scope-simplifier/simplify-scope.sh +68 -0
- package/.claude/skills/simplified-agent-lifecycle/COST_ANALYSIS.md +49 -0
- package/.claude/skills/simplified-agent-lifecycle/DESIGN.md +98 -0
- package/.claude/skills/simplified-agent-lifecycle/MIGRATION_PLAN.md +74 -0
- package/.claude/skills/specialist-injection/SKILL.md +41 -0
- package/.claude/skills/specialist-injection/recommend-specialist.sh +57 -0
- package/.claude/skills/sprint-execution/SKILL.md +27 -0
- package/.claude/skills/sprint-execution/execute-sprint-task.sh +59 -0
- package/.claude/skills/sprint-execution/execute-sprint.sh +65 -0
- package/.claude/skills/sprint-planner/SKILL.md +37 -0
- package/.claude/skills/sprint-planner/plan-sprint.sh +85 -0
- package/.claude/skills/standardized-error-handling/SKILL.md +56 -0
- package/.claude/skills/standardized-error-handling/capture-agent-error.sh +87 -0
- package/.claude/skills/standardized-error-handling/test-error-handling.sh +166 -0
- package/.claude/skills/task-classifier/SKILL.md +94 -0
- package/.claude/skills/task-classifier/classify-task.sh +115 -0
- package/.claude/skills/validation-templates/SKILL.md +47 -0
- package/.claude/skills/validation-templates/content.json +38 -0
- package/.claude/skills/validation-templates/data.json +38 -0
- package/.claude/skills/validation-templates/design.json +38 -0
- package/.claude/skills/validation-templates/infrastructure.json +38 -0
- package/.claude/skills/validation-templates/research.json +38 -0
- package/.claude/skills/validation-templates/software.json +38 -0
- package/.claude/skills/webapp-testing/README.md +142 -0
- package/.claude/skills/webapp-testing/SCREENSHOT_NAMING_CONVENTION.md +547 -0
- package/.claude/skills/webapp-testing/SKILL.md +877 -0
- package/.claude/skills/webapp-testing/capture-screenshot.sh +238 -0
- package/.claude/skills/webapp-testing/cfn-loop-integration.sh +265 -0
- package/.claude/skills/webapp-testing/compare-screenshots.sh +199 -0
- package/.claude/skills/webapp-testing/init-storage.sh +150 -0
- package/.claude/skills/webapp-testing/set-baseline.sh +196 -0
- package/.claude/skills/webapp-testing/test-webapp-testing.sh +233 -0
- package/.claude/spawn-pattern-examples.md +3 -3
- package/CLAUDE.md +319 -45
- package/README.md +598 -251
- package/dist/agents/agent-loader.js +146 -165
- package/dist/agents/agent-loader.js.map +1 -1
- package/dist/cli/agent-command.js +2 -0
- package/dist/cli/agent-command.js.map +1 -1
- package/dist/cli/agent-definition-parser.js +7 -0
- package/dist/cli/agent-definition-parser.js.map +1 -1
- package/dist/cli/agent-executor.js +145 -11
- package/dist/cli/agent-executor.js.map +1 -1
- package/dist/cli/agent-prompt-builder.js +81 -1
- package/dist/cli/agent-prompt-builder.js.map +1 -1
- package/dist/cli/agent-spawn.js +10 -1
- package/dist/cli/agent-spawn.js.map +1 -1
- package/dist/cli/anthropic-client.js +192 -13
- package/dist/cli/anthropic-client.js.map +1 -1
- package/dist/cli/cfn-context.js +150 -0
- package/dist/cli/cfn-context.js.map +1 -1
- package/dist/cli/cfn-fork.js +159 -0
- package/dist/cli/cfn-fork.js.map +1 -0
- package/dist/cli/cli-agent-context.js +8 -3
- package/dist/cli/cli-agent-context.js.map +1 -1
- package/dist/cli/config-manager.js +109 -91
- package/dist/cli/config-manager.js.map +1 -1
- package/dist/cli/conversation-fork.js +201 -0
- package/dist/cli/conversation-fork.js.map +1 -0
- package/dist/cli/index.js +4 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/tool-definitions.js +263 -0
- package/dist/cli/tool-definitions.js.map +1 -0
- package/dist/cli/tool-executor.js +247 -0
- package/dist/cli/tool-executor.js.map +1 -0
- package/dist/hello.js +8 -0
- package/dist/hello.js.map +1 -0
- package/package.json +14 -6
- package/scripts/README.md +68 -0
- package/scripts/cfn-intervention-example.sh +21 -0
- package/scripts/migrate-test-infrastructure.sh +40 -0
- package/scripts/validate-test-migration.sh +49 -0
- package/scripts/verify-no-secrets.sh +55 -0
- package/.claude/agents/architecture/system-architect.md.backup +0 -603
- package/.claude/agents/code-booster.md +0 -131
- package/.claude/agents/consensus/performance-benchmarker.md +0 -101
- package/.claude/agents/consensus/security-manager.md +0 -107
- package/.claude/agents/context-curator.md +0 -167
- package/.claude/agents/context-reflector.md +0 -65
- package/.claude/agents/core-agents/cfn-loop-coordinator.md +0 -134
- package/.claude/agents/core-agents/code-quality-validator.md +0 -149
- package/.claude/agents/core-agents/context-curator.md +0 -452
- package/.claude/agents/core-agents/context-reflector.md +0 -273
- package/.claude/agents/core-agents/cost-savings-cfn-loop-coordinator.md +0 -235
- package/.claude/agents/core-agents/tester.md +0 -170
- package/.claude/agents/development/backend-dev.md +0 -165
- package/.claude/agents/devops/devops-engineer.md +0 -148
- package/.claude/agents/frontend/interaction-tester.md +0 -139
- package/.claude/agents/frontend/react-frontend-engineer.md +0 -9
- package/.claude/agents/personas/accessibility-advocate-persona.md +0 -107
- package/.claude/agents/testing/production-validator.md +0 -179
- package/.claude/agents/testing/tdd-london-swarm.md +0 -209
- package/.claude/agents/testing/unit/tdd-london-swarm.md +0 -43
- package/.claude/agents/testing/validation/production-validator.md +0 -43
- package/.claude/api-configs/config-current-zai-config.env +0 -62
- package/.claude/api-configs/config-test-zai-config.env +0 -62
- package/.claude/api-configs/env-backups/before-anthropic-20251020-025404.env +0 -62
- package/.claude/api-configs/env-backups/before-restore-20251020-025431.env +0 -62
- package/.claude/skills/redis-coordination/orchestrate-cfn-loop.sh.backup-1760949407 +0 -933
- package/dist/cli/cli-agent-context.test.js +0 -451
- package/dist/cli/cli-agent-context.test.js.map +0 -1
- package/dist/coordination/fleet-manager.test.js +0 -141
- package/dist/coordination/fleet-manager.test.js.map +0 -1
- package/dist/middleware/transparency-middleware.test.js +0 -184
- package/dist/middleware/transparency-middleware.test.js.map +0 -1
- /package/.claude/agents/{core-agents → developers}/researcher.md +0 -0
- /package/.claude/agents/{consensus → specialists}/crdt-synchronizer.md +0 -0
- /package/.claude/agents/{consensus → specialists}/quorum-manager.md +0 -0
- /package/.claude/agents/{consensus → specialists}/raft-manager.md +0 -0
- /package/.claude/{agents/core-agents → agents-ignore}/coordinator.md +0 -0
- /package/.claude/{agents/core-agents/cost-savings-coordinator.md → agents-ignore/cost-savings-coordinator.md.backup} +0 -0
- /package/.claude/skills/redis-coordination/{phase4-wake-queue-test-report.md → demos/phase4-wake-queue-test-report.md} +0 -0
- /package/.claude/skills/redis-coordination/{test-bzpopmin-fix.sh → demos/test-bzpopmin-fix.sh} +0 -0
- /package/.claude/skills/redis-coordination/{test-cancel-swarm.sh → demos/test-cancel-swarm.sh} +0 -0
- /package/.claude/skills/redis-coordination/{test-dlq.sh → demos/test-dlq.sh} +0 -0
- /package/.claude/skills/redis-coordination/{test-iteration-feedback.sh → demos/test-iteration-feedback.sh} +0 -0
- /package/.claude/skills/redis-coordination/{test-priority-wake-phase4-unix.sh → demos/test-priority-wake-phase4-unix.sh} +0 -0
- /package/.claude/skills/redis-coordination/{test-priority-wake-phase4.sh → demos/test-priority-wake-phase4.sh} +0 -0
- /package/.claude/skills/redis-coordination/{test-priority-wake.sh → demos/test-priority-wake.sh} +0 -0
- /package/.claude/skills/redis-coordination/{test-quick-fix.sh → demos/test-quick-fix.sh} +0 -0
- /package/.claude/skills/redis-coordination/{test-quorum-absolute.sh → demos/test-quorum-absolute.sh} +0 -0
- /package/.claude/skills/redis-coordination/{test-quorum-fallback.sh → demos/test-quorum-fallback.sh} +0 -0
- /package/.claude/skills/redis-coordination/{test-quorum-percentage.sh → demos/test-quorum-percentage.sh} +0 -0
- /package/.claude/skills/redis-coordination/{test-quorum-with-retry.sh → demos/test-quorum-with-retry.sh} +0 -0
- /package/.claude/skills/redis-coordination/{test-quorum.sh → demos/test-quorum.sh} +0 -0
- /package/.claude/skills/redis-coordination/{test-shutdown-handling.sh → demos/test-shutdown-handling.sh} +0 -0
- /package/.claude/skills/redis-coordination/{test-shutdown.sh → demos/test-shutdown.sh} +0 -0
- /package/.claude/skills/redis-coordination/{test-utils-unix.sh → demos/test-utils-unix.sh} +0 -0
- /package/.claude/skills/redis-coordination/{test-utils.sh → demos/test-utils.sh} +0 -0
- /package/.claude/skills/redis-coordination/{test-waiting-mode.sh → demos/test-waiting-mode.sh} +0 -0
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Webapp Testing Skill - Screenshot Capture
|
|
3
|
+
# Purpose: Capture screenshots with Playwright, store metadata in SQLite, coordinate via Redis
|
|
4
|
+
|
|
5
|
+
set -e
|
|
6
|
+
|
|
7
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
8
|
+
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)"
|
|
9
|
+
cd "$PROJECT_ROOT"
|
|
10
|
+
|
|
11
|
+
# Default configuration
|
|
12
|
+
VARIANT="default"
|
|
13
|
+
TIMEOUT=30000
|
|
14
|
+
WAIT_FOR=""
|
|
15
|
+
SELECTOR=""
|
|
16
|
+
|
|
17
|
+
# Parse arguments
|
|
18
|
+
while [[ $# -gt 0 ]]; do
|
|
19
|
+
case $1 in
|
|
20
|
+
--project) PROJECT="$2"; shift 2 ;;
|
|
21
|
+
--component) COMPONENT="$2"; shift 2 ;;
|
|
22
|
+
--viewport) VIEWPORT="$2"; shift 2 ;;
|
|
23
|
+
--state) STATE="$2"; shift 2 ;;
|
|
24
|
+
--variant) VARIANT="$2"; shift 2 ;;
|
|
25
|
+
--url) URL="$2"; shift 2 ;;
|
|
26
|
+
--task-id) TASK_ID="$2"; shift 2 ;;
|
|
27
|
+
--agent-id) AGENT_ID="$2"; shift 2 ;;
|
|
28
|
+
--selector) SELECTOR="$2"; shift 2 ;;
|
|
29
|
+
--wait-for) WAIT_FOR="$2"; shift 2 ;;
|
|
30
|
+
--timeout) TIMEOUT="$2"; shift 2 ;;
|
|
31
|
+
*) echo "Unknown parameter: $1"; exit 1 ;;
|
|
32
|
+
esac
|
|
33
|
+
done
|
|
34
|
+
|
|
35
|
+
# Validate required parameters
|
|
36
|
+
if [ -z "$PROJECT" ] || [ -z "$COMPONENT" ] || [ -z "$VIEWPORT" ] || [ -z "$STATE" ] || [ -z "$URL" ]; then
|
|
37
|
+
echo "Error: Missing required parameters" >&2
|
|
38
|
+
echo "Usage: $0 --project <project> --component <component> --viewport <viewport> --state <state> --url <url>" >&2
|
|
39
|
+
echo "" >&2
|
|
40
|
+
echo "Required:" >&2
|
|
41
|
+
echo " --project Application/feature namespace (e.g., 'auth-system')" >&2
|
|
42
|
+
echo " --component UI component or page (e.g., 'login-form')" >&2
|
|
43
|
+
echo " --viewport Screen dimensions (e.g., '1920x1080', '375x667')" >&2
|
|
44
|
+
echo " --state Interaction state (e.g., 'default', 'hover', 'error')" >&2
|
|
45
|
+
echo " --url Page URL (e.g., 'http://localhost:3000/login')" >&2
|
|
46
|
+
echo "" >&2
|
|
47
|
+
echo "Optional:" >&2
|
|
48
|
+
echo " --variant Theme/variant (default: 'default')" >&2
|
|
49
|
+
echo " --task-id CFN task identifier" >&2
|
|
50
|
+
echo " --agent-id Agent identifier" >&2
|
|
51
|
+
echo " --selector CSS selector for element capture" >&2
|
|
52
|
+
echo " --wait-for CSS selector to wait for before capture" >&2
|
|
53
|
+
echo " --timeout Playwright timeout in ms (default: 30000)" >&2
|
|
54
|
+
exit 1
|
|
55
|
+
fi
|
|
56
|
+
|
|
57
|
+
# Generate screenshot key and paths
|
|
58
|
+
TIMESTAMP=$(date +%Y%m%d%H%M%S)
|
|
59
|
+
SCREENSHOT_KEY="${PROJECT}/${COMPONENT}/${VIEWPORT}/${STATE}/${VARIANT}"
|
|
60
|
+
OUTPUT_DIR=".screenshots/current/${PROJECT}/${COMPONENT}/${VIEWPORT}/${STATE}"
|
|
61
|
+
OUTPUT_FILE="${OUTPUT_DIR}/${VARIANT}_${TIMESTAMP}.png"
|
|
62
|
+
|
|
63
|
+
mkdir -p "$OUTPUT_DIR"
|
|
64
|
+
|
|
65
|
+
# Parse viewport dimensions
|
|
66
|
+
IFS='x' read -r WIDTH HEIGHT <<< "$VIEWPORT"
|
|
67
|
+
|
|
68
|
+
# Validate viewport dimensions
|
|
69
|
+
if ! [[ "$WIDTH" =~ ^[0-9]+$ ]] || ! [[ "$HEIGHT" =~ ^[0-9]+$ ]]; then
|
|
70
|
+
echo "Error: Invalid viewport format. Expected format: WIDTHxHEIGHT (e.g., 1920x1080)" >&2
|
|
71
|
+
exit 1
|
|
72
|
+
fi
|
|
73
|
+
|
|
74
|
+
# Capture screenshot with Playwright (inline Node.js script)
|
|
75
|
+
echo "Capturing screenshot: $SCREENSHOT_KEY" >&2
|
|
76
|
+
|
|
77
|
+
CAPTURE_RESULT=$(node -e "
|
|
78
|
+
const { chromium } = require('playwright');
|
|
79
|
+
|
|
80
|
+
(async () => {
|
|
81
|
+
const browser = await chromium.launch({ headless: true });
|
|
82
|
+
const context = await browser.newContext({
|
|
83
|
+
viewport: { width: ${WIDTH}, height: ${HEIGHT} },
|
|
84
|
+
deviceScaleFactor: 1
|
|
85
|
+
});
|
|
86
|
+
const page = await context.newPage();
|
|
87
|
+
|
|
88
|
+
try {
|
|
89
|
+
await page.goto('${URL}', { waitUntil: 'networkidle', timeout: ${TIMEOUT} });
|
|
90
|
+
|
|
91
|
+
${WAIT_FOR:+await page.waitForSelector('${WAIT_FOR}', { timeout: ${TIMEOUT} });}
|
|
92
|
+
|
|
93
|
+
// Simulate state if needed
|
|
94
|
+
${SELECTOR:+const element = await page.locator('${SELECTOR}').first();}
|
|
95
|
+
if ('${STATE}' === 'hover' && '${SELECTOR}') {
|
|
96
|
+
await element.hover();
|
|
97
|
+
await page.waitForTimeout(500); // Allow hover effects to render
|
|
98
|
+
} else if ('${STATE}' === 'focus' && '${SELECTOR}') {
|
|
99
|
+
await element.focus();
|
|
100
|
+
await page.waitForTimeout(300);
|
|
101
|
+
} else if ('${STATE}' === 'hover' && !'${SELECTOR}') {
|
|
102
|
+
console.error('Warning: hover state requires --selector parameter');
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const screenshotOptions = {
|
|
106
|
+
path: '${OUTPUT_FILE}',
|
|
107
|
+
type: 'png'
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
${SELECTOR:+
|
|
111
|
+
const boundingBox = await element.boundingBox();
|
|
112
|
+
if (boundingBox) {
|
|
113
|
+
screenshotOptions.clip = boundingBox;
|
|
114
|
+
} else {
|
|
115
|
+
console.error('Warning: Selector element not found, capturing full page');
|
|
116
|
+
screenshotOptions.fullPage = true;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
${SELECTOR:-screenshotOptions.fullPage = true;}
|
|
120
|
+
|
|
121
|
+
await page.screenshot(screenshotOptions);
|
|
122
|
+
|
|
123
|
+
await browser.close();
|
|
124
|
+
|
|
125
|
+
console.log(JSON.stringify({
|
|
126
|
+
screenshot_key: '${SCREENSHOT_KEY}',
|
|
127
|
+
file_path: '${OUTPUT_FILE}',
|
|
128
|
+
viewport_actual: { width: ${WIDTH}, height: ${HEIGHT} },
|
|
129
|
+
captured_at: Math.floor(Date.now() / 1000),
|
|
130
|
+
url: '${URL}',
|
|
131
|
+
state: '${STATE}',
|
|
132
|
+
selector: '${SELECTOR:-null}'
|
|
133
|
+
}));
|
|
134
|
+
} catch (error) {
|
|
135
|
+
await browser.close();
|
|
136
|
+
console.error('Playwright error:', error.message);
|
|
137
|
+
process.exit(1);
|
|
138
|
+
}
|
|
139
|
+
})();
|
|
140
|
+
" 2>&1)
|
|
141
|
+
|
|
142
|
+
# Check if capture succeeded
|
|
143
|
+
if [ $? -ne 0 ]; then
|
|
144
|
+
echo "Error: Screenshot capture failed" >&2
|
|
145
|
+
echo "$CAPTURE_RESULT" >&2
|
|
146
|
+
exit 1
|
|
147
|
+
fi
|
|
148
|
+
|
|
149
|
+
# Extract JSON from output (ignore stderr warnings)
|
|
150
|
+
CAPTURE_JSON=$(echo "$CAPTURE_RESULT" | grep -E '^\{.*\}$' | tail -n 1)
|
|
151
|
+
|
|
152
|
+
if [ -z "$CAPTURE_JSON" ]; then
|
|
153
|
+
echo "Error: Failed to parse capture result" >&2
|
|
154
|
+
echo "Output: $CAPTURE_RESULT" >&2
|
|
155
|
+
exit 1
|
|
156
|
+
fi
|
|
157
|
+
|
|
158
|
+
# Calculate file hash
|
|
159
|
+
if [ ! -f "$OUTPUT_FILE" ]; then
|
|
160
|
+
echo "Error: Screenshot file not created: $OUTPUT_FILE" >&2
|
|
161
|
+
exit 1
|
|
162
|
+
fi
|
|
163
|
+
|
|
164
|
+
FILE_HASH=$(sha256sum "$OUTPUT_FILE" | awk '{print $1}')
|
|
165
|
+
|
|
166
|
+
# Check if baseline exists
|
|
167
|
+
DB_PATH="${HOME}/.claude/memory/adaptive-context.db"
|
|
168
|
+
BASELINE_EXISTS=$(sqlite3 "$DB_PATH" \
|
|
169
|
+
"SELECT COUNT(*) FROM webapp_screenshots WHERE screenshot_key = '${SCREENSHOT_KEY}' AND baseline = 1" 2>/dev/null || echo "0")
|
|
170
|
+
|
|
171
|
+
# Get git metadata
|
|
172
|
+
GIT_COMMIT=$(git rev-parse --short HEAD 2>/dev/null || echo 'unknown')
|
|
173
|
+
GIT_BRANCH=$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo 'unknown')
|
|
174
|
+
|
|
175
|
+
# Prepare metadata JSON (escape single quotes)
|
|
176
|
+
METADATA=$(cat <<EOF | tr '\n' ' ' | sed "s/'/\"/g"
|
|
177
|
+
{
|
|
178
|
+
"browser": "chromium",
|
|
179
|
+
"viewport_actual": { "width": ${WIDTH}, "height": ${HEIGHT} },
|
|
180
|
+
"url": "${URL}",
|
|
181
|
+
"state": "${STATE}",
|
|
182
|
+
"selector": "${SELECTOR:-null}",
|
|
183
|
+
"task_id": "${TASK_ID:-null}",
|
|
184
|
+
"agent_id": "${AGENT_ID:-null}",
|
|
185
|
+
"git_commit": "${GIT_COMMIT}",
|
|
186
|
+
"git_branch": "${GIT_BRANCH}",
|
|
187
|
+
"captured_by": "webapp-testing-skill"
|
|
188
|
+
}
|
|
189
|
+
EOF
|
|
190
|
+
)
|
|
191
|
+
|
|
192
|
+
# Store metadata in SQLite
|
|
193
|
+
sqlite3 "$DB_PATH" <<EOF
|
|
194
|
+
INSERT INTO webapp_screenshots (
|
|
195
|
+
screenshot_key, project, component, viewport, state, variant,
|
|
196
|
+
file_path, file_hash, baseline, captured_at, task_id, agent_id, metadata
|
|
197
|
+
) VALUES (
|
|
198
|
+
'${SCREENSHOT_KEY}',
|
|
199
|
+
'${PROJECT}',
|
|
200
|
+
'${COMPONENT}',
|
|
201
|
+
'${VIEWPORT}',
|
|
202
|
+
'${STATE}',
|
|
203
|
+
'${VARIANT}',
|
|
204
|
+
'${OUTPUT_FILE}',
|
|
205
|
+
'sha256:${FILE_HASH}',
|
|
206
|
+
0,
|
|
207
|
+
$(date +%s),
|
|
208
|
+
'${TASK_ID:-null}',
|
|
209
|
+
'${AGENT_ID:-null}',
|
|
210
|
+
'${METADATA}'
|
|
211
|
+
);
|
|
212
|
+
EOF
|
|
213
|
+
|
|
214
|
+
if [ $? -ne 0 ]; then
|
|
215
|
+
echo "Warning: Failed to store metadata in SQLite" >&2
|
|
216
|
+
fi
|
|
217
|
+
|
|
218
|
+
# Push to Redis queue (for orchestrator tracking)
|
|
219
|
+
if [ -n "$TASK_ID" ]; then
|
|
220
|
+
redis-cli lpush "screenshot:queue:${TASK_ID}" "$SCREENSHOT_KEY" > /dev/null 2>&1 || echo "Warning: Failed to push to Redis queue" >&2
|
|
221
|
+
fi
|
|
222
|
+
|
|
223
|
+
# Output result (JSON)
|
|
224
|
+
cat <<EOF
|
|
225
|
+
{
|
|
226
|
+
"screenshot_key": "${SCREENSHOT_KEY}",
|
|
227
|
+
"file_path": "${OUTPUT_FILE}",
|
|
228
|
+
"file_hash": "sha256:${FILE_HASH}",
|
|
229
|
+
"viewport_actual": { "width": ${WIDTH}, "height": ${HEIGHT} },
|
|
230
|
+
"captured_at": $(date +%s),
|
|
231
|
+
"baseline_exists": $([[ "$BASELINE_EXISTS" -eq 1 ]] && echo "true" || echo "false"),
|
|
232
|
+
"url": "${URL}",
|
|
233
|
+
"state": "${STATE}",
|
|
234
|
+
"variant": "${VARIANT}",
|
|
235
|
+
"task_id": "${TASK_ID:-null}",
|
|
236
|
+
"agent_id": "${AGENT_ID:-null}"
|
|
237
|
+
}
|
|
238
|
+
EOF
|
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Webapp Testing Skill - CFN Loop Integration Helper
|
|
3
|
+
# Purpose: Helper functions for integrating visual regression testing into CFN Loop workflows
|
|
4
|
+
|
|
5
|
+
set -e
|
|
6
|
+
|
|
7
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
8
|
+
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)"
|
|
9
|
+
|
|
10
|
+
# Function: Capture all screenshots for a component
|
|
11
|
+
# Usage: capture_component_screenshots <project> <component> <url> <task-id> <agent-id> [viewports...]
|
|
12
|
+
capture_component_screenshots() {
|
|
13
|
+
local PROJECT="$1"
|
|
14
|
+
local COMPONENT="$2"
|
|
15
|
+
local URL="$3"
|
|
16
|
+
local TASK_ID="$4"
|
|
17
|
+
local AGENT_ID="$5"
|
|
18
|
+
shift 5
|
|
19
|
+
local VIEWPORTS=("${@:-1920x1080}") # Default to desktop
|
|
20
|
+
|
|
21
|
+
local STATES=("default" "hover" "error")
|
|
22
|
+
local VARIANTS=("light-mode" "dark-mode")
|
|
23
|
+
|
|
24
|
+
echo "Capturing screenshots for component: $COMPONENT" >&2
|
|
25
|
+
local TOTAL=$((${#VIEWPORTS[@]} * ${#STATES[@]} * ${#VARIANTS[@]}))
|
|
26
|
+
local COUNT=0
|
|
27
|
+
|
|
28
|
+
for VIEWPORT in "${VIEWPORTS[@]}"; do
|
|
29
|
+
for STATE in "${STATES[@]}"; do
|
|
30
|
+
for VARIANT in "${VARIANTS[@]}"; do
|
|
31
|
+
COUNT=$((COUNT + 1))
|
|
32
|
+
echo "[$COUNT/$TOTAL] Capturing: $VIEWPORT / $STATE / $VARIANT" >&2
|
|
33
|
+
|
|
34
|
+
"$SCRIPT_DIR/capture-screenshot.sh" \
|
|
35
|
+
--project "$PROJECT" \
|
|
36
|
+
--component "$COMPONENT" \
|
|
37
|
+
--viewport "$VIEWPORT" \
|
|
38
|
+
--state "$STATE" \
|
|
39
|
+
--variant "$VARIANT" \
|
|
40
|
+
--url "$URL" \
|
|
41
|
+
--task-id "$TASK_ID" \
|
|
42
|
+
--agent-id "$AGENT_ID" || echo "Warning: Failed to capture $VIEWPORT/$STATE/$VARIANT" >&2
|
|
43
|
+
done
|
|
44
|
+
done
|
|
45
|
+
done
|
|
46
|
+
|
|
47
|
+
echo "✅ Captured $COUNT screenshots" >&2
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
# Function: Compare all screenshots in task queue to baselines
|
|
51
|
+
# Usage: compare_all_screenshots <task-id> [threshold]
|
|
52
|
+
compare_all_screenshots() {
|
|
53
|
+
local TASK_ID="$1"
|
|
54
|
+
local THRESHOLD="${2:-0.95}"
|
|
55
|
+
|
|
56
|
+
echo "Comparing all screenshots in task queue: $TASK_ID" >&2
|
|
57
|
+
|
|
58
|
+
# Get all screenshot keys from Redis queue
|
|
59
|
+
local SCREENSHOT_KEYS=$(redis-cli lrange "screenshot:queue:${TASK_ID}" 0 -1 2>/dev/null || echo "")
|
|
60
|
+
|
|
61
|
+
if [ -z "$SCREENSHOT_KEYS" ]; then
|
|
62
|
+
echo "Warning: No screenshots found in queue for task: $TASK_ID" >&2
|
|
63
|
+
return 1
|
|
64
|
+
fi
|
|
65
|
+
|
|
66
|
+
local TOTAL=$(echo "$SCREENSHOT_KEYS" | wc -l)
|
|
67
|
+
local COUNT=0
|
|
68
|
+
local PASSED=0
|
|
69
|
+
local FAILED=0
|
|
70
|
+
local NO_BASELINE=0
|
|
71
|
+
|
|
72
|
+
echo "$SCREENSHOT_KEYS" | while read -r SCREENSHOT_KEY; do
|
|
73
|
+
[ -z "$SCREENSHOT_KEY" ] && continue
|
|
74
|
+
|
|
75
|
+
COUNT=$((COUNT + 1))
|
|
76
|
+
echo "[$COUNT/$TOTAL] Comparing: $SCREENSHOT_KEY" >&2
|
|
77
|
+
|
|
78
|
+
RESULT=$("$SCRIPT_DIR/compare-screenshots.sh" \
|
|
79
|
+
--screenshot-key "$SCREENSHOT_KEY" \
|
|
80
|
+
--task-id "$TASK_ID" \
|
|
81
|
+
--threshold "$THRESHOLD" 2>/dev/null || echo '{"status":"error"}')
|
|
82
|
+
|
|
83
|
+
STATUS=$(echo "$RESULT" | jq -r '.status')
|
|
84
|
+
|
|
85
|
+
case "$STATUS" in
|
|
86
|
+
passed)
|
|
87
|
+
PASSED=$((PASSED + 1))
|
|
88
|
+
echo " ✅ PASSED ($(echo "$RESULT" | jq -r '.similarity_score'))" >&2
|
|
89
|
+
;;
|
|
90
|
+
failed)
|
|
91
|
+
FAILED=$((FAILED + 1))
|
|
92
|
+
echo " ❌ FAILED ($(echo "$RESULT" | jq -r '.diff_percentage')% difference)" >&2
|
|
93
|
+
;;
|
|
94
|
+
no-baseline)
|
|
95
|
+
NO_BASELINE=$((NO_BASELINE + 1))
|
|
96
|
+
echo " ⚠️ NO BASELINE" >&2
|
|
97
|
+
;;
|
|
98
|
+
*)
|
|
99
|
+
echo " ❌ ERROR" >&2
|
|
100
|
+
;;
|
|
101
|
+
esac
|
|
102
|
+
done
|
|
103
|
+
|
|
104
|
+
# Calculate overall status
|
|
105
|
+
if [ "$FAILED" -eq 0 ] && [ "$NO_BASELINE" -eq 0 ]; then
|
|
106
|
+
echo "✅ All comparisons passed: $PASSED/$TOTAL" >&2
|
|
107
|
+
return 0
|
|
108
|
+
elif [ "$NO_BASELINE" -gt 0 ]; then
|
|
109
|
+
echo "⚠️ Some screenshots lack baselines: $NO_BASELINE missing" >&2
|
|
110
|
+
return 2
|
|
111
|
+
else
|
|
112
|
+
echo "❌ Visual regressions detected: $FAILED/$TOTAL failed" >&2
|
|
113
|
+
return 1
|
|
114
|
+
fi
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
# Function: Calculate validation consensus from comparison results
|
|
118
|
+
# Usage: calculate_validation_consensus <task-id>
|
|
119
|
+
calculate_validation_consensus() {
|
|
120
|
+
local TASK_ID="$1"
|
|
121
|
+
|
|
122
|
+
echo "Calculating validation consensus for task: $TASK_ID" >&2
|
|
123
|
+
|
|
124
|
+
# Get all comparison results from Redis
|
|
125
|
+
local DIFF_KEYS=$(redis-cli keys "screenshot:diff:${TASK_ID}:*" 2>/dev/null || echo "")
|
|
126
|
+
|
|
127
|
+
if [ -z "$DIFF_KEYS" ]; then
|
|
128
|
+
echo '{"confidence": 0.0, "status": "no-comparisons", "message": "No comparison results found"}' | jq '.'
|
|
129
|
+
return 1
|
|
130
|
+
fi
|
|
131
|
+
|
|
132
|
+
local TOTAL=0
|
|
133
|
+
local PASSED=0
|
|
134
|
+
local FAILED=0
|
|
135
|
+
local TOTAL_SIMILARITY=0
|
|
136
|
+
|
|
137
|
+
echo "$DIFF_KEYS" | while read -r KEY; do
|
|
138
|
+
[ -z "$KEY" ] && continue
|
|
139
|
+
|
|
140
|
+
RESULT=$(redis-cli get "$KEY" 2>/dev/null || echo '{}')
|
|
141
|
+
STATUS=$(echo "$RESULT" | jq -r '.status // "unknown"')
|
|
142
|
+
SIMILARITY=$(echo "$RESULT" | jq -r '.similarity_score // 0')
|
|
143
|
+
|
|
144
|
+
TOTAL=$((TOTAL + 1))
|
|
145
|
+
TOTAL_SIMILARITY=$(echo "$TOTAL_SIMILARITY + $SIMILARITY" | bc -l)
|
|
146
|
+
|
|
147
|
+
if [ "$STATUS" = "passed" ]; then
|
|
148
|
+
PASSED=$((PASSED + 1))
|
|
149
|
+
elif [ "$STATUS" = "failed" ]; then
|
|
150
|
+
FAILED=$((FAILED + 1))
|
|
151
|
+
fi
|
|
152
|
+
done
|
|
153
|
+
|
|
154
|
+
# Calculate average similarity and confidence
|
|
155
|
+
AVG_SIMILARITY=$(echo "scale=4; $TOTAL_SIMILARITY / $TOTAL" | bc -l)
|
|
156
|
+
PASS_RATE=$(echo "scale=4; $PASSED / $TOTAL" | bc -l)
|
|
157
|
+
|
|
158
|
+
# Confidence = weighted average of pass rate and similarity
|
|
159
|
+
# Pass rate weighted 60%, average similarity weighted 40%
|
|
160
|
+
CONFIDENCE=$(echo "scale=4; ($PASS_RATE * 0.6) + ($AVG_SIMILARITY * 0.4)" | bc -l)
|
|
161
|
+
|
|
162
|
+
# Determine overall status
|
|
163
|
+
if [ "$FAILED" -eq 0 ]; then
|
|
164
|
+
STATUS="passed"
|
|
165
|
+
MESSAGE="All visual regression tests passed"
|
|
166
|
+
else
|
|
167
|
+
STATUS="failed"
|
|
168
|
+
MESSAGE="Visual regressions detected in $FAILED/$TOTAL screenshots"
|
|
169
|
+
fi
|
|
170
|
+
|
|
171
|
+
# Output JSON result
|
|
172
|
+
cat <<EOF | jq '.'
|
|
173
|
+
{
|
|
174
|
+
"confidence": ${CONFIDENCE},
|
|
175
|
+
"status": "${STATUS}",
|
|
176
|
+
"message": "${MESSAGE}",
|
|
177
|
+
"total_comparisons": ${TOTAL},
|
|
178
|
+
"passed": ${PASSED},
|
|
179
|
+
"failed": ${FAILED},
|
|
180
|
+
"average_similarity": ${AVG_SIMILARITY},
|
|
181
|
+
"pass_rate": ${PASS_RATE},
|
|
182
|
+
"task_id": "${TASK_ID}"
|
|
183
|
+
}
|
|
184
|
+
EOF
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
# Function: Update all baselines after Product Owner approval
|
|
188
|
+
# Usage: update_all_baselines <task-id> <reason> <approved-by>
|
|
189
|
+
update_all_baselines() {
|
|
190
|
+
local TASK_ID="$1"
|
|
191
|
+
local REASON="$2"
|
|
192
|
+
local APPROVED_BY="$3"
|
|
193
|
+
|
|
194
|
+
echo "Updating baselines for task: $TASK_ID" >&2
|
|
195
|
+
|
|
196
|
+
# Get all comparison results from Redis
|
|
197
|
+
local DIFF_KEYS=$(redis-cli keys "screenshot:diff:${TASK_ID}:*" 2>/dev/null || echo "")
|
|
198
|
+
|
|
199
|
+
if [ -z "$DIFF_KEYS" ]; then
|
|
200
|
+
echo "Error: No comparison results found for task: $TASK_ID" >&2
|
|
201
|
+
return 1
|
|
202
|
+
fi
|
|
203
|
+
|
|
204
|
+
local TOTAL=0
|
|
205
|
+
local UPDATED=0
|
|
206
|
+
|
|
207
|
+
echo "$DIFF_KEYS" | while read -r KEY; do
|
|
208
|
+
[ -z "$KEY" ] && continue
|
|
209
|
+
|
|
210
|
+
RESULT=$(redis-cli get "$KEY" 2>/dev/null || echo '{}')
|
|
211
|
+
SCREENSHOT_KEY=$(echo "$KEY" | sed "s|screenshot:diff:${TASK_ID}:||")
|
|
212
|
+
CURRENT_FILE=$(echo "$RESULT" | jq -r '.current_path')
|
|
213
|
+
STATUS=$(echo "$RESULT" | jq -r '.status')
|
|
214
|
+
|
|
215
|
+
TOTAL=$((TOTAL + 1))
|
|
216
|
+
|
|
217
|
+
# Only update if comparison failed (visual change detected)
|
|
218
|
+
if [ "$STATUS" = "failed" ]; then
|
|
219
|
+
echo "[$TOTAL] Updating baseline: $SCREENSHOT_KEY" >&2
|
|
220
|
+
|
|
221
|
+
"$SCRIPT_DIR/set-baseline.sh" \
|
|
222
|
+
--screenshot-key "$SCREENSHOT_KEY" \
|
|
223
|
+
--current-file "$CURRENT_FILE" \
|
|
224
|
+
--reason "$REASON (approved by $APPROVED_BY)" >/dev/null 2>&1 && UPDATED=$((UPDATED + 1))
|
|
225
|
+
else
|
|
226
|
+
echo "[$TOTAL] Skipping (no change): $SCREENSHOT_KEY" >&2
|
|
227
|
+
fi
|
|
228
|
+
done
|
|
229
|
+
|
|
230
|
+
echo "✅ Updated $UPDATED baselines" >&2
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
# Export functions for use in other scripts
|
|
234
|
+
export -f capture_component_screenshots
|
|
235
|
+
export -f compare_all_screenshots
|
|
236
|
+
export -f calculate_validation_consensus
|
|
237
|
+
export -f update_all_baselines
|
|
238
|
+
|
|
239
|
+
# If script is run directly, show usage
|
|
240
|
+
if [ "${BASH_SOURCE[0]}" = "${0}" ]; then
|
|
241
|
+
cat <<EOF
|
|
242
|
+
CFN Loop Integration Helper Functions
|
|
243
|
+
|
|
244
|
+
Usage: source $0
|
|
245
|
+
|
|
246
|
+
Available Functions:
|
|
247
|
+
capture_component_screenshots <project> <component> <url> <task-id> <agent-id> [viewports...]
|
|
248
|
+
compare_all_screenshots <task-id> [threshold]
|
|
249
|
+
calculate_validation_consensus <task-id>
|
|
250
|
+
update_all_baselines <task-id> <reason> <approved-by>
|
|
251
|
+
|
|
252
|
+
Example (Loop 3 - Implementation):
|
|
253
|
+
source ./.claude/skills/webapp-testing/cfn-loop-integration.sh
|
|
254
|
+
capture_component_screenshots "auth-system" "login-form" "http://localhost:3000/login" "\$TASK_ID" "\$AGENT_ID" "1920x1080" "375x667"
|
|
255
|
+
|
|
256
|
+
Example (Loop 2 - Validation):
|
|
257
|
+
compare_all_screenshots "\$TASK_ID" 0.95
|
|
258
|
+
CONSENSUS=\$(calculate_validation_consensus "\$TASK_ID")
|
|
259
|
+
echo "\$CONSENSUS" | jq -r '.confidence'
|
|
260
|
+
|
|
261
|
+
Example (Product Owner Decision):
|
|
262
|
+
update_all_baselines "\$TASK_ID" "Approved visual changes from PR #123" "product-owner-agent"
|
|
263
|
+
|
|
264
|
+
EOF
|
|
265
|
+
fi
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Webapp Testing Skill - Screenshot Comparison
|
|
3
|
+
# Purpose: Compare captured screenshot to baseline using pixelmatch, store results in Redis
|
|
4
|
+
|
|
5
|
+
set -e
|
|
6
|
+
|
|
7
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
8
|
+
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)"
|
|
9
|
+
cd "$PROJECT_ROOT"
|
|
10
|
+
|
|
11
|
+
# Default configuration
|
|
12
|
+
THRESHOLD=0.95
|
|
13
|
+
IGNORE_REGIONS=""
|
|
14
|
+
|
|
15
|
+
# Parse arguments
|
|
16
|
+
while [[ $# -gt 0 ]]; do
|
|
17
|
+
case $1 in
|
|
18
|
+
--screenshot-key) SCREENSHOT_KEY="$2"; shift 2 ;;
|
|
19
|
+
--task-id) TASK_ID="$2"; shift 2 ;;
|
|
20
|
+
--threshold) THRESHOLD="$2"; shift 2 ;;
|
|
21
|
+
--ignore-regions) IGNORE_REGIONS="$2"; shift 2 ;;
|
|
22
|
+
*) echo "Unknown parameter: $1"; exit 1 ;;
|
|
23
|
+
esac
|
|
24
|
+
done
|
|
25
|
+
|
|
26
|
+
# Validate required parameters
|
|
27
|
+
if [ -z "$SCREENSHOT_KEY" ] || [ -z "$TASK_ID" ]; then
|
|
28
|
+
echo "Error: Missing required parameters" >&2
|
|
29
|
+
echo "Usage: $0 --screenshot-key <key> --task-id <task-id>" >&2
|
|
30
|
+
echo "" >&2
|
|
31
|
+
echo "Required:" >&2
|
|
32
|
+
echo " --screenshot-key Screenshot identifier (e.g., 'auth-system/login-form/1920x1080/default/light-mode')" >&2
|
|
33
|
+
echo " --task-id CFN task identifier" >&2
|
|
34
|
+
echo "" >&2
|
|
35
|
+
echo "Optional:" >&2
|
|
36
|
+
echo " --threshold Similarity threshold 0.0-1.0 (default: 0.95)" >&2
|
|
37
|
+
echo " --ignore-regions JSON array of regions to ignore (e.g., '[{\"x\":0,\"y\":0,\"width\":100,\"height\":50}]')" >&2
|
|
38
|
+
exit 1
|
|
39
|
+
fi
|
|
40
|
+
|
|
41
|
+
DB_PATH="${HOME}/.claude/memory/adaptive-context.db"
|
|
42
|
+
|
|
43
|
+
# Get baseline path from SQLite
|
|
44
|
+
BASELINE_PATH=$(sqlite3 "$DB_PATH" \
|
|
45
|
+
"SELECT file_path FROM webapp_screenshots WHERE screenshot_key = '${SCREENSHOT_KEY}' AND baseline = 1" 2>/dev/null)
|
|
46
|
+
|
|
47
|
+
if [ -z "$BASELINE_PATH" ]; then
|
|
48
|
+
# No baseline exists
|
|
49
|
+
echo '{"status": "no-baseline", "screenshot_key": "'"$SCREENSHOT_KEY"'", "message": "No baseline found for comparison. Use set-baseline.sh to create one."}' | jq '.'
|
|
50
|
+
exit 0
|
|
51
|
+
fi
|
|
52
|
+
|
|
53
|
+
# Get current screenshot path from SQLite (most recent non-baseline)
|
|
54
|
+
CURRENT_PATH=$(sqlite3 "$DB_PATH" \
|
|
55
|
+
"SELECT file_path FROM webapp_screenshots WHERE screenshot_key = '${SCREENSHOT_KEY}' AND baseline = 0 ORDER BY captured_at DESC LIMIT 1" 2>/dev/null)
|
|
56
|
+
|
|
57
|
+
if [ -z "$CURRENT_PATH" ]; then
|
|
58
|
+
echo "Error: No current screenshot found for key: $SCREENSHOT_KEY" >&2
|
|
59
|
+
echo "Hint: Run capture-screenshot.sh first to create a capture" >&2
|
|
60
|
+
exit 1
|
|
61
|
+
fi
|
|
62
|
+
|
|
63
|
+
# Verify files exist
|
|
64
|
+
if [ ! -f "$BASELINE_PATH" ]; then
|
|
65
|
+
echo "Error: Baseline file not found: $BASELINE_PATH" >&2
|
|
66
|
+
exit 1
|
|
67
|
+
fi
|
|
68
|
+
|
|
69
|
+
if [ ! -f "$CURRENT_PATH" ]; then
|
|
70
|
+
echo "Error: Current screenshot file not found: $CURRENT_PATH" >&2
|
|
71
|
+
exit 1
|
|
72
|
+
fi
|
|
73
|
+
|
|
74
|
+
# Generate diff path
|
|
75
|
+
DIFF_DIR=".screenshots/diffs/${TASK_ID}"
|
|
76
|
+
mkdir -p "$DIFF_DIR"
|
|
77
|
+
SCREENSHOT_KEY_FLAT=$(echo "$SCREENSHOT_KEY" | tr '/' '_')
|
|
78
|
+
DIFF_PATH="${DIFF_DIR}/${SCREENSHOT_KEY_FLAT}_diff.png"
|
|
79
|
+
|
|
80
|
+
echo "Comparing screenshots:" >&2
|
|
81
|
+
echo " Baseline: $BASELINE_PATH" >&2
|
|
82
|
+
echo " Current: $CURRENT_PATH" >&2
|
|
83
|
+
echo " Diff: $DIFF_PATH" >&2
|
|
84
|
+
|
|
85
|
+
# Compare using pixelmatch (Node.js)
|
|
86
|
+
COMPARISON=$(node -e "
|
|
87
|
+
const fs = require('fs');
|
|
88
|
+
const PNG = require('pngjs').PNG;
|
|
89
|
+
const pixelmatch = require('pixelmatch');
|
|
90
|
+
|
|
91
|
+
try {
|
|
92
|
+
const baseline = PNG.sync.read(fs.readFileSync('${BASELINE_PATH}'));
|
|
93
|
+
const current = PNG.sync.read(fs.readFileSync('${CURRENT_PATH}'));
|
|
94
|
+
|
|
95
|
+
// Validate dimensions match
|
|
96
|
+
if (baseline.width !== current.width || baseline.height !== current.height) {
|
|
97
|
+
console.error(JSON.stringify({
|
|
98
|
+
status: 'error',
|
|
99
|
+
message: 'Screenshot dimensions do not match',
|
|
100
|
+
baseline_dimensions: { width: baseline.width, height: baseline.height },
|
|
101
|
+
current_dimensions: { width: current.width, height: current.height }
|
|
102
|
+
}));
|
|
103
|
+
process.exit(1);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const { width, height } = baseline;
|
|
107
|
+
const diff = new PNG({ width, height });
|
|
108
|
+
|
|
109
|
+
// Configure pixelmatch options
|
|
110
|
+
const options = {
|
|
111
|
+
threshold: 0.1, // Sensitivity (0 = strict, 1 = lenient)
|
|
112
|
+
includeAA: true, // Include anti-aliasing
|
|
113
|
+
alpha: 0.1,
|
|
114
|
+
aaColor: [255, 255, 0],
|
|
115
|
+
diffColor: [255, 0, 0]
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
${IGNORE_REGIONS:+
|
|
119
|
+
// Apply ignore regions (mask pixels)
|
|
120
|
+
const ignoreRegions = ${IGNORE_REGIONS};
|
|
121
|
+
ignoreRegions.forEach(region => {
|
|
122
|
+
for (let y = region.y; y < region.y + region.height && y < height; y++) {
|
|
123
|
+
for (let x = region.x; x < region.x + region.width && x < width; x++) {
|
|
124
|
+
const idx = (width * y + x) * 4;
|
|
125
|
+
baseline.data[idx] = current.data[idx];
|
|
126
|
+
baseline.data[idx + 1] = current.data[idx + 1];
|
|
127
|
+
baseline.data[idx + 2] = current.data[idx + 2];
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const numDiffPixels = pixelmatch(
|
|
134
|
+
baseline.data, current.data, diff.data, width, height, options
|
|
135
|
+
);
|
|
136
|
+
|
|
137
|
+
fs.writeFileSync('${DIFF_PATH}', PNG.sync.write(diff));
|
|
138
|
+
|
|
139
|
+
const totalPixels = width * height;
|
|
140
|
+
const diffPercentage = (numDiffPixels / totalPixels) * 100;
|
|
141
|
+
const similarityScore = 1 - (numDiffPixels / totalPixels);
|
|
142
|
+
const status = similarityScore >= ${THRESHOLD} ? 'passed' : 'failed';
|
|
143
|
+
|
|
144
|
+
console.log(JSON.stringify({
|
|
145
|
+
screenshot_key: '${SCREENSHOT_KEY}',
|
|
146
|
+
similarity_score: parseFloat(similarityScore.toFixed(4)),
|
|
147
|
+
diff_pixels: numDiffPixels,
|
|
148
|
+
total_pixels: totalPixels,
|
|
149
|
+
diff_percentage: parseFloat(diffPercentage.toFixed(2)),
|
|
150
|
+
threshold: ${THRESHOLD},
|
|
151
|
+
status: status,
|
|
152
|
+
diff_path: '${DIFF_PATH}',
|
|
153
|
+
baseline_path: '${BASELINE_PATH}',
|
|
154
|
+
current_path: '${CURRENT_PATH}',
|
|
155
|
+
compared_at: Math.floor(Date.now() / 1000)
|
|
156
|
+
}));
|
|
157
|
+
} catch (error) {
|
|
158
|
+
console.error(JSON.stringify({
|
|
159
|
+
status: 'error',
|
|
160
|
+
message: error.message,
|
|
161
|
+
screenshot_key: '${SCREENSHOT_KEY}'
|
|
162
|
+
}));
|
|
163
|
+
process.exit(1);
|
|
164
|
+
}
|
|
165
|
+
" 2>&1)
|
|
166
|
+
|
|
167
|
+
# Check if comparison succeeded
|
|
168
|
+
if [ $? -ne 0 ]; then
|
|
169
|
+
echo "Error: Screenshot comparison failed" >&2
|
|
170
|
+
echo "$COMPARISON" >&2
|
|
171
|
+
exit 1
|
|
172
|
+
fi
|
|
173
|
+
|
|
174
|
+
# Extract JSON from output
|
|
175
|
+
COMPARISON_JSON=$(echo "$COMPARISON" | grep -E '^\{.*\}$' | tail -n 1)
|
|
176
|
+
|
|
177
|
+
if [ -z "$COMPARISON_JSON" ]; then
|
|
178
|
+
echo "Error: Failed to parse comparison result" >&2
|
|
179
|
+
echo "Output: $COMPARISON" >&2
|
|
180
|
+
exit 1
|
|
181
|
+
fi
|
|
182
|
+
|
|
183
|
+
# Store in Redis (TTL: 1 hour = 3600 seconds)
|
|
184
|
+
redis-cli setex "screenshot:diff:${TASK_ID}:${SCREENSHOT_KEY}" 3600 "$COMPARISON_JSON" > /dev/null 2>&1 || \
|
|
185
|
+
echo "Warning: Failed to store comparison result in Redis" >&2
|
|
186
|
+
|
|
187
|
+
# Output result (formatted JSON)
|
|
188
|
+
echo "$COMPARISON_JSON" | jq '.'
|
|
189
|
+
|
|
190
|
+
# Return appropriate exit code based on status
|
|
191
|
+
STATUS=$(echo "$COMPARISON_JSON" | jq -r '.status')
|
|
192
|
+
if [ "$STATUS" = "passed" ]; then
|
|
193
|
+
exit 0
|
|
194
|
+
elif [ "$STATUS" = "failed" ]; then
|
|
195
|
+
echo "Visual regression detected: $(echo "$COMPARISON_JSON" | jq -r '.diff_percentage')% difference" >&2
|
|
196
|
+
exit 2 # Non-zero but distinct from error
|
|
197
|
+
else
|
|
198
|
+
exit 1
|
|
199
|
+
fi
|