@codyswann/lisa 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/LICENSE +21 -0
- package/README.md +867 -0
- package/all/copy-overwrite/.claude/README.md +205 -0
- package/all/copy-overwrite/.claude/agents/agent-architect.md +311 -0
- package/all/copy-overwrite/.claude/agents/codebase-analyzer.md +146 -0
- package/all/copy-overwrite/.claude/agents/codebase-locator.md +125 -0
- package/all/copy-overwrite/.claude/agents/codebase-pattern-finder.md +237 -0
- package/all/copy-overwrite/.claude/agents/git-history-analyzer.md +183 -0
- package/all/copy-overwrite/.claude/agents/hooks-expert.md +74 -0
- package/all/copy-overwrite/.claude/agents/skill-evaluator.md +246 -0
- package/all/copy-overwrite/.claude/agents/slash-command-architect.md +87 -0
- package/all/copy-overwrite/.claude/agents/web-search-researcher.md +112 -0
- package/all/copy-overwrite/.claude/commands/git/commit-and-submit-pr.md +8 -0
- package/all/copy-overwrite/.claude/commands/git/commit.md +44 -0
- package/all/copy-overwrite/.claude/commands/git/prune.md +34 -0
- package/all/copy-overwrite/.claude/commands/git/submit-pr.md +50 -0
- package/all/copy-overwrite/.claude/commands/jira/create.md +50 -0
- package/all/copy-overwrite/.claude/commands/jira/verify.md +34 -0
- package/all/copy-overwrite/.claude/commands/project/archive.md +8 -0
- package/all/copy-overwrite/.claude/commands/project/bootstrap.md +49 -0
- package/all/copy-overwrite/.claude/commands/project/complete-task.md +7 -0
- package/all/copy-overwrite/.claude/commands/project/debrief.md +65 -0
- package/all/copy-overwrite/.claude/commands/project/execute.md +94 -0
- package/all/copy-overwrite/.claude/commands/project/implement.md +42 -0
- package/all/copy-overwrite/.claude/commands/project/local-code-review.md +88 -0
- package/all/copy-overwrite/.claude/commands/project/lower-code-complexity.md +74 -0
- package/all/copy-overwrite/.claude/commands/project/plan.md +314 -0
- package/all/copy-overwrite/.claude/commands/project/research.md +248 -0
- package/all/copy-overwrite/.claude/commands/project/review.md +63 -0
- package/all/copy-overwrite/.claude/commands/project/setup.md +19 -0
- package/all/copy-overwrite/.claude/commands/project/verify.md +38 -0
- package/all/copy-overwrite/.claude/commands/pull-request/review.md +12 -0
- package/all/copy-overwrite/.claude/commands/rules/format-md.md +72 -0
- package/all/copy-overwrite/.claude/commands/sonarqube/check.md +6 -0
- package/all/copy-overwrite/.claude/commands/sonarqube/fix.md +3 -0
- package/all/copy-overwrite/.claude/hooks/README.md +301 -0
- package/all/copy-overwrite/.claude/hooks/notify-ntfy.sh +181 -0
- package/all/copy-overwrite/.claude/settings.json +41 -0
- package/all/copy-overwrite/.claude/settings.local.json.example +14 -0
- package/all/copy-overwrite/.claude/skills/coding-philosophy/SKILL.md +405 -0
- package/all/copy-overwrite/.claude/skills/coding-philosophy/references/function-structure.md +416 -0
- package/all/copy-overwrite/.claude/skills/coding-philosophy/references/immutable-patterns.md +316 -0
- package/all/copy-overwrite/.claude/skills/prompt-complexity-scorer/SKILL.md +118 -0
- package/all/copy-overwrite/.claude/skills/skill-creator/LICENSE.txt +202 -0
- package/all/copy-overwrite/.claude/skills/skill-creator/SKILL.md +210 -0
- package/all/copy-overwrite/.claude/skills/skill-creator/scripts/__pycache__/quick_validate.cpython-312.pyc +0 -0
- package/all/copy-overwrite/.claude/skills/skill-creator/scripts/init_skill.py +303 -0
- package/all/copy-overwrite/.claude/skills/skill-creator/scripts/package_skill.py +110 -0
- package/all/copy-overwrite/.claude/skills/skill-creator/scripts/quick_validate.py +65 -0
- package/all/copy-overwrite/CLAUDE.md +77 -0
- package/all/copy-overwrite/HUMAN.md +17 -0
- package/all/copy-overwrite/specs/.keep +0 -0
- package/all/create-only/PROJECT_RULES.md +0 -0
- package/cdk/merge/package.json +20 -0
- package/dist/cli/index.d.ts +7 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +107 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/prompts.d.ts +45 -0
- package/dist/cli/prompts.d.ts.map +1 -0
- package/dist/cli/prompts.js +58 -0
- package/dist/cli/prompts.js.map +1 -0
- package/dist/core/config.d.ts +73 -0
- package/dist/core/config.d.ts.map +1 -0
- package/dist/core/config.js +36 -0
- package/dist/core/config.js.map +1 -0
- package/dist/core/index.d.ts +4 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +4 -0
- package/dist/core/index.js.map +1 -0
- package/dist/core/lisa.d.ts +81 -0
- package/dist/core/lisa.d.ts.map +1 -0
- package/dist/core/lisa.js +459 -0
- package/dist/core/lisa.js.map +1 -0
- package/dist/core/manifest.d.ts +58 -0
- package/dist/core/manifest.d.ts.map +1 -0
- package/dist/core/manifest.js +104 -0
- package/dist/core/manifest.js.map +1 -0
- package/dist/detection/detector.interface.d.ts +15 -0
- package/dist/detection/detector.interface.d.ts.map +1 -0
- package/dist/detection/detector.interface.js +2 -0
- package/dist/detection/detector.interface.js.map +1 -0
- package/dist/detection/detectors/cdk.d.ts +10 -0
- package/dist/detection/detectors/cdk.d.ts.map +1 -0
- package/dist/detection/detectors/cdk.js +34 -0
- package/dist/detection/detectors/cdk.js.map +1 -0
- package/dist/detection/detectors/expo.d.ts +10 -0
- package/dist/detection/detectors/expo.d.ts.map +1 -0
- package/dist/detection/detectors/expo.js +30 -0
- package/dist/detection/detectors/expo.js.map +1 -0
- package/dist/detection/detectors/nestjs.d.ts +10 -0
- package/dist/detection/detectors/nestjs.d.ts.map +1 -0
- package/dist/detection/detectors/nestjs.js +34 -0
- package/dist/detection/detectors/nestjs.js.map +1 -0
- package/dist/detection/detectors/npm-package.d.ts +13 -0
- package/dist/detection/detectors/npm-package.d.ts.map +1 -0
- package/dist/detection/detectors/npm-package.js +30 -0
- package/dist/detection/detectors/npm-package.js.map +1 -0
- package/dist/detection/detectors/typescript.d.ts +10 -0
- package/dist/detection/detectors/typescript.d.ts.map +1 -0
- package/dist/detection/detectors/typescript.js +25 -0
- package/dist/detection/detectors/typescript.js.map +1 -0
- package/dist/detection/index.d.ts +24 -0
- package/dist/detection/index.d.ts.map +1 -0
- package/dist/detection/index.js +57 -0
- package/dist/detection/index.js.map +1 -0
- package/dist/errors/index.d.ts +69 -0
- package/dist/errors/index.d.ts.map +1 -0
- package/dist/errors/index.js +110 -0
- package/dist/errors/index.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -0
- package/dist/logging/console-logger.d.ts +12 -0
- package/dist/logging/console-logger.d.ts.map +1 -0
- package/dist/logging/console-logger.js +22 -0
- package/dist/logging/console-logger.js.map +1 -0
- package/dist/logging/index.d.ts +4 -0
- package/dist/logging/index.d.ts.map +1 -0
- package/dist/logging/index.js +3 -0
- package/dist/logging/index.js.map +1 -0
- package/dist/logging/logger.interface.d.ts +20 -0
- package/dist/logging/logger.interface.d.ts.map +1 -0
- package/dist/logging/logger.interface.js +2 -0
- package/dist/logging/logger.interface.js.map +1 -0
- package/dist/logging/silent-logger.d.ts +12 -0
- package/dist/logging/silent-logger.d.ts.map +1 -0
- package/dist/logging/silent-logger.js +21 -0
- package/dist/logging/silent-logger.js.map +1 -0
- package/dist/strategies/copy-contents.d.ts +14 -0
- package/dist/strategies/copy-contents.d.ts.map +1 -0
- package/dist/strategies/copy-contents.js +69 -0
- package/dist/strategies/copy-contents.js.map +1 -0
- package/dist/strategies/copy-overwrite.d.ts +14 -0
- package/dist/strategies/copy-overwrite.d.ts.map +1 -0
- package/dist/strategies/copy-overwrite.js +47 -0
- package/dist/strategies/copy-overwrite.js.map +1 -0
- package/dist/strategies/create-only.d.ts +13 -0
- package/dist/strategies/create-only.d.ts.map +1 -0
- package/dist/strategies/create-only.js +30 -0
- package/dist/strategies/create-only.js.map +1 -0
- package/dist/strategies/index.d.ts +31 -0
- package/dist/strategies/index.d.ts.map +1 -0
- package/dist/strategies/index.js +52 -0
- package/dist/strategies/index.js.map +1 -0
- package/dist/strategies/merge.d.ts +13 -0
- package/dist/strategies/merge.d.ts.map +1 -0
- package/dist/strategies/merge.js +60 -0
- package/dist/strategies/merge.js.map +1 -0
- package/dist/strategies/strategy.interface.d.ts +31 -0
- package/dist/strategies/strategy.interface.d.ts.map +1 -0
- package/dist/strategies/strategy.interface.js +2 -0
- package/dist/strategies/strategy.interface.js.map +1 -0
- package/dist/transaction/backup.d.ts +38 -0
- package/dist/transaction/backup.d.ts.map +1 -0
- package/dist/transaction/backup.js +97 -0
- package/dist/transaction/backup.js.map +1 -0
- package/dist/transaction/index.d.ts +4 -0
- package/dist/transaction/index.d.ts.map +1 -0
- package/dist/transaction/index.js +3 -0
- package/dist/transaction/index.js.map +1 -0
- package/dist/transaction/transaction.d.ts +34 -0
- package/dist/transaction/transaction.d.ts.map +1 -0
- package/dist/transaction/transaction.js +68 -0
- package/dist/transaction/transaction.js.map +1 -0
- package/dist/utils/file-operations.d.ts +29 -0
- package/dist/utils/file-operations.d.ts.map +1 -0
- package/dist/utils/file-operations.js +84 -0
- package/dist/utils/file-operations.js.map +1 -0
- package/dist/utils/index.d.ts +4 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +4 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/json-utils.d.ts +22 -0
- package/dist/utils/json-utils.d.ts.map +1 -0
- package/dist/utils/json-utils.js +57 -0
- package/dist/utils/json-utils.js.map +1 -0
- package/dist/utils/path-utils.d.ts +21 -0
- package/dist/utils/path-utils.d.ts.map +1 -0
- package/dist/utils/path-utils.js +35 -0
- package/dist/utils/path-utils.js.map +1 -0
- package/eslint-plugin-code-organization/README.md +149 -0
- package/eslint-plugin-code-organization/__tests__/enforce-statement-order.test.js +468 -0
- package/eslint-plugin-code-organization/index.js +23 -0
- package/eslint-plugin-code-organization/package.json +10 -0
- package/eslint-plugin-code-organization/rules/enforce-statement-order.js +157 -0
- package/expo/copy-overwrite/.claude/skills/apollo-client/SKILL.md +238 -0
- package/expo/copy-overwrite/.claude/skills/apollo-client/references/mutation-patterns.md +360 -0
- package/expo/copy-overwrite/.claude/skills/atomic-design-gluestack/SKILL.md +360 -0
- package/expo/copy-overwrite/.claude/skills/atomic-design-gluestack/references/atomic-levels.md +417 -0
- package/expo/copy-overwrite/.claude/skills/atomic-design-gluestack/references/folder-structure.md +257 -0
- package/expo/copy-overwrite/.claude/skills/atomic-design-gluestack/references/gluestack-mapping.md +233 -0
- package/expo/copy-overwrite/.claude/skills/atomic-design-gluestack/scripts/validate_atomic_structure.py +327 -0
- package/expo/copy-overwrite/.claude/skills/container-view-pattern/SKILL.md +299 -0
- package/expo/copy-overwrite/.claude/skills/container-view-pattern/references/examples.md +749 -0
- package/expo/copy-overwrite/.claude/skills/container-view-pattern/references/patterns.md +318 -0
- package/expo/copy-overwrite/.claude/skills/container-view-pattern/scripts/create_component.py +198 -0
- package/expo/copy-overwrite/.claude/skills/container-view-pattern/scripts/validate_component.py +207 -0
- package/expo/copy-overwrite/.claude/skills/cross-platform-compatibility/SKILL.md +268 -0
- package/expo/copy-overwrite/.claude/skills/cross-platform-compatibility/references/common-issues.md +619 -0
- package/expo/copy-overwrite/.claude/skills/cross-platform-compatibility/references/file-extensions.md +340 -0
- package/expo/copy-overwrite/.claude/skills/cross-platform-compatibility/references/platform-api.md +276 -0
- package/expo/copy-overwrite/.claude/skills/cross-platform-compatibility/scripts/validate_cross_platform.py +414 -0
- package/expo/copy-overwrite/.claude/skills/directory-structure/SKILL.md +202 -0
- package/expo/copy-overwrite/.claude/skills/directory-structure/scripts/validate_structure.py +443 -0
- package/expo/copy-overwrite/.claude/skills/expo-env-config/SKILL.md +309 -0
- package/expo/copy-overwrite/.claude/skills/expo-env-config/references/validation-patterns.md +417 -0
- package/expo/copy-overwrite/.claude/skills/expo-router-best-practices/SKILL.md +431 -0
- package/expo/copy-overwrite/.claude/skills/expo-router-best-practices/references/official-docs.md +290 -0
- package/expo/copy-overwrite/.claude/skills/expo-router-best-practices/scripts/generate-route.py +169 -0
- package/expo/copy-overwrite/.claude/skills/gluestack-nativewind/SKILL.md +411 -0
- package/expo/copy-overwrite/.claude/skills/gluestack-nativewind/references/color-tokens.md +343 -0
- package/expo/copy-overwrite/.claude/skills/gluestack-nativewind/references/component-mapping.md +307 -0
- package/expo/copy-overwrite/.claude/skills/gluestack-nativewind/references/spacing-scale.md +300 -0
- package/expo/copy-overwrite/.claude/skills/gluestack-nativewind/scripts/validate_styling.py +354 -0
- package/expo/copy-overwrite/.claude/skills/local-state/SKILL.md +362 -0
- package/expo/copy-overwrite/.claude/skills/local-state/references/async-storage.md +505 -0
- package/expo/copy-overwrite/.claude/skills/local-state/references/persistence-patterns.md +711 -0
- package/expo/copy-overwrite/.claude/skills/local-state/references/reactive-variables.md +446 -0
- package/expo/copy-overwrite/.claude/skills/playwright-selectors/SKILL.md +223 -0
- package/expo/copy-overwrite/.claude/skills/testing-library/SKILL.md +319 -0
- package/expo/copy-overwrite/.claude/skills/testing-library/references/async-patterns.md +420 -0
- package/expo/copy-overwrite/.claude/skills/testing-library/references/expo-router-testing.md +556 -0
- package/expo/copy-overwrite/.claude/skills/testing-library/references/mocking-patterns.md +590 -0
- package/expo/copy-overwrite/.claude/skills/testing-library/references/query-priority.md +291 -0
- package/expo/copy-overwrite/.easignore.extra +2 -0
- package/expo/copy-overwrite/.mcp.json +33 -0
- package/expo/copy-overwrite/eslint-plugin-component-structure/README.md +234 -0
- package/expo/copy-overwrite/eslint-plugin-component-structure/__tests__/plugin-index.test.js +84 -0
- package/expo/copy-overwrite/eslint-plugin-component-structure/__tests__/require-memo-in-view.test.js +196 -0
- package/expo/copy-overwrite/eslint-plugin-component-structure/__tests__/single-component-per-file.test.js +289 -0
- package/expo/copy-overwrite/eslint-plugin-component-structure/index.js +32 -0
- package/expo/copy-overwrite/eslint-plugin-component-structure/package.json +10 -0
- package/expo/copy-overwrite/eslint-plugin-component-structure/rules/enforce-component-structure.js +230 -0
- package/expo/copy-overwrite/eslint-plugin-component-structure/rules/no-return-in-view.js +91 -0
- package/expo/copy-overwrite/eslint-plugin-component-structure/rules/require-memo-in-view.js +178 -0
- package/expo/copy-overwrite/eslint-plugin-component-structure/rules/single-component-per-file.js +238 -0
- package/expo/copy-overwrite/eslint-plugin-ui-standards/README.md +260 -0
- package/expo/copy-overwrite/eslint-plugin-ui-standards/index.js +29 -0
- package/expo/copy-overwrite/eslint-plugin-ui-standards/package.json +10 -0
- package/expo/copy-overwrite/eslint-plugin-ui-standards/rules/no-classname-outside-ui.js +51 -0
- package/expo/copy-overwrite/eslint-plugin-ui-standards/rules/no-direct-rn-imports.js +55 -0
- package/expo/copy-overwrite/eslint-plugin-ui-standards/rules/no-inline-styles.js +73 -0
- package/expo/copy-overwrite/eslint.config.mjs +560 -0
- package/expo/copy-overwrite/lighthouserc.js +194 -0
- package/expo/create-only/lighthouserc-config.json +28 -0
- package/expo/merge/package.json +132 -0
- package/lisa.sh +35 -0
- package/nestjs/copy-overwrite/.claude/skills/nestjs-graphql/SKILL.md +176 -0
- package/nestjs/copy-overwrite/.claude/skills/nestjs-graphql/references/advanced-features.md +527 -0
- package/nestjs/copy-overwrite/.claude/skills/nestjs-graphql/references/project-patterns.md +483 -0
- package/nestjs/copy-overwrite/.claude/skills/nestjs-graphql/references/quick-start.md +257 -0
- package/nestjs/copy-overwrite/.claude/skills/nestjs-graphql/references/resolvers-mutations.md +413 -0
- package/nestjs/copy-overwrite/.claude/skills/nestjs-graphql/references/types-scalars.md +513 -0
- package/nestjs/copy-overwrite/.claude/skills/nestjs-rules/SKILL.md +536 -0
- package/nestjs/copy-overwrite/.claude/skills/typeorm-patterns/SKILL.md +275 -0
- package/nestjs/copy-overwrite/.claude/skills/typeorm-patterns/references/configuration-patterns.md +487 -0
- package/nestjs/copy-overwrite/.claude/skills/typeorm-patterns/references/entity-patterns.md +450 -0
- package/nestjs/copy-overwrite/.claude/skills/typeorm-patterns/references/observability-patterns.md +536 -0
- package/nestjs/merge/package.json +75 -0
- package/package.json +124 -0
- package/typescript/copy-contents/.husky/commit-msg +91 -0
- package/typescript/copy-contents/.husky/pre-commit +96 -0
- package/typescript/copy-contents/.husky/pre-push +211 -0
- package/typescript/copy-overwrite/.claude/hooks/format-on-edit.sh +74 -0
- package/typescript/copy-overwrite/.claude/hooks/install_pkgs.sh +59 -0
- package/typescript/copy-overwrite/.claude/hooks/lint-on-edit.sh +103 -0
- package/typescript/copy-overwrite/.claude/skills/jsdoc-best-practices/SKILL.md +388 -0
- package/typescript/copy-overwrite/.github/README.md +455 -0
- package/typescript/copy-overwrite/.github/dependabot.yml +40 -0
- package/typescript/copy-overwrite/.github/k6/BROWSER_TESTING_NOTE.md +129 -0
- package/typescript/copy-overwrite/.github/k6/INTEGRATION_GUIDE.md +354 -0
- package/typescript/copy-overwrite/.github/k6/README.md +386 -0
- package/typescript/copy-overwrite/.github/k6/SCENARIO_SELECTION_GUIDE.md +264 -0
- package/typescript/copy-overwrite/.github/k6/examples/customer-deploy-integration.yml +115 -0
- package/typescript/copy-overwrite/.github/k6/examples/data-driven-test.js +268 -0
- package/typescript/copy-overwrite/.github/k6/scenarios/load.js +142 -0
- package/typescript/copy-overwrite/.github/k6/scenarios/load.json +27 -0
- package/typescript/copy-overwrite/.github/k6/scenarios/smoke.js +26 -0
- package/typescript/copy-overwrite/.github/k6/scenarios/smoke.json +20 -0
- package/typescript/copy-overwrite/.github/k6/scenarios/soak.js +244 -0
- package/typescript/copy-overwrite/.github/k6/scenarios/soak.json +29 -0
- package/typescript/copy-overwrite/.github/k6/scenarios/spike.js +180 -0
- package/typescript/copy-overwrite/.github/k6/scenarios/spike.json +32 -0
- package/typescript/copy-overwrite/.github/k6/scenarios/stress.js +206 -0
- package/typescript/copy-overwrite/.github/k6/scenarios/stress.json +38 -0
- package/typescript/copy-overwrite/.github/k6/scripts/api-test.js +452 -0
- package/typescript/copy-overwrite/.github/k6/scripts/default-test.js +185 -0
- package/typescript/copy-overwrite/.github/k6/thresholds/normal.json +30 -0
- package/typescript/copy-overwrite/.github/k6/thresholds/relaxed.json +21 -0
- package/typescript/copy-overwrite/.github/k6/thresholds/strict.json +29 -0
- package/typescript/copy-overwrite/.github/workflows/build.yml +72 -0
- package/typescript/copy-overwrite/.github/workflows/ci.yml +49 -0
- package/typescript/copy-overwrite/.github/workflows/claude.yml +51 -0
- package/typescript/copy-overwrite/.github/workflows/create-github-issue-on-failure.yml +113 -0
- package/typescript/copy-overwrite/.github/workflows/create-jira-issue-on-failure.yml +195 -0
- package/typescript/copy-overwrite/.github/workflows/create-sentry-issue-on-failure.yml +267 -0
- package/typescript/copy-overwrite/.github/workflows/deploy.yml +228 -0
- package/typescript/copy-overwrite/.github/workflows/k6-load-test-README.md +230 -0
- package/typescript/copy-overwrite/.github/workflows/lighthouse.yml +68 -0
- package/typescript/copy-overwrite/.github/workflows/load-test.yml +282 -0
- package/typescript/copy-overwrite/.github/workflows/quality.yml +1737 -0
- package/typescript/copy-overwrite/.github/workflows/release.yml +1599 -0
- package/typescript/copy-overwrite/.gitleaksignore +28 -0
- package/typescript/copy-overwrite/.nvmrc +1 -0
- package/typescript/copy-overwrite/.prettierignore +23 -0
- package/typescript/copy-overwrite/.prettierrc.json +22 -0
- package/typescript/copy-overwrite/.versionrc +42 -0
- package/typescript/copy-overwrite/.yamllint +20 -0
- package/typescript/copy-overwrite/commitlint.config.js +11 -0
- package/typescript/copy-overwrite/eslint-plugin-code-organization/README.md +149 -0
- package/typescript/copy-overwrite/eslint-plugin-code-organization/__tests__/enforce-statement-order.test.js +468 -0
- package/typescript/copy-overwrite/eslint-plugin-code-organization/index.js +23 -0
- package/typescript/copy-overwrite/eslint-plugin-code-organization/package.json +10 -0
- package/typescript/copy-overwrite/eslint-plugin-code-organization/rules/enforce-statement-order.js +157 -0
- package/typescript/copy-overwrite/eslint.config.mjs +390 -0
- package/typescript/copy-overwrite/eslint.ignore.config.json +57 -0
- package/typescript/copy-overwrite/eslint.thresholds.config.json +5 -0
- package/typescript/github-rulesets/base.json +106 -0
- package/typescript/merge/.claude/settings.json +28 -0
- package/typescript/merge/package.json +71 -0
|
@@ -0,0 +1,443 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Directory Structure Validation Script
|
|
4
|
+
|
|
5
|
+
Validates that files and directories follow the project's documented structure:
|
|
6
|
+
- Feature module structure (components/, screens/, hooks/, etc.)
|
|
7
|
+
- Container/View pattern compliance
|
|
8
|
+
- Test file placement in __tests__/ directories
|
|
9
|
+
- Route file thin wrapper pattern
|
|
10
|
+
- Proper naming conventions
|
|
11
|
+
|
|
12
|
+
Usage:
|
|
13
|
+
python3 validate_structure.py [path]
|
|
14
|
+
|
|
15
|
+
If no path provided, validates from current directory.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
import os
|
|
19
|
+
import re
|
|
20
|
+
import sys
|
|
21
|
+
from pathlib import Path
|
|
22
|
+
from typing import List, Tuple, NamedTuple
|
|
23
|
+
from dataclasses import dataclass, field
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@dataclass
|
|
27
|
+
class ValidationResult:
|
|
28
|
+
"""Result of a validation check."""
|
|
29
|
+
passed: bool
|
|
30
|
+
message: str
|
|
31
|
+
file_path: str = ""
|
|
32
|
+
suggestion: str = ""
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@dataclass
|
|
36
|
+
class ValidationReport:
|
|
37
|
+
"""Complete validation report."""
|
|
38
|
+
errors: List[ValidationResult] = field(default_factory=list)
|
|
39
|
+
warnings: List[ValidationResult] = field(default_factory=list)
|
|
40
|
+
passed: List[ValidationResult] = field(default_factory=list)
|
|
41
|
+
|
|
42
|
+
@property
|
|
43
|
+
def has_errors(self) -> bool:
|
|
44
|
+
return len(self.errors) > 0
|
|
45
|
+
|
|
46
|
+
@property
|
|
47
|
+
def has_warnings(self) -> bool:
|
|
48
|
+
return len(self.warnings) > 0
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
# Directories that should contain __tests__/ subdirectories for test files
|
|
52
|
+
TESTABLE_DIRS = {'hooks', 'utils', 'stores', 'providers'}
|
|
53
|
+
|
|
54
|
+
# Valid feature subdirectories
|
|
55
|
+
FEATURE_SUBDIRS = {'components', 'screens', 'hooks', 'stores', 'utils', 'types', 'constants', 'config'}
|
|
56
|
+
|
|
57
|
+
# Files that can exist at feature root level
|
|
58
|
+
FEATURE_ROOT_FILES = {'types.ts', 'constants.ts', 'operations.graphql', 'index.ts', 'index.tsx'}
|
|
59
|
+
|
|
60
|
+
# Pattern for Container/View files
|
|
61
|
+
CONTAINER_PATTERN = re.compile(r'^([A-Z][a-zA-Z0-9]*)Container\.tsx$')
|
|
62
|
+
VIEW_PATTERN = re.compile(r'^([A-Z][a-zA-Z0-9]*)View\.tsx$')
|
|
63
|
+
|
|
64
|
+
# Directories to skip during validation
|
|
65
|
+
SKIP_DIRS = {
|
|
66
|
+
'node_modules', '.git', 'dist', 'build', '.expo',
|
|
67
|
+
'.next', 'coverage', '__pycache__', '.claude'
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def find_project_root(start_path: Path) -> Path:
|
|
72
|
+
"""Find the project root by looking for package.json."""
|
|
73
|
+
current = start_path.resolve()
|
|
74
|
+
while current != current.parent:
|
|
75
|
+
if (current / 'package.json').exists():
|
|
76
|
+
return current
|
|
77
|
+
current = current.parent
|
|
78
|
+
return start_path.resolve()
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def validate_test_file_placement(root: Path) -> List[ValidationResult]:
|
|
82
|
+
"""Check that test files are in __tests__/ subdirectories."""
|
|
83
|
+
results = []
|
|
84
|
+
test_pattern = re.compile(r'\.(test|spec)\.(ts|tsx|js|jsx)$')
|
|
85
|
+
|
|
86
|
+
for dirpath, dirnames, filenames in os.walk(root):
|
|
87
|
+
# Skip excluded directories
|
|
88
|
+
dirnames[:] = [d for d in dirnames if d not in SKIP_DIRS]
|
|
89
|
+
|
|
90
|
+
path = Path(dirpath)
|
|
91
|
+
|
|
92
|
+
# Skip if we're inside __tests__ directory
|
|
93
|
+
if '__tests__' in path.parts:
|
|
94
|
+
continue
|
|
95
|
+
|
|
96
|
+
# Skip e2e directory (has different structure)
|
|
97
|
+
if 'e2e' in path.parts:
|
|
98
|
+
continue
|
|
99
|
+
|
|
100
|
+
for filename in filenames:
|
|
101
|
+
if test_pattern.search(filename):
|
|
102
|
+
file_path = path / filename
|
|
103
|
+
rel_path = file_path.relative_to(root)
|
|
104
|
+
|
|
105
|
+
# Check if parent directory should have __tests__
|
|
106
|
+
parent_name = path.name
|
|
107
|
+
|
|
108
|
+
results.append(ValidationResult(
|
|
109
|
+
passed=False,
|
|
110
|
+
message=f"Test file not in __tests__/ directory",
|
|
111
|
+
file_path=str(rel_path),
|
|
112
|
+
suggestion=f"Move to {path}/__tests__/{filename}"
|
|
113
|
+
))
|
|
114
|
+
|
|
115
|
+
return results
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def validate_component_structure(component_dir: Path, root: Path) -> List[ValidationResult]:
|
|
119
|
+
"""Validate Container/View pattern in a component directory."""
|
|
120
|
+
results = []
|
|
121
|
+
rel_path = component_dir.relative_to(root)
|
|
122
|
+
|
|
123
|
+
if not component_dir.is_dir():
|
|
124
|
+
return results
|
|
125
|
+
|
|
126
|
+
files = list(component_dir.iterdir())
|
|
127
|
+
file_names = [f.name for f in files if f.is_file()]
|
|
128
|
+
|
|
129
|
+
component_name = component_dir.name
|
|
130
|
+
|
|
131
|
+
expected_container = f"{component_name}Container.tsx"
|
|
132
|
+
expected_view = f"{component_name}View.tsx"
|
|
133
|
+
expected_index = "index.tsx"
|
|
134
|
+
|
|
135
|
+
has_container = expected_container in file_names
|
|
136
|
+
has_view = expected_view in file_names
|
|
137
|
+
has_index = expected_index in file_names or "index.ts" in file_names
|
|
138
|
+
|
|
139
|
+
# Check for Container
|
|
140
|
+
if not has_container:
|
|
141
|
+
# Check for any Container file
|
|
142
|
+
containers = [f for f in file_names if CONTAINER_PATTERN.match(f)]
|
|
143
|
+
if containers:
|
|
144
|
+
results.append(ValidationResult(
|
|
145
|
+
passed=False,
|
|
146
|
+
message=f"Container file name mismatch",
|
|
147
|
+
file_path=str(rel_path),
|
|
148
|
+
suggestion=f"Rename {containers[0]} to {expected_container}"
|
|
149
|
+
))
|
|
150
|
+
else:
|
|
151
|
+
results.append(ValidationResult(
|
|
152
|
+
passed=False,
|
|
153
|
+
message=f"Missing Container file",
|
|
154
|
+
file_path=str(rel_path),
|
|
155
|
+
suggestion=f"Create {expected_container}"
|
|
156
|
+
))
|
|
157
|
+
|
|
158
|
+
# Check for View
|
|
159
|
+
if not has_view:
|
|
160
|
+
views = [f for f in file_names if VIEW_PATTERN.match(f)]
|
|
161
|
+
if views:
|
|
162
|
+
results.append(ValidationResult(
|
|
163
|
+
passed=False,
|
|
164
|
+
message=f"View file name mismatch",
|
|
165
|
+
file_path=str(rel_path),
|
|
166
|
+
suggestion=f"Rename {views[0]} to {expected_view}"
|
|
167
|
+
))
|
|
168
|
+
else:
|
|
169
|
+
results.append(ValidationResult(
|
|
170
|
+
passed=False,
|
|
171
|
+
message=f"Missing View file",
|
|
172
|
+
file_path=str(rel_path),
|
|
173
|
+
suggestion=f"Create {expected_view}"
|
|
174
|
+
))
|
|
175
|
+
|
|
176
|
+
# Check for index
|
|
177
|
+
if not has_index:
|
|
178
|
+
results.append(ValidationResult(
|
|
179
|
+
passed=False,
|
|
180
|
+
message=f"Missing index.tsx",
|
|
181
|
+
file_path=str(rel_path),
|
|
182
|
+
suggestion=f"Create index.tsx that exports Container"
|
|
183
|
+
))
|
|
184
|
+
|
|
185
|
+
return results
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
def validate_feature_structure(feature_dir: Path, root: Path) -> List[ValidationResult]:
|
|
189
|
+
"""Validate feature module structure."""
|
|
190
|
+
results = []
|
|
191
|
+
rel_path = feature_dir.relative_to(root)
|
|
192
|
+
|
|
193
|
+
if not feature_dir.is_dir():
|
|
194
|
+
return results
|
|
195
|
+
|
|
196
|
+
# Check components directory
|
|
197
|
+
components_dir = feature_dir / 'components'
|
|
198
|
+
if components_dir.exists():
|
|
199
|
+
for item in components_dir.iterdir():
|
|
200
|
+
if item.is_dir() and not item.name.startswith('_'):
|
|
201
|
+
results.extend(validate_component_structure(item, root))
|
|
202
|
+
|
|
203
|
+
# Check screens directory
|
|
204
|
+
screens_dir = feature_dir / 'screens'
|
|
205
|
+
if screens_dir.exists():
|
|
206
|
+
for item in screens_dir.iterdir():
|
|
207
|
+
if item.is_dir() and not item.name.startswith('_'):
|
|
208
|
+
results.extend(validate_component_structure(item, root))
|
|
209
|
+
|
|
210
|
+
# Check hooks have __tests__ if there are test files
|
|
211
|
+
hooks_dir = feature_dir / 'hooks'
|
|
212
|
+
if hooks_dir.exists():
|
|
213
|
+
tests_dir = hooks_dir / '__tests__'
|
|
214
|
+
hook_files = [f for f in hooks_dir.iterdir() if f.is_file() and f.suffix in {'.ts', '.tsx'}]
|
|
215
|
+
if hook_files and not tests_dir.exists():
|
|
216
|
+
results.append(ValidationResult(
|
|
217
|
+
passed=False,
|
|
218
|
+
message=f"Missing __tests__/ directory for hooks",
|
|
219
|
+
file_path=str(rel_path / 'hooks'),
|
|
220
|
+
suggestion=f"Create {rel_path}/hooks/__tests__/ for test files"
|
|
221
|
+
))
|
|
222
|
+
|
|
223
|
+
# Check utils have __tests__ if there are test files
|
|
224
|
+
utils_dir = feature_dir / 'utils'
|
|
225
|
+
if utils_dir.exists():
|
|
226
|
+
tests_dir = utils_dir / '__tests__'
|
|
227
|
+
util_files = [f for f in utils_dir.iterdir() if f.is_file() and f.suffix in {'.ts', '.tsx'}]
|
|
228
|
+
if util_files and not tests_dir.exists():
|
|
229
|
+
results.append(ValidationResult(
|
|
230
|
+
passed=False,
|
|
231
|
+
message=f"Missing __tests__/ directory for utils",
|
|
232
|
+
file_path=str(rel_path / 'utils'),
|
|
233
|
+
suggestion=f"Create {rel_path}/utils/__tests__/ for test files"
|
|
234
|
+
))
|
|
235
|
+
|
|
236
|
+
return results
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
def validate_app_directory(app_dir: Path, root: Path) -> List[ValidationResult]:
|
|
240
|
+
"""Validate that app/ directory only contains thin route wrappers."""
|
|
241
|
+
results = []
|
|
242
|
+
|
|
243
|
+
if not app_dir.exists():
|
|
244
|
+
return results
|
|
245
|
+
|
|
246
|
+
# Patterns that indicate non-wrapper code
|
|
247
|
+
business_logic_patterns = [
|
|
248
|
+
(re.compile(r'useState\s*\('), "useState hook usage"),
|
|
249
|
+
(re.compile(r'useEffect\s*\('), "useEffect hook usage"),
|
|
250
|
+
(re.compile(r'useCallback\s*\('), "useCallback hook usage"),
|
|
251
|
+
(re.compile(r'useMemo\s*\('), "useMemo hook usage"),
|
|
252
|
+
(re.compile(r'useQuery\s*\('), "useQuery hook usage"),
|
|
253
|
+
(re.compile(r'useMutation\s*\('), "useMutation hook usage"),
|
|
254
|
+
]
|
|
255
|
+
|
|
256
|
+
# Files that are allowed (layout files, etc.)
|
|
257
|
+
allowed_patterns = {'_layout.tsx', '_layout.ts', '+not-found.tsx', '+html.tsx'}
|
|
258
|
+
|
|
259
|
+
for dirpath, dirnames, filenames in os.walk(app_dir):
|
|
260
|
+
dirnames[:] = [d for d in dirnames if d not in SKIP_DIRS]
|
|
261
|
+
path = Path(dirpath)
|
|
262
|
+
|
|
263
|
+
for filename in filenames:
|
|
264
|
+
if not filename.endswith(('.tsx', '.ts')):
|
|
265
|
+
continue
|
|
266
|
+
|
|
267
|
+
if filename in allowed_patterns:
|
|
268
|
+
continue
|
|
269
|
+
|
|
270
|
+
if filename.startswith('_') or filename.startswith('+'):
|
|
271
|
+
continue
|
|
272
|
+
|
|
273
|
+
file_path = path / filename
|
|
274
|
+
rel_path = file_path.relative_to(root)
|
|
275
|
+
|
|
276
|
+
try:
|
|
277
|
+
content = file_path.read_text()
|
|
278
|
+
|
|
279
|
+
for pattern, description in business_logic_patterns:
|
|
280
|
+
if pattern.search(content):
|
|
281
|
+
results.append(ValidationResult(
|
|
282
|
+
passed=False,
|
|
283
|
+
message=f"Route file contains business logic: {description}",
|
|
284
|
+
file_path=str(rel_path),
|
|
285
|
+
suggestion="Move business logic to features/ directory"
|
|
286
|
+
))
|
|
287
|
+
break # Only report first violation per file
|
|
288
|
+
|
|
289
|
+
except Exception as e:
|
|
290
|
+
pass # Skip files we can't read
|
|
291
|
+
|
|
292
|
+
return results
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
def validate_naming_conventions(root: Path) -> List[ValidationResult]:
|
|
296
|
+
"""Validate file and directory naming conventions."""
|
|
297
|
+
results = []
|
|
298
|
+
|
|
299
|
+
# Feature directory names should be kebab-case
|
|
300
|
+
features_dir = root / 'features'
|
|
301
|
+
if features_dir.exists():
|
|
302
|
+
kebab_pattern = re.compile(r'^[a-z][a-z0-9]*(-[a-z0-9]+)*$')
|
|
303
|
+
for item in features_dir.iterdir():
|
|
304
|
+
if item.is_dir() and not item.name.startswith('.'):
|
|
305
|
+
if not kebab_pattern.match(item.name):
|
|
306
|
+
results.append(ValidationResult(
|
|
307
|
+
passed=False,
|
|
308
|
+
message=f"Feature directory not in kebab-case",
|
|
309
|
+
file_path=f"features/{item.name}",
|
|
310
|
+
suggestion=f"Rename to kebab-case (e.g., 'my-feature')"
|
|
311
|
+
))
|
|
312
|
+
|
|
313
|
+
# Component directories should be PascalCase
|
|
314
|
+
pascal_pattern = re.compile(r'^[A-Z][a-zA-Z0-9]*$')
|
|
315
|
+
|
|
316
|
+
def check_component_dirs(base_dir: Path, dir_type: str):
|
|
317
|
+
if not base_dir.exists():
|
|
318
|
+
return
|
|
319
|
+
for item in base_dir.iterdir():
|
|
320
|
+
if item.is_dir() and not item.name.startswith('_'):
|
|
321
|
+
if not pascal_pattern.match(item.name):
|
|
322
|
+
rel_path = item.relative_to(root)
|
|
323
|
+
results.append(ValidationResult(
|
|
324
|
+
passed=False,
|
|
325
|
+
message=f"{dir_type} directory not in PascalCase",
|
|
326
|
+
file_path=str(rel_path),
|
|
327
|
+
suggestion=f"Rename to PascalCase (e.g., 'MyComponent')"
|
|
328
|
+
))
|
|
329
|
+
|
|
330
|
+
# Check components directory (skip special subdirectories)
|
|
331
|
+
components_dir = root / 'components'
|
|
332
|
+
if components_dir.exists():
|
|
333
|
+
for item in components_dir.iterdir():
|
|
334
|
+
# Skip special directories that are allowed to be lowercase
|
|
335
|
+
if item.name in {'ui', 'icons', 'custom', 'shared'}:
|
|
336
|
+
continue
|
|
337
|
+
if item.is_dir() and not item.name.startswith('_'):
|
|
338
|
+
if not pascal_pattern.match(item.name):
|
|
339
|
+
rel_path = item.relative_to(root)
|
|
340
|
+
results.append(ValidationResult(
|
|
341
|
+
passed=False,
|
|
342
|
+
message=f"Component directory not in PascalCase",
|
|
343
|
+
file_path=str(rel_path),
|
|
344
|
+
suggestion=f"Rename to PascalCase (e.g., 'MyComponent')"
|
|
345
|
+
))
|
|
346
|
+
|
|
347
|
+
# Check feature components and screens
|
|
348
|
+
if features_dir.exists():
|
|
349
|
+
for feature in features_dir.iterdir():
|
|
350
|
+
if feature.is_dir():
|
|
351
|
+
check_component_dirs(feature / 'components', 'Component')
|
|
352
|
+
check_component_dirs(feature / 'screens', 'Screen')
|
|
353
|
+
|
|
354
|
+
return results
|
|
355
|
+
|
|
356
|
+
|
|
357
|
+
def run_validation(path: Path) -> ValidationReport:
|
|
358
|
+
"""Run all validations and return a report."""
|
|
359
|
+
report = ValidationReport()
|
|
360
|
+
root = find_project_root(path)
|
|
361
|
+
|
|
362
|
+
print(f"Validating directory structure from: {root}\n")
|
|
363
|
+
|
|
364
|
+
# Run all validations
|
|
365
|
+
report.errors.extend(validate_test_file_placement(root))
|
|
366
|
+
report.errors.extend(validate_naming_conventions(root))
|
|
367
|
+
report.errors.extend(validate_app_directory(root / 'app', root))
|
|
368
|
+
|
|
369
|
+
# Validate features
|
|
370
|
+
features_dir = root / 'features'
|
|
371
|
+
if features_dir.exists():
|
|
372
|
+
for feature in features_dir.iterdir():
|
|
373
|
+
if feature.is_dir() and not feature.name.startswith('.'):
|
|
374
|
+
report.errors.extend(validate_feature_structure(feature, root))
|
|
375
|
+
|
|
376
|
+
# Validate global components
|
|
377
|
+
components_dir = root / 'components'
|
|
378
|
+
if components_dir.exists():
|
|
379
|
+
for item in components_dir.iterdir():
|
|
380
|
+
# Skip ui/, icons/, custom/ as they have different structure
|
|
381
|
+
if item.name in {'ui', 'icons', 'custom', 'shared'}:
|
|
382
|
+
continue
|
|
383
|
+
if item.is_dir() and not item.name.startswith('_'):
|
|
384
|
+
report.errors.extend(validate_component_structure(item, root))
|
|
385
|
+
|
|
386
|
+
return report
|
|
387
|
+
|
|
388
|
+
|
|
389
|
+
def print_report(report: ValidationReport) -> None:
|
|
390
|
+
"""Print the validation report."""
|
|
391
|
+
if report.errors:
|
|
392
|
+
print("=" * 60)
|
|
393
|
+
print(f"ERRORS ({len(report.errors)})")
|
|
394
|
+
print("=" * 60)
|
|
395
|
+
for result in report.errors:
|
|
396
|
+
print(f"\n❌ {result.message}")
|
|
397
|
+
if result.file_path:
|
|
398
|
+
print(f" File: {result.file_path}")
|
|
399
|
+
if result.suggestion:
|
|
400
|
+
print(f" Fix: {result.suggestion}")
|
|
401
|
+
|
|
402
|
+
if report.warnings:
|
|
403
|
+
print("\n" + "=" * 60)
|
|
404
|
+
print(f"WARNINGS ({len(report.warnings)})")
|
|
405
|
+
print("=" * 60)
|
|
406
|
+
for result in report.warnings:
|
|
407
|
+
print(f"\n⚠️ {result.message}")
|
|
408
|
+
if result.file_path:
|
|
409
|
+
print(f" File: {result.file_path}")
|
|
410
|
+
if result.suggestion:
|
|
411
|
+
print(f" Fix: {result.suggestion}")
|
|
412
|
+
|
|
413
|
+
print("\n" + "=" * 60)
|
|
414
|
+
print("SUMMARY")
|
|
415
|
+
print("=" * 60)
|
|
416
|
+
print(f"Errors: {len(report.errors)}")
|
|
417
|
+
print(f"Warnings: {len(report.warnings)}")
|
|
418
|
+
|
|
419
|
+
if not report.has_errors and not report.has_warnings:
|
|
420
|
+
print("\n✅ All directory structure validations passed!")
|
|
421
|
+
elif report.has_errors:
|
|
422
|
+
print("\n❌ Validation failed with errors")
|
|
423
|
+
else:
|
|
424
|
+
print("\n⚠️ Validation passed with warnings")
|
|
425
|
+
|
|
426
|
+
|
|
427
|
+
def main():
|
|
428
|
+
"""Main entry point."""
|
|
429
|
+
path = Path(sys.argv[1]) if len(sys.argv) > 1 else Path.cwd()
|
|
430
|
+
|
|
431
|
+
if not path.exists():
|
|
432
|
+
print(f"Error: Path does not exist: {path}")
|
|
433
|
+
sys.exit(1)
|
|
434
|
+
|
|
435
|
+
report = run_validation(path)
|
|
436
|
+
print_report(report)
|
|
437
|
+
|
|
438
|
+
# Exit with error code if there are errors
|
|
439
|
+
sys.exit(1 if report.has_errors else 0)
|
|
440
|
+
|
|
441
|
+
|
|
442
|
+
if __name__ == '__main__':
|
|
443
|
+
main()
|