claude-ketchup 0.7.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 +172 -0
- package/bin/cli.ts +6 -0
- package/bin/postinstall.ts +8 -0
- package/bin/preuninstall.ts +8 -0
- package/commands/ketchup.md +107 -0
- package/dist/bin/cli.d.ts +3 -0
- package/dist/bin/cli.d.ts.map +1 -0
- package/dist/bin/cli.js +7 -0
- package/dist/bin/cli.js.map +1 -0
- package/dist/bin/postinstall.d.ts +3 -0
- package/dist/bin/postinstall.d.ts.map +1 -0
- package/dist/bin/postinstall.js +9 -0
- package/dist/bin/postinstall.js.map +1 -0
- package/dist/bin/preuninstall.d.ts +3 -0
- package/dist/bin/preuninstall.d.ts.map +1 -0
- package/dist/bin/preuninstall.js +9 -0
- package/dist/bin/preuninstall.js.map +1 -0
- package/dist/scripts/auto-continue.d.ts +3 -0
- package/dist/scripts/auto-continue.d.ts.map +1 -0
- package/dist/scripts/auto-continue.js +62 -0
- package/dist/scripts/auto-continue.js.map +1 -0
- package/dist/scripts/generate-changeset.d.ts +13 -0
- package/dist/scripts/generate-changeset.d.ts.map +1 -0
- package/dist/scripts/generate-changeset.js +322 -0
- package/dist/scripts/generate-changeset.js.map +1 -0
- package/dist/scripts/pre-tool-use.d.ts +3 -0
- package/dist/scripts/pre-tool-use.d.ts.map +1 -0
- package/dist/scripts/pre-tool-use.js +68 -0
- package/dist/scripts/pre-tool-use.js.map +1 -0
- package/dist/scripts/session-start.d.ts +3 -0
- package/dist/scripts/session-start.d.ts.map +1 -0
- package/dist/scripts/session-start.js +71 -0
- package/dist/scripts/session-start.js.map +1 -0
- package/dist/scripts/user-prompt-submit.d.ts +3 -0
- package/dist/scripts/user-prompt-submit.d.ts.map +1 -0
- package/dist/scripts/user-prompt-submit.js +71 -0
- package/dist/scripts/user-prompt-submit.js.map +1 -0
- package/dist/src/activity-logger.d.ts +2 -0
- package/dist/src/activity-logger.d.ts.map +1 -0
- package/dist/src/activity-logger.js +47 -0
- package/dist/src/activity-logger.js.map +1 -0
- package/dist/src/activity-logger.test.d.ts +2 -0
- package/dist/src/activity-logger.test.d.ts.map +1 -0
- package/dist/src/activity-logger.test.js +121 -0
- package/dist/src/activity-logger.test.js.map +1 -0
- package/dist/src/clean-logs.d.ts +6 -0
- package/dist/src/clean-logs.d.ts.map +1 -0
- package/dist/src/clean-logs.js +38 -0
- package/dist/src/clean-logs.js.map +1 -0
- package/dist/src/clean-logs.test.d.ts +2 -0
- package/dist/src/clean-logs.test.d.ts.map +1 -0
- package/dist/src/clean-logs.test.js +101 -0
- package/dist/src/clean-logs.test.js.map +1 -0
- package/dist/src/cli/cli.d.ts +3 -0
- package/dist/src/cli/cli.d.ts.map +1 -0
- package/dist/src/cli/cli.js +26 -0
- package/dist/src/cli/cli.js.map +1 -0
- package/dist/src/cli/cli.test.d.ts +2 -0
- package/dist/src/cli/cli.test.d.ts.map +1 -0
- package/dist/src/cli/cli.test.js +20 -0
- package/dist/src/cli/cli.test.js.map +1 -0
- package/dist/src/cli/doctor.d.ts +7 -0
- package/dist/src/cli/doctor.d.ts.map +1 -0
- package/dist/src/cli/doctor.js +67 -0
- package/dist/src/cli/doctor.js.map +1 -0
- package/dist/src/cli/doctor.test.d.ts +2 -0
- package/dist/src/cli/doctor.test.d.ts.map +1 -0
- package/dist/src/cli/doctor.test.js +87 -0
- package/dist/src/cli/doctor.test.js.map +1 -0
- package/dist/src/cli/install.d.ts +8 -0
- package/dist/src/cli/install.d.ts.map +1 -0
- package/dist/src/cli/install.js +8 -0
- package/dist/src/cli/install.js.map +1 -0
- package/dist/src/cli/install.test.d.ts +2 -0
- package/dist/src/cli/install.test.d.ts.map +1 -0
- package/dist/src/cli/install.test.js +106 -0
- package/dist/src/cli/install.test.js.map +1 -0
- package/dist/src/cli/reminders.d.ts +12 -0
- package/dist/src/cli/reminders.d.ts.map +1 -0
- package/dist/src/cli/reminders.js +52 -0
- package/dist/src/cli/reminders.js.map +1 -0
- package/dist/src/cli/reminders.test.d.ts +2 -0
- package/dist/src/cli/reminders.test.d.ts.map +1 -0
- package/dist/src/cli/reminders.test.js +72 -0
- package/dist/src/cli/reminders.test.js.map +1 -0
- package/dist/src/cli/repair.d.ts +11 -0
- package/dist/src/cli/repair.d.ts.map +1 -0
- package/dist/src/cli/repair.js +91 -0
- package/dist/src/cli/repair.js.map +1 -0
- package/dist/src/cli/repair.test.d.ts +2 -0
- package/dist/src/cli/repair.test.d.ts.map +1 -0
- package/dist/src/cli/repair.test.js +96 -0
- package/dist/src/cli/repair.test.js.map +1 -0
- package/dist/src/cli/status.d.ts +10 -0
- package/dist/src/cli/status.d.ts.map +1 -0
- package/dist/src/cli/status.js +55 -0
- package/dist/src/cli/status.js.map +1 -0
- package/dist/src/cli/status.test.d.ts +2 -0
- package/dist/src/cli/status.test.d.ts.map +1 -0
- package/dist/src/cli/status.test.js +83 -0
- package/dist/src/cli/status.test.js.map +1 -0
- package/dist/src/clue-collector.d.ts +23 -0
- package/dist/src/clue-collector.d.ts.map +1 -0
- package/dist/src/clue-collector.js +221 -0
- package/dist/src/clue-collector.js.map +1 -0
- package/dist/src/clue-collector.test.d.ts +2 -0
- package/dist/src/clue-collector.test.d.ts.map +1 -0
- package/dist/src/clue-collector.test.js +278 -0
- package/dist/src/clue-collector.test.js.map +1 -0
- package/dist/src/commit-validator.d.ts +35 -0
- package/dist/src/commit-validator.d.ts.map +1 -0
- package/dist/src/commit-validator.js +147 -0
- package/dist/src/commit-validator.js.map +1 -0
- package/dist/src/commit-validator.test.d.ts +2 -0
- package/dist/src/commit-validator.test.d.ts.map +1 -0
- package/dist/src/commit-validator.test.js +443 -0
- package/dist/src/commit-validator.test.js.map +1 -0
- package/dist/src/config-loader.d.ts +15 -0
- package/dist/src/config-loader.d.ts.map +1 -0
- package/dist/src/config-loader.js +12 -0
- package/dist/src/config-loader.js.map +1 -0
- package/dist/src/config-loader.test.d.ts +2 -0
- package/dist/src/config-loader.test.d.ts.map +1 -0
- package/dist/src/config-loader.test.js +69 -0
- package/dist/src/config-loader.test.js.map +1 -0
- package/dist/src/debug-logger.d.ts +2 -0
- package/dist/src/debug-logger.d.ts.map +1 -0
- package/dist/src/debug-logger.js +23 -0
- package/dist/src/debug-logger.js.map +1 -0
- package/dist/src/debug-logger.test.d.ts +2 -0
- package/dist/src/debug-logger.test.d.ts.map +1 -0
- package/dist/src/debug-logger.test.js +63 -0
- package/dist/src/debug-logger.test.js.map +1 -0
- package/dist/src/default-validators.test.d.ts +2 -0
- package/dist/src/default-validators.test.d.ts.map +1 -0
- package/dist/src/default-validators.test.js +119 -0
- package/dist/src/default-validators.test.js.map +1 -0
- package/dist/src/deny-list.d.ts +3 -0
- package/dist/src/deny-list.d.ts.map +1 -0
- package/dist/src/deny-list.js +62 -0
- package/dist/src/deny-list.js.map +1 -0
- package/dist/src/deny-list.test.d.ts +2 -0
- package/dist/src/deny-list.test.d.ts.map +1 -0
- package/dist/src/deny-list.test.js +93 -0
- package/dist/src/deny-list.test.js.map +1 -0
- package/dist/src/e2e.test.d.ts +2 -0
- package/dist/src/e2e.test.d.ts.map +1 -0
- package/dist/src/e2e.test.js +89 -0
- package/dist/src/e2e.test.js.map +1 -0
- package/dist/src/gitignore-manager.d.ts +2 -0
- package/dist/src/gitignore-manager.d.ts.map +1 -0
- package/dist/src/gitignore-manager.js +45 -0
- package/dist/src/gitignore-manager.js.map +1 -0
- package/dist/src/gitignore-manager.test.d.ts +2 -0
- package/dist/src/gitignore-manager.test.d.ts.map +1 -0
- package/dist/src/gitignore-manager.test.js +65 -0
- package/dist/src/gitignore-manager.test.js.map +1 -0
- package/dist/src/hook-input.d.ts +9 -0
- package/dist/src/hook-input.d.ts.map +1 -0
- package/dist/src/hook-input.js +7 -0
- package/dist/src/hook-input.js.map +1 -0
- package/dist/src/hook-input.test.d.ts +2 -0
- package/dist/src/hook-input.test.d.ts.map +1 -0
- package/dist/src/hook-input.test.js +20 -0
- package/dist/src/hook-input.test.js.map +1 -0
- package/dist/src/hook-logger.d.ts +16 -0
- package/dist/src/hook-logger.d.ts.map +1 -0
- package/dist/src/hook-logger.js +91 -0
- package/dist/src/hook-logger.js.map +1 -0
- package/dist/src/hook-logger.test.d.ts +2 -0
- package/dist/src/hook-logger.test.d.ts.map +1 -0
- package/dist/src/hook-logger.test.js +184 -0
- package/dist/src/hook-logger.test.js.map +1 -0
- package/dist/src/hook-state.d.ts +43 -0
- package/dist/src/hook-state.d.ts.map +1 -0
- package/dist/src/hook-state.js +124 -0
- package/dist/src/hook-state.js.map +1 -0
- package/dist/src/hook-state.test.d.ts +2 -0
- package/dist/src/hook-state.test.d.ts.map +1 -0
- package/dist/src/hook-state.test.js +190 -0
- package/dist/src/hook-state.test.js.map +1 -0
- package/dist/src/hooks/auto-continue.d.ts +21 -0
- package/dist/src/hooks/auto-continue.d.ts.map +1 -0
- package/dist/src/hooks/auto-continue.js +70 -0
- package/dist/src/hooks/auto-continue.js.map +1 -0
- package/dist/src/hooks/auto-continue.test.d.ts +2 -0
- package/dist/src/hooks/auto-continue.test.d.ts.map +1 -0
- package/dist/src/hooks/auto-continue.test.js +171 -0
- package/dist/src/hooks/auto-continue.test.js.map +1 -0
- package/dist/src/hooks/pre-tool-use.d.ts +14 -0
- package/dist/src/hooks/pre-tool-use.d.ts.map +1 -0
- package/dist/src/hooks/pre-tool-use.js +62 -0
- package/dist/src/hooks/pre-tool-use.js.map +1 -0
- package/dist/src/hooks/pre-tool-use.test.d.ts +2 -0
- package/dist/src/hooks/pre-tool-use.test.d.ts.map +1 -0
- package/dist/src/hooks/pre-tool-use.test.js +168 -0
- package/dist/src/hooks/pre-tool-use.test.js.map +1 -0
- package/dist/src/hooks/session-start.d.ts +20 -0
- package/dist/src/hooks/session-start.d.ts.map +1 -0
- package/dist/src/hooks/session-start.js +27 -0
- package/dist/src/hooks/session-start.js.map +1 -0
- package/dist/src/hooks/session-start.test.d.ts +2 -0
- package/dist/src/hooks/session-start.test.d.ts.map +1 -0
- package/dist/src/hooks/session-start.test.js +125 -0
- package/dist/src/hooks/session-start.test.js.map +1 -0
- package/dist/src/hooks/user-prompt-submit.d.ts +17 -0
- package/dist/src/hooks/user-prompt-submit.d.ts.map +1 -0
- package/dist/src/hooks/user-prompt-submit.js +28 -0
- package/dist/src/hooks/user-prompt-submit.js.map +1 -0
- package/dist/src/hooks/user-prompt-submit.test.d.ts +2 -0
- package/dist/src/hooks/user-prompt-submit.test.d.ts.map +1 -0
- package/dist/src/hooks/user-prompt-submit.test.js +119 -0
- package/dist/src/hooks/user-prompt-submit.test.js.map +1 -0
- package/dist/src/hooks/validate-commit.d.ts +12 -0
- package/dist/src/hooks/validate-commit.d.ts.map +1 -0
- package/dist/src/hooks/validate-commit.js +58 -0
- package/dist/src/hooks/validate-commit.js.map +1 -0
- package/dist/src/hooks/validate-commit.test.d.ts +2 -0
- package/dist/src/hooks/validate-commit.test.d.ts.map +1 -0
- package/dist/src/hooks/validate-commit.test.js +150 -0
- package/dist/src/hooks/validate-commit.test.js.map +1 -0
- package/dist/src/index.d.ts +15 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +39 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/linker.d.ts +6 -0
- package/dist/src/linker.d.ts.map +1 -0
- package/dist/src/linker.js +78 -0
- package/dist/src/linker.js.map +1 -0
- package/dist/src/linker.test.d.ts +2 -0
- package/dist/src/linker.test.d.ts.map +1 -0
- package/dist/src/linker.test.js +192 -0
- package/dist/src/linker.test.js.map +1 -0
- package/dist/src/logger.d.ts +21 -0
- package/dist/src/logger.d.ts.map +1 -0
- package/dist/src/logger.js +117 -0
- package/dist/src/logger.js.map +1 -0
- package/dist/src/logger.test.d.ts +2 -0
- package/dist/src/logger.test.d.ts.map +1 -0
- package/dist/src/logger.test.js +159 -0
- package/dist/src/logger.test.js.map +1 -0
- package/dist/src/npm-install.test.d.ts +2 -0
- package/dist/src/npm-install.test.d.ts.map +1 -0
- package/dist/src/npm-install.test.js +70 -0
- package/dist/src/npm-install.test.js.map +1 -0
- package/dist/src/path-resolver.d.ts +11 -0
- package/dist/src/path-resolver.d.ts.map +1 -0
- package/dist/src/path-resolver.js +65 -0
- package/dist/src/path-resolver.js.map +1 -0
- package/dist/src/postinstall.d.ts +8 -0
- package/dist/src/postinstall.d.ts.map +1 -0
- package/dist/src/postinstall.js +112 -0
- package/dist/src/postinstall.js.map +1 -0
- package/dist/src/postinstall.test.d.ts +2 -0
- package/dist/src/postinstall.test.d.ts.map +1 -0
- package/dist/src/postinstall.test.js +203 -0
- package/dist/src/postinstall.test.js.map +1 -0
- package/dist/src/preuninstall.d.ts +3 -0
- package/dist/src/preuninstall.d.ts.map +1 -0
- package/dist/src/preuninstall.js +85 -0
- package/dist/src/preuninstall.js.map +1 -0
- package/dist/src/preuninstall.test.d.ts +2 -0
- package/dist/src/preuninstall.test.d.ts.map +1 -0
- package/dist/src/preuninstall.test.js +114 -0
- package/dist/src/preuninstall.test.js.map +1 -0
- package/dist/src/reminder-loader.d.ts +24 -0
- package/dist/src/reminder-loader.d.ts.map +1 -0
- package/dist/src/reminder-loader.js +84 -0
- package/dist/src/reminder-loader.js.map +1 -0
- package/dist/src/reminder-loader.test.d.ts +2 -0
- package/dist/src/reminder-loader.test.d.ts.map +1 -0
- package/dist/src/reminder-loader.test.js +152 -0
- package/dist/src/reminder-loader.test.js.map +1 -0
- package/dist/src/root-finder.d.ts +2 -0
- package/dist/src/root-finder.d.ts.map +1 -0
- package/dist/src/root-finder.js +71 -0
- package/dist/src/root-finder.js.map +1 -0
- package/dist/src/root-finder.test.d.ts +2 -0
- package/dist/src/root-finder.test.d.ts.map +1 -0
- package/dist/src/root-finder.test.js +111 -0
- package/dist/src/root-finder.test.js.map +1 -0
- package/dist/src/settings-merger.d.ts +2 -0
- package/dist/src/settings-merger.d.ts.map +1 -0
- package/dist/src/settings-merger.js +133 -0
- package/dist/src/settings-merger.js.map +1 -0
- package/dist/src/settings-merger.test.d.ts +2 -0
- package/dist/src/settings-merger.test.d.ts.map +1 -0
- package/dist/src/settings-merger.test.js +379 -0
- package/dist/src/settings-merger.test.js.map +1 -0
- package/dist/src/settings-template.test.d.ts +2 -0
- package/dist/src/settings-template.test.d.ts.map +1 -0
- package/dist/src/settings-template.test.js +88 -0
- package/dist/src/settings-template.test.js.map +1 -0
- package/dist/src/state-manager.d.ts +5 -0
- package/dist/src/state-manager.d.ts.map +1 -0
- package/dist/src/state-manager.js +55 -0
- package/dist/src/state-manager.js.map +1 -0
- package/dist/src/state-manager.test.d.ts +2 -0
- package/dist/src/state-manager.test.d.ts.map +1 -0
- package/dist/src/state-manager.test.js +85 -0
- package/dist/src/state-manager.test.js.map +1 -0
- package/dist/src/subagent-classifier.d.ts +4 -0
- package/dist/src/subagent-classifier.d.ts.map +1 -0
- package/dist/src/subagent-classifier.js +53 -0
- package/dist/src/subagent-classifier.js.map +1 -0
- package/dist/src/subagent-classifier.test.d.ts +2 -0
- package/dist/src/subagent-classifier.test.d.ts.map +1 -0
- package/dist/src/subagent-classifier.test.js +84 -0
- package/dist/src/subagent-classifier.test.js.map +1 -0
- package/dist/src/validator-loader.d.ts +9 -0
- package/dist/src/validator-loader.d.ts.map +1 -0
- package/dist/src/validator-loader.js +71 -0
- package/dist/src/validator-loader.js.map +1 -0
- package/dist/src/validator-loader.test.d.ts +2 -0
- package/dist/src/validator-loader.test.d.ts.map +1 -0
- package/dist/src/validator-loader.test.js +140 -0
- package/dist/src/validator-loader.test.js.map +1 -0
- package/package.json +90 -0
- package/reminders/ketchup.md +24 -0
- package/reminders/reminder-documentation.md +30 -0
- package/reminders/reminder-emergent-design.md +41 -0
- package/reminders/reminder-extreme-ownership.md +27 -0
- package/reminders/reminder-ide-diagnostics.md +25 -0
- package/reminders/reminder-ketchup.md +145 -0
- package/reminders/reminder-parallelization.md +27 -0
- package/reminders/reminder-rethink-after-revert.md +25 -0
- package/reminders/reminder-sub-agent-rules.md +27 -0
- package/reminders/reminder-test-title-matches-spec.md +37 -0
- package/scripts/auto-continue.ts +34 -0
- package/scripts/generate-changeset.ts +405 -0
- package/scripts/pre-tool-use.ts +35 -0
- package/scripts/session-start.ts +38 -0
- package/scripts/tail-logs.sh +17 -0
- package/scripts/test-hooks.sh +910 -0
- package/scripts/user-prompt-submit.ts +38 -0
- package/templates/settings.json +48 -0
- package/validators/appeal-system.md +55 -0
- package/validators/backwards-compat.md +33 -0
- package/validators/burst-atomicity.md +37 -0
- package/validators/coverage-rules.md +34 -0
- package/validators/dead-code.md +36 -0
- package/validators/hygiene.md +34 -0
- package/validators/infra-commit-format.md +37 -0
- package/validators/ketchup-plan-format.md +42 -0
- package/validators/new-code-requires-tests.md +36 -0
- package/validators/no-comments.md +35 -0
- package/validators/no-dangerous-git.md +35 -0
- package/validators/tcr-workflow.md +31 -0
- package/validators/testing-no-state-peeking.md +37 -0
- package/validators/testing-structure.md +37 -0
- package/validators/testing-stubs-over-mocks.md +42 -0
- package/validators/testing-weak-assertions.md +36 -0
- package/validators/type-organization.md +30 -0
|
@@ -0,0 +1,405 @@
|
|
|
1
|
+
#!/usr/bin/env tsx
|
|
2
|
+
/**
|
|
3
|
+
* AI-powered changeset generation script
|
|
4
|
+
*
|
|
5
|
+
* This script:
|
|
6
|
+
* 1. Gets commits since last changeset/tag
|
|
7
|
+
* 2. Parses conventional commits (feat/fix/etc)
|
|
8
|
+
* 3. Determines bump type (breakingāmajor, featāminor, elseāpatch)
|
|
9
|
+
* 4. Generates changelog via Claude CLI (with simple fallback)
|
|
10
|
+
* 5. Creates .changeset/*.md file
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { execSync } from 'node:child_process';
|
|
14
|
+
import { randomBytes } from 'node:crypto';
|
|
15
|
+
import { existsSync, mkdirSync, readdirSync, writeFileSync } from 'node:fs';
|
|
16
|
+
import { join } from 'node:path';
|
|
17
|
+
|
|
18
|
+
// Exit codes
|
|
19
|
+
const EXIT_CODE = {
|
|
20
|
+
SUCCESS: 0,
|
|
21
|
+
ERROR: 1,
|
|
22
|
+
NO_COMMITS: 11,
|
|
23
|
+
NO_CONVENTIONAL_COMMITS: 12,
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
// Types
|
|
27
|
+
type CommitType =
|
|
28
|
+
| 'feat'
|
|
29
|
+
| 'fix'
|
|
30
|
+
| 'docs'
|
|
31
|
+
| 'style'
|
|
32
|
+
| 'refactor'
|
|
33
|
+
| 'perf'
|
|
34
|
+
| 'test'
|
|
35
|
+
| 'build'
|
|
36
|
+
| 'ci'
|
|
37
|
+
| 'chore'
|
|
38
|
+
| 'revert';
|
|
39
|
+
type BumpType = 'major' | 'minor' | 'patch';
|
|
40
|
+
|
|
41
|
+
interface ConventionalCommit {
|
|
42
|
+
hash: string;
|
|
43
|
+
type: CommitType;
|
|
44
|
+
scope?: string;
|
|
45
|
+
subject: string;
|
|
46
|
+
body?: string;
|
|
47
|
+
breaking: boolean;
|
|
48
|
+
fullMessage: string;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Conventional commit pattern
|
|
52
|
+
const CONVENTIONAL_PATTERN = /^(feat|fix|docs|style|refactor|perf|test|build|ci|chore|revert)(\(([^)]+)\))?: (.+)/;
|
|
53
|
+
|
|
54
|
+
// Configuration
|
|
55
|
+
const CHANGESET_DIR = '.changeset';
|
|
56
|
+
const PACKAGE_NAME = 'claude-ketchup';
|
|
57
|
+
|
|
58
|
+
// Logging utilities
|
|
59
|
+
function logStep(message: string): void {
|
|
60
|
+
console.log(`\nš ${message}`);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function logInfo(message: string): void {
|
|
64
|
+
console.log(` ${message}`);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function logSuccess(message: string): void {
|
|
68
|
+
console.log(`ā
${message}`);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function logWarning(message: string): void {
|
|
72
|
+
console.warn(`ā ļø ${message}`);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function logError(message: string): void {
|
|
76
|
+
console.error(`ā ${message}`);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Git utilities
|
|
80
|
+
function isGitRepository(): boolean {
|
|
81
|
+
try {
|
|
82
|
+
execSync('git rev-parse --git-dir', { stdio: 'pipe' });
|
|
83
|
+
return true;
|
|
84
|
+
} catch {
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function getCommitMessage(hash: string): string {
|
|
90
|
+
return execSync(`git log -1 --format=%B ${hash}`, { encoding: 'utf8' }).trim();
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function getCommitsSinceLastChangeset(): string[] {
|
|
94
|
+
try {
|
|
95
|
+
const changesetPath = join(process.cwd(), CHANGESET_DIR);
|
|
96
|
+
|
|
97
|
+
if (!existsSync(changesetPath)) {
|
|
98
|
+
return getCommitsSinceLastTag();
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const files = readdirSync(changesetPath).filter((f) => f.endsWith('.md') && f !== 'README.md');
|
|
102
|
+
|
|
103
|
+
if (files.length === 0) {
|
|
104
|
+
return getCommitsSinceLastTag();
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Get the most recent changeset file by git commit time
|
|
108
|
+
const latestChangeset = files
|
|
109
|
+
.map((f) => {
|
|
110
|
+
try {
|
|
111
|
+
const time = execSync(`git log -1 --format=%ct -- ${CHANGESET_DIR}/${f}`, {
|
|
112
|
+
encoding: 'utf8',
|
|
113
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
114
|
+
}).trim();
|
|
115
|
+
return { file: f, time: Number(time) || 0 };
|
|
116
|
+
} catch {
|
|
117
|
+
return { file: f, time: 0 };
|
|
118
|
+
}
|
|
119
|
+
})
|
|
120
|
+
.filter((f) => f.time > 0)
|
|
121
|
+
.sort((a, b) => b.time - a.time)[0];
|
|
122
|
+
|
|
123
|
+
if (!latestChangeset) {
|
|
124
|
+
return getCommitsSinceLastTag();
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Get commits since that changeset was added
|
|
128
|
+
const changesetCommit = execSync(`git log -1 --format=%H -- ${CHANGESET_DIR}/${latestChangeset.file}`, {
|
|
129
|
+
encoding: 'utf8',
|
|
130
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
131
|
+
}).trim();
|
|
132
|
+
|
|
133
|
+
return getCommitsInRange(changesetCommit, 'HEAD');
|
|
134
|
+
} catch (error) {
|
|
135
|
+
logWarning(`Could not get commits since last changeset: ${(error as Error).message}`);
|
|
136
|
+
return [];
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function getCommitsInRange(since: string, until = 'HEAD'): string[] {
|
|
141
|
+
try {
|
|
142
|
+
const output = execSync(`git log ${since}..${until} --format=%H`, {
|
|
143
|
+
encoding: 'utf8',
|
|
144
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
145
|
+
}).trim();
|
|
146
|
+
|
|
147
|
+
return output ? output.split('\n').filter(Boolean) : [];
|
|
148
|
+
} catch {
|
|
149
|
+
return [];
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function getCommitsSinceLastTag(): string[] {
|
|
154
|
+
try {
|
|
155
|
+
const lastTag = execSync('git describe --tags --abbrev=0', {
|
|
156
|
+
encoding: 'utf8',
|
|
157
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
158
|
+
}).trim();
|
|
159
|
+
|
|
160
|
+
return getCommitsInRange(lastTag, 'HEAD');
|
|
161
|
+
} catch {
|
|
162
|
+
// No tags exist, get recent commits (limit to 50)
|
|
163
|
+
try {
|
|
164
|
+
const output = execSync('git log -50 --format=%H', {
|
|
165
|
+
encoding: 'utf8',
|
|
166
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
167
|
+
}).trim();
|
|
168
|
+
|
|
169
|
+
return output ? output.split('\n').filter(Boolean) : [];
|
|
170
|
+
} catch {
|
|
171
|
+
return [];
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Parsing utilities
|
|
177
|
+
function parseConventionalCommit(hash: string): ConventionalCommit | null {
|
|
178
|
+
try {
|
|
179
|
+
const fullMessage = getCommitMessage(hash);
|
|
180
|
+
const match = fullMessage.match(CONVENTIONAL_PATTERN);
|
|
181
|
+
|
|
182
|
+
if (!match) {
|
|
183
|
+
return null;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
const [, type, , scope, subject] = match;
|
|
187
|
+
const body = fullMessage.split('\n').slice(1).join('\n').trim();
|
|
188
|
+
const breaking = fullMessage.includes('BREAKING CHANGE:') || fullMessage.includes('!:');
|
|
189
|
+
|
|
190
|
+
return {
|
|
191
|
+
hash,
|
|
192
|
+
type: type as CommitType,
|
|
193
|
+
scope,
|
|
194
|
+
subject,
|
|
195
|
+
body,
|
|
196
|
+
breaking,
|
|
197
|
+
fullMessage,
|
|
198
|
+
};
|
|
199
|
+
} catch {
|
|
200
|
+
return null;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
function parseConventionalCommits(hashes: string[]): ConventionalCommit[] {
|
|
205
|
+
return hashes.map((hash) => parseConventionalCommit(hash)).filter((c): c is ConventionalCommit => c !== null);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// Semver utilities
|
|
209
|
+
function determineBumpType(commits: ConventionalCommit[]): BumpType {
|
|
210
|
+
if (commits.some((c) => c.breaking)) {
|
|
211
|
+
return 'major';
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
if (commits.some((c) => c.type === 'feat')) {
|
|
215
|
+
return 'minor';
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
return 'patch';
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// Changelog generation
|
|
222
|
+
async function generateChangelogWithClaude(commits: ConventionalCommit[]): Promise<string | null> {
|
|
223
|
+
try {
|
|
224
|
+
// Check if claude CLI is available
|
|
225
|
+
execSync('which claude', { stdio: 'pipe' });
|
|
226
|
+
|
|
227
|
+
const commitSummary = commits
|
|
228
|
+
.map((c) => `- ${c.type}${c.scope ? `(${c.scope})` : ''}: ${c.subject}\n ${c.body || '(no additional details)'}`)
|
|
229
|
+
.join('\n\n');
|
|
230
|
+
|
|
231
|
+
const prompt = `You are analyzing git commits to generate a changelog entry. Here are the commits:
|
|
232
|
+
|
|
233
|
+
${commitSummary}
|
|
234
|
+
|
|
235
|
+
Generate a concise changelog description as bullet points. Rules:
|
|
236
|
+
- Use 2-5 bullet points maximum
|
|
237
|
+
- Focus on user-facing changes and impact
|
|
238
|
+
- Group related changes together
|
|
239
|
+
- Use clear, non-technical language where possible
|
|
240
|
+
- Start each bullet with a dash and capitalize the first word
|
|
241
|
+
- Do NOT include commit hashes, types, or scopes
|
|
242
|
+
- Do NOT use markdown formatting besides the dashes
|
|
243
|
+
|
|
244
|
+
Example format:
|
|
245
|
+
- Added user authentication with OAuth support
|
|
246
|
+
- Fixed critical bug in data synchronization
|
|
247
|
+
- Improved performance of search queries by 50%
|
|
248
|
+
|
|
249
|
+
Now generate the changelog for the commits above:`;
|
|
250
|
+
|
|
251
|
+
const tempFile = join(process.cwd(), `.changeset-prompt-${Date.now()}.txt`);
|
|
252
|
+
writeFileSync(tempFile, prompt);
|
|
253
|
+
|
|
254
|
+
try {
|
|
255
|
+
const result = execSync(`claude -p "$(cat ${tempFile})"`, {
|
|
256
|
+
encoding: 'utf8',
|
|
257
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
258
|
+
timeout: 30000,
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
return result.trim();
|
|
262
|
+
} finally {
|
|
263
|
+
try {
|
|
264
|
+
execSync(`rm ${tempFile}`, { stdio: 'pipe' });
|
|
265
|
+
} catch {
|
|
266
|
+
// Ignore cleanup errors
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
} catch {
|
|
270
|
+
return null;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
function generateSimpleChangelog(commits: ConventionalCommit[]): string {
|
|
275
|
+
const features = commits.filter((c) => c.type === 'feat');
|
|
276
|
+
const fixes = commits.filter((c) => c.type === 'fix');
|
|
277
|
+
const others = commits.filter((c) => !['feat', 'fix'].includes(c.type));
|
|
278
|
+
|
|
279
|
+
const lines: string[] = [];
|
|
280
|
+
|
|
281
|
+
if (features.length > 0) {
|
|
282
|
+
lines.push(...features.map((c) => `- ${c.scope ? `**${c.scope}**: ` : ''}${c.subject}`));
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
if (fixes.length > 0) {
|
|
286
|
+
lines.push(...fixes.map((c) => `- ${c.scope ? `**${c.scope}**: ` : ''}${c.subject}`));
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
if (others.length > 0 && lines.length < 5) {
|
|
290
|
+
lines.push(...others.map((c) => `- ${c.scope ? `**${c.scope}**: ` : ''}${c.subject}`).slice(0, 5 - lines.length));
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
return lines.slice(0, 5).join('\n');
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
async function generateChangelog(commits: ConventionalCommit[]): Promise<string> {
|
|
297
|
+
logInfo('Attempting to generate changelog with Claude CLI...');
|
|
298
|
+
|
|
299
|
+
const aiChangelog = await generateChangelogWithClaude(commits);
|
|
300
|
+
|
|
301
|
+
if (aiChangelog) {
|
|
302
|
+
logSuccess('Changelog generated with Claude CLI');
|
|
303
|
+
return aiChangelog;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
logWarning('Claude CLI not available, using simple changelog generation');
|
|
307
|
+
return generateSimpleChangelog(commits);
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
// Changeset file creation
|
|
311
|
+
function createChangesetFile(bumpType: BumpType, description: string): { filename: string; content: string } {
|
|
312
|
+
const changesetDir = join(process.cwd(), CHANGESET_DIR);
|
|
313
|
+
|
|
314
|
+
if (!existsSync(changesetDir)) {
|
|
315
|
+
mkdirSync(changesetDir, { recursive: true });
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
const id = randomBytes(8).toString('hex');
|
|
319
|
+
const filename = `auto-${id}.md`;
|
|
320
|
+
const filepath = join(changesetDir, filename);
|
|
321
|
+
|
|
322
|
+
const content = `---
|
|
323
|
+
"${PACKAGE_NAME}": ${bumpType}
|
|
324
|
+
---
|
|
325
|
+
|
|
326
|
+
${description}
|
|
327
|
+
`;
|
|
328
|
+
|
|
329
|
+
writeFileSync(filepath, content);
|
|
330
|
+
|
|
331
|
+
return { filename, content };
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
// Main function
|
|
335
|
+
async function main(): Promise<void> {
|
|
336
|
+
const args = process.argv.slice(2);
|
|
337
|
+
const dryRun = args.includes('--dry-run');
|
|
338
|
+
|
|
339
|
+
try {
|
|
340
|
+
if (!isGitRepository()) {
|
|
341
|
+
logError('Not a git repository');
|
|
342
|
+
process.exit(EXIT_CODE.ERROR);
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
logStep('Checking for commits that need changesets...');
|
|
346
|
+
|
|
347
|
+
const commitHashes = getCommitsSinceLastChangeset();
|
|
348
|
+
|
|
349
|
+
if (commitHashes.length === 0) {
|
|
350
|
+
logInfo('No new commits found. Nothing to do.');
|
|
351
|
+
process.exit(EXIT_CODE.NO_COMMITS);
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
logInfo(`Found ${commitHashes.length} commit(s) to process`);
|
|
355
|
+
|
|
356
|
+
const commits = parseConventionalCommits(commitHashes);
|
|
357
|
+
|
|
358
|
+
const skippedCount = commitHashes.length - commits.length;
|
|
359
|
+
if (skippedCount > 0) {
|
|
360
|
+
logWarning(`Skipped ${skippedCount} non-conventional commit(s). Use format: type(scope): subject`);
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
if (commits.length === 0) {
|
|
364
|
+
logWarning('No conventional commits found. Skipping changeset generation.');
|
|
365
|
+
logInfo('Commits must follow format: type(scope): subject (e.g., feat(cli): add new command)');
|
|
366
|
+
process.exit(EXIT_CODE.NO_CONVENTIONAL_COMMITS);
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
logSuccess(`Found ${commits.length} valid conventional commit(s)`);
|
|
370
|
+
|
|
371
|
+
const bumpType = determineBumpType(commits);
|
|
372
|
+
logInfo(`Determined version bump: ${bumpType}`);
|
|
373
|
+
|
|
374
|
+
const description = await generateChangelog(commits);
|
|
375
|
+
|
|
376
|
+
if (dryRun) {
|
|
377
|
+
console.log('\nš Changeset Preview:');
|
|
378
|
+
console.log('---');
|
|
379
|
+
console.log(`Bump type: ${bumpType}`);
|
|
380
|
+
console.log(`Package: ${PACKAGE_NAME}`);
|
|
381
|
+
console.log('\nChangelog:');
|
|
382
|
+
console.log(description);
|
|
383
|
+
console.log('---');
|
|
384
|
+
process.exit(EXIT_CODE.SUCCESS);
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
const result = createChangesetFile(bumpType, description);
|
|
388
|
+
|
|
389
|
+
logSuccess(`Created changeset: ${result.filename}`);
|
|
390
|
+
logInfo(` Bump type: ${bumpType}`);
|
|
391
|
+
logInfo(` Commits: ${commits.length}`);
|
|
392
|
+
|
|
393
|
+
console.log('\nš Changelog preview:');
|
|
394
|
+
console.log('---');
|
|
395
|
+
console.log(description);
|
|
396
|
+
console.log('---');
|
|
397
|
+
|
|
398
|
+
process.exit(EXIT_CODE.SUCCESS);
|
|
399
|
+
} catch (error) {
|
|
400
|
+
logError(`Failed to generate changeset: ${(error as Error).message}`);
|
|
401
|
+
process.exit(EXIT_CODE.ERROR);
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
main();
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
#!/usr/bin/env npx tsx
|
|
2
|
+
import * as fs from 'node:fs';
|
|
3
|
+
import * as path from 'node:path';
|
|
4
|
+
|
|
5
|
+
import { parseHookInput } from '../src/hook-input.js';
|
|
6
|
+
import { writeHookLog } from '../src/hook-logger.js';
|
|
7
|
+
import { handlePreToolUse } from '../src/hooks/pre-tool-use.js';
|
|
8
|
+
|
|
9
|
+
const input = parseHookInput(fs.readFileSync(0, 'utf-8'));
|
|
10
|
+
const claudeDir = path.resolve(process.cwd(), '.claude');
|
|
11
|
+
const startTime = Date.now();
|
|
12
|
+
|
|
13
|
+
handlePreToolUse(claudeDir, input.session_id, input.tool_input || {})
|
|
14
|
+
.then((result) => {
|
|
15
|
+
writeHookLog(claudeDir, {
|
|
16
|
+
hookName: 'pre-tool-use',
|
|
17
|
+
timestamp: new Date().toISOString(),
|
|
18
|
+
input,
|
|
19
|
+
output: result,
|
|
20
|
+
durationMs: Date.now() - startTime,
|
|
21
|
+
});
|
|
22
|
+
console.log(JSON.stringify(result));
|
|
23
|
+
})
|
|
24
|
+
.catch((err) => {
|
|
25
|
+
writeHookLog(claudeDir, {
|
|
26
|
+
hookName: 'pre-tool-use',
|
|
27
|
+
timestamp: new Date().toISOString(),
|
|
28
|
+
input,
|
|
29
|
+
output: null,
|
|
30
|
+
error: String(err),
|
|
31
|
+
durationMs: Date.now() - startTime,
|
|
32
|
+
});
|
|
33
|
+
console.error('pre-tool-use hook failed:', err);
|
|
34
|
+
process.exit(1);
|
|
35
|
+
});
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
#!/usr/bin/env npx tsx
|
|
2
|
+
import * as fs from 'node:fs';
|
|
3
|
+
import * as path from 'node:path';
|
|
4
|
+
|
|
5
|
+
import { parseHookInput } from '../src/hook-input.js';
|
|
6
|
+
import { writeHookLog } from '../src/hook-logger.js';
|
|
7
|
+
import { handleSessionStart } from '../src/hooks/session-start.js';
|
|
8
|
+
|
|
9
|
+
const input = parseHookInput(fs.readFileSync(0, 'utf-8'));
|
|
10
|
+
const claudeDir = path.resolve(process.cwd(), '.claude');
|
|
11
|
+
const startTime = Date.now();
|
|
12
|
+
|
|
13
|
+
handleSessionStart(claudeDir, input.session_id)
|
|
14
|
+
.then(({ diagnostics, ...result }) => {
|
|
15
|
+
writeHookLog(claudeDir, {
|
|
16
|
+
hookName: 'session-start',
|
|
17
|
+
timestamp: new Date().toISOString(),
|
|
18
|
+
input,
|
|
19
|
+
resolvedPaths: diagnostics.resolvedPaths,
|
|
20
|
+
reminderFiles: diagnostics.reminderFiles,
|
|
21
|
+
matchedReminders: diagnostics.matchedReminders,
|
|
22
|
+
output: result,
|
|
23
|
+
durationMs: Date.now() - startTime,
|
|
24
|
+
});
|
|
25
|
+
console.log(JSON.stringify(result));
|
|
26
|
+
})
|
|
27
|
+
.catch((err) => {
|
|
28
|
+
writeHookLog(claudeDir, {
|
|
29
|
+
hookName: 'session-start',
|
|
30
|
+
timestamp: new Date().toISOString(),
|
|
31
|
+
input,
|
|
32
|
+
output: null,
|
|
33
|
+
error: String(err),
|
|
34
|
+
durationMs: Date.now() - startTime,
|
|
35
|
+
});
|
|
36
|
+
console.error('session-start hook failed:', err);
|
|
37
|
+
process.exit(1);
|
|
38
|
+
});
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Tail all ketchup log files in real-time
|
|
3
|
+
# Usage: ./tail-logs.sh [logs-dir]
|
|
4
|
+
|
|
5
|
+
LOGS_DIR="${1:-.claude/logs}"
|
|
6
|
+
|
|
7
|
+
if [ ! -d "$LOGS_DIR" ]; then
|
|
8
|
+
echo "Logs directory not found: $LOGS_DIR"
|
|
9
|
+
exit 1
|
|
10
|
+
fi
|
|
11
|
+
|
|
12
|
+
echo "Tailing logs in $LOGS_DIR..."
|
|
13
|
+
echo "Press Ctrl+C to stop"
|
|
14
|
+
echo
|
|
15
|
+
|
|
16
|
+
# Use tail -f with all .log files, showing which file each line comes from
|
|
17
|
+
find "$LOGS_DIR" -name "*.log" -exec tail -f {} + 2>/dev/null
|