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
30 changes: 30 additions & 0 deletions opc/scripts/setup/claude_integration.py
Original file line number Diff line number Diff line change
Expand Up @@ -431,6 +431,30 @@ def _copy_scripts(opc_source: Path, target_dir: Path) -> int:
return count


def _copy_dotfile_scripts(opc_source: Path, target_dir: Path) -> int:
"""Copy root-level scripts from .claude/scripts/ (e.g. status.py).

_copy_scripts() handles opc/scripts/ subdirs and ROOT_SCRIPTS.
This handles the .claude/scripts/ root files that settings.json
references directly (e.g. statusLine -> status.py).
"""
count = 0
src_scripts = opc_source / "scripts"
dst_scripts = target_dir / "scripts"

if not src_scripts.exists():
return count

dst_scripts.mkdir(parents=True, exist_ok=True)

for script in src_scripts.iterdir():
if script.is_file() and script.suffix in (".py", ".sh"):
shutil.copy2(script, dst_scripts / script.name)
count += 1

return count


def install_opc_integration(
target_dir: Path,
opc_source: Path,
Expand Down Expand Up @@ -539,6 +563,9 @@ def install_opc_integration(
# Copy scripts (core, math, tldr directories + root scripts)
result["installed_scripts"] = _copy_scripts(opc_source, target_dir)

# Copy root scripts from .claude/scripts/ (e.g. status.py for statusLine)
result["installed_scripts"] += _copy_dotfile_scripts(opc_source, target_dir)

# Merge user items if requested
if merge_user_items and existing and conflicts:
# Merge non-conflicting hooks
Expand Down Expand Up @@ -670,6 +697,9 @@ def install_opc_integration_symlink(
# Copy scripts (core, math, tldr directories + root scripts)
_copy_scripts(opc_source, target_dir)

# Copy root scripts from .claude/scripts/ (e.g. status.py for statusLine)
_copy_dotfile_scripts(opc_source, target_dir)

result["success"] = True

except Exception as e:
Expand Down
2 changes: 2 additions & 0 deletions opc/scripts/setup/wizard.py
Original file line number Diff line number Diff line change
Expand Up @@ -740,6 +740,7 @@ async def run_setup_wizard() -> None:
console.print(f" [green]OK[/green] Installed {result['installed_rules']} rules")
console.print(f" [green]OK[/green] Installed {result['installed_agents']} agents")
console.print(f" [green]OK[/green] Installed {result['installed_servers']} MCP servers")
console.print(f" [green]OK[/green] Installed {result['installed_scripts']} scripts")
if result["merged_items"]:
console.print(
f" [green]OK[/green] Merged {len(result['merged_items'])} custom items"
Expand Down Expand Up @@ -802,6 +803,7 @@ async def run_setup_wizard() -> None:
console.print(f" [green]OK[/green] Installed {result['installed_rules']} rules")
console.print(f" [green]OK[/green] Installed {result['installed_agents']} agents")
console.print(f" [green]OK[/green] Installed {result['installed_servers']} MCP servers")
console.print(f" [green]OK[/green] Installed {result['installed_scripts']} scripts")
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Bug: The setup wizard will crash with a KeyError during symlink installation because the install_opc_integration_symlink function fails to return the installed_scripts count.
Severity: CRITICAL

Suggested Fix

In the install_opc_integration_symlink function, initialize the result dictionary with "installed_scripts": 0. Then, capture the integer return values from the _copy_scripts and _copy_dotfile_scripts calls and sum them into the result["installed_scripts"] key before returning.

Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent.
Verify if this is a real issue. If it is, propose a fix; if not, explain why it's not
valid.

Location: opc/scripts/setup/wizard.py#L806

Potential issue: The `install_opc_integration_symlink` function does not initialize or
populate the `installed_scripts` key in the dictionary it returns. However, the setup
wizard in `wizard.py` unconditionally accesses `result['installed_scripts']` when a user
chooses the symlink installation option. This discrepancy will cause a `KeyError` and
crash the setup wizard during the installation process, preventing users from completing
the setup with this option. The function calls `_copy_scripts` and
`_copy_dotfile_scripts` but fails to capture and store their return values, which are
the counts of the installed scripts.

Did we get this right? 👍 / 👎 to inform future reviews.


# Build TypeScript hooks
console.print(" Building TypeScript hooks...")
Expand Down