xtrm-tools 2.2.0 → 2.4.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/README.md +127 -104
- package/cli/dist/index.cjs +1078 -970
- package/cli/dist/index.cjs.map +1 -1
- package/cli/package.json +1 -1
- package/config/pi/extensions/beads.ts +33 -5
- package/config/pi/extensions/service-skills.ts +17 -9
- package/config/pi/install-schema.json +2 -1
- 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,327 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, vi } from 'vitest'
|
|
2
|
-
import { handlePostToolLint, PostToolLintHandler, DEFAULT_RESULT } from './postToolLint'
|
|
3
|
-
import { testData } from '@testUtils'
|
|
4
|
-
import { Linter } from '../linters/Linter'
|
|
5
|
-
import { MemoryStorage } from '../storage/MemoryStorage'
|
|
6
|
-
import { LintData, LintResult } from '../contracts/schemas/lintSchemas'
|
|
7
|
-
import { HookData } from '../contracts/schemas/toolSchemas'
|
|
8
|
-
import { TestResult } from '../contracts/schemas/reporterSchemas'
|
|
9
|
-
import { ValidationResult } from '../contracts/types/ValidationResult'
|
|
10
|
-
|
|
11
|
-
describe('postToolLint', () => {
|
|
12
|
-
describe('PostToolLintHandler', () => {
|
|
13
|
-
it('uses null linter by default', () => {
|
|
14
|
-
const storage = new MemoryStorage()
|
|
15
|
-
const handler = new PostToolLintHandler(storage)
|
|
16
|
-
expect(handler['linter']).toBeNull()
|
|
17
|
-
})
|
|
18
|
-
|
|
19
|
-
it('accepts Linter instance in constructor', () => {
|
|
20
|
-
const storage = new MemoryStorage()
|
|
21
|
-
const testLinter = new TestLinter()
|
|
22
|
-
const handler = new PostToolLintHandler(storage, testLinter)
|
|
23
|
-
expect(handler['linter']).toBe(testLinter)
|
|
24
|
-
})
|
|
25
|
-
|
|
26
|
-
it('accepts null linter in constructor', () => {
|
|
27
|
-
const storage = new MemoryStorage()
|
|
28
|
-
const handler = new PostToolLintHandler(storage, null)
|
|
29
|
-
expect(handler['linter']).toBeNull()
|
|
30
|
-
})
|
|
31
|
-
})
|
|
32
|
-
|
|
33
|
-
it('should return default result for PreToolUse hooks', async () => {
|
|
34
|
-
const { result, parsedLint } = await runLintAndGetResult({
|
|
35
|
-
lintResult: testData.lintResultWithoutErrors(),
|
|
36
|
-
operation: { hook_event_name: 'PreToolUse' }
|
|
37
|
-
})
|
|
38
|
-
|
|
39
|
-
expect(result).toEqual(DEFAULT_RESULT)
|
|
40
|
-
expect(parsedLint).toBeNull()
|
|
41
|
-
})
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
describe('when processing tool operations', () => {
|
|
45
|
-
it('runs lint and saves results for Edit operations', async () => {
|
|
46
|
-
const { parsedLint } = await runLintAndGetResult()
|
|
47
|
-
|
|
48
|
-
expect(parsedLint).not.toBeNull()
|
|
49
|
-
expect(parsedLint!.files).toEqual([])
|
|
50
|
-
expect(parsedLint!.errorCount).toBe(0)
|
|
51
|
-
expect(parsedLint!.warningCount).toBe(0)
|
|
52
|
-
})
|
|
53
|
-
|
|
54
|
-
it('runs lint and saves results for Write operations', async () => {
|
|
55
|
-
const { parsedLint } = await runLintAndGetResult({
|
|
56
|
-
operation: { tool_name: 'Write' }
|
|
57
|
-
})
|
|
58
|
-
|
|
59
|
-
expect(parsedLint).not.toBeNull()
|
|
60
|
-
expect(parsedLint!.hasNotifiedAboutLintIssues).toBe(false)
|
|
61
|
-
})
|
|
62
|
-
|
|
63
|
-
it('runs lint and saves results for MultiEdit operations', async () => {
|
|
64
|
-
const { parsedLint } = await runLintAndGetResult({
|
|
65
|
-
operation: { tool_name: 'MultiEdit' }
|
|
66
|
-
})
|
|
67
|
-
|
|
68
|
-
expect(parsedLint).not.toBeNull()
|
|
69
|
-
expect(parsedLint!.hasNotifiedAboutLintIssues).toBe(false)
|
|
70
|
-
})
|
|
71
|
-
|
|
72
|
-
it('returns default result and does not save lint data when operation has no file paths', async () => {
|
|
73
|
-
const { result, parsedLint } = await runLintAndGetResult({
|
|
74
|
-
operation: {
|
|
75
|
-
tool_name: 'TodoWrite',
|
|
76
|
-
tool_input: { todos: [] }
|
|
77
|
-
}
|
|
78
|
-
})
|
|
79
|
-
|
|
80
|
-
expect(result).toEqual(DEFAULT_RESULT)
|
|
81
|
-
expect(parsedLint).toBeNull()
|
|
82
|
-
})
|
|
83
|
-
|
|
84
|
-
it('processes MultiEdit operations even when some edits lack file_path', async () => {
|
|
85
|
-
const { parsedLint } = await runLintAndGetResult({
|
|
86
|
-
operation: {
|
|
87
|
-
tool_name: 'MultiEdit',
|
|
88
|
-
tool_input: {
|
|
89
|
-
edits: [
|
|
90
|
-
{ old_string: 'a', new_string: 'b' }, // missing file_path
|
|
91
|
-
{ file_path: '/test/file.ts', old_string: 'c', new_string: 'd' }
|
|
92
|
-
]
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
})
|
|
96
|
-
|
|
97
|
-
// Should still process the edit that has a file_path
|
|
98
|
-
expect(parsedLint).not.toBeNull()
|
|
99
|
-
expect(parsedLint!.hasNotifiedAboutLintIssues).toBe(false)
|
|
100
|
-
})
|
|
101
|
-
})
|
|
102
|
-
|
|
103
|
-
it('should always save lint data to storage', async () => {
|
|
104
|
-
const lintData = testData.lintResultWithError()
|
|
105
|
-
|
|
106
|
-
const { parsedLint } = await runLintAndGetResult({ lintResult: lintData })
|
|
107
|
-
|
|
108
|
-
expect(parsedLint!.issues).toEqual(lintData.issues)
|
|
109
|
-
expect(parsedLint!.errorCount).toBe(1)
|
|
110
|
-
})
|
|
111
|
-
|
|
112
|
-
describe('when lint issues exist', () => {
|
|
113
|
-
describe('and notification flag is false', () => {
|
|
114
|
-
it('does not block the operation', async () => {
|
|
115
|
-
const { result } = await runLintAndGetResult({
|
|
116
|
-
lintResult: testData.lintResultWithError()
|
|
117
|
-
})
|
|
118
|
-
|
|
119
|
-
expect(result).toEqual(DEFAULT_RESULT)
|
|
120
|
-
})
|
|
121
|
-
|
|
122
|
-
it('saves hasNotifiedAboutLintIssues as false', async () => {
|
|
123
|
-
const { parsedLint } = await runLintAndGetResult({
|
|
124
|
-
lintResult: testData.lintResultWithError()
|
|
125
|
-
})
|
|
126
|
-
|
|
127
|
-
expect(parsedLint!.hasNotifiedAboutLintIssues).toBe(false)
|
|
128
|
-
})
|
|
129
|
-
})
|
|
130
|
-
|
|
131
|
-
describe('and notification flag is true', () => {
|
|
132
|
-
it('blocks with detailed lint errors', async () => {
|
|
133
|
-
const { result } = await runLintAndGetResult({
|
|
134
|
-
lintResult: testData.lintResultWithError(),
|
|
135
|
-
initialLintData: testData.lintDataWithNotificationFlag()
|
|
136
|
-
})
|
|
137
|
-
|
|
138
|
-
expect(result.decision).toBe('block')
|
|
139
|
-
expect(result.reason).toContain('Lint issues detected:')
|
|
140
|
-
})
|
|
141
|
-
|
|
142
|
-
it('preserves the notification flag', async () => {
|
|
143
|
-
const { parsedLint } = await runLintAndGetResult({
|
|
144
|
-
lintResult: testData.lintResultWithError(),
|
|
145
|
-
initialLintData: testData.lintDataWithNotificationFlag()
|
|
146
|
-
})
|
|
147
|
-
|
|
148
|
-
expect(parsedLint!.hasNotifiedAboutLintIssues).toBe(true)
|
|
149
|
-
})
|
|
150
|
-
})
|
|
151
|
-
|
|
152
|
-
describe('and tests are failing (red phase)', () => {
|
|
153
|
-
it('saves lint data without blocking', async () => {
|
|
154
|
-
const lintData = testData.lintResultWithError()
|
|
155
|
-
|
|
156
|
-
const { parsedLint } = await runLintAndGetResult({
|
|
157
|
-
lintResult: lintData,
|
|
158
|
-
initialTestData: testData.failedTestResults()
|
|
159
|
-
})
|
|
160
|
-
|
|
161
|
-
expect(parsedLint!.issues).toEqual(lintData.issues)
|
|
162
|
-
expect(parsedLint!.errorCount).toBe(1)
|
|
163
|
-
expect(parsedLint!.hasNotifiedAboutLintIssues).toBe(false)
|
|
164
|
-
})
|
|
165
|
-
})
|
|
166
|
-
})
|
|
167
|
-
|
|
168
|
-
describe('when storage errors occur', () => {
|
|
169
|
-
it('should treat storage read errors as no stored lint data', async () => {
|
|
170
|
-
// This test requires manual setup to spy on storage
|
|
171
|
-
const storage = new MemoryStorage()
|
|
172
|
-
const testLinter = new TestLinter(testData.lintResultWithoutErrors())
|
|
173
|
-
|
|
174
|
-
// Make storage.getLint() throw an error only on the first call
|
|
175
|
-
const getLintSpy = vi.spyOn(storage, 'getLint')
|
|
176
|
-
getLintSpy.mockRejectedValueOnce(new Error('Storage error'))
|
|
177
|
-
|
|
178
|
-
const hookData = JSON.stringify({
|
|
179
|
-
...testData.editOperation(),
|
|
180
|
-
hook_event_name: 'PostToolUse'
|
|
181
|
-
})
|
|
182
|
-
|
|
183
|
-
const result = await handlePostToolLint(hookData, storage, testLinter)
|
|
184
|
-
|
|
185
|
-
// Should not block (treats error as no stored data)
|
|
186
|
-
expect(result).toEqual(DEFAULT_RESULT)
|
|
187
|
-
|
|
188
|
-
// Reset the spy and check that storage now contains the new lint data
|
|
189
|
-
getLintSpy.mockRestore()
|
|
190
|
-
const savedLint = await storage.getLint()
|
|
191
|
-
expect(savedLint).toBeTruthy()
|
|
192
|
-
const parsedLint = JSON.parse(savedLint!)
|
|
193
|
-
expect(parsedLint.hasNotifiedAboutLintIssues).toBe(false)
|
|
194
|
-
expect(parsedLint.issues).toEqual([])
|
|
195
|
-
})
|
|
196
|
-
})
|
|
197
|
-
|
|
198
|
-
describe('when linter is null', () => {
|
|
199
|
-
it('skips linting and returns default result', async () => {
|
|
200
|
-
const storage = new MemoryStorage()
|
|
201
|
-
const handler = new PostToolLintHandler(storage, null)
|
|
202
|
-
const hookData = {
|
|
203
|
-
...testData.editOperation(),
|
|
204
|
-
hook_event_name: 'PostToolUse'
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
const result = await handler.handle(JSON.stringify(hookData))
|
|
208
|
-
|
|
209
|
-
expect(result).toEqual(DEFAULT_RESULT)
|
|
210
|
-
const savedLint = await storage.getLint()
|
|
211
|
-
expect(savedLint).toBeNull()
|
|
212
|
-
})
|
|
213
|
-
})
|
|
214
|
-
|
|
215
|
-
describe('when no lint issues exist', () => {
|
|
216
|
-
describe('and previous state had notification flag set', () => {
|
|
217
|
-
it('resets hasNotifiedAboutLintIssues to false', async () => {
|
|
218
|
-
const { parsedLint } = await runLintAndGetResult({
|
|
219
|
-
lintResult: testData.lintResultWithoutErrors(),
|
|
220
|
-
initialLintData: testData.lintDataWithNotificationFlag()
|
|
221
|
-
})
|
|
222
|
-
|
|
223
|
-
expect(parsedLint!.hasNotifiedAboutLintIssues).toBe(false)
|
|
224
|
-
})
|
|
225
|
-
|
|
226
|
-
describe('and tests are passing', () => {
|
|
227
|
-
it('does not block', async () => {
|
|
228
|
-
const { result } = await runLintAndGetResult({
|
|
229
|
-
lintResult: testData.lintResultWithoutErrors(),
|
|
230
|
-
initialLintData: testData.lintDataWithNotificationFlag(),
|
|
231
|
-
initialTestData: testData.passingTestResults()
|
|
232
|
-
})
|
|
233
|
-
|
|
234
|
-
expect(result).toEqual(DEFAULT_RESULT)
|
|
235
|
-
})
|
|
236
|
-
})
|
|
237
|
-
})
|
|
238
|
-
|
|
239
|
-
describe('and previous state had issues and notification flag', () => {
|
|
240
|
-
it('resets hasNotifiedAboutLintIssues to false when issues are resolved', async () => {
|
|
241
|
-
const { parsedLint } = await runLintAndGetResult({
|
|
242
|
-
lintResult: testData.lintResultWithoutErrors(),
|
|
243
|
-
initialLintData: testData.lintData({
|
|
244
|
-
files: ['/src/example.ts'],
|
|
245
|
-
issues: [testData.lintIssue({ file: '/src/example.ts', line: 1, column: 1, message: "Error" })],
|
|
246
|
-
hasNotifiedAboutLintIssues: true
|
|
247
|
-
})
|
|
248
|
-
})
|
|
249
|
-
|
|
250
|
-
expect(parsedLint!.hasNotifiedAboutLintIssues).toBe(false)
|
|
251
|
-
})
|
|
252
|
-
})
|
|
253
|
-
|
|
254
|
-
it('saves clean lint data', async () => {
|
|
255
|
-
const { parsedLint } = await runLintAndGetResult({
|
|
256
|
-
lintResult: testData.lintResultWithoutErrors()
|
|
257
|
-
})
|
|
258
|
-
|
|
259
|
-
expect(parsedLint!.issues).toEqual([])
|
|
260
|
-
expect(parsedLint!.errorCount).toBe(0)
|
|
261
|
-
expect(parsedLint!.warningCount).toBe(0)
|
|
262
|
-
})
|
|
263
|
-
})
|
|
264
|
-
})
|
|
265
|
-
|
|
266
|
-
// Test helper functions
|
|
267
|
-
interface LintTestOptions {
|
|
268
|
-
lintResult?: Omit<LintData, 'hasNotifiedAboutLintIssues'>
|
|
269
|
-
operation?: Partial<HookData>
|
|
270
|
-
storage?: MemoryStorage
|
|
271
|
-
initialLintData?: LintData
|
|
272
|
-
initialTestData?: TestResult
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
async function runLintAndGetResult(options: LintTestOptions = {}): Promise<{
|
|
276
|
-
result: ValidationResult,
|
|
277
|
-
parsedLint: LintData | null,
|
|
278
|
-
storage: MemoryStorage
|
|
279
|
-
}> {
|
|
280
|
-
const { lintResult, operation = {}, storage: existingStorage, initialLintData, initialTestData } = options
|
|
281
|
-
const storage = existingStorage ?? new MemoryStorage()
|
|
282
|
-
|
|
283
|
-
// Set up initial storage state if provided
|
|
284
|
-
if (initialLintData) {
|
|
285
|
-
await storage.saveLint(JSON.stringify(initialLintData))
|
|
286
|
-
}
|
|
287
|
-
if (initialTestData) {
|
|
288
|
-
await storage.saveTest(JSON.stringify(initialTestData))
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
const testLinter = new TestLinter(lintResult ?? testData.lintResultWithoutErrors())
|
|
292
|
-
|
|
293
|
-
let baseOperation
|
|
294
|
-
if (operation.tool_name === 'Write') {
|
|
295
|
-
baseOperation = testData.writeOperation()
|
|
296
|
-
} else if (operation.tool_name === 'MultiEdit') {
|
|
297
|
-
baseOperation = testData.multiEditOperation()
|
|
298
|
-
} else {
|
|
299
|
-
baseOperation = testData.editOperation()
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
const hookData = JSON.stringify({
|
|
303
|
-
...baseOperation,
|
|
304
|
-
hook_event_name: 'PostToolUse',
|
|
305
|
-
...operation
|
|
306
|
-
})
|
|
307
|
-
|
|
308
|
-
const result = await handlePostToolLint(hookData, storage, testLinter)
|
|
309
|
-
|
|
310
|
-
const savedLint = await storage.getLint()
|
|
311
|
-
const parsedLint = savedLint ? JSON.parse(savedLint) : null
|
|
312
|
-
|
|
313
|
-
return { result, parsedLint, storage }
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
// Test Linter implementation with configurable behavior
|
|
317
|
-
class TestLinter implements Linter {
|
|
318
|
-
private readonly lintResult: LintResult
|
|
319
|
-
|
|
320
|
-
constructor(lintResult?: LintResult) {
|
|
321
|
-
this.lintResult = lintResult ?? testData.lintResultWithoutErrors()
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
async lint(): Promise<LintResult> {
|
|
325
|
-
return this.lintResult
|
|
326
|
-
}
|
|
327
|
-
}
|
|
@@ -1,165 +0,0 @@
|
|
|
1
|
-
import { ValidationResult } from '../contracts/types/ValidationResult'
|
|
2
|
-
import { HookData, HookDataSchema } from '../contracts/schemas/toolSchemas'
|
|
3
|
-
import { LintData, LintDataSchema, LintResult } from '../contracts/schemas/lintSchemas'
|
|
4
|
-
import { Storage } from '../storage/Storage'
|
|
5
|
-
import { Linter } from '../linters/Linter'
|
|
6
|
-
|
|
7
|
-
export const DEFAULT_RESULT: ValidationResult = {
|
|
8
|
-
decision: undefined,
|
|
9
|
-
reason: ''
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export class PostToolLintHandler {
|
|
13
|
-
private readonly linter: Linter | null
|
|
14
|
-
private readonly storage: Storage
|
|
15
|
-
|
|
16
|
-
constructor(storage: Storage, linter?: Linter | null) {
|
|
17
|
-
this.storage = storage
|
|
18
|
-
this.linter = linter ?? null
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
async handle(hookData: string): Promise<ValidationResult> {
|
|
22
|
-
// If no linter is configured, skip linting
|
|
23
|
-
if (!this.linter) {
|
|
24
|
-
return DEFAULT_RESULT
|
|
25
|
-
}
|
|
26
|
-
return handlePostToolLint(hookData, this.storage, this.linter)
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
function parseAndValidateHookData(hookData: string): HookData | null {
|
|
31
|
-
try {
|
|
32
|
-
const parsedData = JSON.parse(hookData)
|
|
33
|
-
const hookResult = HookDataSchema.safeParse(parsedData)
|
|
34
|
-
|
|
35
|
-
if (!hookResult.success) {
|
|
36
|
-
return null
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
// Only process PostToolUse hooks
|
|
40
|
-
if (hookResult.data.hook_event_name !== 'PostToolUse') {
|
|
41
|
-
return null
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
return hookResult.data
|
|
45
|
-
} catch {
|
|
46
|
-
return null
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
async function getStoredLintData(storage: Storage): Promise<LintData | null> {
|
|
51
|
-
try {
|
|
52
|
-
const lintDataStr = await storage.getLint()
|
|
53
|
-
if (lintDataStr) {
|
|
54
|
-
return LintDataSchema.parse(JSON.parse(lintDataStr))
|
|
55
|
-
}
|
|
56
|
-
} catch {
|
|
57
|
-
// Treat any error as no stored data
|
|
58
|
-
}
|
|
59
|
-
return null
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
function createLintData(
|
|
63
|
-
lintResults: LintResult,
|
|
64
|
-
storedLintData: LintData | null
|
|
65
|
-
): LintData {
|
|
66
|
-
const hasIssues = lintResults.errorCount > 0 || lintResults.warningCount > 0
|
|
67
|
-
|
|
68
|
-
return {
|
|
69
|
-
...lintResults,
|
|
70
|
-
hasNotifiedAboutLintIssues: hasIssues
|
|
71
|
-
? (storedLintData?.hasNotifiedAboutLintIssues ?? false) // Preserve flag when issues exist
|
|
72
|
-
: false // Reset flag when no issues
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
function createBlockResult(lintData: LintData): ValidationResult {
|
|
77
|
-
const formattedIssues = formatLintIssues(lintData.issues)
|
|
78
|
-
const summary = `\n✖ ${lintData.errorCount + lintData.warningCount} problems (${lintData.errorCount} errors, ${lintData.warningCount} warnings)`
|
|
79
|
-
|
|
80
|
-
return {
|
|
81
|
-
decision: 'block',
|
|
82
|
-
reason: `Lint issues detected:${formattedIssues}\n${summary}\n\nPlease fix these issues before proceeding.`
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
function formatLintIssues(issues: LintData['issues']): string {
|
|
87
|
-
const issuesByFile = new Map<string, string[]>()
|
|
88
|
-
|
|
89
|
-
for (const issue of issues) {
|
|
90
|
-
if (!issuesByFile.has(issue.file)) {
|
|
91
|
-
issuesByFile.set(issue.file, [])
|
|
92
|
-
}
|
|
93
|
-
const ruleInfo = issue.rule ? ` ${issue.rule}` : ''
|
|
94
|
-
issuesByFile.get(issue.file)!.push(
|
|
95
|
-
` ${issue.line}:${issue.column} ${issue.severity} ${issue.message}${ruleInfo}`
|
|
96
|
-
)
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
let formattedIssues = ''
|
|
100
|
-
for (const [file, fileIssues] of issuesByFile) {
|
|
101
|
-
formattedIssues += `\n${file}\n${fileIssues.join('\n')}`
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
return formattedIssues
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
export async function handlePostToolLint(
|
|
108
|
-
hookData: string,
|
|
109
|
-
storage: Storage,
|
|
110
|
-
linter: Linter
|
|
111
|
-
): Promise<ValidationResult> {
|
|
112
|
-
const validatedHookData = parseAndValidateHookData(hookData)
|
|
113
|
-
if (!validatedHookData) {
|
|
114
|
-
return DEFAULT_RESULT
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
// Extract file paths from tool operation
|
|
118
|
-
const filePaths = extractFilePaths(validatedHookData)
|
|
119
|
-
if (filePaths.length === 0) {
|
|
120
|
-
return DEFAULT_RESULT
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
// Get current lint data to check hasNotifiedAboutLintIssues state
|
|
124
|
-
const storedLintData = await getStoredLintData(storage)
|
|
125
|
-
|
|
126
|
-
// Run linting on the files
|
|
127
|
-
const lintResults = await linter.lint(filePaths)
|
|
128
|
-
|
|
129
|
-
// Create and save lint data
|
|
130
|
-
const lintData = createLintData(lintResults, storedLintData)
|
|
131
|
-
await storage.saveLint(JSON.stringify(lintData))
|
|
132
|
-
|
|
133
|
-
const hasIssues = lintResults.errorCount > 0 || lintResults.warningCount > 0
|
|
134
|
-
|
|
135
|
-
// Block if:
|
|
136
|
-
// 1. PreToolUse has notified (flag is true)
|
|
137
|
-
// 2. There are still issues
|
|
138
|
-
if (storedLintData?.hasNotifiedAboutLintIssues && hasIssues) {
|
|
139
|
-
return createBlockResult(lintData)
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
return DEFAULT_RESULT
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
function extractFilePaths(hookData: HookData): string[] {
|
|
146
|
-
const toolInput = hookData.tool_input
|
|
147
|
-
if (!toolInput || typeof toolInput !== 'object') return []
|
|
148
|
-
|
|
149
|
-
const paths: string[] = []
|
|
150
|
-
|
|
151
|
-
if ('file_path' in toolInput && typeof toolInput.file_path === 'string') {
|
|
152
|
-
paths.push(toolInput.file_path)
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
// Handle multi-edit operations
|
|
156
|
-
if ('edits' in toolInput && Array.isArray(toolInput.edits)) {
|
|
157
|
-
for (const edit of toolInput.edits) {
|
|
158
|
-
if ('file_path' in edit && typeof edit.file_path === 'string') {
|
|
159
|
-
paths.push(edit.file_path)
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
return [...new Set(paths)] // Remove duplicates
|
|
165
|
-
}
|