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 +9 -60
- package/lib/session-writer.js +150 -0
- package/package.json +1 -1
- package/skills-templates/speed-mode/SKILL.md +14 -6
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
|
|
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
|
-
//
|
|
78
|
-
|
|
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
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
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
|
@@ -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
|
-
✅
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
```
|