jettypod 4.1.6 → 4.2.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/lib/claudemd.js CHANGED
@@ -2,6 +2,7 @@ const fs = require('fs');
2
2
  const path = require('path');
3
3
  const config = require('./config');
4
4
  const { getBugWorkflowForClaudeMd } = require('./bug-workflow');
5
+ const { writeSessionFile } = require('./session-writer');
5
6
 
6
7
  /**
7
8
  * Get absolute path to CLAUDE.md file
@@ -41,7 +42,8 @@ function validateCurrentWork(currentWork) {
41
42
  // See .claude/skills/ directory for skill implementations
42
43
 
43
44
  /**
44
- * Update current work section in CLAUDE.md
45
+ * Update current work - writes to session file instead of CLAUDE.md
46
+ * Session file is gitignored to prevent merge conflicts and stale context
45
47
  * @param {Object} currentWork - Current work item to display
46
48
  * @param {string|null} mode - Work item mode (null for epics, undefined to read from config)
47
49
  * @throws {Error} If currentWork is invalid or file cannot be written
@@ -49,19 +51,6 @@ function validateCurrentWork(currentWork) {
49
51
  function updateCurrentWork(currentWork, mode) {
50
52
  validateCurrentWork(currentWork);
51
53
 
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
54
  // Use provided mode
66
55
  // null = epic (no mode), undefined = fallback to config for backwards compatibility
67
56
  let currentMode = mode;
@@ -74,53 +63,13 @@ function updateCurrentWork(currentWork, mode) {
74
63
  }
75
64
  }
76
65
 
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
66
+ // Write to session file (gitignored) instead of CLAUDE.md
67
+ // This prevents merge conflicts and stale context on main branch
113
68
  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}`);
69
+ writeSessionFile(currentWork, currentMode, {
70
+ epicId: currentWork.epic_id,
71
+ epicTitle: currentWork.epic_title
72
+ });
124
73
  }
125
74
  }
126
75
 
@@ -0,0 +1,150 @@
1
+ /**
2
+ * Session File Writer
3
+ *
4
+ * Writes session-specific context to .claude/session.md (gitignored)
5
+ * instead of CLAUDE.md to prevent merge conflicts and stale context.
6
+ *
7
+ * Includes skill invocation directives for auto-invoking mode skills
8
+ * when resuming work on chores.
9
+ */
10
+
11
+ const fs = require('fs');
12
+ const path = require('path');
13
+
14
+ /**
15
+ * Get absolute path to session file
16
+ * @returns {string} Absolute path to .claude/session.md
17
+ */
18
+ function getSessionFilePath() {
19
+ return path.join(process.cwd(), '.claude', 'session.md');
20
+ }
21
+
22
+ /**
23
+ * Ensure .claude directory exists
24
+ */
25
+ function ensureClaudeDir() {
26
+ const claudeDir = path.join(process.cwd(), '.claude');
27
+ if (!fs.existsSync(claudeDir)) {
28
+ fs.mkdirSync(claudeDir, { recursive: true });
29
+ }
30
+ }
31
+
32
+ /**
33
+ * Map mode to skill name
34
+ * @param {string} mode - Work item mode
35
+ * @returns {string} Skill name
36
+ */
37
+ function modeToSkillName(mode) {
38
+ const mapping = {
39
+ 'speed': 'speed-mode',
40
+ 'stable': 'stable-mode',
41
+ 'production': 'production-mode'
42
+ };
43
+ return mapping[mode] || `${mode}-mode`;
44
+ }
45
+
46
+ /**
47
+ * Write session file with current work context and skill invocation directive
48
+ *
49
+ * @param {Object} workItem - Work item object with id, title, type, status
50
+ * @param {string} mode - Work item mode (speed, stable, production)
51
+ * @param {Object} options - Additional options
52
+ * @param {number} options.epicId - Parent epic ID
53
+ * @param {string} options.epicTitle - Parent epic title
54
+ */
55
+ function writeSessionFile(workItem, mode, options = {}) {
56
+ if (!workItem || typeof workItem !== 'object') {
57
+ throw new Error('Work item must be an object');
58
+ }
59
+ if (!workItem.id || typeof workItem.id !== 'number') {
60
+ throw new Error('Work item must have a numeric id');
61
+ }
62
+ if (!workItem.title || typeof workItem.title !== 'string') {
63
+ throw new Error('Work item must have a string title');
64
+ }
65
+ if (!workItem.type || typeof workItem.type !== 'string') {
66
+ throw new Error('Work item must have a string type');
67
+ }
68
+ if (!workItem.status || typeof workItem.status !== 'string') {
69
+ throw new Error('Work item must have a string status');
70
+ }
71
+
72
+ ensureClaudeDir();
73
+
74
+ const skillName = modeToSkillName(mode);
75
+
76
+ // Build session file content
77
+ let content = `# Current Work Session
78
+
79
+ ## Work Context
80
+
81
+ Working on: [#${workItem.id}] ${workItem.title} (${workItem.type})
82
+ `;
83
+
84
+ if (options.epicId && options.epicTitle) {
85
+ content += `Epic: [#${options.epicId}] ${options.epicTitle}
86
+ `;
87
+ }
88
+
89
+ content += `Mode: ${mode}
90
+ Status: ${workItem.status}
91
+
92
+ ## IMMEDIATE ACTION REQUIRED
93
+
94
+ You are resuming work on a chore in ${mode} mode.
95
+
96
+ **Invoke the ${skillName} skill now to continue the workflow.**
97
+
98
+ This directive ensures the correct mode skill is activated automatically
99
+ when Claude Code starts a new session in this worktree.
100
+ `;
101
+
102
+ const sessionPath = getSessionFilePath();
103
+
104
+ try {
105
+ fs.writeFileSync(sessionPath, content);
106
+ } catch (err) {
107
+ throw new Error(`Failed to write session file: ${err.message}`);
108
+ }
109
+ }
110
+
111
+ /**
112
+ * Clear the session file (delete it)
113
+ */
114
+ function clearSessionFile() {
115
+ const sessionPath = getSessionFilePath();
116
+
117
+ if (fs.existsSync(sessionPath)) {
118
+ try {
119
+ fs.unlinkSync(sessionPath);
120
+ } catch (err) {
121
+ throw new Error(`Failed to clear session file: ${err.message}`);
122
+ }
123
+ }
124
+ }
125
+
126
+ /**
127
+ * Read the session file content
128
+ * @returns {string|null} Session file content or null if not found
129
+ */
130
+ function readSessionFile() {
131
+ const sessionPath = getSessionFilePath();
132
+
133
+ if (!fs.existsSync(sessionPath)) {
134
+ return null;
135
+ }
136
+
137
+ try {
138
+ return fs.readFileSync(sessionPath, 'utf-8');
139
+ } catch (err) {
140
+ throw new Error(`Failed to read session file: ${err.message}`);
141
+ }
142
+ }
143
+
144
+ module.exports = {
145
+ writeSessionFile,
146
+ clearSessionFile,
147
+ readSessionFile,
148
+ getSessionFilePath,
149
+ modeToSkillName
150
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jettypod",
3
- "version": "4.1.6",
3
+ "version": "4.2.0",
4
4
  "description": "AI-powered development workflow manager with TDD, BDD, and automatic test generation",
5
5
  "main": "jettypod.js",
6
6
  "bin": {
@@ -683,7 +683,7 @@ console.log(`Next step: Elevate to stable mode using 'jettypod work set-mode <fe
683
683
  **Display:**
684
684
  ```
685
685
  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
686
- SPEED MODE COMPLETE - GREEN STATE ACHIEVED
686
+ GREEN STATE ACHIEVED - Now Creating Stable Chores
687
687
  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
688
688
 
689
689
  🎉 Happy path scenario: ALL TESTS PASSING
@@ -700,7 +700,8 @@ Files modified:
700
700
  ✏️ src/app.js
701
701
  ✏️ src/index.html
702
702
 
703
- Next step: Elevate to stable mode using 'node jettypod.js work set-mode <feature-id> stable'
703
+ ⚠️ Speed mode is NOT complete until stable chores exist.
704
+ Proceeding to Step 4 to create stable mode chores...
704
705
  ```
705
706
 
706
707
  **On FAILURE (max iterations or timeout):**
@@ -781,14 +782,18 @@ What to do:
781
782
  DO NOT proceed to generate stable mode chores until GREEN is achieved.
782
783
  ```
783
784
 
784
- **Move to Step 4 only if GREEN achieved.**
785
+ **Move to Step 4 IMMEDIATELY if GREEN achieved.**
786
+
787
+ **CRITICAL: GREEN is NOT completion - it's just a checkpoint. Speed mode is INCOMPLETE until stable mode chores are created. You MUST continue to Step 4.**
785
788
 
786
789
  <!-- ═══════════════════════════════════════════════════════════════════════════
787
790
  PHASE 4: STABLE CHORE GENERATION
788
791
  ⚡ ASYNC BOUNDARY - Must wait for user to confirm proposed chores
789
792
  ═══════════════════════════════════════════════════════════════════════════ -->
790
793
 
791
- ### Step 4: Generate Stable Mode Chores
794
+ ### Step 4: Generate Stable Mode Chores (REQUIRED - Do Not Skip)
795
+
796
+ **CRITICAL:** This step is MANDATORY. Speed mode is NOT complete until stable mode chores exist. Without them, users cannot resume work on this feature.
792
797
 
793
798
  **Two phases: Analyze and propose → Get confirmation → Create autonomously**
794
799
 
@@ -984,15 +989,18 @@ Stable mode adds:
984
989
 
985
990
  ```
986
991
  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
987
- 🎯 Speed Mode Chore Complete!
992
+ 🎯 Speed Mode Chore Done - Stable Chores Created
988
993
  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
989
994
 
990
995
  What we accomplished:
991
996
  ✅ Happy path scenario passes for this chore
992
- ✅ Stable mode chores created
997
+ ✅ Stable mode chores created for when speed mode finishes
993
998
 
994
999
  Remaining speed mode chores: [count]
995
1000
 
1001
+ ⚠️ Speed mode is NOT complete yet - [count] chores remain.
1002
+ You can take a break here - stable mode chores exist and work can resume.
1003
+
996
1004
  **Next step:** Continue with next speed mode chore
997
1005
  jettypod work start [next-speed-chore-id]
998
1006
  ```