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,85 +0,0 @@
|
|
|
1
|
-
name: Test Safety Verification
|
|
2
|
-
|
|
3
|
-
on:
|
|
4
|
-
push:
|
|
5
|
-
branches: [ main ]
|
|
6
|
-
pull_request:
|
|
7
|
-
branches: [ main ]
|
|
8
|
-
|
|
9
|
-
jobs:
|
|
10
|
-
test-safety:
|
|
11
|
-
runs-on: ubuntu-latest
|
|
12
|
-
|
|
13
|
-
steps:
|
|
14
|
-
- uses: actions/checkout@v3
|
|
15
|
-
|
|
16
|
-
- name: Setup Node.js
|
|
17
|
-
uses: actions/setup-node@v3
|
|
18
|
-
with:
|
|
19
|
-
node-version: '18'
|
|
20
|
-
|
|
21
|
-
- name: Install dependencies
|
|
22
|
-
run: npm install
|
|
23
|
-
|
|
24
|
-
- name: Create production snapshot
|
|
25
|
-
run: |
|
|
26
|
-
echo "Creating MD5 checksums of all production files..."
|
|
27
|
-
find . -type f \( -name "*.json" -o -name "*.js" -o -name "*.md" \) \
|
|
28
|
-
-not -path "./node_modules/*" \
|
|
29
|
-
-not -path "./.git/*" \
|
|
30
|
-
-not -path "./tmp/*" \
|
|
31
|
-
| xargs md5sum > before-tests.md5
|
|
32
|
-
|
|
33
|
-
echo "Recording package.json state..."
|
|
34
|
-
cp package.json package.json.backup
|
|
35
|
-
|
|
36
|
-
echo "Recording directory structure..."
|
|
37
|
-
ls -la > before-tests-ls.txt
|
|
38
|
-
|
|
39
|
-
- name: Run tests
|
|
40
|
-
run: npm test
|
|
41
|
-
continue-on-error: true
|
|
42
|
-
|
|
43
|
-
- name: Verify no production changes
|
|
44
|
-
run: |
|
|
45
|
-
echo "Checking for file modifications..."
|
|
46
|
-
find . -type f \( -name "*.json" -o -name "*.js" -o -name "*.md" \) \
|
|
47
|
-
-not -path "./node_modules/*" \
|
|
48
|
-
-not -path "./.git/*" \
|
|
49
|
-
-not -path "./tmp/*" \
|
|
50
|
-
| xargs md5sum > after-tests.md5
|
|
51
|
-
|
|
52
|
-
echo "Comparing checksums..."
|
|
53
|
-
if ! diff before-tests.md5 after-tests.md5; then
|
|
54
|
-
echo "❌ CRITICAL: Tests modified production files!"
|
|
55
|
-
echo "Modified files:"
|
|
56
|
-
diff before-tests.md5 after-tests.md5 | grep "^[<>]"
|
|
57
|
-
exit 1
|
|
58
|
-
fi
|
|
59
|
-
|
|
60
|
-
echo "Checking package.json..."
|
|
61
|
-
if ! diff package.json.backup package.json; then
|
|
62
|
-
echo "❌ CRITICAL: package.json was modified!"
|
|
63
|
-
diff package.json.backup package.json
|
|
64
|
-
exit 1
|
|
65
|
-
fi
|
|
66
|
-
|
|
67
|
-
echo "Checking for leaked test files..."
|
|
68
|
-
ls -la > after-tests-ls.txt
|
|
69
|
-
NEW_FILES=$(diff before-tests-ls.txt after-tests-ls.txt | grep "^>" | grep -E "(temp_|actual_|main|server|utils|Calculator|helper|app)" || true)
|
|
70
|
-
if [ ! -z "$NEW_FILES" ]; then
|
|
71
|
-
echo "❌ CRITICAL: Tests leaked files to production!"
|
|
72
|
-
echo "$NEW_FILES"
|
|
73
|
-
exit 1
|
|
74
|
-
fi
|
|
75
|
-
|
|
76
|
-
echo "✅ All safety checks passed - production directory unchanged"
|
|
77
|
-
|
|
78
|
-
- name: Check for temp directory cleanup
|
|
79
|
-
run: |
|
|
80
|
-
echo "Checking /tmp for leaked test directories..."
|
|
81
|
-
LEAKED_DIRS=$(ls -la /tmp | grep "devpod-test-" || true)
|
|
82
|
-
if [ ! -z "$LEAKED_DIRS" ]; then
|
|
83
|
-
echo "⚠️ WARNING: Test directories not cleaned up in /tmp:"
|
|
84
|
-
echo "$LEAKED_DIRS"
|
|
85
|
-
fi
|
package/.jettypod/config.json
DELETED
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
# JettyPod Hooks
|
|
2
|
-
|
|
3
|
-
## protect-claude-md.js
|
|
4
|
-
|
|
5
|
-
**Purpose**: Prevents Claude Code from directly editing protected sections of CLAUDE.md
|
|
6
|
-
|
|
7
|
-
**Status**: ✅ Shipped (integrated in jettypod init)
|
|
8
|
-
|
|
9
|
-
**What it does**:
|
|
10
|
-
- Intercepts Edit/Write operations via Claude Code's PreToolUse hook
|
|
11
|
-
- Blocks direct edits to: `<mode>`, `<stage>`, `<current_work>`, work status
|
|
12
|
-
- Returns helpful errors: "Use: jettypod speed" instead of "forbidden"
|
|
13
|
-
- Allows all other CLAUDE.md edits (mission, standards, etc.)
|
|
14
|
-
- Zero impact on non-CLAUDE.md files
|
|
15
|
-
|
|
16
|
-
**Installation**:
|
|
17
|
-
Automatically installed when you run `jettypod init`. Hook is configured in `.claude/settings.json`.
|
|
18
|
-
|
|
19
|
-
**Manual installation**:
|
|
20
|
-
```bash
|
|
21
|
-
# Hook is already in .jettypod/hooks/
|
|
22
|
-
# Just configure Claude Code:
|
|
23
|
-
cat > .claude/settings.json << 'EOF'
|
|
24
|
-
{
|
|
25
|
-
"hooks": {
|
|
26
|
-
"PreToolUse": [
|
|
27
|
-
{
|
|
28
|
-
"matcher": "Edit",
|
|
29
|
-
"hooks": [{ "type": "command", "command": ".jettypod/hooks/protect-claude-md.js" }]
|
|
30
|
-
},
|
|
31
|
-
{
|
|
32
|
-
"matcher": "Write",
|
|
33
|
-
"hooks": [{ "type": "command", "command": ".jettypod/hooks/protect-claude-md.js" }]
|
|
34
|
-
}
|
|
35
|
-
]
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
EOF
|
|
39
|
-
```
|
|
40
|
-
|
|
41
|
-
**Testing**:
|
|
42
|
-
```bash
|
|
43
|
-
# Run built-in tests
|
|
44
|
-
node .jettypod/hooks/protect-claude-md.js test
|
|
45
|
-
|
|
46
|
-
# Test with sample edit
|
|
47
|
-
echo '{
|
|
48
|
-
"tool_name": "Edit",
|
|
49
|
-
"tool_input": {
|
|
50
|
-
"file_path": "CLAUDE.md",
|
|
51
|
-
"old_string": "<mode>discovery</mode>",
|
|
52
|
-
"new_string": "<mode>speed</mode>"
|
|
53
|
-
}
|
|
54
|
-
}' | .jettypod/hooks/protect-claude-md.js
|
|
55
|
-
```
|
|
56
|
-
|
|
57
|
-
**Expected behavior**:
|
|
58
|
-
When Claude tries to edit CLAUDE.md directly:
|
|
59
|
-
```
|
|
60
|
-
❌ Cannot change <mode> tag directly
|
|
61
|
-
|
|
62
|
-
💡 Hint: Use: jettypod speed
|
|
63
|
-
```
|
|
64
|
-
|
|
65
|
-
**Debugging**:
|
|
66
|
-
If hook isn't working:
|
|
67
|
-
1. Check `.claude/settings.json` exists with hook config
|
|
68
|
-
2. Verify hook is executable: `chmod +x .jettypod/hooks/protect-claude-md.js`
|
|
69
|
-
3. Test hook directly (see Testing above)
|
|
70
|
-
4. Check Claude Code version supports PreToolUse hooks
|
|
71
|
-
|
|
72
|
-
**Known limitations**:
|
|
73
|
-
- Can be bypassed with Bash redirection (`echo > CLAUDE.md`)
|
|
74
|
-
- Not a concern in practice - Claude uses Edit tool by default
|
|
75
|
-
- Could add Bash hook if bypass becomes common
|
|
76
|
-
|
|
77
|
-
**Performance**: <10ms per edit check
|
|
@@ -1,338 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
// Prototype: Enforce jettypod command usage - Balanced (Claude Code Protocol)
|
|
3
|
-
// Created: 2025-10-16
|
|
4
|
-
// Purpose: PreToolUse hook that blocks direct CLAUDE.md edits and suggests jettypod commands
|
|
5
|
-
// Decision: Updated to match actual Claude Code hook protocol
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Claude Code PreToolUse Hook
|
|
9
|
-
*
|
|
10
|
-
* Intercepts Edit/Write operations on CLAUDE.md and blocks
|
|
11
|
-
* forbidden patterns like mode changes or current_work updates.
|
|
12
|
-
*
|
|
13
|
-
* Configuration (add to ~/.claude/settings.json or .claude/settings.json):
|
|
14
|
-
* {
|
|
15
|
-
* "hooks": {
|
|
16
|
-
* "PreToolUse": [
|
|
17
|
-
* {
|
|
18
|
-
* "matcher": "Edit",
|
|
19
|
-
* "hooks": [
|
|
20
|
-
* {
|
|
21
|
-
* "type": "command",
|
|
22
|
-
* "command": "./prototypes/2025-10-16-enforce-jettypod-balanced-v2.js"
|
|
23
|
-
* }
|
|
24
|
-
* ]
|
|
25
|
-
* },
|
|
26
|
-
* {
|
|
27
|
-
* "matcher": "Write",
|
|
28
|
-
* "hooks": [
|
|
29
|
-
* {
|
|
30
|
-
* "type": "command",
|
|
31
|
-
* "command": "./prototypes/2025-10-16-enforce-jettypod-balanced-v2.js"
|
|
32
|
-
* }
|
|
33
|
-
* ]
|
|
34
|
-
* }
|
|
35
|
-
* ]
|
|
36
|
-
* }
|
|
37
|
-
* }
|
|
38
|
-
*
|
|
39
|
-
* Hook Input (via stdin):
|
|
40
|
-
* {
|
|
41
|
-
* "session_id": "...",
|
|
42
|
-
* "transcript_path": "...",
|
|
43
|
-
* "cwd": "...",
|
|
44
|
-
* "hook_event_name": "PreToolUse",
|
|
45
|
-
* "tool_name": "Edit" | "Write",
|
|
46
|
-
* "tool_input": {
|
|
47
|
-
* "file_path": "/path/to/CLAUDE.md",
|
|
48
|
-
* "old_string": "original content", // Edit only
|
|
49
|
-
* "new_string": "proposed new content", // Edit only
|
|
50
|
-
* "content": "file content" // Write only
|
|
51
|
-
* }
|
|
52
|
-
* }
|
|
53
|
-
*
|
|
54
|
-
* Hook Output (to stdout):
|
|
55
|
-
* {
|
|
56
|
-
* "hookSpecificOutput": {
|
|
57
|
-
* "hookEventName": "PreToolUse",
|
|
58
|
-
* "permissionDecision": "allow" | "deny" | "ask",
|
|
59
|
-
* "permissionDecisionReason": "Explanation"
|
|
60
|
-
* }
|
|
61
|
-
* }
|
|
62
|
-
*/
|
|
63
|
-
|
|
64
|
-
const fs = require('fs');
|
|
65
|
-
|
|
66
|
-
// Read hook input from stdin
|
|
67
|
-
let input = '';
|
|
68
|
-
process.stdin.on('data', chunk => input += chunk);
|
|
69
|
-
process.stdin.on('end', () => {
|
|
70
|
-
try {
|
|
71
|
-
const hookInput = JSON.parse(input);
|
|
72
|
-
const { tool_name, tool_input, cwd } = hookInput;
|
|
73
|
-
|
|
74
|
-
// Only check CLAUDE.md edits
|
|
75
|
-
if (!tool_input.file_path || !tool_input.file_path.endsWith('CLAUDE.md')) {
|
|
76
|
-
allowEdit();
|
|
77
|
-
return;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
let oldContent, newContent;
|
|
81
|
-
|
|
82
|
-
if (tool_name === 'Edit') {
|
|
83
|
-
oldContent = tool_input.old_string;
|
|
84
|
-
newContent = tool_input.new_string;
|
|
85
|
-
} else if (tool_name === 'Write') {
|
|
86
|
-
// For Write, read the current file content (if exists) to compare
|
|
87
|
-
try {
|
|
88
|
-
oldContent = fs.readFileSync(tool_input.file_path, 'utf8');
|
|
89
|
-
newContent = tool_input.content;
|
|
90
|
-
} catch (err) {
|
|
91
|
-
// File doesn't exist yet - allow new file creation
|
|
92
|
-
allowEdit();
|
|
93
|
-
return;
|
|
94
|
-
}
|
|
95
|
-
} else {
|
|
96
|
-
// Unknown tool
|
|
97
|
-
allowEdit();
|
|
98
|
-
return;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
const result = checkEdit(oldContent, newContent);
|
|
102
|
-
|
|
103
|
-
if (result.blocked) {
|
|
104
|
-
denyEdit(result.message, result.suggestion);
|
|
105
|
-
} else {
|
|
106
|
-
allowEdit();
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
} catch (err) {
|
|
110
|
-
// If we can't parse input, allow the edit (fail open)
|
|
111
|
-
console.error('Hook error:', err.message);
|
|
112
|
-
allowEdit();
|
|
113
|
-
}
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
/**
|
|
117
|
-
* Allow the edit
|
|
118
|
-
*/
|
|
119
|
-
function allowEdit() {
|
|
120
|
-
console.log(JSON.stringify({
|
|
121
|
-
hookSpecificOutput: {
|
|
122
|
-
hookEventName: "PreToolUse",
|
|
123
|
-
permissionDecision: "allow"
|
|
124
|
-
}
|
|
125
|
-
}));
|
|
126
|
-
process.exit(0);
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
/**
|
|
130
|
-
* Deny the edit with explanation
|
|
131
|
-
*/
|
|
132
|
-
function denyEdit(message, suggestion) {
|
|
133
|
-
const reason = `❌ ${message}\n\n💡 Hint: ${suggestion}`;
|
|
134
|
-
|
|
135
|
-
console.log(JSON.stringify({
|
|
136
|
-
hookSpecificOutput: {
|
|
137
|
-
hookEventName: "PreToolUse",
|
|
138
|
-
permissionDecision: "deny",
|
|
139
|
-
permissionDecisionReason: reason
|
|
140
|
-
}
|
|
141
|
-
}));
|
|
142
|
-
process.exit(0);
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
/**
|
|
146
|
-
* Pattern definitions for forbidden edits
|
|
147
|
-
*/
|
|
148
|
-
const FORBIDDEN_PATTERNS = [
|
|
149
|
-
{
|
|
150
|
-
name: 'mode_change',
|
|
151
|
-
detect: (old, newStr) => {
|
|
152
|
-
const oldMode = old.match(/<mode>(\w+)<\/mode>/);
|
|
153
|
-
const newMode = newStr.match(/<mode>(\w+)<\/mode>/);
|
|
154
|
-
return oldMode && newMode && oldMode[1] !== newMode[1];
|
|
155
|
-
},
|
|
156
|
-
message: 'Cannot change <mode> tag directly',
|
|
157
|
-
suggestion: (old, newStr) => {
|
|
158
|
-
const newMode = newStr.match(/<mode>(\w+)<\/mode>/)?.[1];
|
|
159
|
-
return `Use: jettypod ${newMode}`;
|
|
160
|
-
}
|
|
161
|
-
},
|
|
162
|
-
{
|
|
163
|
-
name: 'current_work_change',
|
|
164
|
-
detect: (old, newStr) => {
|
|
165
|
-
const oldWork = old.match(/<current_work>[\s\S]*?<\/current_work>/);
|
|
166
|
-
const newWork = newStr.match(/<current_work>[\s\S]*?<\/current_work>/);
|
|
167
|
-
|
|
168
|
-
// Block if current_work section content changes
|
|
169
|
-
if (oldWork && newWork && oldWork[0] !== newWork[0]) {
|
|
170
|
-
return true;
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
// Block if adding/removing current_work section
|
|
174
|
-
if ((!oldWork && newWork) || (oldWork && !newWork)) {
|
|
175
|
-
return true;
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
return false;
|
|
179
|
-
},
|
|
180
|
-
message: 'Cannot modify <current_work> section directly',
|
|
181
|
-
suggestion: (old, newStr) => {
|
|
182
|
-
const workId = newStr.match(/Working on: \[#(\d+)\]/)?.[1];
|
|
183
|
-
if (workId) {
|
|
184
|
-
return `Use: jettypod work start ${workId}`;
|
|
185
|
-
}
|
|
186
|
-
return 'Use: jettypod work start <id> or jettypod work stop';
|
|
187
|
-
}
|
|
188
|
-
},
|
|
189
|
-
{
|
|
190
|
-
name: 'work_status_change',
|
|
191
|
-
detect: (old, newStr) => {
|
|
192
|
-
const oldStatus = old.match(/Status: (\w+)/);
|
|
193
|
-
const newStatus = newStr.match(/Status: (\w+)/);
|
|
194
|
-
return oldStatus && newStatus && oldStatus[1] !== newStatus[1];
|
|
195
|
-
},
|
|
196
|
-
message: 'Cannot change work status directly',
|
|
197
|
-
suggestion: (old, newStr) => {
|
|
198
|
-
const workId = newStr.match(/Working on: \[#(\d+)\]/)?.[1];
|
|
199
|
-
const newStatus = newStr.match(/Status: (\w+)/)?.[1];
|
|
200
|
-
if (workId && newStatus) {
|
|
201
|
-
return `Use: jettypod work status ${workId} ${newStatus}`;
|
|
202
|
-
}
|
|
203
|
-
return 'Use: jettypod work status <id> <status>';
|
|
204
|
-
}
|
|
205
|
-
},
|
|
206
|
-
{
|
|
207
|
-
name: 'stage_change',
|
|
208
|
-
detect: (old, newStr) => {
|
|
209
|
-
const oldStage = old.match(/<stage>(\w+)<\/stage>/);
|
|
210
|
-
const newStage = newStr.match(/<stage>(\w+)<\/stage>/);
|
|
211
|
-
return oldStage && newStage && oldStage[1] !== newStage[1];
|
|
212
|
-
},
|
|
213
|
-
message: 'Cannot change <stage> tag directly',
|
|
214
|
-
suggestion: (old, newStr) => {
|
|
215
|
-
const newStage = newStr.match(/<stage>(\w+)<\/stage>/)?.[1];
|
|
216
|
-
return `Use: jettypod stage ${newStage}`;
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
];
|
|
220
|
-
|
|
221
|
-
/**
|
|
222
|
-
* Check if an edit should be blocked
|
|
223
|
-
*/
|
|
224
|
-
function checkEdit(oldStr, newStr) {
|
|
225
|
-
for (const pattern of FORBIDDEN_PATTERNS) {
|
|
226
|
-
if (pattern.detect(oldStr, newStr)) {
|
|
227
|
-
return {
|
|
228
|
-
blocked: true,
|
|
229
|
-
pattern: pattern.name,
|
|
230
|
-
message: pattern.message,
|
|
231
|
-
suggestion: pattern.suggestion(oldStr, newStr)
|
|
232
|
-
};
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
return { blocked: false };
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
/**
|
|
240
|
-
* Test cases (run with: node prototypes/2025-10-16-enforce-jettypod-balanced-v2.js test)
|
|
241
|
-
*/
|
|
242
|
-
if (process.argv[2] === 'test') {
|
|
243
|
-
console.log('Running tests...\n');
|
|
244
|
-
|
|
245
|
-
const tests = [
|
|
246
|
-
{
|
|
247
|
-
name: 'Block mode change (Edit)',
|
|
248
|
-
input: {
|
|
249
|
-
tool_name: 'Edit',
|
|
250
|
-
tool_input: {
|
|
251
|
-
file_path: '/path/to/CLAUDE.md',
|
|
252
|
-
old_string: '<mode>discovery</mode>',
|
|
253
|
-
new_string: '<mode>speed</mode>'
|
|
254
|
-
}
|
|
255
|
-
},
|
|
256
|
-
shouldBlock: true
|
|
257
|
-
},
|
|
258
|
-
{
|
|
259
|
-
name: 'Block current_work addition (Edit)',
|
|
260
|
-
input: {
|
|
261
|
-
tool_name: 'Edit',
|
|
262
|
-
tool_input: {
|
|
263
|
-
file_path: '/path/to/CLAUDE.md',
|
|
264
|
-
old_string: '<project_summary>test</project_summary>',
|
|
265
|
-
new_string: '<current_work>Working on: [#75]</current_work>'
|
|
266
|
-
}
|
|
267
|
-
},
|
|
268
|
-
shouldBlock: true
|
|
269
|
-
},
|
|
270
|
-
{
|
|
271
|
-
name: 'Block status change (Edit)',
|
|
272
|
-
input: {
|
|
273
|
-
tool_name: 'Edit',
|
|
274
|
-
tool_input: {
|
|
275
|
-
file_path: '/path/to/CLAUDE.md',
|
|
276
|
-
old_string: 'Status: in_progress',
|
|
277
|
-
new_string: 'Status: done'
|
|
278
|
-
}
|
|
279
|
-
},
|
|
280
|
-
shouldBlock: true
|
|
281
|
-
},
|
|
282
|
-
{
|
|
283
|
-
name: 'Allow other edits (Edit)',
|
|
284
|
-
input: {
|
|
285
|
-
tool_name: 'Edit',
|
|
286
|
-
tool_input: {
|
|
287
|
-
file_path: '/path/to/CLAUDE.md',
|
|
288
|
-
old_string: '<mission>Old mission</mission>',
|
|
289
|
-
new_string: '<mission>New mission</mission>'
|
|
290
|
-
}
|
|
291
|
-
},
|
|
292
|
-
shouldBlock: false
|
|
293
|
-
},
|
|
294
|
-
{
|
|
295
|
-
name: 'Allow non-CLAUDE.md edits',
|
|
296
|
-
input: {
|
|
297
|
-
tool_name: 'Edit',
|
|
298
|
-
tool_input: {
|
|
299
|
-
file_path: '/path/to/src/index.js',
|
|
300
|
-
old_string: 'const x = 1',
|
|
301
|
-
new_string: 'const x = 2'
|
|
302
|
-
}
|
|
303
|
-
},
|
|
304
|
-
shouldBlock: false
|
|
305
|
-
}
|
|
306
|
-
];
|
|
307
|
-
|
|
308
|
-
let passed = 0;
|
|
309
|
-
let failed = 0;
|
|
310
|
-
|
|
311
|
-
tests.forEach(test => {
|
|
312
|
-
const toolInput = test.input.tool_input;
|
|
313
|
-
const oldStr = toolInput.old_string || '';
|
|
314
|
-
const newStr = toolInput.new_string || '';
|
|
315
|
-
|
|
316
|
-
// Skip file check for testing
|
|
317
|
-
const isClaudeMd = toolInput.file_path.endsWith('CLAUDE.md');
|
|
318
|
-
const result = isClaudeMd ? checkEdit(oldStr, newStr) : { blocked: false };
|
|
319
|
-
const matches = result.blocked === test.shouldBlock;
|
|
320
|
-
|
|
321
|
-
if (matches) {
|
|
322
|
-
console.log(`✅ ${test.name}`);
|
|
323
|
-
if (result.blocked) {
|
|
324
|
-
console.log(` Message: ${result.message}`);
|
|
325
|
-
console.log(` Suggestion: ${result.suggestion}`);
|
|
326
|
-
}
|
|
327
|
-
passed++;
|
|
328
|
-
} else {
|
|
329
|
-
console.log(`❌ ${test.name}`);
|
|
330
|
-
console.log(` Expected blocked=${test.shouldBlock}, got blocked=${result.blocked}`);
|
|
331
|
-
failed++;
|
|
332
|
-
}
|
|
333
|
-
console.log();
|
|
334
|
-
});
|
|
335
|
-
|
|
336
|
-
console.log(`Results: ${passed} passed, ${failed} failed`);
|
|
337
|
-
process.exit(failed > 0 ? 1 : 0);
|
|
338
|
-
}
|
package/.jettypod/test-work.db
DELETED
|
Binary file
|
package/.jettypod/work.db
DELETED
|
Binary file
|
package/CLAUDE.md
DELETED
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
<claude_context project="jettypod-source">
|
|
2
|
-
|
|
3
|
-
<current_work>
|
|
4
|
-
Working on: [#2] Test Epic (epic)
|
|
5
|
-
Status: backlog
|
|
6
|
-
</current_work>
|
|
7
|
-
<project_summary>
|
|
8
|
-
Not yet set
|
|
9
|
-
</project_summary>
|
|
10
|
-
<project_state>
|
|
11
|
-
internal - Internal (team only, staging/preview - no external users)
|
|
12
|
-
</project_state>
|
|
13
|
-
<mission>
|
|
14
|
-
undefined
|
|
15
|
-
</mission>
|
|
16
|
-
<tech_stack>
|
|
17
|
-
Node.js, Jest
|
|
18
|
-
</tech_stack>
|
|
19
|
-
</claude_context>
|
|
20
|
-
<jettypod_essentials>
|
|
21
|
-
JettyPod: Structured workflow system with skills that guide complex workflows.
|
|
22
|
-
|
|
23
|
-
## ⚠️ CRITICAL: Skills are MANDATORY for workflows
|
|
24
|
-
Skills auto-activate and MUST complete their full workflow:
|
|
25
|
-
- epic-planning: Guides architectural decisions
|
|
26
|
-
- feature-planning: Guides UX discovery + BDD scenarios
|
|
27
|
-
- speed-mode: Implements happy path, THEN auto-generates stable chores
|
|
28
|
-
- stable-mode: Adds error handling to speed implementation
|
|
29
|
-
- external-transition: Guides launch preparation
|
|
30
|
-
|
|
31
|
-
❌ DO NOT bypass skills by manually running commands halfway through
|
|
32
|
-
❌ DO NOT mark work complete until skill finishes its ENTIRE workflow
|
|
33
|
-
❌ DO NOT manually create chores when a skill should generate them
|
|
34
|
-
✅ ALWAYS let skills complete autonomously before taking manual actions
|
|
35
|
-
|
|
36
|
-
## Basic Commands (for non-workflow operations)
|
|
37
|
-
jettypod work create epic "<title>"
|
|
38
|
-
jettypod work create feature "<title>" --parent=<id>
|
|
39
|
-
jettypod work start <id>
|
|
40
|
-
jettypod work status <id> cancelled
|
|
41
|
-
jettypod backlog
|
|
42
|
-
|
|
43
|
-
## Advanced Commands
|
|
44
|
-
For mode management, decisions, project state: docs/COMMAND_REFERENCE.md
|
|
45
|
-
</jettypod_essentials>
|
|
46
|
-
|
|
47
|
-
<communication_style>
|
|
48
|
-
Be direct and opinionated. Skip hedging, apologies, and excessive politeness. Lead with the main point, challenge flawed ideas, give honest assessments even if unwelcome. Respond to what's literally asked, not what's "helpful". Prioritize brevity and practical utility over comprehensiveness.
|
|
49
|
-
</communication_style>
|