Skip to content

feat: Add CLI for local Potpie usage#700

Open
sofia-willow wants to merge 3 commits intopotpie-ai:mainfrom
sofia-willow:feat/cli-tool
Open

feat: Add CLI for local Potpie usage#700
sofia-willow wants to merge 3 commits intopotpie-ai:mainfrom
sofia-willow:feat/cli-tool

Conversation

@sofia-willow
Copy link
Copy Markdown

@sofia-willow sofia-willow commented Mar 18, 2026

Summary

Implements the CLI tool requested in #224.

Commands

Command Description
potpie start Start Docker services + app server
potpie stop Stop all Potpie services
potpie parse <path> [--branch <name>] Submit repo for parsing, poll until complete
potpie chat <project-id> --agent <name> Interactive console chat with streaming
potpie status <project-id> Check parsing status
potpie projects List all parsed projects
potpie agents List available agents

Architecture

potpie/cli/
├── __init__.py
├── client.py    # HTTP client wrapping the REST API
└── main.py      # CLI commands with argparse + Rich output

Design Decisions

  • Thin HTTP client — wraps existing REST API endpoints, no direct DB access
  • Rich console output — color-coded tables, spinners, streaming responses
  • Polling with progressparse command shows dots while waiting
  • Streaming chat — responses stream in real-time via httpx streaming
  • Entry point — added [project.scripts] potpie = potpie.cli.main:main to pyproject.toml
  • No new heavy dependencies — only uses httpx (already a dep) + rich

Tests

  • tests/cli/test_client.py — client unit tests (health, polling, timeout)
  • tests/cli/test_main.py — argument parsing and command dispatch tests

Usage

# Start the server
potpie start

# Parse a repository
potpie parse /path/to/my-repo --branch main

# Check status
potpie status <project-id>

# Start chatting
potpie chat <project-id> --agent codebase_qna_agent

# List projects and agents
potpie projects
potpie agents

Closes #224

Summary by CodeRabbit

  • New Features

    • Added a user-facing CLI (potpie) to start/stop the local server, submit and monitor repo parsing, list projects and agents, view parsing status, and run interactive chat sessions with streamed assistant output; includes rich terminal formatting and a packaged CLI entry point.
  • Tests

    • Added unit tests covering CLI argument parsing, command dispatch, and HTTP client behaviors (health, polling, auth headers).

Implements the CLI requested in issue potpie-ai#224 with all required commands:

Commands:
- potpie start     — Start Docker services + app server
- potpie stop      — Stop all Potpie services
- potpie parse     — Submit repo for parsing, poll until complete
- potpie chat      — Interactive console chat with streaming responses
- potpie status    — Check parsing status for a project
- potpie projects  — List all parsed projects
- potpie agents    — List available agents

Features:
- HTTP client wrapping the existing REST API (no new dependencies beyond httpx + rich)
- Polling with progress dots for parse status
- Streaming response display for chat
- Clean error handling and input validation
- Color-coded Rich console output
- Configurable server URL via --url flag or POTPIE_URL env var
- pyproject.toml entry point: potpie = potpie.cli.main:main

Tests:
- Client unit tests (health, polling, timeout)
- Parser/dispatch tests for all commands

Closes potpie-ai#224
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Mar 18, 2026

Walkthrough

Adds a new potpie CLI package: a synchronous httpx-based PotpieClient, a CLI entrypoint with start/stop/parse/status/projects/agents/chat commands, pyproject script + rich dependency, and unit tests for the client and CLI parsing/dispatch.

Changes

Cohort / File(s) Summary
Package Init
potpie/cli/__init__.py
New package module with module docstring.
HTTP Client
potpie/cli/client.py
New PotpieClient (httpx.Client wrapper) with defaults, auth header handling, health/is_alive, parse, get_parsing_status, poll_parsing, list_projects, list_agents, create_conversation, send_message (streaming), ensure_auth, and close().
CLI Entrypoint
potpie/cli/main.py
New CLI: project-root discovery, client construction, argparse subcommands and handlers for start, stop, parse, status, projects, agents, and chat; subprocess management, polling, and interactive chat loop.
Project Config
pyproject.toml
Added console script potpie = "potpie.cli.main:main" and dependency rich>=13.0.0.
Tests
tests/cli/__init__.py, tests/cli/test_client.py, tests/cli/test_main.py
New tests covering PotpieClient behavior (init, headers, health, polling) and CLI parser/dispatch for commands and options.

Sequence Diagrams

sequenceDiagram
    participant User
    participant CLI as CLI (main)
    participant Client as PotpieClient
    participant Server as Potpie Server

    User->>CLI: potpie parse <repo_path>
    CLI->>Client: is_alive()
    Client->>Server: GET /health
    Server-->>Client: 200 OK
    Client-->>CLI: True

    CLI->>Client: parse(repo_path)
    Client->>Server: POST /api/v1/parse
    Server-->>Client: {"project_id":"..."}
    Client-->>CLI: project data

    CLI->>Client: poll_parsing(project_id)
    loop Until terminal state
        Client->>Server: GET /api/v1/parsing-status/{project_id}
        Server-->>Client: {"status":"parsing"}
        Client->>Client: wait(interval)
    end

    Server-->>Client: {"status":"ready"}
    Client-->>CLI: {"status":"ready"}
    CLI->>User: Parse completed
Loading
sequenceDiagram
    participant User
    participant CLI as CLI (main)
    participant Client as PotpieClient
    participant Server as Potpie Server

    User->>CLI: potpie chat <project_id> --agent <agent>
    CLI->>Client: list_projects()
    Client->>Server: GET /api/v1/projects/list
    Server-->>Client: [projects...]
    Client-->>CLI: projects

    CLI->>Client: create_conversation(project_id, agent_id)
    Client->>Server: POST /api/v1/conversations/
    Server-->>Client: {"conversation_id":"..."}
    Client-->>CLI: conversation data

    User->>CLI: Enter message
    CLI->>Client: send_message(conversation_id, content)
    Client->>Server: POST /api/v1/conversations/{id}/message/ (stream)
    Server-->>Client: chunk1, chunk2...
    Client-->>CLI: Generator<chunks>
    CLI->>User: Display response chunks
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~70 minutes

Poem

🐇 I hopped through code with a curious cheer,
New client and CLI have scurried here,
Start, parse, chat — I twitch my nose and seat,
Projects and agents lined up to greet,
Potpie local magic — nibble, repeat!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 43.14% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: Add CLI for local Potpie usage' directly and clearly summarizes the main objective of the PR—implementing a command-line interface for local Potpie interaction.
Linked Issues check ✅ Passed The PR fully implements all coding objectives from issue #224: start/stop commands, parse with polling, chat with streaming, status/projects/agents commands, input validation, and proper error handling.
Out of Scope Changes check ✅ Passed All changes (CLI client, main module, tests, pyproject.toml entry point, Rich dependency) are directly related to the CLI implementation objective with no unrelated modifications.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
📝 Coding Plan
  • Generate coding plan for human review comments

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

🧹 Nitpick comments (3)
tests/cli/test_main.py (1)

13-98: Add parser/dispatch tests for timeout and chat branch semantics.

