diff --git a/Makefile b/Makefile
index e7fab3f56..dac577380 100644
--- a/Makefile
+++ b/Makefile
@@ -7,36 +7,53 @@ EXECUTABLE_PATH_RELEASE = $(BUILD_PATH_RELEASE)/Toolkit
BUILD_PATH_DEBUG = $(TOOLKIT_PATH)/.build/debug
EXECUTABLE_PATH_DEBUG = $(BUILD_PATH_DEBUG)/Toolkit
+.PHONY: clean
clean:
rm -rf $(TOOLKIT_PATH)/.build $(EXECUTABLE_NAME)
+.PHONY: build
build: clean
swift build -c release --disable-sandbox --package-path $(TOOLKIT_PATH)
ln -s $(EXECUTABLE_PATH_RELEASE) $(EXECUTABLE_NAME)
+.PHONY: build-debug
build-debug:
if [ -f $(EXECUTABLE_NAME) ]; then rm $(EXECUTABLE_NAME); fi
swift build --package-path $(TOOLKIT_PATH)
ln -s $(EXECUTABLE_PATH_DEBUG) $(EXECUTABLE_NAME)
+.PHONY: gen-docs
gen-docs:
./$(EXECUTABLE_NAME) generate-documentation
+.PHONY: gen-docs-and-commit
gen-docs-and-commit: gen-docs
./$(TOOLKIT_PATH)/integration.sh commit_documentation
+.PHONY: set-executable
set-executable:
./$(EXECUTABLE_NAME) set-executable
+.PHONY: set-executable-and-commit
set-executable-and-commit: set-executable
./$(TOOLKIT_PATH)/integration.sh commit_executable
+.PHONY: lint
lint:
- swiftlint lint
+ @echo "Linting code format with SwiftLint"
+ @swiftlint $(TOOLKIT_PATH) --config $(TOOLKIT_PATH)/.swiftlint.yml
+.PHONY: fix
fix:
- swiftlint --fix
+ @echo "Fixing code format with SwiftLint"
+ @swiftlint $(TOOLKIT_PATH) --fix --config $(TOOLKIT_PATH)/.swiftlint.yml
+.PHONY: format
+format:
+ @echo "Fixing code format with SwiftFormat"
+ @swiftformat $(TOOLKIT_PATH) --config $(TOOLKIT_PATH)/.swiftformat
+
+.PHONY: open
open:
open -a /Applications/Xcode.app $(TOOLKIT_PATH)
diff --git a/README.md b/README.md
index fcccd6f14..c37130628 100644
--- a/README.md
+++ b/README.md
@@ -20,6 +20,7 @@
**✨ Looking to build richer extensions?** Check out the Extensions API [here](https://github.com/raycast/extensions).
+
🚨 For anything that is not related to script commands, please [send us an email](mailto:feedback@raycast.com), use the feedback command within Raycast, or join the [Slack community](https://www.raycast.com/community).
@@ -35,20 +36,20 @@
To install new commands, follow these steps:
1. Choose a script from the [community repo](https://github.com/raycast/script-commands/tree/master/commands#apps) and save it into a new directory.
-
+
Scripts containing the word `.template.` in the filename require some values to be set (check [the troubleshooting section](#troubleshooting-and-faqs) for more information).
-
+
Alternatively, instead of creating a new directory you can reuse the repo's [`_enable-commands` folder](https://github.com/raycast/script-commands/tree/master/_enabled-commands).
-3. Open the Extensions tab in the Raycast preferences
-4. Click the plus button
-5. Click `Add Script Directory`
-6. Select directories containing your Script Commands
+
+2. Open the Extensions tab in the Raycast preferences
+3. Click the plus button
+4. Click `Add Script Directory`
+5. Select directories containing your Script Commands
**💡 Hint:** We recommend that you don't directly load the community script directories into Raycast to avoid potential restructuring and new script commands suddenly appearing in Raycast.

-
## Create your own Script Commands
To write your own custom Script Commands, go over the following steps:
@@ -57,7 +58,7 @@ To write your own custom Script Commands, go over the following steps:
2. Write and edit your script using your favourite code editor
3. Run your Script Command from the Raycast root search
-**💡 Hint:** If you choose to write your script in `Bash`, we highly recommend using the [Shellcheck](https://marketplace.visualstudio.com/items?itemName=timonwong.shellcheck) linter as this will ensure smooth running of your script. All scripts uploaded to GitHub will need to have been run through ShellCheck.
+**💡 Hint:** If you choose to write your script in `Bash`, we highly recommend using the [Shellcheck](https://marketplace.visualstudio.com/items?itemName=timonwong.shellcheck) linter as this will ensure smooth running of your script. All scripts uploaded to GitHub will need to have been run through ShellCheck.

@@ -65,30 +66,31 @@ To write your own custom Script Commands, go over the following steps:
These parameters are available for you to customize your Script Command in Raycast. For practical examples of how these should be used, as well as best practices and supported languages, please browse our templates and community-built scripts.
-| Name | Description | Required | App Version |
-|----------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------|---------------------|
-|schemaVersion | Schema version to prepare for future changes in the API. Currently there is only version 1 available. | Yes | 0.29+ |
-| title | Display name of the Script Command that is shown as title in the root search. | Yes | 0.29+ |
-| mode | Specifies how the script is executed and how the output is presented. [Details of the options for this parameter can be viewed here](https://github.com/raycast/script-commands/blob/master/documentation/OUTPUTMODES.md) | Yes | 0.29+ |
-| packageName | Display name of the package that is shown as subtitle in the root search. When not provided, the name will be inferred from the script directory name. | No | 0.29+ |
-| icon | Icon that is displayed in the root search. Can be an emoji, a file path (relative or full) or a remote URL (only https). Supported formats for images are PNG and JPEG. Please make sure to use small icons, recommended size - 64px. | No | 0.29+ |
-| iconDark | Same as `icon`, but for dark theme. If not specified, then `icon` will be used in both themes. | No | 1.3.0+ |
-| currentDirectoryPath | Path from which the script is executed. Default is the path of the script. | No | 0.29+ |
-| needsConfirmation | Specify `true` if you would like to show confirmation alert dialog before running the script. Can be helpful with destructive scripts like "Quit All Apps" or "Empty Trash". Default value is `false`. | No | 0.30+ |
-| refreshTime | Specify a refresh interval for inline mode scripts in seconds, minutes, hours or days. Examples: 10s, 1m, 12h, 1d. Note that the actual times can vary depending on how the OS prioritises scheduled work. The minimum refresh interval is 10 seconds. If you have more than 10 inline commands, only the first 10 will be refreshed automatically; the rest have to be manually refreshed by navigating to them and pressing `return`.| No | 0.31+ |
-| argument[1...3] | [Custom arguments, see Passing Arguments page](https://github.com/raycast/script-commands/blob/master/documentation/ARGUMENTS.md) for detail of how to use this field | No | 1.2.0+ |
-| author | Define an author name to be part of the script commands documentation | No | |
-| authorURL | Author social media, website, email or anything to help the users to get in touch | No | |
-| description | A brief description about the script command to be presented in the documentation | No | |
+| Name | Description | Required | App Version |
+| -------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- | ----------- |
+| schemaVersion | Schema version to prepare for future changes in the API. Currently there is only version 1 available. | Yes | 0.29+ |
+| title | Display name of the Script Command that is shown as title in the root search. | Yes | 0.29+ |
+| mode | Specifies how the script is executed and how the output is presented. [Details of the options for this parameter can be viewed here](https://github.com/raycast/script-commands/blob/master/documentation/OUTPUTMODES.md) | Yes | 0.29+ |
+| packageName | Display name of the package that is shown as subtitle in the root search. When not provided, the name will be inferred from the script directory name. | No | 0.29+ |
+| icon | Icon that is displayed in the root search. Can be an emoji, a file path (relative or full) or a remote URL (only https). Supported formats for images are PNG and JPEG. Please make sure to use small icons, recommended size - 64px. | No | 0.29+ |
+| iconDark | Same as `icon`, but for dark theme. If not specified, then `icon` will be used in both themes. | No | 1.3.0+ |
+| currentDirectoryPath | Path from which the script is executed. Default is the path of the script. | No | 0.29+ |
+| needsConfirmation | Specify `true` if you would like to show confirmation alert dialog before running the script. Can be helpful with destructive scripts like "Quit All Apps" or "Empty Trash". Default value is `false`. | No | 0.30+ |
+| refreshTime | Specify a refresh interval for inline mode scripts in seconds, minutes, hours or days. Examples: 10s, 1m, 12h, 1d. Note that the actual times can vary depending on how the OS prioritises scheduled work. The minimum refresh interval is 10 seconds. If you have more than 10 inline commands, only the first 10 will be refreshed automatically; the rest have to be manually refreshed by navigating to them and pressing `return`. | No | 0.31+ |
+| argument[1...3] | [Custom arguments, see Passing Arguments page](https://github.com/raycast/script-commands/blob/master/documentation/ARGUMENTS.md) for detail of how to use this field | No | 1.2.0+ |
+| author | Define an author name to be part of the Script Commands documentation | No | |
+| authorURL | Author social media, website, email or anything to help the users to get in touch | No | |
+| description | A brief description about the script command to be presented in the documentation | No | |
+| platform | Platform supported by the Script Command. Currently values supported: `macos` (default) and `windows` | No | |
### Output Mode
You can use the standard output to present messages in Raycast. Depending on the `mode`, the standard output of your scripts is differently presented.`fullOutput` and `inline` modes support ANSI Escape codes allowing to color generated output by changing its background and foreground color. [You can view the different output mode options as well as their various forms and color options here.](https://github.com/raycast/script-commands/blob/master/documentation/OUTPUTMODES.md)
-
### Error Handling
If the script exits with a status code not equal to 0, Raycast interprets it as failed and shows a toast that the script failed to run. If this script has inline or compact mode, the last line of the output will be used as an error message. Consider this example for a bash script:
+
```bash
if ! [[ $value =~ $regex ]] ; then
echo "Invalid value provided"
@@ -98,20 +100,21 @@ else
```
## Troubleshooting and FAQs
+
Why isn't my script appearing in Raycast?
-* Ensure the filename doesn't contain `.template.` string
-* Check that all required metadata parameters are provided. See the table above which parameters are required.
-* Ensure you use either `#` or `//` comments for metadata parameters
-* If nothing helps, try to go step by step from a [template](https://github.com/raycast/script-commands/tree/master/templates) Script Command or use one of the examples in this repo.
+- Ensure the filename doesn't contain `.template.` string
+- Check that all required metadata parameters are provided. See the table above which parameters are required.
+- Ensure you use either `#` or `//` comments for metadata parameters
+- If nothing helps, try to go step by step from a [template](https://github.com/raycast/script-commands/tree/master/templates) Script Command or use one of the examples in this repo.
Why isn't my Shell script working?
-* Ensure the filename doesn't contain `.template.` string
-* Run your code through [ShellCheck](https://www.shellcheck.net/) to check for syntax errors or unexpected characters
+- Ensure the filename doesn't contain `.template.` string
+- Run your code through [ShellCheck](https://www.shellcheck.net/) to check for syntax errors or unexpected characters
@@ -119,6 +122,7 @@ else
**We only allow Script Commands that run in a non-login shell in this repository as agreed on in our [contribution guidelines](https://github.com/raycast/script-commands/blob/master/CONTRIBUTING.md), due to any dependencies.**
However, if you need to run your local script as login-shell, you can specify an argument after shebang, e.g. `#!/bin/bash -l` for bash. We also append `/usr/local/bin` to `$PATH` variable so you can use your local shell commands without any additional steps. If this is not enough, you can always extend `$PATH` by adding `export PATH='/some/extra/path:$PATH'` at the top of your script.
+
## Community
diff --git a/Tools/Toolkit/.editorconfig b/Tools/Toolkit/.editorconfig
new file mode 100644
index 000000000..b3cc92190
--- /dev/null
+++ b/Tools/Toolkit/.editorconfig
@@ -0,0 +1,11 @@
+[*.swift]
+indent_style = space
+indent_size = 2
+tab_width = 2
+end_of_line = lf
+insert_final_newline = true
+max_line_length = 120
+trim_trailing_whitespace = true
+
+[*.ps1]
+end_of_line = crlf
diff --git a/Tools/Toolkit/.swift-version b/Tools/Toolkit/.swift-version
new file mode 100644
index 000000000..e0ea36fee
--- /dev/null
+++ b/Tools/Toolkit/.swift-version
@@ -0,0 +1 @@
+6.0
diff --git a/Tools/Toolkit/.swiftformat b/Tools/Toolkit/.swiftformat
new file mode 100644
index 000000000..423c5217b
--- /dev/null
+++ b/Tools/Toolkit/.swiftformat
@@ -0,0 +1,162 @@
+--acronyms ID,URL,UUID
+--allman false
+--anonymousforeach convert
+--assetliterals visual-width
+--asynccapturing
+--beforemarks
+--binarygrouping 4,8
+--callsiteparen default
+--categorymark "MARK: %c"
+--classthreshold 0
+--closingparen balanced
+--closurevoid remove
+--commas always
+--complexattrs preserve
+--computedvarattrs preserve
+--condassignment after-property
+--conflictmarkers reject
+--dateformat system
+--decimalgrouping 3,6
+--doccomments before-declarations
+--emptybraces no-space
+--enumnamespaces always
+--enumthreshold 0
+--equatablemacro none
+--exponentcase lowercase
+--exponentgrouping disabled
+--extensionacl on-extension
+--extensionlength 0
+--extensionmark "MARK: - %t + %c"
+--filemacro "#file"
+--fractiongrouping disabled
+--fragment false
+--funcattributes prev-line
+--generictypes
+--groupblanklines true
+--groupedextension "MARK: - %c"
+--guardelse next-line
+--header strip
+--hexgrouping 4,8
+--hexliteralcase uppercase
+--ifdef indent
+--importgrouping alpha
+--indent 2
+--indentcase false
+--indentstrings false
+--inferredtypes always
+--initcodernil false
+--inlinedforeach ignore
+--lifecycle
+--lineaftermarks true
+--linebreaks lf
+--markcategories true
+--markextensions always
+--marktypes always
+--maxwidth 120
+--modifierorder
+--nevertrailing
+--nilinit remove
+--noncomplexattrs
+--nospaceoperators
+--nowrapoperators
+--octalgrouping 4,8
+--operatorfunc spaced
+--organizationmode visibility
+--organizetypes actor,class,enum,struct
+--patternlet hoist
+--preservedecls
+--preserveacronyms
+--preservedsymbols Package
+--propertytypes infer-locals-only
+--ranges spaced
+--self remove
+--selfrequired
+--semicolons inline
+--shortoptionals except-properties
+--smarttabs enabled
+--someany true
+--sortedpatterns
+--sortswiftuiprops none
+--storedvarattrs preserve
+--stripunusedargs always
+--structthreshold 0
+--tabwidth unspecified
+--throwcapturing
+--timezone system
+--trailingclosures
+--trimwhitespace always
+--typeattributes prev-line
+--typeblanklines remove
+--typedelimiter space-after
+--typemark "MARK: - %t"
+--typemarks
+--typeorder
+--visibilitymarks
+--visibilityorder
+--voidtype void
+--wraparguments before-first
+--wrapcollections before-first
+--wrapconditions preserve
+--wrapeffects preserve
+--wrapenumcases always
+--wrapparameters default
+--wrapreturntype preserve
+--wrapternary default
+--wraptypealiases preserve
+--xcodeindentation disabled
+--xctestsymbols
+--yodaswap always
+
+--enable acronyms
+--enable blankLineAfterSwitchCase
+--enable isEmpty
+--enable markTypes
+--enable redundantVariable
+--enable sortSwitchCases
+--enable wrapConditionalBodies
+--enable wrapEnumCases
+--enable wrapSwitchCases
+
+--disable assertionFailures
+--disable blankLinesAtStartOfScope
+--disable blankLinesBetweenChainedFunctions
+--disable braces
+--disable docCommentsBeforeModifiers
+--disable extensionAccessControl
+--disable genericExtensions
+--disable headerFileName
+--disable initCoderUnavailable
+--disable leadingDelimiters
+--disable linebreakAtEndOfFile
+--disable linebreaks
+--disable modifierOrder
+--disable numberFormatting
+--disable opaqueGenericParameters
+--disable preferForLoop
+--disable redundantNilInit
+--disable redundantType
+--disable redundantVoidReturnType
+--disable spaceAroundBraces
+--disable spaceAroundBrackets
+--disable spaceAroundComments
+--disable spaceAroundGenerics
+--disable spaceAroundOperators
+--disable spaceAroundParens
+--disable spaceInsideBraces
+--disable spaceInsideBrackets
+--disable spaceInsideGenerics
+--disable strongOutlets
+--disable strongifiedSelf
+--disable typeSugar
+--disable unusedArguments
+--disable void
+--disable wrap
+--disable wrapArguments
+--disable wrapAttributes
+--disable wrapLoopBodies
+--disable wrapMultilineStatementBraces
+--disable wrapSingleLineComments
+--disable yodaConditions
+
+--enable fileHeader
+--header \nMIT License\nCopyright (c) 2020-{year} Raycast. All rights reserved.\n
diff --git a/.swiftlint.yml b/Tools/Toolkit/.swiftlint.yml
similarity index 90%
rename from .swiftlint.yml
rename to Tools/Toolkit/.swiftlint.yml
index 4b8ae8cdb..1b0a72847 100644
--- a/.swiftlint.yml
+++ b/Tools/Toolkit/.swiftlint.yml
@@ -1,11 +1,7 @@
indentation: 2
-included:
- - Tools/Toolkit
excluded:
- - Tools/Toolkit/.build
+ - .build
- _enabled-commands
- - commands
- - templates
disabled_rules:
- cyclomatic_complexity
- file_length
@@ -14,7 +10,6 @@ disabled_rules:
- todo # Use custom_todo
- unused_setter_value
- generic_type_name
- - identifier_name
- function_parameter_count
- type_name
- function_body_length
@@ -33,11 +28,12 @@ opt_in_rules:
- file_header
- overridden_super_call
- sorted_imports
- - unused_declaration
- - unused_import
- vertical_whitespace_closing_braces
- vertical_whitespace_opening_braces
- yoda_condition
+analyzer_rules:
+ - unused_declaration
+ - unused_import
indentation_width:
indentation_width: 2
trailing_comma:
diff --git a/Tools/Toolkit/Package.resolved b/Tools/Toolkit/Package.resolved
index b28930b2b..9bac5bd61 100644
--- a/Tools/Toolkit/Package.resolved
+++ b/Tools/Toolkit/Package.resolved
@@ -1,25 +1,15 @@
{
- "object": {
- "pins": [
- {
- "package": "swift-argument-parser",
- "repositoryURL": "https://github.com/apple/swift-argument-parser.git",
- "state": {
- "branch": null,
- "revision": "d2930e8fcf9c33162b9fcc1d522bc975e2d4179b",
- "version": "1.0.1"
- }
- },
- {
- "package": "swift-tools-support-core",
- "repositoryURL": "https://github.com/apple/swift-tools-support-core.git",
- "state": {
- "branch": null,
- "revision": "f9bbd6b80d67408021576adf6247e17c2e957d92",
- "version": "0.2.4"
- }
+ "originHash" : "47c7783adcb33e6ba4c3c4934f278dd5fa5bced40c9b4e101b6ea9a83858ca5c",
+ "pins" : [
+ {
+ "identity" : "swift-argument-parser",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/apple/swift-argument-parser.git",
+ "state" : {
+ "revision" : "626b5b7b2f45e1b0b1c6f4a309296d1d21d7311b",
+ "version" : "1.7.1"
}
- ]
- },
- "version": 1
+ }
+ ],
+ "version" : 3
}
diff --git a/Tools/Toolkit/Package.swift b/Tools/Toolkit/Package.swift
index 2d91c0bc5..db1f7f606 100644
--- a/Tools/Toolkit/Package.swift
+++ b/Tools/Toolkit/Package.swift
@@ -1,39 +1,34 @@
-// swift-tools-version:5.3
+// swift-tools-version:6.0
import PackageDescription
let package = Package(
name: "toolkit",
platforms: [
- .macOS(.v11),
+ .macOS(.v13),
],
dependencies: [
- .package(
- url: "https://github.com/apple/swift-tools-support-core.git",
- .upToNextMinor(from: "0.2.4")
- ),
.package(
url: "https://github.com/apple/swift-argument-parser.git",
- .upToNextMinor(from: "1.0.0")
+ .upToNextMajor(from: "1.5.0"),
),
],
targets: [
- .target(
+ .executableTarget(
name: "Toolkit",
dependencies: [
"ToolkitLibrary",
- .product(name: "SwiftToolsSupport-auto", package: "swift-tools-support-core"),
.product(name: "ArgumentParser", package: "swift-argument-parser"),
- ]
+ ],
),
.target(
name: "ToolkitLibrary",
- dependencies: [
- .product(name: "SwiftToolsSupport-auto", package: "swift-tools-support-core"),
- ]
+ dependencies: [],
),
.testTarget(
name: "ToolkitLibraryTests",
- dependencies: ["ToolkitLibrary"]),
- ]
+ dependencies: ["ToolkitLibrary"],
+ ),
+ ],
+ swiftLanguageModes: [.v6],
)
diff --git a/Tools/Toolkit/Sources/Toolkit/SubCommands/GenerateDocumentation.swift b/Tools/Toolkit/Sources/Toolkit/SubCommands/GenerateDocumentation.swift
index 97fe64d4a..480621618 100644
--- a/Tools/Toolkit/Sources/Toolkit/SubCommands/GenerateDocumentation.swift
+++ b/Tools/Toolkit/Sources/Toolkit/SubCommands/GenerateDocumentation.swift
@@ -1,15 +1,15 @@
//
-// MIT License
-// Copyright (c) 2020-2021 Raycast. All rights reserved.
+// MIT License
+// Copyright (c) 2020-2026 Raycast. All rights reserved.
//
import ArgumentParser
import ToolkitLibrary
extension ToolkitCommand {
- struct GenerateDocumentation: ParsableCommand {
- static var configuration = CommandConfiguration(
- abstract: "Generate the documentation in JSON and Markdown format"
+ struct GenerateDocumentation: AsyncParsableCommand {
+ static let configuration = CommandConfiguration(
+ abstract: "Generate the documentation in JSON and Markdown format",
)
@Argument(help: "Path of the Raycast extensions folder.\n")
@@ -18,23 +18,21 @@ extension ToolkitCommand {
@Argument(help: "Output file name for the Markdown documentation.\n")
var outputMarkdownFilename: String = "README.md"
- @Argument(help: "Output file name for the Markdown documentation.\n")
+ @Argument(help: "Output file name for the JSON documentation.\n")
var outputJSONFilename: String = "extensions.json"
- func run() throws {
+ func run() async throws {
do {
let dataManager = try DataManager(
extensionsPath: path,
- extensionsFilename: outputJSONFilename
+ extensionsFilename: outputJSONFilename,
)
- let toolkit = Toolkit(
- dataManager: dataManager
- )
+ let toolkit = Toolkit(dataManager: dataManager)
- try toolkit.generateDocumentation(
+ try await toolkit.generateDocumentation(
outputJSONFilename: outputJSONFilename,
- outputMarkdownFilename: outputMarkdownFilename
+ outputMarkdownFilename: outputMarkdownFilename,
)
Toolkit.raycastDescription()
diff --git a/Tools/Toolkit/Sources/Toolkit/SubCommands/Report.swift b/Tools/Toolkit/Sources/Toolkit/SubCommands/Report.swift
deleted file mode 100644
index d59ebee8f..000000000
--- a/Tools/Toolkit/Sources/Toolkit/SubCommands/Report.swift
+++ /dev/null
@@ -1,61 +0,0 @@
-//
-// MIT License
-// Copyright (c) 2020-2021 Raycast. All rights reserved.
-//
-
-import ArgumentParser
-import SwiftUI
-import ToolkitLibrary
-
-extension ToolkitCommand {
- struct Report: ParsableCommand {
- static var configuration = CommandConfiguration(
- abstract: "Generate a report about the health of the Script Commands"
- )
-
- @Option(
- name: [
- .customShort("t"),
- .customLong("type"),
- ],
- help: "\(Toolkit.ReportType.allOptions)\n "
- )
- var reportType: Toolkit.ReportType = .allScripts
-
- @Argument(
- help: "Path of the Raycast extensions folder.\n "
- )
- var path: String = "./commands"
-
- @Flag(help: "Print report without colors")
- var noColor: Bool = false
-
- func run() throws {
- do {
- let dataManager = try DataManager(
- extensionsPath: path
- )
-
- let toolkit = Toolkit(
- dataManager: dataManager
- )
-
- try toolkit.report(
- type: reportType,
- noColor: noColor
- )
- } catch {
- Toolkit.raycastDescription()
- Console.shared.writeRed("Error: \(error)")
- }
- }
- }
-}
-
-// MARK: - Expressible By Argument
-
-extension ToolkitLibrary.Toolkit.ReportType: ExpressibleByArgument {
- static var allOptions: String {
- Self.allValueStrings.joined(separator: "|")
- }
-}
diff --git a/Tools/Toolkit/Sources/Toolkit/SubCommands/SetExecutable.swift b/Tools/Toolkit/Sources/Toolkit/SubCommands/SetExecutable.swift
index 551b66fbe..cd9c4f731 100644
--- a/Tools/Toolkit/Sources/Toolkit/SubCommands/SetExecutable.swift
+++ b/Tools/Toolkit/Sources/Toolkit/SubCommands/SetExecutable.swift
@@ -1,31 +1,26 @@
//
-// MIT License
-// Copyright (c) 2020-2021 Raycast. All rights reserved.
+// MIT License
+// Copyright (c) 2020-2026 Raycast. All rights reserved.
//
import ArgumentParser
import ToolkitLibrary
extension ToolkitCommand {
- struct SetExecutable: ParsableCommand {
- static var configuration = CommandConfiguration(
- abstract: "Set file mode \"executable\" to Script Commands"
+ struct SetExecutable: AsyncParsableCommand {
+ static let configuration = CommandConfiguration(
+ abstract: "Set file mode \"executable\" to Script Commands",
)
@Argument(help: "Path of the Raycast extensions folder.\n")
var path: String = "./commands"
- func run() throws {
+ func run() async throws {
do {
- let dataManager = try DataManager(
- extensionsPath: path
- )
+ let dataManager = try DataManager(extensionsPath: path)
+ let toolkit = Toolkit(dataManager: dataManager)
- let toolkit = Toolkit(
- dataManager: dataManager
- )
-
- try toolkit.setScriptCommandsAsExecutable()
+ try await toolkit.setScriptCommandsAsExecutable()
} catch {
Toolkit.raycastDescription()
Console.shared.writeRed("Error: \(error)")
diff --git a/Tools/Toolkit/Sources/Toolkit/SubCommands/Version.swift b/Tools/Toolkit/Sources/Toolkit/SubCommands/Version.swift
index 9ebf25086..ea6f0b18c 100644
--- a/Tools/Toolkit/Sources/Toolkit/SubCommands/Version.swift
+++ b/Tools/Toolkit/Sources/Toolkit/SubCommands/Version.swift
@@ -1,6 +1,6 @@
//
-// MIT License
-// Copyright (c) 2020-2021 Raycast. All rights reserved.
+// MIT License
+// Copyright (c) 2020-2026 Raycast. All rights reserved.
//
import ArgumentParser
@@ -8,8 +8,8 @@ import ToolkitLibrary
extension ToolkitCommand {
struct Version: ParsableCommand {
- static var configuration = CommandConfiguration(
- abstract: "Print the current Toolkit version"
+ static let configuration = CommandConfiguration(
+ abstract: "Print the current Toolkit version",
)
func run() throws {
diff --git a/Tools/Toolkit/Sources/Toolkit/main.swift b/Tools/Toolkit/Sources/Toolkit/ToolkitCommand.swift
similarity index 53%
rename from Tools/Toolkit/Sources/Toolkit/main.swift
rename to Tools/Toolkit/Sources/Toolkit/ToolkitCommand.swift
index 09a50a1d7..2ab31e577 100644
--- a/Tools/Toolkit/Sources/Toolkit/main.swift
+++ b/Tools/Toolkit/Sources/Toolkit/ToolkitCommand.swift
@@ -1,25 +1,21 @@
//
-// MIT License
-// Copyright (c) 2020-2021 Raycast. All rights reserved.
+// MIT License
+// Copyright (c) 2020-2026 Raycast. All rights reserved.
//
-import Foundation
-
import ArgumentParser
+import Foundation
import ToolkitLibrary
-import TSCBasic
-struct ToolkitCommand: ParsableCommand {
- static var configuration = CommandConfiguration(
+@main
+struct ToolkitCommand: AsyncParsableCommand {
+ static let configuration = CommandConfiguration(
commandName: "toolkit",
abstract: "A tool to generate automatized documentation",
subcommands: [
GenerateDocumentation.self,
- Report.self,
SetExecutable.self,
Version.self,
- ]
+ ],
)
}
-
-ToolkitCommand.main()
diff --git a/Tools/Toolkit/Sources/ToolkitLibrary/Core/Console.swift b/Tools/Toolkit/Sources/ToolkitLibrary/Core/Console.swift
index b5bd32aac..667ae9757 100644
--- a/Tools/Toolkit/Sources/ToolkitLibrary/Core/Console.swift
+++ b/Tools/Toolkit/Sources/ToolkitLibrary/Core/Console.swift
@@ -1,47 +1,69 @@
//
-// MIT License
-// Copyright (c) 2020-2021 Raycast. All rights reserved.
+// MIT License
+// Copyright (c) 2020-2026 Raycast. All rights reserved.
//
import Foundation
-import TSCBasic
-public final class Console {
- private var noColor: Bool
- private let terminalController: TerminalController?
+// MARK: - Console
+
+public final class Console: @unchecked Sendable {
+ private enum ANSI {
+ static let bold = "\u{001B}[1m"
+ static let red = "\u{001B}[31m"
+ static let green = "\u{001B}[32m"
+ static let yellow = "\u{001B}[33m"
+ static let reset = "\u{001B}[0m"
+ }
public static let shared = Console()
- init(noColor: Bool = false) {
- self.noColor = noColor
- self.terminalController = TerminalController(stream: stdoutStream)
- }
+ init() {}
public func writeRed(_ message: String, bold: Bool = false, endLine: Bool = true) {
- write(string: message, color: .red, bold: bold, endLine: endLine)
+ write(message, color: ANSI.red, bold: bold, endLine: endLine)
}
public func writeYellow(_ message: String, bold: Bool = false, endLine: Bool = true) {
- write(string: message, color: .yellow, bold: bold, endLine: endLine)
+ write(message, color: ANSI.yellow, bold: bold, endLine: endLine)
}
public func writeGreen(_ message: String, bold: Bool = false, endLine: Bool = true) {
- write(string: message, color: .green, bold: bold, endLine: endLine)
+ write(message, color: ANSI.green, bold: bold, endLine: endLine)
}
public func write(_ message: String, bold: Bool = false, endLine: Bool = true) {
- write(string: message, color: .noColor, bold: bold, endLine: endLine)
+ write(message, color: nil, bold: bold, endLine: endLine)
}
- public func write(string: String, color: TerminalController.Color, bold: Bool = false, endLine: Bool = true) {
- terminalController?.write(string, inColor: noColor ? .noColor : color, bold: bold)
+ public func endLine() {
+ print()
+ }
+}
- if endLine {
- terminalController?.endLine()
+// MARK: - Private
+
+private extension Console {
+ func write(_ message: String, color: String?, bold: Bool, endLine: Bool) {
+ var output = ""
+
+ if bold {
+ output += ANSI.bold
+ }
+ if let color {
+ output += color
}
- }
- public func endLine() {
- terminalController?.endLine()
+ output += message
+
+ if bold || color != nil {
+ output += ANSI.reset
+ }
+
+ if endLine {
+ print(output)
+ } else {
+ print(output, terminator: "")
+ }
}
}
diff --git a/Tools/Toolkit/Sources/ToolkitLibrary/Core/Documentation/Documentation.swift b/Tools/Toolkit/Sources/ToolkitLibrary/Core/Documentation/Documentation.swift
index 0d07a8739..ebd4ce957 100644
--- a/Tools/Toolkit/Sources/ToolkitLibrary/Core/Documentation/Documentation.swift
+++ b/Tools/Toolkit/Sources/ToolkitLibrary/Core/Documentation/Documentation.swift
@@ -1,22 +1,21 @@
//
-// MIT License
-// Copyright (c) 2020-2021 Raycast. All rights reserved.
+// MIT License
+// Copyright (c) 2020-2026 Raycast. All rights reserved.
//
import Foundation
-import TSCBasic
-final class Documentation {
- private let fileSystem = TSCBasic.localFileSystem
+// MARK: - Documentation
- private let path: AbsolutePath
+final class Documentation {
+ private let path: URL
private let markdownFilename: String
private let jsonFilename: String
- init(path: AbsolutePath, jsonFilename: String, markdownFilename: String) {
- self.path = path
- self.jsonFilename = jsonFilename
+ init(path: URL, jsonFilename: String, markdownFilename: String) {
+ self.path = path
+ self.jsonFilename = jsonFilename
self.markdownFilename = markdownFilename
}
@@ -30,31 +29,19 @@ final class Documentation {
private extension Documentation {
func generateMarkdown(for raycastData: RaycastData) throws {
- let documentFilePath = path.appending(
- component: markdownFilename
- )
+ let documentFilePath = path.appendingPathComponent(markdownFilename)
guard let data = markdownData(for: raycastData) else {
return
}
- try fileSystem.writeFileContents(
- documentFilePath,
- bytes: ByteString(data.uint8Array)
- )
+ try data.write(to: documentFilePath, options: .atomic)
}
func generateJSON(for raycastData: RaycastData) throws {
- let documentFilePath = path.appending(
- component: jsonFilename
- )
-
+ let documentFilePath = path.appendingPathComponent(jsonFilename)
let data = try raycastData.toData()
-
- try fileSystem.writeFileContents(
- documentFilePath,
- bytes: ByteString(data.uint8Array)
- )
+ try data.write(to: documentFilePath, options: .atomic)
}
func markdownData(for raycastData: RaycastData) -> Data? {
@@ -70,40 +57,35 @@ private extension Documentation {
sortedGroups.forEach { group in
contentString += .newLine + group.sectionTitle
-
contentString += renderMarkdown(for: group)
}
let markdown = """
-
- \(renderBadges())
+
+ \(renderBadges())
- # Raycast Script Commands
+ # Raycast Script Commands
- [Raycast](https://raycast.com) lets you control your tools with a few keystrokes
- and Script Commands makes it possible to execute scripts from anywhere on your desktop.
- They are a great way to speed up every-day tasks such as converting data, opening bookmarks
- or triggering dev workflows.
+ [Raycast](https://raycast.com) lets you control your tools with a few keystrokes
+ and Script Commands makes it possible to execute scripts from anywhere on your desktop.
+ They are a great way to speed up every-day tasks such as converting data, opening bookmarks
+ or triggering dev workflows.
- This repository contains sample commands and documentation to write your own ones.
+ This repository contains sample commands and documentation to write your own ones.
- ### Categories
- \(tableOfContents)\(contentString)
+ ### Categories
+ \(tableOfContents)\(contentString)
- ## Community
+ ## Community
- This is a shared place and we're always looking for new Script Commands or other ways to improve Raycast.
- If you have anything cool to show, please send us a pull request. If we screwed something up,
- please report a bug. Join our
- [Slack community](https://www.raycast.com/community)
- to brainstorm ideas with like-minded folks.
- """
-
- guard let contentData = markdown.data(using: .utf8) else {
- return nil
- }
+ This is a shared place and we're always looking for new Script Commands or other ways to improve Raycast.
+ If you have anything cool to show, please send us a pull request. If we screwed something up,
+ please report a bug. Join our
+ [Slack community](https://www.raycast.com/community)
+ to brainstorm ideas with like-minded folks.
+ """
- return contentData
+ return markdown.data(using: .utf8)
}
func renderMarkdown(for group: Group, headline: Bool = false) -> String {
@@ -116,8 +98,10 @@ private extension Documentation {
}
contentString += .newLine
- contentString += .newLine + "| Icon | Title | Description | Author | Args | Templ | Lang |"
- contentString += .newLine + "| :--: | ----- | ----------- | :----: | :--: | :---: | :--: |"
+ contentString +=
+ .newLine + "| Icon | Title | Description | Author | Args | Templ | Lang | Platform |"
+ contentString +=
+ .newLine + "| :--: | ----- | ----------- | :----: | :--: | :---: | :--: | :------: |"
for scriptCommand in group.scriptCommands.sorted() {
contentString += scriptCommand.markdownDescription
@@ -128,7 +112,7 @@ private extension Documentation {
for subGroup in subGroups {
contentString += renderMarkdown(
for: subGroup,
- headline: true
+ headline: true,
)
}
}
@@ -137,14 +121,16 @@ private extension Documentation {
}
func renderBadges() -> String {
- let logo = "data:image/png;base64,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"
+ let logo =
+ "data:image/png;base64,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"
- let style = "for-the-badge"
+ let style = "for-the-badge"
let labelColor = "202123"
- let dataURL = "https:%2F%2Fraw.githubusercontent.com%2Fraycast%2Fscript-commands%2Fmaster%2Fcommands%2Fextensions.json"
- let jsonPath = "$.totalScriptCommands"
+ let dataURL =
+ "https:%2F%2Fraw.githubusercontent.com%2Fraycast%2Fscript-commands%2Fmaster%2Fcommands%2Fextensions.json"
+ let jsonPath = "$.totalScriptCommands"
- let badges = """
+ return """