xtrm-tools 2.3.0 → 2.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +132 -111
- package/cli/dist/index.cjs +142 -53
- package/cli/dist/index.cjs.map +1 -1
- package/cli/package.json +1 -1
- package/config/pi/extensions/beads.ts +24 -0
- package/hooks/beads-gate-core.mjs +6 -4
- package/hooks/beads-memory-gate.mjs +12 -5
- package/hooks/hooks.json +126 -0
- package/package.json +3 -1
- package/skills/test-planning/SKILL.md +208 -0
- package/skills/test-planning/evals/evals.json +23 -0
- package/skills/using-xtrm/SKILL.md +5 -27
- package/project-skills/tdd-guard/.claude/hooks/tdd-guard-pretool-bridge.cjs +0 -103
- package/project-skills/tdd-guard/.claude/settings.json +0 -38
- package/project-skills/tdd-guard/.claude/skills/using-tdd-guard/SKILL.md +0 -79
- package/project-skills/tdd-guard/CLAUDE.md +0 -98
- package/project-skills/tdd-guard/CONTRIBUTING.md +0 -38
- package/project-skills/tdd-guard/DEVELOPMENT.md +0 -127
- package/project-skills/tdd-guard/LICENSE +0 -21
- package/project-skills/tdd-guard/README.md +0 -398
- package/project-skills/tdd-guard/docs/adr/001-claude-session-subdirectory.md +0 -52
- package/project-skills/tdd-guard/docs/adr/002-secure-claude-binary-path.md +0 -56
- package/project-skills/tdd-guard/docs/adr/003-remove-configurable-data-directory.md +0 -56
- package/project-skills/tdd-guard/docs/adr/004-monorepo-architecture.md +0 -64
- package/project-skills/tdd-guard/docs/adr/005-claude-project-dir-support.md +0 -55
- package/project-skills/tdd-guard/docs/adr/006-phpunit-separate-repository.md +0 -93
- package/project-skills/tdd-guard/docs/adr/007-golangci-lint-path-support.md +0 -83
- package/project-skills/tdd-guard/docs/adr/008-storybook-reporter-design.md +0 -182
- package/project-skills/tdd-guard/docs/assets/tdd-guard-demo-screenshot.gif +0 -0
- package/project-skills/tdd-guard/docs/config-migration.md +0 -143
- package/project-skills/tdd-guard/docs/configuration.md +0 -137
- package/project-skills/tdd-guard/docs/custom-instructions.md +0 -43
- package/project-skills/tdd-guard/docs/enforcement.md +0 -46
- package/project-skills/tdd-guard/docs/ignore-patterns.md +0 -81
- package/project-skills/tdd-guard/docs/linting.md +0 -109
- package/project-skills/tdd-guard/docs/quick-commands.md +0 -52
- package/project-skills/tdd-guard/docs/session-management.md +0 -75
- package/project-skills/tdd-guard/docs/storybook-vitest-addon.md +0 -120
- package/project-skills/tdd-guard/docs/validation-model.md +0 -63
- package/project-skills/tdd-guard/eslint.config.mjs +0 -140
- package/project-skills/tdd-guard/package-lock.json +0 -16937
- package/project-skills/tdd-guard/package.json +0 -102
- package/project-skills/tdd-guard/reporters/go/README.md +0 -67
- package/project-skills/tdd-guard/reporters/go/cmd/tdd-guard-go/main.go +0 -127
- package/project-skills/tdd-guard/reporters/go/cmd/tdd-guard-go/main_test.go +0 -280
- package/project-skills/tdd-guard/reporters/go/go.mod +0 -3
- package/project-skills/tdd-guard/reporters/go/go.sum +0 -0
- package/project-skills/tdd-guard/reporters/go/internal/formatter/formatter.go +0 -126
- package/project-skills/tdd-guard/reporters/go/internal/formatter/formatter_test.go +0 -264
- package/project-skills/tdd-guard/reporters/go/internal/io/tee_reader.go +0 -26
- package/project-skills/tdd-guard/reporters/go/internal/io/tee_reader_test.go +0 -37
- package/project-skills/tdd-guard/reporters/go/internal/parser/mixed_reader.go +0 -94
- package/project-skills/tdd-guard/reporters/go/internal/parser/mixed_reader_test.go +0 -198
- package/project-skills/tdd-guard/reporters/go/internal/parser/parser.go +0 -245
- package/project-skills/tdd-guard/reporters/go/internal/parser/parser_test.go +0 -547
- package/project-skills/tdd-guard/reporters/go/internal/storage/storage.go +0 -35
- package/project-skills/tdd-guard/reporters/go/internal/storage/storage_test.go +0 -113
- package/project-skills/tdd-guard/reporters/go/internal/transformer/transformer.go +0 -103
- package/project-skills/tdd-guard/reporters/go/internal/transformer/transformer_test.go +0 -303
- package/project-skills/tdd-guard/reporters/jest/README.md +0 -102
- package/project-skills/tdd-guard/reporters/jest/package.json +0 -38
- package/project-skills/tdd-guard/reporters/jest/src/JestReporter.test-data.ts +0 -199
- package/project-skills/tdd-guard/reporters/jest/src/JestReporter.test.ts +0 -302
- package/project-skills/tdd-guard/reporters/jest/src/JestReporter.ts +0 -201
- package/project-skills/tdd-guard/reporters/jest/src/index.ts +0 -4
- package/project-skills/tdd-guard/reporters/jest/src/types.ts +0 -42
- package/project-skills/tdd-guard/reporters/jest/tsconfig.json +0 -11
- package/project-skills/tdd-guard/reporters/phpunit/.php-cs-fixer.php +0 -28
- package/project-skills/tdd-guard/reporters/phpunit/README.md +0 -97
- package/project-skills/tdd-guard/reporters/phpunit/SYNC_README.md +0 -29
- package/project-skills/tdd-guard/reporters/phpunit/composer.json +0 -55
- package/project-skills/tdd-guard/reporters/phpunit/phpunit.xml.dist +0 -19
- package/project-skills/tdd-guard/reporters/phpunit/psalm.xml +0 -44
- package/project-skills/tdd-guard/reporters/phpunit/src/Event/ErroredTestSubscriber.php +0 -28
- package/project-skills/tdd-guard/reporters/phpunit/src/Event/FailedTestSubscriber.php +0 -28
- package/project-skills/tdd-guard/reporters/phpunit/src/Event/IncompleteTestSubscriber.php +0 -28
- package/project-skills/tdd-guard/reporters/phpunit/src/Event/PassedTestSubscriber.php +0 -27
- package/project-skills/tdd-guard/reporters/phpunit/src/Event/SkippedTestSubscriber.php +0 -28
- package/project-skills/tdd-guard/reporters/phpunit/src/Event/TestRunnerFinishedSubscriber.php +0 -24
- package/project-skills/tdd-guard/reporters/phpunit/src/PathValidator.php +0 -88
- package/project-skills/tdd-guard/reporters/phpunit/src/Storage.php +0 -26
- package/project-skills/tdd-guard/reporters/phpunit/src/TddGuardExtension.php +0 -33
- package/project-skills/tdd-guard/reporters/phpunit/src/TddGuardListener.php +0 -158
- package/project-skills/tdd-guard/reporters/phpunit/src/TddGuardSubscriber.php +0 -35
- package/project-skills/tdd-guard/reporters/phpunit/src/TestResultCollector.php +0 -105
- package/project-skills/tdd-guard/reporters/phpunit/tests/PathValidatorTest.php +0 -74
- package/project-skills/tdd-guard/reporters/phpunit/tests/TddGuardExtensionFailedTest.php +0 -241
- package/project-skills/tdd-guard/reporters/phpunit/tests/TddGuardExtensionTest.php +0 -84
- package/project-skills/tdd-guard/reporters/phpunit/tests/TddGuardStorageLocationTest.php +0 -71
- package/project-skills/tdd-guard/reporters/pytest/README.md +0 -77
- package/project-skills/tdd-guard/reporters/pytest/pyproject.toml +0 -43
- package/project-skills/tdd-guard/reporters/pytest/pytest.ini.example +0 -7
- package/project-skills/tdd-guard/reporters/pytest/tdd_guard_pytest/__init__.py +0 -1
- package/project-skills/tdd-guard/reporters/pytest/tdd_guard_pytest/pytest_reporter.py +0 -134
- package/project-skills/tdd-guard/reporters/pytest/tests/__init__.py +0 -1
- package/project-skills/tdd-guard/reporters/pytest/tests/conftest.py +0 -3
- package/project-skills/tdd-guard/reporters/pytest/tests/helpers.py +0 -293
- package/project-skills/tdd-guard/reporters/pytest/tests/test_config_option.py +0 -38
- package/project-skills/tdd-guard/reporters/pytest/tests/test_path_validation.py +0 -59
- package/project-skills/tdd-guard/reporters/pytest/tests/test_plugin_config.py +0 -32
- package/project-skills/tdd-guard/reporters/pytest/tests/test_project_root.py +0 -296
- package/project-skills/tdd-guard/reporters/pytest/tests/test_pytest_reporter.py +0 -137
- package/project-skills/tdd-guard/reporters/rspec/Gemfile +0 -3
- package/project-skills/tdd-guard/reporters/rust/Cargo.lock +0 -458
- package/project-skills/tdd-guard/reporters/rust/Cargo.toml +0 -33
- package/project-skills/tdd-guard/reporters/rust/Makefile.example +0 -95
- package/project-skills/tdd-guard/reporters/rust/README.md +0 -88
- package/project-skills/tdd-guard/reporters/rust/src/error_parser.rs +0 -309
- package/project-skills/tdd-guard/reporters/rust/src/main.rs +0 -464
- package/project-skills/tdd-guard/reporters/rust/src/parser.rs +0 -225
- package/project-skills/tdd-guard/reporters/rust/src/transformer.rs +0 -409
- package/project-skills/tdd-guard/reporters/storybook/README.md +0 -108
- package/project-skills/tdd-guard/reporters/storybook/package-lock.json +0 -9482
- package/project-skills/tdd-guard/reporters/storybook/package.json +0 -43
- package/project-skills/tdd-guard/reporters/storybook/src/StorybookReporter.test-data.ts +0 -22
- package/project-skills/tdd-guard/reporters/storybook/src/StorybookReporter.test.ts +0 -190
- package/project-skills/tdd-guard/reporters/storybook/src/StorybookReporter.ts +0 -88
- package/project-skills/tdd-guard/reporters/storybook/src/index.ts +0 -12
- package/project-skills/tdd-guard/reporters/storybook/src/types.ts +0 -37
- package/project-skills/tdd-guard/reporters/storybook/tsconfig.json +0 -11
- package/project-skills/tdd-guard/reporters/test/artifacts/go/failing/go.mod +0 -3
- package/project-skills/tdd-guard/reporters/test/artifacts/go/failing/single_failing_test.go +0 -13
- package/project-skills/tdd-guard/reporters/test/artifacts/go/import/go.mod +0 -3
- package/project-skills/tdd-guard/reporters/test/artifacts/go/import/single_import_error_test.go +0 -17
- package/project-skills/tdd-guard/reporters/test/artifacts/go/passing/go.mod +0 -3
- package/project-skills/tdd-guard/reporters/test/artifacts/go/passing/single_passing_test.go +0 -13
- package/project-skills/tdd-guard/reporters/test/artifacts/jest/single-failing.test.js +0 -5
- package/project-skills/tdd-guard/reporters/test/artifacts/jest/single-import-error.test.js +0 -8
- package/project-skills/tdd-guard/reporters/test/artifacts/jest/single-passing.test.js +0 -5
- package/project-skills/tdd-guard/reporters/test/artifacts/phpunit/SingleFailingTest.php +0 -11
- package/project-skills/tdd-guard/reporters/test/artifacts/phpunit/SingleImportErrorTest.php +0 -14
- package/project-skills/tdd-guard/reporters/test/artifacts/phpunit/SinglePassingTest.php +0 -11
- package/project-skills/tdd-guard/reporters/test/artifacts/pytest/test_single_failing.py +0 -3
- package/project-skills/tdd-guard/reporters/test/artifacts/pytest/test_single_import_error.py +0 -6
- package/project-skills/tdd-guard/reporters/test/artifacts/pytest/test_single_passing.py +0 -3
- package/project-skills/tdd-guard/reporters/test/artifacts/rust/failing/Cargo.lock +0 -7
- package/project-skills/tdd-guard/reporters/test/artifacts/rust/failing/Cargo.toml +0 -4
- package/project-skills/tdd-guard/reporters/test/artifacts/rust/failing/src/lib.rs +0 -14
- package/project-skills/tdd-guard/reporters/test/artifacts/rust/import/Cargo.lock +0 -7
- package/project-skills/tdd-guard/reporters/test/artifacts/rust/import/Cargo.toml +0 -4
- package/project-skills/tdd-guard/reporters/test/artifacts/rust/import/src/lib.rs +0 -13
- package/project-skills/tdd-guard/reporters/test/artifacts/rust/passing/Cargo.lock +0 -7
- package/project-skills/tdd-guard/reporters/test/artifacts/rust/passing/Cargo.toml +0 -4
- package/project-skills/tdd-guard/reporters/test/artifacts/rust/passing/src/lib.rs +0 -14
- package/project-skills/tdd-guard/reporters/test/artifacts/storybook/Calculator.js +0 -4
- package/project-skills/tdd-guard/reporters/test/artifacts/storybook/single-failing.stories.js +0 -15
- package/project-skills/tdd-guard/reporters/test/artifacts/storybook/single-import-error.stories.js +0 -14
- package/project-skills/tdd-guard/reporters/test/artifacts/storybook/single-passing.stories.js +0 -15
- package/project-skills/tdd-guard/reporters/test/artifacts/vitest/single-failing.test.js +0 -7
- package/project-skills/tdd-guard/reporters/test/artifacts/vitest/single-import-error.test.js +0 -9
- package/project-skills/tdd-guard/reporters/test/artifacts/vitest/single-passing.test.js +0 -7
- package/project-skills/tdd-guard/reporters/test/factories/go.ts +0 -59
- package/project-skills/tdd-guard/reporters/test/factories/helpers.ts +0 -48
- package/project-skills/tdd-guard/reporters/test/factories/index.ts +0 -7
- package/project-skills/tdd-guard/reporters/test/factories/jest.ts +0 -51
- package/project-skills/tdd-guard/reporters/test/factories/phpunit.ts +0 -63
- package/project-skills/tdd-guard/reporters/test/factories/pytest.ts +0 -41
- package/project-skills/tdd-guard/reporters/test/factories/rust.ts +0 -158
- package/project-skills/tdd-guard/reporters/test/factories/storybook.ts +0 -198
- package/project-skills/tdd-guard/reporters/test/factories/vitest.ts +0 -51
- package/project-skills/tdd-guard/reporters/test/reporters.integration.test.ts +0 -735
- package/project-skills/tdd-guard/reporters/test/types.ts +0 -28
- package/project-skills/tdd-guard/reporters/vitest/README.md +0 -64
- package/project-skills/tdd-guard/reporters/vitest/package.json +0 -35
- package/project-skills/tdd-guard/reporters/vitest/src/VitestReporter.test-data.ts +0 -85
- package/project-skills/tdd-guard/reporters/vitest/src/VitestReporter.test.ts +0 -446
- package/project-skills/tdd-guard/reporters/vitest/src/VitestReporter.ts +0 -110
- package/project-skills/tdd-guard/reporters/vitest/src/index.ts +0 -4
- package/project-skills/tdd-guard/reporters/vitest/src/types.ts +0 -39
- package/project-skills/tdd-guard/reporters/vitest/tsconfig.json +0 -11
- package/project-skills/tdd-guard/src/cli/buildContext.test.ts +0 -200
- package/project-skills/tdd-guard/src/cli/buildContext.ts +0 -48
- package/project-skills/tdd-guard/src/cli/tdd-guard.test.ts +0 -159
- package/project-skills/tdd-guard/src/cli/tdd-guard.ts +0 -48
- package/project-skills/tdd-guard/src/config/Config.test.ts +0 -538
- package/project-skills/tdd-guard/src/config/Config.ts +0 -172
- package/project-skills/tdd-guard/src/contracts/schemas/guardSchemas.test.ts +0 -58
- package/project-skills/tdd-guard/src/contracts/schemas/guardSchemas.ts +0 -8
- package/project-skills/tdd-guard/src/contracts/schemas/lintSchemas.test.ts +0 -347
- package/project-skills/tdd-guard/src/contracts/schemas/lintSchemas.ts +0 -61
- package/project-skills/tdd-guard/src/contracts/schemas/pytestSchemas.test.ts +0 -24
- package/project-skills/tdd-guard/src/contracts/schemas/pytestSchemas.ts +0 -7
- package/project-skills/tdd-guard/src/contracts/schemas/reporterSchemas.test.ts +0 -377
- package/project-skills/tdd-guard/src/contracts/schemas/reporterSchemas.ts +0 -75
- package/project-skills/tdd-guard/src/contracts/schemas/toolSchemas.test.ts +0 -563
- package/project-skills/tdd-guard/src/contracts/schemas/toolSchemas.ts +0 -140
- package/project-skills/tdd-guard/src/contracts/types/ClientType.ts +0 -1
- package/project-skills/tdd-guard/src/contracts/types/ConfigOptions.ts +0 -12
- package/project-skills/tdd-guard/src/contracts/types/Context.ts +0 -16
- package/project-skills/tdd-guard/src/contracts/types/ModelClient.ts +0 -3
- package/project-skills/tdd-guard/src/contracts/types/ValidationResult.ts +0 -6
- package/project-skills/tdd-guard/src/guard/GuardManager.test.ts +0 -336
- package/project-skills/tdd-guard/src/guard/GuardManager.ts +0 -83
- package/project-skills/tdd-guard/src/hooks/HookEvents.test.ts +0 -107
- package/project-skills/tdd-guard/src/hooks/HookEvents.ts +0 -39
- package/project-skills/tdd-guard/src/hooks/fileTypeDetection.ts +0 -16
- package/project-skills/tdd-guard/src/hooks/postToolLint.test.ts +0 -327
- package/project-skills/tdd-guard/src/hooks/postToolLint.ts +0 -165
- package/project-skills/tdd-guard/src/hooks/processHookData.test.ts +0 -465
- package/project-skills/tdd-guard/src/hooks/processHookData.ts +0 -203
- package/project-skills/tdd-guard/src/hooks/sessionHandler.test.ts +0 -136
- package/project-skills/tdd-guard/src/hooks/sessionHandler.ts +0 -31
- package/project-skills/tdd-guard/src/hooks/userPromptHandler.test.ts +0 -131
- package/project-skills/tdd-guard/src/hooks/userPromptHandler.ts +0 -55
- package/project-skills/tdd-guard/src/index.ts +0 -19
- package/project-skills/tdd-guard/src/linters/Linter.ts +0 -5
- package/project-skills/tdd-guard/src/linters/eslint/ESLint.test.ts +0 -183
- package/project-skills/tdd-guard/src/linters/eslint/ESLint.ts +0 -82
- package/project-skills/tdd-guard/src/linters/golangci/GolangciLint.test.ts +0 -170
- package/project-skills/tdd-guard/src/linters/golangci/GolangciLint.ts +0 -148
- package/project-skills/tdd-guard/src/processors/index.ts +0 -1
- package/project-skills/tdd-guard/src/processors/lintProcessor.ts +0 -77
- package/project-skills/tdd-guard/src/processors/testResults/TestResultsProcessor.test.ts +0 -303
- package/project-skills/tdd-guard/src/processors/testResults/TestResultsProcessor.ts +0 -255
- package/project-skills/tdd-guard/src/providers/LinterProvider.test.ts +0 -43
- package/project-skills/tdd-guard/src/providers/LinterProvider.ts +0 -20
- package/project-skills/tdd-guard/src/providers/ModelClientProvider.test.ts +0 -68
- package/project-skills/tdd-guard/src/providers/ModelClientProvider.ts +0 -22
- package/project-skills/tdd-guard/src/storage/FileStorage.test.ts +0 -76
- package/project-skills/tdd-guard/src/storage/FileStorage.ts +0 -108
- package/project-skills/tdd-guard/src/storage/MemoryStorage.ts +0 -57
- package/project-skills/tdd-guard/src/storage/Storage.test.ts +0 -227
- package/project-skills/tdd-guard/src/storage/Storage.ts +0 -17
- package/project-skills/tdd-guard/src/validation/context/context.test.ts +0 -364
- package/project-skills/tdd-guard/src/validation/context/context.ts +0 -155
- package/project-skills/tdd-guard/src/validation/models/AnthropicApi.test.ts +0 -171
- package/project-skills/tdd-guard/src/validation/models/AnthropicApi.ts +0 -49
- package/project-skills/tdd-guard/src/validation/models/ClaudeAgentSdk.test.ts +0 -167
- package/project-skills/tdd-guard/src/validation/models/ClaudeAgentSdk.ts +0 -54
- package/project-skills/tdd-guard/src/validation/models/ClaudeCli.test.ts +0 -239
- package/project-skills/tdd-guard/src/validation/models/ClaudeCli.ts +0 -57
- package/project-skills/tdd-guard/src/validation/prompts/file-types.ts +0 -52
- package/project-skills/tdd-guard/src/validation/prompts/operations/edit.ts +0 -58
- package/project-skills/tdd-guard/src/validation/prompts/operations/multi-edit.ts +0 -54
- package/project-skills/tdd-guard/src/validation/prompts/operations/write.ts +0 -54
- package/project-skills/tdd-guard/src/validation/prompts/response.ts +0 -40
- package/project-skills/tdd-guard/src/validation/prompts/rules.ts +0 -51
- package/project-skills/tdd-guard/src/validation/prompts/system-prompt.ts +0 -10
- package/project-skills/tdd-guard/src/validation/prompts/tools/lint-results.ts +0 -15
- package/project-skills/tdd-guard/src/validation/prompts/tools/test-output.ts +0 -14
- package/project-skills/tdd-guard/src/validation/prompts/tools/todos.ts +0 -9
- package/project-skills/tdd-guard/src/validation/validator.test.ts +0 -268
- package/project-skills/tdd-guard/src/validation/validator.ts +0 -159
- package/project-skills/tdd-guard/test/artifacts/go/.golangci.yml +0 -6
- package/project-skills/tdd-guard/test/artifacts/go/with-issues/file-with-issues.go +0 -12
- package/project-skills/tdd-guard/test/artifacts/go/with-issues/go.mod +0 -3
- package/project-skills/tdd-guard/test/artifacts/go/without-issues/file-without-issues.go +0 -7
- package/project-skills/tdd-guard/test/artifacts/go/without-issues/go.mod +0 -3
- package/project-skills/tdd-guard/test/artifacts/javascript/eslint.config.js +0 -20
- package/project-skills/tdd-guard/test/artifacts/javascript/file-with-issues.js +0 -12
- package/project-skills/tdd-guard/test/artifacts/javascript/file-without-issues.js +0 -10
- package/project-skills/tdd-guard/test/hooks/fileTypeDetection.test.ts +0 -26
- package/project-skills/tdd-guard/test/hooks/processHookData.fileType.test.ts +0 -46
- package/project-skills/tdd-guard/test/hooks/processHookData.python.test.ts +0 -68
- package/project-skills/tdd-guard/test/integration/test-context.test.ts +0 -66
- package/project-skills/tdd-guard/test/integration/validator.core.test.ts +0 -96
- package/project-skills/tdd-guard/test/integration/validator.scenarios.test.ts +0 -497
- package/project-skills/tdd-guard/test/utils/assertions.ts +0 -29
- package/project-skills/tdd-guard/test/utils/factories/contextFactory.ts +0 -30
- package/project-skills/tdd-guard/test/utils/factories/editFactory.ts +0 -82
- package/project-skills/tdd-guard/test/utils/factories/helpers.test.ts +0 -46
- package/project-skills/tdd-guard/test/utils/factories/helpers.ts +0 -46
- package/project-skills/tdd-guard/test/utils/factories/lintFactory.ts +0 -352
- package/project-skills/tdd-guard/test/utils/factories/modelClientProviderFactory.ts +0 -21
- package/project-skills/tdd-guard/test/utils/factories/multiEditFactory.ts +0 -79
- package/project-skills/tdd-guard/test/utils/factories/operations.ts +0 -57
- package/project-skills/tdd-guard/test/utils/factories/reporterFactory.ts +0 -55
- package/project-skills/tdd-guard/test/utils/factories/scenarios/index.ts +0 -22
- package/project-skills/tdd-guard/test/utils/factories/scenarios/languages/python.ts +0 -745
- package/project-skills/tdd-guard/test/utils/factories/scenarios/languages/typescript.ts +0 -767
- package/project-skills/tdd-guard/test/utils/factories/scenarios/types.ts +0 -77
- package/project-skills/tdd-guard/test/utils/factories/scenarios/utils.ts +0 -15
- package/project-skills/tdd-guard/test/utils/factories/sessionStartFactory.ts +0 -36
- package/project-skills/tdd-guard/test/utils/factories/testDefaults.ts +0 -90
- package/project-skills/tdd-guard/test/utils/factories/testResultsFactory.ts +0 -234
- package/project-skills/tdd-guard/test/utils/factories/todoFactory.ts +0 -99
- package/project-skills/tdd-guard/test/utils/factories/userPromptSubmitFactory.ts +0 -39
- package/project-skills/tdd-guard/test/utils/factories/writeFactory.ts +0 -70
- package/project-skills/tdd-guard/test/utils/index.ts +0 -131
- package/project-skills/tdd-guard/tsconfig.build.json +0 -16
- package/project-skills/tdd-guard/tsconfig.eslint.json +0 -17
- package/project-skills/tdd-guard/tsconfig.json +0 -32
- package/project-skills/tdd-guard/tsconfig.node.json +0 -10
- package/project-skills/tdd-guard/vitest.config.ts +0 -85
|
@@ -1,465 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, beforeEach, vi } from 'vitest'
|
|
2
|
-
import { processHookData, defaultResult } from './processHookData'
|
|
3
|
-
import { testData } from '@testUtils'
|
|
4
|
-
import { UserPromptHandler } from './userPromptHandler'
|
|
5
|
-
import { GuardManager } from '../guard/GuardManager'
|
|
6
|
-
import { MemoryStorage } from '../storage/MemoryStorage'
|
|
7
|
-
import { ValidationResult } from '../contracts/types/ValidationResult'
|
|
8
|
-
import { Context } from '../contracts/types/Context'
|
|
9
|
-
|
|
10
|
-
const BLOCK_RESULT = {
|
|
11
|
-
decision: 'block',
|
|
12
|
-
reason: 'TDD violation',
|
|
13
|
-
} as const
|
|
14
|
-
|
|
15
|
-
const WRITE_HOOK_DATA = testData.writeOperation()
|
|
16
|
-
const EDIT_HOOK_DATA = testData.editOperation()
|
|
17
|
-
const TODO_WRITE_HOOK_DATA = testData.todoWriteOperation()
|
|
18
|
-
|
|
19
|
-
describe('processHookData', () => {
|
|
20
|
-
let sut: ReturnType<typeof createTestProcessor>
|
|
21
|
-
|
|
22
|
-
beforeEach(() => {
|
|
23
|
-
sut = createTestProcessor()
|
|
24
|
-
})
|
|
25
|
-
|
|
26
|
-
it('should return a ValidationResult', async () => {
|
|
27
|
-
const hookData = { type: 'test', data: 'some data' }
|
|
28
|
-
|
|
29
|
-
const result = await sut.process(hookData)
|
|
30
|
-
|
|
31
|
-
expect(result).toBeDefined()
|
|
32
|
-
expect(result).toHaveProperty('decision')
|
|
33
|
-
expect(result).toHaveProperty('reason')
|
|
34
|
-
})
|
|
35
|
-
|
|
36
|
-
it('should throw error on invalid JSON', async () => {
|
|
37
|
-
const invalidJson = '{ invalid json'
|
|
38
|
-
|
|
39
|
-
// For this test, we need to use processHookData directly since we're testing JSON parsing
|
|
40
|
-
await expect(processHookData(invalidJson)).rejects.toThrow()
|
|
41
|
-
})
|
|
42
|
-
|
|
43
|
-
it('should save modifications content to storage when tool is Edit', async () => {
|
|
44
|
-
await sut.process(EDIT_HOOK_DATA)
|
|
45
|
-
|
|
46
|
-
const savedModifications = await sut.getModifications()
|
|
47
|
-
const parsedModifications = JSON.parse(savedModifications!)
|
|
48
|
-
expect(parsedModifications).toEqual(EDIT_HOOK_DATA)
|
|
49
|
-
})
|
|
50
|
-
|
|
51
|
-
it('should save todo content to storage when tool is TodoWrite', async () => {
|
|
52
|
-
await sut.process(TODO_WRITE_HOOK_DATA)
|
|
53
|
-
|
|
54
|
-
const savedTodo = await sut.getTodo()
|
|
55
|
-
const parsedTodo = JSON.parse(savedTodo!)
|
|
56
|
-
expect(parsedTodo).toEqual(TODO_WRITE_HOOK_DATA)
|
|
57
|
-
})
|
|
58
|
-
|
|
59
|
-
it('should save modifications content when tool has content field', async () => {
|
|
60
|
-
await sut.process(WRITE_HOOK_DATA)
|
|
61
|
-
|
|
62
|
-
const savedModifications = await sut.getModifications()
|
|
63
|
-
const parsedModifications = JSON.parse(savedModifications!)
|
|
64
|
-
expect(parsedModifications).toEqual(WRITE_HOOK_DATA)
|
|
65
|
-
})
|
|
66
|
-
|
|
67
|
-
it('should call validator with context built from storage', async () => {
|
|
68
|
-
// Pre-populate storage
|
|
69
|
-
await sut.populateStorage({
|
|
70
|
-
modifications: 'existing modifications',
|
|
71
|
-
test: 'existing test',
|
|
72
|
-
todo: 'existing todo',
|
|
73
|
-
})
|
|
74
|
-
|
|
75
|
-
const result = await sut.process(EDIT_HOOK_DATA)
|
|
76
|
-
|
|
77
|
-
const actualContext = sut.getValidatorCallArgs()
|
|
78
|
-
|
|
79
|
-
// Verify the context, parsing JSON to handle formatting differences
|
|
80
|
-
expect({
|
|
81
|
-
...actualContext,
|
|
82
|
-
modifications: JSON.parse(actualContext!.modifications),
|
|
83
|
-
}).toEqual({
|
|
84
|
-
modifications: EDIT_HOOK_DATA,
|
|
85
|
-
test: 'existing test',
|
|
86
|
-
todo: 'existing todo',
|
|
87
|
-
lint: {
|
|
88
|
-
errorCount: 0,
|
|
89
|
-
warningCount: 0,
|
|
90
|
-
hasIssues: false,
|
|
91
|
-
totalIssues: 0,
|
|
92
|
-
issuesByFile: new Map(),
|
|
93
|
-
summary: 'No lint data available'
|
|
94
|
-
}
|
|
95
|
-
})
|
|
96
|
-
expect(result).toEqual(BLOCK_RESULT)
|
|
97
|
-
})
|
|
98
|
-
|
|
99
|
-
it('should not call validator for TodoWrite operations', async () => {
|
|
100
|
-
// Pre-populate storage with existing edits that might cause false blocks
|
|
101
|
-
await sut.populateStorage({
|
|
102
|
-
modifications: 'existing modifications that might trigger validation',
|
|
103
|
-
})
|
|
104
|
-
|
|
105
|
-
const result = await sut.process(TODO_WRITE_HOOK_DATA)
|
|
106
|
-
|
|
107
|
-
expect(sut.validatorHasBeenCalled()).toBe(false)
|
|
108
|
-
expect(result).toEqual(defaultResult)
|
|
109
|
-
})
|
|
110
|
-
|
|
111
|
-
it('should handle hook data with invalid schema gracefully', async () => {
|
|
112
|
-
// Invalid hook data that doesn't match either SimpleHookDataSchema or FullHookEventSchema
|
|
113
|
-
const invalidHookData = {
|
|
114
|
-
// This doesn't match FullHookEventSchema (missing required fields)
|
|
115
|
-
// and has invalid types for SimpleHookDataSchema
|
|
116
|
-
tool_name: 123, // Should be string
|
|
117
|
-
tool_input: "not an object", // Should be object
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
const result = await sut.process(invalidHookData)
|
|
121
|
-
|
|
122
|
-
// Should return default result without calling validator
|
|
123
|
-
expect(sut.validatorHasBeenCalled()).toBe(false)
|
|
124
|
-
expect(result).toEqual(defaultResult)
|
|
125
|
-
})
|
|
126
|
-
|
|
127
|
-
describe('PostToolUse hook handling', () => {
|
|
128
|
-
it('should delegate to handlePostToolLint for PostToolUse events', async () => {
|
|
129
|
-
const postToolUseHook = {
|
|
130
|
-
...EDIT_HOOK_DATA,
|
|
131
|
-
hook_event_name: 'PostToolUse',
|
|
132
|
-
tool_output: { success: true }
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
const result = await sut.process(postToolUseHook)
|
|
136
|
-
|
|
137
|
-
// Should not call the validator
|
|
138
|
-
expect(sut.validatorHasBeenCalled()).toBe(false)
|
|
139
|
-
// Result depends on lint state, but should return a valid result
|
|
140
|
-
expect(result).toHaveProperty('decision')
|
|
141
|
-
expect(result).toHaveProperty('reason')
|
|
142
|
-
})
|
|
143
|
-
})
|
|
144
|
-
|
|
145
|
-
describe('Ignore patterns filtering', () => {
|
|
146
|
-
it('skips validation when using default ignore patterns', async () => {
|
|
147
|
-
for (const pattern of GuardManager.DEFAULT_IGNORE_PATTERNS) {
|
|
148
|
-
// Convert pattern to file path (e.g., '*.md' -> '/path/to/file.md')
|
|
149
|
-
const filePath = pattern.replaceAll('*', '/path/to/file')
|
|
150
|
-
|
|
151
|
-
const nonCodeFileData = {
|
|
152
|
-
...EDIT_HOOK_DATA,
|
|
153
|
-
tool_input: {
|
|
154
|
-
file_path: filePath,
|
|
155
|
-
old_string: 'old content',
|
|
156
|
-
new_string: 'new content'
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
const result = await sut.process(nonCodeFileData)
|
|
161
|
-
|
|
162
|
-
expect(sut.validatorHasBeenCalled()).toBe(false)
|
|
163
|
-
expect(result).toEqual(defaultResult)
|
|
164
|
-
}
|
|
165
|
-
})
|
|
166
|
-
|
|
167
|
-
it.each([
|
|
168
|
-
{
|
|
169
|
-
description: 'files matching custom extensions',
|
|
170
|
-
filePath: 'file.custom',
|
|
171
|
-
},
|
|
172
|
-
{
|
|
173
|
-
description: 'files in ignored directories',
|
|
174
|
-
filePath: 'build/output.js',
|
|
175
|
-
},
|
|
176
|
-
{
|
|
177
|
-
description: 'files matching glob patterns',
|
|
178
|
-
filePath: 'src/api/schema.generated.ts',
|
|
179
|
-
},
|
|
180
|
-
])('skips validation when using custom ignore patterns for $description', async ({ filePath }) => {
|
|
181
|
-
// Set up custom ignore patterns
|
|
182
|
-
const customPatterns = ['*.custom', 'build/**', '**/*.generated.ts']
|
|
183
|
-
await sut.storage.saveConfig(JSON.stringify({
|
|
184
|
-
guardEnabled: true,
|
|
185
|
-
ignorePatterns: customPatterns
|
|
186
|
-
}))
|
|
187
|
-
|
|
188
|
-
const fileData = {
|
|
189
|
-
...EDIT_HOOK_DATA,
|
|
190
|
-
tool_input: {
|
|
191
|
-
file_path: filePath,
|
|
192
|
-
old_string: 'old content',
|
|
193
|
-
new_string: 'new content'
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
const result = await sut.process(fileData)
|
|
198
|
-
|
|
199
|
-
expect(sut.validatorHasBeenCalled()).toBe(false)
|
|
200
|
-
expect(result).toEqual(defaultResult)
|
|
201
|
-
})
|
|
202
|
-
})
|
|
203
|
-
|
|
204
|
-
describe('PreToolUse lint notification', () => {
|
|
205
|
-
it('should block when tests pass, lint issues exist, and not yet notified', async () => {
|
|
206
|
-
// Setup: passing tests
|
|
207
|
-
await sut.populateStorage({
|
|
208
|
-
test: JSON.stringify(testData.passingTestResults())
|
|
209
|
-
})
|
|
210
|
-
|
|
211
|
-
// Setup: lint issues with notification flag false
|
|
212
|
-
await sut.storage.saveLint(JSON.stringify(
|
|
213
|
-
testData.lintDataWithError({
|
|
214
|
-
hasNotifiedAboutLintIssues: false
|
|
215
|
-
})
|
|
216
|
-
))
|
|
217
|
-
|
|
218
|
-
const result = await sut.process(EDIT_HOOK_DATA)
|
|
219
|
-
|
|
220
|
-
expect(result.decision).toBe('block')
|
|
221
|
-
expect(result.reason).toContain('Code quality issues detected')
|
|
222
|
-
// Should not call the main validator
|
|
223
|
-
expect(sut.validatorHasBeenCalled()).toBe(false)
|
|
224
|
-
})
|
|
225
|
-
|
|
226
|
-
it('should not block when tests are failing (red phase)', async () => {
|
|
227
|
-
// Setup: failing tests
|
|
228
|
-
await sut.populateStorage({
|
|
229
|
-
test: JSON.stringify(testData.failedTestResults())
|
|
230
|
-
})
|
|
231
|
-
|
|
232
|
-
// Setup: lint issues with notification flag false
|
|
233
|
-
await sut.storage.saveLint(JSON.stringify(
|
|
234
|
-
testData.lintDataWithError({
|
|
235
|
-
hasNotifiedAboutLintIssues: false
|
|
236
|
-
})
|
|
237
|
-
))
|
|
238
|
-
|
|
239
|
-
const result = await sut.process(EDIT_HOOK_DATA)
|
|
240
|
-
|
|
241
|
-
// Should proceed to normal validation
|
|
242
|
-
expect(result).toEqual(BLOCK_RESULT)
|
|
243
|
-
expect(sut.validatorHasBeenCalled()).toBe(true)
|
|
244
|
-
})
|
|
245
|
-
|
|
246
|
-
it('should not block when no lint issues exist', async () => {
|
|
247
|
-
// Setup: passing tests
|
|
248
|
-
await sut.populateStorage({
|
|
249
|
-
test: JSON.stringify(testData.passingTestResults())
|
|
250
|
-
})
|
|
251
|
-
|
|
252
|
-
// Setup: no lint issues
|
|
253
|
-
await sut.storage.saveLint(JSON.stringify(
|
|
254
|
-
testData.lintDataWithoutErrors({
|
|
255
|
-
hasNotifiedAboutLintIssues: false
|
|
256
|
-
})
|
|
257
|
-
))
|
|
258
|
-
|
|
259
|
-
const result = await sut.process(EDIT_HOOK_DATA)
|
|
260
|
-
|
|
261
|
-
// Should proceed to normal validation
|
|
262
|
-
expect(result).toEqual(BLOCK_RESULT)
|
|
263
|
-
expect(sut.validatorHasBeenCalled()).toBe(true)
|
|
264
|
-
})
|
|
265
|
-
|
|
266
|
-
it('should not block when already notified', async () => {
|
|
267
|
-
// Setup: passing tests
|
|
268
|
-
await sut.populateStorage({
|
|
269
|
-
test: JSON.stringify(testData.passingTestResults())
|
|
270
|
-
})
|
|
271
|
-
|
|
272
|
-
// Setup: lint issues with notification flag true
|
|
273
|
-
await sut.storage.saveLint(JSON.stringify(
|
|
274
|
-
testData.lintDataWithError({
|
|
275
|
-
hasNotifiedAboutLintIssues: true
|
|
276
|
-
})
|
|
277
|
-
))
|
|
278
|
-
|
|
279
|
-
const result = await sut.process(EDIT_HOOK_DATA)
|
|
280
|
-
|
|
281
|
-
// Should proceed to normal validation
|
|
282
|
-
expect(result).toEqual(BLOCK_RESULT)
|
|
283
|
-
expect(sut.validatorHasBeenCalled()).toBe(true)
|
|
284
|
-
})
|
|
285
|
-
|
|
286
|
-
it('should set notification flag after blocking', async () => {
|
|
287
|
-
// Setup: passing tests
|
|
288
|
-
await sut.populateStorage({
|
|
289
|
-
test: JSON.stringify(testData.passingTestResults())
|
|
290
|
-
})
|
|
291
|
-
|
|
292
|
-
// Setup: lint issues with notification flag false
|
|
293
|
-
await sut.storage.saveLint(JSON.stringify(
|
|
294
|
-
testData.lintDataWithError({
|
|
295
|
-
hasNotifiedAboutLintIssues: false
|
|
296
|
-
})
|
|
297
|
-
))
|
|
298
|
-
|
|
299
|
-
await sut.process(EDIT_HOOK_DATA)
|
|
300
|
-
|
|
301
|
-
// Check that the flag was updated
|
|
302
|
-
const savedLint = await sut.storage.getLint()
|
|
303
|
-
const parsedLint = JSON.parse(savedLint!)
|
|
304
|
-
expect(parsedLint.hasNotifiedAboutLintIssues).toBe(true)
|
|
305
|
-
})
|
|
306
|
-
})
|
|
307
|
-
|
|
308
|
-
describe('SessionStart handling', () => {
|
|
309
|
-
let result: ValidationResult
|
|
310
|
-
|
|
311
|
-
beforeEach(async () => {
|
|
312
|
-
// Populate storage with data
|
|
313
|
-
await sut.populateStorage({
|
|
314
|
-
test: JSON.stringify(testData.passingTestResults()),
|
|
315
|
-
todo: JSON.stringify(testData.todoWriteOperation()),
|
|
316
|
-
modifications: JSON.stringify(testData.editOperation()),
|
|
317
|
-
lint: JSON.stringify(testData.lintDataWithoutErrors()),
|
|
318
|
-
config: JSON.stringify({ guardEnabled: true })
|
|
319
|
-
})
|
|
320
|
-
|
|
321
|
-
const sessionStartData = testData.sessionStart()
|
|
322
|
-
result = await sut.process(sessionStartData)
|
|
323
|
-
})
|
|
324
|
-
|
|
325
|
-
it('should clear transient data when SessionStart event is received', async () => {
|
|
326
|
-
// Verify transient data is cleared
|
|
327
|
-
expect(await sut.getTest()).toBeNull()
|
|
328
|
-
expect(await sut.getTodo()).toBeNull()
|
|
329
|
-
expect(await sut.getModifications()).toBeNull()
|
|
330
|
-
expect(await sut.getLint()).toBeNull()
|
|
331
|
-
})
|
|
332
|
-
|
|
333
|
-
it('should preserve config data when SessionStart event is received', async () => {
|
|
334
|
-
expect(await sut.getConfig()).toBe(JSON.stringify({ guardEnabled: true }))
|
|
335
|
-
})
|
|
336
|
-
|
|
337
|
-
it('should return defaultResult when SessionStart event is processed', () => {
|
|
338
|
-
expect(result).toEqual(defaultResult)
|
|
339
|
-
})
|
|
340
|
-
})
|
|
341
|
-
|
|
342
|
-
describe('UserPromptHandler integration', () => {
|
|
343
|
-
it('should enable TDD Guard when user sends "tdd-guard on"', async () => {
|
|
344
|
-
const storage = new MemoryStorage()
|
|
345
|
-
const guardManager = new GuardManager(storage)
|
|
346
|
-
await guardManager.disable() // Ensure it starts disabled
|
|
347
|
-
const userPromptHandler = new UserPromptHandler(guardManager)
|
|
348
|
-
const userPromptData = testData.userPromptSubmit({ prompt: 'tdd-guard on' })
|
|
349
|
-
|
|
350
|
-
await processHookData(JSON.stringify(userPromptData), {
|
|
351
|
-
userPromptHandler
|
|
352
|
-
})
|
|
353
|
-
|
|
354
|
-
expect(await guardManager.isEnabled()).toBe(true)
|
|
355
|
-
})
|
|
356
|
-
|
|
357
|
-
it('should disable TDD Guard when user sends "tdd-guard off"', async () => {
|
|
358
|
-
const storage = new MemoryStorage()
|
|
359
|
-
const guardManager = new GuardManager(storage)
|
|
360
|
-
await guardManager.enable() // Ensure it starts enabled
|
|
361
|
-
const userPromptHandler = new UserPromptHandler(guardManager)
|
|
362
|
-
const userPromptData = testData.userPromptSubmit({ prompt: 'tdd-guard off' })
|
|
363
|
-
|
|
364
|
-
await processHookData(JSON.stringify(userPromptData), {
|
|
365
|
-
userPromptHandler
|
|
366
|
-
})
|
|
367
|
-
|
|
368
|
-
expect(await guardManager.isEnabled()).toBe(false)
|
|
369
|
-
})
|
|
370
|
-
|
|
371
|
-
it('should not proceed with validation when TDD Guard is disabled', async () => {
|
|
372
|
-
const storage = new MemoryStorage()
|
|
373
|
-
const guardManager = new GuardManager(storage)
|
|
374
|
-
await guardManager.disable() // Ensure guard is disabled
|
|
375
|
-
const userPromptHandler = new UserPromptHandler(guardManager)
|
|
376
|
-
const mockValidator = vi.fn()
|
|
377
|
-
|
|
378
|
-
// Try to process an edit operation
|
|
379
|
-
const editData = testData.editOperation()
|
|
380
|
-
|
|
381
|
-
const result = await processHookData(JSON.stringify(editData), {
|
|
382
|
-
storage,
|
|
383
|
-
userPromptHandler,
|
|
384
|
-
validator: mockValidator
|
|
385
|
-
})
|
|
386
|
-
|
|
387
|
-
expect(mockValidator).not.toHaveBeenCalled()
|
|
388
|
-
expect(result).toEqual(defaultResult)
|
|
389
|
-
})
|
|
390
|
-
|
|
391
|
-
it('should proceed with validation when TDD Guard is enabled', async () => {
|
|
392
|
-
const storage = new MemoryStorage()
|
|
393
|
-
const guardManager = new GuardManager(storage)
|
|
394
|
-
await guardManager.enable() // Ensure guard is enabled
|
|
395
|
-
const userPromptHandler = new UserPromptHandler(guardManager)
|
|
396
|
-
const mockValidator = vi.fn().mockResolvedValue(BLOCK_RESULT)
|
|
397
|
-
|
|
398
|
-
// Try to process an edit operation
|
|
399
|
-
const editData = testData.editOperation()
|
|
400
|
-
|
|
401
|
-
const result = await processHookData(JSON.stringify(editData), {
|
|
402
|
-
storage,
|
|
403
|
-
userPromptHandler,
|
|
404
|
-
validator: mockValidator
|
|
405
|
-
})
|
|
406
|
-
|
|
407
|
-
expect(mockValidator).toHaveBeenCalled()
|
|
408
|
-
expect(result).toEqual(BLOCK_RESULT)
|
|
409
|
-
})
|
|
410
|
-
})
|
|
411
|
-
})
|
|
412
|
-
|
|
413
|
-
// Test setup helper
|
|
414
|
-
function createTestProcessor() {
|
|
415
|
-
const storage = new MemoryStorage()
|
|
416
|
-
const mockValidator = vi.fn().mockResolvedValue(BLOCK_RESULT)
|
|
417
|
-
|
|
418
|
-
// Create a GuardManager and UserPromptHandler that defaults to enabled for tests
|
|
419
|
-
const guardManager = new GuardManager(storage)
|
|
420
|
-
const userPromptHandler = new UserPromptHandler(guardManager)
|
|
421
|
-
|
|
422
|
-
// Helper to process hook data
|
|
423
|
-
const process = async (hookData: unknown): Promise<ValidationResult> => {
|
|
424
|
-
// Ensure TDD Guard is enabled for tests unless explicitly disabled
|
|
425
|
-
await guardManager.enable()
|
|
426
|
-
|
|
427
|
-
return processHookData(JSON.stringify(hookData), {
|
|
428
|
-
storage,
|
|
429
|
-
validator: mockValidator,
|
|
430
|
-
userPromptHandler
|
|
431
|
-
})
|
|
432
|
-
}
|
|
433
|
-
|
|
434
|
-
// Pre-populate storage helper
|
|
435
|
-
const populateStorage = async (data: {
|
|
436
|
-
modifications?: string;
|
|
437
|
-
test?: string;
|
|
438
|
-
todo?: string;
|
|
439
|
-
lint?: string;
|
|
440
|
-
config?: string;
|
|
441
|
-
}): Promise<void> => {
|
|
442
|
-
if (data.modifications) await storage.saveModifications(data.modifications)
|
|
443
|
-
if (data.test) await storage.saveTest(data.test)
|
|
444
|
-
if (data.todo) await storage.saveTodo(data.todo)
|
|
445
|
-
if (data.lint) await storage.saveLint(data.lint)
|
|
446
|
-
if (data.config) await storage.saveConfig(data.config)
|
|
447
|
-
}
|
|
448
|
-
|
|
449
|
-
return {
|
|
450
|
-
storage,
|
|
451
|
-
process,
|
|
452
|
-
populateStorage,
|
|
453
|
-
|
|
454
|
-
// Storage accessors
|
|
455
|
-
getModifications: (): Promise<string | null> => storage.getModifications(),
|
|
456
|
-
getTest: (): Promise<string | null> => storage.getTest(),
|
|
457
|
-
getTodo: (): Promise<string | null> => storage.getTodo(),
|
|
458
|
-
getLint: (): Promise<string | null> => storage.getLint(),
|
|
459
|
-
getConfig: (): Promise<string | null> => storage.getConfig(),
|
|
460
|
-
|
|
461
|
-
// Validator checks
|
|
462
|
-
validatorHasBeenCalled: (): boolean => mockValidator.mock.calls.length > 0,
|
|
463
|
-
getValidatorCallArgs: (): Context | null => mockValidator.mock.calls[0]?.[0] ?? null,
|
|
464
|
-
}
|
|
465
|
-
}
|
|
@@ -1,203 +0,0 @@
|
|
|
1
|
-
import { buildContext } from '../cli/buildContext'
|
|
2
|
-
import { HookData, HookEvents } from './HookEvents'
|
|
3
|
-
import { PostToolLintHandler } from './postToolLint'
|
|
4
|
-
import { detectFileType } from './fileTypeDetection'
|
|
5
|
-
import { LinterProvider } from '../providers/LinterProvider'
|
|
6
|
-
import { UserPromptHandler } from './userPromptHandler'
|
|
7
|
-
import { SessionHandler } from './sessionHandler'
|
|
8
|
-
import { GuardManager } from '../guard/GuardManager'
|
|
9
|
-
import { Storage } from '../storage/Storage'
|
|
10
|
-
import { FileStorage } from '../storage/FileStorage'
|
|
11
|
-
import { ValidationResult } from '../contracts/types/ValidationResult'
|
|
12
|
-
import { Context } from '../contracts/types/Context'
|
|
13
|
-
import { HookDataSchema, isTodoWriteOperation, ToolOperationSchema } from '../contracts/schemas/toolSchemas'
|
|
14
|
-
import { PytestResultSchema } from '../contracts/schemas/pytestSchemas'
|
|
15
|
-
import { isTestPassing, TestResultSchema } from '../contracts/schemas/reporterSchemas'
|
|
16
|
-
import { LintDataSchema } from '../contracts/schemas/lintSchemas'
|
|
17
|
-
|
|
18
|
-
export interface ProcessHookDataDeps {
|
|
19
|
-
storage?: Storage
|
|
20
|
-
validator?: (context: Context) => Promise<ValidationResult>
|
|
21
|
-
userPromptHandler?: UserPromptHandler
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export const defaultResult: ValidationResult = {
|
|
25
|
-
decision: undefined,
|
|
26
|
-
reason: '',
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
function extractFilePath(parsedData: unknown): string | null {
|
|
30
|
-
if (!parsedData || typeof parsedData !== 'object') {
|
|
31
|
-
return null
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
const data = parsedData as Record<string, unknown>
|
|
35
|
-
const toolInput = data.tool_input
|
|
36
|
-
|
|
37
|
-
if (!toolInput || typeof toolInput !== 'object' || !('file_path' in toolInput)) {
|
|
38
|
-
return null
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
const filePath = (toolInput as Record<string, unknown>).file_path
|
|
42
|
-
if (typeof filePath !== 'string') {
|
|
43
|
-
return null
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
return filePath
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
export async function processHookData(
|
|
50
|
-
inputData: string,
|
|
51
|
-
deps: ProcessHookDataDeps = {}
|
|
52
|
-
): Promise<ValidationResult> {
|
|
53
|
-
const parsedData = JSON.parse(inputData)
|
|
54
|
-
|
|
55
|
-
// Initialize dependencies
|
|
56
|
-
const storage = deps.storage ?? new FileStorage()
|
|
57
|
-
const guardManager = new GuardManager(storage)
|
|
58
|
-
const userPromptHandler = deps.userPromptHandler ?? new UserPromptHandler(guardManager)
|
|
59
|
-
|
|
60
|
-
// Skip validation for ignored files based on patterns
|
|
61
|
-
const filePath = extractFilePath(parsedData)
|
|
62
|
-
if (filePath && await guardManager.shouldIgnoreFile(filePath)) {
|
|
63
|
-
return defaultResult
|
|
64
|
-
}
|
|
65
|
-
const sessionHandler = new SessionHandler(storage)
|
|
66
|
-
|
|
67
|
-
// Process SessionStart events
|
|
68
|
-
if (parsedData.hook_event_name === 'SessionStart') {
|
|
69
|
-
await sessionHandler.processSessionStart(inputData)
|
|
70
|
-
return defaultResult
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
// Process user commands
|
|
74
|
-
const stateResult = await userPromptHandler.processUserCommand(inputData)
|
|
75
|
-
if (stateResult) {
|
|
76
|
-
return stateResult
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
// Check if guard is disabled and return early if so
|
|
80
|
-
const disabledResult = await userPromptHandler.getDisabledResult()
|
|
81
|
-
if (disabledResult) {
|
|
82
|
-
return disabledResult
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
// Create lintHandler with linter from provider
|
|
86
|
-
const linterProvider = new LinterProvider()
|
|
87
|
-
const linter = linterProvider.getLinter()
|
|
88
|
-
const lintHandler = new PostToolLintHandler(storage, linter)
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
const hookResult = HookDataSchema.safeParse(parsedData)
|
|
92
|
-
if (!hookResult.success) {
|
|
93
|
-
return defaultResult
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
await processHookEvent(parsedData, storage)
|
|
97
|
-
|
|
98
|
-
// Check if this is a PostToolUse event
|
|
99
|
-
if (hookResult.data.hook_event_name === 'PostToolUse') {
|
|
100
|
-
return await lintHandler.handle(inputData)
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
if (shouldSkipValidation(hookResult.data)) {
|
|
104
|
-
return defaultResult
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
// For PreToolUse, check if we should notify about lint issues
|
|
108
|
-
if (hookResult.data.hook_event_name === 'PreToolUse') {
|
|
109
|
-
const lintNotification = await checkLintNotification(storage, hookResult.data)
|
|
110
|
-
if (lintNotification.decision === 'block') {
|
|
111
|
-
return lintNotification
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
return await performValidation(deps)
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
async function processHookEvent(parsedData: unknown, storage?: Storage): Promise<void> {
|
|
119
|
-
if (storage) {
|
|
120
|
-
const hookEvents = new HookEvents(storage)
|
|
121
|
-
await hookEvents.processEvent(parsedData)
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
function shouldSkipValidation(hookData: HookData): boolean {
|
|
126
|
-
const operationResult = ToolOperationSchema.safeParse({
|
|
127
|
-
...hookData,
|
|
128
|
-
tool_input: hookData.tool_input,
|
|
129
|
-
})
|
|
130
|
-
|
|
131
|
-
return !operationResult.success || isTodoWriteOperation(operationResult.data)
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
async function performValidation(deps: ProcessHookDataDeps): Promise<ValidationResult> {
|
|
135
|
-
if (deps.validator && deps.storage) {
|
|
136
|
-
const context = await buildContext(deps.storage)
|
|
137
|
-
return await deps.validator(context)
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
return defaultResult
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
async function checkLintNotification(storage: Storage, hookData: HookData): Promise<ValidationResult> {
|
|
144
|
-
// Get test results to check if tests are passing
|
|
145
|
-
let testsPassing = false
|
|
146
|
-
try {
|
|
147
|
-
const testStr = await storage.getTest()
|
|
148
|
-
if (testStr) {
|
|
149
|
-
const fileType = detectFileType(hookData)
|
|
150
|
-
const testResult = fileType === 'python'
|
|
151
|
-
? PytestResultSchema.safeParse(JSON.parse(testStr))
|
|
152
|
-
: TestResultSchema.safeParse(JSON.parse(testStr))
|
|
153
|
-
if (testResult.success) {
|
|
154
|
-
testsPassing = isTestPassing(testResult.data)
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
} catch {
|
|
158
|
-
testsPassing = false
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
// Only proceed if tests are passing
|
|
162
|
-
if (!testsPassing) {
|
|
163
|
-
return defaultResult
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
// Get lint data
|
|
167
|
-
let lintData
|
|
168
|
-
try {
|
|
169
|
-
const lintStr = await storage.getLint()
|
|
170
|
-
if (lintStr) {
|
|
171
|
-
lintData = LintDataSchema.parse(JSON.parse(lintStr))
|
|
172
|
-
}
|
|
173
|
-
} catch {
|
|
174
|
-
return defaultResult
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
// Only proceed if lint data exists
|
|
178
|
-
if (!lintData) {
|
|
179
|
-
return defaultResult
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
const hasIssues = lintData.errorCount > 0 || lintData.warningCount > 0
|
|
183
|
-
|
|
184
|
-
// Block if:
|
|
185
|
-
// 1. Tests are passing (already checked)
|
|
186
|
-
// 2. There are lint issues
|
|
187
|
-
// 3. hasNotifiedAboutLintIssues is false (not yet notified)
|
|
188
|
-
if (hasIssues && !lintData.hasNotifiedAboutLintIssues) {
|
|
189
|
-
// Update the notification flag and save
|
|
190
|
-
const updatedLintData = {
|
|
191
|
-
...lintData,
|
|
192
|
-
hasNotifiedAboutLintIssues: true
|
|
193
|
-
}
|
|
194
|
-
await storage.saveLint(JSON.stringify(updatedLintData))
|
|
195
|
-
|
|
196
|
-
return {
|
|
197
|
-
decision: 'block',
|
|
198
|
-
reason: 'Code quality issues detected. You need to fix those first before making any other changes. Remember to exercise system thinking and design awareness to ensure continuous architectural improvements. Consider: design patterns, SOLID principles, DRY, types and interfaces, and architectural improvements. Apply equally to implementation and test code. Use test data factories, helpers, and beforeEach to better organize tests.'
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
return defaultResult
|
|
203
|
-
}
|