[v1.4.0] Introducing StudioCLI - Terminal & Built-In Git + Package Manager

About

I’ll get right to the point. StudioCLI is a terminal and package manager combined in one. It features many popular Bash commands altered to fit Roblox and has its own package manager, similar to npm (NodeJS).

Picture

Commands

Here’s the fun part - StudioCLI features over 45 commands upon installation. Their documentation is divided into sections below.

Bash & Added
(Also accessible from running help in the Terminal)

Expand

cd - Change the current directory
echo - Print some output
edit - Edit the script (current directory)
exit - Kill the terminal
head - Read the first 10 lines of the specified file
less - View the specified file
ls - View the children of the current directory
mkdir - Make a new directory in the current one
pwd - Output the current directory
rm - Destroy the current path
rmdir - Destroy the current directory
tail - Read the last 10 lines of the specified file
touch - Create a new file in the current directory

git (Version Control)

Summary

git add {fileName: string}
Add a file to the staged file list

git branch [name]
Include name to add a new remote + local branch, don’t include name to list local branches.

git checkout {name}
Change current branch to name

git clone username/repo_name
Clone a git repository - it even support Rojo projects!

git commit [-m "message_here"]
Commit staged files to the local .git folder

git config {displayname|token} [new value] [-g] [--print]
Include a new value to overwrite the current one, and add the g flag to make it global (mainly for tokens), but don’t include a new value to list the current one. Add the print argument to print it to the Output tab rather than the Terminal window.

git init [name] [--remote] [--both]
Initialize the git repository. Use --remote to only initialize a remote repo, --both for both local and remote, or neither for only local.

git push [branch]
Pushes the current local branch’s latest commit to the specified branch in the remote repository.

git reset [file] [-f]
Remove a staged file from the staged list or only use the f flag to clear the staged list entirely.

git staged
View the staged files

git version
Print the current version of git

rbx (Package Manager)

Expand

rbx cloud
Registers you with the rbx package cloud server

rbx ls [-g]
List the current installed packages in the current directory (if the g flag is set, list the global packages)

rbx import [-g]
Prompts you to import a .lua file as a script (.rbxmx format coming soon!)

rbx init [-f]
Create a new PackageFile (similar to package.json in Node projects) in ReplicatedStorage. If the f flag is set, overwrite the current PackageFile.

rbx install {packageName: string|rbxassetid: number} [--global] [-g]
Probably the most important feature - installing! You can install a “package”, or module (although this uses game:GetObjects() under the hood), by providing it’s registry name (see below) or Roblox AssetId. If the global argument or g flag is provided, the package will be installed in a folder under ReplicatedStorage called “rbx_modules”. Otherwise, it will install in the current directory.

What is a “registry name”, you may ask?
A registry name is essentially a string to represent a module’s ID. For example, @AstrealDev’s MathEvaluator, which has the ID of 7323648466, is represented by the registry name “MATHEVALUATOR” (case-insensitive). If I wanted to install MathEvaluator, I could simply type rbx install MathEvaluator for it to work.

rbx key {key: string}
Sets the rbx cloud auth key - you shouldn’t use this unless there’s a problem with authentication

rbx publish {name: string} {assetid: string}
Publish a Roblox AssetId to the Cloud Registry with a specific name - works just like the normal registry but this one’s in the cloud and you can publish

Example: rbx publish test3 2949893496

rbx run {scriptName: string}
This is the equivalent of npm run <scriptName>, but you cannot run Lua files. For those who are not familiar with npm run, this command runs a “script”, aka function, defined in the PackageFile.

Detailed Explanation

If my PackageFIle were the following…

return {
	['name'] = 'my-roblox-game',
	['version'] = '0.0.1',
	['description'] = 'my roblox game',
	['scripts'] = {
		['build'] = function()
            print("Building project!")
        end,
	}
}

…and I ran rbx run build in the Terminal, it would run the build function and print Building project!.

rbx star {packageName: string|rbxassetid: number}
Star your favorite packages and view them with rbx stars.

rbx stars
List your starred packages.

rbx whoami
Output your Username & UserId into the terminal.

