Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

All notable changes to this project will be documented in this file.

## [1.48.0] (25/09/2025)
In this version we are introducing TAB autocompletion for the CLI commands. It should autocomplete command names, flags, and template handles and names.
To enable it, run `silverfin config --set-autocompletion` and follow the instructions.

## [1.47.1] (13/11/2025)
- Fix: Update authorize command to use user-inputted firm ID when calling `getFirmName` function rather than default firm ID

Expand All @@ -24,7 +28,7 @@ All notable changes to this project will be documented in this file.

## [1.45.0] (28/08/2025)
- A new config file attribute `test_firm_id` was added for account templates and reconciliations texts.
Adding it with a specific firm will make sure that this firm is used for the Github actions.
Adding it with a specific firm will make sure that this firm is used for the Github actions.

## [1.44.0] (11/08/2025)
- `create-account-template` command will now create an empty .yml file
Expand Down
7 changes: 6 additions & 1 deletion bin/cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ const path = require("path");
const { consola } = require("consola");
const { runCommandChecks } = require("../lib/cli/utils");
const { CwdValidator } = require("../lib/cli/cwdValidator");
const { AutoCompletions } = require("../lib/cli/autoCompletions");

const firmIdDefault = cliUtils.loadDefaultFirmId();
cliUtils.handleUncaughtErrors();
Expand Down Expand Up @@ -536,8 +537,9 @@ program
.addOption(new Option("--refresh-partner-token <partnerId>", "Get a new partner api key using the stored api key"))
.addOption(new Option("--set-host <host>", "Set a custom host for the Silverfin API (e.g. https://live.getsilverfin.com)"))
.addOption(new Option("--get-host", "Get the current host for the Silverfin API"))
.addOption(new Option("--set-autocompletion", "Set TAB autocompletion for the CLI"))
.action(async (options) => {
cliUtils.checkUniqueOption(["setFirm", "getFirm", "listAll", "updateName", "refreshToken", "refreshPartnerToken", "setHost", "getHost"], options);
cliUtils.checkUniqueOption(["setFirm", "getFirm", "listAll", "updateName", "refreshToken", "refreshPartnerToken", "setHost", "getHost", "setAutocompletion"], options);
if (options.setFirm) {
firmCredentials.setDefaultFirmId(options.setFirm);
const currentDirectory = path.basename(process.cwd());
Expand Down Expand Up @@ -592,6 +594,9 @@ program
const host = firmCredentials.getHost();
consola.info(`Current host: ${host}`);
}
if (options.setAutocompletion) {
AutoCompletions.set();
}
});

program
Expand Down
39 changes: 39 additions & 0 deletions lib/cli/autoCompletions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
const fs = require("fs");
const path = require("path");
const { consola } = require("consola");
const homedir = require("os").homedir();

