Skip to content

Add NeuroRift package scaffold, runtime & model checks, secure command runner, CLI and skill system#38

Open
Neuro-Rift wants to merge 2 commits into
mainfrom
codex/improve-code-robustness-and-error-handling-e8du09
Open

Add NeuroRift package scaffold, runtime & model checks, secure command runner, CLI and skill system#38
Neuro-Rift wants to merge 2 commits into
mainfrom
codex/improve-code-robustness-and-error-handling-e8du09

Conversation

@Neuro-Rift
Copy link
Copy Markdown
Owner

@Neuro-Rift Neuro-Rift commented Mar 14, 2026

User description

Motivation

  • Scaffold a modular neurorift package and CLI, add runtime and model capability checks, and harden tool execution for safer autonomous agent operation.
  • Integrate a simple skill management pipeline (ClawHub/local registry) and provide a launch wrapper script for guided startup.

Description

  • Added model_capability_check.py to query an Ollama model with a capability prompt and parse a JSON readiness response, and added runtime_environment_check.py to validate required system tools and workspace.
  • Introduced a neurorift package with many lightweight module stubs and implementations (channels, core, execution, gateway, memory, models, sessions, skills, storage, tools, utils) and an example recon_scanner skill under skill_store/installed.
  • Reworked neurorift_main.py to initialize the new skill manager, wire in model/runtime checks, and replace ad-hoc subprocess usage with a hardened _run_command_secure runner plus validation, sandboxing, resource limits, retries, and improved logging.
  • Added neurorift_cli.py as the global CLI entry wrapper, neurorift_launch.sh for guided startup, and updated setup.py to include the new package and entry point adjustment.

Testing

  • Executed python3 runtime_environment_check.py which ran the automated tool probes and returned a JSON report of tool statuses successfully.
  • Ran python3 model_capability_check.py --model nonexistent_model in an environment without ollama and observed the expected ollama_missing error JSON response from the script.
  • Invoked python3 neurorift_cli.py --help to validate the CLI parser and confirmed the help output returned successfully.

Codex Task


CodeAnt-AI Description

Add NeuroRift package scaffold, robust startup checks, secure command runner, CLI wrapper, and skill manager

What Changed

  • Provides a new neurorift package with lightweight modules for channels, core, execution, gateway, memory, models, sessions, skills, storage and tools so the app can be imported and managed as a package.
  • Adds a global CLI entry that can install an /usr/local/bin wrapper and forward arguments to the main app; includes a helper shell wrapper and a guided launch script for safe startup.
  • Adds runtime environment checks that report missing or non-executable system tools and a model capability check that queries a local Ollama model and returns a structured JSON readiness result.
  • Replaces ad-hoc subprocess calls with a hardened, retrying, sandbox-aware command runner that validates arguments, enforces resource limits, sanitizes outputs, and logs execution outcomes.
  • Integrates a simple skill system (install, list, run, uninstall) with a local registry and a ClawHub client to fetch example skills from a packaged skill store.

Impact

✅ Clearer startup checks (missing tools and model readiness reported)
✅ Safer tool execution (argument validation, time/memory limits, retries)
✅ Easier skill install and management from packaged examples

💡 Usage Guide

Checking Your Pull Request

Every time you make a pull request, our system automatically looks through it. We check for security issues, mistakes in how you're setting up your infrastructure, and common code problems. We do this to make sure your changes are solid and won't cause any trouble later.

Talking to CodeAnt AI

Got a question or need a hand with something in your pull request? You can easily get in touch with CodeAnt AI right here. Just type the following in a comment on your pull request, and replace "Your question here" with whatever you want to ask:

@codeant-ai ask: Your question here

This lets you have a chat with CodeAnt AI about your pull request, making it easier to understand and improve your code.

Example

@codeant-ai ask: Can you suggest a safer alternative to storing this secret?

Preserve Org Learnings with CodeAnt

You can record team preferences so CodeAnt AI applies them in future reviews. Reply directly to the specific CodeAnt AI suggestion (in the same thread) and replace "Your feedback here" with your input:

@codeant-ai: Your feedback here

This helps CodeAnt AI learn and adapt to your team's coding style and standards.

Example

@codeant-ai: Do not flag unused imports.

Retrigger review

Ask CodeAnt AI to review the PR again, by typing:

@codeant-ai: review

Check Your Repository Health

