Skip to content

[supersede #1968] [supersede #1853] feat(tools): add Feishu document operation tool with 13 actions#1986

Merged
chumyin merged 1 commit into
mainfrom
supersede-pr-1968-20260226164943-141738-theirs
Mar 1, 2026
Merged

[supersede #1968] [supersede #1853] feat(tools): add Feishu document operation tool with 13 actions#1986
chumyin merged 1 commit into
mainfrom
supersede-pr-1968-20260226164943-141738-theirs

Conversation

@chumyin
Copy link
Copy Markdown
Contributor

@chumyin chumyin commented Feb 26, 2026

Summary

Problem

Current PR #1986 failed to merge due branch drift and repeated merge conflicts against the latest integration branch.

Why It Matters

This PR introduces a Feishu document tool with 13 actions. Keeping it blocked delays channel-lark feature delivery and leaves duplicate supersede tracks unresolved.

What Changed

  • Rebased supersede-pr-1968-20260226164943-141738-theirs onto latest origin/dev.
  • Resolved tool-registry conflicts in src/tools/mod.rs while preserving Feishu tool registration.
  • Kept the Feishu implementation + review-fix commit stack intact (src/tools/feishu_doc.rs, src/main.rs, tests/agent_e2e.rs).
  • Dropped the transient lockfile-only replay commit after moving to dev baseline (not needed on dev).

Change Metadata

  • Change type: feature + rebase/conflict-resolution
  • Primary scope: tools, channel-lark
  • PR type: supersede continuation

Linked Issue

Supersede Attribution

Validation Evidence

Commands

  • cargo check --locked --features channel-lark
  • cargo check --features channel-lark

Results

  • Both commands pass locally on the rebased branch.

Security Impact

Risk

  • Moderate: introduces external API write operations for Feishu docs/files/images.

Mitigation

  • Tool remains behind channel-lark feature path and existing security policy gates.
  • Uses existing credential config path (channels_config.feishu / channels_config.lark) and keeps explicit operation routing.

Privacy and Data Hygiene

  • No new telemetry sink introduced.
  • Tool operates on caller-supplied document/file payloads only.
  • No additional persistent secrets beyond existing configured app credentials.

Compatibility

  • No config schema migration required.
  • Non-channel-lark builds remain unaffected.

Rollback Plan

  • Revert this PR commit stack from supersede-pr-1968-20260226164943-141738-theirs.
  • If needed, disable by excluding the channel-lark feature path.

Human Verification

  • Confirmed PR branch is conflict-free and mergeable after rebase to dev.
  • Confirmed local compile checks pass with --locked.

Summary by CodeRabbit

  • Documentation
    • Added feishu_doc tool documentation detailing support for Feishu/Lark document operations including read, write, append, create, block management, table operations, and file uploads.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Feb 26, 2026

Note

.coderabbit.yaml has unrecognized properties

CodeRabbit is using all valid settings from your configuration. Unrecognized properties (listed below) have been ignored and may indicate typos or deprecated fields that can be removed.

⚠️ Parsing warnings (1)
Validation error: Unrecognized key(s) in object: 'tools', 'path_filters', 'review_instructions'
⚙️ Configuration instructions
  • Please see the configuration documentation for more information.
  • You can also validate your configuration using the online YAML validator.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8958b0d and efac154.

📒 Files selected for processing (1)
  • CHANGELOG.md
🚧 Files skipped from review as they are similar to previous changes (1)
  • CHANGELOG.md

📝 Walkthrough

Walkthrough

This pull request adds a single-line entry to the CHANGELOG documenting the addition of the feishu_doc tool, which provides operations for Feishu/Lark document management including read, write, append, create, and block/table manipulation capabilities.

Changes

Cohort / File(s) Summary
Documentation
CHANGELOG.md
Added feishu_doc tool entry under Unreleased -> Added section, listing all supported operations (read, write, append, create, list_blocks, get_block, update_block, delete_block, create_table, write_table_cells, create_table_with_values, upload_image, upload_file).

Estimated code review effort

🎯 1 (Trivial) | ⏱️ ~3 minutes

Possibly related PRs

Suggested labels

size: XL, risk: medium, tool: feishu_doc, channel

Suggested reviewers

  • theonlyhennygod
🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description is incomplete. While it covers Summary, Change Metadata, Linked Issues, Security, and Compatibility, it is missing critical required sections: Label Snapshot (risk/size/scope/module labels), complete Validation Evidence with test outputs, complete Supersede Attribution with co-authored-by confirmation, i18n Follow-Through, Human Verification details, Side Effects/Blast Radius, and Rollback Plan specifics. Add missing required sections: Label Snapshot (risk/size/scope labels), detailed validation evidence with full test outputs, Supersede Attribution with co-author trailers, i18n follow-through status, detailed Human Verification (scenarios/edge cases), Side Effects assessment, and complete Rollback command. Ensure all required checkboxes are explicitly answered.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly specifies the primary change: adding a Feishu document operation tool with 13 actions, and includes context about superseding previous PRs.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch supersede-pr-1968-20260226164943-141738-theirs

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

@github-actions github-actions Bot added core Auto scope: root src/*.rs files changed. tool Auto scope: src/tools/** changed. tests Auto scope: tests/** changed. size: XL Auto size: >1000 non-doc changed lines. risk: high Auto risk: security/runtime/gateway/tools/workflows. distinguished contributor Contributor with 50+ merged PRs. tool: feishu_doc Auto module: tool/feishu_doc changed. and removed tool Auto scope: src/tools/** changed. labels Feb 26, 2026
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: 6

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/tools/feishu_doc.rs`:
- Around line 462-464: After parsing inputs with required_usize for row_size and
column_size (and parsing column_width with parse_column_width), validate that
row_size and column_size are > 0 and return a structured ToolResult error
instead of allowing 0 to proceed; update the handler that calls required_usize
(the scope where row_size, column_size, column_width are read) to check these
values and return an appropriate ToolResult error (with a clear message) when
either is 0, ensuring no panic or API call happens with zero dimensions.
- Around line 857-872: The function enable_link_share is currently discarding
the result of authed_request_with_query and always returning Ok(()), masking API
failures; change the call to await and propagate errors (e.g., use the `?`
operator on the result of self.authed_request_with_query(...).await) or
explicitly map Err into an anyhow::Error and return it so that failures from the
PATCH request (Method::PATCH) are not swallowed; locate enable_link_share and
replace the ignored assignment of the request result with proper error handling
of authed_request_with_query.
- Around line 978-983: The no-redirect HTTP client (no_redirect_client) is
created without a timeout so .send().await, resp.text().await, and the chunked
stream can hang; update the Client::builder() that constructs no_redirect_client
to set an explicit timeout (e.g., via
.timeout(std::time::Duration::from_secs(...)) or a configurable value) so the
overall request+body operations will fail fast on slow/stalled endpoints, and
propagate/map the timeout error consistently where .send().await,
resp.text().await, and the chunked streaming code handle errors.

In `@src/tools/mod.rs`:
- Around line 527-559: The FeishuDocTool is being instantiated
(FeishuDocTool::new) but not exposed in the agent's tool descriptions; update
the tool_descs vector in agent/loop_.rs to include an entry for "feishu_doc"
with a short description (e.g., ("feishu_doc", "Feishu/Lark document tool for
reading and writing docs")) so the agent can advertise and use the tool, and add
a brief "Added — feishu_doc: Feishu/Lark document tool" bullet under the
[Unreleased] section of CHANGELOG.md to document the new tool.

In `@tests/agent_e2e.rs`:
- Around line 870-916: The research-phase tests are too weak:
e2e_agent_research_phase_integration builds an Agent with ResearchPhaseConfig
and ResearchTrigger::Keywords but never sends a keyword-containing message, and
the Always-trigger test similarly lacks assertions that research actually ran;
update the tests to send at least one additional turn that contains a trigger
keyword (e.g., "please search for X" or "find details about Y") using
agent.turn(...) so the research path executes, then inspect the
RecordingProvider via recorded.lock() to assert an extra provider call occurred
and that the captured user message includes the research context/prefix (check
for "[Research" or whatever prefix your code uses) and that the number of
requests increased accordingly; likewise, enhance the Always-trigger test to
assert research context injection and increased provider invocations when
ResearchPhaseConfig.trigger is set to Always.
- Around line 850-853: The Err(e) arm in the research-phase match currently only
logs and continues; change it so only explicit auth/env-missing errors are
treated as soft-skips and all other errors cause the test to fail: in the Err(e)
branch (the match handling the research phase error) inspect the error (by type
or by checking e.to_string() for known markers like "credentials", "auth", or
"environment variable") and if it matches an auth/env-missing case print a
soft-skip message and return/continue; otherwise propagate the failure (panic!,
return Err(...), or assert) so the test fails and surfaces unexpected
regressions.

ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c54a30f and 9064959.

📒 Files selected for processing (4)
  • src/main.rs
  • src/tools/feishu_doc.rs
  • src/tools/mod.rs
  • tests/agent_e2e.rs

Comment thread src/tools/feishu_doc.rs
Comment thread src/tools/feishu_doc.rs
Comment thread src/tools/feishu_doc.rs
Comment thread src/tools/mod.rs
Comment thread tests/agent_e2e.rs
Comment thread tests/agent_e2e.rs
@theonlyhennygod
Copy link
Copy Markdown
Collaborator

Maintainer scan (2026-02-27):

  • Branch refresh attempt against main: blocked by merge conflicts.
  • Current merge gate: not merge-ready.
  • Required CI gates are failing (CI Required Gate, plus failing matrix/lint/build lanes).

Next actions to unblock merge:

  1. Rebase this branch onto current main and resolve conflicts.
  2. Re-run CI until required gates are green.
  3. I will re-run rebase-merge immediately after the branch is conflict-free and checks pass.

@chumyin chumyin force-pushed the supersede-pr-1968-20260226164943-141738-theirs branch from 9064959 to cf1e1d8 Compare February 27, 2026 10:39
@github-actions github-actions Bot added dependencies Auto scope: dependency manifest/lock/policy changed. tool Auto scope: src/tools/** changed. and removed tool Auto scope: src/tools/** changed. labels Feb 27, 2026
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.

♻️ Duplicate comments (3)
src/tools/feishu_doc.rs (3)

857-872: ⚠️ Potential issue | 🟠 Major

enable_link_share currently masks API failures.

The function ignores the request result (line 868-870) and always returns Ok(()), so the warning logic in action_create (lines 346-356) can never trigger.

🐛 Suggested fix
     async fn enable_link_share(&self, document_id: &str) -> anyhow::Result<()> {
         let url = format!(
             "{}/drive/v2/permissions/{}/public",
             self.api_base(),
             document_id
         );
         let body = json!({
             "link_share_entity": "anyone_readable",
             "external_access_entity": "open"
         });
         let query = [("type", "docx".to_string())];
-        let _ = self
-            .authed_request_with_query(Method::PATCH, &url, Some(body), Some(&query))
-            .await;
+        let _ = self
+            .authed_request_with_query(Method::PATCH, &url, Some(body), Some(&query))
+            .await?;
         Ok(())
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/tools/feishu_doc.rs` around lines 857 - 872, The enable_link_share
function currently discards the result of authed_request_with_query and always
returns Ok(()), hiding API failures; update enable_link_share to surface errors
by returning the Result from authed_request_with_query (or mapping its Err into
anyhow::Error) so callers like action_create can detect and log failures—locate
enable_link_share and replace the ignored call to
self.authed_request_with_query(...) with code that properly propagates or
converts its error instead of unconditionally returning Ok(()).

978-983: ⚠️ Potential issue | 🟠 Major

Add timeout to the no-redirect download client.

The no_redirect_client has no timeout configured, which can hang tool execution indefinitely on slow or stalled endpoints. This affects the initial .send().await, response body reads, and chunked streaming.

💡 Suggested fix
+        use std::time::Duration;
         let no_redirect_client = reqwest::Client::builder()
             .redirect(reqwest::redirect::Policy::none())
+            .connect_timeout(Duration::from_secs(10))
+            .timeout(Duration::from_secs(30))
             .build()
             .map_err(|e| anyhow::anyhow!("failed to build no-redirect HTTP client: {}", e))?;

Note: Duration is already imported at line 9, so only the builder changes are needed.

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

In `@src/tools/feishu_doc.rs` around lines 978 - 983, The no-redirect reqwest
client (created in no_redirect_client via reqwest::Client::builder() with
redirect Policy::none()) lacks a timeout; update the builder to set a sensible
timeout (e.g., .timeout(Duration::from_secs(10)) or another app-appropriate
Duration) so requests, response body reads, and streaming cannot hang
indefinitely—keep the rest of the builder and the subsequent
.build().map_err(...) logic unchanged (Duration is already imported).

462-464: ⚠️ Potential issue | 🟡 Minor

Reject zero-sized table dimensions before calling the API.

row_size/column_size accept 0, which should fail fast at input validation time rather than sending an invalid request to the API.

💡 Suggested fix
         let row_size = required_usize(args, "row_size")?;
         let column_size = required_usize(args, "column_size")?;
+        if row_size == 0 || column_size == 0 {
+            anyhow::bail!("'row_size' and 'column_size' must be greater than 0");
+        }
         let column_width = parse_column_width(args)?;

As per coding guidelines: Implement Tool with strict parameter schema; validate and sanitize all inputs.

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

In `@src/tools/feishu_doc.rs` around lines 462 - 464, After parsing args, reject
zero-sized table dimensions by validating the values returned from
required_usize for row_size and column_size (and any parsed column_width if
relevant) before proceeding to call the API: if row_size == 0 or column_size ==
0 return an Err with a clear validation message (e.g. "row_size must be > 0" /
"column_size must be > 0") from the function that constructs or runs the Tool;
update the code around the calls to required_usize("row_size"),
required_usize("column_size"), and parse_column_width to perform these checks
and fail fast instead of sending an invalid request.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@src/tools/feishu_doc.rs`:
- Around line 857-872: The enable_link_share function currently discards the
result of authed_request_with_query and always returns Ok(()), hiding API
failures; update enable_link_share to surface errors by returning the Result
from authed_request_with_query (or mapping its Err into anyhow::Error) so
callers like action_create can detect and log failures—locate enable_link_share
and replace the ignored call to self.authed_request_with_query(...) with code
that properly propagates or converts its error instead of unconditionally
returning Ok(()).
- Around line 978-983: The no-redirect reqwest client (created in
no_redirect_client via reqwest::Client::builder() with redirect Policy::none())
lacks a timeout; update the builder to set a sensible timeout (e.g.,
.timeout(Duration::from_secs(10)) or another app-appropriate Duration) so
requests, response body reads, and streaming cannot hang indefinitely—keep the
rest of the builder and the subsequent .build().map_err(...) logic unchanged
(Duration is already imported).
- Around line 462-464: After parsing args, reject zero-sized table dimensions by
validating the values returned from required_usize for row_size and column_size
(and any parsed column_width if relevant) before proceeding to call the API: if
row_size == 0 or column_size == 0 return an Err with a clear validation message
(e.g. "row_size must be > 0" / "column_size must be > 0") from the function that
constructs or runs the Tool; update the code around the calls to
required_usize("row_size"), required_usize("column_size"), and
parse_column_width to perform these checks and fail fast instead of sending an
invalid request.

ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9064959 and cf1e1d8.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (4)
  • src/main.rs
  • src/tools/feishu_doc.rs
  • src/tools/mod.rs
  • tests/agent_e2e.rs
💤 Files with no reviewable changes (1)
  • tests/agent_e2e.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/main.rs

@chumyin chumyin force-pushed the supersede-pr-1968-20260226164943-141738-theirs branch from cf1e1d8 to 8eb17b4 Compare February 27, 2026 11:05
@github-actions github-actions Bot added ci Auto scope: CI/workflow/hook files changed. docs Auto scope: docs/markdown/template files changed. agent Auto scope: src/agent/** changed. channel Auto scope: src/channels/** changed. labels Feb 27, 2026
@github-actions github-actions Bot added tool Auto scope: src/tools/** changed. and removed tool Auto scope: src/tools/** changed. labels Feb 27, 2026
@chumyin chumyin changed the base branch from dev to main February 27, 2026 11:08
@github-actions github-actions Bot added ci Auto scope: CI/workflow/hook files changed. docs Auto scope: docs/markdown/template files changed. dependencies Auto scope: dependency manifest/lock/policy changed. agent Auto scope: src/agent/** changed. channel Auto scope: src/channels/** changed. config Auto scope: src/config/** changed. cron Auto scope: src/cron/** changed. daemon Auto scope: src/daemon/** changed. gateway Auto scope: src/gateway/** changed. integration Auto scope: src/integrations/** changed. onboard Auto scope: src/onboard/** changed. tool Auto scope: src/tools/** changed. scripts Auto scope: scripts/** changed. and removed config Auto scope: src/config/** changed. cron Auto scope: src/cron/** changed. daemon Auto scope: src/daemon/** changed. gateway Auto scope: src/gateway/** changed. integration Auto scope: src/integrations/** changed. onboard Auto scope: src/onboard/** changed. labels Feb 27, 2026
Copy link
Copy Markdown
Collaborator

@theonlyhennygod theonlyhennygod left a comment

Choose a reason for hiding this comment

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

Approved per maintainer request; merging if conflict-free.

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

🧹 Nitpick comments (1)
src/agent/loop_.rs (1)

1883-1889: Keep feishu_doc prompt descriptor text consistent across both entry paths.

run and process_message currently use different descriptions for the same tool, which can cause prompt-behavior drift between CLI and channel execution paths.

♻️ Suggested consolidation
+const FEISHU_DOC_TOOL_DESC: &str =
+    "Manage Feishu/Lark documents: read, write, append, create, table operations, and media upload support.";
@@
-        tool_descs.push((
-            "feishu_doc",
-            "Manage Feishu/Lark documents: read, write, append, create, and table operations with media upload support.",
-        ));
+        tool_descs.push(("feishu_doc", FEISHU_DOC_TOOL_DESC));
@@
-        tool_descs.push((
-            "feishu_doc",
-            "Manage Feishu/Lark documents with read/write/table operations.",
-        ));
+        tool_descs.push(("feishu_doc", FEISHU_DOC_TOOL_DESC));

Also applies to: 2334-2340

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

In `@src/agent/loop_.rs` around lines 1883 - 1889, The prompt descriptor for the
"feishu_doc" tool is inconsistent between the two entry paths (the code that
builds tool_descs in run and in process_message), causing different prompts
depending on CLI vs channel execution; locate the tuples pushed into tool_descs
referencing "feishu_doc" (e.g., the push in function run and the similar push in
process_message) and replace the duplicated description strings so both use one
canonical description text (choose one clear, complete string and apply it to
both occurrences) to ensure identical prompt behavior across both paths.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/agent/loop_.rs`:
- Line 2039: The interactive editor is created with rustyline::DefaultEditor
which uses an empty helper, so SlashCommandCompleter (the helper implementing
Completer/Hinter/Highlighter/Validator/Helper) is never wired and completion is
disabled; replace DefaultEditor with the generic Editor so you can call
set_helper(...) on the rl instance and pass an instance of SlashCommandCompleter
to enable completion and hints (use Editor::<SlashCommandCompleter, _>::new() or
Editor::new() then rl.set_helper(Some(SlashCommandCompleter::new(...)))).

---

Nitpick comments:
In `@src/agent/loop_.rs`:
- Around line 1883-1889: The prompt descriptor for the "feishu_doc" tool is
inconsistent between the two entry paths (the code that builds tool_descs in run
and in process_message), causing different prompts depending on CLI vs channel
execution; locate the tuples pushed into tool_descs referencing "feishu_doc"
(e.g., the push in function run and the similar push in process_message) and
replace the duplicated description strings so both use one canonical description
text (choose one clear, complete string and apply it to both occurrences) to
ensure identical prompt behavior across both paths.

ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between cf1e1d8 and 8958b0d.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (5)
  • .github/workflows/pub-prerelease.yml
  • CHANGELOG.md
  • src/agent/loop_.rs
  • src/tools/cron_add.rs
  • tests/agent_e2e.rs
🚧 Files skipped from review as they are similar to previous changes (2)
  • .github/workflows/pub-prerelease.yml
  • src/tools/cron_add.rs

Comment thread src/agent/loop_.rs Outdated
.build(),
)?;
rl.set_helper(Some(SlashCommandCompleter));
let mut rl = rustyline::DefaultEditor::new()?;
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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# First, verify the existence and location of SlashCommandCompleter and the current DefaultEditor usage
rg -n "SlashCommandCompleter|DefaultEditor::new" src/agent/loop_.rs | head -20

Repository: zeroclaw-labs/zeroclaw

Length of output: 390


🏁 Script executed:

#!/bin/bash
# Check the context around line 2039 to see the editor initialization
sed -n '2030,2050p' src/agent/loop_.rs

Repository: zeroclaw-labs/zeroclaw

Length of output: 887


🏁 Script executed:

#!/bin/bash
# Look for the SlashCommandCompleter definition and any struct/impl blocks
rg -n "struct SlashCommandCompleter|impl.*SlashCommandCompleter" src/agent/loop_.rs

Repository: zeroclaw-labs/zeroclaw

Length of output: 330


🏁 Script executed:

#!/bin/bash
# Check git history to see if this was a recent change and what it replaced
cd src/agent && git log --oneline -n 20 -- loop_.rs

Repository: zeroclaw-labs/zeroclaw

Length of output: 140


🏁 Script executed:

#!/bin/bash
# Check if set_helper is called anywhere on the rl variable or nearby
sed -n '2030,2100p' src/agent/loop_.rs | grep -n "set_helper\|rl\."

Repository: zeroclaw-labs/zeroclaw

Length of output: 298


🏁 Script executed:

#!/bin/bash
# Check rustyline module imports to understand what DefaultEditor is
rg -n "use rustyline|extern crate rustyline" src/agent/loop_.rs | head -10

Repository: zeroclaw-labs/zeroclaw

Length of output: 304


🏁 Script executed:

#!/bin/bash
# Search for any historical usage of SlashCommandCompleter with the editor
rg -n "set_helper.*SlashCommand|Editor.*Slash" src/agent/

Repository: zeroclaw-labs/zeroclaw

Length of output: 48


🏁 Script executed:

#!/bin/bash
# Check rustyline version and understand the Editor/DefaultEditor API
rg -n "rustyline" Cargo.toml | grep -v "^Binary"

Repository: zeroclaw-labs/zeroclaw

Length of output: 88


🏁 Script executed:

#!/bin/bash
# Look for Editor usage patterns in the codebase to see if there are other examples
rg -n "Editor::<|DefaultEditor" src/ --type rs

Repository: zeroclaw-labs/zeroclaw

Length of output: 92


🏁 Script executed:

#!/bin/bash
# Check the full context of how SlashCommandCompleter is used to understand intent
sed -n '85,160p' src/agent/loop_.rs

Repository: zeroclaw-labs/zeroclaw

Length of output: 2230


🏁 Script executed:

#!/bin/bash
# Verify rustyline 17.0 API - check what DefaultEditor actually is
pip install rustyline-stubs 2>/dev/null || true
python3 <<'EOF'
# Check rustyline documentation for DefaultEditor type
print("In rustyline 17.0, DefaultEditor is a type alias for Editor::<(), DefaultHistory>")
print("This means it uses an empty () helper, so custom helpers cannot be set.")
print("\nTo use a custom helper like SlashCommandCompleter:")
print("1. Create Editor::<SlashCommandCompleter, DefaultHistory>::new()")
print("2. Call rl.set_helper(Some(SlashCommandCompleter))")
EOF

Repository: zeroclaw-labs/zeroclaw

Length of output: 465


🏁 Script executed:

#!/bin/bash
# Final verification: confirm no set_helper is called anywhere in the file
rg "set_helper" src/agent/loop_.rs

Repository: zeroclaw-labs/zeroclaw

Length of output: 48


SlashCommandCompleter is not wired to the interactive editor.

The SlashCommandCompleter struct (lines 90–149) implements all helper traits (Completer, Hinter, Highlighter, Validator, Helper), but DefaultEditor is just an alias for Editor::<(), DefaultHistory>, which uses an empty helper type and ignores custom helpers. This silently disables slash-command completion and hints at line 2039.

Wire the custom helper by using the generic Editor type and calling set_helper():

Suggested fix
-        let mut rl = rustyline::DefaultEditor::new()?;
+        let mut rl =
+            rustyline::Editor::<SlashCommandCompleter, rustyline::history::DefaultHistory>::new()?;
+        rl.set_helper(Some(SlashCommandCompleter));
📝 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
let mut rl = rustyline::DefaultEditor::new()?;
let mut rl =
rustyline::Editor::<SlashCommandCompleter, rustyline::history::DefaultHistory>::new()?;
rl.set_helper(Some(SlashCommandCompleter));
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/agent/loop_.rs` at line 2039, The interactive editor is created with
rustyline::DefaultEditor which uses an empty helper, so SlashCommandCompleter
(the helper implementing Completer/Hinter/Highlighter/Validator/Helper) is never
wired and completion is disabled; replace DefaultEditor with the generic Editor
so you can call set_helper(...) on the rl instance and pass an instance of
SlashCommandCompleter to enable completion and hints (use
Editor::<SlashCommandCompleter, _>::new() or Editor::new() then
rl.set_helper(Some(SlashCommandCompleter::new(...)))).

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Feb 28, 2026

PR intake checks found warnings (non-blocking)

Fast safe checks found advisory issues. CI lint/test/build gates still enforce merge quality.

  • Missing required PR template sections: ## Validation Evidence (required), ## Security Impact (required), ## Privacy and Data Hygiene (required), ## Rollback Plan (required)
  • Incomplete required PR template fields: summary problem, summary why it matters, summary what changed, validation commands, security risk/mitigation, privacy status, rollback plan

Action items:

  1. Complete required PR template sections/fields.
  2. Link this PR to exactly one active Linear issue key (RMN-xxx/CDV-xxx/COM-xxx).
  3. Remove tabs, trailing whitespace, and merge conflict markers from added lines.
  4. Re-run local checks before pushing:
    • ./scripts/ci/rust_quality_gate.sh
    • ./scripts/ci/rust_strict_delta_gate.sh
    • ./scripts/ci/docs_quality_gate.sh

Detected Linear keys: RMN-140

Run logs: https://github.com/zeroclaw-labs/zeroclaw/actions/runs/22532450309

Detected blocking line issues (sample):

  • none

Detected advisory line issues (sample):

  • none

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

Labels

distinguished contributor Contributor with 50+ merged PRs. docs Auto scope: docs/markdown/template files changed. risk: low Auto risk: docs/chore-only paths. size: XS Auto size: <=80 non-doc changed lines.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants