@nextsparkjs/ai-workflow 0.1.0-beta.100
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +115 -0
- package/claude/_docs/workflows-optimizations.md +359 -0
- package/claude/agents/api-tester.md +634 -0
- package/claude/agents/architecture-supervisor.md +1351 -0
- package/claude/agents/backend-developer.md +997 -0
- package/claude/agents/backend-validator.md +417 -0
- package/claude/agents/bdd-docs-writer.md +737 -0
- package/claude/agents/block-developer.md +677 -0
- package/claude/agents/code-reviewer.md +1432 -0
- package/claude/agents/db-developer.md +721 -0
- package/claude/agents/db-validator.md +407 -0
- package/claude/agents/demo-video-generator.md +493 -0
- package/claude/agents/documentation-writer.md +1268 -0
- package/claude/agents/frontend-developer.md +1234 -0
- package/claude/agents/frontend-validator.md +777 -0
- package/claude/agents/functional-validator.md +630 -0
- package/claude/agents/mock-analyst.md +387 -0
- package/claude/agents/product-manager.md +963 -0
- package/claude/agents/qa-automation.md +1762 -0
- package/claude/agents/release-manager.md +634 -0
- package/claude/agents/selectors-translator.md +262 -0
- package/claude/agents/unit-test-writer.md +785 -0
- package/claude/agents/visual-comparator.md +329 -0
- package/claude/agents/workflow-maintainer.md +352 -0
- package/claude/commands/do/README.md +88 -0
- package/claude/commands/do/create-api.md +64 -0
- package/claude/commands/do/create-entity.md +66 -0
- package/claude/commands/do/create-migration.md +64 -0
- package/claude/commands/do/create-plugin.md +56 -0
- package/claude/commands/do/create-theme.md +70 -0
- package/claude/commands/do/mock-data.md +67 -0
- package/claude/commands/do/reset-db.md +71 -0
- package/claude/commands/do/setup-scheduled-action.md +75 -0
- package/claude/commands/do/sync-code-review.md +117 -0
- package/claude/commands/do/update-selectors.md +112 -0
- package/claude/commands/do/use-skills.md +90 -0
- package/claude/commands/do/validate-blocks.md +69 -0
- package/claude/commands/how-to/README.md +261 -0
- package/claude/commands/how-to/add-metadata.md +692 -0
- package/claude/commands/how-to/add-taxonomies.md +806 -0
- package/claude/commands/how-to/add-translations.md +571 -0
- package/claude/commands/how-to/create-api.md +577 -0
- package/claude/commands/how-to/create-block.md +575 -0
- package/claude/commands/how-to/create-child-entities.md +771 -0
- package/claude/commands/how-to/create-entity.md +597 -0
- package/claude/commands/how-to/create-migrations.md +605 -0
- package/claude/commands/how-to/create-plugin.md +654 -0
- package/claude/commands/how-to/customize-app.md +481 -0
- package/claude/commands/how-to/customize-dashboard.md +553 -0
- package/claude/commands/how-to/customize-theme.md +438 -0
- package/claude/commands/how-to/define-features-flows.md +632 -0
- package/claude/commands/how-to/deploy.md +507 -0
- package/claude/commands/how-to/handle-file-uploads.md +746 -0
- package/claude/commands/how-to/implement-search.md +1001 -0
- package/claude/commands/how-to/install-plugins.md +352 -0
- package/claude/commands/how-to/manage-test-coverage.md +984 -0
- package/claude/commands/how-to/run-tests.md +400 -0
- package/claude/commands/how-to/set-app-languages.md +601 -0
- package/claude/commands/how-to/set-plans-and-permissions.md +575 -0
- package/claude/commands/how-to/set-scheduled-actions.md +527 -0
- package/claude/commands/how-to/set-user-roles-and-permissions.md +550 -0
- package/claude/commands/how-to/setup-authentication.md +388 -0
- package/claude/commands/how-to/setup-claude-code.md +440 -0
- package/claude/commands/how-to/setup-database.md +274 -0
- package/claude/commands/how-to/setup-email-providers.md +598 -0
- package/claude/commands/how-to/setup-mobile-dev.md +627 -0
- package/claude/commands/how-to/start.md +500 -0
- package/claude/commands/how-to/use-devtools.md +639 -0
- package/claude/commands/how-to/use-superadmin.md +622 -0
- package/claude/commands/session/README.md +193 -0
- package/claude/commands/session/block-create.md +190 -0
- package/claude/commands/session/block-list.md +203 -0
- package/claude/commands/session/block-update.md +192 -0
- package/claude/commands/session/block-validate.md +218 -0
- package/claude/commands/session/changelog.md +115 -0
- package/claude/commands/session/close.md +225 -0
- package/claude/commands/session/commit.md +174 -0
- package/claude/commands/session/db-entity.md +206 -0
- package/claude/commands/session/db-fix.md +212 -0
- package/claude/commands/session/db-sample.md +206 -0
- package/claude/commands/session/demo.md +178 -0
- package/claude/commands/session/doc-bdd.md +207 -0
- package/claude/commands/session/doc-feature.md +218 -0
- package/claude/commands/session/doc-read.md +225 -0
- package/claude/commands/session/execute.md +204 -0
- package/claude/commands/session/explain.md +202 -0
- package/claude/commands/session/fix-bug.md +210 -0
- package/claude/commands/session/fix-build.md +182 -0
- package/claude/commands/session/fix-test.md +189 -0
- package/claude/commands/session/pending.md +232 -0
- package/claude/commands/session/refine.md +188 -0
- package/claude/commands/session/resume.md +192 -0
- package/claude/commands/session/review.md +192 -0
- package/claude/commands/session/scope-change.md +181 -0
- package/claude/commands/session/start-blocks.md +347 -0
- package/claude/commands/session/start.md +604 -0
- package/claude/commands/session/status.md +169 -0
- package/claude/commands/session/test-fix.md +221 -0
- package/claude/commands/session/test-run.md +203 -0
- package/claude/commands/session/test-write.md +242 -0
- package/claude/commands/session/validate.md +162 -0
- package/claude/config/context.json +40 -0
- package/claude/config/github.json +69 -0
- package/claude/config/github.schema.json +106 -0
- package/claude/config/team.json +46 -0
- package/claude/config/team.schema.json +106 -0
- package/claude/config/workspace.json +43 -0
- package/claude/config/workspace.schema.json +75 -0
- package/claude/skills/README.md +228 -0
- package/claude/skills/accessibility/SKILL.md +573 -0
- package/claude/skills/api-bypass-layers/SKILL.md +550 -0
- package/claude/skills/asana-integration/SKILL.md +499 -0
- package/claude/skills/better-auth/SKILL.md +666 -0
- package/claude/skills/billing-subscriptions/SKILL.md +660 -0
- package/claude/skills/block-decision-matrix/SKILL.md +359 -0
- package/claude/skills/clickup-integration/SKILL.md +434 -0
- package/claude/skills/core-theme-responsibilities/SKILL.md +485 -0
- package/claude/skills/create-plugin/SKILL.md +425 -0
- package/claude/skills/create-theme/SKILL.md +331 -0
- package/claude/skills/cypress-api/SKILL.md +511 -0
- package/claude/skills/cypress-api/scripts/generate-api-controller.py +329 -0
- package/claude/skills/cypress-api/scripts/generate-api-test.py +930 -0
- package/claude/skills/cypress-e2e/SKILL.md +526 -0
- package/claude/skills/cypress-e2e/scripts/extract-selectors.py +383 -0
- package/claude/skills/cypress-e2e/scripts/generate-uat-test.py +788 -0
- package/claude/skills/cypress-selectors/SKILL.md +309 -0
- package/claude/skills/cypress-selectors/scripts/extract-missing.py +243 -0
- package/claude/skills/cypress-selectors/scripts/generate-block-selectors.py +283 -0
- package/claude/skills/cypress-selectors/scripts/validate-selectors.py +145 -0
- package/claude/skills/database-migrations/SKILL.md +335 -0
- package/claude/skills/database-migrations/scripts/generate-sample-data.py +284 -0
- package/claude/skills/database-migrations/scripts/validate-migration.py +323 -0
- package/claude/skills/design-system/SKILL.md +682 -0
- package/claude/skills/documentation/SKILL.md +540 -0
- package/claude/skills/entity-api/SKILL.md +482 -0
- package/claude/skills/entity-system/SKILL.md +635 -0
- package/claude/skills/entity-system/scripts/generate-child-migration.py +298 -0
- package/claude/skills/entity-system/scripts/generate-metas-migration.py +233 -0
- package/claude/skills/entity-system/scripts/generate-migration.py +382 -0
- package/claude/skills/entity-system/scripts/generate-sample-data.py +418 -0
- package/claude/skills/entity-system/scripts/scaffold-entity.py +661 -0
- package/claude/skills/github/SKILL.md +467 -0
- package/claude/skills/i18n-nextintl/SKILL.md +302 -0
- package/claude/skills/i18n-nextintl/scripts/add-translation.py +243 -0
- package/claude/skills/i18n-nextintl/scripts/extract-hardcoded.py +246 -0
- package/claude/skills/i18n-nextintl/scripts/validate-translations.py +260 -0
- package/claude/skills/impact-analysis/SKILL.md +203 -0
- package/claude/skills/jest-unit/SKILL.md +306 -0
- package/claude/skills/jest-unit/references/component-testing.md +371 -0
- package/claude/skills/jest-unit/references/mocking-patterns.md +380 -0
- package/claude/skills/jest-unit/references/service-hook-testing.md +454 -0
- package/claude/skills/jira-integration/SKILL.md +539 -0
- package/claude/skills/media-library/SKILL.md +743 -0
- package/claude/skills/mock-analysis/SKILL.md +276 -0
- package/claude/skills/monorepo-architecture/SKILL.md +162 -0
- package/claude/skills/nextjs-api-development/SKILL.md +364 -0
- package/claude/skills/nextjs-api-development/scripts/generate-crud-tests.py +456 -0
- package/claude/skills/nextjs-api-development/scripts/scaffold-endpoint.py +481 -0
- package/claude/skills/nextjs-api-development/scripts/validate-api.py +283 -0
- package/claude/skills/notion-integration/SKILL.md +641 -0
- package/claude/skills/npm-development-workflow/SKILL.md +480 -0
- package/claude/skills/page-builder-blocks/SKILL.md +530 -0
- package/claude/skills/page-builder-blocks/scripts/scaffold-block.py +444 -0
- package/claude/skills/permissions-system/SKILL.md +619 -0
- package/claude/skills/plugins/SKILL.md +340 -0
- package/claude/skills/plugins/references/plugin-templates.md +414 -0
- package/claude/skills/plugins/references/plugin-testing.md +353 -0
- package/claude/skills/plugins/references/plugin-types.md +198 -0
- package/claude/skills/plugins/scripts/scaffold-plugin.py +443 -0
- package/claude/skills/pom-patterns/SKILL.md +452 -0
- package/claude/skills/pom-patterns/scripts/generate-pom.py +392 -0
- package/claude/skills/rate-limiting/SKILL.md +342 -0
- package/claude/skills/react-best-practices/AGENTS.md +2410 -0
- package/claude/skills/react-best-practices/README.md +123 -0
- package/claude/skills/react-best-practices/SKILL.md +125 -0
- package/claude/skills/react-best-practices/metadata.json +15 -0
- package/claude/skills/react-best-practices/rules/_sections.md +46 -0
- package/claude/skills/react-best-practices/rules/_template.md +28 -0
- package/claude/skills/react-best-practices/rules/advanced-event-handler-refs.md +55 -0
- package/claude/skills/react-best-practices/rules/advanced-use-latest.md +49 -0
- package/claude/skills/react-best-practices/rules/async-api-routes.md +38 -0
- package/claude/skills/react-best-practices/rules/async-defer-await.md +80 -0
- package/claude/skills/react-best-practices/rules/async-dependencies.md +36 -0
- package/claude/skills/react-best-practices/rules/async-parallel.md +28 -0
- package/claude/skills/react-best-practices/rules/async-suspense-boundaries.md +99 -0
- package/claude/skills/react-best-practices/rules/bundle-barrel-imports.md +59 -0
- package/claude/skills/react-best-practices/rules/bundle-conditional.md +31 -0
- package/claude/skills/react-best-practices/rules/bundle-defer-third-party.md +49 -0
- package/claude/skills/react-best-practices/rules/bundle-dynamic-imports.md +35 -0
- package/claude/skills/react-best-practices/rules/bundle-preload.md +50 -0
- package/claude/skills/react-best-practices/rules/client-event-listeners.md +74 -0
- package/claude/skills/react-best-practices/rules/client-localstorage-schema.md +71 -0
- package/claude/skills/react-best-practices/rules/client-passive-event-listeners.md +48 -0
- package/claude/skills/react-best-practices/rules/client-swr-dedup.md +56 -0
- package/claude/skills/react-best-practices/rules/js-batch-dom-css.md +82 -0
- package/claude/skills/react-best-practices/rules/js-cache-function-results.md +80 -0
- package/claude/skills/react-best-practices/rules/js-cache-property-access.md +28 -0
- package/claude/skills/react-best-practices/rules/js-cache-storage.md +70 -0
- package/claude/skills/react-best-practices/rules/js-combine-iterations.md +32 -0
- package/claude/skills/react-best-practices/rules/js-early-exit.md +50 -0
- package/claude/skills/react-best-practices/rules/js-hoist-regexp.md +45 -0
- package/claude/skills/react-best-practices/rules/js-index-maps.md +37 -0
- package/claude/skills/react-best-practices/rules/js-length-check-first.md +49 -0
- package/claude/skills/react-best-practices/rules/js-min-max-loop.md +82 -0
- package/claude/skills/react-best-practices/rules/js-set-map-lookups.md +24 -0
- package/claude/skills/react-best-practices/rules/js-tosorted-immutable.md +57 -0
- package/claude/skills/react-best-practices/rules/rendering-activity.md +26 -0
- package/claude/skills/react-best-practices/rules/rendering-animate-svg-wrapper.md +47 -0
- package/claude/skills/react-best-practices/rules/rendering-conditional-render.md +40 -0
- package/claude/skills/react-best-practices/rules/rendering-content-visibility.md +38 -0
- package/claude/skills/react-best-practices/rules/rendering-hoist-jsx.md +46 -0
- package/claude/skills/react-best-practices/rules/rendering-hydration-no-flicker.md +82 -0
- package/claude/skills/react-best-practices/rules/rendering-svg-precision.md +28 -0
- package/claude/skills/react-best-practices/rules/rerender-defer-reads.md +39 -0
- package/claude/skills/react-best-practices/rules/rerender-dependencies.md +45 -0
- package/claude/skills/react-best-practices/rules/rerender-derived-state.md +29 -0
- package/claude/skills/react-best-practices/rules/rerender-functional-setstate.md +74 -0
- package/claude/skills/react-best-practices/rules/rerender-lazy-state-init.md +58 -0
- package/claude/skills/react-best-practices/rules/rerender-memo.md +44 -0
- package/claude/skills/react-best-practices/rules/rerender-transitions.md +40 -0
- package/claude/skills/react-best-practices/rules/server-after-nonblocking.md +73 -0
- package/claude/skills/react-best-practices/rules/server-cache-lru.md +41 -0
- package/claude/skills/react-best-practices/rules/server-cache-react.md +76 -0
- package/claude/skills/react-best-practices/rules/server-parallel-fetching.md +83 -0
- package/claude/skills/react-best-practices/rules/server-serialization.md +38 -0
- package/claude/skills/react-patterns/SKILL.md +688 -0
- package/claude/skills/registry-system/SKILL.md +331 -0
- package/claude/skills/scheduled-actions/SKILL.md +671 -0
- package/claude/skills/scope-enforcement/SKILL.md +542 -0
- package/claude/skills/scope-enforcement/scripts/validate-scope.py +357 -0
- package/claude/skills/server-actions/SKILL.md +493 -0
- package/claude/skills/service-layer/SKILL.md +587 -0
- package/claude/skills/session-management/SKILL.md +266 -0
- package/claude/skills/session-management/scripts/create-session.py +166 -0
- package/claude/skills/session-management/scripts/iteration-close.sh +105 -0
- package/claude/skills/session-management/scripts/iteration-init.sh +180 -0
- package/claude/skills/session-management/scripts/session-archive.sh +87 -0
- package/claude/skills/session-management/scripts/session-close.sh +133 -0
- package/claude/skills/session-management/scripts/session-init.sh +225 -0
- package/claude/skills/session-management/scripts/session-list.sh +163 -0
- package/claude/skills/session-management/scripts/split-plan.sh +116 -0
- package/claude/skills/shadcn-components/SKILL.md +586 -0
- package/claude/skills/shadcn-theming/SKILL.md +446 -0
- package/claude/skills/suspense-loading/SKILL.md +280 -0
- package/claude/skills/tailwind-theming/SKILL.md +507 -0
- package/claude/skills/tanstack-query/SKILL.md +608 -0
- package/claude/skills/test-coverage/SKILL.md +239 -0
- package/claude/skills/web-design-guidelines/SKILL.md +39 -0
- package/claude/skills/zod-validation/SKILL.md +537 -0
- package/claude/templates/blocks/progress.md +86 -0
- package/claude/templates/iteration/changes.md +61 -0
- package/claude/templates/iteration/progress.md +55 -0
- package/claude/templates/log.md +31 -0
- package/claude/templates/story/context.md +77 -0
- package/claude/templates/story/pendings.md +37 -0
- package/claude/templates/story/plan.md +299 -0
- package/claude/templates/story/requirements.md +109 -0
- package/claude/templates/story/scope.json +10 -0
- package/claude/templates/story/tests.md +91 -0
- package/claude/templates/task/progress.md +58 -0
- package/claude/templates/task/requirements.md +54 -0
- package/claude/workflows/README.md +154 -0
- package/claude/workflows/blocks.md +614 -0
- package/claude/workflows/story.md +1207 -0
- package/claude/workflows/task.md +927 -0
- package/claude/workflows/tweak.md +527 -0
- package/cursor/.gitkeep +0 -0
- package/package.json +35 -0
- package/scripts/postinstall.mjs +198 -0
- package/scripts/setup.mjs +282 -0
- package/scripts/sync.mjs +209 -0
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Extract Hardcoded Strings Script
|
|
4
|
+
|
|
5
|
+
Finds hardcoded user-facing strings in React/TypeScript components that should
|
|
6
|
+
be translated using next-intl.
|
|
7
|
+
|
|
8
|
+
Usage:
|
|
9
|
+
python extract-hardcoded.py --path PATH [--dry-run]
|
|
10
|
+
|
|
11
|
+
Options:
|
|
12
|
+
--path PATH Path to scan for hardcoded strings
|
|
13
|
+
--dry-run Preview findings without file details
|
|
14
|
+
--ignore-tests Skip test files (*.test.*, *.spec.*, *.cy.*)
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
import os
|
|
18
|
+
import re
|
|
19
|
+
import sys
|
|
20
|
+
import argparse
|
|
21
|
+
from pathlib import Path
|
|
22
|
+
from typing import List, Dict, Tuple
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def get_active_theme() -> str:
|
|
26
|
+
"""Get active theme from environment or default."""
|
|
27
|
+
return os.environ.get('NEXT_PUBLIC_ACTIVE_THEME', 'default')
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def should_ignore_file(file_path: str, ignore_tests: bool) -> bool:
|
|
31
|
+
"""Check if file should be ignored."""
|
|
32
|
+
ignore_patterns = [
|
|
33
|
+
'node_modules',
|
|
34
|
+
'.next',
|
|
35
|
+
'dist',
|
|
36
|
+
'.git',
|
|
37
|
+
'__pycache__',
|
|
38
|
+
'messages/', # Translation files themselves
|
|
39
|
+
'.json',
|
|
40
|
+
'.md',
|
|
41
|
+
'.css',
|
|
42
|
+
'.scss',
|
|
43
|
+
]
|
|
44
|
+
|
|
45
|
+
if ignore_tests:
|
|
46
|
+
ignore_patterns.extend(['.test.', '.spec.', '.cy.'])
|
|
47
|
+
|
|
48
|
+
for pattern in ignore_patterns:
|
|
49
|
+
if pattern in file_path:
|
|
50
|
+
return True
|
|
51
|
+
|
|
52
|
+
return False
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def extract_jsx_strings(content: str, file_path: str) -> List[Dict]:
|
|
56
|
+
"""Extract potential hardcoded strings from JSX/TSX content."""
|
|
57
|
+
findings = []
|
|
58
|
+
|
|
59
|
+
# Pattern 1: Strings in JSX text content (between > and <)
|
|
60
|
+
# Matches: >Hello World< but not >{variable}<
|
|
61
|
+
jsx_text_pattern = r'>([A-Z][^<>{}\n]{2,})<'
|
|
62
|
+
for match in re.finditer(jsx_text_pattern, content):
|
|
63
|
+
text = match.group(1).strip()
|
|
64
|
+
if text and not text.startswith('{') and len(text) > 2:
|
|
65
|
+
line_num = content[:match.start()].count('\n') + 1
|
|
66
|
+
findings.append({
|
|
67
|
+
'type': 'jsx_text',
|
|
68
|
+
'text': text[:80] + ('...' if len(text) > 80 else ''),
|
|
69
|
+
'line': line_num,
|
|
70
|
+
'file': file_path,
|
|
71
|
+
'suggestion': suggest_key(text)
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
# Pattern 2: Hardcoded strings in common props (title, placeholder, label, etc.)
|
|
75
|
+
prop_patterns = [
|
|
76
|
+
(r'title=["\']([^"\']+)["\']', 'title_prop'),
|
|
77
|
+
(r'placeholder=["\']([^"\']+)["\']', 'placeholder_prop'),
|
|
78
|
+
(r'label=["\']([^"\']+)["\']', 'label_prop'),
|
|
79
|
+
(r'alt=["\']([^"\']+)["\']', 'alt_prop'),
|
|
80
|
+
(r'aria-label=["\']([^"\']+)["\']', 'aria_label_prop'),
|
|
81
|
+
(r'description=["\']([^"\']+)["\']', 'description_prop'),
|
|
82
|
+
]
|
|
83
|
+
|
|
84
|
+
for pattern, prop_type in prop_patterns:
|
|
85
|
+
for match in re.finditer(pattern, content, re.IGNORECASE):
|
|
86
|
+
text = match.group(1).strip()
|
|
87
|
+
# Skip if it looks like a variable or translation key
|
|
88
|
+
if text and not text.startswith('{') and not is_likely_non_text(text):
|
|
89
|
+
line_num = content[:match.start()].count('\n') + 1
|
|
90
|
+
findings.append({
|
|
91
|
+
'type': prop_type,
|
|
92
|
+
'text': text[:60] + ('...' if len(text) > 60 else ''),
|
|
93
|
+
'line': line_num,
|
|
94
|
+
'file': file_path,
|
|
95
|
+
'suggestion': suggest_key(text)
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
# Pattern 3: String literals that look like user-facing text
|
|
99
|
+
# This catches: const message = "Welcome back!"
|
|
100
|
+
string_literal_pattern = r'(?:const|let|var)\s+\w+\s*=\s*["\']([A-Z][^"\']{5,})["\']'
|
|
101
|
+
for match in re.finditer(string_literal_pattern, content):
|
|
102
|
+
text = match.group(1).strip()
|
|
103
|
+
if not is_likely_non_text(text):
|
|
104
|
+
line_num = content[:match.start()].count('\n') + 1
|
|
105
|
+
findings.append({
|
|
106
|
+
'type': 'string_literal',
|
|
107
|
+
'text': text[:60] + ('...' if len(text) > 60 else ''),
|
|
108
|
+
'line': line_num,
|
|
109
|
+
'file': file_path,
|
|
110
|
+
'suggestion': suggest_key(text)
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
return findings
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def is_likely_non_text(text: str) -> bool:
|
|
117
|
+
"""Check if string is likely NOT user-facing text."""
|
|
118
|
+
# Skip URLs, paths, technical strings
|
|
119
|
+
non_text_indicators = [
|
|
120
|
+
text.startswith('http'),
|
|
121
|
+
text.startswith('/'),
|
|
122
|
+
text.startswith('@'),
|
|
123
|
+
text.startswith('#'),
|
|
124
|
+
'.com' in text,
|
|
125
|
+
'.org' in text,
|
|
126
|
+
text.isupper() and '_' in text, # Constants like API_URL
|
|
127
|
+
len(text.split()) == 1 and text.islower(), # Single lowercase word
|
|
128
|
+
re.match(r'^[a-z-]+$', text), # kebab-case identifiers
|
|
129
|
+
re.match(r'^[a-zA-Z]+\.[a-zA-Z.]+$', text), # Dot notation
|
|
130
|
+
]
|
|
131
|
+
return any(non_text_indicators)
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def suggest_key(text: str) -> str:
|
|
135
|
+
"""Suggest a translation key based on the text."""
|
|
136
|
+
# Take first few words, convert to camelCase
|
|
137
|
+
words = re.sub(r'[^a-zA-Z\s]', '', text).lower().split()[:4]
|
|
138
|
+
if not words:
|
|
139
|
+
return 'common.text'
|
|
140
|
+
|
|
141
|
+
# Convert to camelCase
|
|
142
|
+
key = words[0] + ''.join(w.title() for w in words[1:])
|
|
143
|
+
return f'common.{key}'
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def scan_directory(path: str, ignore_tests: bool) -> List[Dict]:
|
|
147
|
+
"""Scan directory for hardcoded strings."""
|
|
148
|
+
all_findings = []
|
|
149
|
+
path_obj = Path(path)
|
|
150
|
+
|
|
151
|
+
if path_obj.is_file():
|
|
152
|
+
files = [path_obj]
|
|
153
|
+
else:
|
|
154
|
+
files = list(path_obj.rglob('*.tsx')) + list(path_obj.rglob('*.ts'))
|
|
155
|
+
|
|
156
|
+
for file_path in files:
|
|
157
|
+
str_path = str(file_path)
|
|
158
|
+
if should_ignore_file(str_path, ignore_tests):
|
|
159
|
+
continue
|
|
160
|
+
|
|
161
|
+
try:
|
|
162
|
+
with open(file_path, 'r', encoding='utf-8') as f:
|
|
163
|
+
content = f.read()
|
|
164
|
+
|
|
165
|
+
# Skip files that already use translations properly
|
|
166
|
+
if 'useTranslations' in content or 'getTranslations' in content:
|
|
167
|
+
# Still check for any remaining hardcoded strings
|
|
168
|
+
pass
|
|
169
|
+
|
|
170
|
+
findings = extract_jsx_strings(content, str_path)
|
|
171
|
+
all_findings.extend(findings)
|
|
172
|
+
|
|
173
|
+
except Exception as e:
|
|
174
|
+
print(f"Warning: Could not read {file_path}: {e}")
|
|
175
|
+
|
|
176
|
+
return all_findings
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
def main():
|
|
180
|
+
parser = argparse.ArgumentParser(description='Find hardcoded strings')
|
|
181
|
+
parser.add_argument('--path', required=True, help='Path to scan')
|
|
182
|
+
parser.add_argument('--dry-run', action='store_true', help='Preview without details')
|
|
183
|
+
parser.add_argument('--ignore-tests', action='store_true', help='Skip test files')
|
|
184
|
+
|
|
185
|
+
args = parser.parse_args()
|
|
186
|
+
|
|
187
|
+
if not os.path.exists(args.path):
|
|
188
|
+
print(f"Error: Path not found: {args.path}")
|
|
189
|
+
return 1
|
|
190
|
+
|
|
191
|
+
print(f"\n{'=' * 60}")
|
|
192
|
+
print(f"SCANNING FOR HARDCODED STRINGS")
|
|
193
|
+
print(f"{'=' * 60}")
|
|
194
|
+
print(f"Path: {args.path}")
|
|
195
|
+
print(f"Ignore tests: {args.ignore_tests}")
|
|
196
|
+
print(f"{'=' * 60}\n")
|
|
197
|
+
|
|
198
|
+
findings = scan_directory(args.path, args.ignore_tests)
|
|
199
|
+
|
|
200
|
+
if not findings:
|
|
201
|
+
print("No hardcoded strings found.")
|
|
202
|
+
return 0
|
|
203
|
+
|
|
204
|
+
# Group by file
|
|
205
|
+
by_file: Dict[str, List[Dict]] = {}
|
|
206
|
+
for finding in findings:
|
|
207
|
+
file_path = finding['file']
|
|
208
|
+
if file_path not in by_file:
|
|
209
|
+
by_file[file_path] = []
|
|
210
|
+
by_file[file_path].append(finding)
|
|
211
|
+
|
|
212
|
+
print(f"Found {len(findings)} potential hardcoded strings in {len(by_file)} files:\n")
|
|
213
|
+
|
|
214
|
+
if args.dry_run:
|
|
215
|
+
# Summary only
|
|
216
|
+
for file_path, file_findings in sorted(by_file.items()):
|
|
217
|
+
rel_path = os.path.relpath(file_path)
|
|
218
|
+
print(f" {rel_path}: {len(file_findings)} findings")
|
|
219
|
+
else:
|
|
220
|
+
# Detailed output
|
|
221
|
+
for file_path, file_findings in sorted(by_file.items()):
|
|
222
|
+
rel_path = os.path.relpath(file_path)
|
|
223
|
+
print(f"\n{'-' * 60}")
|
|
224
|
+
print(f"FILE: {rel_path}")
|
|
225
|
+
print(f"{'-' * 60}")
|
|
226
|
+
|
|
227
|
+
for finding in file_findings:
|
|
228
|
+
print(f" Line {finding['line']}: [{finding['type']}]")
|
|
229
|
+
print(f" Text: \"{finding['text']}\"")
|
|
230
|
+
print(f" Suggested key: {finding['suggestion']}")
|
|
231
|
+
print()
|
|
232
|
+
|
|
233
|
+
print(f"\n{'=' * 60}")
|
|
234
|
+
print(f"SUMMARY: {len(findings)} hardcoded strings in {len(by_file)} files")
|
|
235
|
+
print(f"{'=' * 60}")
|
|
236
|
+
print("\nTo fix:")
|
|
237
|
+
print(" 1. Import useTranslations: import { useTranslations } from 'next-intl'")
|
|
238
|
+
print(" 2. Add translations to core/messages/{locale}/ or theme messages")
|
|
239
|
+
print(" 3. Replace hardcoded text with t('key')")
|
|
240
|
+
print()
|
|
241
|
+
|
|
242
|
+
return 0
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
if __name__ == '__main__':
|
|
246
|
+
sys.exit(main())
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Validate Translations Script
|
|
4
|
+
|
|
5
|
+
Compares translation files between locales to find missing keys.
|
|
6
|
+
Validates EN and ES translation completeness.
|
|
7
|
+
|
|
8
|
+
Usage:
|
|
9
|
+
python validate-translations.py [--theme THEME] [--strict]
|
|
10
|
+
|
|
11
|
+
Options:
|
|
12
|
+
--theme THEME Theme to validate (default: from NEXT_PUBLIC_ACTIVE_THEME or 'default')
|
|
13
|
+
--strict Exit with error if missing keys found
|
|
14
|
+
--core-only Only validate core messages
|
|
15
|
+
--theme-only Only validate theme messages
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
import os
|
|
19
|
+
import sys
|
|
20
|
+
import json
|
|
21
|
+
import argparse
|
|
22
|
+
from pathlib import Path
|
|
23
|
+
from typing import Dict, List, Set, Tuple
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def get_active_theme() -> str:
|
|
27
|
+
"""Get active theme from environment or default."""
|
|
28
|
+
return os.environ.get('NEXT_PUBLIC_ACTIVE_THEME', 'default')
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def flatten_keys(obj: dict, prefix: str = '') -> Set[str]:
|
|
32
|
+
"""Flatten nested dict to set of dot-notation keys."""
|
|
33
|
+
keys = set()
|
|
34
|
+
for key, value in obj.items():
|
|
35
|
+
full_key = f"{prefix}.{key}" if prefix else key
|
|
36
|
+
if isinstance(value, dict):
|
|
37
|
+
keys.update(flatten_keys(value, full_key))
|
|
38
|
+
else:
|
|
39
|
+
keys.add(full_key)
|
|
40
|
+
return keys
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def load_json_file(file_path: Path) -> dict:
|
|
44
|
+
"""Load JSON file and return dict."""
|
|
45
|
+
try:
|
|
46
|
+
with open(file_path, 'r', encoding='utf-8') as f:
|
|
47
|
+
return json.load(f)
|
|
48
|
+
except FileNotFoundError:
|
|
49
|
+
return {}
|
|
50
|
+
except json.JSONDecodeError as e:
|
|
51
|
+
print(f" Warning: Invalid JSON in {file_path}: {e}")
|
|
52
|
+
return {}
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def load_core_messages(locale: str) -> dict:
|
|
56
|
+
"""Load core messages for a locale by combining all JSON files."""
|
|
57
|
+
core_path = Path(f'core/messages/{locale}')
|
|
58
|
+
if not core_path.exists():
|
|
59
|
+
return {}
|
|
60
|
+
|
|
61
|
+
combined = {}
|
|
62
|
+
for json_file in core_path.glob('*.json'):
|
|
63
|
+
data = load_json_file(json_file)
|
|
64
|
+
# Use filename (without extension) as namespace
|
|
65
|
+
namespace = json_file.stem
|
|
66
|
+
if namespace == 'index':
|
|
67
|
+
continue
|
|
68
|
+
combined[namespace] = data
|
|
69
|
+
|
|
70
|
+
return combined
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def load_theme_messages(theme: str, locale: str) -> dict:
|
|
74
|
+
"""Load theme messages for a locale."""
|
|
75
|
+
theme_path = Path(f'contents/themes/{theme}/messages/{locale}.json')
|
|
76
|
+
return load_json_file(theme_path)
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def compare_translations(
|
|
80
|
+
en_keys: Set[str],
|
|
81
|
+
es_keys: Set[str],
|
|
82
|
+
source_name: str
|
|
83
|
+
) -> Tuple[Set[str], Set[str]]:
|
|
84
|
+
"""Compare EN and ES keys, return missing in each."""
|
|
85
|
+
missing_in_es = en_keys - es_keys
|
|
86
|
+
missing_in_en = es_keys - en_keys
|
|
87
|
+
return missing_in_es, missing_in_en
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def validate_core_messages() -> Tuple[int, int]:
|
|
91
|
+
"""Validate core messages between EN and ES."""
|
|
92
|
+
print("\n" + "=" * 60)
|
|
93
|
+
print("VALIDATING CORE MESSAGES")
|
|
94
|
+
print("=" * 60)
|
|
95
|
+
|
|
96
|
+
en_messages = load_core_messages('en')
|
|
97
|
+
es_messages = load_core_messages('es')
|
|
98
|
+
|
|
99
|
+
if not en_messages:
|
|
100
|
+
print(" Warning: No English core messages found at core/messages/en/")
|
|
101
|
+
return 0, 0
|
|
102
|
+
|
|
103
|
+
en_keys = flatten_keys(en_messages)
|
|
104
|
+
es_keys = flatten_keys(es_messages)
|
|
105
|
+
|
|
106
|
+
print(f" EN keys: {len(en_keys)}")
|
|
107
|
+
print(f" ES keys: {len(es_keys)}")
|
|
108
|
+
|
|
109
|
+
missing_in_es, missing_in_en = compare_translations(en_keys, es_keys, "core")
|
|
110
|
+
|
|
111
|
+
total_missing = len(missing_in_es) + len(missing_in_en)
|
|
112
|
+
|
|
113
|
+
if missing_in_es:
|
|
114
|
+
print(f"\n Missing in ES ({len(missing_in_es)}):")
|
|
115
|
+
for key in sorted(missing_in_es)[:20]:
|
|
116
|
+
print(f" - {key}")
|
|
117
|
+
if len(missing_in_es) > 20:
|
|
118
|
+
print(f" ... and {len(missing_in_es) - 20} more")
|
|
119
|
+
|
|
120
|
+
if missing_in_en:
|
|
121
|
+
print(f"\n Extra in ES (not in EN) ({len(missing_in_en)}):")
|
|
122
|
+
for key in sorted(missing_in_en)[:10]:
|
|
123
|
+
print(f" - {key}")
|
|
124
|
+
if len(missing_in_en) > 10:
|
|
125
|
+
print(f" ... and {len(missing_in_en) - 10} more")
|
|
126
|
+
|
|
127
|
+
if total_missing == 0:
|
|
128
|
+
print("\n Core messages: COMPLETE")
|
|
129
|
+
|
|
130
|
+
return len(missing_in_es), len(missing_in_en)
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def validate_theme_messages(theme: str) -> Tuple[int, int]:
|
|
134
|
+
"""Validate theme messages between EN and ES."""
|
|
135
|
+
print("\n" + "=" * 60)
|
|
136
|
+
print(f"VALIDATING THEME MESSAGES: {theme}")
|
|
137
|
+
print("=" * 60)
|
|
138
|
+
|
|
139
|
+
en_messages = load_theme_messages(theme, 'en')
|
|
140
|
+
es_messages = load_theme_messages(theme, 'es')
|
|
141
|
+
|
|
142
|
+
if not en_messages:
|
|
143
|
+
print(f" Warning: No English theme messages found for theme '{theme}'")
|
|
144
|
+
return 0, 0
|
|
145
|
+
|
|
146
|
+
en_keys = flatten_keys(en_messages)
|
|
147
|
+
es_keys = flatten_keys(es_messages)
|
|
148
|
+
|
|
149
|
+
print(f" EN keys: {len(en_keys)}")
|
|
150
|
+
print(f" ES keys: {len(es_keys)}")
|
|
151
|
+
|
|
152
|
+
missing_in_es, missing_in_en = compare_translations(en_keys, es_keys, f"theme-{theme}")
|
|
153
|
+
|
|
154
|
+
total_missing = len(missing_in_es) + len(missing_in_en)
|
|
155
|
+
|
|
156
|
+
if missing_in_es:
|
|
157
|
+
print(f"\n Missing in ES ({len(missing_in_es)}):")
|
|
158
|
+
for key in sorted(missing_in_es)[:20]:
|
|
159
|
+
print(f" - {key}")
|
|
160
|
+
if len(missing_in_es) > 20:
|
|
161
|
+
print(f" ... and {len(missing_in_es) - 20} more")
|
|
162
|
+
|
|
163
|
+
if missing_in_en:
|
|
164
|
+
print(f"\n Extra in ES (not in EN) ({len(missing_in_en)}):")
|
|
165
|
+
for key in sorted(missing_in_en)[:10]:
|
|
166
|
+
print(f" - {key}")
|
|
167
|
+
if len(missing_in_en) > 10:
|
|
168
|
+
print(f" ... and {len(missing_in_en) - 10} more")
|
|
169
|
+
|
|
170
|
+
if total_missing == 0:
|
|
171
|
+
print("\n Theme messages: COMPLETE")
|
|
172
|
+
|
|
173
|
+
return len(missing_in_es), len(missing_in_en)
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
def check_custom_roles_in_core() -> List[str]:
|
|
177
|
+
"""Check if custom roles are incorrectly defined in core messages."""
|
|
178
|
+
print("\n" + "=" * 60)
|
|
179
|
+
print("CHECKING CUSTOM ROLES POLICY")
|
|
180
|
+
print("=" * 60)
|
|
181
|
+
|
|
182
|
+
core_en = load_core_messages('en')
|
|
183
|
+
violations = []
|
|
184
|
+
|
|
185
|
+
# Custom roles that should NEVER be in core
|
|
186
|
+
forbidden_roles = ['editor', 'moderator', 'contributor', 'reviewer', 'publisher']
|
|
187
|
+
|
|
188
|
+
# Check teams.roles namespace
|
|
189
|
+
teams_roles = core_en.get('teams', {}).get('roles', {})
|
|
190
|
+
for role in forbidden_roles:
|
|
191
|
+
if role in teams_roles:
|
|
192
|
+
violations.append(f"teams.roles.{role}")
|
|
193
|
+
|
|
194
|
+
if violations:
|
|
195
|
+
print(f"\n VIOLATION: Custom roles found in core/messages/")
|
|
196
|
+
print(f" These should be in theme messages only:")
|
|
197
|
+
for v in violations:
|
|
198
|
+
print(f" - {v}")
|
|
199
|
+
else:
|
|
200
|
+
print("\n Custom roles policy: OK")
|
|
201
|
+
|
|
202
|
+
return violations
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
def main():
|
|
206
|
+
parser = argparse.ArgumentParser(description='Validate translations')
|
|
207
|
+
parser.add_argument('--theme', default=None, help='Theme to validate')
|
|
208
|
+
parser.add_argument('--strict', action='store_true', help='Exit with error if issues found')
|
|
209
|
+
parser.add_argument('--core-only', action='store_true', help='Only validate core messages')
|
|
210
|
+
parser.add_argument('--theme-only', action='store_true', help='Only validate theme messages')
|
|
211
|
+
|
|
212
|
+
args = parser.parse_args()
|
|
213
|
+
|
|
214
|
+
theme = args.theme or get_active_theme()
|
|
215
|
+
|
|
216
|
+
print(f"\n{'=' * 60}")
|
|
217
|
+
print(f"TRANSLATION VALIDATION")
|
|
218
|
+
print(f"{'=' * 60}")
|
|
219
|
+
print(f"Theme: {theme}")
|
|
220
|
+
print(f"Strict mode: {args.strict}")
|
|
221
|
+
print(f"{'=' * 60}")
|
|
222
|
+
|
|
223
|
+
total_issues = 0
|
|
224
|
+
|
|
225
|
+
# Validate core messages
|
|
226
|
+
if not args.theme_only:
|
|
227
|
+
missing_es, extra_es = validate_core_messages()
|
|
228
|
+
total_issues += missing_es
|
|
229
|
+
|
|
230
|
+
# Check custom roles policy
|
|
231
|
+
role_violations = check_custom_roles_in_core()
|
|
232
|
+
total_issues += len(role_violations)
|
|
233
|
+
|
|
234
|
+
# Validate theme messages
|
|
235
|
+
if not args.core_only:
|
|
236
|
+
missing_es, extra_es = validate_theme_messages(theme)
|
|
237
|
+
total_issues += missing_es
|
|
238
|
+
|
|
239
|
+
# Summary
|
|
240
|
+
print("\n" + "=" * 60)
|
|
241
|
+
print("SUMMARY")
|
|
242
|
+
print("=" * 60)
|
|
243
|
+
|
|
244
|
+
if total_issues == 0:
|
|
245
|
+
print(" All translations are complete!")
|
|
246
|
+
print("=" * 60 + "\n")
|
|
247
|
+
return 0
|
|
248
|
+
else:
|
|
249
|
+
print(f" Total issues found: {total_issues}")
|
|
250
|
+
print("=" * 60 + "\n")
|
|
251
|
+
|
|
252
|
+
if args.strict:
|
|
253
|
+
print("Strict mode: Exiting with error due to missing translations")
|
|
254
|
+
return 1
|
|
255
|
+
|
|
256
|
+
return 0
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
if __name__ == '__main__':
|
|
260
|
+
sys.exit(main())
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: impact-analysis
|
|
3
|
+
description: |
|
|
4
|
+
Impact analysis for understanding how code changes affect the system.
|
|
5
|
+
Covers git diff analysis, entity mapping, flow identification, and regression suggestions.
|
|
6
|
+
Use this skill when analyzing branches, planning testing, or understanding change scope.
|
|
7
|
+
allowed-tools: Read, Glob, Grep, Bash(git:*)
|
|
8
|
+
version: 1.0.0
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# Impact Analysis Skill
|
|
12
|
+
|
|
13
|
+
Analyze the potential impact of code changes on the system.
|
|
14
|
+
|
|
15
|
+
## Overview
|
|
16
|
+
|
|
17
|
+
Impact analysis identifies what parts of the system may be affected by a code change. Think of it like a **domino effect** - pushing one domino (making a change) may cause others to fall.
|
|
18
|
+
|
|
19
|
+
```
|
|
20
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
21
|
+
│ 1. Git Diff │
|
|
22
|
+
│ ↓ │
|
|
23
|
+
│ Gets list of modified files │
|
|
24
|
+
│ │
|
|
25
|
+
│ 2. Classification │
|
|
26
|
+
│ ↓ │
|
|
27
|
+
│ Groups by type: API, UI, DB, Tests │
|
|
28
|
+
│ │
|
|
29
|
+
│ 3. Entity Mapping │
|
|
30
|
+
│ ↓ │
|
|
31
|
+
│ Relates files to system entities │
|
|
32
|
+
│ │
|
|
33
|
+
│ 4. Flow Lookup │
|
|
34
|
+
│ ↓ │
|
|
35
|
+
│ Queries registries for related flows │
|
|
36
|
+
│ │
|
|
37
|
+
│ 5. Suggestions │
|
|
38
|
+
│ ↓ │
|
|
39
|
+
│ Generates list of potentially affected areas │
|
|
40
|
+
└─────────────────────────────────────────────────────────────┘
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## When to Use This Skill
|
|
44
|
+
|
|
45
|
+
- Analyzing a feature branch before merge
|
|
46
|
+
- Planning regression testing scope
|
|
47
|
+
- Understanding the blast radius of a change
|
|
48
|
+
- Identifying related tests to run
|
|
49
|
+
- Code review impact assessment
|
|
50
|
+
|
|
51
|
+
## Analysis Capabilities
|
|
52
|
+
|
|
53
|
+
### What It CAN Identify
|
|
54
|
+
|
|
55
|
+
| Capability | Example |
|
|
56
|
+
|------------|---------|
|
|
57
|
+
| Modified files | "5 files changed in /products" |
|
|
58
|
+
| Direct dependencies | "Orders depends on Products" |
|
|
59
|
+
| Existing tests | "3 tests cover this area" |
|
|
60
|
+
| Documented flows | "Checkout flow uses this component" |
|
|
61
|
+
| Entity relationships | "FK relationship to customers" |
|
|
62
|
+
|
|
63
|
+
### What It CANNOT Identify
|
|
64
|
+
|
|
65
|
+
| Limitation | Reason |
|
|
66
|
+
|------------|--------|
|
|
67
|
+
| Business impact | Doesn't know business rules |
|
|
68
|
+
| Real severity | Cannot prioritize by criticality |
|
|
69
|
+
| Undocumented dependencies | Only sees what's in code |
|
|
70
|
+
| Complex side effects | Doesn't execute the code |
|
|
71
|
+
| Runtime behavior | Static analysis only |
|
|
72
|
+
|
|
73
|
+
## Output Format
|
|
74
|
+
|
|
75
|
+
```markdown
|
|
76
|
+
## Impact Analysis: feature/new-pricing
|
|
77
|
+
|
|
78
|
+
### Files Modified (23 files)
|
|
79
|
+
| Type | Count | Key Files |
|
|
80
|
+
|------|-------|-----------|
|
|
81
|
+
| API | 5 | orders/service.ts, payments/route.ts |
|
|
82
|
+
| UI | 12 | CheckoutForm.tsx, OrderSummary.tsx |
|
|
83
|
+
| DB | 2 | 001_add_payment_status.sql |
|
|
84
|
+
| Tests | 4 | checkout.cy.ts |
|
|
85
|
+
|
|
86
|
+
### Entities Affected
|
|
87
|
+
- **products**: API and UI modifications
|
|
88
|
+
- **orders**: Potentially affected (FK relationship)
|
|
89
|
+
- **payments**: New functionality
|
|
90
|
+
|
|
91
|
+
### Flows Potentially Impacted
|
|
92
|
+
- Complete checkout flow
|
|
93
|
+
- Order confirmation flow
|
|
94
|
+
- Payment processing flow
|
|
95
|
+
|
|
96
|
+
### Regression Suggestions
|
|
97
|
+
| Confidence | Area | Reason |
|
|
98
|
+
|------------|------|--------|
|
|
99
|
+
| High | Product CRUD | Direct changes |
|
|
100
|
+
| Medium | Order totals | Uses pricing |
|
|
101
|
+
| Low | Sales reports | May reference products |
|
|
102
|
+
|
|
103
|
+
### Related Tests
|
|
104
|
+
- checkout.cy.ts (@uat, @flow-checkout)
|
|
105
|
+
- orders.cy.ts (@api, @entity-orders)
|
|
106
|
+
- payments.cy.ts (@api)
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## Confidence Levels
|
|
110
|
+
|
|
111
|
+
| Level | Meaning | Recommendation |
|
|
112
|
+
|-------|---------|----------------|
|
|
113
|
+
| **High** | Direct dependency found | Test this area |
|
|
114
|
+
| **Medium** | Indirect dependency | Consider testing |
|
|
115
|
+
| **Low** | Possible relationship | Evaluate with context |
|
|
116
|
+
|
|
117
|
+
## Interpreting Results
|
|
118
|
+
|
|
119
|
+
### Correct Interpretation
|
|
120
|
+
|
|
121
|
+
- "Analysis suggests pricing is directly affected"
|
|
122
|
+
- "Orders might be affected because it uses prices"
|
|
123
|
+
- "Should verify with dev team about reports impact"
|
|
124
|
+
|
|
125
|
+
### Incorrect Interpretation
|
|
126
|
+
|
|
127
|
+
- "Analysis says I MUST test everything listed"
|
|
128
|
+
- "If not in the list, there's no risk"
|
|
129
|
+
- "This is the complete list of affected areas"
|
|
130
|
+
|
|
131
|
+
## Complementing the Analysis
|
|
132
|
+
|
|
133
|
+
Technical analysis should be complemented with:
|
|
134
|
+
|
|
135
|
+
### 1. Business Knowledge
|
|
136
|
+
|
|
137
|
+
- What areas are critical for the business?
|
|
138
|
+
- Are there special seasons or events?
|
|
139
|
+
- Which customers use these features?
|
|
140
|
+
|
|
141
|
+
### 2. Bug History
|
|
142
|
+
|
|
143
|
+
- Have there been bugs in these areas before?
|
|
144
|
+
- Are there known "fragile" areas?
|
|
145
|
+
|
|
146
|
+
### 3. Developer Conversation
|
|
147
|
+
|
|
148
|
+
- What exactly changed?
|
|
149
|
+
- Are there impacts the code doesn't show?
|
|
150
|
+
- Are there affected external dependencies?
|
|
151
|
+
|
|
152
|
+
## Usage Examples
|
|
153
|
+
|
|
154
|
+
### Analyze a Branch
|
|
155
|
+
|
|
156
|
+
```bash
|
|
157
|
+
# Get diff from main
|
|
158
|
+
git diff main...feature/checkout-v2 --name-only
|
|
159
|
+
|
|
160
|
+
# Classify files
|
|
161
|
+
# API: src/app/api/**
|
|
162
|
+
# UI: src/components/**, src/app/(routes)/**
|
|
163
|
+
# DB: migrations/**
|
|
164
|
+
# Tests: cypress/**, __tests__/**
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
### Map to Entities
|
|
168
|
+
|
|
169
|
+
```typescript
|
|
170
|
+
// File path patterns to entities
|
|
171
|
+
const entityPatterns = {
|
|
172
|
+
'entities/products': 'products',
|
|
173
|
+
'entities/orders': 'orders',
|
|
174
|
+
'api/v1/products': 'products',
|
|
175
|
+
'components/Product': 'products',
|
|
176
|
+
}
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
### Lookup Related Flows
|
|
180
|
+
|
|
181
|
+
```typescript
|
|
182
|
+
// Query FLOW_REGISTRY for flows using affected entities
|
|
183
|
+
const affectedFlows = Object.entries(FLOW_REGISTRY)
|
|
184
|
+
.filter(([_, flow]) =>
|
|
185
|
+
flow.entities.some(e => affectedEntities.includes(e))
|
|
186
|
+
)
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
## Commands
|
|
190
|
+
|
|
191
|
+
| Command | Description |
|
|
192
|
+
|---------|-------------|
|
|
193
|
+
| `/impact:analyze [branch]` | Analyze branch impact |
|
|
194
|
+
| `/impact:regression [feature]` | Identify regression areas |
|
|
195
|
+
|
|
196
|
+
## Important Notes
|
|
197
|
+
|
|
198
|
+
1. **This is a starting point** - not a definitive list
|
|
199
|
+
2. **Complements human judgment** - doesn't replace it
|
|
200
|
+
3. **Static analysis only** - cannot predict runtime behavior
|
|
201
|
+
4. **Business context required** - you decide what's critical
|
|
202
|
+
|
|
203
|
+
The analysis is an **assistance tool**. YOU decide what to test and with what priority.
|