To analyze the health of your code repository, visit our dashboard at https://app.codeant.ai. This tool helps you identify potential issues and areas for improvement in your codebase, ensuring your repository maintains high standards of code health.

@codeant-ai
Copy link
Copy Markdown

codeant-ai Bot commented Mar 14, 2026

CodeAnt AI is reviewing your PR.


Thanks for using CodeAnt! 🎉

We're free for open-source projects. if you're enjoying it, help us grow by sharing.

Share on X ·
Reddit ·
LinkedIn

Copy link
Copy Markdown

@greptile-apps greptile-apps Bot left a comment

Choose a reason for hiding this comment

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

Your free trial has ended. If you'd like to continue receiving code reviews, you can add a payment method here.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 14, 2026

Warning

Rate limit exceeded

@deepsource-autofix[bot] has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 20 minutes and 33 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: d1b45bbb-f79d-46c9-b0da-4adb242a35be

📥 Commits

Reviewing files that changed from the base of the PR and between ddb27a9 and 8e49f90.

📒 Files selected for processing (82)
  • model_capability_check.py
  • neurorift/__init__.py
  • neurorift/channels/__init__.py
  • neurorift/channels/channel_router.py
  • neurorift/channels/cli_channel.py
  • neurorift/channels/discord_channel.py
  • neurorift/channels/telegram_channel.py
  • neurorift/channels/web_channel.py
  • neurorift/clawhub/__init__.py
  • neurorift/clawhub/clawhub_api.py
  • neurorift/clawhub/clawhub_client.py
  • neurorift/clawhub/clawhub_resolver.py
  • neurorift/cli/__init__.py
  • neurorift/cli/neurorift_cli.py
  • neurorift/config/__init__.py
  • neurorift/config/channel_config.py
  • neurorift/config/model_config.py
  • neurorift/config/settings.py
  • neurorift/core/__init__.py
  • neurorift/core/agent_loop.py
  • neurorift/core/agent_manager.py
  • neurorift/core/planner.py
  • neurorift/core/task_router.py
  • neurorift/execution/__init__.py
  • neurorift/execution/command_runner.py
  • neurorift/execution/resource_limits.py
  • neurorift/execution/retry_manager.py
  • neurorift/execution/sandbox_runner.py
  • neurorift/gateway/__init__.py
  • neurorift/gateway/api_server.py
  • neurorift/gateway/auth_manager.py
  • neurorift/gateway/websocket_gateway.py
  • neurorift/memory/__init__.py
  • neurorift/memory/long_term_memory.py
  • neurorift/memory/memory_compaction.py
  • neurorift/memory/short_term_memory.py
  • neurorift/memory/vector_memory.py
  • neurorift/models/__init__.py
  • neurorift/models/context_builder.py
  • neurorift/models/model_failover.py
  • neurorift/models/model_router.py
  • neurorift/models/prompt_builder.py
  • neurorift/sessions/__init__.py
  • neurorift/sessions/session_context.py
  • neurorift/sessions/session_manager.py
  • neurorift/sessions/session_pruner.py
  • neurorift/sessions/session_store.py
  • neurorift/skill_store/__init__.py
  • neurorift/skill_store/cache/__init__.py
  • neurorift/skill_store/installed/__init__.py
  • neurorift/skill_store/installed/recon_scanner/README.md
  • neurorift/skill_store/installed/recon_scanner/__init__.py
  • neurorift/skill_store/installed/recon_scanner/requirements.txt
  • neurorift/skill_store/installed/recon_scanner/skill.json
  • neurorift/skill_store/installed/recon_scanner/skill.py
  • neurorift/skills/__init__.py
  • neurorift/skills/installer.py
  • neurorift/skills/loader.py
  • neurorift/skills/registry.py
  • neurorift/skills/skill_manager.py
  • neurorift/skills/skill_validator.py
  • neurorift/storage/__init__.py
  • neurorift/storage/database.py
  • neurorift/storage/logs_db.py
  • neurorift/storage/memory_db.py
  • neurorift/storage/session_db.py
  • neurorift/tools/__init__.py
  • neurorift/tools/tool_executor.py
  • neurorift/tools/tool_registry.py
  • neurorift/tools/tool_sandbox.py
  • neurorift/tools/tool_validator.py
  • neurorift/utils/__init__.py
  • neurorift/utils/logger.py
  • neurorift/utils/serializer.py
  • neurorift/utils/time_utils.py
  • neurorift/utils/validator.py
  • neurorift_cli.py
  • neurorift_launch.sh
  • neurorift_main.py
  • neurorift_wrapper.sh
  • runtime_environment_check.py
  • setup.py
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch codex/improve-code-robustness-and-error-handling-e8du09
📝 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.

