jettypod 3.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (122) hide show
  1. package/.claude/PROTECT_SKILLS.md +28 -0
  2. package/.claude/settings.json +24 -0
  3. package/.claude/settings.local.json +16 -0
  4. package/.claude/skills/epic-discover/SKILL.md +262 -0
  5. package/.claude/skills/feature-discover/SKILL.md +393 -0
  6. package/.claude/skills/speed-mode/SKILL.md +364 -0
  7. package/.claude/skills/stable-mode/SKILL.md +591 -0
  8. package/.github/workflows/test-safety.yml +85 -0
  9. package/README.md +25 -0
  10. package/SPEED-STABLE-AUDIT.md +853 -0
  11. package/SYSTEM-BEHAVIOR.md +1241 -0
  12. package/TEST_SAFETY_AUDIT.md +314 -0
  13. package/TEST_SAFETY_IMPLEMENTATION.md +97 -0
  14. package/cucumber.js +8 -0
  15. package/docs/COMMAND_REFERENCE.md +903 -0
  16. package/docs/DECISIONS.md +68 -0
  17. package/docs/README.md +48 -0
  18. package/docs/STANDARDS-SYSTEM-DOCUMENTATION.md +374 -0
  19. package/docs/TEST-REWRITE-PLAN.md +261 -0
  20. package/docs/ai-test-writing-requirements.md +219 -0
  21. package/docs/claude-code-skills.md +607 -0
  22. package/docs/core-jettypod-methodology/comprehensive-jettypod-methodology.md +582 -0
  23. package/docs/core-jettypod-methodology/deprecated/jettypod-comprehensive-standards.md +1222 -0
  24. package/docs/core-jettypod-methodology/deprecated/jettypod-operating-guide.md +3399 -0
  25. package/docs/core-jettypod-methodology/deprecated/jettypod-technical-checklist.md +1325 -0
  26. package/docs/core-jettypod-methodology/deprecated/jettypod-vibe-coding-framework.md +1544 -0
  27. package/docs/core-jettypod-methodology/deprecated/prompt-engineering-guide.md +320 -0
  28. package/docs/core-jettypod-methodology/deprecated/vibe-coding-cheatsheet (1).md +516 -0
  29. package/docs/core-jettypod-methodology/deprecated/vibe-coding-framework.md +1544 -0
  30. package/docs/features/jettypod-standards-explained.md +543 -0
  31. package/docs/features/standards-inventory.md +257 -0
  32. package/docs/gap-analysis-current-vs-comprehensive-methodology.md +939 -0
  33. package/docs/jettypod-system-overview.md +409 -0
  34. package/features/auto-generate-production-chores.feature +14 -0
  35. package/features/claude-md-protection/steps.js +487 -0
  36. package/features/decisions/index.js +490 -0
  37. package/features/decisions/index.test.js +208 -0
  38. package/features/git-hooks/git-hooks.feature +30 -0
  39. package/features/git-hooks/index.js +93 -0
  40. package/features/git-hooks/index.test.js +137 -0
  41. package/features/git-hooks/post-commit +56 -0
  42. package/features/git-hooks/post-merge +47 -0
  43. package/features/git-hooks/pre-commit +28 -0
  44. package/features/git-hooks/simple-steps.js +53 -0
  45. package/features/git-hooks/simple-test.feature +10 -0
  46. package/features/git-hooks/steps.js +196 -0
  47. package/features/jettypod-update-command.feature +46 -0
  48. package/features/mode-prompts/index.js +95 -0
  49. package/features/mode-prompts/simple-steps.js +44 -0
  50. package/features/mode-prompts/simple-test.feature +9 -0
  51. package/features/mode-prompts/validation.test.js +120 -0
  52. package/features/refactor-mode/steps.js +217 -0
  53. package/features/refactor-mode.feature +49 -0
  54. package/features/skills-update/index.test.js +216 -0
  55. package/features/step_definitions/auto-generate-production-chores.steps.js +162 -0
  56. package/features/step_definitions/terminal-logo.steps.js +145 -0
  57. package/features/step_definitions/update-command.steps.js +183 -0
  58. package/features/terminal-logo/index.js +39 -0
  59. package/features/terminal-logo/terminal-logo.feature +30 -0
  60. package/features/update-command/index.js +181 -0
  61. package/features/update-command/index.test.js +225 -0
  62. package/features/work-commands/bug-workflow-display.feature +22 -0
  63. package/features/work-commands/index.js +311 -0
  64. package/features/work-commands/simple-steps.js +69 -0
  65. package/features/work-commands/stable-tests.feature +57 -0
  66. package/features/work-commands/steps.js +1120 -0
  67. package/features/work-commands/validation.test.js +88 -0
  68. package/features/work-commands/work-commands.feature +13 -0
  69. package/features/work-tracking/discovery-validation.test.js +228 -0
  70. package/features/work-tracking/index.js +1511 -0
  71. package/features/work-tracking/mode-required.feature +112 -0
  72. package/features/work-tracking/phase-tracking.test.js +482 -0
  73. package/features/work-tracking/prototype-tracking.test.js +485 -0
  74. package/features/work-tracking/tree-view.test.js +310 -0
  75. package/features/work-tracking/work-set-mode.feature +71 -0
  76. package/features/work-tracking/work-start-mode.feature +88 -0
  77. package/full-test.txt +0 -0
  78. package/install.sh +89 -0
  79. package/jettypod.js +1640 -0
  80. package/lib/bug-workflow.js +94 -0
  81. package/lib/bug-workflow.test.js +177 -0
  82. package/lib/claudemd.js +130 -0
  83. package/lib/claudemd.test.js +195 -0
  84. package/lib/comprehensive-standards-full.json +1778 -0
  85. package/lib/config.js +181 -0
  86. package/lib/config.test.js +511 -0
  87. package/lib/constants.js +107 -0
  88. package/lib/constants.test.js +164 -0
  89. package/lib/current-work.js +130 -0
  90. package/lib/current-work.test.js +146 -0
  91. package/lib/database-project-config.test.js +107 -0
  92. package/lib/database.js +256 -0
  93. package/lib/database.test.js +106 -0
  94. package/lib/decisions-generator.js +102 -0
  95. package/lib/decisions-generator.test.js +457 -0
  96. package/lib/decisions-helpers.js +119 -0
  97. package/lib/decisions-helpers.test.js +310 -0
  98. package/lib/discovery-checkpoint.js +83 -0
  99. package/lib/docs-generator.js +280 -0
  100. package/lib/external-checklist.js +177 -0
  101. package/lib/git.js +142 -0
  102. package/lib/git.test.js +145 -0
  103. package/lib/logo.js +3 -0
  104. package/lib/migrations/001-epic-to-parent.js +24 -0
  105. package/lib/migrations/002-default-work-item-modes.js +37 -0
  106. package/lib/migrations/002-default-work-item-modes.test.js +351 -0
  107. package/lib/migrations/003-epic-discovery-fields.js +52 -0
  108. package/lib/migrations/004-discovery-decisions-table.js +32 -0
  109. package/lib/migrations/005-migrate-decision-data.js +62 -0
  110. package/lib/migrations/006-feature-phase-field.js +61 -0
  111. package/lib/migrations/007-prototype-tracking.js +38 -0
  112. package/lib/migrations/008-scenario-file-field.js +24 -0
  113. package/lib/migrations/index.js +74 -0
  114. package/lib/production-helpers.js +69 -0
  115. package/lib/project-state.test.js +92 -0
  116. package/lib/test-helpers.js +184 -0
  117. package/lib/test-helpers.test.js +255 -0
  118. package/package.json +36 -0
  119. package/prototypes/test/index.html +1 -0
  120. package/setup-dist-repo.sh +68 -0
  121. package/test-safety-check.sh +80 -0
  122. package/work-item-tracking-plan.md +199 -0
