jettypod 4.1.2 → 4.1.4
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/.nvmrc +1 -0
- package/docs/COMPLETE-TESTING-STRATEGY.md +970 -0
- package/docs/DECISIONS.md +10 -12
- package/docs/NODE_VERSION.md +83 -0
- package/docs/TDD-INFRASTRUCTURE-STRATEGY.md +1374 -0
- package/docs/TESTING-FOR-NON-ENGINEERS.md +1588 -0
- package/docs/TESTING-STRATEGY-AUDIT.md +698 -0
- package/hooks/post-checkout +17 -0
- package/hooks/post-merge +17 -0
- package/hooks/pre-commit +30 -0
- package/jettypod.js +259 -120
- package/lib/coverage-tracker.js +218 -0
- package/lib/database.js +2 -0
- package/lib/db-export.js +192 -0
- package/lib/db-import.js +193 -0
- package/lib/external-transition-handler.js +32 -0
- package/lib/git-hook-helpers.js +174 -0
- package/lib/git-root.js +90 -0
- package/lib/infrastructure-chore-generator.js +45 -0
- package/lib/install-hooks.js +52 -0
- package/lib/jettypod-backup.js +238 -0
- package/lib/merge-lock.js +193 -0
- package/lib/migrations/012-add-worktree-path.js +38 -0
- package/lib/migrations/013-worktrees-table.js +86 -0
- package/lib/migrations/014-migrate-worktree-data.js +161 -0
- package/lib/migrations/015-merge-locks-table.js +67 -0
- package/lib/pattern-finder.js +152 -0
- package/lib/process-manager.js +140 -0
- package/lib/production-standards-reader.js +13 -2
- package/lib/production-standards-writer.js +85 -0
- package/lib/skills/feature-planning/dry-run-validator.js +135 -0
- package/lib/skills/feature-planning/validation-formatter.js +160 -0
- package/lib/smart-conflict-detection.js +168 -0
- package/lib/smart-fetch-rebase.js +614 -0
- package/lib/step-definition-parser.js +76 -0
- package/lib/unit-test-generator.js +232 -0
- package/lib/verification-command-generator.js +66 -0
- package/lib/worktree-diagnostics.js +413 -0
- package/lib/worktree-facade.js +174 -0
- package/lib/worktree-manager.js +636 -0
- package/lib/worktree-reconciler.js +429 -0
- package/package.json +30 -3
- package/skills-templates/external-transition/SKILL.md +34 -3
- package/skills-templates/feature-planning/SKILL.md +190 -24
- package/skills-templates/production-mode/SKILL.md +127 -9
- package/skills-templates/speed-mode/SKILL.md +454 -51
- package/skills-templates/stable-mode/SKILL.md +285 -76
- package/.claude/PROTECT_SKILLS.md +0 -28
- package/.claude/settings.json +0 -24
- package/.claude/settings.local.json +0 -16
- package/.claude/skills/epic-planning/SKILL.md +0 -297
- package/.claude/skills/external-transition/SKILL.md +0 -384
- package/.claude/skills/feature-planning/SKILL.md +0 -464
- package/.claude/skills/production-mode/SKILL.md +0 -369
- package/.claude/skills/speed-mode/SKILL.md +0 -481
- package/.claude/skills/stable-mode/SKILL.md +0 -713
- package/.claude/skills.backup-2025-11-10T23-33-09-368Z/epic-planning/SKILL.md +0 -297
- package/.claude/skills.backup-2025-11-10T23-33-09-368Z/feature-planning/SKILL.md +0 -464
- package/.claude/skills.backup-2025-11-10T23-33-09-368Z/speed-mode/SKILL.md +0 -467
- package/.claude/skills.backup-2025-11-10T23-33-09-368Z/stable-mode/SKILL.md +0 -673
- package/.claude/skills.backup-2025-11-11T16-15-10-070Z/epic-discover/SKILL.md +0 -297
- package/.claude/skills.backup-2025-11-11T16-42-43-212Z/epic-planning/SKILL.md +0 -297
- package/.claude/skills.backup-2025-11-11T16-42-43-212Z/feature-planning/SKILL.md +0 -464
- package/.claude/skills.backup-2025-11-11T16-42-43-212Z/speed-mode/SKILL.md +0 -467
- package/.claude/skills.backup-2025-11-11T16-42-43-212Z/stable-mode/SKILL.md +0 -673
- package/.claude/skills.backup-2025-11-11T17-06-09-783Z/epic-planning/SKILL.md +0 -297
- package/.claude/skills.backup-2025-11-11T17-06-09-783Z/feature-planning/SKILL.md +0 -464
- package/.claude/skills.backup-2025-11-11T17-06-09-783Z/speed-mode/SKILL.md +0 -467
- package/.claude/skills.backup-2025-11-11T17-06-09-783Z/stable-mode/SKILL.md +0 -673
- package/.devpod/current-work.json +0 -10
- package/.devpod/work.db +0 -0
- package/.github/workflows/test-safety.yml +0 -85
- package/.jettypod/config.json +0 -5
- package/.jettypod/current-work.json +0 -10
- package/.jettypod/hooks/README.md +0 -77
- package/.jettypod/hooks/protect-claude-md.js +0 -338
- package/.jettypod/test-work.db +0 -0
- package/.jettypod/work.db +0 -0
- package/CLAUDE.md +0 -49
- package/SPEED-STABLE-AUDIT.md +0 -853
- package/SYSTEM-BEHAVIOR.md +0 -2199
- package/TEST_SAFETY_AUDIT.md +0 -314
- package/TEST_SAFETY_IMPLEMENTATION.md +0 -97
- package/cucumber-report.html +0 -45
- package/dist/devpod-linux +0 -0
- package/dist/devpod-macos +0 -0
- package/dist/devpod-win.exe +0 -0
- package/docs/features/jettypod-standards-explained.md +0 -543
- package/docs/features/standards-inventory.md +0 -257
- package/features/auto-generate-production-chores.feature +0 -13
- package/features/backlog-command.feature +0 -26
- package/features/backlog-filtering-production.feature +0 -10
- package/features/claude-md-protection/steps.js +0 -498
- package/features/decisions/index.js +0 -490
- package/features/decisions/index.test.js +0 -208
- package/features/fix-text-wrapping.feature +0 -42
- package/features/git-hooks/git-hooks.feature +0 -30
- package/features/git-hooks/index.js +0 -93
- package/features/git-hooks/index.test.js +0 -137
- package/features/git-hooks/post-commit +0 -56
- package/features/git-hooks/post-merge +0 -47
- package/features/git-hooks/pre-commit +0 -28
- package/features/git-hooks/simple-steps.js +0 -53
- package/features/git-hooks/simple-test.feature +0 -10
- package/features/git-hooks/steps.js +0 -196
- package/features/jettypod-update-command.feature +0 -46
- package/features/mode-prompts/index.js +0 -95
- package/features/mode-prompts/simple-steps.js +0 -44
- package/features/mode-prompts/simple-test.feature +0 -9
- package/features/mode-prompts/validation.test.js +0 -120
- package/features/multiple-claude-instances.feature +0 -121
- package/features/production-mode-skill.feature +0 -121
- package/features/refactor-mode/steps.js +0 -217
- package/features/refactor-mode.feature +0 -49
- package/features/simplify-external-transition.feature +0 -166
- package/features/skills-update/index.test.js +0 -216
- package/features/step_definitions/backlog-command.steps.js +0 -37
- package/features/step_definitions/fix-text-wrapping.steps.js +0 -271
- package/features/step_definitions/multiple-claude-instances.steps.js +0 -621
- package/features/step_definitions/production-mode-skill.steps.js +0 -862
- package/features/step_definitions/simplify-external-transition.steps.js +0 -370
- package/features/step_definitions/terminal-logo.steps.js +0 -145
- package/features/step_definitions/update-command.steps.js +0 -183
- package/features/support/hooks.js +0 -9
- package/features/terminal-logo/index.js +0 -39
- package/features/terminal-logo/terminal-logo.feature +0 -30
- package/features/update-command/index.js +0 -181
- package/features/update-command/index.test.js +0 -225
- package/features/work-commands/bug-workflow-display.feature +0 -22
- package/features/work-commands/index.js +0 -498
- package/features/work-commands/simple-steps.js +0 -69
- package/features/work-commands/stable-tests.feature +0 -57
- package/features/work-commands/steps.js +0 -1174
- package/features/work-commands/validation.test.js +0 -88
- package/features/work-commands/work-commands.feature +0 -13
- package/features/work-tracking/discovery-validation.test.js +0 -228
- package/features/work-tracking/index.js +0 -1921
- package/features/work-tracking/mode-required.feature +0 -112
- package/features/work-tracking/phase-tracking.test.js +0 -482
- package/features/work-tracking/prototype-tracking.test.js +0 -485
- package/features/work-tracking/tree-view.test.js +0 -310
- package/features/work-tracking/work-set-mode.feature +0 -71
- package/features/work-tracking/work-start-mode.feature +0 -88
- package/full-test.txt +0 -0
- package/lib/bug-workflow.test.js +0 -177
- package/lib/claudemd.test.js +0 -195
- package/lib/config.test.js +0 -511
- package/lib/constants.test.js +0 -164
- package/lib/current-work.test.js +0 -146
- package/lib/database-project-config.test.js +0 -111
- package/lib/database.test.js +0 -106
- package/lib/decisions-generator.test.js +0 -457
- package/lib/decisions-helpers.test.js +0 -310
- package/lib/git-coordinator.js +0 -167
- package/lib/git.test.js +0 -145
- package/lib/migrations/002-default-work-item-modes.test.js +0 -351
- package/lib/production-chore-generator.test.js +0 -432
- package/lib/production-context-detector.test.js +0 -277
- package/lib/production-scenario-appender.test.js +0 -235
- package/lib/production-scenario-validator.test.js +0 -246
- package/lib/production-standards-reader.test.js +0 -270
- package/lib/project-state.test.js +0 -92
- package/lib/push-queue.js +0 -417
- package/lib/queue-processor.js +0 -74
- package/lib/test-helpers.js +0 -202
- package/lib/test-helpers.test.js +0 -255
- package/prototypes/2025-01-11-production-mode-autonomous.js +0 -119
- package/prototypes/2025-01-11-production-mode-collaborative.js +0 -166
- package/prototypes/2025-01-11-production-mode-guided.js +0 -217
- package/prototypes/2025-01-11-production-mode-smart-context.js +0 -347
- package/prototypes/2025-01-11-production-standards-example.md +0 -204
- package/prototypes/2025-11-10-backlog-filtering-tree-aware.js +0 -242
- package/prototypes/test/index.html +0 -1
- package/setup-dist-repo.sh +0 -68
- package/test-production-standards-engine.js +0 -130
- package/test-results.json +0 -2195
- package/test-safety-check.sh +0 -80
- package/work-item-tracking-plan.md +0 -199
- /package/{.jettypod/devpod.db → jettypod.db} +0 -0
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
Feature: Fix Text Wrapping in Terminal Output
|
|
2
|
-
Terminal-aware word wrapping that respects terminal width and aligns
|
|
3
|
-
continuation lines with tree structure indentation
|
|
4
|
-
|
|
5
|
-
Approach: Terminal-Aware Word-Wrap with Alignment
|
|
6
|
-
|
|
7
|
-
@text-wrapping
|
|
8
|
-
Scenario: Long titles wrap at word boundaries within terminal width
|
|
9
|
-
Given I have a feature with a title longer than the terminal width
|
|
10
|
-
When I run jettypod backlog with current terminal width
|
|
11
|
-
Then the title wraps at word boundaries
|
|
12
|
-
And continuation lines align with the content indentation
|
|
13
|
-
And no words are split mid-character
|
|
14
|
-
|
|
15
|
-
@text-wrapping
|
|
16
|
-
Scenario: Long descriptions wrap cleanly in expanded view
|
|
17
|
-
Given I have a feature with a long description
|
|
18
|
-
When I run jettypod backlog --expand=1 with current terminal width
|
|
19
|
-
Then the description wraps at word boundaries
|
|
20
|
-
And continuation lines have proper "Description: " prefix indentation
|
|
21
|
-
And the text fits within terminal width
|
|
22
|
-
|
|
23
|
-
@text-wrapping
|
|
24
|
-
Scenario: Tree structure preserved with wrapped text
|
|
25
|
-
Given I have nested items with long titles
|
|
26
|
-
When I run jettypod backlog --expand=all with current terminal width
|
|
27
|
-
Then tree connectors (├──, └──) display correctly
|
|
28
|
-
And wrapped continuation lines align under the item content
|
|
29
|
-
And tree structure remains visually clear
|
|
30
|
-
|
|
31
|
-
@text-wrapping
|
|
32
|
-
Scenario: Terminal width changes are respected
|
|
33
|
-
Given the terminal width is 120 columns
|
|
34
|
-
And I have items with titles that wrap at 120 columns
|
|
35
|
-
When the terminal is resized to 80 columns
|
|
36
|
-
And I run jettypod backlog with current terminal width
|
|
37
|
-
Then text wraps to fit 80 columns
|
|
38
|
-
And alignment adjusts to the new width
|
|
39
|
-
|
|
40
|
-
# SPEED MODE: Only happy paths above
|
|
41
|
-
# STABLE MODE: Will add error handling (very narrow terminals, missing columns value, very long words)
|
|
42
|
-
# PRODUCTION MODE: Will add performance testing for large backlogs
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
Feature: Git Hook Integration
|
|
2
|
-
As a developer
|
|
3
|
-
I want work item status to update automatically on git operations
|
|
4
|
-
So that I don't have to manually track progress
|
|
5
|
-
|
|
6
|
-
Scenario: First commit updates status to in_progress
|
|
7
|
-
Given I have a work item with status "todo"
|
|
8
|
-
And the work item is set as current work
|
|
9
|
-
When I make my first commit
|
|
10
|
-
Then the work item status should be "in_progress"
|
|
11
|
-
|
|
12
|
-
Scenario: Merge to main updates status to done
|
|
13
|
-
Given I have a work item with status "in_progress"
|
|
14
|
-
And the work item is set as current work
|
|
15
|
-
And I am on a feature branch
|
|
16
|
-
When I merge to main
|
|
17
|
-
Then the work item status should be "done"
|
|
18
|
-
|
|
19
|
-
Scenario: No current work item - hooks do nothing
|
|
20
|
-
Given no work item is set as current
|
|
21
|
-
When I make a commit
|
|
22
|
-
Then no errors occur
|
|
23
|
-
|
|
24
|
-
Scenario: Integration - hooks work with existing work commands
|
|
25
|
-
Given I have initialized jettypod with git
|
|
26
|
-
And I create a work item via work commands
|
|
27
|
-
And I start work on the item
|
|
28
|
-
When I commit changes
|
|
29
|
-
Then the work item status updates automatically
|
|
30
|
-
And the current work file still exists
|
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
const fs = require('fs');
|
|
2
|
-
const path = require('path');
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Install JettyPod git hooks into .git/hooks directory
|
|
6
|
-
* @returns {boolean} True if hooks installed successfully, false if not a git repo
|
|
7
|
-
* @throws {Error} If hook files cannot be copied or made executable
|
|
8
|
-
*/
|
|
9
|
-
function installHooks() {
|
|
10
|
-
const gitHooksDir = path.join(process.cwd(), '.git', 'hooks');
|
|
11
|
-
const jettypodHooksDir = path.join(__dirname);
|
|
12
|
-
|
|
13
|
-
if (!fs.existsSync(gitHooksDir)) {
|
|
14
|
-
console.log('⚠️ Not a git repository - skipping hook installation');
|
|
15
|
-
return false;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
const hooks = ['pre-commit', 'post-commit', 'post-merge'];
|
|
19
|
-
|
|
20
|
-
hooks.forEach(hook => {
|
|
21
|
-
// Check both locations: features/git-hooks and .jettypod/hooks
|
|
22
|
-
let sourcePath = path.join(jettypodHooksDir, hook);
|
|
23
|
-
if (!fs.existsSync(sourcePath)) {
|
|
24
|
-
sourcePath = path.join(process.cwd(), '.jettypod', 'hooks', hook);
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
if (!fs.existsSync(sourcePath)) {
|
|
28
|
-
console.log(`⚠️ Hook ${hook} not found, skipping`);
|
|
29
|
-
return;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
const targetPath = path.join(gitHooksDir, hook);
|
|
33
|
-
|
|
34
|
-
try {
|
|
35
|
-
// Read hook content and inject module path
|
|
36
|
-
let hookContent = fs.readFileSync(sourcePath, 'utf-8');
|
|
37
|
-
|
|
38
|
-
// Find the jettypod root (where node_modules is)
|
|
39
|
-
const jettypodRoot = path.dirname(require.main.filename);
|
|
40
|
-
const modulesPath = path.join(jettypodRoot, 'node_modules');
|
|
41
|
-
|
|
42
|
-
// Inject NODE_PATH setup after shebang
|
|
43
|
-
if (hookContent.includes('require(\'sqlite3\')')) {
|
|
44
|
-
hookContent = hookContent.replace(
|
|
45
|
-
'#!/usr/bin/env node',
|
|
46
|
-
`#!/usr/bin/env node\n\n// Injected module path\nprocess.env.NODE_PATH = '${modulesPath}' + ':' + (process.env.NODE_PATH || '');require('module').Module._initPaths();`
|
|
47
|
-
);
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
// Replace __JETTYPOD_ROOT__ placeholder with actual jettypod root path
|
|
51
|
-
hookContent = hookContent.replace(/__JETTYPOD_ROOT__/g, jettypodRoot);
|
|
52
|
-
|
|
53
|
-
fs.writeFileSync(targetPath, hookContent);
|
|
54
|
-
|
|
55
|
-
// Make executable
|
|
56
|
-
fs.chmodSync(targetPath, 0o755);
|
|
57
|
-
} catch (err) {
|
|
58
|
-
throw new Error(`Failed to install ${hook} hook: ${err.message}`);
|
|
59
|
-
}
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
console.log('✓ Git hooks installed (pre-commit, post-commit, post-merge)');
|
|
63
|
-
return true;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* Check if JettyPod git hooks are installed
|
|
68
|
-
* @returns {boolean} True if hooks are installed
|
|
69
|
-
*/
|
|
70
|
-
function areHooksInstalled() {
|
|
71
|
-
const gitHooksDir = path.join(process.cwd(), '.git', 'hooks');
|
|
72
|
-
if (!fs.existsSync(gitHooksDir)) {
|
|
73
|
-
return false;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
try {
|
|
77
|
-
const postCommitPath = path.join(gitHooksDir, 'post-commit');
|
|
78
|
-
if (!fs.existsSync(postCommitPath)) {
|
|
79
|
-
return false;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
const content = fs.readFileSync(postCommitPath, 'utf-8');
|
|
83
|
-
return content.includes('JettyPod');
|
|
84
|
-
} catch (err) {
|
|
85
|
-
// If we can't read the file, assume not installed
|
|
86
|
-
return false;
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
module.exports = {
|
|
91
|
-
installHooks,
|
|
92
|
-
areHooksInstalled
|
|
93
|
-
};
|
|
@@ -1,137 +0,0 @@
|
|
|
1
|
-
const fs = require('fs');
|
|
2
|
-
const path = require('path');
|
|
3
|
-
const { createTestEnvironment } = require('../../lib/test-helpers');
|
|
4
|
-
const { installHooks, areHooksInstalled } = require('./index');
|
|
5
|
-
const { execSync } = require('child_process');
|
|
6
|
-
|
|
7
|
-
describe('Git Hooks', () => {
|
|
8
|
-
let testEnv;
|
|
9
|
-
|
|
10
|
-
beforeEach(() => {
|
|
11
|
-
testEnv = createTestEnvironment();
|
|
12
|
-
process.chdir(testEnv.testDir);
|
|
13
|
-
});
|
|
14
|
-
|
|
15
|
-
afterEach(() => {
|
|
16
|
-
testEnv.cleanup();
|
|
17
|
-
});
|
|
18
|
-
|
|
19
|
-
describe('installHooks()', () => {
|
|
20
|
-
test('should return false if not a git repository', () => {
|
|
21
|
-
const result = installHooks();
|
|
22
|
-
expect(result).toBe(false);
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
test('should install hooks in git repository', () => {
|
|
26
|
-
// Initialize git repo
|
|
27
|
-
execSync('git init', { stdio: 'pipe' });
|
|
28
|
-
|
|
29
|
-
// Create source hooks
|
|
30
|
-
const hooksSourceDir = path.join(testEnv.testDir, 'features', 'git-hooks');
|
|
31
|
-
fs.mkdirSync(hooksSourceDir, { recursive: true });
|
|
32
|
-
|
|
33
|
-
fs.writeFileSync(path.join(hooksSourceDir, 'pre-commit'), '#!/usr/bin/env node\nconsole.log("test");');
|
|
34
|
-
fs.writeFileSync(path.join(hooksSourceDir, 'post-commit'), '#!/usr/bin/env node\n// JettyPod hook');
|
|
35
|
-
fs.writeFileSync(path.join(hooksSourceDir, 'post-merge'), '#!/usr/bin/env node\nconsole.log("test");');
|
|
36
|
-
|
|
37
|
-
const result = installHooks();
|
|
38
|
-
|
|
39
|
-
expect(result).toBe(true);
|
|
40
|
-
expect(fs.existsSync('.git/hooks/pre-commit')).toBe(true);
|
|
41
|
-
expect(fs.existsSync('.git/hooks/post-commit')).toBe(true);
|
|
42
|
-
expect(fs.existsSync('.git/hooks/post-merge')).toBe(true);
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
test('should make hooks executable', () => {
|
|
46
|
-
execSync('git init', { stdio: 'pipe' });
|
|
47
|
-
|
|
48
|
-
const hooksSourceDir = path.join(testEnv.testDir, 'features', 'git-hooks');
|
|
49
|
-
fs.mkdirSync(hooksSourceDir, { recursive: true });
|
|
50
|
-
|
|
51
|
-
fs.writeFileSync(path.join(hooksSourceDir, 'pre-commit'), '#!/usr/bin/env node\nconsole.log("test");');
|
|
52
|
-
fs.writeFileSync(path.join(hooksSourceDir, 'post-commit'), '#!/usr/bin/env node\n// JettyPod');
|
|
53
|
-
fs.writeFileSync(path.join(hooksSourceDir, 'post-merge'), '#!/usr/bin/env node\nconsole.log("test");');
|
|
54
|
-
|
|
55
|
-
installHooks();
|
|
56
|
-
|
|
57
|
-
const preCommitPath = path.join(testEnv.testDir, '.git', 'hooks', 'pre-commit');
|
|
58
|
-
const stats = fs.statSync(preCommitPath);
|
|
59
|
-
// Check if executable bit is set (mode & 0o111)
|
|
60
|
-
expect(stats.mode & 0o111).not.toBe(0);
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
test('should succeed when hooks exist in production directory', () => {
|
|
64
|
-
execSync('git init', { stdio: 'pipe' });
|
|
65
|
-
|
|
66
|
-
const consoleSpy = jest.spyOn(console, 'log').mockImplementation();
|
|
67
|
-
|
|
68
|
-
// This will find hooks in the real features/git-hooks directory
|
|
69
|
-
const result = installHooks();
|
|
70
|
-
|
|
71
|
-
expect(result).toBe(true);
|
|
72
|
-
consoleSpy.mockRestore();
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
test('should check alternate hook location (.jettypod/hooks)', () => {
|
|
76
|
-
execSync('git init', { stdio: 'pipe' });
|
|
77
|
-
|
|
78
|
-
const altHooksDir = path.join(testEnv.testDir, '.jettypod', 'hooks');
|
|
79
|
-
fs.mkdirSync(altHooksDir, { recursive: true });
|
|
80
|
-
|
|
81
|
-
fs.writeFileSync(path.join(altHooksDir, 'pre-commit'), '#!/usr/bin/env node\nconsole.log("test");');
|
|
82
|
-
fs.writeFileSync(path.join(altHooksDir, 'post-commit'), '#!/usr/bin/env node\n// JettyPod');
|
|
83
|
-
fs.writeFileSync(path.join(altHooksDir, 'post-merge'), '#!/usr/bin/env node\nconsole.log("test");');
|
|
84
|
-
|
|
85
|
-
const result = installHooks();
|
|
86
|
-
|
|
87
|
-
expect(result).toBe(true);
|
|
88
|
-
expect(fs.existsSync('.git/hooks/post-commit')).toBe(true);
|
|
89
|
-
});
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
describe('areHooksInstalled()', () => {
|
|
93
|
-
test('should return false if not a git repository', () => {
|
|
94
|
-
const result = areHooksInstalled();
|
|
95
|
-
expect(result).toBe(false);
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
test('should return false if hooks not installed', () => {
|
|
99
|
-
execSync('git init', { stdio: 'pipe' });
|
|
100
|
-
|
|
101
|
-
const result = areHooksInstalled();
|
|
102
|
-
expect(result).toBe(false);
|
|
103
|
-
});
|
|
104
|
-
|
|
105
|
-
test('should return true if JettyPod hooks installed', () => {
|
|
106
|
-
execSync('git init', { stdio: 'pipe' });
|
|
107
|
-
|
|
108
|
-
fs.writeFileSync('.git/hooks/post-commit', '#!/usr/bin/env node\n// JettyPod hook\nconsole.log("test");');
|
|
109
|
-
|
|
110
|
-
const result = areHooksInstalled();
|
|
111
|
-
expect(result).toBe(true);
|
|
112
|
-
});
|
|
113
|
-
|
|
114
|
-
test('should return false if hooks exist but are not JettyPod hooks', () => {
|
|
115
|
-
execSync('git init', { stdio: 'pipe' });
|
|
116
|
-
|
|
117
|
-
fs.writeFileSync('.git/hooks/post-commit', '#!/usr/bin/env node\nconsole.log("other hook");');
|
|
118
|
-
|
|
119
|
-
const result = areHooksInstalled();
|
|
120
|
-
expect(result).toBe(false);
|
|
121
|
-
});
|
|
122
|
-
|
|
123
|
-
test('should return false if hook file cannot be read', () => {
|
|
124
|
-
execSync('git init', { stdio: 'pipe' });
|
|
125
|
-
|
|
126
|
-
fs.writeFileSync('.git/hooks/post-commit', '#!/usr/bin/env node\n// JettyPod');
|
|
127
|
-
fs.chmodSync('.git/hooks/post-commit', 0o000);
|
|
128
|
-
|
|
129
|
-
const result = areHooksInstalled();
|
|
130
|
-
|
|
131
|
-
// Cleanup
|
|
132
|
-
fs.chmodSync('.git/hooks/post-commit', 0o644);
|
|
133
|
-
|
|
134
|
-
expect(result).toBe(false);
|
|
135
|
-
});
|
|
136
|
-
});
|
|
137
|
-
});
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
// JettyPod Git Hook: post-commit
|
|
4
|
-
// Auto-updates work item status and regenerates docs
|
|
5
|
-
|
|
6
|
-
const fs = require('fs');
|
|
7
|
-
const path = require('path');
|
|
8
|
-
const { execSync } = require('child_process');
|
|
9
|
-
|
|
10
|
-
const jettypodDir = path.join(process.cwd(), '.jettypod');
|
|
11
|
-
const currentWorkPath = path.join(jettypodDir, 'current-work.json');
|
|
12
|
-
const dbPath = path.join(jettypodDir, 'work.db');
|
|
13
|
-
|
|
14
|
-
// Update work item status if needed
|
|
15
|
-
(async () => {
|
|
16
|
-
if (fs.existsSync(currentWorkPath)) {
|
|
17
|
-
const currentWork = JSON.parse(fs.readFileSync(currentWorkPath, 'utf-8'));
|
|
18
|
-
|
|
19
|
-
// Only update if status is todo
|
|
20
|
-
if (currentWork.status === 'todo') {
|
|
21
|
-
try {
|
|
22
|
-
const { updateStatus } = require('../../features/work-tracking');
|
|
23
|
-
await updateStatus(currentWork.id, 'in_progress');
|
|
24
|
-
|
|
25
|
-
// Update current work file
|
|
26
|
-
currentWork.status = 'in_progress';
|
|
27
|
-
fs.writeFileSync(currentWorkPath, JSON.stringify(currentWork, null, 2));
|
|
28
|
-
|
|
29
|
-
console.log(`\n✓ Work item #${currentWork.id} status updated: todo → in_progress`);
|
|
30
|
-
} catch (err) {
|
|
31
|
-
console.error('Failed to update work item:', err);
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
})();
|
|
36
|
-
|
|
37
|
-
// Always generate docs on every commit
|
|
38
|
-
generateDocs();
|
|
39
|
-
|
|
40
|
-
function generateDocs() {
|
|
41
|
-
const featuresDir = path.join(process.cwd(), 'features');
|
|
42
|
-
const outputPath = path.join(process.cwd(), 'SYSTEM-BEHAVIOR.md');
|
|
43
|
-
|
|
44
|
-
// Skip if no features directory
|
|
45
|
-
if (!fs.existsSync(featuresDir)) {
|
|
46
|
-
return;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
try {
|
|
50
|
-
const docsGenerator = require(path.join(process.cwd(), 'lib', 'docs-generator'));
|
|
51
|
-
docsGenerator.generate();
|
|
52
|
-
console.log('📚 Documentation updated');
|
|
53
|
-
} catch (err) {
|
|
54
|
-
// Fail silently - docs generation is optional
|
|
55
|
-
}
|
|
56
|
-
}
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
// JettyPod Git Hook: post-merge
|
|
4
|
-
// Auto-updates work item status to done when merging to main
|
|
5
|
-
|
|
6
|
-
const fs = require('fs');
|
|
7
|
-
const path = require('path');
|
|
8
|
-
const { execSync } = require('child_process');
|
|
9
|
-
|
|
10
|
-
const jettypodDir = path.join(process.cwd(), '.jettypod');
|
|
11
|
-
const currentWorkPath = path.join(jettypodDir, 'current-work.json');
|
|
12
|
-
const dbPath = path.join(jettypodDir, 'work.db');
|
|
13
|
-
|
|
14
|
-
// Skip if no current work
|
|
15
|
-
if (!fs.existsSync(currentWorkPath)) {
|
|
16
|
-
process.exit(0);
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
// Check if we're on main/master
|
|
20
|
-
let currentBranch;
|
|
21
|
-
try {
|
|
22
|
-
currentBranch = execSync('git branch --show-current', { encoding: 'utf-8' }).trim();
|
|
23
|
-
} catch (e) {
|
|
24
|
-
process.exit(0);
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
if (currentBranch !== 'main' && currentBranch !== 'master') {
|
|
28
|
-
process.exit(0);
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
const currentWork = JSON.parse(fs.readFileSync(currentWorkPath, 'utf-8'));
|
|
32
|
-
|
|
33
|
-
// Update to done using shared updateStatus (includes epic auto-close logic)
|
|
34
|
-
(async () => {
|
|
35
|
-
try {
|
|
36
|
-
const { updateStatus } = require('__JETTYPOD_ROOT__/features/work-tracking');
|
|
37
|
-
await updateStatus(currentWork.id, 'done');
|
|
38
|
-
|
|
39
|
-
// Clear current work pointer since work is done
|
|
40
|
-
fs.unlinkSync(currentWorkPath);
|
|
41
|
-
|
|
42
|
-
console.log(`\n✓ Work item #${currentWork.id} status updated: → done (merged to ${currentBranch})`);
|
|
43
|
-
} catch (err) {
|
|
44
|
-
console.error('Failed to update work item:', err);
|
|
45
|
-
process.exit(1);
|
|
46
|
-
}
|
|
47
|
-
})();
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
// Pre-commit hook: Run tests before allowing commit
|
|
4
|
-
|
|
5
|
-
const { execSync } = require('child_process');
|
|
6
|
-
const fs = require('fs');
|
|
7
|
-
const path = require('path');
|
|
8
|
-
|
|
9
|
-
// Check if we're in a real project (not a test directory)
|
|
10
|
-
const packageJsonPath = path.join(process.cwd(), 'package.json');
|
|
11
|
-
if (!fs.existsSync(packageJsonPath)) {
|
|
12
|
-
// Skip tests in test directories
|
|
13
|
-
process.exit(0);
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
console.log('\n🧪 Running tests before commit...\n');
|
|
17
|
-
|
|
18
|
-
try {
|
|
19
|
-
// Run tests
|
|
20
|
-
execSync('npm test', { stdio: 'inherit' });
|
|
21
|
-
|
|
22
|
-
console.log('\n✅ Tests passed! Proceeding with commit.\n');
|
|
23
|
-
process.exit(0);
|
|
24
|
-
} catch (err) {
|
|
25
|
-
console.log('\n❌ Tests failed! Commit blocked.\n');
|
|
26
|
-
console.log('Fix the failing tests or use --no-verify to skip this check.\n');
|
|
27
|
-
process.exit(1);
|
|
28
|
-
}
|
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
const { Given, When, Then } = require('@cucumber/cucumber');
|
|
2
|
-
const assert = require('assert');
|
|
3
|
-
const fs = require('fs');
|
|
4
|
-
const path = require('path');
|
|
5
|
-
const { execSync } = require('child_process');
|
|
6
|
-
const sqlite3 = require('sqlite3').verbose();
|
|
7
|
-
|
|
8
|
-
const testDir = path.join('/tmp', 'hooks-simple-' + Date.now());
|
|
9
|
-
const dbFileName = process.env.NODE_ENV === 'test' ? 'test-work.db' : 'work.db';
|
|
10
|
-
let originalDir;
|
|
11
|
-
let workItemId;
|
|
12
|
-
|
|
13
|
-
Given('I initialize a git repo with jettypod', function () {
|
|
14
|
-
originalDir = process.cwd();
|
|
15
|
-
// SAFETY: Only delete if testDir is in /tmp
|
|
16
|
-
if (fs.existsSync(testDir) && testDir.startsWith('/tmp/')) {
|
|
17
|
-
fs.rmSync(testDir, { recursive: true, force: true });
|
|
18
|
-
}
|
|
19
|
-
fs.mkdirSync(testDir, { recursive: true });
|
|
20
|
-
process.chdir(testDir);
|
|
21
|
-
|
|
22
|
-
execSync('git init', { stdio: 'pipe' });
|
|
23
|
-
execSync('git config user.email "test@test.com"', { stdio: 'pipe' });
|
|
24
|
-
execSync('git config user.name "Test"', { stdio: 'pipe' });
|
|
25
|
-
execSync(`node ${path.join(originalDir, 'jettypod.js')} init`, { stdio: 'pipe', env: { ...process.env, NODE_ENV: 'test' } });
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
Given('I create and start work on a todo item', function () {
|
|
29
|
-
execSync(`node ${path.join(originalDir, 'jettypod.js')} work create feature "Test"`, { stdio: 'pipe', env: { ...process.env, NODE_ENV: 'test' } });
|
|
30
|
-
execSync(`node ${path.join(originalDir, 'jettypod.js')} work status 1 todo`, { stdio: 'pipe', env: { ...process.env, NODE_ENV: 'test' } });
|
|
31
|
-
execSync(`node ${path.join(originalDir, 'jettypod.js')} work start 1`, { stdio: 'pipe', env: { ...process.env, NODE_ENV: 'test' } });
|
|
32
|
-
workItemId = 1;
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
Then('the hook updates status to in_progress', function () {
|
|
36
|
-
const dbPath = path.join(testDir, '.jettypod', dbFileName);
|
|
37
|
-
const db = new sqlite3.Database(dbPath);
|
|
38
|
-
|
|
39
|
-
return new Promise((resolve) => {
|
|
40
|
-
db.get(`SELECT status FROM work_items WHERE id = ?`, [workItemId], (err, row) => {
|
|
41
|
-
db.close();
|
|
42
|
-
assert.strictEqual(row.status, 'in_progress');
|
|
43
|
-
|
|
44
|
-
// Cleanup
|
|
45
|
-
if (fs.existsSync(testDir)) {
|
|
46
|
-
process.chdir(originalDir);
|
|
47
|
-
fs.rmSync(testDir, { recursive: true, force: true });
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
resolve();
|
|
51
|
-
});
|
|
52
|
-
});
|
|
53
|
-
});
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
Feature: Git Hooks Integration
|
|
2
|
-
As a developer
|
|
3
|
-
I want work item status to auto-update on commits
|
|
4
|
-
So I don't manually track progress
|
|
5
|
-
|
|
6
|
-
Scenario: Integration - post-commit hook updates status
|
|
7
|
-
Given I initialize a git repo with jettypod
|
|
8
|
-
And I create and start work on a todo item
|
|
9
|
-
When I make a commit
|
|
10
|
-
Then the hook updates status to in_progress
|