This commit fixes the style issues introduced in ac3341c according to the output
from Black.

Details: #38
@codeant-ai codeant-ai Bot added the size:XXL This PR changes 1000+ lines, ignoring generated files label Mar 14, 2026
@codeant-ai
Copy link
Copy Markdown

codeant-ai Bot commented Mar 14, 2026

Sequence Diagram

This PR introduces a new global CLI flow that routes commands into either the skill management pipeline or autonomous agent startup. For agent startup, it adds runtime and model readiness checks before launching the secured execution path.

sequenceDiagram
    participant User
    participant CLI
    participant Main
    participant SkillManager
    participant RuntimeCheck
    participant ModelCheck
    participant AgentRuntime

    User->>CLI: Run neurorift command
    CLI->>Main: Forward parsed arguments
    Main->>SkillManager: Initialize skill manager

    alt Skill command path
        Main->>SkillManager: Install list run or uninstall skill
        SkillManager-->>Main: Skill operation result
        Main-->>User: Return skill response
    else Run agent path
        Main->>RuntimeCheck: Verify runtime tools and workspace
        RuntimeCheck-->>Main: Runtime status
        Main->>ModelCheck: Verify model capabilities
        ModelCheck-->>Main: Agent readiness
        Main->>AgentRuntime: Start autonomous mode with secure execution
        AgentRuntime-->>User: Agent startup response
    end
Loading

Generated by CodeAnt AI

@deepsource-io
Copy link
Copy Markdown
Contributor

deepsource-io Bot commented Mar 14, 2026

DeepSource Code Review

We reviewed changes in ddb27a9...8e49f90 on this pull request. Below is the summary for the review, and you can see the individual issues we found as inline review comments.

See full review on DeepSource ↗

PR Report Card

Overall Grade  

Focus Area: Complexity
Security  

Reliability  

Complexity  

Hygiene  

Feedback

  • State-free methods masquerading as instance methods
    • Many methods are bound to classes without using instance state, bloating class interfaces and increasing cognitive complexity. Converting them to staticmethods or module-level helpers and grouping stateless utilities will shrink class surface and simplify reasoning.
  • Leftover refactor artifacts causing clutter
    • Unused imports, shadowed names, dead variables, and unused args point to incomplete refactors that obscure intent and hide real issues. Systematic cleanup and stricter linting for unused symbols will prevent this noise from accumulating.
  • Implicit environment trust causes security and fragility
    • The code assumes safe temp paths, partial executable resolution, and reaches into protected internals, producing insecure temp usage, fragile process launches, and encapsulation breaches. Require explicit validation, secure-temp APIs, absolute executable resolution, and respect for private APIs to break the pattern.

Code Review Summary

Analyzer Status Updated (UTC) Details
Python Mar 14, 2026 12:52p.m. Review ↗

@codeant-ai
Copy link
Copy Markdown

codeant-ai Bot commented Mar 14, 2026

Nitpicks 🔍

🔒 No security issues identified
⚡ Recommended areas for review

  • Untrusted code execution
    SkillManager.run delegates to SkillLoader.run, which imports and executes arbitrary code from skill entrypoints. Without sandboxing or strict trust validation, executing installed skills is a code-execution risk.

  • Path traversal
    skill_name is concatenated into filesystem paths (used with source_dir / skill_name and self.installer.installed / skill_name) without sanitization. A crafted skill_name containing '../' or absolute paths may lead to reading/writing outside expected directories.

  • Unsafe installer write
    The installer writes directly to /usr/local/bin/neurorift using Path.write_text and then chmod. This can be subject to TOCTOU/symlink attacks and may overwrite an existing file without atomic replace checks. Also there is no check for existing file ownership, and no atomic replace semantics.

  • Input validation / symlink risk
    The code accepts an arbitrary pkg Path and blindly copies its content. If pkg contains symlinks or unexpected files, copytree may bring in files from outside the package or include symlinked sensitive paths. The installer should validate pkg is a directory under allowed roots and either refuse or sanitize symlinks.

  • Registry consistency
    The registry is updated after copying files with no transactional guard or rollback. If registry.add fails (I/O error, concurrent write), the filesystem and registry can diverge. Concurrent installs can also corrupt registry.json since writes are not atomic or locked.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ac3341c9c7

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread neurorift_cli.py

