Skip to content

Rework CLI commands to be composable via piping#1522

Draft
Copilot wants to merge 23 commits intoprimefrom
copilot/rework-cli-commands-composability
Draft

Rework CLI commands to be composable via piping#1522
Copilot wants to merge 23 commits intoprimefrom
copilot/rework-cli-commands-composability

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 3, 2026

  • Add stub list command that errors with help text when executed
  • Update ListCommand error message to "Use one of the named commands listed below."
  • Add dce (renamed from dce.sh) pass-through script to CLI project
  • Add dce.bat pass-through script to CLI project
  • Include dce/dce.bat in the .csproj for copy to output/publish directories
  • Update all CLI docs command examples to use ./dce (Linux/macOS) and dce (Windows) alias
  • Fix docs: positional parameters now appear before options in all command examples (CliFx convention)
  • Add ReadLinesAsync() extension on TextReader in ConsoleExtensions.cs
  • Simplify stdin reading in ExportChannelsCommand to use ReadLinesAsync() and Snowflake.Parse(line.Trim())
  • Rename CLI command list guildslist servers; update guild-ids positional parameter to server-ids; replace all user-facing "guild(s)" references with "server(s)"
  • Docs: introduce DISCORD_TOKEN env var setup once near the top
  • Add CLI-side SnowflakeJsonConverter and CliJsonSerializerContext
  • list channels, list channels dm, and list servers always output a compact JSON array
  • export stdin reading accepts a JSON array or plain Snowflake IDs; detection now uses numeric check
  • Docs: use -short|--long convention for option names
  • Add list unwrap command; remove category unwrapping from export; list unwrap expands both category channels to their children and forum channels to their thread posts
  • Docs: add [!IMPORTANT] callouts to pipeline sections
  • Fix export stdin JSON detection to use !char.IsAsciiDigit(trimmed[0]) instead of StartsWith('[')
  • Rename internal GuildIds to ServerIds in GetChannelsCommand

Copilot AI linked an issue Apr 3, 2026 that may be closed by this pull request
Copilot AI changed the title [WIP] Rework CLI commands for better composability Rework CLI commands to be composable via piping Apr 3, 2026
Copilot AI requested a review from Tyrrrz April 3, 2026 14:47
@Tyrrrz
Copy link
Copy Markdown
Owner

Tyrrrz commented Apr 3, 2026

@copilot

Let's employ the following command structure:

  • list channels <guild-ids...> ... (allow multiple guild ids as input)
  • list channels dm
  • list guilds
  • export ...

Keep the option binding for --token but use parameter bindings for primary parameters like guild-id, channel-ids, etc.

Copy link
Copy Markdown
Contributor Author

Copilot AI commented Apr 3, 2026

@copilot

Let's employ the following command structure:
...

Done in commit 0e3b455. The commands are now:

  • list channels <guild-ids...> — accepts one or more guild IDs as positional parameters; supports --include-vc and --include-threads options
  • list channels dm — lists direct message channels
  • list guilds — lists accessible servers
  • export <channel-ids...> — channel IDs are now positional parameters; falls back to reading from stdin when none are provided (enabling piping from list channels / list channels dm)

--token and other secondary configuration remain as --option bindings. Example pipe flow:

