{"id":37530990,"url":"https://github.com/s2quake/commands","last_synced_at":"2026-01-16T08:32:12.178Z","repository":{"id":116356236,"uuid":"282594989","full_name":"s2quake/commands","owner":"s2quake","description":"Parses a string to set a value to a specified property or invoke a specified method. Like Terminal or Powershell, it provides a REPL environment to provide a command-based development environment.","archived":false,"fork":false,"pushed_at":"2025-02-13T02:57:36.000Z","size":3591,"stargazers_count":3,"open_issues_count":2,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-10-29T22:33:39.013Z","etag":null,"topics":["command","commandcontext","console","csharp","dotnet","parse","parser","repl","terminal"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/s2quake.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGES.md","contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-07-26T07:00:35.000Z","updated_at":"2025-02-05T13:31:09.000Z","dependencies_parsed_at":"2023-09-23T08:51:22.412Z","dependency_job_id":"6823bea1-479b-435f-8076-ac2deb575196","html_url":"https://github.com/s2quake/commands","commit_stats":null,"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"purl":"pkg:github/s2quake/commands","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/s2quake%2Fcommands","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/s2quake%2Fcommands/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/s2quake%2Fcommands/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/s2quake%2Fcommands/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/s2quake","download_url":"https://codeload.github.com/s2quake/commands/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/s2quake%2Fcommands/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28478047,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-16T06:30:42.265Z","status":"ssl_error","status_checked_at":"2026-01-16T06:30:16.248Z","response_time":107,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["command","commandcontext","console","csharp","dotnet","parse","parser","repl","terminal"],"created_at":"2026-01-16T08:32:12.075Z","updated_at":"2026-01-16T08:32:12.149Z","avatar_url":"https://github.com/s2quake.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Commands\n\n[![NuGet](https://img.shields.io/nuget/v/JSSoft.Commands.svg?style=flat)][NuGet]\n[![NuGet (prerelease)](https://img.shields.io/nuget/vpre/JSSoft.Commands.svg?style=flat)][NuGet]\n\n[NuGet]: https://www.nuget.org/packages/JSSoft.Commands/\n\n## Summary\n\nParses a string to set a value to a specified property or invoke a specified method.\n\nLike Terminal or Powershell, it provides a REPL environment to provide a command-based development environment.\n\n## Requirements\n\n```plain\ndotnet sdk 8.0.100 or later\nc# 12\n```\n\n## Clone\n\n```plain\ngit clone https://github.com/s2quake/commands.git\n```\n\n## Build - NET 8.0\n\n```plain\ndotnet build\n```\n\n## Other Framework Build\n\n```shell\n# net7.0\ndotnet build -p:TargetFrameworks=net7.0 --framework net7.0\n\n# netcoreapp3.1\ndotnet build -p:TargetFrameworks=netcoreapp3.1 --framework netcoreapp3.1\n\n# netstandard2.1\ndotnet build -p:TargetFrameworks=netstandard2.1 --framework netstandard2.1\n\n# net481\ndotnet build -p:TargetFrameworks=net481 --framework net481\n\n```\n\n## Run Examples\n\n```shell\n# Run the property settings example project\ndotnet run --project JSSoft.Commands.Parse --framework net8.0 -- --help\n\n# Run the method call example project\ndotnet run --project JSSoft.Commands.Invoke --framework net8.0 -- --help\n\n# Run the CommandContext Execution example Project\ndotnet run --project JSSoft.Commands.Sets --framework net8.0 -- --help\n\n# Run the CommandContext Execution Example Project in the REPL environment\ndotnet run --project JSSoft.Commands.Repl --framework net8.0\n\n# Run the CommandContext Execution Example Project with Avalonia UI\ndotnet run --project JSSoft.Commands.AppUI --framework net8.0\n```\n\n## Parse\n\nThis is the most basic way to parse the command. Provides a function to set a value for a specified property.\n\n```csharp\nvar settings = new Settings();\nvar parser = new CommandParser(settings);\nparser.Parse(args);\n```\n\n\u003e See the JSSoft.Commands.Parse project\n\n## Invoke\n\nAs an extension of Parse, it provides the ability to call a specified method by parsing the command.\n\n```csharp\nvar commands = new Commands();\nvar invoker = new CommandInvoker(commands);\ninvoker.Invoke(args);\n```\n\n\u003e See the JSSoft.Commands.Invoke project\n\n## CommandContext\n\nIt provides various functions to manage and process more commands.\n\n```csharp\nvar commands = new ICommand[]\n{\n    new LoginCommand(),\n    new LogoutCommand(),\n    new ExitCommand()\n};\nvar commandContext = new CommandContext(commands);\ncommandContext.Execute(args);\n\nor\n\nawait commandContext.ExecuteAsync(args);\n```\n\n\u003e See the JSSoft.Commands.Sets project\n\nIt can be combined with user input such as EditBox, TextBox, InputText to build a console or REPL-like environment.\n\n```csharp\nvar commands = new ICommand[]\n{\n    new LoginCommand(),\n    new LogoutCommand(),\n    new ExitCommand()\n};\nvar commandContext = new CommandContext(commands);\nvar terminal = new SystemTerminal(commandContext);\nawait terminal.StartAsync(CancellationToken.None);\n```\n\n\u003e See the JSSoft.Commands.Repl project\n\n## Property\n\n### Required argument definition\n\nTo define the value required for command syntax, define **CommandPropertyRequired** in the property.\n\n```csharp\n[CommandPropertyRequired]\npublic string Value1 { get; set; } = string.Empty;\n\n[CommandPropertyRequired]\npublic int Value2 { get; set; }\n```\n\n```plain\n\"value\"   // error! value for Value2 does not exists.\n3         // format error!\n\"value\" 3 // Value1 is \"value\", Value2 is 3\n```\n\nYou can set default values ​​like this: If there is no value in the command syntax, it is replaced with the default value.\n\n```csharp\n[CommandPropertyRequired]\npublic string Value1 { get; set; } = string.Empty;\n\n[CommandPropertyRequired(DefaultValue = 1)]\npublic int Value2 { get; set; }\n```\n\n```plain\n\"value\" 2 // Value1 is \"value\", Value2 is 2\n\"value\"   // Value1 is \"value\", Value2 is 1\n```\n\n### Explicit required argument definition\n\nAn explicit required argument indicates that the command syntax must have a value, but must include a switch statement, such as --value \"2\".\n\n```csharp\n[CommandPropertyRequired]\npublic string Value1 { get; set; } = string.Empty;\n\n[CommandPropertyExplicitRequired]\npublic int Value2 { get; set; }\n```\n\n```plain\n\"value\"            // error!\n\"value\" 2          // error!\n\"value\" --value2   // error!\n\"value\" --value2 3 // Value1 is \"value\", Value2 is 3\n--value2 3 \"value\" // Value1 is \"value\", Value2 is 3\n```\n\nIn order to use the default values ​​of explicit required arguments, the command syntax must include a switch statement such as --value.\n\n```csharp\n[CommandPropertyRequired]\npublic string Value1 { get; set; } = string.Empty;\n\n[CommandPropertyExplicitRequired(DefaultValue = 1)]\npublic int Value2 { get; set; }\n```\n\n```plain\n\"value\"            // error!\n\"value\" 2          // error!\n\"value\" --value2   // Value1 is \"value\", Value2 is 1\n\"value\" --value2 3 // Value1 is \"value\", Value2 is 3\n--value2 3 \"value\" // Value1 is \"value\", Value2 is 3\n--value2 \"value\"   // error! \"value\" is not int\n```\n\n### Optional argument definition\n\nThe optional argument can be set whether or not to use a value using a switch statement.\n\n```csharp\n[CommandProperty]\npublic string Value { get; set; } = string.Empty;\n```\n\n```plain\n--value      // error\n--value text // value is \"text\"\n```\n\nTo use the default, the command syntax must include a switch statement such as --value.\n\n```csharp\n[CommandProperty(DefaultValue = \"1\")]\npublic string Value { get; set; } = string.Empty;\n```\n\n```plain\n--value      // value is \"1\"\n--value text // value is \"text\"\n```\n\nA bool type switch statement that does not use a value should be defined as follows.\n\n```csharp\n[CommandPropertySwitch]\npublic bool Switch { get; set; }\n```\n\n### Variable arguments definition\n\nVariable arguments represent the values ​​of the remaining arguments that were not parsed in the command syntax.\n\nThe property type of a variable arguments must be an array and must be defined for only one property.\n\n```csharp\n[CommandPropertyArray]\npublic string[] Values { get; set; } = Array.Empty\u003cstring\u003e();\n```\n\n```plain\n-- value1 value2 value3 \"value4\"\n```\n\n## Method\n\n### Method definition\n\nTo execute an attribute method through command syntax, you must define a **CommandMethod** in the method as follows.\n\nEach parameter of the method is automatically defined as a required argument.\n\n```csharp\n[CommandMethod]\npublic void Save(string message)\n{\n}\n```\n\n```plain\nsave \"message\"\n```\n\nIf you want to additionally define optional arguments in the method, you can use **CommandMethodProperty** and add the name of the property defined as CommandProperty.\n\n```csharp\n[CommandMethod]\n[CommandMethodProperty(\"Value\")]\npublic void Save(string message)\n{\n}\n```\n\n```plain\nsave \"comment\"\nsave \"comment\" --value text\n```\n\nYou can use params as below as a variable arguments.\n\n```csharp\n[CommandMethod]\npublic void Save(string message, params string[] args)\n{\n}\n```\n\n```plain\nsave \"comment\"\nsave \"comment\" -- \"1\" \"text\" \"string\"\n```\n\n### Enable or Disable Method\n\nDefine the properties by prefixing \"Can\" to the method name as shown below.\n\n```csharp\npublic bool Can{MethodName} { get; }\n```\n\nexample:\n\n```csharp\npublic bool CanSave =\u003e true;\n```\n\n## Static properties and methods\n\nProperties and methods defined as static can be included in the object and used.\n\n```csharp\nstatic class GlobalSettings\n{\n    [CommandProperty]\n    public static string ID { get; set; } = string.Empty;\n\n    [CommandProperty]\n    public static string Password { get; set; }\n}\n\n[CommandStaticProperty(typeof(GlobalSettings))]\nclass Settings\n{\n}\n```\n\n```csharp\nstatic class StaticCommand\n{\n    [CommandMethod]\n    [CommandMethodProperty(nameof(Value))]\n    public static void List()\n    {\n    }\n\n    [CommandProperty]\n    public static int Value { get; set; }\n}\n\n[CommandStaticMethod(typeof(StaticCommand))]\nclass Commands\n{\n}\n```\n\n## Naming\n\nThe property and method names defined as CommandProperty and CommandMethod are changed to [kebab-case (spinal-case, Train-Case, Lisp-case)](https://en.wikipedia.org/wiki/Letter_case).\n\n### Property name example\n\n| Property name | Changed property name |\n| --------- | ---------------- |\n| Value     | --value          |\n| Message   | --message        |\n| IsLocked  | --is-locked      |\n\n\u003e When using a name and a short name\n\n```csharp\n[CommandProperty(\"custom-value\", 'v')]\npublic string Value { get; set; } = string.Empty;\n```\n\n```plain\n--custom-value or -v\n```\n\n\u003e When using only short names\n\n```csharp\n[CommandProperty('v')]\npublic string Value { get; set; } = string.Empty;\n```\n\n```plain\n-v\n```\n\n\u003e When using a short name and a default name\n\n```csharp\n[CommandProperty('v', AllowName = true)]\npublic string Value { get; set; } = string.Empty;\n```\n\n```plain\n-v or --value\n```\n\n### Method name example\n\n| Method name | Changed method name  |\n| ----------- | ------------------ |\n| Save        | save               |\n| LockTable   | lock-table         |\n\nYou can also set the method name yourself.\n\n```csharp\n[CommandMethod(\"save\")]\npublic void Save(string message)\n{\n}\n```\n\n## Command\n\nYou can define commands in the CommandContext.\n\n```csharp\nclass ExitCommand : CommandBase\n{\n    [CommandPropertyRequired(DefaultValue = 0)]\n    public int ExitCode { get; set; }\n\n    protected override void OnExecute()\n    {\n        Environment.Exit(ExitCode);\n    }\n}\n```\n\n```plain\nexit\nexit 0\n```\n\n## SubCommand\n\nYou can define commands that have subcommands in CommandContext.\n\n```csharp\nclass UserCommand : CommandMethodBase\n{\n    [CommandMethod]\n    [CommandMethodProperty(nameof(Message))]\n    public void Create(string userID)\n    {\n    }\n\n    [CommandMethod]\n    public void Delete(string userID)\n    {\n    }\n\n    [CommandMethod]\n    public void List()\n    {\n    }\n\n    [CommandProperty]\n    public string Message { get; set; }\n}\n```\n\n```plain\nuser create \"user1\"\nuser create \"user1\" --message \"new user\"\nuser delete \"user1\"\nuser list\n```\n\n## SubCommand extension\n\nBy implementing a partial class, you can add subcommand to the already implemented command.\n\n```csharp\n[PartialCommand]\nclass UserPartialCommand : CommandMethodBase\n{\n    public UserPartialCommand()\n        : base(\"user\")\n    {\n    }\n\n    [CommandMethod]\n    public void SendMessage(string userID, string message)\n    {\n    }\n}\n```\n\n## SubCommand AsyncMethod\n\nYou can use asynchronous methods, as shown in the example below.\nThe parameters CancellationToken and IProgress\u003cProgressInfo\u003e are optional, but should always be the last declaration.\n\nFor more information, see the ``Choosing the overloads to provide`` topic in the [TAP](https://learn.microsoft.com/en-us/dotnet/standard/asynchronous-programming-patterns/task-based-asynchronous-pattern-tap).\n\nThe name of an asynchronous method is used without the suffix Async.\n\n```csharp\nclass UserCommand : CommandMethodBase\n{\n    [CommandMethod]\n    public Task Invoke1Async()\n    {\n        return Task.CompletedTask;\n    }\n\n    [CommandMethod]\n    public Task Invoke2Async(CancellationToken cancellationToken)\n    {\n        return Task.CompletedTask;\n    }\n\n    [CommandMethod]\n    public Task Invoke3Async(IProgress\u003cProgressInfo\u003e progress)\n    {\n        return Task.CompletedTask;\n    }\n\n    [CommandMethod]\n    public Task Invoke4Async(CancellationToken cancellationToken, IProgress\u003cProgressInfo\u003e progress)\n    {\n        return Task.CompletedTask;\n    }\n}\n```\n\n## License\n\nReleased under the MIT License.\n\nCopyright (c) 2024 Jeesu Choi\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the\nrights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit\npersons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the\nSoftware.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE\nWARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\nCOPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR\nOTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fs2quake%2Fcommands","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fs2quake%2Fcommands","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fs2quake%2Fcommands/lists"}