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
39 changes: 27 additions & 12 deletions cecli/commands/add.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,15 @@
from cecli.utils import is_image_file, run_fzf


def _is_chat_attachment_staging_path(rel_norm: str) -> bool:
"""
Host UIs (e.g. desktop IDEs) stage chat uploads under ``.<app>/attachments/``.
Do not offer to create missing paths there via ``/add`` — re-attach in the UI instead.
"""
parts = rel_norm.replace("\\", "/").strip("/").split("/")
return len(parts) >= 2 and parts[0].startswith(".") and parts[1] == "attachments"


class AddCommand(BaseCommand):
NORM_NAME = "add"
DESCRIPTION = "Add files to the chat so cecli can edit them or review them in detail"
Expand Down Expand Up @@ -71,19 +80,25 @@ async def execute(cls, io, coder, args, **kwargs):
if len(confirm_fname) > 64:
confirm_fname = f".../{os.path.basename(confirm_fname)}"

# Check if the path matches any exempt-path regex patterns
try:
rel_norm = os.path.relpath(fname, coder.root).replace("\\", "/")
except ValueError:
rel_norm = str(fname).replace("\\", "/")

exempt_paths = getattr(coder.args, "exempt_paths", None) or []
if exempt_paths:
try:
rel_norm = os.path.relpath(fname, coder.root).replace("\\", "/")
except ValueError:
rel_norm = str(fname).replace("\\", "/")
if any(re.search(p, rel_norm) for p in exempt_paths):
io.tool_error(
f"Path '{confirm_fname}' matches an exempt-path pattern. "
"Skipping file creation."
)
continue
if exempt_paths and any(re.search(p, rel_norm) for p in exempt_paths):
io.tool_error(
f"Path '{confirm_fname}' matches an exempt-path pattern. "
"Skipping file creation."
)
continue

if _is_chat_attachment_staging_path(rel_norm):
io.tool_error(
f"Attachment not found: {confirm_fname}. "
"Re-attach the file in chat instead of using /add on a staging path."
)
continue

if await io.confirm_ask(
f"No files matched '{confirm_fname}'. Do you want to create this file?"
Expand Down
12 changes: 10 additions & 2 deletions cecli/repomap.py
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,12 @@ def get_repo_map(
"has_chat_files": bool(chat_files),
}

def _resolve_abs_fname(self, fname: str) -> str:
"""Normalize repo file paths for existence checks and tag parsing."""
if os.path.isabs(fname):
return os.path.normpath(fname)
return os.path.normpath(os.path.join(self.root, fname))

def get_rel_fname(self, fname):
try:
return os.path.relpath(fname, self.root)
Expand Down Expand Up @@ -757,8 +763,9 @@ def get_ranked_tags(
else:
self.io.update_spinner(f"{UPDATING_REPO_MAP_MESSAGE}: {fname}")

abs_fname = self._resolve_abs_fname(fname)
try:
file_ok = os.path.isfile(fname)
file_ok = os.path.isfile(abs_fname)
except OSError:
file_ok = False

Expand All @@ -768,11 +775,12 @@ def get_ranked_tags(
self.warned_files.add(fname)
if skipped_missing <= 2:
self.io.tool_warning(
f"Repo-map skipping missing file: {fname}"
f"Repo-map skipping missing file: {abs_fname}"
" (removed on disk or not yet written)."
)
continue

fname = abs_fname
# dump(fname)
rel_fname = self.get_rel_fname(fname)
current_pers = 0.0 # Start with 0 personalization score
Expand Down
4 changes: 2 additions & 2 deletions cecli/tools/command.py
Original file line number Diff line number Diff line change
Expand Up @@ -287,8 +287,8 @@ async def _execute_with_timeout(cls, coder, command_string, timeout, use_pty=Fal
# Remove from background tracking since it's done
BackgroundCommandManager.stop_background_command(command_key)

# Output to TUI console if TUI exists (same logic as _execute_foreground)
if coder.tui and coder.tui():
# Emit output to frontend (tool card collapsible output)
if output_content.strip():
coder.io.tool_output(output_content, type="tool-result")

if exit_code == 0:
Expand Down
2 changes: 2 additions & 0 deletions cecli/tools/ls.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,8 @@ def execute(cls, coder, path=None, **kwargs):
f"📋 Listed {len(contents)} file(s) in '{dir_path}'", type="tool-result"
)
sorted_contents = sorted(contents)
# Emit full listing for frontend tool card (collapsible output)
coder.io.tool_output("\n".join(sorted_contents))
if len(sorted_contents) > 10:
return (
f"Found {len(sorted_contents)} files: {', '.join(sorted_contents[:10])}..."
Expand Down
Loading
Loading