def main() -> int:
parser = build_parser()
ns = parser.parse_args()
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Forward unknown CLI flags to neurorift_main

Because setup.py switches the console entrypoint to neurorift_cli:main, this parse_args() call now gates all normal CLI invocations; options that belong to neurorift_main (for example neurorift --target example.com --orchestrated) are rejected as unrecognized before they are forwarded. In practice this breaks existing flag-based workflows unless users add a manual -- separator, so the wrapper should accept/pass through unknown args instead of hard-failing.

Useful? React with 👍 / 👎.

Comment thread neurorift_main.py
Comment on lines +252 to +254
if not stdout.strip() and not stderr.strip():
validation_passed = False
error_type = error_type or "empty_output"
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Accept empty stdout/stderr for successful commands

This logic treats any command with both output streams empty as a validation failure, even when the process exits with code 0. _run_command_secure then marks the run unsuccessful, which causes false negatives for legitimately silent commands such as go install ...; install_missing_tools will report those installs as failed even when they succeeded, breaking automated tool bootstrapping.

Useful? React with 👍 / 👎.

Comment thread model_capability_check.py
Comment on lines +28 to +32
start = raw_text.find("{")
end = raw_text.rfind("}")
if start == -1 or end == -1 or end <= start:
raise ValueError("No JSON object found in model response")
return json.loads(raw_text[start : end + 1])
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Suggestion: JSON extraction is fragile because it slices from the first { to the last } in the whole response. If the model returns a valid JSON object plus any extra brace in trailing commentary, parsing fails and the model is incorrectly rejected. Parse the first valid JSON object instead of using a greedy substring. [logic error]

Severity Level: Major ⚠️
- ❌ run-agent startup fails on otherwise usable model output.
- ⚠️ Capability check becomes brittle to minor output noise.
Suggested change
start = raw_text.find("{")
end = raw_text.rfind("}")
if start == -1 or end == -1 or end <= start:
raise ValueError("No JSON object found in model response")
return json.loads(raw_text[start : end + 1])
decoder = json.JSONDecoder()
for idx, ch in enumerate(raw_text):
if ch != "{":
continue
try:
obj, _ = decoder.raw_decode(raw_text[idx:])
except json.JSONDecodeError:
continue
if isinstance(obj, dict):
return obj
raise ValueError("No JSON object found in model response")
Steps of Reproduction ✅
1. Launch autonomous mode through real CLI path: `neurorift_cli.py:51-52` forwards args to
`neurorift_main.main()`, and parser exposes `run-agent` at `neurorift_main.py:937`.

2. Run `neurorift run-agent --model test-model` so `neurorift_main.py:1007-1018` calls
`verify_model_capabilities()` from `model_capability_check.py:35`.

3. Have `ollama run` return text containing a valid JSON object followed by commentary
with an extra `}`; parser in `_extract_json` (`model_capability_check.py:28-32`) slices
from first `{` to last `}`, creating invalid JSON.

4. `json.loads` fails, `verify_model_capabilities` returns `invalid_capability_json` at
`model_capability_check.py:59-64`, and `run-agent` is blocked by readiness gate at
`neurorift_main.py:1019-1023`.
Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** model_capability_check.py
**Line:** 28:32
**Comment:**
	*Logic Error: JSON extraction is fragile because it slices from the first `{` to the last `}` in the whole response. If the model returns a valid JSON object plus any extra brace in trailing commentary, parsing fails and the model is incorrectly rejected. Parse the first valid JSON object instead of using a greedy substring.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
👍 | 👎

Comment thread model_capability_check.py
Comment on lines +74 to +83
if not required.issubset(parsed):
return {
"ok": False,
"error": "capability_fields_missing",
"parsed": parsed,
"agent_ready": False,
}

parsed["ok"] = bool(parsed.get("agent_ready"))
return parsed
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Suggestion: Capability fields are only checked for presence, not type, so string values like "false" are treated as truthy by callers and can incorrectly allow non-ready models to pass. Enforce boolean types for all required fields and compute readiness from a real boolean. [type error]