Given the new command surface, tests should explicitly cover parse timeout handling and (if intended by requirements) chat --branch parsing/dispatch behavior.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/cli/test_main.py` around lines 13 - 98, Add tests that cover timeout
parsing and chat branch semantics: extend tests in tests/cli/test_main.py to
assert build_parser() parses a --timeout value into args.timeout (and that
default timeout is used when omitted), and add a test that main([...]) with the
chat command and a --branch flag (e.g.,
["chat","project-123","--branch","develop"]) results in dispatch to cmd_chat
with the branch preserved; reference build_parser, main, args.timeout, and
cmd_chat/_get_client in the new tests to locate where to assert parsing and
dispatch behavior.
tests/cli/test_client.py (1)

12-64: Core HTTP contract paths are not covered by tests yet.

Please add targeted tests for parse, get_parsing_status, list_projects, list_agents, create_conversation, and send_message (including endpoint prefix, headers, and payload schema). This would catch regressions in the most failure-prone parts of the new client.

potpie/cli/main.py (1)

133-135: Add local validation for repo_path and branch before API submission.

This command currently accepts empty/invalid values without preflight checks, which misses the stated input-validation requirement.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@potpie/cli/main.py` around lines 133 - 135, Validate repo_path and branch
locally before the API call: ensure args.repo_path (repo_path) is non-empty,
exists and is a directory (os.path.exists/os.path.isdir) and that it contains a
git repo (e.g., check for .git or run a git command), and ensure args.branch
(branch) is non-empty and corresponds to an existing branch in that repo (e.g.,
git rev-parse --verify or list branches). If any check fails, print a clear
error and exit or raise an argparse error instead of proceeding to the API
submission so bad inputs are rejected locally.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@potpie/cli/client.py`:
- Around line 71-72: Several API calls in client.py are using
headers={"isDevelopmentMode": "enabled"} which fails authentication because
get_api_key_user expects x-api-key / x-user-id; update those calls (e.g., the
requests where headers={"isDevelopmentMode": "enabled"} at lines around 71-72,
87-88, 133-134, 146-147, 178-179, 201-202) to use the client's authentication
helper by replacing headers={"isDevelopmentMode": "enabled"} with
headers=self._auth_headers() so the required x-api-key and x-user-id are sent
(keep any other header keys if needed by merging with self._auth_headers()).
- Around line 66-67: The client is calling mounted endpoints under "/api/v1"
which no longer match the backend routes (they are under "/api/v2"); update
every request URL in potpie/cli/client.py that uses "/api/v1" (e.g. the JSON
POST to "/api/v1/parse" and the other occurrences at the noted locations) to use
the "/api/v2" base path instead so the requests match the server routes; search
for the string "/api/v1" in client.py and replace with "/api/v2" in the requests
(e.g., the requests.post calls constructing "/api/v1/parse", "/api/v1/..." etc.)
to ensure all endpoints target the v2 API.
- Around line 173-176: The conversation create payload is using the wrong key
"agent_id" which should be "agent_ids" (an array) to match the backend request
model; update the JSON payload where project_ids, agent_id, title are set so
that "agent_ids": [agent_id] is sent instead of "agent_id": agent_id, and verify
any code handling the response or constructing that dict (refer to the variables
project_id and agent_id and the conversation creation call) expects a list of
agent IDs.

In `@potpie/cli/main.py`:
- Around line 209-210: The status coloring compares the raw status string
case-sensitively; normalize the status before comparing by replacing the
assignment of state (from status.get("status", "unknown")) with a lower-cased
value (e.g., state = status.get("status", "unknown").lower()) and update the
style logic in the same place (the expression that sets style based on state) to
compare against lowercase literals ("ready", "completed", "parsed",
"processing"); repeat this normalization and comparison change for the second
occurrence of the same logic later in the file (the other style assignment
around the 250–251 area).
- Around line 413-416: The chat subparser (chat_cmd created for "chat") is
missing the --branch argument required by the CLI contract; update the parser
where chat_cmd is defined to add a branch option (e.g.,
chat_cmd.add_argument("--branch", help="Branch context for the chat",
default=... or required=...) so the chat command accepts branch context; ensure
the option name and help text match other commands that already use --branch to
maintain consistency with existing behavior.
- Around line 160-187: The polling loop in main.py never enforces a timeout so
the except TimeoutError branch is unreachable; update the loop that begins with
start_time = time.monotonic() / while True to compute elapsed each iteration and
compare it to a configurable timeout (e.g., PARSE_TIMEOUT_SECONDS or a CLI flag)
and when elapsed > timeout either raise TimeoutError or break/exit with the same
behavior as the current except block; make this change around the
get_parsing_status loop (references: start_time, while True,
client.get_parsing_status, TimeoutError) so a stuck parse will trigger the
timeout handling and show the timed-out message.

In `@pyproject.toml`:
- Around line 8-13: The TOML places the dependencies array under
[project.scripts], which breaks PEP 621; move the dependencies table so it is a
direct child of [project] (i.e., place the dependencies = [...] block before or
above the [project.scripts] section) and leave the potpie =
"potpie.cli.main:main" entry only inside [project.scripts]; ensure the final
structure has [project] with dependencies and then a separate [project.scripts]
containing the potpie entry.

---

Nitpick comments:
In `@potpie/cli/main.py`:
- Around line 133-135: Validate repo_path and branch locally before the API
call: ensure args.repo_path (repo_path) is non-empty, exists and is a directory
(os.path.exists/os.path.isdir) and that it contains a git repo (e.g., check for
.git or run a git command), and ensure args.branch (branch) is non-empty and
corresponds to an existing branch in that repo (e.g., git rev-parse --verify or
list branches). If any check fails, print a clear error and exit or raise an
argparse error instead of proceeding to the API submission so bad inputs are
rejected locally.

In `@tests/cli/test_main.py`:
- Around line 13-98: Add tests that cover timeout parsing and chat branch
semantics: extend tests in tests/cli/test_main.py to assert build_parser()
parses a --timeout value into args.timeout (and that default timeout is used
when omitted), and add a test that main([...]) with the chat command and a
--branch flag (e.g., ["chat","project-123","--branch","develop"]) results in
dispatch to cmd_chat with the branch preserved; reference build_parser, main,
args.timeout, and cmd_chat/_get_client in the new tests to locate where to
assert parsing and dispatch behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: dcd3540e-94ef-41c7-9a56-b7c13e5fce63

📥 Commits

Reviewing files that changed from the base of the PR and between 71b61bd and 23ce324.

📒 Files selected for processing (7)
  • potpie/cli/__init__.py
  • potpie/cli/client.py
  • potpie/cli/main.py
  • pyproject.toml
  • tests/cli/__init__.py
  • tests/cli/test_client.py
  • tests/cli/test_main.py

Comment thread potpie/cli/client.py Outdated
Comment on lines +66 to +67
"/api/v1/parse",
json={
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Client endpoints are versioned to /api/v1, but backend routes are /api/v2.

These calls will fail with 404 against the current router contract.

🔧 Proposed fix
 DEFAULT_BASE_URL = "http://localhost:8001"
 DEFAULT_TIMEOUT = 30.0
+API_PREFIX = "/api/v2"
@@
-            "/api/v1/parse",
+            f"{API_PREFIX}/parse",
@@
-            f"/api/v1/parsing-status/{project_id}",
+            f"{API_PREFIX}/parsing-status/{project_id}",
@@
-            "/api/v1/projects/list",
+            f"{API_PREFIX}/projects/list",
@@
-            "/api/v1/list-available-agents",
+            f"{API_PREFIX}/list-available-agents",
@@
-            "/api/v1/conversations/",
+            f"{API_PREFIX}/conversations/",
@@
-            f"/api/v1/conversations/{conversation_id}/message/",
+            f"{API_PREFIX}/conversations/{conversation_id}/message/",

Also applies to: 86-87, 132-133, 145-146, 172-173, 199-199

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@potpie/cli/client.py` around lines 66 - 67, The client is calling mounted
endpoints under "/api/v1" which no longer match the backend routes (they are
under "/api/v2"); update every request URL in potpie/cli/client.py that uses
"/api/v1" (e.g. the JSON POST to "/api/v1/parse" and the other occurrences at
the noted locations) to use the "/api/v2" base path instead so the requests
match the server routes; search for the string "/api/v1" in client.py and
replace with "/api/v2" in the requests (e.g., the requests.post calls
constructing "/api/v1/parse", "/api/v1/..." etc.) to ensure all endpoints target
the v2 API.