rbx uninstall {packageName: string|rbxassetid: number} [--global] [-g]
Uninstall an installed package. If the global argument or g flag is provided, it will uninstall the package from the rbx_modules folder.

rbx unstar {packageName: string|rbxassetid: number}
Unstar a no-longer-favorite package.

rbx update {packageName: string|rbxassetid: number} [--global] [-g] [--all] [-a]
Update a package to the latest version. If the global argument or g flag is provided, update it globally. If the all argument or a flag is provided, update all packages in that directory. Remember, when updating all packages, the packages will install with their default name and not take into consideration if you renamed them after initial installation!

rbx version
Outputs the installed version of rbx

HTTP

The Terminal also comes with two built-in HTTP commands. Why? Since I felt it may be useful in certain scenarios.

Expand

http get {url: string}
GET a URL, there’s not much to it but that.

http post {url: string} {data: JSON string} [--contentType: string = applicationJson] [--compress: boolean = false]
POST to a URL with some data (cannot include spaces or it’ll break)

Audio

Yes, this also comes with a built-in audio player! (however, you may prefer Studio Radio)

Expand

audio play {audioId: number} [--looped] [-l] [--volume]
Play the specified audioId. The looped argument and l flag indicated the song’s Looped value and the volume argument lets you alter the volume.

audio pause
Pause the audio.

audio resume
Resume the audio.

audio stop
Stop the audio.

audio reset
Reset the audio.

Misc

Expand

code {directory: string}
Open a VSCode-style side widget that has the specified directory open

toggle {setting: string}
You can choose the setting from the list here:

colors -- Syntax highlighting toggle

lua {code: string}
Execute a lua string

Installation

You can install the plugin here.

Contributing

You can make a Pull Request to the repository with your changes to the Commands file.

StudioCLI API

When creating a new function, you have three options: making a regular function, making a table-function, or making a library.

Function

['test'] = function(self, regular, args, flags)
	print(regular)
	print(args)
	print(flags)
end,

Table-Function

['test2'] = {
	"Hello testing!",
	function(self, regular, args, flags)
	    print(regular)
	    print(args)
	    print(flags)
	end
},

Library

['test3'] = {
	['test3-1'] = function(self, regular, args, flags)
		print(regular)
		print(args)
		print(flags)
	end,
	['test3-2'] = {
		"Hello testing!",
		function(self, regular, args, flags)
			print(regular)
			print(args)
			print(flags)
		end
	}
}

You are given four parameters: self, regular, args, and flags.

Regular (indexed table) consists of regular text provided, such as the “world” in hello world --hi bye --g.

The args is a dictionary consisting of the double-hyphen arguments provided, such as the “–hi bye”. You would retrieve the “hi” argument by doing args['hi']/

The flags (indexed table) consist of the single-hyphen arguments provided, such as the “-g”. You would check for flags with table.find(flags, "<letter here>").

self, on the other hand, refers to the actual TerminalHandler class. Therefore, you are able to call methods such as :NewLine for a blank line, :NewMsg to output a new message, and :__evaluate to basically loadstring a Terminal command.

That’s it for the API. Thanks for taking the time to create more functions for StudioCLI and contributing to its growth!


Thanks for reading, and as always, happy coding!

61 Likes

If you would like to submit a package to the registry for your unique name, please DM me with the title “StudioCLI Registry Addition” and please include the module(s) + your registry name preferences.

1 Like

So is this like a plugin market inside of a plugin? (plug-ception)

3 Likes

Yeah, it’s kinda like that but you can use it to insert literally anything into the game in the editing state without having to download a rbxm or requireing IDs.

2 Likes

Now that I think about it, this can also be used to inspect the source of plugins (since it uses game:GetObjects()).

2 Likes

I love how you used a unique name for your package manager rather than just stealing dpkg/pacman. I 100% plan on using this in the future.

2 Likes

Poll

In the coming v1.1.0, I am introducing live syntax highlighting. However, due to how Roblox’s DockWidgetPluginGuis work, the caret (blinking text cursor) will be invisible.

I’m so dumb… I figured it out seconds after posting this :man_facepalming:
v1.1.0 is coming very soon!

