Skip to content
This repository was archived by the owner on Apr 22, 2026. It is now read-only.

Commit f19b389

Browse files
authored
feat(workspace): simplify navigation after workspace creation (#30)
Combine cd and venv activation into single command to reduce copy/paste operations. Users now see one command instead of 2-3 separate steps. Changes: - Combine 'cd' and 'source .venv/bin/activate' with && operator - Remove 'workspace remove' from next steps (confusing after creation) - Add shlex.quote() for shell-safe path handling - Update tests to verify new behavior Closes agentspaces-yjh
1 parent d4dc410 commit f19b389

4 files changed

Lines changed: 34 additions & 13 deletions

File tree

.beads/issues.jsonl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,4 @@
1717
{"id":"agentspaces-u7i","title":"Add E2E tests for project create command","description":"## Context\nThe agentspaces CLI has a `project create` command (src/agentspaces/cli/project.py) that initializes new projects with templates. It:\n- Works in current directory (requires empty or git-only directory)\n- Runs `git init` if not already a git repo\n- Creates skeleton files (CLAUDE.md, README.md, docs/, .claude/)\n- Optionally creates Python pack files with `--python` flag\n\n## Goal\nAdd comprehensive E2E/integration tests that exercise the full command flow with real git operations in isolated temporary directories.\n\n## Deliverables\n1. pytest marker configuration in pyproject.toml\n2. Integration test fixtures in tests/integration/cli/conftest.py\n3. Test implementation in tests/integration/cli/test_project_create.py (~22 tests)\n\n## Test Categories\n- Basic creation (empty dir, existing git, --python flag)\n- Python package naming (edge cases for name derivation)\n- Error handling (non-empty dir, confirmation prompt)\n- Content validation (project info in generated files)\n- Git integration (valid repo, untracked files)\n- Edge cases (special chars, unicode, nested dirs)\n\n## Safety Requirements\n- All tests use temporary directories (auto-cleanup)\n- Tests marked with @pytest.mark.integration for selective runs\n- No modifications to host environment or ~/.agentspaces","status":"closed","priority":2,"issue_type":"feature","created_at":"2026-01-09T07:56:26.870453-05:00","created_by":"ckrough","updated_at":"2026-01-09T09:58:34.334635-05:00","closed_at":"2026-01-09T09:58:34.334635-05:00","close_reason":"All subtasks completed: pytest markers, fixtures, 23 tests, CLAUDE.md updated"}
1818
{"id":"agentspaces-um3","title":"Phase 2: Extend Project CLAUDE.md with Beads section","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-08T14:52:51.261354-05:00","created_by":"ckrough","updated_at":"2026-01-08T14:54:02.518158-05:00","closed_at":"2026-01-08T14:54:02.518158-05:00","close_reason":"Added Beads workflow and Learned Patterns sections to project CLAUDE.md"}
1919
{"id":"agentspaces-w0j","title":"Phase 8: Add PostToolUse auto-format hook","status":"closed","priority":2,"issue_type":"feature","created_at":"2026-01-08T14:52:52.768009-05:00","created_by":"ckrough","updated_at":"2026-01-08T15:30:39.116749-05:00","closed_at":"2026-01-08T15:30:39.116749-05:00","close_reason":"Created PostToolUse auto-format hook for Python files"}
20+
{"id":"agentspaces-yjh","title":"Simplify workspace navigation and venv activation after creation","description":"## Context\nAfter running 'agentspaces workspace create', users see a 'Next Steps' section with commands to cd and activate venv. This requires copying/pasting long paths.\n\n## Goal\nProvide a simpler way for users to navigate to workspace and activate venv without manual copying/pasting.\n\n## Current Behavior\nUsers must copy/paste:\n1. cd /Users/ckrough/.agentspaces/agentspaces/sleepy-fermat\n2. source .venv/bin/activate\n\n## Desired Behavior\nSingle command or automated navigation that minimizes user effort.\n\n## Files Involved\n- src/agentspaces/cli/workspace.py - workspace create command\n- src/agentspaces/modules/workspace/service.py - workspace creation logic\n\n## Verification\n1. Create a new workspace\n2. Verify simplified navigation/activation works\n3. uv run pytest tests/ -k workspace","status":"closed","priority":2,"issue_type":"feature","created_at":"2026-01-09T14:52:35.442563-05:00","created_by":"ckrough","updated_at":"2026-01-09T15:21:26.303403-05:00","closed_at":"2026-01-09T15:21:26.303403-05:00","close_reason":"Implemented: Combined cd and venv activation into single command, removed workspace remove step, added path quoting for safety"}

src/agentspaces/cli/formatters.py

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
from __future__ import annotations
44

5+
import shlex
56
from datetime import UTC, datetime
67
from typing import TYPE_CHECKING
78

@@ -97,18 +98,21 @@ def print_workspace_created(
9798
console.print(panel)
9899

99100

100-
def print_next_steps(workspace_name: str, workspace_path: str, has_venv: bool) -> None:
101+
def print_next_steps(workspace_path: str, has_venv: bool) -> None:
101102
"""Print actionable next steps after workspace creation.
102103
103104
Args:
104-
workspace_name: Name of the created workspace.
105105
workspace_path: Path to the workspace directory.
106106
has_venv: Whether a virtual environment was created.
107107
"""
108-
steps = [f"cd {workspace_path}"]
108+
# Quote path for shell safety
109+
quoted_path = shlex.quote(workspace_path)
110+
111+
# Combine cd and venv activation into single command
109112
if has_venv:
110-
steps.append("source .venv/bin/activate")
111-
steps.append(f"agentspaces workspace remove {workspace_name}")
113+
steps = [f"cd {quoted_path} && source .venv/bin/activate"]
114+
else:
115+
steps = [f"cd {quoted_path}"]
112116

113117
lines = [f" {i + 1}. [cyan]{step}[/cyan]" for i, step in enumerate(steps)]
114118
panel = Panel(

src/agentspaces/cli/workspace.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,6 @@ def create(
110110
)
111111

112112
print_next_steps(
113-
workspace_name=workspace.name,
114113
workspace_path=str(workspace.path),
115114
has_venv=workspace.has_venv,
116115
)

tests/unit/cli/test_formatters.py

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ class TestPrintNextSteps:
5757
def test_prints_cd_step(self) -> None:
5858
"""Should include cd to workspace path."""
5959
with patch("agentspaces.cli.formatters.console") as mock_console:
60-
print_next_steps("test-ws", "/path/to/workspace", has_venv=False)
60+
print_next_steps("/path/to/workspace", has_venv=False)
6161
mock_console.print.assert_called()
6262
# Get all printed content - find the Panel with "Next Steps"
6363
panel = _find_next_steps_panel(mock_console)
@@ -67,23 +67,40 @@ def test_prints_cd_step(self) -> None:
6767
def test_includes_venv_activation_when_has_venv(self) -> None:
6868
"""Should include venv activation when has_venv is True."""
6969
with patch("agentspaces.cli.formatters.console") as mock_console:
70-
print_next_steps("test-ws", "/path/to/workspace", has_venv=True)
70+
print_next_steps("/path/to/workspace", has_venv=True)
7171
panel = _find_next_steps_panel(mock_console)
7272
assert "source .venv/bin/activate" in panel.renderable
7373

7474
def test_excludes_venv_activation_when_no_venv(self) -> None:
7575
"""Should not include venv activation when has_venv is False."""
7676
with patch("agentspaces.cli.formatters.console") as mock_console:
77-
print_next_steps("test-ws", "/path/to/workspace", has_venv=False)
77+
print_next_steps("/path/to/workspace", has_venv=False)
7878
panel = _find_next_steps_panel(mock_console)
7979
assert "source .venv/bin/activate" not in panel.renderable
8080

81-
def test_includes_remove_step(self) -> None:
82-
"""Should include workspace remove step with workspace name."""
81+
def test_combines_cd_and_activation(self) -> None:
82+
"""Should combine cd and activation into single command with &&."""
8383
with patch("agentspaces.cli.formatters.console") as mock_console:
84-
print_next_steps("test-ws", "/path/to/workspace", has_venv=False)
84+
print_next_steps("/path/to/workspace", has_venv=True)
8585
panel = _find_next_steps_panel(mock_console)
86-
assert "agentspaces workspace remove test-ws" in panel.renderable
86+
assert "cd" in panel.renderable
87+
assert "&&" in panel.renderable
88+
assert "source .venv/bin/activate" in panel.renderable
89+
90+
def test_does_not_include_remove_command(self) -> None:
91+
"""Should not include workspace remove command."""
92+
with patch("agentspaces.cli.formatters.console") as mock_console:
93+
print_next_steps("/path/to/workspace", has_venv=False)
94+
panel = _find_next_steps_panel(mock_console)
95+
assert "remove" not in panel.renderable
96+
97+
def test_quotes_path_with_spaces(self) -> None:
98+
"""Should properly quote paths containing spaces."""
99+
with patch("agentspaces.cli.formatters.console") as mock_console:
100+
print_next_steps("/path/with spaces/workspace", has_venv=False)
101+
panel = _find_next_steps_panel(mock_console)
102+
# shlex.quote adds single quotes around paths with spaces
103+
assert "'/path/with spaces/workspace'" in panel.renderable
87104

88105

89106
class TestPrintDidYouMean:

0 commit comments

Comments
 (0)