Severity Level: Critical 🚨
- ❌ Non-ready models can pass autonomous readiness gate.
- ⚠️ Autonomous execution reliability drops with weak model capability.
Suggested change
if not required.issubset(parsed):
return {
"ok": False,
"error": "capability_fields_missing",
"parsed": parsed,
"agent_ready": False,
}
parsed["ok"] = bool(parsed.get("agent_ready"))
return parsed
if not required.issubset(parsed):
return {
"ok": False,
"error": "capability_fields_missing",
"parsed": parsed,
"agent_ready": False,
}
if not all(isinstance(parsed.get(field), bool) for field in required):
return {
"ok": False,
"error": "capability_fields_invalid_type",
"parsed": parsed,
"agent_ready": False,
}
parsed["ok"] = parsed["agent_ready"]
return parsed
Steps of Reproduction ✅
1. Trigger autonomous startup via `neurorift_cli.py:51-52` into `run-agent` path in
`neurorift_main.py:1007`.

2. Execute with a model output where capability fields are strings (e.g., `"agent_ready":
"false"`), which is realistic for LLM formatting drift; `verify_model_capabilities` parses
it at `model_capability_check.py:57-58`.

3. Current code only checks key presence (`model_capability_check.py:74`) and then
computes `ok` using `bool(parsed.get("agent_ready"))` (`model_capability_check.py:82`), so
`"false"` is treated truthy.

4. Caller checks raw `capability.get("agent_ready")` at `neurorift_main.py:1019`;
non-empty string bypasses rejection, allowing autonomous run with a non-ready model.
Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** model_capability_check.py
**Line:** 74:83
**Comment:**
	*Type Error: Capability fields are only checked for presence, not type, so string values like `"false"` are treated as truthy by callers and can incorrectly allow non-ready models to pass. Enforce boolean types for all required fields and compute readiness from a real boolean.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
👍 | 👎

Comment thread neurorift/channels/telegram_channel.py Outdated
@@ -0,0 +1,5 @@
class Channel:
def connect(self): return True
def receive_message(self, payload=None): return payload or {}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Suggestion: Using payload or {} incorrectly rewrites valid falsy payloads (such as "", 0, [], or False) into an empty dict, which can break message handling and type expectations at runtime. Only substitute {} when the payload is actually None. [logic error]

Severity Level: Major ⚠️
- ⚠️ Channel receive path drops valid falsy message content.
- ⚠️ Telegram adapter returns wrong type for empty payload.
- ⚠️ Same bug duplicated in CLI/Discord/Web channels.
Suggested change
def receive_message(self, payload=None): return payload or {}
def receive_message(self, payload=None): return {} if payload is None else payload
Steps of Reproduction ✅
1. Open `neurorift/channels/telegram_channel.py:1-4` and instantiate `Channel` (class
defined at line 1), then call `receive_message("")` on it.

2. Execution reaches `receive_message` at `neurorift/channels/telegram_channel.py:3`,
which evaluates `payload or {}`.

3. Because `""` is falsy, method returns `{}` instead of the original payload, changing
both value and type.

4. This is reproducible in current code and is not isolated: identical logic exists in
`neurorift/channels/cli_channel.py:3`, `discord_channel.py:3`, and `web_channel.py:3`
(verified by search), so the same payload-rewrite behavior appears across all channel
stubs.
Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** neurorift/channels/telegram_channel.py
**Line:** 3:3
**Comment:**
	*Logic Error: Using `payload or {}` incorrectly rewrites valid falsy payloads (such as `""`, `0`, `[]`, or `False`) into an empty dict, which can break message handling and type expectations at runtime. Only substitute `{}` when the payload is actually `None`.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
👍 | 👎

class ClawHubClient:
def __init__(self, cache_dir: Path): self.cache_dir=cache_dir; cache_dir.mkdir(parents=True, exist_ok=True)
def fetch_skill(self, skill_name: str, source_dir: Path) -> Path:
src = source_dir / skill_name
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Suggestion: skill_name is directly joined into the source path without containment checks, so values like ../... can escape the intended skill directory and copy arbitrary local folders. Resolve the path and verify it still stays under source_dir before using it. [security]