I guess a better question would be:

  • I am fine with no caret (text cursor)
  • I am fine with a barely noticeable bolder text

0 voters

Edited the poll, one vote in favor of bolder text was lost

1 Like

Instead of using branches for releases, use the GitHub Releases feature.
It’ll also automatically create a tag, so you can go back to it!

1 Like

I considered that, but this is a plugin. It should only be installed through the website (unless a file type exists that automatically installs plugins).

A github release is the same as a branch.
It creates a tag, which is essentially the same as a branch but not cluttering it up. Branches are used for new features being worked on etc.
You could create some CI using Github Action and Foreman (or aftman once it’s complete) to run rojo build and then publish the .rbxm/rbxmx file to the releases

Here’s an example from one of my open-source repos:


1 Like

Version 1.1.0

The next version of StudioCLI is released! Featuring live syntax highlighting and a new package manager command, there’s no reason to not upgrade!

Please update the plugin to get the latest features.

You can read more about this release here.

I have seen a lot of CLI plugins lack an important feature: scripts! Instead of manually typing out commands every time I want to do something, I should be able to quickly run a script that automates the process. I should never have to add a custom command and wait for it to be approved and released. Most CLI plugins just tell you to write the script in lua and then manually execute it.

also please add ability to make custom aliases for commands thanks :smiley:

Aliases were always supported but recently broke so I never mentioned this feature. It will be fixed next update.

1 Like

I will be adding Profiles (inspired from Powershell) in the next update as well but I think you mean creating functions through the Terminal (Like if I typed function st { npm start } in Powershell)

Some Ideas

  • Startup file, like .bashrc or .zshrc to allow users to create startup scripts, themes, and other settings. @comsurg 's idea of command aliases can be added in this startup script, just like bash/zsh.
  • Be able to “dev install” a package. Unlike normal installing, this is a temporary install in it’s out environment for developers to test their package. OR this could also be a way for users to install betas/nightly/unstable releases of packages.
2 Likes

Powershell-like Profiles will be added in v1.2.0 to take care of custom functions/aliases & startup commands but I will definitely consider this idea of a configuration.

Although this is a great idea, I don’t know how I could make this work. One idea I considered is installing the package but removing it on place shutdown (but there’s no event for that afaik). Could you explain how this could be achieved?

This is a plugin, right? If so, you can save data using plugin:SetSetting(). If this is the case, during a dev installation, two things can happen:

    1. The plugin is saved to the actual plugin library where fully installed plugins are, you can tag it as “DEV” or something, and ignore it while saving data.
    1. The plugin is saved in it’s own developer environment, as long as the saving system doesn’t check there, the plugin should naturally be swept away once the user closes studio/exits that game.

No, I don’t mean functions or “profiles”. By “scripts” I mean files/scripts that I can put anywhere in the data model and then run using the terminal.
Ideally, the scripts shouldn’t use Lua and should instead have commands that will be executed as if I typed them in the CLI.
Typing in the scripts in the CLI would be inconvenient unless you make a text editor for your CLI, and I would rather use the built-in text editor

1 Like

I finally got a chance to mess around with this plugin and it’s pretty nice. One thing I would really like to see is autoscrolling to the bottom of the scrolling frame so I don’t have to manually scroll down which is annoying. Also if clicking anywhere on the console would focus the text box it would be much more user friendly.

Finally I think the package manager needs more work. It makes more sense if packages were grabbed from a GitHub repo or gist that way developers don’t need to update their packages in two places. This also gives you the ability to integrate some sort of version control.

Packages should contain some sort of data to integrate properly with StudioCLI. The way you do this is up to you but it makes the most sense as a top level module. Maybe something like a package.lua file?

For example:

  • SomePackage (Instance – Doesn’t really matter what it is as long as package.lua is a direct child)
    • package (ModuleScript)
-- package.lua

-- Just provide some data to the package manager
return {
  id = 'some-package',
  name = 'Some Package',
  version = '1.0.0',
}

You could then support commands like:

rbx info some-package
Some Package (some-package) @ v1.0.0

Just some ideas as I think this plugin has a lot of potential! Really great work :thumbsup:

1 Like