Comment thread potpie/cli/client.py Outdated
Comment thread potpie/cli/client.py Outdated
Comment thread potpie/cli/main.py Outdated
Comment thread potpie/cli/main.py Outdated
Comment on lines +209 to +210
state = status.get("status", "unknown")
style = "green" if state in ("ready", "completed", "parsed") else "yellow" if state == "processing" else "red"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Status coloring is case-sensitive and can mislabel valid success states.

Normalize status values before comparison so "READY"/"PARSED" render correctly.

🔧 Proposed fix
-    state = status.get("status", "unknown")
-    style = "green" if state in ("ready", "completed", "parsed") else "yellow" if state == "processing" else "red"
+    state = str(status.get("status", "unknown"))
+    normalized_state = state.lower()
+    style = "green" if normalized_state in ("ready", "completed", "parsed") else "yellow" if normalized_state == "processing" else "red"
@@
-        status = p.get("status", "?")
-        status_style = "green" if status in ("ready", "parsed") else "yellow"
+        status = str(p.get("status", "?"))
+        status_style = "green" if status.lower() in ("ready", "parsed") else "yellow"

Also applies to: 250-251

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@potpie/cli/main.py` around lines 209 - 210, The status coloring compares the
raw status string case-sensitively; normalize the status before comparing by
replacing the assignment of state (from status.get("status", "unknown")) with a
lower-cased value (e.g., state = status.get("status", "unknown").lower()) and
update the style logic in the same place (the expression that sets style based
on state) to compare against lowercase literals ("ready", "completed", "parsed",
"processing"); repeat this normalization and comparison change for the second
occurrence of the same logic later in the file (the other style assignment
around the 250–251 area).

Comment thread potpie/cli/main.py
Comment thread pyproject.toml
Comment on lines +8 to +13
[project.scripts]
potpie = "potpie.cli.main:main"

dependencies = [
"aiohttp>=3.13.3",
"rich>=13.0.0",
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

python - <<'PY'
import tomllib
from pathlib import Path

data = tomllib.loads(Path("pyproject.toml").read_text())
project = data.get("project", {})
scripts = project.get("scripts", {})

print("project.dependencies type:", type(project.get("dependencies")).__name__)
print("project.dependencies exists:", "dependencies" in project)
print("project.scripts keys:", sorted(scripts.keys()) if isinstance(scripts, dict) else scripts)
print("dependencies incorrectly under project.scripts:", isinstance(scripts, dict) and "dependencies" in scripts)
PY

Repository: potpie-ai/potpie

Length of output: 232


dependencies is incorrectly nested under [project.scripts].

This violates PEP 621 metadata structure. The dependencies list should be a direct child of [project], not [project.scripts]. Move the [project.scripts] section after the dependencies list to fix the nesting.

Proposed fix
 [project]
 name = "potpie"
 version = "0.1.0"
 description = "Add your description here"
 readme = "README.md"
 requires-python = ">=3.10"
-
-[project.scripts]
-potpie = "potpie.cli.main:main"
 
 dependencies = [
     "aiohttp>=3.13.3",
     "rich>=13.0.0",
     "greenlet>=3.3.0",
 ]
+
+[project.scripts]
+potpie = "potpie.cli.main:main"
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
[project.scripts]
potpie = "potpie.cli.main:main"
dependencies = [
"aiohttp>=3.13.3",
"rich>=13.0.0",
[project]
name = "potpie"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.10"
dependencies = [
"aiohttp>=3.13.3",
"rich>=13.0.0",
"greenlet>=3.3.0",
]
[project.scripts]
potpie = "potpie.cli.main:main"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pyproject.toml` around lines 8 - 13, The TOML places the dependencies array
under [project.scripts], which breaks PEP 621; move the dependencies table so it
is a direct child of [project] (i.e., place the dependencies = [...] block
before or above the [project.scripts] section) and leave the potpie =
"potpie.cli.main:main" entry only inside [project.scripts]; ensure the final
structure has [project] with dependencies and then a separate [project.scripts]
containing the potpie entry.

- Fix auth: use x-api-key/x-user-id headers instead of isDevelopmentMode
  (reads from POTPIE_API_KEY or INTERNAL_ADMIN_SECRET env vars)
- Fix conversation creation: use agent_ids (list) instead of agent_id
- Fix parse polling: add actual timeout (600s) to prevent infinite hang
- Fix status display: normalize status to lowercase before comparison
- Add --branch flag to chat command per issue spec
- Extract API_PREFIX constant for endpoint paths
- Update tests to cover new auth headers and API prefix

Addresses all critical and major findings from CodeRabbit review.
@sofia-willow
Copy link
Copy Markdown
Author

Thanks for the thorough review @coderabbitai! I've pushed fixes for all the findings:

  • Auth headers: Now uses x-api-key / x-user-id headers (reads from POTPIE_API_KEY or INTERNAL_ADMIN_SECRET env vars)
  • agent_ids: Fixed to use agent_ids list instead of agent_id
  • Parse timeout: Added actual 600s timeout to prevent infinite hang
  • Status normalization: .lower() before comparison
  • --branch flag: Added to chat command
  • API prefix: Confirmed /api/v1 is correct per app/main.py router registration

Note on the API version: I verified that all routes in app/main.py are registered under prefix="/api/v1", so the v1 prefix is correct for the current codebase.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Mar 19, 2026

@sofia-willow Thanks for the detailed summary and for addressing all the findings so thoroughly! Here's a quick recap of each fix:

  • Auth headers — Using x-api-key / x-user-id sourced from POTPIE_API_KEY or INTERNAL_ADMIN_SECRET env vars is the right approach for local dev flows.
  • agent_ids as a list — Correct alignment with the API contract.
  • Parse timeout — Good to have a hard 600s httpx-level timeout in addition to the polling max_wait guard.
  • Status normalization.lower() before comparison avoids silent mismatches on case variations.
  • --branch flag on chat — Keeps the interface consistent with parse.
  • API prefix — Good to have that confirmed against app/main.py.

I'll kick off a fresh review to verify the updated code.

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

♻️ Duplicate comments (1)
potpie/cli/main.py (1)

251-253: ⚠️ Potential issue | 🟡 Minor

Normalize project status before color mapping.

Line 252 compares raw status values case-sensitively, so "READY"/"PARSED" can be miscolored.