Severity Level: Critical 🚨
- ⚠️ ClawHub install reads directories outside skill_store.
- ❌ Unauthorized local folder contents copied into cache.
Suggested change
src = source_dir / skill_name
src = (source_dir / skill_name).resolve()
if src.parent != source_dir.resolve():
raise ValueError(f"Invalid skill name: {skill_name}")
Steps of Reproduction ✅
1. Run CLI install flow with user-controlled argument: `python3 neurorift_main.py
--clawhub ../../..`; `--clawhub` is accepted at `neurorift_main.py:914` and executed at
`neurorift_main.py:983`.

2. Call chain is `main()` (`neurorift_main.py:1472`) → `_async_main()`
(`neurorift_main.py:973`) → `SkillManager.install_clawhub()`
(`neurorift/skills/skill_manager.py:13-14`) → `ClawHubClient.fetch_skill()`
(`neurorift/clawhub/clawhub_client.py:5-6`).

3. At `clawhub_client.py:6`, `src = source_dir / skill_name` allows `..` traversal, so
`src` can resolve outside `self.examples` (`skill_manager.py:12`) without any containment
check.

4. `shutil.copytree(src, dst)` at `clawhub_client.py:10` then copies an out-of-scope local
directory into cache before validation, proving source-directory escape.
Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** neurorift/clawhub/clawhub_client.py
**Line:** 6:6
**Comment:**
	*Security: `skill_name` is directly joined into the source path without containment checks, so values like `../...` can escape the intended skill directory and copy arbitrary local folders. Resolve the path and verify it still stays under `source_dir` before using it.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
👍 | 👎

Comment thread neurorift/clawhub/clawhub_client.py Outdated
def __init__(self, cache_dir: Path): self.cache_dir=cache_dir; cache_dir.mkdir(parents=True, exist_ok=True)
def fetch_skill(self, skill_name: str, source_dir: Path) -> Path:
src = source_dir / skill_name
if not src.exists(): raise FileNotFoundError(skill_name)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Suggestion: Checking only exists() lets regular files pass, and then shutil.copytree raises NotADirectoryError at runtime. Validate that the source is a directory before copying. [logic error]

Severity Level: Major ⚠️
-`--clawhub` command crashes on file-name input.
- ⚠️ Skill install UX returns traceback, not clean error.
Suggested change
if not src.exists(): raise FileNotFoundError(skill_name)
if not src.is_dir(): raise FileNotFoundError(skill_name)
Steps of Reproduction ✅
1. Confirm source store contains a file:
`/workspace/NeuroRift/neurorift/skill_store/installed/__init__.py` (listed by `LS`), not
only skill directories.

2. Run `python3 neurorift_main.py --clawhub __init__.py`; this reaches
`skill_manager.install_clawhub()` at `neurorift/skills/skill_manager.py:13-14`.

3. In `fetch_skill()` (`neurorift/clawhub/clawhub_client.py:7`), `src.exists()` passes for
that file, so no early rejection occurs.

4. `shutil.copytree(src, dst)` at `clawhub_client.py:10` raises `NotADirectoryError`;
there is no surrounding handler in `_async_main`/`main` (`neurorift_main.py:973`, `1472`,
`1521`), so CLI run fails with traceback.
Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** neurorift/clawhub/clawhub_client.py
**Line:** 7:7
**Comment:**
	*Logic Error: Checking only `exists()` lets regular files pass, and then `shutil.copytree` raises `NotADirectoryError` at runtime. Validate that the source is a directory before copying.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
👍 | 👎

Comment thread neurorift/skills/loader.py Outdated
class SkillLoader:
def run(self, skill_dir: Path, **kwargs):
meta=json.loads((skill_dir/'skill.json').read_text(encoding='utf-8'))
entry=skill_dir/meta['entrypoint']
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Suggestion: Accessing required metadata keys with direct indexing will raise a runtime exception when a skill package omits entrypoint, which crashes skill execution instead of returning a controlled error response. [logic error]

Severity Level: Major ⚠️
-`--skill run` crashes on incomplete `skill.json`.
- ⚠️ Skill CLI reliability depends on perfect metadata.
Suggested change
entry=skill_dir/meta['entrypoint']
entrypoint = meta.get('entrypoint')
if not entrypoint:
return {'error': 'missing entrypoint in skill.json'}
entry = skill_dir / entrypoint
Steps of Reproduction ✅
1. Use CLI skill flow in `neurorift_main.py` (`_async_main`, lines 91-99 from Grep output)
by running `python3 neurorift_main.py --skill run recon_scanner`; this path calls
`SkillManager.run()` at `neurorift/skills/skill_manager.py:17-18`.