@@ -0,0 +1,94 @@
1
+ /**
2
+ * Bug workflow guidance constants
3
+ * Shared between CLAUDE.md generation and terminal display
4
+ */
5
+
6
+ const BUG_WORKFLOW_STEPS = `1. REPRODUCE
7
+ □ Create minimal reproduction case
8
+ □ Write down exact steps to trigger
9
+ □ Note what you expect vs what happens
10
+ □ Check if issue exists in latest code
11
+
12
+ 2. INVESTIGATE
13
+ □ Read error messages/stack traces completely
14
+ □ Check relevant code around the issue
15
+ □ Look for recent changes (git log/blame)
16
+ □ Search for similar bugs (closed issues)
17
+
18
+ 3. ISOLATE
19
+ □ Narrow down to specific file/function
20
+ □ Identify root cause, not just symptoms
21
+ □ Verify your hypothesis with logging/debugging
22
+ □ Understand why the bug occurs
23
+
24
+ 4. FIX
25
+ □ Make the minimal change that fixes root cause
26
+ □ Avoid "while I'm here" refactoring
27
+ □ Add defensive checks if appropriate
28
+ □ Update error messages to be clearer
29
+
30
+ 5. VERIFY
31
+ □ Confirm original reproduction case now works
32
+ □ Run existing tests - ensure nothing broke
33
+ □ Add regression test for this specific bug
34
+ □ Test edge cases around the fix
35
+
36
+ 6. COMMIT
37
+ □ Clear commit message: "fix: [brief description]"
38
+ □ Reference issue number if applicable
39
+ □ Explain what caused it and how you fixed it
40
+ □ Push and verify CI passes`;
41
+
42
+ const BUG_WORKFLOW_TECHNIQUES = `DEBUGGING TECHNIQUES:
43
+ • Add strategic console.log/print statements
44
+ • Use git bisect to find when bug was introduced
45
+ • Compare working vs broken behavior side-by-side
46
+ • Rubber duck: explain the bug out loud
47
+ • Take breaks - fresh eyes spot issues faster`;
48
+
49
+ const BUG_WORKFLOW_RED_FLAGS = `RED FLAGS:
50
+ ❌ "It works on my machine" - not fixed
51
+ ❌ Fixing symptoms without understanding cause
52
+ ❌ Adding try/catch to hide errors
53
+ ❌ Skipping reproduction - you'll miss edge cases`;
54
+
55
+ /**
56
+ * Get bug workflow for CLAUDE.md (plain text format)
57
+ * @returns {string} Formatted workflow for markdown
58
+ */
59
+ function getBugWorkflowForClaudeMd() {
60
+ return `SYSTEMATIC BUG FIXING WORKFLOW:
61
+
62
+ ${BUG_WORKFLOW_STEPS}
63
+
64
+ ${BUG_WORKFLOW_TECHNIQUES}
65
+
66
+ ${BUG_WORKFLOW_RED_FLAGS}`;
67
+ }
68
+
69
+ /**
70
+ * Get bug workflow for terminal display (with formatting/colors)
71
+ * @returns {string} Formatted workflow for terminal
72
+ */
73
+ function getBugWorkflowForTerminal() {
74
+ return `
75
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
76
+ 🐛 BUG FIXING WORKFLOW
77
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
78
+
79
+ ${BUG_WORKFLOW_STEPS}
80
+
81
+ ${BUG_WORKFLOW_TECHNIQUES}
82
+
83
+ ${BUG_WORKFLOW_RED_FLAGS}
84
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
85
+ `;
86
+ }
87
+
88
+ module.exports = {
89
+ BUG_WORKFLOW_STEPS,
90
+ BUG_WORKFLOW_TECHNIQUES,
91
+ BUG_WORKFLOW_RED_FLAGS,
92
+ getBugWorkflowForClaudeMd,
93
+ getBugWorkflowForTerminal
94
+ };
@@ -0,0 +1,177 @@
1
+ const {
2
+ BUG_WORKFLOW_STEPS,
3
+ BUG_WORKFLOW_TECHNIQUES,
4
+ BUG_WORKFLOW_RED_FLAGS,
5
+ getBugWorkflowForClaudeMd,
6
+ getBugWorkflowForTerminal
7
+ } = require('./bug-workflow');
8
+
9
+ describe('Bug Workflow Module', () => {
10
+ describe('Constants', () => {
11
+ test('BUG_WORKFLOW_STEPS contains all 6 steps', () => {
12
+ expect(BUG_WORKFLOW_STEPS).toContain('1. REPRODUCE');
13
+ expect(BUG_WORKFLOW_STEPS).toContain('2. INVESTIGATE');
14
+ expect(BUG_WORKFLOW_STEPS).toContain('3. ISOLATE');
15
+ expect(BUG_WORKFLOW_STEPS).toContain('4. FIX');
16
+ expect(BUG_WORKFLOW_STEPS).toContain('5. VERIFY');
17
+ expect(BUG_WORKFLOW_STEPS).toContain('6. COMMIT');
18
+ });
19
+
20
+ test('BUG_WORKFLOW_STEPS contains critical checklist items', () => {
21
+ // REPRODUCE checklist
22
+ expect(BUG_WORKFLOW_STEPS).toContain('Create minimal reproduction case');
23
+ expect(BUG_WORKFLOW_STEPS).toContain('Write down exact steps to trigger');
24
+
25
+ // INVESTIGATE checklist
26
+ expect(BUG_WORKFLOW_STEPS).toContain('Read error messages/stack traces completely');
27
+ expect(BUG_WORKFLOW_STEPS).toContain('Look for recent changes (git log/blame)');
28
+
29
+ // ISOLATE checklist
30
+ expect(BUG_WORKFLOW_STEPS).toContain('Identify root cause, not just symptoms');
31
+ expect(BUG_WORKFLOW_STEPS).toContain('Understand why the bug occurs');
32
+
33
+ // FIX checklist
34
+ expect(BUG_WORKFLOW_STEPS).toContain('Make the minimal change that fixes root cause');
35
+ expect(BUG_WORKFLOW_STEPS).toContain('Avoid "while I\'m here" refactoring');
36
+
37
+ // VERIFY checklist
38
+ expect(BUG_WORKFLOW_STEPS).toContain('Add regression test for this specific bug');
39
+ expect(BUG_WORKFLOW_STEPS).toContain('Run existing tests - ensure nothing broke');
40
+
41
+ // COMMIT checklist
42
+ expect(BUG_WORKFLOW_STEPS).toContain('Clear commit message: "fix: [brief description]"');
43
+ expect(BUG_WORKFLOW_STEPS).toContain('Reference issue number if applicable');
44
+ });
45
+
46
+ test('BUG_WORKFLOW_TECHNIQUES contains debugging tips', () => {
47
+ expect(BUG_WORKFLOW_TECHNIQUES).toContain('DEBUGGING TECHNIQUES:');
48
+ expect(BUG_WORKFLOW_TECHNIQUES).toContain('git bisect');
49
+ expect(BUG_WORKFLOW_TECHNIQUES).toContain('Rubber duck');
50
+ expect(BUG_WORKFLOW_TECHNIQUES).toContain('Take breaks');
51
+ });
52
+
53
+ test('BUG_WORKFLOW_RED_FLAGS contains anti-patterns', () => {
54
+ expect(BUG_WORKFLOW_RED_FLAGS).toContain('RED FLAGS:');
55
+ expect(BUG_WORKFLOW_RED_FLAGS).toContain('"It works on my machine"');
56
+ expect(BUG_WORKFLOW_RED_FLAGS).toContain('Fixing symptoms without understanding cause');
57
+ expect(BUG_WORKFLOW_RED_FLAGS).toContain('Adding try/catch to hide errors');
58
+ expect(BUG_WORKFLOW_RED_FLAGS).toContain('Skipping reproduction');
59
+ });
60
+ });
61
+
62
+ describe('getBugWorkflowForClaudeMd()', () => {
63
+ test('returns string with all workflow sections', () => {
64
+ const result = getBugWorkflowForClaudeMd();
65
+
66
+ expect(typeof result).toBe('string');
67
+ expect(result).toContain('SYSTEMATIC BUG FIXING WORKFLOW:');
68
+ expect(result).toContain(BUG_WORKFLOW_STEPS);
69
+ expect(result).toContain(BUG_WORKFLOW_TECHNIQUES);
70
+ expect(result).toContain(BUG_WORKFLOW_RED_FLAGS);
71
+ });
72
+
73
+ test('does not contain terminal formatting characters', () => {
74
+ const result = getBugWorkflowForClaudeMd();
75
+
76
+ // Should not have Unicode box-drawing characters
77
+ expect(result).not.toContain('━');
78
+ // Should not have emojis
79
+ expect(result).not.toContain('🐛');
80
+ });
81
+
82
+ test('has plain text header for markdown compatibility', () => {
83
+ const result = getBugWorkflowForClaudeMd();
84
+
85
+ expect(result).toContain('SYSTEMATIC BUG FIXING WORKFLOW:');
86
+ });
87
+ });
88
+
89
+ describe('getBugWorkflowForTerminal()', () => {
90
+ test('returns string with all workflow sections', () => {
91
+ const result = getBugWorkflowForTerminal();
92
+
93
+ expect(typeof result).toBe('string');
94
+ expect(result).toContain(BUG_WORKFLOW_STEPS);
95
+ expect(result).toContain(BUG_WORKFLOW_TECHNIQUES);
96
+ expect(result).toContain(BUG_WORKFLOW_RED_FLAGS);
97
+ });
98
+
99
+ test('contains terminal formatting characters', () => {
100
+ const result = getBugWorkflowForTerminal();
101
+
102
+ // Should have Unicode box-drawing characters for borders
103
+ expect(result).toContain('━');
104
+ // Should have bug emoji
105
+ expect(result).toContain('🐛');
106
+ });
107
+
108
+ test('has formatted header with emoji', () => {
109
+ const result = getBugWorkflowForTerminal();
110
+
111
+ expect(result).toContain('🐛 BUG FIXING WORKFLOW');
112
+ });
113
+
114
+ test('has top and bottom borders', () => {
115
+ const result = getBugWorkflowForTerminal();
116
+
117
+ // Count border lines
118
+ const borderCount = (result.match(/━━━━━━━/g) || []).length;
119
+ expect(borderCount).toBeGreaterThanOrEqual(2); // Top and bottom borders
120
+ });
121
+ });
122
+
123
+ describe('Content Consistency', () => {
124
+ test('both formats contain the same workflow steps', () => {
125
+ const claudeMd = getBugWorkflowForClaudeMd();
126
+ const terminal = getBugWorkflowForTerminal();
127
+
128
+ // Both should contain the exact same steps content
129
+ expect(claudeMd).toContain(BUG_WORKFLOW_STEPS);
130
+ expect(terminal).toContain(BUG_WORKFLOW_STEPS);
131
+ });
132
+
133
+ test('both formats contain the same techniques', () => {
134
+ const claudeMd = getBugWorkflowForClaudeMd();
135
+ const terminal = getBugWorkflowForTerminal();
136
+
137
+ expect(claudeMd).toContain(BUG_WORKFLOW_TECHNIQUES);
138
+ expect(terminal).toContain(BUG_WORKFLOW_TECHNIQUES);
139
+ });
140
+
141
+ test('both formats contain the same red flags', () => {
142
+ const claudeMd = getBugWorkflowForClaudeMd();
143
+ const terminal = getBugWorkflowForTerminal();
144
+
145
+ expect(claudeMd).toContain(BUG_WORKFLOW_RED_FLAGS);
146
+ expect(terminal).toContain(BUG_WORKFLOW_RED_FLAGS);
147
+ });
148
+ });
149
+
150
+ describe('Edge Cases', () => {
151
+ test('functions return non-empty strings', () => {
152
+ expect(getBugWorkflowForClaudeMd().length).toBeGreaterThan(0);
153
+ expect(getBugWorkflowForTerminal().length).toBeGreaterThan(0);
154
+ });
155
+
156
+ test('functions are deterministic (same output on multiple calls)', () => {
157
+ const claudeMd1 = getBugWorkflowForClaudeMd();
158
+ const claudeMd2 = getBugWorkflowForClaudeMd();
159
+ expect(claudeMd1).toBe(claudeMd2);
160
+
161
+ const terminal1 = getBugWorkflowForTerminal();
162
+ const terminal2 = getBugWorkflowForTerminal();
163
+ expect(terminal1).toBe(terminal2);
164
+ });
165
+
166
+ test('constants are immutable strings', () => {
167
+ expect(typeof BUG_WORKFLOW_STEPS).toBe('string');
168
+ expect(typeof BUG_WORKFLOW_TECHNIQUES).toBe('string');
169
+ expect(typeof BUG_WORKFLOW_RED_FLAGS).toBe('string');
170
+
171
+ // Verify they're not empty
172
+ expect(BUG_WORKFLOW_STEPS.length).toBeGreaterThan(0);
173
+ expect(BUG_WORKFLOW_TECHNIQUES.length).toBeGreaterThan(0);
174
+ expect(BUG_WORKFLOW_RED_FLAGS.length).toBeGreaterThan(0);
175
+ });
176
+ });
177
+ });
@@ -0,0 +1,130 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+ const config = require('./config');
4
+ const { getBugWorkflowForClaudeMd } = require('./bug-workflow');
5
+
6
+ /**
7
+ * Get absolute path to CLAUDE.md file
8
+ * @returns {string} Absolute path to CLAUDE.md
9
+ */
10
+ function getClaudeMdPath() {
11
+ return path.join(process.cwd(), 'CLAUDE.md');
12
+ }
13
+
14
+ /**
15
+ * Validate current work object structure
16
+ * @param {Object} currentWork - Current work item
17
+ * @throws {Error} If currentWork is invalid
18
+ */
19
+ function validateCurrentWork(currentWork) {
20
+ if (!currentWork || typeof currentWork !== 'object') {
21
+ throw new Error('Current work must be an object');
22
+ }
23
+ if (!currentWork.id || typeof currentWork.id !== 'number') {
24
+ throw new Error('Current work must have a numeric id');
25
+ }
26
+ if (!currentWork.title || typeof currentWork.title !== 'string') {
27
+ throw new Error('Current work must have a string title');
28
+ }
29
+ if (!currentWork.type || typeof currentWork.type !== 'string') {
30
+ throw new Error('Current work must have a string type');
31
+ }
32
+ if (!currentWork.status || typeof currentWork.status !== 'string') {
33
+ throw new Error('Current work must have a string status');
34
+ }
35
+ }
36
+
37
+ // getModeBehaviorContent function removed - skills now provide all mode guidance
38
+ // Mode-specific workflows are now handled by:
39
+ // - speed-mode skill (activated on speed chore start)
40
+ // - stable-mode skill (activated on stable chore start)
41
+ // See .claude/skills/ directory for skill implementations
42
+
43
+ /**
44
+ * Update current work section in CLAUDE.md
45
+ * @param {Object} currentWork - Current work item to display
46
+ * @param {string|null} mode - Work item mode (null for epics, undefined to read from config)
47
+ * @throws {Error} If currentWork is invalid or file cannot be written
48
+ */
49
+ function updateCurrentWork(currentWork, mode) {
50
+ validateCurrentWork(currentWork);
51
+
52
+ const claudePath = getClaudeMdPath();
53
+
54
+ if (!fs.existsSync(claudePath)) {
55
+ return;
56
+ }
57
+
58
+ let content;
59
+ try {
60
+ content = fs.readFileSync(claudePath, 'utf-8');
61
+ } catch (err) {
62
+ throw new Error(`Failed to read CLAUDE.md: ${err.message}`);
63
+ }
64
+
65
+ // Use provided mode
66
+ // null = epic (no mode), undefined = fallback to config for backwards compatibility
67
+ let currentMode = mode;
68
+ if (currentMode === undefined) {
69
+ try {
70
+ const cfg = config.read();
71
+ currentMode = cfg.mode || 'speed';
72
+ } catch (err) {
73
+ currentMode = 'speed';
74
+ }
75
+ }
76
+
77
+ // Build new current_work section
78
+ let newCurrentWork = `<current_work>
79
+ Working on: [#${currentWork.id}] ${currentWork.title} (${currentWork.type})`;
80
+
81
+ if (currentWork.epic_title && currentWork.epic_id !== currentWork.id) {
82
+ newCurrentWork += `
83
+ Epic: [#${currentWork.epic_id}] ${currentWork.epic_title}`;
84
+ }
85
+
86
+ // Only add Mode line if mode is not null (epics don't have modes)
87
+ if (currentMode !== null) {
88
+ newCurrentWork += `
89
+ Mode: ${currentMode}`;
90
+ }
91
+
92
+ newCurrentWork += `
93
+ Status: ${currentWork.status}
94
+ </current_work>`;
95
+
96
+ // Replace existing current_work section or add after project context
97
+ if (content.includes('<current_work>')) {
98
+ content = content.replace(/<current_work>[\s\S]*?<\/current_work>/, newCurrentWork);
99
+ } else {
100
+ // Insert after <claude_context> opening
101
+ if (!content.includes('<claude_context')) {
102
+ throw new Error('CLAUDE.md does not contain <claude_context> tag');
103
+ }
104
+ content = content.replace(/(<claude_context[^>]*>)/, `$1\n\n${newCurrentWork}`);
105
+ }
106
+
107
+ // Remove both <bug_workflow> and <mode_behavior> sections
108
+ // Skills now provide all mode-specific guidance via activation messages
109
+ content = content.replace(/<bug_workflow>[\s\S]*?<\/bug_workflow>/, '');
110
+ content = content.replace(/<mode_behavior>[\s\S]*?<\/mode_behavior>/, '');
111
+
112
+ // Update <mode> tag
113
+ if (currentMode !== null) {
114
+ content = content.replace(/<mode>.*?<\/mode>/, `<mode>${currentMode}</mode>`);
115
+ } else {
116
+ // Remove <mode> tag for epics
117
+ content = content.replace(/<mode>.*?<\/mode>\n?/, '');
118
+ }
119
+
120
+ try {
121
+ fs.writeFileSync(claudePath, content);
122
+ } catch (err) {
123
+ throw new Error(`Failed to write CLAUDE.md: ${err.message}`);
124
+ }
125
+ }
126
+
127
+ module.exports = {
128
+ updateCurrentWork,
129
+ getClaudeMdPath
130
+ };
@@ -0,0 +1,195 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+ const { createTestEnvironment } = require('./test-helpers');
4
+ const { updateCurrentWork, getClaudeMdPath } = require('./claudemd');
5
+ const config = require('./config');
6
+
7
+ describe('CLAUDE.md Updater Module', () => {
8
+ let testEnv;
9
+
10
+ beforeEach(() => {
11
+ testEnv = createTestEnvironment();
12
+ process.chdir(testEnv.testDir);
13
+
14
+ // Initialize config
15
+ const jettypodDir = path.join(testEnv.testDir, '.jettypod');
16
+ fs.mkdirSync(jettypodDir);
17
+ config.write({ mode: 'speed', stage: 'starting' });
18
+ });
19
+
20
+ afterEach(() => {
21
+ testEnv.cleanup();
22
+ });
23
+
24
+ const validWorkItem = {
25
+ id: 1,
26
+ title: 'Test Feature',
27
+ type: 'feature',
28
+ status: 'in_progress',
29
+ epic_id: 10,
30
+ epic_title: 'Test Epic'
31
+ };
32
+
33
+ describe('getClaudeMdPath()', () => {
34
+ test('should return path to CLAUDE.md in current directory', () => {
35
+ const claudePath = getClaudeMdPath();
36
+ expect(claudePath).toContain('CLAUDE.md');
37
+ expect(claudePath).toBe(path.join(process.cwd(), 'CLAUDE.md'));
38
+ });
39
+ });
40
+
41
+ describe('updateCurrentWork()', () => {
42
+ const basicClaudeContent = `<claude_context project="test">
43
+ <project_summary>Test project</project_summary>
44
+ </claude_context>`;
45
+
46
+ test('should do nothing if CLAUDE.md does not exist', () => {
47
+ expect(() => updateCurrentWork(validWorkItem)).not.toThrow();
48
+ });
49
+
50
+ test('should add current_work section if it does not exist', () => {
51
+ fs.writeFileSync('CLAUDE.md', basicClaudeContent);
52
+
53
+ updateCurrentWork(validWorkItem);
54
+
55
+ const content = fs.readFileSync('CLAUDE.md', 'utf-8');
56
+ expect(content).toContain('<current_work>');
57
+ expect(content).toContain('Working on: [#1] Test Feature (feature)');
58
+ expect(content).toContain('Epic: [#10] Test Epic');
59
+ expect(content).toContain('Mode: speed');
60
+ expect(content).toContain('Status: in_progress');
61
+ expect(content).toContain('</current_work>');
62
+ });
63
+
64
+ test('should replace existing current_work section', () => {
65
+ const existingContent = `<claude_context project="test">
66
+ <current_work>
67
+ Working on: [#999] Old Feature (bug)
68
+ Status: done
69
+ </current_work>
70
+ <project_summary>Test project</project_summary>
71
+ </claude_context>`;
72
+
73
+ fs.writeFileSync('CLAUDE.md', existingContent);
74
+
75
+ updateCurrentWork(validWorkItem);
76
+
77
+ const content = fs.readFileSync('CLAUDE.md', 'utf-8');
78
+ expect(content).not.toContain('[#999] Old Feature');
79
+ expect(content).toContain('[#1] Test Feature');
80
+ expect(content).toContain('Status: in_progress');
81
+ });
82
+
83
+ test('should not include epic if work item is the epic itself', () => {
84
+ const epicWorkItem = {
85
+ id: 10,
86
+ title: 'Test Epic',
87
+ type: 'epic',
88
+ status: 'in_progress',
89
+ epic_id: 10,
90
+ epic_title: 'Test Epic'
91
+ };
92
+
93
+ fs.writeFileSync('CLAUDE.md', basicClaudeContent);
94
+
95
+ updateCurrentWork(epicWorkItem);
96
+
97
+ const content = fs.readFileSync('CLAUDE.md', 'utf-8');
98
+ expect(content).not.toContain('Epic: [#10]');
99
+ expect(content).toContain('[#10] Test Epic (epic)');
100
+ });
101
+
102
+ test('should not include epic if epic_title is missing', () => {
103
+ const workItemNoEpic = {
104
+ id: 1,
105
+ title: 'Test Feature',
106
+ type: 'feature',
107
+ status: 'in_progress'
108
+ };
109
+
110
+ fs.writeFileSync('CLAUDE.md', basicClaudeContent);
111
+
112
+ updateCurrentWork(workItemNoEpic);
113
+
114
+ const content = fs.readFileSync('CLAUDE.md', 'utf-8');
115
+ expect(content).not.toContain('Epic:');
116
+ });
117
+
118
+ test('should use mode from config', () => {
119
+ config.write({ mode: 'stable', stage: 'building' });
120
+ fs.writeFileSync('CLAUDE.md', basicClaudeContent);
121
+
122
+ updateCurrentWork(validWorkItem);
123
+
124
+ const content = fs.readFileSync('CLAUDE.md', 'utf-8');
125
+ expect(content).toContain('Mode: stable');
126
+ });
127
+
128
+ test('should use mode from config even if malformed', () => {
129
+ // Config module caches config, so we just verify it uses whatever mode is set
130
+ const currentConfig = config.read();
131
+ fs.writeFileSync('CLAUDE.md', basicClaudeContent);
132
+
133
+ updateCurrentWork(validWorkItem);
134
+
135
+ const content = fs.readFileSync('CLAUDE.md', 'utf-8');
136
+ expect(content).toContain(`Mode: ${currentConfig.mode || 'speed'}`);
137
+ });
138
+
139
+ test('should throw error for invalid work item - not an object', () => {
140
+ expect(() => updateCurrentWork(null)).toThrow('Current work must be an object');
141
+ expect(() => updateCurrentWork('string')).toThrow('Current work must be an object');
142
+ });
143
+
144
+ test('should throw error for missing id', () => {
145
+ const invalid = { ...validWorkItem, id: null };
146
+ expect(() => updateCurrentWork(invalid)).toThrow('Current work must have a numeric id');
147
+ });
148
+
149
+ test('should throw error for non-numeric id', () => {
150
+ const invalid = { ...validWorkItem, id: 'not-a-number' };
151
+ expect(() => updateCurrentWork(invalid)).toThrow('Current work must have a numeric id');
152
+ });
153
+
154
+ test('should throw error for missing title', () => {
155
+ const invalid = { ...validWorkItem, title: null };
156
+ expect(() => updateCurrentWork(invalid)).toThrow('Current work must have a string title');
157
+ });
158
+
159
+ test('should throw error for missing type', () => {
160
+ const invalid = { ...validWorkItem, type: null };
161
+ expect(() => updateCurrentWork(invalid)).toThrow('Current work must have a string type');
162
+ });
163
+
164
+ test('should throw error for missing status', () => {
165
+ const invalid = { ...validWorkItem, status: null };
166
+ expect(() => updateCurrentWork(invalid)).toThrow('Current work must have a string status');
167
+ });
168
+
169
+ test('should throw error if CLAUDE.md does not contain claude_context tag', () => {
170
+ fs.writeFileSync('CLAUDE.md', 'Just plain text, no tags');
171
+
172
+ expect(() => updateCurrentWork(validWorkItem)).toThrow('CLAUDE.md does not contain <claude_context> tag');
173
+ });
174
+
175
+ test('should preserve other sections in CLAUDE.md', () => {
176
+ const fullContent = `<claude_context project="test">
177
+ <current_work>
178
+ Working on: [#999] Old Feature (bug)
179
+ </current_work>
180
+ <project_summary>Test project</project_summary>
181
+ <tech_stack>Node.js, Jest</tech_stack>
182
+ <mode>speed</mode>
183
+ </claude_context>`;
184
+
185
+ fs.writeFileSync('CLAUDE.md', fullContent);
186
+
187
+ updateCurrentWork(validWorkItem);
188
+
189
+ const content = fs.readFileSync('CLAUDE.md', 'utf-8');
190
+ expect(content).toContain('<project_summary>Test project</project_summary>');
191
+ expect(content).toContain('<tech_stack>Node.js, Jest</tech_stack>');
192
+ expect(content).toContain('<mode>speed</mode>');
193
+ });
194
+ });
195
+ });