Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
105 changes: 92 additions & 13 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ User preferences in `sessions/sessions-config.json`:
- **Environment**: developer_name, os, shell
- **Trigger Phrases**: Customizable for all mode transitions
- **Git Preferences**: Branch naming, commit styles, auto-merge/push, submodules
- **Feature Toggles**: branch_enforcement, task_detection, auto_ultrathink, icon_style, context warnings
- **Feature Toggles**: sessions_enabled, branch_enforcement, task_detection, auto_ultrathink, icon_style, context warnings
- **Blocking Patterns**: implementation_only_tools, bash_read_patterns, bash_write_patterns

### Templated Protocols
Expand Down Expand Up @@ -93,8 +93,6 @@ Configuration-driven protocol system that auto-adapts based on user preferences:
- `cc_sessions/install.py` (Python) or `install.js` (JavaScript) - Language-specific installers
- `cc_sessions/commands/` - Slash command wrappers
- `cc_sessions/templates/` - Task templates
- `scripts/prepare-release.py` - Pre-flight validation for releases
- `scripts/publish-release.py` - Atomic dual-package publishing workflow

## Installation

Expand All @@ -114,11 +112,44 @@ uv pip install cc-sessions # UV package manager
npx cc-sessions
```

### Development
### Development Setup

For local development, install cc-sessions in symlinked mode from the repository:

**Python Development**:
```bash
cd /path/to/cc-sessions
python cc_sessions/install.py
```

**JavaScript Development**:
```bash
cd /path/to/cc-sessions
node cc_sessions/install.js
```

The installer detects `CLAUDE_PROJECT_DIR` environment variable and creates symlinked hooks that point back to your repository for live development.

**Building Packages**:

Python:
```bash
python -m build
# Creates dist/cc_sessions-*.tar.gz and dist/cc_sessions-*.whl
```

JavaScript:
```bash
./install.sh # From repository root
npm pack
# Creates cc-sessions-*.tgz
```

**Testing Local Changes**:
After modifying hook or API files:
1. Changes are immediately reflected (symlinked development)
2. Start a new Claude Code session to test hook behavior
3. Use `/sessions` commands to test API changes

Both installers:
- Detect existing installations and create timestamped backups
- Preserve task files and agent customizations automatically
Expand Down Expand Up @@ -168,6 +199,32 @@ sessions tasks start @<task-name> # Start task with validation
- `/sessions` - Unified interface with subsystem routing
- All commands support `--from-slash` flag for contextual output

### Disabling Sessions
The entire sessions framework can be toggled on/off using the `sessions_enabled` feature flag:

```bash
sessions config features toggle sessions_enabled # Toggle on/off
sessions config features set sessions_enabled false # Explicitly disable
sessions config features set sessions_enabled true # Explicitly enable
```

**When `sessions_enabled` is false**:
- All hooks exit immediately without enforcement
- DAIC mode system is inactive (Edit/Write/MultiEdit tools are not blocked)
- Trigger phrase detection is disabled
- Task management workflows are inactive
- Protocol loading is disabled
- Sessions API commands remain available for re-enabling

**Use cases for disabling**:
- Testing code changes without DAIC constraints
- Quick exploratory work outside task workflows
- Emergency hotfixes that need immediate implementation
- Pairing with teammates unfamiliar with DAIC methodology
- Alternative VCS workflows (Jujutsu, Mercurial) without sessions structure

The statusline displays the disabled state when `sessions_enabled` is false. Re-enable anytime with the same toggle command.

## Key Patterns

### Hook Architecture
Expand Down Expand Up @@ -225,9 +282,8 @@ sessions tasks start @<task-name> # Start task with validation
- **Subagent Protection**: DAIC reminders suppressed, state editing blocked

### Update Detection System
- **Dual-Language Publishing**: Atomic workflow for PyPI and npm
- **Pre-Flight Validation**: 7 automated checks via prepare-release.py
- **Version Sync**: check-version-sync.sh ensures consistency
- **Dual-Language Publishing**: Simultaneous releases to PyPI and npm
- **Version Sync**: pyproject.toml and package.json version fields must match
- **Update Detection**: Flag-based caching in STATE.metadata
- **Agent-Directive Notifications**: Prompts Claude to stop and ask user before any installation
- **Update Commands**: suppress, check, status operations for user control
Expand Down Expand Up @@ -260,6 +316,29 @@ sessions tasks start @<task-name> # Start task with validation
- **Native Scripts**: daic.cmd and daic.ps1 for shell compatibility
- **Path Handling**: Windows-style paths with %CLAUDE_PROJECT_DIR%

## Testing

### Current Status
No automated test suite yet (package.json shows `"test": "echo \"No test specified yet\""`)

### Manual Testing Workflow
1. **Install in Development Mode**: Set up symlinked installation in a test repository
2. **Test DAIC Enforcement**: Attempt writes in discussion mode to verify blocking
3. **Test Trigger Phrases**: Use various trigger phrases to validate mode transitions
4. **Test Task Workflows**:
- Create tasks with different priorities and types
- Start tasks and verify context loading
- Complete tasks and verify commit/merge behavior
5. **Test Sessions API**: Run all `/sessions` commands to verify functionality
6. **Test Feature Parity**: Verify identical behavior between Python and JavaScript implementations
7. **Test Platform Compatibility**: Validate on Linux, macOS, and Windows

### Testing Considerations
- Hook behavior requires full Claude Code session context
- State persistence should be verified across session restarts
- Branch enforcement requires git repository with commits
- Protocol template rendering should be validated with different config options

## Known Issues

### Claude Code Version Compatibility
Expand Down Expand Up @@ -308,12 +387,12 @@ sessions tasks start @<task-name> # Start task with validation

## Related Documentation

- **RELEASE.md** - Maintainer guide for version releases
- **README.md** - User-facing feature overview
- **docs/INSTALL.md** - Detailed installation instructions
- **docs/USAGE_GUIDE.md** - Workflow and feature documentation
- **README.md** - User-facing feature overview and installation guide
- **CHANGELOG.md** - Version history and release notes
- **CLAUDE.md** - This file (architecture and development guide)
- **cc_sessions/knowledge/** - Internal architecture details
- **sessions/protocols/** - Installed protocol specifications
- **cc_sessions/protocols/** - Protocol template sources
- **sessions/protocols/** - Installed protocol specifications (after installation)

## Integration Points

Expand Down
10 changes: 7 additions & 3 deletions cc_sessions/javascript/api/config_commands.js
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ function formatConfigHuman(config) {
` Branch Enforcement: ${config.features.branch_enforcement}`,
` Task Detection: ${config.features.task_detection}`,
` Auto Ultrathink: ${config.features.auto_ultrathink}`,
` Sessions Enabled: ${config.features.sessions_enabled}`,
` Icon Style: ${getValue(config.features.icon_style)}`,
` Context Warnings (85%): ${config.features.context_warnings.warn_85}`,
` Context Warnings (90%): ${config.features.context_warnings.warn_90}`,
Expand Down Expand Up @@ -763,6 +764,7 @@ function handleFeaturesCommand(args, jsonOutput = false, fromSlash = false) {
branch_enforcement: features.branch_enforcement,
task_detection: features.task_detection,
auto_ultrathink: features.auto_ultrathink,
sessions_enabled: features.sessions_enabled,
icon_style: getValue(features.icon_style),
warn_85: features.context_warnings.warn_85,
warn_90: features.context_warnings.warn_90,
Expand All @@ -775,6 +777,7 @@ function handleFeaturesCommand(args, jsonOutput = false, fromSlash = false) {
` branch_enforcement: ${features.branch_enforcement}`,
` task_detection: ${features.task_detection}`,
` auto_ultrathink: ${features.auto_ultrathink}`,
` sessions_enabled: ${features.sessions_enabled}`,
` icon_style: ${getValue(features.icon_style)}`,
` warn_85: ${features.context_warnings.warn_85}`,
` warn_90: ${features.context_warnings.warn_90}`,
Expand All @@ -792,7 +795,7 @@ function handleFeaturesCommand(args, jsonOutput = false, fromSlash = false) {
let finalValue;

editConfig(config => {
if (['task_detection', 'auto_ultrathink', 'branch_enforcement'].includes(key)) {
if (['task_detection', 'auto_ultrathink', 'branch_enforcement', 'sessions_enabled'].includes(key)) {
// Boolean features
const boolValue = ['true', '1', 'yes', 'on'].includes(value.toLowerCase());
config.features[key] = boolValue;
Expand Down Expand Up @@ -834,7 +837,7 @@ function handleFeaturesCommand(args, jsonOutput = false, fromSlash = false) {
// Get current value
const config = loadConfig();
let currentValue;
if (['task_detection', 'auto_ultrathink', 'branch_enforcement'].includes(key)) {
if (['task_detection', 'auto_ultrathink', 'branch_enforcement', 'sessions_enabled'].includes(key)) {
currentValue = config.features[key];
} else if (key === 'icon_style') {
currentValue = config.features.icon_style;
Expand All @@ -858,7 +861,7 @@ function handleFeaturesCommand(args, jsonOutput = false, fromSlash = false) {

// Save the toggled value
editConfig(config => {
if (['task_detection', 'auto_ultrathink', 'branch_enforcement'].includes(key)) {
if (['task_detection', 'auto_ultrathink', 'branch_enforcement', 'sessions_enabled'].includes(key)) {
config.features[key] = newValue;
} else if (key === 'icon_style') {
config.features.icon_style = newValue;
Expand Down Expand Up @@ -897,6 +900,7 @@ function formatFeaturesHelp() {
" branch_enforcement - Git branch validation (default: true)",
" task_detection - Task-based workflow automation (default: true)",
" auto_ultrathink - Enhanced AI reasoning (default: true)",
" sessions_enabled - Enable/disable all cc-sessions hooks except statusline (default: true)",
" icon_style - Statusline icon style: nerd_fonts, emoji, or ascii (default: nerd_fonts)",
" warn_85 - Context warning at 85% (default: true)",
" warn_90 - Context warning at 90% (default: true)",
Expand Down
8 changes: 7 additions & 1 deletion cc_sessions/javascript/hooks/kickstart_session_start.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ const path = require('path');
/// ===== LOCAL ===== ///
// Import from shared_state (same pattern as normal hooks). Runtime file lives in sessions/hooks
const PROJECT_ROOT = path.resolve(__dirname, '..', '..');
const { loadState, editState } = require('./shared_state.js');
const { loadState, editState, loadConfig } = require('./shared_state.js');
///-///

//-//
Expand Down Expand Up @@ -88,6 +88,12 @@ Handles onboarding flow for users who chose kickstart in installer:

//!> 1. Load state and check kickstart metadata
const STATE = loadState();
const CONFIG = loadConfig();

// Early exit if sessions are disabled
if (!CONFIG.features.sessions_enabled) {
process.exit(0);
}

// Get kickstart metadata (should ALWAYS exist if this hook is running)
const kickstartMeta = STATE.metadata?.kickstart;
Expand Down
9 changes: 8 additions & 1 deletion cc_sessions/javascript/hooks/post_tool_use.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ const {
SessionsProtocol,
listOpenTasks,
TaskState,
StateError
StateError,
loadConfig
} = require('./shared_state.js');
///-///

Expand Down Expand Up @@ -62,6 +63,12 @@ const cwd = inputData.cwd || "";
let mod = false;

const STATE = loadState();
const CONFIG = loadConfig();

// Early exit if sessions are disabled
if (!CONFIG.features.sessions_enabled) {
process.exit(0);
}
//-//

/*
Expand Down
6 changes: 6 additions & 0 deletions cc_sessions/javascript/hooks/session_start.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,12 @@ const { editState, PROJECT_ROOT, loadConfig, SessionsProtocol, getTaskFilePath,
const sessionsDir = path.join(PROJECT_ROOT, 'sessions');
let STATE = null;
const CONFIG = loadConfig();

// Early exit if sessions are disabled
if (!CONFIG.features.sessions_enabled) {
process.exit(0);
}

const developerName = CONFIG.environment?.developer_name || 'developer';

// Initialize context
Expand Down
5 changes: 5 additions & 0 deletions cc_sessions/javascript/hooks/sessions_enforce.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,11 @@ if (filePathString) {
const STATE = loadState();
const CONFIG = loadConfig();

// Early exit if sessions are disabled
if (!CONFIG.features.sessions_enabled) {
process.exit(0);
}

let command = "";
let incomingTodos = [];
if (toolName === "Bash") {
Expand Down
2 changes: 2 additions & 0 deletions cc_sessions/javascript/hooks/shared_state.js
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,7 @@ class EnabledFeatures {
this.branch_enforcement = data.branch_enforcement !== undefined ? data.branch_enforcement : true;
this.task_detection = data.task_detection !== undefined ? data.task_detection : true;
this.auto_ultrathink = data.auto_ultrathink !== undefined ? data.auto_ultrathink : true;
this.sessions_enabled = data.sessions_enabled !== undefined ? data.sessions_enabled : true;

// Handle migration from old use_nerd_fonts boolean to new icon_style enum
let iconStyleValue = data.icon_style;
Expand Down Expand Up @@ -373,6 +374,7 @@ class SessionsConfig {
branch_enforcement: this.features.branch_enforcement,
task_detection: this.features.task_detection,
auto_ultrathink: this.features.auto_ultrathink,
sessions_enabled: this.features.sessions_enabled,
icon_style: this.features.icon_style,
context_warnings: {
warn_85: this.features.context_warnings.warn_85,
Expand Down
8 changes: 7 additions & 1 deletion cc_sessions/javascript/hooks/subagent_hooks.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ const path = require('path');
///-///

/// ===== LOCAL ===== ///
const { editState, PROJECT_ROOT, loadState } = require('./shared_state.js');
const { editState, PROJECT_ROOT, loadState, loadConfig } = require('./shared_state.js');
///-///

//-//
Expand Down Expand Up @@ -133,6 +133,12 @@ try {
process.exit(1);
}

// Load config and check if sessions are enabled
const CONFIG = loadConfig();
if (!CONFIG.features.sessions_enabled) {
process.exit(0);
}

// Check if this is a Task tool call
const toolName = inputData.tool_name || "";
if (toolName !== "Task") {
Expand Down
5 changes: 5 additions & 0 deletions cc_sessions/javascript/hooks/user_messages.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,11 @@ const transcriptPath = inputData.transcript_path || "";
const STATE = loadState();
const CONFIG = loadConfig();

// Early exit if sessions are disabled
if (!CONFIG.features.sessions_enabled) {
process.exit(0);
}

// Check if this is a slash command we handle via API
const promptStripped = prompt.trim();
const apiCommands = ['/mode', '/state', '/config', '/add-trigger', '/remove-trigger'];
Expand Down
21 changes: 21 additions & 0 deletions cc_sessions/javascript/statusline.js
Original file line number Diff line number Diff line change
Expand Up @@ -370,6 +370,27 @@ function main() {
}
}

// Sessions disabled check - show simplified statusline and exit
if (!config.features.sessions_enabled) {
// Line 1 - Progress bar | Disabled message
const contextPart = progressBar || `${gray}No context usage data${reset}`;
const disabledMsg = `${gray}cc-sessions disabled${reset}`;
console.log(contextPart + ' | ' + disabledMsg);

// Line 2 - Git branch only (if available)
if (gitBranchInfo) {
const line2Parts = [gitBranchInfo];
if (upstreamInfo) {
line2Parts.push(upstreamInfo);
}
console.log(line2Parts.join(' | '));
} else {
console.log('');
}

process.exit(0);
}

// Current task
const currTask = state?.current_task?.name || null;

Expand Down
Loading