2. Ensure installed skill metadata is incomplete by removing `entrypoint` from
`~/.neurorift/skills/installed/recon_scanner/skill.json` (validator only checks file
presence at `neurorift/skills/skill_validator.py:3-5`, not required JSON keys).

3. Re-run `--skill run recon_scanner`; execution reaches `SkillLoader.run()` at
`neurorift/skills/loader.py:4-6`.

4. Observe crash: `meta['entrypoint']` at `loader.py:6` raises `KeyError`, and
`_async_main` has no try/except around `skill_manager.run` branch, so skill execution
terminates.
Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** neurorift/skills/loader.py
**Line:** 6:6
**Comment:**
	*Logic Error: Accessing required metadata keys with direct indexing will raise a runtime exception when a skill package omits `entrypoint`, which crashes skill execution instead of returning a controlled error response.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
👍 | 👎

Comment thread neurorift/skills/loader.py Outdated
meta=json.loads((skill_dir/'skill.json').read_text(encoding='utf-8'))
entry=skill_dir/meta['entrypoint']
spec=importlib.util.spec_from_file_location(f"skill_{meta['name']}", entry)
mod=importlib.util.module_from_spec(spec); spec.loader.exec_module(mod)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Suggestion: The import spec and loader are dereferenced without checking for None; invalid module specs will raise runtime exceptions during module loading. [null pointer]

Severity Level: Major ⚠️
- ❌ Invalid entrypoint crashes skill execution flow.
- ⚠️ Users receive traceback instead of structured error JSON.
Suggested change
mod=importlib.util.module_from_spec(spec); spec.loader.exec_module(mod)
if spec is None or spec.loader is None:
return {'error': 'invalid skill entrypoint module'}
mod = importlib.util.module_from_spec(spec)
spec.loader.exec_module(mod)
Steps of Reproduction ✅
1. Run skill path `--skill run <name>` via `_async_main` (`neurorift_main.py:91-99`),
which always calls `SkillLoader.run` through `SkillManager.run`
(`neurorift/skills/skill_manager.py:18`).

2. Set skill `entrypoint` in `skill.json` to a directory-like target (for example `"."`),
producing a non-loadable module location.

3. At `neurorift/skills/loader.py:7`, `spec_from_file_location(...)` returns `None` for
directory paths (verified in runtime check), but code immediately continues.

4. `module_from_spec(spec)` at `loader.py:8` raises `AttributeError` (`NoneType` loader),
causing unhandled crash instead of controlled error.
Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** neurorift/skills/loader.py
**Line:** 8:8
**Comment:**
	*Null Pointer: The import spec and loader are dereferenced without checking for `None`; invalid module specs will raise runtime exceptions during module loading.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
👍 | 👎

Comment thread neurorift/skills/loader.py Outdated
entry=skill_dir/meta['entrypoint']
spec=importlib.util.spec_from_file_location(f"skill_{meta['name']}", entry)
mod=importlib.util.module_from_spec(spec); spec.loader.exec_module(mod)
return mod.run(**kwargs) if hasattr(mod, 'run') else {'error':'missing run()'}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Suggestion: Checking only attribute existence is insufficient because run may exist but be non-callable, which raises a TypeError when invoked. [type error]

Severity Level: Major ⚠️
- ❌ Broken skills crash `--skill run` command.
- ⚠️ No graceful validation for malformed skill modules.
Suggested change
return mod.run(**kwargs) if hasattr(mod, 'run') else {'error':'missing run()'}
run_fn = getattr(mod, 'run', None)
return run_fn(**kwargs) if callable(run_fn) else {'error': 'missing callable run()'}
Steps of Reproduction ✅
1. Execute skill command path `python3 neurorift_main.py --skill run recon_scanner`
(`neurorift_main.py:91-99`), which calls `SkillLoader.run` via `SkillManager.run`
(`neurorift/skills/skill_manager.py:18`).

2. Make skill module define non-callable `run`, e.g., `run = "not_callable"` in installed
`skill.py`.

3. Loader condition at `neurorift/skills/loader.py:9` passes because `hasattr(mod, 'run')`
is true.

4. Call `mod.run(**kwargs)` throws `TypeError: 'str' object is not callable`, crashing CLI
skill invocation.
Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** neurorift/skills/loader.py
**Line:** 9:9
**Comment:**
	*Type Error: Checking only attribute existence is insufficient because `run` may exist but be non-callable, which raises a `TypeError` when invoked.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