// Create the necesary files to enable tab auto-completion for your CLI tool
class AutoCompletions {
static #SF_FOLDER_PATH = path.resolve(homedir, ".silverfin/");
static #SCRIPT_ORIGIN_PATH = path.resolve(__dirname, "../../resources/autoCompletion/autocomplete");
static #SCRIPT_DESTINATION_PATH = path.resolve(this.#SF_FOLDER_PATH, "autocomplete");

static set() {
this.#copyFile();
this.#addToShellConfig();
}

static #copyFile() {
try {
if (!fs.existsSync(this.#SF_FOLDER_PATH)) {
fs.mkdirSync(this.#SF_FOLDER_PATH, { recursive: true });
}

if (fs.existsSync(this.#SCRIPT_DESTINATION_PATH)) {
fs.unlinkSync(this.#SCRIPT_DESTINATION_PATH);
}

fs.copyFileSync(this.#SCRIPT_ORIGIN_PATH, this.#SCRIPT_DESTINATION_PATH);
} catch (error) {
consola.error("Error copying the auto-completion script:", error);
}
}

static #addToShellConfig() {
consola.info(`To enable auto-completions, add the following line to your shell config (e.g. ~/.zshrc or ~/.bashrc):`);
consola.info(`source ${this.#SCRIPT_DESTINATION_PATH}`);
}
}

module.exports = { AutoCompletions };
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "silverfin-cli",
"version": "1.47.1",
"version": "1.48.0",
"description": "Command line tool for Silverfin template development",
"main": "index.js",
"license": "MIT",
Expand Down
93 changes: 93 additions & 0 deletions resources/autoCompletion/autocomplete
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
#!/usr/bin/env bash
_complete_directories() {
local directory="$1"
local cur="$2"
if [ -d "$directory" ]; then
local IFS=$'\n'
local dirs=()

# Use appropriate find command based on availability
if command -v gfind &>/dev/null; then
# macOS with GNU findutils
dirs=($(gfind "$directory" -mindepth 1 -maxdepth 1 -type d -printf '%f\n'))
elif find --version 2>/dev/null | grep -q GNU; then
# Linux with GNU find
dirs=($(find "$directory" -mindepth 1 -maxdepth 1 -type d -printf '%f\n'))
else
# BSD find (macOS default) - use alternative
dirs=($(find "$directory" -mindepth 1 -maxdepth 1 -type d -exec basename {} \;))
fi

COMPREPLY=()
for dir in "${dirs[@]}"; do
if [[ "$dir" == "$cur"* ]]; then
if [[ "$dir" == *" "* ]]; then
COMPREPLY+=("\"$dir\"")
else
COMPREPLY+=("$dir")
fi
fi
done
fi
}
_silverfin() {
local cur=${COMP_WORDS[COMP_CWORD]}
local prev=${COMP_WORDS[COMP_CWORD - 1]}
local command=${COMP_WORDS[1]}
COMPREPLY=()
# Command
if [ $COMP_CWORD -eq 1 ]; then
local sf_commands=""
sf_commands=$(silverfin --help 2>/dev/null | grep -E "^\s+[a-z][a-z-]*\s" | awk '{print $1}' | tr '\n' ' ')
# Fallback
if [ -z "$sf_commands" ]; then
sf_commands="import-reconciliation update-reconciliation create-reconciliation import-export-file update-export-file create-export-file import-account-template update-account-template create-account-template import-shared-part update-shared-part create-shared-part add-shared-part remove-shared-part run-test create-test authorize authorize-partner stats config get-reconciliation-id get-export-file-id get-account-template-id get-shared-part-id development-mode update help"
fi
COMPREPLY=($(compgen -W "$sf_commands" -- "$cur"))
return 0
fi
# Template name
case "$prev" in
--handle | -h)
if [[ "$command" == *-reconciliation ]]; then
_complete_directories "./reconciliation_texts" "$cur"
fi
return 0
;;
--name | -n)
if [[ "$command" == *-export-file ]]; then
_complete_directories "./export_files" "$cur"
elif [[ "$command" == *-account-template ]]; then
_complete_directories "./account_templates" "$cur"
fi
return 0
;;
--shared-part | -s)
if [[ "$command" == *-shared-part ]]; then
_complete_directories "./shared_parts" "$cur"
fi
return 0
;;
esac
# Flags
if [[ "$cur" == --* ]]; then
local flags=""
flags=$(silverfin "$command" --help 2>/dev/null | grep -oE -- '--[a-zA-Z][a-zA-Z0-9-]*' | sort -u | tr '\n' ' ')
COMPREPLY=($(compgen -W "$flags" -- "$cur"))
return 0
fi
}
_silverfin_init_completion() {
if [[ -n ${ZSH_VERSION-} ]]; then
if ! command -v bashcompinit >/dev/null 2>&1; then
autoload -U bashcompinit
fi
if ! command -v complete >/dev/null 2>&1; then
bashcompinit
fi
fi
if command -v complete >/dev/null 2>&1; then
complete -F _silverfin silverfin
fi
}
_silverfin_init_completion