Skip to content
Merged
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
2 changes: 1 addition & 1 deletion TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ directory before launching the interactive session bypasses the dialog.
- [x] T004: Fix pretrust v2 — trust is stored in `~/.claude.json` projects[path].hasTrustDialogAccepted, write it directly (PR #17 merged)
- [x] T005: Verified — pretrust-test tab launched WITHOUT trust dialog. Feature works end-to-end.
- [x] T006: Parent trust walk — check parent dirs before writing per-project entries, update CLAUDE.md docs (70 tests)
- [ ] T007: Add `--stop` flag to new_session.py — kills current tab without launching a new one
- [x] T007: Add `--stop` flag to new_session.py — kills current tab without launching a new one (PR #19)

## Rename: context-reset → new-session (007)

Expand Down
49 changes: 35 additions & 14 deletions new_session.py
Original file line number Diff line number Diff line change
Expand Up @@ -1047,6 +1047,8 @@ def main():
help="Auto-close terminal tab (Windows: sets WT closeOnExit=always temporarily)")
parser.add_argument("--timeout", type=int, default=45, help="Phase 2 verification timeout in seconds")
parser.add_argument("--dry-run", action="store_true")
parser.add_argument("--stop", action="store_true",
help="Kill current tab without launching a new one (self-close)")
args = parser.parse_args()

# Exclusive OS-level file lock: prevents concurrent resets on the same project.
Expand Down Expand Up @@ -1108,9 +1110,41 @@ def main():
return
log(f"Cross-project reset: saving state in {project_dir}, launching in {launch_dir}")

prompt = args.prompt or build_prompt(launch_dir)
launch_name = os.path.basename(launch_dir)

def _remove_lock():
nonlocal _lock_fh
try:
if _lock_fh:
_lock_fh.close() # Closing file handle releases OS lock
_lock_fh = None
if os.path.exists(lock_file):
os.remove(lock_file)
except Exception:
pass

if args.stop:
log(f"=== Stop mode: closing current tab for {launch_name} ===")
# Save session state before dying
context = extract_session_context(project_dir)
if context:
state_path = os.path.join(project_dir, "SESSION_STATE.md")
with open(state_path, "w", encoding="utf-8") as f:
f.write(f"# Session State (auto-generated by context-reset)\n\n")
f.write(f"Generated: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n\n")
f.write(f"## Last Session Conversation\n\n{context}\n")
log(f"Saved session state to {state_path}")
shell_pid = find_shell_pid()
if shell_pid:
log(f"Killing current tab (shell PID {shell_pid})")
kill_old_tab(shell_pid, close_tab=args.close_tab)
else:
log("WARNING: could not find shell PID to kill")
_remove_lock()
return

# Build launch command (needed for dry-run and normal mode)
prompt = args.prompt or build_prompt(launch_dir)
log(f"=== Context reset started for {launch_name} ===")
log(f"Project dir (state): {project_dir}")
if launch_dir != project_dir:
Expand All @@ -1119,24 +1153,11 @@ def main():
log(f"Prompt: {prompt[:80]}...")
log(f"Close old tab: {not args.no_close}")

# Tab title: always the folder name (short, stable)
tab_title = launch_name
tab_color = get_tab_color(launch_dir)
log(f"Tab: title='{tab_title}', color={tab_color}")

cmd = build_launch_cmd(launch_dir, prompt, tab_title, tab_color)

def _remove_lock():
nonlocal _lock_fh
try:
if _lock_fh:
_lock_fh.close() # Closing file handle releases OS lock
_lock_fh = None
if os.path.exists(lock_file):
os.remove(lock_file)
except Exception:
pass

if args.dry_run:
log(f"DRY RUN - command: {cmd}")
shell_pid = find_shell_pid()
Expand Down
35 changes: 31 additions & 4 deletions scripts/test/test-T007-stop-flag.sh
Original file line number Diff line number Diff line change
@@ -1,6 +1,33 @@
#!/usr/bin/env bash
# Test: --stop flag kills current tab without launching new one
# Placeholder — will be implemented with T007
# Test: --stop flag argument parsing and stop-mode logic
set -e
echo "test-T007-stop-flag: SKIPPED (not yet implemented)"
exit 0

cd "$(dirname "$0")/../.."

python3 -c "
import sys, os, argparse
sys.path.insert(0, '.')
import new_session

# Test 1: argparse accepts --stop
parser = argparse.ArgumentParser()
parser.add_argument('--stop', action='store_true')
parser.add_argument('--project-dir', default='.')
args = parser.parse_args(['--stop', '--project-dir', '.'])
assert args.stop is True, '--stop flag not parsed'
print('PASS: --stop flag accepted by argparse')

# Test 2: --stop is in new_session.main's parser (check source)
import inspect
src = inspect.getsource(new_session.main)
assert \"'--stop'\" in src or '\"--stop\"' in src, '--stop not in main parser'
print('PASS: --stop flag defined in main()')

# Test 3: stop mode saves SESSION_STATE.md (check the code path exists)
assert 'args.stop' in src, 'args.stop not referenced in main'
assert 'Stop mode' in src, 'Stop mode log message not in main'
assert 'extract_session_context' in src, 'extract_session_context not called in stop mode'
print('PASS: stop mode code path exists with state saving')
"

echo "test-T007-stop-flag: ALL PASSED"
Loading