🔧 Proposed fix
-        status = p.get("status", "?")
-        status_style = "green" if status in ("ready", "parsed") else "yellow"
-        table.add_row(pid, name, branch, f"[{status_style}]{status}[/{status_style}]")
+        status = str(p.get("status", "?"))
+        status_norm = status.lower()
+        status_style = "green" if status_norm in ("ready", "completed", "parsed") else "yellow"
+        table.add_row(pid, name, branch, f"[{status_style}]{status}[/{status_style}]")
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@potpie/cli/main.py` around lines 251 - 253, Normalize the project's status
before deciding color: fetch the raw status with p.get("status", "?") into a
variable (e.g., status_raw), compute a lowercased version (e.g., status_norm =
status_raw.lower()), use status_norm when checking membership in ("ready",
"parsed") to set status_style, but keep using the original status_raw (or a
formatted display string) in table.add_row so the displayed label remains
unchanged; update references around p.get, status_style, and table.add_row
accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@potpie/cli/client.py`:
- Around line 36-47: Remove the placeholder "defaultuser" and fail fast when no
credentials are provided: in the constructor (where self._api_key and
self._user_id are set) stop defaulting self._user_id to "defaultuser" and only
assign it from the user_id argument or POTPIE_USER_ID env var (leave None/empty
if not present); validate that an API key exists (from api_key or POTPIE_API_KEY
or INTERNAL_ADMIN_SECRET) and raise a clear ValueError if missing; keep
_headers() as the single place that adds headers and ensure it only includes
"x-user-id" when self._user_id is explicitly present and "x-api-key" when
self._api_key is present so requests won’t send placeholder credentials.

In `@potpie/cli/main.py`:
- Around line 95-100: The readiness probe currently constructs PotpieClient()
with defaults and prints a hardcoded http://localhost:8001; update the start
logic to instantiate PotpieClient with the configured URL (use the CLI/options
variable or POTPIE_URL value passed into the command) so client.is_alive()
probes the correct endpoint, and replace the hardcoded URL in the console.print
with the same configured URL variable so the printed server address matches the
probe target.
- Around line 133-147: The CLI defers repo and branch validation to the server;
add preflight checks in main before calling client.parse: validate that
repo_path (from args.repo_path) is non-empty and exists on disk (use
os.path.exists/os.path.isdir) and that branch (args.branch) is non-empty; if
validation fails, print a clear console error and exit with sys.exit(1). Perform
these checks after assigning repo_path and branch and before the
client.is_alive() check or before calling client.parse to avoid unnecessary API
calls, and keep error messages consistent with existing console.print
formatting.

---

Duplicate comments:
In `@potpie/cli/main.py`:
- Around line 251-253: Normalize the project's status before deciding color:
fetch the raw status with p.get("status", "?") into a variable (e.g.,
status_raw), compute a lowercased version (e.g., status_norm =
status_raw.lower()), use status_norm when checking membership in ("ready",
"parsed") to set status_style, but keep using the original status_raw (or a
formatted display string) in table.add_row so the displayed label remains
unchanged; update references around p.get, status_style, and table.add_row
accordingly.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: e47b36b5-e47f-4213-ac40-3bd3987e7c55

📥 Commits

Reviewing files that changed from the base of the PR and between 23ce324 and c3d2a9b.

📒 Files selected for processing (3)
  • potpie/cli/client.py
  • potpie/cli/main.py
  • tests/cli/test_client.py
✅ Files skipped from review due to trivial changes (1)
  • tests/cli/test_client.py

Comment thread potpie/cli/client.py Outdated
Comment thread potpie/cli/main.py Outdated
Comment thread potpie/cli/main.py Outdated
- Remove defaultuser fallback for user_id to prevent auth issues
- Add ensure_auth() method for explicit credential validation
- Fix start command to use _get_client(args) for correct URL handling
- Show actual base_url instead of hardcoded localhost in start output
- Add CLI-side input validation for parse: empty path/branch, local path existence
- Only send x-user-id header when explicitly provided
@sonarqubecloud
Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
7 Security Hotspots

See analysis details on SonarQube Cloud

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (2)
potpie/cli/client.py (1)

40-51: ⚠️ Potential issue | 🟠 Major

Fail fast when using INTERNAL_ADMIN_SECRET without POTPIE_USER_ID.

Line 53 only validates API key presence. If the key is INTERNAL_ADMIN_SECRET, Line 40-51 can still send no x-user-id, which the backend rejects as 401 Invalid user_id (app/api/router.py:56-88). Please enforce this case in ensure_auth() and call ensure_auth() from _headers().

