Alias for bash commands #326
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Extend alias so that it can also execute bash commands in the context of the folder.
Keep the syntax clear and understandable to distinguish which is which.
Example use case is a bash script
streamd syncI'd like to include into .streamd.toml that adds all streamd files, commits them, pulls, and then pushes.Make editing aliases available in the Android app, but not usable.
Plan: shell aliases
Decisions
type = "alias"keeps its two kinds apart by the field: exactly one ofcommand(a streamd command, as today) orshell(a script). Both, or neither, is an error.$SHELL -c SCRIPT; if$SHELLis unset or empty,sh.$1,$2, … ($SHELL -c SCRIPT streamd-<key> ARGS…;"$@"is all of them). Nothing is spliced into the text of the script.STREAMD_BASE_FOLDERis set to it, so astreamdthe script calls uses the same stream.1. File model (core,
config/project/file.rs), with serde typesThe table is read through a derived raw struct and turned into an enum by
try_from. The raw struct keepsdeny_unknown_fields, so an unknown field is still reported at its line with the list of fields. The either/or error lands on the command's table, where a missing required field is reported today.Why not
#[serde(flatten)]on an externally tagged enum:flattenturns offdeny_unknown_fieldsand buffers the table, which loses the error lines (the same reasonProjectConfigFile::parseavoids tagged enums). Tests: both kinds parse; both fields, neither field and an unknown field give errors at the right line.2. Runtime type and validation (core)
commands/alias/mod.rs:AliasCommand { description, target: Alias }, withenum Alias { Command(Vec<String>), Shell(String) }.stands_for()givesstreamd new editor the first line of the script (with…if there is more).validate.rs: thecommandchecks stay as they are. Ashellthat is empty or only whitespace is an error atcommands.<key>.shell. A command alias MUST NOT name a shell alias either (it is still an alias of an alias).default_commandMAY name a shell alias.schema.rs: addshelltoALIAS_COMMAND("A script run by $SHELL in the stream folder; arguments are $1, $2, …") and rewordcommand. The schema test holds this against the serde model.change.rs: writecommandorshelland remove the other one when the kind changes. Use a multi-line string if the script has a newline. Test the round trip of both kinds and a switch between them.complete.rs/index.rs/rename.rs: onlycommandrefers to a command key.shellis not indexed, sorename_commandleaves a script that callsstreamd oldalone (say so in R32).3. CLI
alias/cli.rs: the clap command is the same for both kinds (no flags of its own, trailing args). The help isRuns \`` when there is no description.commands/cli.rs::alias_argumentsreturnsNonefor a shell alias.rungets the shell arm: newalias/run.rswith a pureshell_invocation(shell_env: Option<OsString>, script, key, args) -> (program, Vec<OsString>)(unit-tested), thenstd::process::Commandwithcurrent_dir(base_folder),STREAMD_BASE_FOLDER, inherited stdio. streamd exits with the script's exit code (1 if the script was killed by a signal). A shell that cannot be started is aStreamdErrornaming the program.default_commandthat is a shell alias selects nothing; the picker opens as without a default.shellthrough the schema.crates/streamd-cli/tests/cli.rswithSHELL=/bin/sh: runs in the stream folder (pwd), gets$1/"$@", the exit code is passed on,STREAMD_BASE_FOLDERis set, the--helptext.4. Bindings (
streamd-ffi/src/outline.rs)AliasSettings { description, target: AliasTarget }with a UniFFI enumAliasTarget { Command { command }, Shell { script } }, converted both ways. Extend the round-trip test with a shell alias.5. Android
ConfigCommandForms.ktAliasFields: aConnectedChoice"streamd command" / "Shell script" above the field. Switching keeps the text. Shell shows a multi-line monospaceTextEntrywith the supporting text "Runs only from the command line, in the stream folder; arguments are $1, $2, …".ConfigList.ktcommandText:= new editfor a command,$ <first line>for a shell alias.ConfigDraft.newCommandkeeps starting as a command alias.ConfigListTestfor the text. A screenshot scene for the form of a shell alias inConfigEditScreenshots.kt(record the reference). Add a shell alias to the fixture ofConfigScreenshots.kt(update the reference).res/values/strings.xml.6. Starter template and docs
init.toml: a commented-out[commands.sync]shell alias like the example above, next toedit. It must stay valid when uncommented (existing template tests).REQUIREMENTS.mdR32 (both kinds, interpreter, arguments, working directory, env, exit code, validation, rename and front-end behaviour; note that only an explicitstreamd <key>runs a script, never the LSP or the app), R28's list of fields (alias:description, andcommandorshell), R21c. AlsoREADME.mdandAGENTS.md(commands/alias/:run.rs,AliasTarget).nix flake checkandnix build .#streamd-app.Implemented in PR #329, branch
326_shell-command-aliases, in four GPG-signed commits (head6d3f097).Aliases support exactly one of
commandandshell. Shell scripts run through$SHELL -c(fallbacksh) in the stream folder, with separate positional arguments, inherited stdio, an absoluteSTREAMD_BASE_FOLDERand exit-code propagation. Android edits both kinds, preserves text when switching, and labels scripts as CLI-only; it never executes aliases. The starter template includes a commented sync example, and README, requirements and agent documentation are updated.Validation completed locally:
nix flake check: passed, including 1,734 Rust tests, architecture checks, clippy, formatting, audit and 102 Kotlin binding checks.nix build .#streamd-app: passed for the final commit, including the offline APK build, app unit tests using the native bindings, and exact screenshot comparison.Findings:
try_fromdrops the error span for contradictory/missing alias targets. These errors now point at the command table, while unknown fields retain their precise positions.--helpand-iare passed unchanged, without interpolation into script text. No dependencies were added.Time spent: about 30 minutes (2026-10-25, 09:03–09:33 UTC).
Token usage from the session metric immediately before this closing note: 10,082,323 total — 168,799 uncached input tokens, 9,876,224 cached input tokens and 37,300 output tokens. Cached context is included in the total.