Skip to content

feat(cli): add ironclaw channels list subcommand#933

Merged
zmanian merged 1 commit intonearai:stagingfrom
reidliu41:feat/channels-cli
Mar 12, 2026
Merged

feat(cli): add ironclaw channels list subcommand#933
zmanian merged 1 commit intonearai:stagingfrom
reidliu41:feat/channels-cli

Conversation

@reidliu41
Copy link
Copy Markdown
Contributor

Summary

  • Add channels list with --verbose and --json flags to show all configured built-in and WASM channels with their enabled state
  • WASM discovery scans top-level *.wasm files, matching the real loader's load_from_dir() behavior
  • Respect global --config flag via Config::from_env_with_toml()
  • Document why enable/disable/status are deferred: ChannelsConfig::resolve() reads env vars only, settings.channels.* is not consumed, so toggling via settings would silently fail; runtime status needs IPC not yet built
  • Update FEATURE_PARITY.md channels row to partial (list only)

Change Type

  • Bug fix
  • New feature
  • Refactor
  • Documentation
  • CI/Infrastructure
  • Security
  • Dependencies

Linked Issue

None

Validation

  • cargo fmt
  • cargo clippy --all --benches --tests --examples --all-features
  • Relevant tests pass:
  • Manual testing:

Security Impact

None

Database Impact

None

Blast Radius

Rollback Plan


Review track:

@github-actions github-actions bot added scope: channel/cli TUI / CLI channel scope: docs Documentation size: L 200-499 changed lines risk: low Changes to docs, tests, or low-risk modules contributor: experienced 6-19 merged PRs labels Mar 11, 2026
@gemini-code-assist
Copy link
Copy Markdown
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request introduces a new channels subcommand to the IronClaw CLI, specifically adding a list functionality. This enhancement allows users to inspect all configured messaging channels, both built-in and WASM-based, along with their current enabled status and detailed configuration. The change improves visibility into the system's communication capabilities and lays groundwork for future channel management features.

Highlights

  • New channels list subcommand: Introduced a new ironclaw channels list subcommand to display configured built-in and WASM channels, including their enabled state.
  • Detailed and JSON output: Implemented --verbose and --json flags for the channels list subcommand to provide detailed or machine-readable output.
  • WASM Channel Discovery: Added logic for discovering WASM channels by scanning top-level .wasm files in the configured directory, mirroring the loader's behavior.
  • Configuration Handling: Ensured the channels command respects the global --config flag by utilizing Config::from_env_with_toml().
  • Feature Parity Update: Updated the FEATURE_PARITY.md to reflect the partial implementation of channel management, noting that enable, disable, and status subcommands are deferred due to current configuration source limitations and lack of IPC for runtime status.
Changelog
  • FEATURE_PARITY.md
    • Updated the channels row to indicate partial implementation (list is done, others deferred).
  • src/cli/channels.rs
    • Added a new module for channel management CLI commands.
    • Defined ChannelsCommand enum with a List subcommand, including verbose and json flags.
    • Implemented run_channels_command to handle the List subcommand.
    • Created ChannelInfo struct for structured channel data.
    • Developed cmd_list function to gather and display information for built-in (cli, gateway, http, signal) and WASM channels.
    • Included discover_wasm_channels function to find .wasm files in a directory.
    • Added unit tests for discover_wasm_channels and ChannelInfo.
  • src/cli/mod.rs
    • Imported the new channels module.
    • Exported ChannelsCommand and run_channels_command.
    • Added Channels variant to the main Command enum, providing CLI help text and examples.
    • Updated the module-level documentation to include channels list.
  • src/cli/snapshots/ironclaw__cli__tests__help_output_without_import.snap
    • Updated the snapshot for the CLI's short help output to include the new channels command.
  • src/cli/snapshots/ironclaw__cli__tests__long_help_output_without_import.snap
    • Updated the snapshot for the CLI's long help output to include the new channels command.
  • src/main.rs
    • Added a match arm to async_main to dispatch Command::Channels to ironclaw::cli::run_channels_command.
Activity
  • No human activity (comments, reviews, or progress updates) has been recorded for this pull request yet.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a new channels list subcommand to the CLI, which is a great first step for channel management. However, a potential security vulnerability exists where sensitive information, specifically the Signal channel's URL and account (phone number), is printed in verbose output without redaction, which could lead to the leakage of credentials or PII. This is inconsistent with existing redaction logic and violates the principle of preventing sensitive information leakage in tool output. Additionally, there are opportunities to improve code clarity and robustness by reducing code duplication, enhancing JSON serialization logic, and adding more informative error handling when scanning for WASM channels.

Comment on lines +116 to +117
("http_url", sig.http_url.clone()),
("account", sig.account.clone()),
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.

security-medium medium

The channels list --verbose command prints the Signal http_url and account (phone number) directly to stdout. The http_url may contain sensitive credentials if configured with basic auth, and the account is Personally Identifiable Information (PII). In src/main.rs, the URL is redacted before being logged, indicating it is considered sensitive. Printing these details in the CLI output can lead to accidental leakage of credentials or PII if the output is shared (e.g., in bug reports or logs).

References
  1. Tool output should be managed to prevent excessive memory/bandwidth usage and reduce the risk of leaking sensitive information.

Comment on lines +76 to +90
if let Some(ref gw) = config.gateway {
channels.push(ChannelInfo {
name: "gateway".to_string(),
kind: "built-in",
enabled: true,
details: vec![("host", gw.host.clone()), ("port", gw.port.to_string())],
});
} else {
channels.push(ChannelInfo {
name: "gateway".to_string(),
kind: "built-in",
enabled: false,
details: vec![],
});
}
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.

medium

The logic for creating ChannelInfo for optional channels like gateway, http, and signal is duplicated. This can be refactored to reduce repetition and improve maintainability. By determining the enabled status and details within the if let block, you can use a single channels.push call.

    let (enabled, details) = if let Some(ref gw) = config.gateway {
        (true, vec![("host", gw.host.clone()), ("port", gw.port.to_string())])
    } else {
        (false, vec![])
    };
    channels.push(ChannelInfo {
        name: "gateway".to_string(),
        kind: "built-in",
        enabled,
        details,
    });

Comment on lines +150 to +168
let entries: Vec<serde_json::Value> = channels
.iter()
.map(|ch| {
let mut v = serde_json::json!({
"name": ch.name,
"kind": ch.kind,
"enabled": ch.enabled,
});
if verbose {
let details: serde_json::Map<String, serde_json::Value> = ch
.details
.iter()
.map(|(k, v)| (k.to_string(), serde_json::Value::String(v.clone())))
.collect();
v["details"] = serde_json::Value::Object(details);
}
v
})
.collect();
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.

medium

Manually constructing serde_json::Value can be verbose and less type-safe. Consider defining a dedicated serializable struct for the JSON output. This improves readability and maintainability. You can use #[serde(skip_serializing_if = "Option::is_none")] to conditionally include the details field for verbose output.

For example:

#[derive(serde::Serialize)]
struct JsonChannel<'a> {
    name: &'a str,
    kind: &'a str,
    enabled: bool,
    #[serde(skip_serializing_if = "Option::is_none")]
    details: Option<std::collections::HashMap<&'a str, &'a str>>,
}

Then you can map your ChannelInfo instances to this struct and serialize the collection.

Comment on lines +226 to +229
let mut entries = match tokio::fs::read_dir(dir).await {
Ok(entries) => entries,
Err(_) => return names,
};
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.

medium

The read_dir error is currently ignored, which means the function will silently return an empty list for any error, including permission issues. It's better to log a warning for errors other than NotFound to provide more feedback to the user during debugging.

    let mut entries = match tokio::fs::read_dir(dir).await {
        Ok(entries) => entries,
        Err(e) => {
            if e.kind() != std::io::ErrorKind::NotFound {
                eprintln!(
                    "Warning: Could not read WASM channels directory '{}': {}",
                    dir.display(),
                    e
                );
            }
            return names;
        }
    };

@reidliu41
Copy link
Copy Markdown
Contributor Author

Part of #83

@reidliu41
Copy link
Copy Markdown
Contributor Author

Could you review this when you get a chance? @zmanian

Copy link
Copy Markdown
Collaborator

@zmanian zmanian left a comment

Choose a reason for hiding this comment

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

Review: Add ironclaw channels list subcommand

Well-structured CLI addition that follows established patterns in the codebase.

Positives:

  • Thorough module-level documentation explaining why enable/disable/status are deferred (config source split, no IPC control plane) -- this is the right call
  • WASM channel discovery matches the real loader's load_from_dir() behavior (top-level *.wasm only)
  • --verbose and --json flags consistent with other CLI subcommands
  • Good test coverage for WASM discovery edge cases (missing dir, non-wasm files, subdirectories)

Minor note:

  • This PR and #918 (skills CLI) both modify src/cli/mod.rs and snapshot files. Whichever merges second will need a conflict resolution pass. Not blocking.

LGTM.

@reidliu41
Copy link
Copy Markdown
Contributor Author

If ready to merge, you merge one of them first. and then I will fix another one.

  - Add channels list with --verbose and --json flags to show all
    configured built-in and WASM channels with their enabled state
  - WASM discovery scans top-level *.wasm files, matching the real
    loader's load_from_dir() behavior
  - Respect global --config flag via Config::from_env_with_toml()
  - Document why enable/disable/status are deferred: ChannelsConfig::resolve()
    reads env vars only, settings.channels.* is not consumed, so toggling
    via settings would silently fail; runtime status needs IPC not yet built
  - Update FEATURE_PARITY.md channels row to partial (list only)
@reidliu41
Copy link
Copy Markdown
Contributor Author

No conflict.

@zmanian zmanian merged commit 5d9d17b into nearai:staging Mar 12, 2026
10 checks passed
@ironclaw-ci ironclaw-ci bot mentioned this pull request Mar 12, 2026
bkutasi pushed a commit to bkutasi/ironclaw that referenced this pull request Mar 28, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

contributor: experienced 6-19 merged PRs risk: low Changes to docs, tests, or low-risk modules scope: channel/cli TUI / CLI channel scope: docs Documentation size: L 200-499 changed lines

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants