{"id":"ceaselessquokka/roshell","name":"roshell","scope":"ceaselessquokka","platform":"roblox","description":"A typed command console for Roblox: typed commands, a real input language, IDE-grade completion and a modern console UI","version":"0.1.0","latest":"0.1.0","versions":["0.1.0"],"license":"MIT","licenseRating":"safe","licenseCaveats":[],"licenseVerified":true,"dependencies":{},"integrity":"af2f07129f4da4c420c7085c5265b54bc566d8002f9500c44fd746d37f379da2","likes":0,"downloads":0,"install":"forest install ceaselessquokka/roshell","url":"https://forest.dev/p/roblox/ceaselessquokka/roshell","files":"https://api.forest.dev/ai/package/roblox/ceaselessquokka/roshell/files","readme":"# RoShell\n\n**A typed command console for Roblox.** Define commands once and get fully typed arguments, a real input language\n(pipes, chains, wildcards, set arithmetic, variables, embedded commands), IDE-grade completion and a command bar\nthat stays out of the way until you need it. A ground-up remake of [Cmdr](https://github.com/evaera/cmdr).\n\n![The command bar: the command on the left, the argument being typed and its candidates on the right](docs/images/completion.jpg)\n\n```lua\nlocal RoShell = require(ReplicatedStorage.RoShell)\n\nreturn RoShell.Command({\n\tName = \"give\",\n\tArgs = {\n\t\t{ targets = RoShell.Default(RoShell.Types.Players, \"me\") },\n\t\t{ item = RoShell.Arg(Items) },\n\t\t{ amount = RoShell.Default(RoShell.Types.Integer, 1) },\n\t},\n}):Run(function(ctx, args)\n\t-- args.targets: { Player }   args.item: ItemDef   args.amount: number   (inferred, no annotations)\n\treturn ctx:Success(`Gave {args.amount}× {args.item.Name}`)\nend)\n```\n\n```\ngive %Red-Bob ~embers 3 && announce \"Loot dropped!\"\nplayers --team Blue | kick --reason \"friendly fire\"\nin 5m shutdown \"Update!\" --dry\n```\n\n## Highlights\n\n- **Precise types end to end.** `Run(ctx, args)` receives a record computed from `Args` by Luau type functions:\n  choices become literal unions, maps and dynamic types carry their value type, optionals are `T?`, rests are\n  `{ T }`. Mistakes are compile errors (see `tests/types/negative`). No `any` anywhere in the library.\n- **Custom types in a few lines.** `Type.Choice`, `Type.Map`, `Type.Dynamic`, `Type.Struct`, `Type.Union`,\n  `Type.Transform`, `Type.Refine`, … and 40+ built-in types (players, teams, durations, colors, vectors, instances,\n  enums, timestamps, …).\n- **A real input language.** Quotes and escapes, `&&` `||` `;` `|`, `${embedded commands}`, `$variables`,\n  `$functions()`, aliases with `$1…$9`, flags and named arguments, `--dry` previews and `--yes`. See\n  [docs/GRAMMAR.md](docs/GRAMMAR.md).\n- **Operators everywhere.** `*`, `**`, `.`, `?3`, `~fuzzy`, globs, `%Team`, `#Tag`, `a,b`, `a+b`, `a-b`, `a&b`,\n  `!a`, parentheses and numeric ranges, for every enumerable type, including your own.\n- **Completion that understands the line.** Works mid-text, inside quotes and `${…}`, after pipes; fuzzy ranking\n  with highlights and frecency; ghost text; signature hints with the active argument; live validation; resolution\n  previews (`→ 7 players`). 10 000 candidates complete in under 1 ms.\n- **A console that stays out of the way.** A command bar docked to the top (or bottom, or anywhere you drag it)\n  with a panel that pops out only when there is something to show: the argument you are typing and its candidates,\n  the output of what you ran, the whole log on demand (Ctrl+H), prompts, a command palette (Ctrl+K), fuzzy history\n  search (Ctrl+R) and a theme picker with live previews. Sixteen contrast-audited themes (Midnight, Sakura, Ocean,\n  Light...), icons from Roblox's icon font, springs that respect reduced motion, and touch and gamepad support.\n- **Secure by default.** Default-deny permissions (users, groups, roles, game passes, badges, predicates), the\n  server re-parses every request, typed schema validation at the network boundary, rate limits, cooldowns and an\n  audit log.\n- **Batteries included.** 60+ built-in commands: help, aliases, binds, variables, history, undo/redo, scheduling\n  (`in`, `every`, `repeat`), scripts, moderation, inspection, cross-server announcements, theme and settings.\n- **Testable.** `RoShell.Test.Run({ Text = \"give Bob sword\" })` runs commands headlessly with a virtual clock and\n  scripted prompt answers.\n\n| | |\n|---|---|\n| ![A command's output](docs/images/output.jpg) | ![Command palette](docs/images/palette.jpg) |\n| ![Prompts raised by commands](docs/images/prompt.jpg) | ![Sakura theme](docs/images/sakura.jpg) |\n\n## Quickstart\n\nPut RoShell in `ReplicatedStorage` (Wally, the `.rbxm` from the releases, or Rojo with `default.project.json`).\nThen one line on each side.\n\n**Server** (a `Script` in `ServerScriptService`):\n\n```lua\nlocal RoShell = require(game.ReplicatedStorage.RoShell)\nRoShell.Server.new({ Admins = { 156 } }):Start()   -- user ids that may run every command\n```\n\n**Client** (a `LocalScript` in `StarterPlayerScripts`):\n\n```lua\nrequire(game.ReplicatedStorage:WaitForChild(\"RoShell\")).Client.new():Start()\n```\n\nPress **F2** or **`** to open the console. Everyone gets the built-in commands that are open to all (help,\nhistory, aliases, themes, settings...), the admins get everything, and in Studio every command is allowed for\ntesting.\n\n**Your own commands** go in a folder in `ReplicatedStorage`, named on the server. Clients load the same folder\nby themselves:\n\n```lua\nRoShell.Server.new({ Admins = { 156 }, Commands = game.ReplicatedStorage.Commands }):Start()\n```\n\nOrganize them however you like: subfolders at any depth are loaded, and `Commands` also takes a list\n(`{ ReplicatedStorage.Commands, ReplicatedStorage.MinigameCommands }`).\n\n**Everything else is optional** and there when you want it: `Admins` also takes rules\n(`{ 156, RoShell.Permissions.Group(1234567, 250) }`), per-group and per-command permissions and roles, hooks,\nmiddleware, audit sinks, `DefaultCommands = { \"Help\", \"Utility\" }` to pick the built-ins, client options for keys,\nthemes and settings. See [examples/00-MinimalSetup.luau](examples/00-MinimalSetup.luau) and\n[examples/05-ServerSetup.luau](examples/05-ServerSetup.luau) for a production setup.\n\n**Do I need a DataStore?** No. Players' history, aliases, key binds, variables and console settings are kept in\nmemory for the life of the server by default. To keep them between sessions, give the server a DataStore-backed\nadapter: `RoShell.Server.new({ Storage = RoShell.Storage.DataStore(\"RoShell\") })` (or your own adapter with\n`Get`/`Set`).\n\n## Guides\n\n- [Your first command](docs/guides/FirstCommand.md)\n- [Custom types in 60 seconds](docs/guides/CustomTypes.md)\n- [Operators and wildcards](docs/guides/Operators.md)\n- [Permissions](docs/guides/Permissions.md)\n- [The console: anatomy, keys, settings, themes, icons](docs/guides/Console.md)\n- [Writing plugins and extending RoShell](docs/guides/Extending.md)\n- [Security model](docs/guides/Security.md)\n- [Testing commands](docs/guides/Testing.md)\n- Reference: [input grammar](docs/GRAMMAR.md), [built-in commands, types and functions](docs/Reference.md)\n  (generated by `RoShell.Docs.Markdown`), [benchmarks](docs/BENCHMARKS.md), [design notes](DESIGN_NOTES.md)\n- Runnable [examples](examples/) and a [demo place](demo/) (`rojo serve demo.project.json`)\n\n## Coming from Cmdr\n\n| Cmdr v1 | RoShell |\n|---|---|\n| Definition module + separate `…Server` module | One module: `RoShell.Command({ … }):Run(fn)` (`:ClientRun(fn)` for client code) |\n| `Args = { { Type = \"player\", Name = \"target\" } }` | `Args = { { target = RoShell.Arg(RoShell.Types.Player) } }`, typed in `Run` |\n| `Optional = true`, `Default = …` | `RoShell.Optional(T)`, `RoShell.Default(T, value or \"text\")`, `RoShell.DefaultFn(T, fn)` |\n| Type tables `{ Transform, Validate, Autocomplete, Parse }` | `RoShell.Type.Custom({ Parse, Complete })` or a constructor (`Choice`, `Map`, `Dynamic`, …) |\n| `Util.MakeEnumType`, `MakeListableType`, `MakeFuzzyFinder` | `Type.Choice`, `Type.List`, fuzzy matching built into every enumerable type |\n| `Registry:RegisterType(\"name\", type)` | `Registry:RegisterType(type)` (the name comes from the type) |\n| `Registry:RegisterHook(\"BeforeRun\", fn)` | `Registry:RegisterHooks({ BeforeRun = fn })`, plus `RegisterMiddleware` |\n| Group checks inside a `BeforeRun` hook | Default-deny `Permissions` per command or per group (`Permissions:SetGroup`) |\n| `context:Reply(text, color)` | `ctx:Reply(text, level)`, `ctx:Success/Info/Warn/Error`, `ctx:Table/List/KeyValue/Color/Progress` |\n| `Data = function(context, …)` | `:Data(fn)`; read with `ctx:GetData()` |\n| `CmdrClient:HandleEvent(name, fn)` / `context:SendEvent` | `client:OnEvent(name, fn)` / `ctx:SendEvent(player, name, payload)` |\n| `CmdrClient:SetActivationKeys`, `SetPlaceName`, `SetEnabled`, `Show/Hide/Toggle`, `SetMashToEnable`, `SetActivationUnlocksMouse`, `SetHideOnLostFocus` | Same names on `client` |\n| `Cmdr.Dispatcher:EvaluateAndRun(text, player)` | `server:Run(text, player)` / `client:Run(text)` |\n| `${…}`, `$1`, `alias`, `bind`, `var` | Same ideas, plus pipes, `&&`/`||`, `$$`, `$@`, `$fn()`, set operators and ranges |\n\n## Development\n\nTools are pinned in `rokit.toml` (`rokit install`): Luau LSP, Larvae (formatter), selene, Rojo and the Luau CLI.\n\n```bash\nbash scripts/analyze.sh       # strict type check (new solver), zero errors\nbash scripts/typetests.sh     # negative type tests: marked lines must fail\nluau tests/cli.luau           # unit tests (headless)\nluau --codegen tests/bench.luau\nlarvae fmt src tests demo examples\n```\n\nIn Studio, `require(game.ServerStorage.RoShellDev.tests.Studio)()` runs the same suite plus the engine-only specs.\n\n## License\n\nMIT. RoShell is inspired by [Cmdr](https://github.com/evaera/cmdr) by evaera and contributors.\n","readmeTruncated":false}