@softspark/ai-toolkit 1.0.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/AGENTS.md +412 -0
- package/CHANGELOG.md +68 -0
- package/LICENSE +21 -0
- package/README.md +632 -0
- package/action.yml +53 -0
- package/app/.claude-plugin/plugin.json +44 -0
- package/app/ARCHITECTURE.md +306 -0
- package/app/CLAUDE.md.template +23 -0
- package/app/agents/ai-engineer.md +128 -0
- package/app/agents/backend-specialist.md +193 -0
- package/app/agents/business-intelligence.md +54 -0
- package/app/agents/chaos-monkey.md +67 -0
- package/app/agents/chief-of-staff.md +51 -0
- package/app/agents/code-archaeologist.md +127 -0
- package/app/agents/code-reviewer.md +184 -0
- package/app/agents/command-expert.md +131 -0
- package/app/agents/data-analyst.md +205 -0
- package/app/agents/data-scientist.md +151 -0
- package/app/agents/database-architect.md +317 -0
- package/app/agents/debugger.md +238 -0
- package/app/agents/devops-implementer.md +194 -0
- package/app/agents/documenter.md +364 -0
- package/app/agents/explorer-agent.md +145 -0
- package/app/agents/fact-checker.md +172 -0
- package/app/agents/frontend-specialist.md +209 -0
- package/app/agents/game-developer.md +216 -0
- package/app/agents/incident-responder.md +226 -0
- package/app/agents/infrastructure-architect.md +127 -0
- package/app/agents/infrastructure-validator.md +247 -0
- package/app/agents/llm-ops-engineer.md +237 -0
- package/app/agents/mcp-expert.md +228 -0
- package/app/agents/mcp-server-architect.md +195 -0
- package/app/agents/mcp-testing-engineer.md +292 -0
- package/app/agents/meta-architect.md +58 -0
- package/app/agents/ml-engineer.md +136 -0
- package/app/agents/mobile-developer.md +190 -0
- package/app/agents/night-watchman.md +55 -0
- package/app/agents/nlp-engineer.md +154 -0
- package/app/agents/orchestrator.md +437 -0
- package/app/agents/performance-optimizer.md +254 -0
- package/app/agents/predictive-analyst.md +57 -0
- package/app/agents/product-manager.md +194 -0
- package/app/agents/project-planner.md +287 -0
- package/app/agents/prompt-engineer.md +103 -0
- package/app/agents/qa-automation-engineer.md +182 -0
- package/app/agents/rag-engineer.md +201 -0
- package/app/agents/research-synthesizer.md +138 -0
- package/app/agents/search-specialist.md +101 -0
- package/app/agents/security-architect.md +62 -0
- package/app/agents/security-auditor.md +293 -0
- package/app/agents/seo-specialist.md +111 -0
- package/app/agents/system-governor.md +57 -0
- package/app/agents/tech-lead.md +62 -0
- package/app/agents/technical-researcher.md +103 -0
- package/app/agents/test-engineer.md +264 -0
- package/app/constitution.md +38 -0
- package/app/hooks/_profile-check.sh +11 -0
- package/app/hooks/guard-destructive.sh +74 -0
- package/app/hooks/guard-path.sh +73 -0
- package/app/hooks/post-tool-use.sh +35 -0
- package/app/hooks/pre-compact.sh +31 -0
- package/app/hooks/quality-check.sh +22 -0
- package/app/hooks/quality-gate.sh +49 -0
- package/app/hooks/save-session.sh +24 -0
- package/app/hooks/session-end.sh +37 -0
- package/app/hooks/session-start.sh +29 -0
- package/app/hooks/subagent-start.sh +16 -0
- package/app/hooks/subagent-stop.sh +16 -0
- package/app/hooks/track-usage.sh +50 -0
- package/app/hooks/user-prompt-submit.sh +25 -0
- package/app/hooks.json +178 -0
- package/app/mcp-defaults.json +23 -0
- package/app/output-styles/golden-rules.md +43 -0
- package/app/plugins/README.md +19 -0
- package/app/plugins/csharp-pack/README.md +11 -0
- package/app/plugins/csharp-pack/plugin.json +18 -0
- package/app/plugins/enterprise-pack/README.md +16 -0
- package/app/plugins/enterprise-pack/hooks/output-style.sh +6 -0
- package/app/plugins/enterprise-pack/hooks/status-line.sh +8 -0
- package/app/plugins/enterprise-pack/plugin.json +24 -0
- package/app/plugins/frontend-pack/README.md +14 -0
- package/app/plugins/frontend-pack/plugin.json +22 -0
- package/app/plugins/java-pack/README.md +11 -0
- package/app/plugins/java-pack/plugin.json +18 -0
- package/app/plugins/kotlin-pack/README.md +11 -0
- package/app/plugins/kotlin-pack/plugin.json +18 -0
- package/app/plugins/memory-pack/README.md +24 -0
- package/app/plugins/memory-pack/hooks/observation-capture.sh +67 -0
- package/app/plugins/memory-pack/hooks/session-summary.sh +71 -0
- package/app/plugins/memory-pack/plugin.json +22 -0
- package/app/plugins/memory-pack/scripts/init_db.py +81 -0
- package/app/plugins/memory-pack/scripts/strip_private.py +22 -0
- package/app/plugins/memory-pack/skills/mem-search/SKILL.md +70 -0
- package/app/plugins/research-pack/README.md +14 -0
- package/app/plugins/research-pack/plugin.json +22 -0
- package/app/plugins/ruby-pack/README.md +11 -0
- package/app/plugins/ruby-pack/plugin.json +18 -0
- package/app/plugins/rust-pack/README.md +11 -0
- package/app/plugins/rust-pack/plugin.json +18 -0
- package/app/plugins/security-pack/README.md +15 -0
- package/app/plugins/security-pack/plugin.json +23 -0
- package/app/plugins/swift-pack/README.md +11 -0
- package/app/plugins/swift-pack/plugin.json +18 -0
- package/app/rules/claude-toolkit-rules.md +21 -0
- package/app/rules/git-conventions.md +5 -0
- package/app/rules/quality-gates.md +10 -0
- package/app/skills/_lib/__init__.py +1 -0
- package/app/skills/_lib/detect_utils.py +150 -0
- package/app/skills/agent-creator/SKILL.md +82 -0
- package/app/skills/analyze/SKILL.md +92 -0
- package/app/skills/analyze/scripts/complexity.py +165 -0
- package/app/skills/api-patterns/SKILL.md +305 -0
- package/app/skills/app-builder/SKILL.md +187 -0
- package/app/skills/architecture-audit/SKILL.md +141 -0
- package/app/skills/architecture-decision/SKILL.md +55 -0
- package/app/skills/architecture-decision/templates/adr-template.md +36 -0
- package/app/skills/biz-scan/SKILL.md +30 -0
- package/app/skills/briefing/SKILL.md +27 -0
- package/app/skills/build/SKILL.md +97 -0
- package/app/skills/build/scripts/detect-build.py +151 -0
- package/app/skills/chaos/SKILL.md +32 -0
- package/app/skills/ci/SKILL.md +77 -0
- package/app/skills/ci/scripts/ci-detect.py +135 -0
- package/app/skills/ci/templates/github-actions-node.yml +38 -0
- package/app/skills/ci/templates/github-actions-python.yml +42 -0
- package/app/skills/ci-cd-patterns/SKILL.md +299 -0
- package/app/skills/clean-code/SKILL.md +110 -0
- package/app/skills/clean-code/reference/dart.md +18 -0
- package/app/skills/clean-code/reference/go.md +23 -0
- package/app/skills/clean-code/reference/php.md +32 -0
- package/app/skills/clean-code/reference/python.md +180 -0
- package/app/skills/clean-code/reference/typescript.md +26 -0
- package/app/skills/command-creator/SKILL.md +83 -0
- package/app/skills/commit/SKILL.md +98 -0
- package/app/skills/commit/scripts/pre-commit-check.py +87 -0
- package/app/skills/commit/templates/conventional-commit.md +52 -0
- package/app/skills/csharp-patterns/SKILL.md +450 -0
- package/app/skills/database-patterns/SKILL.md +297 -0
- package/app/skills/debug/SKILL.md +154 -0
- package/app/skills/debug/scripts/error-parser.py +187 -0
- package/app/skills/debugging-tactics/SKILL.md +136 -0
- package/app/skills/deploy/SKILL.md +130 -0
- package/app/skills/deploy/scripts/pre_deploy_check.py +171 -0
- package/app/skills/deploy/templates/deployment-checklist.md +31 -0
- package/app/skills/design-an-interface/SKILL.md +105 -0
- package/app/skills/design-engineering/SKILL.md +260 -0
- package/app/skills/docker-devops/SKILL.md +303 -0
- package/app/skills/docs/SKILL.md +145 -0
- package/app/skills/docs/scripts/doc-inventory.py +176 -0
- package/app/skills/docs/templates/adr-template.md +36 -0
- package/app/skills/docs/templates/readme-template.md +67 -0
- package/app/skills/documentation-standards/SKILL.md +191 -0
- package/app/skills/ecommerce-patterns/SKILL.md +209 -0
- package/app/skills/evaluate/SKILL.md +132 -0
- package/app/skills/evolve/SKILL.md +27 -0
- package/app/skills/explain/SKILL.md +54 -0
- package/app/skills/explain/scripts/dependency-graph.py +215 -0
- package/app/skills/explore/SKILL.md +112 -0
- package/app/skills/explore/scripts/visualize.py +117 -0
- package/app/skills/fix/SKILL.md +78 -0
- package/app/skills/fix/scripts/error-classifier.py +191 -0
- package/app/skills/flutter-patterns/SKILL.md +254 -0
- package/app/skills/git-mastery/SKILL.md +70 -0
- package/app/skills/grill-me/SKILL.md +38 -0
- package/app/skills/health/SKILL.md +91 -0
- package/app/skills/health/scripts/health_check.py +162 -0
- package/app/skills/hive-mind/SKILL.md +56 -0
- package/app/skills/hook-creator/SKILL.md +107 -0
- package/app/skills/index/SKILL.md +74 -0
- package/app/skills/instinct-review/SKILL.md +77 -0
- package/app/skills/java-patterns/SKILL.md +442 -0
- package/app/skills/kotlin-patterns/SKILL.md +446 -0
- package/app/skills/lint/SKILL.md +103 -0
- package/app/skills/lint/scripts/detect-linters.py +112 -0
- package/app/skills/mcp-patterns/SKILL.md +270 -0
- package/app/skills/mem-search/SKILL.md +70 -0
- package/app/skills/migrate/SKILL.md +90 -0
- package/app/skills/migrate/scripts/migration-status.py +195 -0
- package/app/skills/migration-patterns/SKILL.md +260 -0
- package/app/skills/night-watch/SKILL.md +28 -0
- package/app/skills/observability-patterns/SKILL.md +203 -0
- package/app/skills/onboard/SKILL.md +76 -0
- package/app/skills/orchestrate/SKILL.md +86 -0
- package/app/skills/panic/SKILL.md +30 -0
- package/app/skills/performance-profiling/SKILL.md +59 -0
- package/app/skills/plan/SKILL.md +110 -0
- package/app/skills/plan/templates/plan-template.md +40 -0
- package/app/skills/plan-writing/SKILL.md +201 -0
- package/app/skills/plugin-creator/SKILL.md +78 -0
- package/app/skills/pr/SKILL.md +129 -0
- package/app/skills/pr/scripts/pr-summary.py +175 -0
- package/app/skills/prd-to-issues/SKILL.md +108 -0
- package/app/skills/prd-to-plan/SKILL.md +120 -0
- package/app/skills/predict/SKILL.md +30 -0
- package/app/skills/qa-session/SKILL.md +110 -0
- package/app/skills/rag-patterns/SKILL.md +203 -0
- package/app/skills/refactor/SKILL.md +124 -0
- package/app/skills/refactor/scripts/refactor-scan.py +210 -0
- package/app/skills/refactor-plan/SKILL.md +112 -0
- package/app/skills/repeat/SKILL.md +149 -0
- package/app/skills/research-mastery/SKILL.md +56 -0
- package/app/skills/review/SKILL.md +141 -0
- package/app/skills/review/scripts/diff-analyzer.py +170 -0
- package/app/skills/rollback/SKILL.md +87 -0
- package/app/skills/rollback/scripts/rollback_info.py +149 -0
- package/app/skills/ruby-patterns/SKILL.md +454 -0
- package/app/skills/rust-patterns/SKILL.md +446 -0
- package/app/skills/search/SKILL.md +64 -0
- package/app/skills/security-patterns/SKILL.md +91 -0
- package/app/skills/security-patterns/reference/authentication.md +37 -0
- package/app/skills/security-patterns/reference/authorization.md +22 -0
- package/app/skills/security-patterns/reference/input-validation.md +30 -0
- package/app/skills/security-patterns/reference/oauth-csrf-audit.md +131 -0
- package/app/skills/skill-creator/SKILL.md +154 -0
- package/app/skills/skill-creator/templates/dashboard/index.html +130 -0
- package/app/skills/skill-creator/templates/reasoning-engine/assets/example.json +12 -0
- package/app/skills/skill-creator/templates/reasoning-engine/search.py +110 -0
- package/app/skills/subagent-development/SKILL.md +225 -0
- package/app/skills/subagent-development/reference/code-quality-reviewer-prompt.md +145 -0
- package/app/skills/subagent-development/reference/implementer-prompt.md +118 -0
- package/app/skills/subagent-development/reference/spec-reviewer-prompt.md +100 -0
- package/app/skills/swarm/SKILL.md +81 -0
- package/app/skills/swift-patterns/SKILL.md +500 -0
- package/app/skills/tdd/SKILL.md +174 -0
- package/app/skills/tdd/reference/deep-modules.md +32 -0
- package/app/skills/tdd/reference/interface-design.md +32 -0
- package/app/skills/tdd/reference/mocking.md +52 -0
- package/app/skills/tdd/reference/refactoring.md +10 -0
- package/app/skills/tdd/reference/tests.md +59 -0
- package/app/skills/teams/SKILL.md +101 -0
- package/app/skills/test/SKILL.md +107 -0
- package/app/skills/test/scripts/detect-runner.py +113 -0
- package/app/skills/testing-patterns/SKILL.md +73 -0
- package/app/skills/testing-patterns/reference/flutter-testing.md +33 -0
- package/app/skills/testing-patterns/reference/go-testing.md +52 -0
- package/app/skills/testing-patterns/reference/php-phpunit.md +39 -0
- package/app/skills/testing-patterns/reference/python-pytest.md +228 -0
- package/app/skills/testing-patterns/reference/typescript-vitest.md +50 -0
- package/app/skills/triage-issue/SKILL.md +120 -0
- package/app/skills/typescript-patterns/SKILL.md +256 -0
- package/app/skills/ubiquitous-language/SKILL.md +74 -0
- package/app/skills/verification-before-completion/SKILL.md +108 -0
- package/app/skills/workflow/SKILL.md +250 -0
- package/app/skills/write-a-prd/SKILL.md +129 -0
- package/app/skills/write-a-prd/reference/visual-companion.md +78 -0
- package/app/skills/write-a-prd/scripts/frame-template.html +111 -0
- package/app/skills/write-a-prd/scripts/visual-server.cjs +79 -0
- package/app/templates/skill/generator/SKILL.md.template +40 -0
- package/app/templates/skill/knowledge/SKILL.md.template +52 -0
- package/app/templates/skill/linter/SKILL.md.template +34 -0
- package/app/templates/skill/reviewer/SKILL.md.template +51 -0
- package/app/templates/skill/workflow/SKILL.md.template +49 -0
- package/benchmarks/README.md +111 -0
- package/benchmarks/ecosystem-dashboard.json +148 -0
- package/benchmarks/ecosystem-harvest.json +148 -0
- package/benchmarks/results.json +38 -0
- package/benchmarks/run.py +351 -0
- package/bin/ai-toolkit.js +345 -0
- package/kb/best-practices/README.md +11 -0
- package/kb/howto/README.md +11 -0
- package/kb/procedures/maintenance-sop.md +306 -0
- package/kb/reference/agents-catalog.md +124 -0
- package/kb/reference/anti-pattern-registry-format.md +221 -0
- package/kb/reference/architecture-overview.md +232 -0
- package/kb/reference/benchmark-config.md +62 -0
- package/kb/reference/ci-integration.md +66 -0
- package/kb/reference/claude-ecosystem-benchmark-snapshot.md +80 -0
- package/kb/reference/claude-ecosystem-expansion-foundations.md +102 -0
- package/kb/reference/commands-catalog.md +21 -0
- package/kb/reference/distribution-model.md +63 -0
- package/kb/reference/global-install-model.md +56 -0
- package/kb/reference/hierarchical-override-pattern.md +200 -0
- package/kb/reference/hooks-catalog.md +306 -0
- package/kb/reference/integrations.md +88 -0
- package/kb/reference/language-packs.md +52 -0
- package/kb/reference/merge-friendly-install-model.md +58 -0
- package/kb/reference/plugin-pack-conventions.md +151 -0
- package/kb/reference/quick-wins-implementation-summary.md +70 -0
- package/kb/reference/skill-templates.md +50 -0
- package/kb/reference/skills-catalog.md +215 -0
- package/kb/reference/skills-unification.md +57 -0
- package/kb/reference/stats.md +69 -0
- package/kb/reference/sync.md +76 -0
- package/kb/troubleshooting/README.md +11 -0
- package/llms-full.txt +3068 -0
- package/llms.txt +39 -0
- package/package.json +75 -0
- package/scripts/_common.py +160 -0
- package/scripts/add_rule.py +50 -0
- package/scripts/benchmark_config.py +127 -0
- package/scripts/benchmark_ecosystem.py +288 -0
- package/scripts/check_deps.py +260 -0
- package/scripts/create_skill.py +118 -0
- package/scripts/doctor.py +504 -0
- package/scripts/eject.py +113 -0
- package/scripts/emission.py +256 -0
- package/scripts/evaluate_skills.py +260 -0
- package/scripts/frontmatter.py +58 -0
- package/scripts/generate_agents_md.py +91 -0
- package/scripts/generate_aider_conf.py +51 -0
- package/scripts/generate_cline.py +35 -0
- package/scripts/generate_copilot.py +30 -0
- package/scripts/generate_cursor_rules.py +35 -0
- package/scripts/generate_gemini.py +28 -0
- package/scripts/generate_llms_txt.py +164 -0
- package/scripts/generate_roo_modes.py +80 -0
- package/scripts/generate_windsurf.py +35 -0
- package/scripts/generator_base.py +140 -0
- package/scripts/harvest_ecosystem.py +50 -0
- package/scripts/inject_rule_cli.py +101 -0
- package/scripts/inject_section_cli.py +47 -0
- package/scripts/injection.py +180 -0
- package/scripts/install.py +236 -0
- package/scripts/install_git_hooks.py +71 -0
- package/scripts/install_steps/__init__.py +5 -0
- package/scripts/install_steps/ai_tools.py +261 -0
- package/scripts/install_steps/hooks.py +90 -0
- package/scripts/install_steps/markers.py +79 -0
- package/scripts/install_steps/symlinks.py +87 -0
- package/scripts/merge-hooks.py +192 -0
- package/scripts/plugin.py +642 -0
- package/scripts/plugin_schema.py +138 -0
- package/scripts/remove_rule.py +58 -0
- package/scripts/stats.py +81 -0
- package/scripts/sync.py +215 -0
- package/scripts/uninstall.py +292 -0
- package/scripts/validate.py +700 -0
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
# Python Pytest Patterns
|
|
2
|
+
|
|
3
|
+
## Fixtures
|
|
4
|
+
|
|
5
|
+
### Basic Fixture
|
|
6
|
+
|
|
7
|
+
```python
|
|
8
|
+
import pytest
|
|
9
|
+
|
|
10
|
+
@pytest.fixture
|
|
11
|
+
def sample_document():
|
|
12
|
+
"""Sample document for testing."""
|
|
13
|
+
return {
|
|
14
|
+
"path": "kb/test/doc.md",
|
|
15
|
+
"title": "Test Document",
|
|
16
|
+
"content": "Test content"
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
def test_process_document(sample_document):
|
|
20
|
+
result = process(sample_document)
|
|
21
|
+
assert result.title == "Test Document"
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
### Async Fixture
|
|
25
|
+
|
|
26
|
+
```python
|
|
27
|
+
import pytest
|
|
28
|
+
import httpx
|
|
29
|
+
|
|
30
|
+
@pytest.fixture
|
|
31
|
+
async def async_client(app):
|
|
32
|
+
"""Create async HTTP client for testing."""
|
|
33
|
+
async with httpx.AsyncClient(app=app, base_url="http://test") as client:
|
|
34
|
+
yield client
|
|
35
|
+
|
|
36
|
+
@pytest.mark.asyncio
|
|
37
|
+
async def test_api_endpoint(async_client):
|
|
38
|
+
response = await async_client.get("/health")
|
|
39
|
+
assert response.status_code == 200
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### Scoped Fixtures
|
|
43
|
+
|
|
44
|
+
```python
|
|
45
|
+
@pytest.fixture(scope="module")
|
|
46
|
+
def database():
|
|
47
|
+
"""Database connection reused across module."""
|
|
48
|
+
db = create_database()
|
|
49
|
+
yield db
|
|
50
|
+
db.close()
|
|
51
|
+
|
|
52
|
+
@pytest.fixture(scope="session")
|
|
53
|
+
def config():
|
|
54
|
+
"""Configuration reused across entire session."""
|
|
55
|
+
return load_config()
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Mocking
|
|
59
|
+
|
|
60
|
+
### Basic Mock
|
|
61
|
+
|
|
62
|
+
```python
|
|
63
|
+
from unittest.mock import Mock, patch
|
|
64
|
+
|
|
65
|
+
def test_with_mock():
|
|
66
|
+
mock_client = Mock()
|
|
67
|
+
mock_client.search.return_value = [{"id": 1}]
|
|
68
|
+
|
|
69
|
+
result = function_using_client(mock_client)
|
|
70
|
+
|
|
71
|
+
mock_client.search.assert_called_once_with("query")
|
|
72
|
+
assert len(result) == 1
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### Patch Decorator
|
|
76
|
+
|
|
77
|
+
```python
|
|
78
|
+
@patch("module.external_api")
|
|
79
|
+
def test_with_patch(mock_api):
|
|
80
|
+
mock_api.return_value = {"data": "mocked"}
|
|
81
|
+
|
|
82
|
+
result = function_calling_api()
|
|
83
|
+
|
|
84
|
+
assert result == {"data": "mocked"}
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### Async Mock
|
|
88
|
+
|
|
89
|
+
```python
|
|
90
|
+
from unittest.mock import AsyncMock
|
|
91
|
+
|
|
92
|
+
@pytest.mark.asyncio
|
|
93
|
+
async def test_async_function():
|
|
94
|
+
mock_client = AsyncMock()
|
|
95
|
+
mock_client.fetch.return_value = {"result": "data"}
|
|
96
|
+
|
|
97
|
+
result = await async_function(mock_client)
|
|
98
|
+
|
|
99
|
+
mock_client.fetch.assert_awaited_once()
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### pytest-mock (Recommended)
|
|
103
|
+
|
|
104
|
+
```python
|
|
105
|
+
def test_with_mocker(mocker):
|
|
106
|
+
mock_client = mocker.patch("module.Client")
|
|
107
|
+
mock_client.return_value.search.return_value = []
|
|
108
|
+
|
|
109
|
+
result = search_function()
|
|
110
|
+
|
|
111
|
+
assert result == []
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## Parametrization
|
|
115
|
+
|
|
116
|
+
```python
|
|
117
|
+
@pytest.mark.parametrize("input_val,expected", [
|
|
118
|
+
("hello", "HELLO"),
|
|
119
|
+
("world", "WORLD"),
|
|
120
|
+
("test", "TEST"),
|
|
121
|
+
])
|
|
122
|
+
def test_uppercase(input_val, expected):
|
|
123
|
+
assert input_val.upper() == expected
|
|
124
|
+
|
|
125
|
+
@pytest.mark.parametrize("query,limit,expected_count", [
|
|
126
|
+
("test", 5, 5),
|
|
127
|
+
("test", 10, 10),
|
|
128
|
+
("", 5, 0),
|
|
129
|
+
])
|
|
130
|
+
def test_search_with_params(query, limit, expected_count):
|
|
131
|
+
results = search(query, limit)
|
|
132
|
+
assert len(results) <= expected_count
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
## Exception Testing
|
|
136
|
+
|
|
137
|
+
```python
|
|
138
|
+
def test_raises_value_error():
|
|
139
|
+
with pytest.raises(ValueError, match="Invalid input"):
|
|
140
|
+
function_that_raises(None)
|
|
141
|
+
|
|
142
|
+
def test_raises_any_exception():
|
|
143
|
+
with pytest.raises(Exception):
|
|
144
|
+
risky_function()
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
## Markers
|
|
148
|
+
|
|
149
|
+
```python
|
|
150
|
+
@pytest.mark.slow
|
|
151
|
+
def test_slow_operation():
|
|
152
|
+
"""Marked as slow, can be skipped with -m 'not slow'."""
|
|
153
|
+
pass
|
|
154
|
+
|
|
155
|
+
@pytest.mark.skip(reason="Not implemented yet")
|
|
156
|
+
def test_future_feature():
|
|
157
|
+
pass
|
|
158
|
+
|
|
159
|
+
@pytest.mark.skipif(sys.version_info < (3, 10), reason="Requires Python 3.10+")
|
|
160
|
+
def test_new_feature():
|
|
161
|
+
pass
|
|
162
|
+
|
|
163
|
+
@pytest.mark.xfail(reason="Known bug #123")
|
|
164
|
+
def test_known_bug():
|
|
165
|
+
pass
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
## conftest.py
|
|
169
|
+
|
|
170
|
+
```python
|
|
171
|
+
# tests/conftest.py
|
|
172
|
+
import pytest
|
|
173
|
+
|
|
174
|
+
@pytest.fixture
|
|
175
|
+
def app():
|
|
176
|
+
"""Create test application."""
|
|
177
|
+
from app import create_app
|
|
178
|
+
return create_app(testing=True)
|
|
179
|
+
|
|
180
|
+
@pytest.fixture
|
|
181
|
+
def mock_llm(mocker):
|
|
182
|
+
"""Mock LLM client for all tests."""
|
|
183
|
+
return mocker.patch("scripts.llm_client.LLMClient")
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
## Running Tests
|
|
187
|
+
|
|
188
|
+
**Direct execution:**
|
|
189
|
+
```bash
|
|
190
|
+
# All tests
|
|
191
|
+
pytest tests/
|
|
192
|
+
|
|
193
|
+
# With coverage
|
|
194
|
+
pytest --cov=src --cov-report=term-missing
|
|
195
|
+
|
|
196
|
+
# Specific file
|
|
197
|
+
pytest tests/unit/test_search_core.py
|
|
198
|
+
|
|
199
|
+
# By marker
|
|
200
|
+
pytest -m "not slow"
|
|
201
|
+
|
|
202
|
+
# Verbose with print output
|
|
203
|
+
pytest -v -s
|
|
204
|
+
|
|
205
|
+
# Failed tests only
|
|
206
|
+
pytest --lf
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
**Docker execution (if containerized):**
|
|
210
|
+
```bash
|
|
211
|
+
# All tests
|
|
212
|
+
docker exec {app-container} pytest tests/
|
|
213
|
+
|
|
214
|
+
# With coverage
|
|
215
|
+
docker exec {app-container} pytest --cov=src --cov-report=term-missing
|
|
216
|
+
|
|
217
|
+
# Specific file
|
|
218
|
+
docker exec {app-container} pytest tests/unit/test_search_core.py
|
|
219
|
+
|
|
220
|
+
# By marker
|
|
221
|
+
docker exec {app-container} pytest -m "not slow"
|
|
222
|
+
|
|
223
|
+
# Verbose with print output
|
|
224
|
+
docker exec {app-container} pytest -v -s
|
|
225
|
+
|
|
226
|
+
# Failed tests only
|
|
227
|
+
docker exec {app-container} pytest --lf
|
|
228
|
+
```
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# TypeScript Vitest/Jest Patterns
|
|
2
|
+
|
|
3
|
+
## Basic Test
|
|
4
|
+
|
|
5
|
+
```typescript
|
|
6
|
+
import { describe, it, expect, vi } from "vitest";
|
|
7
|
+
|
|
8
|
+
describe("UserService", () => {
|
|
9
|
+
it("creates user with valid data", async () => {
|
|
10
|
+
const mockRepo = { create: vi.fn().mockResolvedValue({ id: "1", name: "Test" }) };
|
|
11
|
+
const service = new UserService(mockRepo);
|
|
12
|
+
const user = await service.create({ name: "Test", email: "t@t.com" });
|
|
13
|
+
expect(user.id).toBe("1");
|
|
14
|
+
expect(mockRepo.create).toHaveBeenCalledOnce();
|
|
15
|
+
});
|
|
16
|
+
});
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## React Testing Library
|
|
20
|
+
|
|
21
|
+
```tsx
|
|
22
|
+
import { render, screen, fireEvent } from "@testing-library/react";
|
|
23
|
+
|
|
24
|
+
test("button click increments counter", () => {
|
|
25
|
+
render(<Counter />);
|
|
26
|
+
fireEvent.click(screen.getByRole("button"));
|
|
27
|
+
expect(screen.getByText("Count: 1")).toBeInTheDocument();
|
|
28
|
+
});
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Mocking
|
|
32
|
+
|
|
33
|
+
```typescript
|
|
34
|
+
// Module mock
|
|
35
|
+
vi.mock("./api", () => ({ fetchUsers: vi.fn().mockResolvedValue([]) }));
|
|
36
|
+
|
|
37
|
+
// Spy
|
|
38
|
+
const spy = vi.spyOn(service, "save");
|
|
39
|
+
await service.process(data);
|
|
40
|
+
expect(spy).toHaveBeenCalledWith(data);
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Running
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
npx vitest run # All tests
|
|
47
|
+
npx vitest run --coverage # With coverage
|
|
48
|
+
npx vitest run src/utils.test.ts # Specific file
|
|
49
|
+
npx vitest --watch # Watch mode
|
|
50
|
+
```
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: triage-issue
|
|
3
|
+
description: "Triage a bug by deeply exploring the codebase for root cause, then create a GitHub issue with a TDD-based fix plan. Mostly hands-off — minimal user interaction. Use when user reports a bug, wants to investigate an issue, mentions triage, or wants a fix plan."
|
|
4
|
+
user-invocable: true
|
|
5
|
+
effort: high
|
|
6
|
+
argument-hint: "[bug description or symptom]"
|
|
7
|
+
agent: debugger
|
|
8
|
+
context: fork
|
|
9
|
+
allowed-tools: Read, Grep, Glob, Bash, Agent
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# Triage Issue
|
|
13
|
+
|
|
14
|
+
$ARGUMENTS
|
|
15
|
+
|
|
16
|
+
Investigate a bug, find root cause, and create a GitHub issue with a TDD fix plan. Mostly hands-off.
|
|
17
|
+
|
|
18
|
+
## Usage
|
|
19
|
+
|
|
20
|
+
```
|
|
21
|
+
/triage-issue [bug description or symptom]
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## What This Command Does
|
|
25
|
+
|
|
26
|
+
1. **Captures** problem description (ONE question max)
|
|
27
|
+
2. **Explores** codebase deeply for root cause
|
|
28
|
+
3. **Identifies** fix approach
|
|
29
|
+
4. **Designs** TDD fix plan (RED→GREEN cycles)
|
|
30
|
+
5. **Creates** GitHub issue via `gh issue create`
|
|
31
|
+
|
|
32
|
+
## Process
|
|
33
|
+
|
|
34
|
+
### 1. Capture the Problem
|
|
35
|
+
|
|
36
|
+
Get brief description. If not provided, ask ONE question: "What's the problem you're seeing?"
|
|
37
|
+
|
|
38
|
+
Do NOT ask follow-up questions. Start investigating immediately.
|
|
39
|
+
|
|
40
|
+
### 2. Explore and Diagnose
|
|
41
|
+
|
|
42
|
+
Use Agent (subagent_type=Explore) to deeply investigate:
|
|
43
|
+
|
|
44
|
+
| Target | What to look for |
|
|
45
|
+
|--------|-----------------|
|
|
46
|
+
| Manifestation | Where the bug appears (entry points, UI, API) |
|
|
47
|
+
| Code path | Trace the flow from trigger to symptom |
|
|
48
|
+
| Root cause | Why it fails (not just the symptom) |
|
|
49
|
+
| Related code | Similar patterns, adjacent modules, existing tests |
|
|
50
|
+
| Recent changes | `git log` on relevant files |
|
|
51
|
+
| Working patterns | Similar code elsewhere that works correctly |
|
|
52
|
+
|
|
53
|
+
### 3. Identify Fix Approach
|
|
54
|
+
|
|
55
|
+
Determine:
|
|
56
|
+
- Minimal change to fix root cause
|
|
57
|
+
- Affected modules/interfaces
|
|
58
|
+
- Behaviors to verify via tests
|
|
59
|
+
- Classification: regression, missing feature, or design flaw
|
|
60
|
+
|
|
61
|
+
### 4. Design TDD Fix Plan
|
|
62
|
+
|
|
63
|
+
Ordered list of RED→GREEN cycles. Each cycle is one vertical slice:
|
|
64
|
+
|
|
65
|
+
- **RED**: Specific test capturing broken/missing behavior
|
|
66
|
+
- **GREEN**: Minimal code change to pass that test
|
|
67
|
+
|
|
68
|
+
Rules:
|
|
69
|
+
- Tests verify behavior through public interfaces
|
|
70
|
+
- One test at a time, vertical slices
|
|
71
|
+
- Tests must survive internal refactors
|
|
72
|
+
- Describe behaviors and contracts, not internal structure
|
|
73
|
+
|
|
74
|
+
### 5. Create GitHub Issue
|
|
75
|
+
|
|
76
|
+
Use `gh issue create` with template below. Share URL immediately.
|
|
77
|
+
|
|
78
|
+
## Issue Template
|
|
79
|
+
|
|
80
|
+
<issue-template>
|
|
81
|
+
|
|
82
|
+
## Problem
|
|
83
|
+
|
|
84
|
+
- What happens (actual behavior)
|
|
85
|
+
- What should happen (expected behavior)
|
|
86
|
+
- How to reproduce
|
|
87
|
+
|
|
88
|
+
## Root Cause Analysis
|
|
89
|
+
|
|
90
|
+
What was found during investigation:
|
|
91
|
+
- The code path involved
|
|
92
|
+
- Why the current code fails
|
|
93
|
+
- Contributing factors
|
|
94
|
+
|
|
95
|
+
Do NOT include file paths, line numbers, or implementation details. Describe modules, behaviors, and contracts.
|
|
96
|
+
|
|
97
|
+
## TDD Fix Plan
|
|
98
|
+
|
|
99
|
+
1. **RED**: Write test that [expected behavior]
|
|
100
|
+
**GREEN**: [Minimal change to pass]
|
|
101
|
+
|
|
102
|
+
2. **RED**: Write test that [next behavior]
|
|
103
|
+
**GREEN**: [Minimal change to pass]
|
|
104
|
+
|
|
105
|
+
**REFACTOR**: [Cleanup after all tests pass]
|
|
106
|
+
|
|
107
|
+
## Acceptance Criteria
|
|
108
|
+
|
|
109
|
+
- [ ] Criterion 1
|
|
110
|
+
- [ ] All new tests pass
|
|
111
|
+
- [ ] Existing tests still pass
|
|
112
|
+
|
|
113
|
+
</issue-template>
|
|
114
|
+
|
|
115
|
+
## Rules
|
|
116
|
+
|
|
117
|
+
- Minimal user interaction — investigate autonomously
|
|
118
|
+
- No file paths or line numbers in the issue (durability)
|
|
119
|
+
- TDD plan uses vertical slices, not horizontal
|
|
120
|
+
- File immediately — don't ask for review
|
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: typescript-patterns
|
|
3
|
+
description: "Loaded when user asks about TypeScript patterns or type safety"
|
|
4
|
+
effort: medium
|
|
5
|
+
user-invocable: false
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# TypeScript/JavaScript Patterns
|
|
9
|
+
|
|
10
|
+
## TypeScript Configuration
|
|
11
|
+
|
|
12
|
+
### Strict tsconfig.json
|
|
13
|
+
```json
|
|
14
|
+
{
|
|
15
|
+
"compilerOptions": {
|
|
16
|
+
"strict": true,
|
|
17
|
+
"noUncheckedIndexedAccess": true,
|
|
18
|
+
"noImplicitReturns": true,
|
|
19
|
+
"noFallthroughCasesInSwitch": true,
|
|
20
|
+
"exactOptionalPropertyTypes": true,
|
|
21
|
+
"moduleResolution": "bundler",
|
|
22
|
+
"module": "ESNext",
|
|
23
|
+
"target": "ES2022",
|
|
24
|
+
"esModuleInterop": true,
|
|
25
|
+
"skipLibCheck": true,
|
|
26
|
+
"forceConsistentCasingInFileNames": true,
|
|
27
|
+
"resolveJsonModule": true,
|
|
28
|
+
"isolatedModules": true,
|
|
29
|
+
"declaration": true,
|
|
30
|
+
"declarationMap": true,
|
|
31
|
+
"sourceMap": true,
|
|
32
|
+
"outDir": "./dist",
|
|
33
|
+
"rootDir": "./src"
|
|
34
|
+
},
|
|
35
|
+
"include": ["src/**/*"],
|
|
36
|
+
"exclude": ["node_modules", "dist"]
|
|
37
|
+
}
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## ESLint + Prettier
|
|
41
|
+
|
|
42
|
+
```json
|
|
43
|
+
// .eslintrc.json (flat config recommended for new projects)
|
|
44
|
+
{
|
|
45
|
+
"extends": [
|
|
46
|
+
"eslint:recommended",
|
|
47
|
+
"plugin:@typescript-eslint/recommended-type-checked",
|
|
48
|
+
"prettier"
|
|
49
|
+
],
|
|
50
|
+
"parser": "@typescript-eslint/parser",
|
|
51
|
+
"parserOptions": {
|
|
52
|
+
"project": "./tsconfig.json"
|
|
53
|
+
},
|
|
54
|
+
"rules": {
|
|
55
|
+
"@typescript-eslint/no-unused-vars": ["error", { "argsIgnorePattern": "^_" }],
|
|
56
|
+
"@typescript-eslint/no-floating-promises": "error",
|
|
57
|
+
"@typescript-eslint/no-misused-promises": "error"
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## React Patterns
|
|
63
|
+
|
|
64
|
+
### Functional Component
|
|
65
|
+
```tsx
|
|
66
|
+
interface UserCardProps {
|
|
67
|
+
user: User;
|
|
68
|
+
onSelect?: (id: string) => void;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export function UserCard({ user, onSelect }: UserCardProps) {
|
|
72
|
+
const handleClick = useCallback(() => {
|
|
73
|
+
onSelect?.(user.id);
|
|
74
|
+
}, [user.id, onSelect]);
|
|
75
|
+
|
|
76
|
+
return (
|
|
77
|
+
<div onClick={handleClick} role="button" tabIndex={0}>
|
|
78
|
+
<h3>{user.name}</h3>
|
|
79
|
+
<p>{user.email}</p>
|
|
80
|
+
</div>
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### Custom Hook
|
|
86
|
+
```tsx
|
|
87
|
+
function useAsync<T>(fn: () => Promise<T>, deps: unknown[]) {
|
|
88
|
+
const [state, setState] = useState<{
|
|
89
|
+
data: T | null;
|
|
90
|
+
error: Error | null;
|
|
91
|
+
loading: boolean;
|
|
92
|
+
}>({ data: null, error: null, loading: true });
|
|
93
|
+
|
|
94
|
+
useEffect(() => {
|
|
95
|
+
let cancelled = false;
|
|
96
|
+
setState(prev => ({ ...prev, loading: true }));
|
|
97
|
+
fn()
|
|
98
|
+
.then(data => { if (!cancelled) setState({ data, error: null, loading: false }); })
|
|
99
|
+
.catch(error => { if (!cancelled) setState({ data: null, error, loading: false }); });
|
|
100
|
+
return () => { cancelled = true; };
|
|
101
|
+
}, deps);
|
|
102
|
+
|
|
103
|
+
return state;
|
|
104
|
+
}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### Context Pattern
|
|
108
|
+
```tsx
|
|
109
|
+
interface AuthContextType {
|
|
110
|
+
user: User | null;
|
|
111
|
+
login: (credentials: Credentials) => Promise<void>;
|
|
112
|
+
logout: () => void;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const AuthContext = createContext<AuthContextType | null>(null);
|
|
116
|
+
|
|
117
|
+
export function useAuth() {
|
|
118
|
+
const context = useContext(AuthContext);
|
|
119
|
+
if (!context) throw new Error("useAuth must be within AuthProvider");
|
|
120
|
+
return context;
|
|
121
|
+
}
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## Next.js App Router
|
|
125
|
+
|
|
126
|
+
### Server Component (default)
|
|
127
|
+
```tsx
|
|
128
|
+
// app/users/page.tsx
|
|
129
|
+
export default async function UsersPage() {
|
|
130
|
+
const users = await db.user.findMany();
|
|
131
|
+
return <UserList users={users} />;
|
|
132
|
+
}
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### Server Action
|
|
136
|
+
```tsx
|
|
137
|
+
"use server";
|
|
138
|
+
|
|
139
|
+
import { revalidatePath } from "next/cache";
|
|
140
|
+
|
|
141
|
+
export async function createUser(formData: FormData) {
|
|
142
|
+
const name = formData.get("name") as string;
|
|
143
|
+
await db.user.create({ data: { name } });
|
|
144
|
+
revalidatePath("/users");
|
|
145
|
+
}
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
## Node.js/Express Patterns
|
|
149
|
+
|
|
150
|
+
### Type-safe Route Handler
|
|
151
|
+
```typescript
|
|
152
|
+
import { Router, Request, Response } from "express";
|
|
153
|
+
import { z } from "zod";
|
|
154
|
+
|
|
155
|
+
const CreateUserSchema = z.object({
|
|
156
|
+
name: z.string().min(1).max(100),
|
|
157
|
+
email: z.string().email(),
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
router.post("/users", async (req: Request, res: Response) => {
|
|
161
|
+
const result = CreateUserSchema.safeParse(req.body);
|
|
162
|
+
if (!result.success) {
|
|
163
|
+
return res.status(400).json({ errors: result.error.flatten() });
|
|
164
|
+
}
|
|
165
|
+
const user = await userService.create(result.data);
|
|
166
|
+
res.status(201).json(user);
|
|
167
|
+
});
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
## State Management
|
|
171
|
+
|
|
172
|
+
### Zustand Store
|
|
173
|
+
```typescript
|
|
174
|
+
import { create } from "zustand";
|
|
175
|
+
|
|
176
|
+
interface CartStore {
|
|
177
|
+
items: CartItem[];
|
|
178
|
+
addItem: (item: CartItem) => void;
|
|
179
|
+
removeItem: (id: string) => void;
|
|
180
|
+
total: () => number;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
export const useCartStore = create<CartStore>((set, get) => ({
|
|
184
|
+
items: [],
|
|
185
|
+
addItem: (item) => set((state) => ({ items: [...state.items, item] })),
|
|
186
|
+
removeItem: (id) => set((state) => ({ items: state.items.filter(i => i.id !== id) })),
|
|
187
|
+
total: () => get().items.reduce((sum, item) => sum + item.price, 0),
|
|
188
|
+
}));
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
## Type-safe API (tRPC / Zod)
|
|
192
|
+
|
|
193
|
+
### Zod Schema Validation
|
|
194
|
+
```typescript
|
|
195
|
+
import { z } from "zod";
|
|
196
|
+
|
|
197
|
+
export const UserSchema = z.object({
|
|
198
|
+
id: z.string().uuid(),
|
|
199
|
+
name: z.string().min(1).max(100),
|
|
200
|
+
email: z.string().email(),
|
|
201
|
+
role: z.enum(["admin", "user", "guest"]),
|
|
202
|
+
createdAt: z.coerce.date(),
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
export type User = z.infer<typeof UserSchema>;
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
## Error Handling
|
|
209
|
+
|
|
210
|
+
```typescript
|
|
211
|
+
class AppError extends Error {
|
|
212
|
+
constructor(
|
|
213
|
+
message: string,
|
|
214
|
+
public statusCode: number = 500,
|
|
215
|
+
public code: string = "INTERNAL_ERROR"
|
|
216
|
+
) {
|
|
217
|
+
super(message);
|
|
218
|
+
this.name = "AppError";
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// Result pattern (no exceptions)
|
|
223
|
+
type Result<T, E = Error> =
|
|
224
|
+
| { success: true; data: T }
|
|
225
|
+
| { success: false; error: E };
|
|
226
|
+
|
|
227
|
+
function parseUser(raw: unknown): Result<User> {
|
|
228
|
+
const result = UserSchema.safeParse(raw);
|
|
229
|
+
if (result.success) return { success: true, data: result.data };
|
|
230
|
+
return { success: false, error: new Error(result.error.message) };
|
|
231
|
+
}
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
## Testing with Vitest
|
|
235
|
+
|
|
236
|
+
```typescript
|
|
237
|
+
import { describe, it, expect, vi } from "vitest";
|
|
238
|
+
|
|
239
|
+
describe("UserService", () => {
|
|
240
|
+
it("creates user with valid data", async () => {
|
|
241
|
+
const mockRepo = { create: vi.fn().mockResolvedValue({ id: "1", name: "Test" }) };
|
|
242
|
+
const service = new UserService(mockRepo);
|
|
243
|
+
const user = await service.create({ name: "Test", email: "t@t.com" });
|
|
244
|
+
expect(user.id).toBe("1");
|
|
245
|
+
expect(mockRepo.create).toHaveBeenCalledOnce();
|
|
246
|
+
});
|
|
247
|
+
});
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
## Anti-Patterns
|
|
251
|
+
- `any` type usage (use `unknown` and narrow)
|
|
252
|
+
- Non-null assertions `!` without checks
|
|
253
|
+
- `as` type casting instead of type guards
|
|
254
|
+
- Mutable state in React (use immutable updates)
|
|
255
|
+
- Missing error boundaries in React
|
|
256
|
+
- Barrel exports that break tree-shaking
|