🔧 Suggested fix
 def _headers(self) -> dict[str, str]:
     """Build authentication headers for API requests.
@@
-    headers: dict[str, str] = {}
-    if self._api_key:
-        headers["x-api-key"] = self._api_key
+    self.ensure_auth()
+    headers: dict[str, str] = {"x-api-key": self._api_key}
     if self._user_id:
         headers["x-user-id"] = self._user_id
     return headers
@@
 def ensure_auth(self) -> None:
@@
     if not self._api_key:
         raise RuntimeError(
             "No API key configured. Set POTPIE_API_KEY or INTERNAL_ADMIN_SECRET "
             "environment variable, or pass --api-key to the CLI."
         )
+    if (
+        self._api_key == os.environ.get("INTERNAL_ADMIN_SECRET")
+        and not self._user_id
+    ):
+        raise RuntimeError(
+            "POTPIE_USER_ID is required when using INTERNAL_ADMIN_SECRET."
+        )

Also applies to: 53-63

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@potpie/cli/client.py` around lines 40 - 51, The code must fail fast when the
API key is the INTERNAL_ADMIN_SECRET but no user id is provided: update
ensure_auth() to check if self._api_key equals INTERNAL_ADMIN_SECRET (or env
POTPIE_INTERNAL_ADMIN_SECRET) and raise an authentication error if self._user_id
(or env POTPIE_USER_ID) is missing; then call ensure_auth() at the start of
_headers() so _headers() never returns headers with an admin key and no
x-user-id. Reference: modify ensure_auth() and the _headers() method to perform
this strict validation and raise/exit immediately when the admin-secret is used
without a user id.
potpie/cli/main.py (1)

267-269: ⚠️ Potential issue | 🟡 Minor

Normalize project status before styling.

Line 268 is case-sensitive and excludes completed, so valid success states can be miscolored.

🔧 Suggested fix
-        status = p.get("status", "?")
-        status_style = "green" if status in ("ready", "parsed") else "yellow"
+        status = str(p.get("status", "?"))
+        normalized = status.lower()
+        status_style = "green" if normalized in ("ready", "completed", "parsed") else "yellow"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@potpie/cli/main.py` around lines 267 - 269, Normalize the project status
string before styling: read status = p.get("status", "?") into a normalized
variable (e.g., status_norm = (status or "?").lower()), use that to compute
status_style (treat "ready", "parsed", and "completed" as success states,
case-insensitively), and then use the original or normalized status when calling
table.add_row so output still shows the original casing if desired; update the
logic around status, status_style, and the table.add_row call accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@potpie/cli/main.py`:
- Around line 145-150: The current check treats any string with a slash as a
local path, falsely rejecting remote repo forms like "owner/repo"; update the
logic around repo_path/local_path to first check if Path(repo_path).exists() and
only error when it does not exist and the input clearly looks like a filesystem
path (starts with "/" or "./" or "../" or contains a scheme like "://"); treat
inputs matching remote patterns (e.g., simple "owner/repo" or URLs) as remote
and skip the existence check. Locate the conditional using repo_path,
local_path, Path(repo_path), console.print and sys.exit and implement the
described ordering and pattern checks so remote forms are not rejected.

---

Duplicate comments:
In `@potpie/cli/client.py`:
- Around line 40-51: The code must fail fast when the API key is the
INTERNAL_ADMIN_SECRET but no user id is provided: update ensure_auth() to check
if self._api_key equals INTERNAL_ADMIN_SECRET (or env
POTPIE_INTERNAL_ADMIN_SECRET) and raise an authentication error if self._user_id
(or env POTPIE_USER_ID) is missing; then call ensure_auth() at the start of
_headers() so _headers() never returns headers with an admin key and no
x-user-id. Reference: modify ensure_auth() and the _headers() method to perform
this strict validation and raise/exit immediately when the admin-secret is used
without a user id.

In `@potpie/cli/main.py`:
- Around line 267-269: Normalize the project status string before styling: read
status = p.get("status", "?") into a normalized variable (e.g., status_norm =
(status or "?").lower()), use that to compute status_style (treat "ready",
"parsed", and "completed" as success states, case-insensitively), and then use
the original or normalized status when calling table.add_row so output still
shows the original casing if desired; update the logic around status,
status_style, and the table.add_row call accordingly.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 5e334982-4774-4d32-9210-b82d47a12c26

📥 Commits

Reviewing files that changed from the base of the PR and between c3d2a9b and 07f3f6d.

📒 Files selected for processing (2)
  • potpie/cli/client.py
  • potpie/cli/main.py

Comment thread potpie/cli/main.py
Comment on lines +145 to +150
# Check if local path exists (skip for remote repos like owner/repo)
if os.path.sep in repo_path or repo_path.startswith((".", "/")):
local_path = Path(repo_path)
if not local_path.exists():
console.print(f"[red]Error:[/red] Local path '{repo_path}' does not exist.")
sys.exit(1)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Remote repo inputs are incorrectly rejected as missing local paths.

Line 146 treats any path containing / as local. That breaks documented inputs like owner/repo (Line 396) and URL forms, causing false “does not exist” errors.

🔧 Suggested fix
-    # Check if local path exists (skip for remote repos like owner/repo)
-    if os.path.sep in repo_path or repo_path.startswith((".", "/")):
-        local_path = Path(repo_path)
-        if not local_path.exists():
-            console.print(f"[red]Error:[/red] Local path '{repo_path}' does not exist.")
-            sys.exit(1)
+    # Check local path existence only when input is not a remote repo reference.
+    is_remote = repo_path.startswith(("http://", "https://", "git@", "ssh://")) or (
+        repo_path.count("/") == 1 and not repo_path.startswith((".", "/", "~"))
+    )
+    if not is_remote:
+        local_path = Path(repo_path).expanduser()
+        if not local_path.exists():
+            console.print(f"[red]Error:[/red] Local path '{repo_path}' does not exist.")
+            sys.exit(1)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@potpie/cli/main.py` around lines 145 - 150, The current check treats any
string with a slash as a local path, falsely rejecting remote repo forms like
"owner/repo"; update the logic around repo_path/local_path to first check if
Path(repo_path).exists() and only error when it does not exist and the input
clearly looks like a filesystem path (starts with "/" or "./" or "../" or
contains a scheme like "://"); treat inputs matching remote patterns (e.g.,
simple "owner/repo" or URLs) as remote and skip the existence check. Locate the
conditional using repo_path, local_path, Path(repo_path), console.print and
sys.exit and implement the described ordering and pattern checks so remote forms
are not rejected.

@humblefoo02
Copy link
Copy Markdown
Contributor

PR Summary

This PR adds a CLI tool for local Potpie usage, implementing commands to start/stop services, parse repositories, check status, list projects/agents, and chat interactively with streaming responses.

File Changes
potpie/cli/init.py Adds package docstring
potpie/cli/client.py Implements HTTP client wrapping Potpie REST API
potpie/cli/main.py Implements CLI commands with argparse and Rich output
pyproject.toml Adds CLI entry point and Rich dependency
tests/cli/init.py Adds test package docstring
tests/cli/test_client.py Adds HTTP client unit tests
tests/cli/test_main.py Adds CLI parser/dispatch tests

Estimated effort: moderate
Potential risks:

  • Potential authentication issues if API keys aren't properly configured
  • Polling logic could hang if server responses are delayed
  • Streaming chat responses may have performance implications

Comment thread potpie/cli/client.py
) -> None:
self.base_url = base_url.rstrip("/")
self.timeout = timeout
self._api_key = api_key or os.environ.get("POTPIE_API_KEY") or os.environ.get("INTERNAL_ADMIN_SECRET") or ""
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[warning][security] Empty string fallback for API key could mask missing auth configuration
Risk: May silently fail auth checks if neither env var is set
Suggested change: Remove empty string fallback and let ensure_auth() handle missing keys
Confidence: high

Comment thread potpie/cli/main.py
sys.exit(e.returncode)


def cmd_parse(args: argparse.Namespace) -> None:
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[warning][security] Repository path validation should handle path traversal attempts
Risk: Malicious paths could access files outside intended directory
Suggested change: Add Path(repo_path).resolve().is_relative_to(Path.cwd()) check for local paths
Confidence: high

Comment thread potpie/cli/main.py
console.print(" Parsing", end="")
max_wait = 600.0
try:
start_time = time.monotonic()
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[warning][correctness] Status check should handle HTTP errors explicitly
Risk: Network errors could be misinterpreted as parsing failures
Suggested change: Catch httpx.HTTPStatusError separately from other exceptions
Confidence: medium

Comment thread potpie/cli/main.py
console.print("[bold blue]Potpie>[/bold blue] ", end="")
try:
full_response = ""
for chunk in client.send_message(conversation_id, user_input):
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[warning][reliability] Streaming response should have timeout protection
Risk: Hanging connections could block CLI indefinitely
Suggested change: Add timeout parameter to client.send_message()
Confidence: medium

Comment thread potpie/cli/main.py

# Create conversation
try:
conv = client.create_conversation(project_id, agent)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[warning][correctness] Agent validation should check against list_agents()
Risk: Invalid agent names could cause confusing errors
Suggested change: Pre-validate agent name against available agents
Confidence: high

@humblefoo02
Copy link
Copy Markdown
Contributor

PR-level observations

  • [warning/reliability] tests/cli/test_client.py — Missing tests for error cases and edge conditions
    • Risk: Untested error paths could hide bugs
    • Suggested fix: Add tests for auth failures, timeouts, and malformed responses
  • [warning/reliability] potpie/cli/client.py — Missing tests for error cases and edge conditions
    • Risk: Untested error paths could fail in production
    • Suggested fix: Add tests for network errors, timeouts, and malformed responses
  • [warning/reliability] potpie/cli/main.py — Missing integration tests for CLI commands
    • Risk: Untested command combinations could fail in real usage
    • Suggested fix: Add end-to-end tests with mocked server responses

@humblefoo02
Copy link
Copy Markdown
Contributor

PR Review Agent summary

  • Parsed findings: 28
  • Posted inline findings: 8
  • Severity: error=0, warning=8, info=0
  • Top categories: reliability: 4, security: 2, correctness: 2
  • Scope: correctness, security, reliability, cross-file impact
  • Estimated effort: moderate

@humblefoo02
Copy link
Copy Markdown
Contributor

PR Summary

This PR adds a CLI tool for local Potpie usage, implementing commands to start/stop services, parse repositories, check status, list projects/agents, and chat interactively with streaming responses. The CLI wraps the existing REST API with a thin HTTP client and provides rich terminal output.

File Changes
potpie/cli/init.py Adds package docstring
potpie/cli/client.py Implements HTTP client wrapping Potpie REST API
potpie/cli/main.py Implements CLI commands with argparse and Rich output
pyproject.toml Adds CLI entry point and Rich dependency
tests/cli/init.py Adds test package docstring
tests/cli/test_client.py Adds HTTP client unit tests
tests/cli/test_main.py Adds CLI parser/dispatch tests

Estimated effort: moderate
Potential risks:

  • Potential authentication issues if API keys aren't properly configured
  • Polling logic could hang if server responses are delayed
  • Streaming chat responses may have performance implications

Comment thread potpie/cli/client.py
) -> None:
self.base_url = base_url.rstrip("/")
self.timeout = timeout
self._api_key = api_key or os.environ.get("POTPIE_API_KEY") or os.environ.get("INTERNAL_ADMIN_SECRET") or ""
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[warning][security] Long line with multiple env var fallbacks makes secret handling hard to audit
Risk: Complex secret fallback chains can hide credential issues and make debugging harder
Suggested change: Break into multiple lines with clear comments for each fallback source
Confidence: medium

Comment thread potpie/cli/client.py
return status
if time.monotonic() - start > max_wait:
raise TimeoutError(
f"Parsing did not complete within {max_wait}s (last status: {state})"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[warning][reliability] Timeout error message should include project_id for debugging
Risk: Without project_id in error, debugging failed parsing jobs is harder
Suggested change: Include project_id in timeout error message
Confidence: high

Comment thread potpie/cli/main.py
console.print()

try:
process = subprocess.Popen(
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[warning][security] Subprocess call uses untrusted input without validation
Risk: Potential command injection if start_script contains malicious content
Suggested change: Validate start_script path against allowlist of known scripts
Confidence: high

Comment thread potpie/cli/main.py

try:
process = subprocess.Popen(
["bash", str(start_script)],
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[warning][reliability] Subprocess uses partial executable path ('bash')
Risk: Relies on PATH lookup which may resolve to unexpected binary
Suggested change: Use full path to bash executable (e.g. /bin/bash)
Confidence: medium

Comment thread potpie/cli/main.py
sys.exit(e.returncode)


def cmd_parse(args: argparse.Namespace) -> None:
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[warning][maintainability] cmd_parse function complexity exceeds threshold (13 > 10)
Risk: High complexity makes code harder to maintain and test
Suggested change: Refactor into smaller helper methods
Confidence: high

Comment thread potpie/cli/main.py
sys.exit(1)

if not client.is_alive():
console.print("[red]Error:[/red] Potpie server is not running. Run [bold]potpie start[/bold] first.")
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[warning][correctness] Error message assumes server is always started via CLI
Risk: Misleading message if server is started via other means
Suggested change: Generalize message to not assume CLI start method
Confidence: medium

@humblefoo02
Copy link
Copy Markdown
Contributor

PR Review Agent summary

  • Parsed findings: 8
  • Posted inline findings: 6
  • Severity: error=0, warning=6, info=0
  • Top categories: security: 2, reliability: 2, maintainability: 1
  • Scope: correctness, security, reliability, cross-file impact
  • Merge readiness: 🚫 HOLD — 3 high-confidence warnings suggest non-trivial risk.
  • Estimated effort: moderate

@humblefoo02
Copy link
Copy Markdown
Contributor

PR Summary

This PR adds a CLI tool for local Potpie usage, implementing commands to start/stop services, parse repositories, check status, list projects/agents, and chat interactively with streaming responses. The CLI wraps the existing REST API with a thin HTTP client and provides rich terminal output.

File Changes
potpie/cli/init.py Adds package docstring
potpie/cli/client.py Implements HTTP client wrapping Potpie REST API
potpie/cli/main.py Implements CLI commands with argparse and Rich output
pyproject.toml Adds CLI entry point and Rich dependency
tests/cli/init.py Adds test package docstring
tests/cli/test_client.py Adds HTTP client unit tests
tests/cli/test_main.py Adds CLI parser/dispatch tests

Estimated effort: moderate
Potential risks:

  • Potential authentication issues if API keys aren't properly configured
  • Polling logic could hang if server responses are delayed
  • Streaming chat responses may have performance implications

Comment thread potpie/cli/client.py
return status
if time.monotonic() - start > max_wait:
raise TimeoutError(
f"Parsing did not complete within {max_wait}s (last status: {state})"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[warning][reliability] Timeout error message could be more actionable
Risk: Users may not know how to proceed when parsing times out
Suggested change: Include troubleshooting steps in timeout message (e.g. check server logs, try smaller repo)
Confidence: medium

Comment thread potpie/cli/main.py
"""Walk up from cwd to find the Potpie project root (has compose.yaml)."""
current = Path.cwd()
for parent in [current, *current.parents]:
if (parent / "compose.yaml").exists() and (parent / "scripts" / "start.sh").exists():
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[warning][correctness] Path validation could incorrectly match GitHub owner/repo format
Risk: Remote repo identifiers containing slashes (owner/repo) will be incorrectly treated as local paths
Suggested change: First check for GitHub-style owner/repo format before testing as local path
Confidence: high

Comment thread potpie/cli/main.py
console.print()

try:
process = subprocess.Popen(
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[warning][reliability] Subprocess lacks signal handling for clean shutdown
Risk: If parent process is killed, child processes could become orphaned
Suggested change: Add signal handler to properly terminate subprocess on SIGTERM/SIGINT
Confidence: medium

Comment thread potpie/cli/main.py
console.print(Panel.fit("🛑 Stopping Potpie", style="bold red"))

try:
subprocess.run(
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[warning][reliability] Subprocess lacks signal handling for clean shutdown
Risk: If parent process is killed, child processes could become orphaned
Suggested change: Add signal handler to properly terminate subprocess on SIGTERM/SIGINT
Confidence: medium

Comment thread potpie/cli/main.py
sys.exit(e.returncode)


def cmd_parse(args: argparse.Namespace) -> None:
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[warning][maintainability] cmd_parse function is too complex (C901 > 10)
Risk: High complexity makes the function harder to maintain and test
Suggested change: Break into smaller helper functions for parse submission, polling, and status handling
Confidence: high

@humblefoo02
Copy link
Copy Markdown
Contributor

PR Review Agent summary

  • Parsed findings: 11
  • Posted inline findings: 5
  • Severity: error=0, warning=5, info=0
  • Top categories: reliability: 3, correctness: 1, maintainability: 1
  • Scope: correctness, security, reliability, cross-file impact
  • Merge readiness: ⚠️ REVIEW — 5 warning(s) noted. Author should address or explicitly acknowledge each.
  • Estimated effort: moderate

@humblefoo02
Copy link
Copy Markdown
Contributor

PR Summary

This PR adds a CLI tool for local Potpie usage, implementing commands to start/stop services, parse repositories, check status, list projects/agents, and chat interactively with streaming responses. The CLI wraps the existing REST API with a thin HTTP client and provides rich terminal output.

File Changes
potpie/cli/init.py Adds package docstring
potpie/cli/client.py Implements HTTP client wrapping Potpie REST API
potpie/cli/main.py Implements CLI commands with argparse and Rich output
pyproject.toml Adds CLI entry point and Rich dependency
tests/cli/init.py Adds test package docstring
tests/cli/test_client.py Adds HTTP client unit tests
tests/cli/test_main.py Adds CLI parser/dispatch tests

Estimated effort: moderate
Potential risks:

  • Potential authentication issues if API keys aren't properly configured
  • Polling logic could hang if server responses are delayed
  • Streaming chat responses may have performance implications

Comment thread potpie/cli/client.py
) -> None:
self.base_url = base_url.rstrip("/")
self.timeout = timeout
self._api_key = api_key or os.environ.get("POTPIE_API_KEY") or os.environ.get("INTERNAL_ADMIN_SECRET") or ""
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[warning][security] API key fallback to empty string may cause silent auth failures
Risk: Silent auth failures make debugging harder and could lead to unexpected behavior
Suggested change: Remove empty string fallback to force explicit auth configuration
Confidence: high

Comment thread potpie/cli/client.py
) -> None:
self.base_url = base_url.rstrip("/")
self.timeout = timeout
self._api_key = api_key or os.environ.get("POTPIE_API_KEY") or os.environ.get("INTERNAL_ADMIN_SECRET") or ""
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[warning][maintainability] Long line exceeds PEP 8 maximum length
Risk: Reduces code readability and maintainability
Suggested change: Break line into multiple lines using parentheses
Confidence: high

Comment thread potpie/cli/client.py
return status
if time.monotonic() - start > max_wait:
raise TimeoutError(
f"Parsing did not complete within {max_wait}s (last status: {state})"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[warning][maintainability] Long line exceeds PEP 8 maximum length
Risk: Reduces code readability and maintainability
Suggested change: Break line into multiple lines using parentheses
Confidence: high

Comment thread potpie/cli/main.py
"""Walk up from cwd to find the Potpie project root (has compose.yaml)."""
current = Path.cwd()
for parent in [current, *current.parents]:
if (parent / "compose.yaml").exists() and (parent / "scripts" / "start.sh").exists():
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[warning][correctness] Path validation could be more robust
Risk: Current validation may incorrectly match remote identifiers containing forward slashes
Suggested change: Use Path(x).exists() OR regex to detect owner/repo format first
Confidence: medium

Comment thread potpie/cli/main.py
console.print()

try:
process = subprocess.Popen(
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[warning][reliability] Subprocess call lacks signal handler
Risk: Process may become orphaned if parent is killed
Suggested change: Add signal handler to ensure clean process termination
Confidence: medium

Comment thread potpie/cli/main.py

try:
process = subprocess.Popen(
["bash", str(start_script)],
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[warning][security] Subprocess call uses partial executable path
Risk: Partial paths may execute unexpected binaries if PATH is compromised
Suggested change: Use full path to bash executable
Confidence: medium

Comment thread potpie/cli/main.py
console.print(Panel.fit("🛑 Stopping Potpie", style="bold red"))

try:
subprocess.run(
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[warning][reliability] Subprocess call lacks signal handler
Risk: Process may become orphaned if parent is killed
Suggested change: Add signal handler to ensure clean process termination
Confidence: medium

Comment thread potpie/cli/main.py

try:
subprocess.run(
["bash", str(stop_script)],
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[warning][security] Subprocess call uses partial executable path
Risk: Partial paths may execute unexpected binaries if PATH is compromised
Suggested change: Use full path to bash executable
Confidence: medium

Comment thread potpie/cli/main.py
sys.exit(e.returncode)


def cmd_parse(args: argparse.Namespace) -> None:
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[warning][maintainability] cmd_parse function is too complex
Risk: High complexity makes code harder to maintain and test
Suggested change: Refactor into smaller, focused functions
Confidence: medium

Comment thread potpie/cli/main.py
sys.exit(1)

if not client.is_alive():
console.print("[red]Error:[/red] Potpie server is not running. Run [bold]potpie start[/bold] first.")
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[warning][correctness] Error message could be more informative
Risk: Users may not understand how to fix the server not running issue
Suggested change: Include troubleshooting tips in error message
Confidence: medium

@humblefoo02
Copy link
Copy Markdown
Contributor

PR Review Agent summary

  • Parsed findings: 16
  • Posted inline findings: 10
  • Severity: error=0, warning=10, info=0
  • Top categories: security: 3, maintainability: 3, correctness: 2
  • Scope: correctness, security, reliability, cross-file impact
  • Merge readiness: 🚫 HOLD — 3 high-confidence warnings suggest non-trivial risk.
  • Estimated effort: moderate

@humblefoo02
Copy link
Copy Markdown
Contributor

PR Summary

This PR adds a CLI tool for local Potpie usage, implementing commands to start/stop services, parse repositories, check status, list projects/agents, and chat interactively with streaming responses. The CLI wraps the existing REST API with a thin HTTP client and provides rich terminal output.

File Changes
potpie/cli/init.py Adds package docstring
potpie/cli/client.py Implements HTTP client wrapping Potpie REST API
potpie/cli/main.py Implements CLI commands with argparse and Rich output
pyproject.toml Adds CLI entry point and Rich dependency
tests/cli/init.py Adds test package docstring
tests/cli/test_client.py Adds HTTP client unit tests
tests/cli/test_main.py Adds CLI parser/dispatch tests

Estimated effort: moderate
Potential risks:

  • Potential authentication issues if API keys aren't properly configured
  • Polling logic could hang if server responses are delayed
  • Streaming chat responses may have performance implications

Comment thread potpie/cli/client.py
return status
if time.monotonic() - start > max_wait:
raise TimeoutError(
f"Parsing did not complete within {max_wait}s (last status: {state})"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[warning][reliability] Timeout error message could be more actionable
Risk: Users may not know how to recover from parsing timeout
Suggested change: Add guidance on how to check parsing logs or retry
Confidence: medium

Comment thread potpie/cli/main.py
"""Walk up from cwd to find the Potpie project root (has compose.yaml)."""
current = Path.cwd()
for parent in [current, *current.parents]:
if (parent / "compose.yaml").exists() and (parent / "scripts" / "start.sh").exists():
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[warning][correctness] Path validation could incorrectly match GitHub owner/repo format
Risk: Remote repo identifiers containing forward slashes (like 'owner/repo') could be incorrectly treated as local paths
Suggested change: Add explicit check for GitHub-style owner/repo format before checking local paths
Confidence: medium

Comment thread potpie/cli/main.py
console.print()

try:
process = subprocess.Popen(
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[warning][reliability] Subprocess could become orphaned if parent is killed
Risk: If the CLI process is killed while start.sh is running, the child process may continue running
Suggested change: Add signal handler to clean up subprocess on SIGTERM/SIGINT
Confidence: medium

Comment thread potpie/cli/main.py
console.print(Panel.fit("🛑 Stopping Potpie", style="bold red"))

try:
subprocess.run(
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[warning][reliability] Subprocess could become orphaned if parent is killed
Risk: If the CLI process is killed while stop.sh is running, the child process may continue running
Suggested change: Add signal handler to clean up subprocess on SIGTERM/SIGINT
Confidence: medium

Comment thread potpie/cli/main.py
sys.exit(e.returncode)


def cmd_parse(args: argparse.Namespace) -> None:
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[warning][maintainability] Function complexity exceeds recommended threshold (13 > 10)
Risk: High complexity makes the function harder to understand and maintain
Suggested change: Extract polling logic and status handling into separate helper functions
Confidence: high

Comment thread potpie/cli/main.py
console.print("[bold blue]Potpie>[/bold blue] ", end="")
try:
full_response = ""
for chunk in client.send_message(conversation_id, user_input):
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[warning][reliability] Streaming response lacks timeout
Risk: Hanging connections could block the CLI indefinitely
Suggested change: Add timeout parameter to streaming request
Confidence: medium

@humblefoo02
Copy link
Copy Markdown
Contributor

PR Review Agent summary

  • Parsed findings: 7
  • Posted inline findings: 6
  • Severity: error=0, warning=6, info=0
  • Top categories: reliability: 4, correctness: 1, maintainability: 1
  • Scope: correctness, security, reliability, cross-file impact
  • Merge readiness: ⚠️ REVIEW — 6 warning(s) noted. Author should address or explicitly acknowledge each.
  • Estimated effort: moderate

@humblefoo02
Copy link
Copy Markdown
Contributor

PR Summary

This PR adds a CLI tool for local Potpie usage, implementing commands to start/stop services, parse repositories, check status, list projects/agents, and chat interactively with streaming responses. The CLI wraps the existing REST API with a thin HTTP client and provides rich terminal output.

File Changes
potpie/cli/init.py Adds package docstring
potpie/cli/client.py Implements HTTP client wrapping Potpie REST API
potpie/cli/main.py Implements CLI commands with argparse and Rich output
pyproject.toml Adds CLI entry point and Rich dependency
tests/cli/init.py Adds test package docstring
tests/cli/test_client.py Adds HTTP client unit tests
tests/cli/test_main.py Adds CLI parser/dispatch tests

Estimated effort: moderate
Potential risks:

  • Potential authentication issues if API keys aren't properly configured
  • Polling logic could hang if server responses are delayed
  • Streaming chat responses may have performance implications

Comment thread potpie/cli/main.py
sys.exit(e.returncode)


def cmd_parse(args: argparse.Namespace) -> None:
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[warning][correctness] Path validation could incorrectly match GitHub owner/repo format
Risk: Remote repo identifiers containing forward slashes (owner/repo) could be incorrectly treated as local paths
Suggested change: First check for GitHub-style owner/repo format before testing as local path
Confidence: medium

@humblefoo02
Copy link
Copy Markdown
Contributor

PR Review Agent summary

  • Parsed findings: 10
  • Posted inline findings: 1
  • Severity: error=0, warning=1, info=0
  • Top categories: correctness: 1
  • Scope: correctness, security, reliability, cross-file impact
  • Merge readiness: ⚠️ REVIEW — 1 warning(s) noted. Author should address or explicitly acknowledge each.
  • Estimated effort: moderate

@humblefoo02
Copy link
Copy Markdown
Contributor

PR Summary

This PR adds a CLI tool for local Potpie usage, implementing commands to start/stop services, parse repositories, check status, list projects/agents, and chat interactively with streaming responses. The CLI wraps the existing REST API with a thin HTTP client and provides rich terminal output.

File Changes
potpie/cli/init.py Adds package docstring
potpie/cli/client.py Implements HTTP client wrapping Potpie REST API
potpie/cli/main.py Implements CLI commands with argparse and Rich output
pyproject.toml Adds CLI entry point and Rich dependency
tests/cli/init.py Adds test package docstring
tests/cli/test_client.py Adds HTTP client unit tests
tests/cli/test_main.py Adds CLI parser/dispatch tests

Estimated effort: moderate
Potential risks:

  • Potential authentication issues if API keys aren't properly configured
  • Polling logic could hang if server responses are delayed
  • Streaming chat responses may have performance implications

Comment thread pyproject.toml
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.10"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[error][correctness] dependencies nested under [project.scripts] — breaks pip install
Risk: pip will ignore the dependencies list entirely. Package installs without its deps.
Suggested change: Move dependencies = [...] to be a direct child of [project], not [project.scripts].
Confidence: high

Comment thread potpie/cli/main.py
sys.exit(1)

# Check if local path exists (skip for remote repos like owner/repo)
if os.path.sep in repo_path or repo_path.startswith((".", "/")):
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[error][correctness] os.path.sep in x incorrectly matches owner/repo format on Unix
Risk: Remote repo identifiers like owner/repo contain '/' which is os.path.sep on Unix, causing them to be treated as local paths and rejected as missing.
Suggested change: Check Path(x).exists() for local paths, or match owner/repo with re.match(r'^[\w.-]+/[\w.-]+$', x) first.
Confidence: high

Comment thread potpie/cli/client.py

DEFAULT_BASE_URL = "http://localhost:8001"
DEFAULT_TIMEOUT = 30.0
API_PREFIX = "/api/v1"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[error][correctness] /api/v1 client calls but router registers /api/v2
Risk: Client and router API versions diverge (/api/v1 vs /api/v2). Calls may 404 or hit the wrong handlers at runtime.
Suggested change: Update the client hardcoded base URL/prefix to match the router prefix (/api/v2), or change the router prefix to /api/v1 intentionally.
Confidence: high
Cross-file refs: app/main.py

Comment thread tests/cli/test_client.py
"""Test that API prefix is correctly configured."""

def test_api_prefix_value(self):
assert API_PREFIX == "/api/v1"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[error][correctness] /api/v1 client calls but router registers /api/v2
Risk: Client and router API versions diverge (/api/v1 vs /api/v2). Calls may 404 or hit the wrong handlers at runtime.
Suggested change: Update the client hardcoded base URL/prefix to match the router prefix (/api/v2), or change the router prefix to /api/v1 intentionally.
Confidence: high
Cross-file refs: app/main.py

Comment thread potpie/cli/client.py
return status
if time.monotonic() - start > max_wait:
raise TimeoutError(
f"Parsing did not complete within {max_wait}s (last status: {state})"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[warning][reliability] Timeout error message could leak internal state
Risk: Exposing internal status in error messages could aid attackers
Suggested change: Use a generic timeout message without internal status details
Confidence: medium

Comment thread potpie/cli/main.py
console.print()

try:
process = subprocess.Popen(
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[warning][reliability] subprocess.Popen without signal handler risks orphaned processes
Risk: If parent process is killed, child process may continue running indefinitely
Suggested change: Add signal handler to ensure child process is terminated on parent exit
Confidence: high

Comment thread potpie/cli/main.py
console.print(Panel.fit("🛑 Stopping Potpie", style="bold red"))

try:
subprocess.run(
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[warning][reliability] subprocess.run without signal handler risks orphaned processes
Risk: If parent process is killed, child process may continue running indefinitely
Suggested change: Add signal handler to ensure child process is terminated on parent exit
Confidence: high

Comment thread potpie/cli/main.py
sys.exit(e.returncode)


def cmd_parse(args: argparse.Namespace) -> None:
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[warning][maintainability] cmd_parse function is too complex (13 > 10)
Risk: High complexity makes the function harder to maintain and test
Suggested change: Refactor into smaller helper functions
Confidence: high

Comment thread potpie/cli/main.py
elapsed = time.monotonic() - start_time
console.print(f" [green]✓ Complete![/green] ({elapsed:.0f}s)")
console.print()
console.print(f" You can now chat with this project:")
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[warning][maintainability] Unnecessary f-string prefix
Risk: Redundant syntax makes code harder to read
Suggested change: Remove f-string prefix since no variables are interpolated
Confidence: high

Comment thread potpie/cli/main.py
return

if state in ("error", "failed"):
console.print(f" [red]✗ Failed![/red]")
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[warning][maintainability] Unnecessary f-string prefix
Risk: Redundant syntax makes code harder to read
Suggested change: Remove f-string prefix since no variables are interpolated
Confidence: high

@humblefoo02
Copy link
Copy Markdown
Contributor

PR-level observations

  • [warning/reliability] tests/cli/test_main.py — Missing integration tests for CLI commands
    • Risk: Untested command combinations could fail in real usage
    • Suggested fix: Add end-to-end tests with mocked server responses

@humblefoo02
Copy link
Copy Markdown
Contributor

PR Review Agent summary

  • Parsed findings: 13
  • Posted inline findings: 11
  • Severity: error=4, warning=7, info=0
  • Top categories: correctness: 4, reliability: 4, maintainability: 3
  • Scope: correctness, security, reliability, cross-file impact
  • Merge readiness: 🚫 HOLD — 4 error-severity finding(s) must be resolved before merge.
  • Estimated effort: moderate

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Create a CLI for using potpie locally

2 participants