coder-config 0.42.23 → 0.42.26

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/README.md CHANGED
@@ -211,7 +211,9 @@ coder-config workstream auto-activate "My Work" off # Never auto-activate
211
211
  coder-config workstream auto-activate "My Work" default # Use global setting
212
212
  ```
213
213
 
214
- ### Loop Commands (Ralph Loop)
214
+ ### Loop Commands (Ralph Loop) - Experimental
215
+
216
+ > **Note:** Ralph Loops is an experimental feature, disabled by default. Enable it in the Web UI under **Preferences > Experimental Features**.
215
217
 
216
218
  Ralph Loops enable autonomous development - Claude Code runs continuously until a task is completed.
217
219
 
@@ -268,7 +270,7 @@ coder-config update --check # Check for updates without installing
268
270
  coder-config update /path/src # Update from local development source
269
271
  ```
270
272
 
271
- The UI also checks for updates automatically and shows a notification when a new version is available.
273
+ The UI checks for updates automatically and auto-updates when enabled in Preferences. After server updates, the UI auto-refreshes to load the new version.
272
274
 
273
275
  ### Web UI
274
276
 
@@ -380,6 +382,44 @@ Persistent memory for Claude Code sessions.
380
382
 
381
383
  Manage via Web UI or edit files directly.
382
384
 
385
+ ## Session Persistence
386
+
387
+ Save context from a Claude Code session and restore it on the next session start.
388
+
389
+ ### How It Works
390
+
391
+ 1. **Save context** - Use `/flush` in Claude Code to write a context summary
392
+ 2. **Auto-preserve** - The `session-end` hook preserves your flushed context when the session ends
393
+ 3. **Auto-restore** - The `session-start` hook injects saved context into your next session
394
+
395
+ Context is automatically restored within 24 hours of being saved.
396
+
397
+ ### Setup
398
+
399
+ ```bash
400
+ # Install the Claude Code hooks
401
+ coder-config session install-hooks
402
+
403
+ # Copy the /flush command to your Claude Code commands
404
+ cp /path/to/coder-config/templates/commands/flush.md ~/.claude/commands/
405
+ ```
406
+
407
+ ### CLI Commands
408
+
409
+ ```bash
410
+ coder-config session # Show session status
411
+ coder-config session flush # Instructions for saving context
412
+ coder-config session clear # Clear saved context
413
+ coder-config session install-hooks # Install Claude Code hooks
414
+ ```
415
+
416
+ ### Storage Location
417
+
418
+ Session data is stored in `~/.coder-config/sessions/`:
419
+ - `flushed-context.md` - Context saved by /flush command
420
+ - `last-flushed-context.md` - Preserved context from last session end
421
+ - `last-session.json` - Metadata about the last session
422
+
383
423
  ## Workstreams
384
424
 
385
425
  Workstreams are **context sets** for multi-project workflows. They group related projects and inject context rules into every Claude session.
package/config-loader.js CHANGED
@@ -31,6 +31,7 @@ const { getProjectsRegistryPath, loadProjectsRegistry, saveProjectsRegistry, pro
31
31
  const { getWorkstreamsPath, loadWorkstreams, saveWorkstreams, workstreamList, workstreamCreate, workstreamUpdate, workstreamDelete, workstreamUse, workstreamActive, workstreamAddProject, workstreamRemoveProject, workstreamInject, workstreamDetect, workstreamGet, getActiveWorkstream, countWorkstreamsForProject, workstreamInstallHook, workstreamInstallHookGemini, workstreamInstallHookCodex, workstreamDeactivate, workstreamCheckPath, getSettingsPath, loadSettings, saveSettings, workstreamAddTrigger, workstreamRemoveTrigger, workstreamSetAutoActivate, setGlobalAutoActivate, shouldAutoActivate, workstreamCheckFolder, workstreamInstallCdHook, workstreamUninstallCdHook, workstreamCdHookStatus } = require('./lib/workstreams');
32
32
  const { getActivityPath, getDefaultActivity, loadActivity, saveActivity, detectProjectRoot, activityLog, activitySummary, generateWorkstreamName, activitySuggestWorkstreams, activityClear } = require('./lib/activity');
33
33
  const { getLoopsPath, loadLoops, saveLoops, loadLoopState, saveLoopState, loadHistory, saveHistory, loopList, loopCreate, loopGet, loopUpdate, loopDelete, loopStart, loopPause, loopResume, loopCancel, loopApprove, loopComplete, loopStatus, loopHistory, loopConfig, getActiveLoop, recordIteration, saveClarifications, savePlan, loadClarifications, loadPlan, loopInject, archiveLoop } = require('./lib/loops');
34
+ const { getSessionStatus, showSessionStatus, flushContext, clearContext, installHooks: sessionInstallHooks, getFlushedContext, SESSION_DIR, FLUSHED_CONTEXT_FILE } = require('./lib/sessions');
34
35
  const { runCli } = require('./lib/cli');
35
36
 
36
37
  class ClaudeConfigManager {
@@ -205,6 +206,16 @@ class ClaudeConfigManager {
205
206
  activitySuggestWorkstreams() { return activitySuggestWorkstreams(this.installDir); }
206
207
  activityClear(olderThanDays) { return activityClear(this.installDir, olderThanDays); }
207
208
 
209
+ // Sessions
210
+ getSessionStatus() { return getSessionStatus(); }
211
+ sessionStatus() { return showSessionStatus(); }
212
+ sessionFlush() { return flushContext(); }
213
+ sessionClear() { return clearContext(); }
214
+ sessionInstallHooks() { return sessionInstallHooks(); }
215
+ getFlushedContext() { return getFlushedContext(); }
216
+ getSessionDir() { return SESSION_DIR; }
217
+ getFlushedContextPath() { return FLUSHED_CONTEXT_FILE; }
218
+
208
219
  // Update - check npm for updates or update from local source
209
220
  async update(args = []) {
210
221
  const https = require('https');
@@ -0,0 +1,36 @@
1
+ #!/bin/bash
2
+ # Session End Hook - Save session info for potential resume
3
+ # Install: Add to ~/.claude/settings.json hooks.SessionEnd
4
+
5
+ # Read hook input
6
+ INPUT=$(cat)
7
+ SESSION_ID=$(echo "$INPUT" | jq -r '.session_id // empty')
8
+ TRANSCRIPT_PATH=$(echo "$INPUT" | jq -r '.transcript_path // empty')
9
+ CWD=$(echo "$INPUT" | jq -r '.cwd // empty')
10
+ REASON=$(echo "$INPUT" | jq -r '.reason // empty')
11
+
12
+ # Skip if no session ID
13
+ [ -z "$SESSION_ID" ] && exit 0
14
+
15
+ # Session state directory
16
+ STATE_DIR="$HOME/.coder-config/sessions"
17
+ mkdir -p "$STATE_DIR"
18
+
19
+ # Save session metadata
20
+ cat > "$STATE_DIR/last-session.json" << EOF
21
+ {
22
+ "session_id": "$SESSION_ID",
23
+ "transcript_path": "$TRANSCRIPT_PATH",
24
+ "cwd": "$CWD",
25
+ "reason": "$REASON",
26
+ "timestamp": "$(date -u +"%Y-%m-%dT%H:%M:%SZ")"
27
+ }
28
+ EOF
29
+
30
+ # If there's a flushed context file, preserve it
31
+ FLUSH_FILE="$STATE_DIR/flushed-context.md"
32
+ if [ -f "$FLUSH_FILE" ]; then
33
+ cp "$FLUSH_FILE" "$STATE_DIR/last-flushed-context.md"
34
+ fi
35
+
36
+ exit 0
@@ -0,0 +1,35 @@
1
+ #!/bin/bash
2
+ # Session Start Hook - Restore context from previous session
3
+ # Install: Add to ~/.claude/settings.json hooks.SessionStart
4
+
5
+ # Session state directory
6
+ STATE_DIR="$HOME/.coder-config/sessions"
7
+
8
+ # Check for saved context to inject
9
+ LAST_CONTEXT="$STATE_DIR/last-flushed-context.md"
10
+ LAST_SESSION="$STATE_DIR/last-session.json"
11
+
12
+ # Only inject if we have flushed context
13
+ if [ -f "$LAST_CONTEXT" ]; then
14
+ # Check if context is recent (within 24 hours)
15
+ if [ "$(uname)" = "Darwin" ]; then
16
+ # macOS
17
+ FILE_AGE=$(( $(date +%s) - $(stat -f %m "$LAST_CONTEXT") ))
18
+ else
19
+ # Linux
20
+ FILE_AGE=$(( $(date +%s) - $(stat -c %Y "$LAST_CONTEXT") ))
21
+ fi
22
+
23
+ # 24 hours = 86400 seconds
24
+ if [ "$FILE_AGE" -lt 86400 ]; then
25
+ # Output context - Claude will see this in the session
26
+ echo "<session-context source=\"previous-session\">"
27
+ echo "The following context was saved from a previous session:"
28
+ echo ""
29
+ cat "$LAST_CONTEXT"
30
+ echo ""
31
+ echo "</session-context>"
32
+ fi
33
+ fi
34
+
35
+ exit 0
package/lib/cli.js CHANGED
@@ -272,6 +272,19 @@ function runCli(manager) {
272
272
  }
273
273
  break;
274
274
 
275
+ // Session management
276
+ case 'session':
277
+ if (args[1] === 'flush') {
278
+ manager.sessionFlush();
279
+ } else if (args[1] === 'clear') {
280
+ manager.sessionClear();
281
+ } else if (args[1] === 'install-hooks') {
282
+ manager.sessionInstallHooks();
283
+ } else {
284
+ manager.sessionStatus();
285
+ }
286
+ break;
287
+
275
288
  // Maintenance
276
289
  case 'update':
277
290
  manager.update(args.slice(1)).catch(err => {
@@ -392,6 +405,12 @@ Workstream Commands:
392
405
  Per-session activation (enables parallel work):
393
406
  export CODER_WORKSTREAM=<name-or-id>
394
407
  ${loopHelp}
408
+ Session Persistence:
409
+ session Show session status (saved context)
410
+ session flush Manually save current context
411
+ session clear Clear saved session context
412
+ session install-hooks Install Claude Code hooks for session persistence
413
+
395
414
  Registry Commands:
396
415
  registry List MCPs in global registry
397
416
  registry add <name> '<json>' Add MCP to global registry
package/lib/constants.js CHANGED
@@ -2,7 +2,7 @@
2
2
  * Constants and tool path configurations
3
3
  */
4
4
 
5
- const VERSION = '0.42.23';
5
+ const VERSION = '0.42.26';
6
6
 
7
7
  // Tool-specific path configurations
8
8
  const TOOL_PATHS = {
@@ -0,0 +1,262 @@
1
+ /**
2
+ * Session persistence management
3
+ * Saves and restores Claude Code session context
4
+ */
5
+
6
+ const fs = require('fs');
7
+ const path = require('path');
8
+ const os = require('os');
9
+
10
+ const SESSION_DIR = path.join(os.homedir(), '.coder-config', 'sessions');
11
+ const LAST_SESSION_FILE = path.join(SESSION_DIR, 'last-session.json');
12
+ const FLUSHED_CONTEXT_FILE = path.join(SESSION_DIR, 'flushed-context.md');
13
+ const LAST_FLUSHED_FILE = path.join(SESSION_DIR, 'last-flushed-context.md');
14
+
15
+ /**
16
+ * Ensure session directory exists
17
+ */
18
+ function ensureSessionDir() {
19
+ if (!fs.existsSync(SESSION_DIR)) {
20
+ fs.mkdirSync(SESSION_DIR, { recursive: true });
21
+ }
22
+ }
23
+
24
+ /**
25
+ * Get session status
26
+ */
27
+ function getSessionStatus() {
28
+ ensureSessionDir();
29
+
30
+ const status = {
31
+ hasSavedContext: false,
32
+ lastSession: null,
33
+ contextAge: null,
34
+ };
35
+
36
+ // Check for flushed context
37
+ if (fs.existsSync(LAST_FLUSHED_FILE)) {
38
+ status.hasSavedContext = true;
39
+ const stat = fs.statSync(LAST_FLUSHED_FILE);
40
+ status.contextAge = Math.floor((Date.now() - stat.mtimeMs) / 1000 / 60); // minutes
41
+ } else if (fs.existsSync(FLUSHED_CONTEXT_FILE)) {
42
+ status.hasSavedContext = true;
43
+ const stat = fs.statSync(FLUSHED_CONTEXT_FILE);
44
+ status.contextAge = Math.floor((Date.now() - stat.mtimeMs) / 1000 / 60);
45
+ }
46
+
47
+ // Check for last session metadata
48
+ if (fs.existsSync(LAST_SESSION_FILE)) {
49
+ try {
50
+ status.lastSession = JSON.parse(fs.readFileSync(LAST_SESSION_FILE, 'utf8'));
51
+ } catch (e) {
52
+ // Ignore parse errors
53
+ }
54
+ }
55
+
56
+ return status;
57
+ }
58
+
59
+ /**
60
+ * Show session status
61
+ */
62
+ function showSessionStatus() {
63
+ const status = getSessionStatus();
64
+
65
+ console.log('Session Persistence Status\n');
66
+
67
+ if (status.hasSavedContext) {
68
+ console.log(` Saved context: Yes`);
69
+ if (status.contextAge !== null) {
70
+ if (status.contextAge < 60) {
71
+ console.log(` Context age: ${status.contextAge} minutes`);
72
+ } else if (status.contextAge < 1440) {
73
+ console.log(` Context age: ${Math.floor(status.contextAge / 60)} hours`);
74
+ } else {
75
+ console.log(` Context age: ${Math.floor(status.contextAge / 1440)} days`);
76
+ }
77
+ }
78
+ } else {
79
+ console.log(' Saved context: None');
80
+ }
81
+
82
+ if (status.lastSession) {
83
+ console.log(`\n Last session:`);
84
+ console.log(` ID: ${status.lastSession.session_id || 'unknown'}`);
85
+ console.log(` CWD: ${status.lastSession.cwd || 'unknown'}`);
86
+ console.log(` Ended: ${status.lastSession.timestamp || 'unknown'}`);
87
+ console.log(` Reason: ${status.lastSession.reason || 'unknown'}`);
88
+ }
89
+
90
+ console.log(`\n Storage: ${SESSION_DIR}`);
91
+
92
+ if (!status.hasSavedContext) {
93
+ console.log('\nTo save context, use /flush in Claude Code or:');
94
+ console.log(' coder-config session flush');
95
+ }
96
+ }
97
+
98
+ /**
99
+ * Print instructions for manual flush
100
+ * (Actual flush happens in Claude Code via /flush command)
101
+ */
102
+ function flushContext() {
103
+ console.log('Session context flush\n');
104
+ console.log('To save session context, use the /flush command in Claude Code.');
105
+ console.log('This tells Claude to write a summary to:');
106
+ console.log(` ${FLUSHED_CONTEXT_FILE}\n`);
107
+ console.log('The context will be automatically restored on the next session start');
108
+ console.log('(if session hooks are installed).\n');
109
+ console.log('Install hooks with:');
110
+ console.log(' coder-config session install-hooks');
111
+ }
112
+
113
+ /**
114
+ * Clear saved session context
115
+ */
116
+ function clearContext() {
117
+ ensureSessionDir();
118
+
119
+ let cleared = false;
120
+
121
+ if (fs.existsSync(FLUSHED_CONTEXT_FILE)) {
122
+ fs.unlinkSync(FLUSHED_CONTEXT_FILE);
123
+ cleared = true;
124
+ }
125
+
126
+ if (fs.existsSync(LAST_FLUSHED_FILE)) {
127
+ fs.unlinkSync(LAST_FLUSHED_FILE);
128
+ cleared = true;
129
+ }
130
+
131
+ if (fs.existsSync(LAST_SESSION_FILE)) {
132
+ fs.unlinkSync(LAST_SESSION_FILE);
133
+ cleared = true;
134
+ }
135
+
136
+ if (cleared) {
137
+ console.log('Session context cleared.');
138
+ } else {
139
+ console.log('No session context to clear.');
140
+ }
141
+ }
142
+
143
+ /**
144
+ * Install Claude Code hooks for session persistence
145
+ */
146
+ function installHooks() {
147
+ const claudeDir = path.join(os.homedir(), '.claude');
148
+ const settingsFile = path.join(claudeDir, 'settings.json');
149
+
150
+ // Ensure .claude directory exists
151
+ if (!fs.existsSync(claudeDir)) {
152
+ fs.mkdirSync(claudeDir, { recursive: true });
153
+ }
154
+
155
+ // Load existing settings
156
+ let settings = {};
157
+ if (fs.existsSync(settingsFile)) {
158
+ try {
159
+ settings = JSON.parse(fs.readFileSync(settingsFile, 'utf8'));
160
+ } catch (e) {
161
+ console.error('Error reading settings.json:', e.message);
162
+ return;
163
+ }
164
+ }
165
+
166
+ // Initialize hooks if needed
167
+ if (!settings.hooks) {
168
+ settings.hooks = {};
169
+ }
170
+
171
+ // Find coder-config hooks directory
172
+ const hooksSrcDir = path.join(__dirname, '..', 'hooks');
173
+ const sessionStartHook = path.join(hooksSrcDir, 'session-start.sh');
174
+ const sessionEndHook = path.join(hooksSrcDir, 'session-end.sh');
175
+
176
+ // Verify hooks exist
177
+ if (!fs.existsSync(sessionStartHook) || !fs.existsSync(sessionEndHook)) {
178
+ console.error('Session hooks not found in coder-config package.');
179
+ console.log('Expected locations:');
180
+ console.log(` ${sessionStartHook}`);
181
+ console.log(` ${sessionEndHook}`);
182
+ return;
183
+ }
184
+
185
+ // Make hooks executable
186
+ try {
187
+ fs.chmodSync(sessionStartHook, '755');
188
+ fs.chmodSync(sessionEndHook, '755');
189
+ } catch (e) {
190
+ console.warn('Could not set executable permission on hooks:', e.message);
191
+ }
192
+
193
+ // Add SessionStart hook
194
+ if (!settings.hooks.SessionStart) {
195
+ settings.hooks.SessionStart = [];
196
+ }
197
+ if (!Array.isArray(settings.hooks.SessionStart)) {
198
+ settings.hooks.SessionStart = [settings.hooks.SessionStart];
199
+ }
200
+
201
+ // Check if our hook is already installed
202
+ const startHookEntry = { type: 'command', command: sessionStartHook };
203
+ const hasStartHook = settings.hooks.SessionStart.some(h =>
204
+ typeof h === 'object' && h.command === sessionStartHook
205
+ );
206
+ if (!hasStartHook) {
207
+ settings.hooks.SessionStart.push(startHookEntry);
208
+ }
209
+
210
+ // Add SessionEnd hook
211
+ if (!settings.hooks.SessionEnd) {
212
+ settings.hooks.SessionEnd = [];
213
+ }
214
+ if (!Array.isArray(settings.hooks.SessionEnd)) {
215
+ settings.hooks.SessionEnd = [settings.hooks.SessionEnd];
216
+ }
217
+
218
+ const endHookEntry = { type: 'command', command: sessionEndHook };
219
+ const hasEndHook = settings.hooks.SessionEnd.some(h =>
220
+ typeof h === 'object' && h.command === sessionEndHook
221
+ );
222
+ if (!hasEndHook) {
223
+ settings.hooks.SessionEnd.push(endHookEntry);
224
+ }
225
+
226
+ // Save settings
227
+ try {
228
+ fs.writeFileSync(settingsFile, JSON.stringify(settings, null, 2));
229
+ console.log('Session hooks installed.\n');
230
+ console.log('Hooks added:');
231
+ console.log(' - SessionStart: Restores saved context');
232
+ console.log(' - SessionEnd: Preserves flushed context\n');
233
+ console.log('To save context before exiting, use /flush in Claude Code.');
234
+ console.log('The saved context will be restored on the next session start.');
235
+ } catch (e) {
236
+ console.error('Error writing settings.json:', e.message);
237
+ }
238
+ }
239
+
240
+ /**
241
+ * Get flushed context content (for hooks)
242
+ */
243
+ function getFlushedContext() {
244
+ if (fs.existsSync(LAST_FLUSHED_FILE)) {
245
+ return fs.readFileSync(LAST_FLUSHED_FILE, 'utf8');
246
+ }
247
+ if (fs.existsSync(FLUSHED_CONTEXT_FILE)) {
248
+ return fs.readFileSync(FLUSHED_CONTEXT_FILE, 'utf8');
249
+ }
250
+ return null;
251
+ }
252
+
253
+ module.exports = {
254
+ getSessionStatus,
255
+ showSessionStatus,
256
+ flushContext,
257
+ clearContext,
258
+ installHooks,
259
+ getFlushedContext,
260
+ SESSION_DIR,
261
+ FLUSHED_CONTEXT_FILE,
262
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "coder-config",
3
- "version": "0.42.23",
3
+ "version": "0.42.26",
4
4
  "description": "Configuration manager for AI coding tools - Claude Code, Gemini CLI, Codex CLI, Antigravity. Manage MCPs, rules, permissions, memory, and workstreams.",
5
5
  "author": "regression.io",
6
6
  "main": "config-loader.js",
@@ -0,0 +1,47 @@
1
+ # Flush Context to Resumable Doc
2
+
3
+ Save all current session context to a resumable document that will be automatically restored on the next session start.
4
+
5
+ ## Instructions
6
+
7
+ 1. Create a comprehensive summary of the current session including:
8
+ - **Task Summary**: What the user asked for and the overall goal
9
+ - **Current State**: Where we are in the task (completed, in-progress, blocked)
10
+ - **Key Decisions Made**: Important choices and their rationale
11
+ - **Files Modified**: List of files created or changed
12
+ - **Pending Work**: What still needs to be done
13
+ - **Important Context**: Any critical information needed to continue
14
+
15
+ 2. Write this summary to: `~/.coder-config/sessions/flushed-context.md`
16
+
17
+ 3. Confirm to the user that context has been saved and will be restored on next session.
18
+
19
+ ## Output Format
20
+
21
+ Write the file in this format:
22
+
23
+ ```markdown
24
+ # Session Context - [Date]
25
+
26
+ ## Task Summary
27
+ [What the user wanted to accomplish]
28
+
29
+ ## Current State
30
+ [in-progress | completed | blocked]
31
+ [Brief status description]
32
+
33
+ ## Key Decisions
34
+ - [Decision 1]: [Rationale]
35
+ - [Decision 2]: [Rationale]
36
+
37
+ ## Files Modified
38
+ - `path/to/file1` - [what was changed]
39
+ - `path/to/file2` - [what was changed]
40
+
41
+ ## Pending Work
42
+ - [ ] [Task 1]
43
+ - [ ] [Task 2]
44
+
45
+ ## Important Context
46
+ [Any critical information needed to continue this work]
47
+ ```