👍 | 👎

Comment on lines +13 to +15
def install_clawhub(self, skill_name: str):
pkg=self.client.fetch_skill(skill_name, self.examples)
return self.installer.install(pkg)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Suggestion: install_clawhub calls fetch_skill directly and lets FileNotFoundError bubble up. When a user requests a non-existent skill, the CLI flow will crash instead of returning a structured failure result. Catch this specific exception and return an error payload. [possible bug]

Severity Level: Major ⚠️
-`--clawhub` install flow terminates on missing skill names.
- ⚠️ User gets traceback, not structured install error JSON.
Suggested change
def install_clawhub(self, skill_name: str):
pkg=self.client.fetch_skill(skill_name, self.examples)
return self.installer.install(pkg)
def install_clawhub(self, skill_name: str):
try:
pkg = self.client.fetch_skill(skill_name, self.examples)
except FileNotFoundError:
return {"success": False, "error": f"skill_not_found:{skill_name}"}
return self.installer.install(pkg)
Steps of Reproduction ✅
1. Run CLI entrypoint with an unavailable skill name, e.g. `python3 neurorift_main.py
--clawhub does_not_exist`; this path is wired by `--clawhub` argument in
`neurorift_main.py:914`.

2. In `_async_main`, `args.clawhub` dispatches to
`skill_manager.install_clawhub(args.clawhub.strip())` at `neurorift_main.py:983`.

3. `SkillManager.install_clawhub()` calls `ClawHubClient.fetch_skill()` directly at
`neurorift/skills/skill_manager.py:14`; `fetch_skill()` raises `FileNotFoundError` when
source directory is missing at `neurorift/clawhub/clawhub_client.py:7`.

4. No local exception handling exists in `install_clawhub()` or `_async_main` around that
call, so execution aborts before printing the intended failure branch
(`result.get("success")` block at `neurorift_main.py:984-988`).
Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** neurorift/skills/skill_manager.py
**Line:** 13:15
**Comment:**
	*Possible Bug: `install_clawhub` calls `fetch_skill` directly and lets `FileNotFoundError` bubble up. When a user requests a non-existent skill, the CLI flow will crash instead of returning a structured failure result. Catch this specific exception and return an error payload.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
👍 | 👎

Comment on lines +17 to +18
def run(self, skill_name: str, **kwargs):
return self.loader.run(self.installer.installed / skill_name, **kwargs)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Suggestion: run forwards execution to the loader without verifying the skill directory exists, so invoking an unknown skill raises file access errors from deep inside the loader. Add an existence check and return a controlled error response. [possible bug]

Severity Level: Major ⚠️
-`--skill run` crashes for uninstalled skill names.
- ⚠️ Skill execution CLI lacks graceful not-installed response.
Suggested change
def run(self, skill_name: str, **kwargs):
return self.loader.run(self.installer.installed / skill_name, **kwargs)
def run(self, skill_name: str, **kwargs):
skill_dir = self.installer.installed / skill_name
if not skill_dir.is_dir():
return {"error": f"skill_not_installed:{skill_name}"}
return self.loader.run(skill_dir, **kwargs)
Steps of Reproduction ✅
1. Execute `python3 neurorift_main.py --skill run missing_skill`; `--skill` command is
defined in `neurorift_main.py:915`.

2. `_async_main` routes `run` action to `skill_manager.run(skill_name, ...)` at
`neurorift_main.py:995-998`.

3. `SkillManager.run()` forwards directly to loader with computed path at
`neurorift/skills/skill_manager.py:18`, without checking existence.

4. `SkillLoader.run()` immediately reads `skill.json` from that directory at
`neurorift/skills/loader.py:5`; for unknown skill this raises file-access exception and
aborts CLI before JSON output line at `neurorift_main.py:998`.
Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** neurorift/skills/skill_manager.py
**Line:** 17:18
**Comment:**
	*Possible Bug: `run` forwards execution to the loader without verifying the skill directory exists, so invoking an unknown skill raises file access errors from deep inside the loader. Add an existence check and return a controlled error response.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
👍 | 👎

@codeant-ai
Copy link
Copy Markdown

codeant-ai Bot commented Mar 14, 2026

CodeAnt AI finished reviewing your PR.

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

Labels

codex size:XXL This PR changes 1000+ lines, ignoring generated files

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant