mirror of
https://github.com/affaan-m/ECC.git
synced 2026-08-21 15:02:23 +02:00
* fix(ci): resync lockfiles with package.json (eslint 10) + migrate yarn.lock to Yarn 4 format package.json requires eslint@^10.6.0 but the committed locks pinned 9.39.2, so npm ci aborted and Yarn 4 hardened mode rejected the stale v1-classic yarn.lock (YN0028). Regenerate package-lock.json and rewrite yarn.lock in Yarn 4 (berry) format so npm ci and immutable yarn installs both pass. Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> * fix(ci): require clean probe exit for Windows shell/bash detection; add pyyaml dev dep Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> * refactor: consolidate duplicated hook-root resolver into shared resolveEccRoot() (#2368) The inline node -e resolver blob was duplicated ~60x across hooks.json, command docs, and translations. Each copy inlined the full ~700-char plugin-root search using a spread over nested array literals (p.join(d,'plugins',...s) over [['ecc'],...]), which breaks Windows hook execution due to shell quoting (#2368). Collapse every copy to a 250-char locator that loads the committed resolve-ecc-root module and delegates to resolveEccRoot() — no spread, no nested array literals, no escaped double quotes. The real search logic now lives in one tested module. Also route session-start-bootstrap.js through resolveEccRoot() instead of its own duplicated reimplementation, and fix the auto-update.md 'marketplace' (singular) typo along the way. Guard tests updated: discovery behavior is asserted against resolveEccRoot(); the inline is asserted to delegate and to contain no Windows-fragile constructs. Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> * fix(resolve-ecc-root): restore full env-unset discovery in inline resolver Address Greptile review on #2410: when CLAUDE_PLUGIN_ROOT is unset the delegating inline could only load the resolver module from ~/.claude, returning ~/.claude without ever reaching the plugin/cache search. Restore the old inline's discovery breadth (exact plugin roots + versioned cache) Windows-safely (no spread, nested arrays, or escaped quotes), then delegate the authoritative decision to resolveEccRoot(). Add regression tests for plugin-subdir and versioned-cache bootstrap with env unset. Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --------- Co-authored-by: affaan <affaan@itomarkets.com> Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
340 lines
14 KiB
Markdown
340 lines
14 KiB
Markdown
---
|
|
description: Manage Claude Code session history, aliases, and session metadata.
|
|
---
|
|
|
|
# Sessions Command
|
|
|
|
Manage Claude Code session history - list, load, alias, and edit sessions stored in `~/.claude/session-data/` with legacy reads from `~/.claude/sessions/`.
|
|
|
|
## Usage
|
|
|
|
`/sessions [list|load|alias|info|help] [options]`
|
|
|
|
## Actions
|
|
|
|
### List Sessions
|
|
|
|
Display all sessions with metadata, filtering, and pagination.
|
|
|
|
Use `/sessions info` when you need operator-surface context for a swarm: branch, worktree path, and session recency.
|
|
|
|
```bash
|
|
/sessions # List all sessions (default)
|
|
/sessions list # Same as above
|
|
/sessions list --limit 10 # Show 10 sessions
|
|
/sessions list --date 2026-02-01 # Filter by date
|
|
/sessions list --search abc # Search by session ID
|
|
```
|
|
|
|
**Script:**
|
|
```bash
|
|
node -e "
|
|
const _r = (function(){var p=require('path'),f=require('fs'),o=require('os');var e=process.env.CLAUDE_PLUGIN_ROOT;if(e&&e.trim())return e.trim();var d=p.join(o.homedir(),'.claude');function L(x){try{return require(p.join(x,'scripts','lib','resolve-ecc-root')).resolveEccRoot()}catch(_){return null}}var r=L(d);if(r)return r;var s=['ecc','ecc@ecc','marketplaces/ecc','everything-claude-code','everything-claude-code@everything-claude-code','marketplaces/everything-claude-code'];for(var i=0;i<s.length;i++){r=L(p.join(d,'plugins',s[i]));if(r)return r}try{var g=['ecc','everything-claude-code'];for(var j=0;j<g.length;j++){var c=p.join(d,'plugins','cache',g[j]);var O=f.readdirSync(c);for(var k=0;k<O.length;k++){var q=p.join(c,O[k]);var V=f.readdirSync(q);for(var m=0;m<V.length;m++){r=L(p.join(q,V[m]));if(r)return r}}}}catch(_){}return d})();
|
|
const sm = require(_r + '/scripts/lib/session-manager');
|
|
const aa = require(_r + '/scripts/lib/session-aliases');
|
|
const path = require('path');
|
|
|
|
const result = sm.getAllSessions({ limit: 20 });
|
|
const aliases = aa.listAliases();
|
|
const aliasMap = {};
|
|
for (const a of aliases) aliasMap[a.sessionPath] = a.name;
|
|
|
|
console.log('Sessions (showing ' + result.sessions.length + ' of ' + result.total + '):');
|
|
console.log('');
|
|
console.log('ID Date Time Branch Worktree Alias');
|
|
console.log('────────────────────────────────────────────────────────────────────');
|
|
|
|
for (const s of result.sessions) {
|
|
const alias = aliasMap[s.filename] || '';
|
|
const metadata = sm.parseSessionMetadata(sm.getSessionContent(s.sessionPath));
|
|
const id = s.shortId === 'no-id' ? '(none)' : s.shortId.slice(0, 8);
|
|
const time = s.modifiedTime.toTimeString().slice(0, 5);
|
|
const branch = (metadata.branch || '-').slice(0, 12);
|
|
const worktree = metadata.worktree ? path.basename(metadata.worktree).slice(0, 18) : '-';
|
|
|
|
console.log(id.padEnd(8) + ' ' + s.date + ' ' + time + ' ' + branch.padEnd(12) + ' ' + worktree.padEnd(18) + ' ' + alias);
|
|
}
|
|
"
|
|
```
|
|
|
|
### Load Session
|
|
|
|
Load and display a session's content (by ID or alias).
|
|
|
|
```bash
|
|
/sessions load <id|alias> # Load session
|
|
/sessions load 2026-02-01 # By date (for no-id sessions)
|
|
/sessions load a1b2c3d4 # By short ID
|
|
/sessions load my-alias # By alias name
|
|
```
|
|
|
|
**Script:**
|
|
```bash
|
|
node -e "
|
|
const _r = (function(){var p=require('path'),f=require('fs'),o=require('os');var e=process.env.CLAUDE_PLUGIN_ROOT;if(e&&e.trim())return e.trim();var d=p.join(o.homedir(),'.claude');function L(x){try{return require(p.join(x,'scripts','lib','resolve-ecc-root')).resolveEccRoot()}catch(_){return null}}var r=L(d);if(r)return r;var s=['ecc','ecc@ecc','marketplaces/ecc','everything-claude-code','everything-claude-code@everything-claude-code','marketplaces/everything-claude-code'];for(var i=0;i<s.length;i++){r=L(p.join(d,'plugins',s[i]));if(r)return r}try{var g=['ecc','everything-claude-code'];for(var j=0;j<g.length;j++){var c=p.join(d,'plugins','cache',g[j]);var O=f.readdirSync(c);for(var k=0;k<O.length;k++){var q=p.join(c,O[k]);var V=f.readdirSync(q);for(var m=0;m<V.length;m++){r=L(p.join(q,V[m]));if(r)return r}}}}catch(_){}return d})();
|
|
const sm = require(_r + '/scripts/lib/session-manager');
|
|
const aa = require(_r + '/scripts/lib/session-aliases');
|
|
const id = process.argv[1];
|
|
|
|
// First try to resolve as alias
|
|
const resolved = aa.resolveAlias(id);
|
|
const sessionId = resolved ? resolved.sessionPath : id;
|
|
|
|
const session = sm.getSessionById(sessionId, true);
|
|
if (!session) {
|
|
console.log('Session not found: ' + id);
|
|
process.exit(1);
|
|
}
|
|
|
|
const stats = sm.getSessionStats(session.sessionPath);
|
|
const size = sm.getSessionSize(session.sessionPath);
|
|
const aliases = aa.getAliasesForSession(session.filename);
|
|
|
|
console.log('Session: ' + session.filename);
|
|
console.log('Path: ' + session.sessionPath);
|
|
console.log('');
|
|
console.log('Statistics:');
|
|
console.log(' Lines: ' + stats.lineCount);
|
|
console.log(' Total items: ' + stats.totalItems);
|
|
console.log(' Completed: ' + stats.completedItems);
|
|
console.log(' In progress: ' + stats.inProgressItems);
|
|
console.log(' Size: ' + size);
|
|
console.log('');
|
|
|
|
if (aliases.length > 0) {
|
|
console.log('Aliases: ' + aliases.map(a => a.name).join(', '));
|
|
console.log('');
|
|
}
|
|
|
|
if (session.metadata.title) {
|
|
console.log('Title: ' + session.metadata.title);
|
|
console.log('');
|
|
}
|
|
|
|
if (session.metadata.started) {
|
|
console.log('Started: ' + session.metadata.started);
|
|
}
|
|
|
|
if (session.metadata.lastUpdated) {
|
|
console.log('Last Updated: ' + session.metadata.lastUpdated);
|
|
}
|
|
|
|
if (session.metadata.project) {
|
|
console.log('Project: ' + session.metadata.project);
|
|
}
|
|
|
|
if (session.metadata.branch) {
|
|
console.log('Branch: ' + session.metadata.branch);
|
|
}
|
|
|
|
if (session.metadata.worktree) {
|
|
console.log('Worktree: ' + session.metadata.worktree);
|
|
}
|
|
" "$ARGUMENTS"
|
|
```
|
|
|
|
### Create Alias
|
|
|
|
Create a memorable alias for a session.
|
|
|
|
```bash
|
|
/sessions alias <id> <name> # Create alias
|
|
/sessions alias 2026-02-01 today-work # Create alias named "today-work"
|
|
```
|
|
|
|
**Script:**
|
|
```bash
|
|
node -e "
|
|
const _r = (function(){var p=require('path'),f=require('fs'),o=require('os');var e=process.env.CLAUDE_PLUGIN_ROOT;if(e&&e.trim())return e.trim();var d=p.join(o.homedir(),'.claude');function L(x){try{return require(p.join(x,'scripts','lib','resolve-ecc-root')).resolveEccRoot()}catch(_){return null}}var r=L(d);if(r)return r;var s=['ecc','ecc@ecc','marketplaces/ecc','everything-claude-code','everything-claude-code@everything-claude-code','marketplaces/everything-claude-code'];for(var i=0;i<s.length;i++){r=L(p.join(d,'plugins',s[i]));if(r)return r}try{var g=['ecc','everything-claude-code'];for(var j=0;j<g.length;j++){var c=p.join(d,'plugins','cache',g[j]);var O=f.readdirSync(c);for(var k=0;k<O.length;k++){var q=p.join(c,O[k]);var V=f.readdirSync(q);for(var m=0;m<V.length;m++){r=L(p.join(q,V[m]));if(r)return r}}}}catch(_){}return d})();
|
|
const sm = require(_r + '/scripts/lib/session-manager');
|
|
const aa = require(_r + '/scripts/lib/session-aliases');
|
|
|
|
const sessionId = process.argv[1];
|
|
const aliasName = process.argv[2];
|
|
|
|
if (!sessionId || !aliasName) {
|
|
console.log('Usage: /sessions alias <id> <name>');
|
|
process.exit(1);
|
|
}
|
|
|
|
// Get session filename
|
|
const session = sm.getSessionById(sessionId);
|
|
if (!session) {
|
|
console.log('Session not found: ' + sessionId);
|
|
process.exit(1);
|
|
}
|
|
|
|
const result = aa.setAlias(aliasName, session.filename);
|
|
if (result.success) {
|
|
console.log('✓ Alias created: ' + aliasName + ' → ' + session.filename);
|
|
} else {
|
|
console.log('✗ Error: ' + result.error);
|
|
process.exit(1);
|
|
}
|
|
" "$ARGUMENTS"
|
|
```
|
|
|
|
### Remove Alias
|
|
|
|
Delete an existing alias.
|
|
|
|
```bash
|
|
/sessions alias --remove <name> # Remove alias
|
|
/sessions unalias <name> # Same as above
|
|
```
|
|
|
|
**Script:**
|
|
```bash
|
|
node -e "
|
|
const _r = (function(){var p=require('path'),f=require('fs'),o=require('os');var e=process.env.CLAUDE_PLUGIN_ROOT;if(e&&e.trim())return e.trim();var d=p.join(o.homedir(),'.claude');function L(x){try{return require(p.join(x,'scripts','lib','resolve-ecc-root')).resolveEccRoot()}catch(_){return null}}var r=L(d);if(r)return r;var s=['ecc','ecc@ecc','marketplaces/ecc','everything-claude-code','everything-claude-code@everything-claude-code','marketplaces/everything-claude-code'];for(var i=0;i<s.length;i++){r=L(p.join(d,'plugins',s[i]));if(r)return r}try{var g=['ecc','everything-claude-code'];for(var j=0;j<g.length;j++){var c=p.join(d,'plugins','cache',g[j]);var O=f.readdirSync(c);for(var k=0;k<O.length;k++){var q=p.join(c,O[k]);var V=f.readdirSync(q);for(var m=0;m<V.length;m++){r=L(p.join(q,V[m]));if(r)return r}}}}catch(_){}return d})();
|
|
const aa = require(_r + '/scripts/lib/session-aliases');
|
|
|
|
const aliasName = process.argv[1];
|
|
if (!aliasName) {
|
|
console.log('Usage: /sessions alias --remove <name>');
|
|
process.exit(1);
|
|
}
|
|
|
|
const result = aa.deleteAlias(aliasName);
|
|
if (result.success) {
|
|
console.log('✓ Alias removed: ' + aliasName);
|
|
} else {
|
|
console.log('✗ Error: ' + result.error);
|
|
process.exit(1);
|
|
}
|
|
" "$ARGUMENTS"
|
|
```
|
|
|
|
### Session Info
|
|
|
|
Show detailed information about a session.
|
|
|
|
```bash
|
|
/sessions info <id|alias> # Show session details
|
|
```
|
|
|
|
**Script:**
|
|
```bash
|
|
node -e "
|
|
const _r = (function(){var p=require('path'),f=require('fs'),o=require('os');var e=process.env.CLAUDE_PLUGIN_ROOT;if(e&&e.trim())return e.trim();var d=p.join(o.homedir(),'.claude');function L(x){try{return require(p.join(x,'scripts','lib','resolve-ecc-root')).resolveEccRoot()}catch(_){return null}}var r=L(d);if(r)return r;var s=['ecc','ecc@ecc','marketplaces/ecc','everything-claude-code','everything-claude-code@everything-claude-code','marketplaces/everything-claude-code'];for(var i=0;i<s.length;i++){r=L(p.join(d,'plugins',s[i]));if(r)return r}try{var g=['ecc','everything-claude-code'];for(var j=0;j<g.length;j++){var c=p.join(d,'plugins','cache',g[j]);var O=f.readdirSync(c);for(var k=0;k<O.length;k++){var q=p.join(c,O[k]);var V=f.readdirSync(q);for(var m=0;m<V.length;m++){r=L(p.join(q,V[m]));if(r)return r}}}}catch(_){}return d})();
|
|
const sm = require(_r + '/scripts/lib/session-manager');
|
|
const aa = require(_r + '/scripts/lib/session-aliases');
|
|
|
|
const id = process.argv[1];
|
|
const resolved = aa.resolveAlias(id);
|
|
const sessionId = resolved ? resolved.sessionPath : id;
|
|
|
|
const session = sm.getSessionById(sessionId, true);
|
|
if (!session) {
|
|
console.log('Session not found: ' + id);
|
|
process.exit(1);
|
|
}
|
|
|
|
const stats = sm.getSessionStats(session.sessionPath);
|
|
const size = sm.getSessionSize(session.sessionPath);
|
|
const aliases = aa.getAliasesForSession(session.filename);
|
|
|
|
console.log('Session Information');
|
|
console.log('════════════════════');
|
|
console.log('ID: ' + (session.shortId === 'no-id' ? '(none)' : session.shortId));
|
|
console.log('Filename: ' + session.filename);
|
|
console.log('Date: ' + session.date);
|
|
console.log('Modified: ' + session.modifiedTime.toISOString().slice(0, 19).replace('T', ' '));
|
|
console.log('Project: ' + (session.metadata.project || '-'));
|
|
console.log('Branch: ' + (session.metadata.branch || '-'));
|
|
console.log('Worktree: ' + (session.metadata.worktree || '-'));
|
|
console.log('');
|
|
console.log('Content:');
|
|
console.log(' Lines: ' + stats.lineCount);
|
|
console.log(' Total items: ' + stats.totalItems);
|
|
console.log(' Completed: ' + stats.completedItems);
|
|
console.log(' In progress: ' + stats.inProgressItems);
|
|
console.log(' Size: ' + size);
|
|
if (aliases.length > 0) {
|
|
console.log('Aliases: ' + aliases.map(a => a.name).join(', '));
|
|
}
|
|
" "$ARGUMENTS"
|
|
```
|
|
|
|
### List Aliases
|
|
|
|
Show all session aliases.
|
|
|
|
```bash
|
|
/sessions aliases # List all aliases
|
|
```
|
|
|
|
**Script:**
|
|
```bash
|
|
node -e "
|
|
const _r = (function(){var p=require('path'),f=require('fs'),o=require('os');var e=process.env.CLAUDE_PLUGIN_ROOT;if(e&&e.trim())return e.trim();var d=p.join(o.homedir(),'.claude');function L(x){try{return require(p.join(x,'scripts','lib','resolve-ecc-root')).resolveEccRoot()}catch(_){return null}}var r=L(d);if(r)return r;var s=['ecc','ecc@ecc','marketplaces/ecc','everything-claude-code','everything-claude-code@everything-claude-code','marketplaces/everything-claude-code'];for(var i=0;i<s.length;i++){r=L(p.join(d,'plugins',s[i]));if(r)return r}try{var g=['ecc','everything-claude-code'];for(var j=0;j<g.length;j++){var c=p.join(d,'plugins','cache',g[j]);var O=f.readdirSync(c);for(var k=0;k<O.length;k++){var q=p.join(c,O[k]);var V=f.readdirSync(q);for(var m=0;m<V.length;m++){r=L(p.join(q,V[m]));if(r)return r}}}}catch(_){}return d})();
|
|
const aa = require(_r + '/scripts/lib/session-aliases');
|
|
|
|
const aliases = aa.listAliases();
|
|
console.log('Session Aliases (' + aliases.length + '):');
|
|
console.log('');
|
|
|
|
if (aliases.length === 0) {
|
|
console.log('No aliases found.');
|
|
} else {
|
|
console.log('Name Session File Title');
|
|
console.log('─────────────────────────────────────────────────────────────');
|
|
for (const a of aliases) {
|
|
const name = a.name.padEnd(12);
|
|
const file = (a.sessionPath.length > 30 ? a.sessionPath.slice(0, 27) + '...' : a.sessionPath).padEnd(30);
|
|
const title = a.title || '';
|
|
console.log(name + ' ' + file + ' ' + title);
|
|
}
|
|
}
|
|
"
|
|
```
|
|
|
|
## Operator Notes
|
|
|
|
- Session files persist `Project`, `Branch`, and `Worktree` in the header so `/sessions info` can disambiguate parallel tmux/worktree runs.
|
|
- For command-center style monitoring, combine `/sessions info`, `git diff --stat`, and the cost metrics emitted by `scripts/hooks/cost-tracker.js`.
|
|
|
|
## Arguments
|
|
|
|
$ARGUMENTS:
|
|
- `list [options]` - List sessions
|
|
- `--limit <n>` - Max sessions to show (default: 50)
|
|
- `--date <YYYY-MM-DD>` - Filter by date
|
|
- `--search <pattern>` - Search in session ID
|
|
- `load <id|alias>` - Load session content
|
|
- `alias <id> <name>` - Create alias for session
|
|
- `alias --remove <name>` - Remove alias
|
|
- `unalias <name>` - Same as `--remove`
|
|
- `info <id|alias>` - Show session statistics
|
|
- `aliases` - List all aliases
|
|
- `help` - Show this help
|
|
|
|
## Examples
|
|
|
|
```bash
|
|
# List all sessions
|
|
/sessions list
|
|
|
|
# Create an alias for today's session
|
|
/sessions alias 2026-02-01 today
|
|
|
|
# Load session by alias
|
|
/sessions load today
|
|
|
|
# Show session info
|
|
/sessions info today
|
|
|
|
# Remove alias
|
|
/sessions alias --remove today
|
|
|
|
# List all aliases
|
|
/sessions aliases
|
|
```
|
|
|
|
## Notes
|
|
|
|
- Sessions are stored as markdown files in `~/.claude/session-data/` with legacy reads from `~/.claude/sessions/`
|
|
- Aliases are stored in `~/.claude/session-aliases.json`
|
|
- Session IDs can be shortened (first 4-8 characters usually unique enough)
|
|
- Use aliases for frequently referenced sessions
|