./DiscordChatExporter.Cli list channels 123456 789012 | ./DiscordChatExporter.Cli export

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • av-build-tel-api-v1.avaloniaui.net
    • Triggering command: /usr/share/dotnet/dotnet dotnet exec --runtimeconfig /home/REDACTED/.nuget/packages/avalonia.buildservices/11.3.2/tools/netstandard2.0/runtimeconfig.json /home/REDACTED/.nuget/packages/avalonia.buildservices/11.3.2/tools/netstandard2.0/Avalonia.BuildServices.Collector.dll (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

@Tyrrrz
Copy link
Copy Markdown
Owner

Tyrrrz commented Apr 3, 2026

@copilot add a stub list command that errors with help text when executed. It should be used only to group related list commands in the help text.

@Tyrrrz
Copy link
Copy Markdown
Owner

Tyrrrz commented Apr 3, 2026

@copilot in the docs, show an example of the pipeline that starts by setting the environment variable for the token. Linux/macOS can be one-liners.

@Tyrrrz
Copy link
Copy Markdown
Owner

Tyrrrz commented Apr 3, 2026

@copilot
Add dce.sh file to the DCE.CLI project that acts as an alias/pass-through to DiscordChatExporter.Cli. Not sure if something equivalent can be done with cmd/bat but do that too if possible.

Copy link
Copy Markdown
Contributor Author

Copilot AI commented Apr 3, 2026

@copilot add a stub list command that errors with help text when executed. It should be used only to group related list commands in the help text.

Done in commit f3638ec. ListCommand.cs uses CommandException with showHelp: true, so running list directly shows the error message and then the scoped help listing the available subcommands:

A subcommand is required ('channels', 'channels dm', or 'guilds').

USAGE
  DiscordChatExporter.Cli list [options]
  DiscordChatExporter.Cli list [command] ...

DESCRIPTION
  Lists channels, DMs, or servers.

COMMANDS
  channels     Gets the list of channels in one or more servers.
  guilds       Gets the list of accessible servers.

The top-level --help also now shows list as a grouped entry with its subcommands.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • av-build-tel-api-v1.avaloniaui.net
    • Triggering command: /usr/share/dotnet/dotnet dotnet exec --runtimeconfig /home/REDACTED/.nuget/packages/avalonia.buildservices/11.3.2/tools/netstandard2.0/runtimeconfig.json /home/REDACTED/.nuget/packages/avalonia.buildservices/11.3.2/tools/netstandard2.0/Avalonia.BuildServices.Collector.dll (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

Copy link
Copy Markdown
Contributor Author

Copilot AI commented Apr 3, 2026

@copilot in the docs, show an example of the pipeline that starts by setting the environment variable for the token. Linux/macOS can be one-liners.

Done in commit 55c06ef. Pipeline sections now show Linux/macOS one-liners with DISCORD_TOKEN set inline, and Windows examples as two-step (set then pipe). For example:

Linux/macOS:

DISCORD_TOKEN="mfa.Ifrn" ./DiscordChatExporter.Cli list channels 21814 | ./DiscordChatExporter.Cli export

Windows:

set DISCORD_TOKEN=mfa.Ifrn
DiscordChatExporter.Cli list channels 21814 | DiscordChatExporter.Cli export

@copilot
Add dce.sh file to the DCE.CLI project that acts as an alias/pass-through to DiscordChatExporter.Cli. Not sure if something equivalent can be done with cmd/bat but do that too if po...

Done in commit 55c06ef. Added two scripts to the CLI project:

  • dce.sh — delegates to DiscordChatExporter.Cli via exec "$(dirname "$0")/DiscordChatExporter.Cli" "$@"
  • dce.bat — delegates to DiscordChatExporter.Cli.exe via "%~dp0DiscordChatExporter.Cli.exe" %*

Both are included in the .csproj with CopyToOutputDirectory and CopyToPublishDirectory set to PreserveNewest, so they'll appear alongside the binary in both debug builds and published output.

@Tyrrrz
Copy link
Copy Markdown
Owner

Tyrrrz commented Apr 3, 2026

@copilot

For the ListCommand, error out with "Use of the nammed commands listed below".

Update CLI docs to use the new dce alias for all command executions.

Name = "channel-ids",
Description = "Channel ID(s). "
+ "If provided with category ID(s), all channels inside those categories will be exported."
+ "If provided with category ID(s), all channels inside those categories will be exported. "
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Safe to remove this, since we have list unwrap? Along with any code responsible for this @copilot

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done in commit f8ab926. Removed the category description and unwrapping code from export — the command now simply resolves each channel ID and exports it. Added a new list unwrap command that reads a JSON channel array from stdin (produced by list channels / list channels dm) and replaces any category channels with their children:

./dce list channels 21814 | ./dce list unwrap | ./dce export

./dce list channels dm -t "mfa.Ifrn"
```

When the output is redirected or piped, the `list channels dm` command prints only channel IDs (one per line). This allows you to pipe the output directly to the `export` command:
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Docs are outdated on this @copilot

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Already addressed in commit f8ab926 — the outdated "prints only channel IDs (one per line)" text was updated to "outputs a JSON array of channel objects" for both list channels and list channels dm sections.

[CommandOption("guild", 'g', Description = "Server ID.")]
public required Snowflake GuildId { get; set; }
[CommandParameter(0, Name = "server-ids", Description = "Server ID(s).")]
public required IReadOnlyList<Snowflake> GuildIds { get; set; }
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Update internal naming too @copilot

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done in commit f6e7109. Renamed GuildIds to ServerIds and the loop variable from guildId to serverId throughout GetChannelsCommand.

Copilot AI and others added 2 commits April 4, 2026 11:23
…rverIds in GetChannelsCommand

Agent-Logs-Url: https://github.com/Tyrrrz/DiscordChatExporter/sessions/ab01ba49-ad07-42ba-b382-27f4d105a92b

Co-authored-by: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com>

[Command(
"list unwrap",
Description = "Resolves categories in a channel list to their child channels."
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

We should also resolve forum channels I think... @copilot

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done in commit ea0b47f. list unwrap now also expands forum channels (GuildForum) to their thread posts using Discord.GetChannelThreadsAsync. Updated the command description and docs to mention forums alongside categories.

Copilot AI requested a review from Tyrrrz April 4, 2026 20:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Rework the CLI commands to be more composable

2 participants