From bd3d391fbeef2137a476f65951541b718186c8ad Mon Sep 17 00:00:00 2001 From: Ramesh Padmanabhaiah Date: Sat, 6 Jun 2026 11:18:28 -0700 Subject: [PATCH] Remove migrated utility commands --- .github/workflows/tests.yml | 2 - CHANGELOG.md | 2 + README.md | 24 +- STANDARDS.md | 9 +- bin/caff | 2 - bin/sort-in-place | 2 - cli/bash/commands/basectl/README.md | 7 +- .../basectl/tests/runtime-dispatch.bats | 22 +- cli/bash/commands/caff/README.md | 9 - cli/bash/commands/caff/caff.sh | 136 ----------- cli/bash/commands/caff/tests/caff.bats | 225 ------------------ cli/bash/commands/sort-in-place/README.md | 9 - .../commands/sort-in-place/sort-in-place.sh | 76 ------ .../sort-in-place/tests/sort-in-place.bats | 78 ------ docs/execution-model.md | 22 +- 15 files changed, 34 insertions(+), 591 deletions(-) delete mode 100755 bin/caff delete mode 100755 bin/sort-in-place delete mode 100644 cli/bash/commands/caff/README.md delete mode 100644 cli/bash/commands/caff/caff.sh delete mode 100644 cli/bash/commands/caff/tests/caff.bats delete mode 100644 cli/bash/commands/sort-in-place/README.md delete mode 100644 cli/bash/commands/sort-in-place/sort-in-place.sh delete mode 100644 cli/bash/commands/sort-in-place/tests/sort-in-place.bats diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 20aa7d2..760b292 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -44,8 +44,6 @@ jobs: run: | bats \ cli/bash/commands/basectl/tests/*.bats \ - cli/bash/commands/caff/tests/caff.bats \ - cli/bash/commands/sort-in-place/tests/sort-in-place.bats \ lib/bash/file/tests/lib_file.bats \ lib/bash/git/tests/lib_git.bats \ lib/bash/runtime/tests/runtime_bashrc.bats \ diff --git a/CHANGELOG.md b/CHANGELOG.md index c4beff6..0b13780 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -51,6 +51,8 @@ and Base versions are tracked in the repo-root `VERSION` file. default, with explicit `--public` and `--private` visibility flags. - Updated Base-managed shell profile sections to use shorter `>>> base: ...` markers, clearer overwrite guidance, and quoted source paths. +- Moved the optional `caff` and `sort-in-place` utility CLIs out of Base and + into `codeforester/base-platform-tools`. ### Fixed diff --git a/README.md b/README.md index ee7daf9..0374391 100644 --- a/README.md +++ b/README.md @@ -625,16 +625,17 @@ invoke it from whatever shell state they already had. Base exposes its own commands through `$BASE_HOME/bin`. That directory is added to `PATH` by Base's managed shell startup snippets. -`bin/basectl` is the control-plane command. Additional public commands, when -needed, are tiny real launcher files in `bin/` that delegate to `basectl`; their -implementation remains under `cli/bash/commands//` or, in the future, +`bin/basectl` is the control-plane command. Additional Base-owned public +commands, when needed, are tiny real launcher files in `bin/` that delegate to +`basectl`; their implementation remains under +`cli/bash/commands//` or, in the future, `cli/python/commands//`. -Example launcher: +Example launcher for a hypothetical Base-owned Bash command: ```bash #!/usr/bin/env bash -exec "$(dirname "$0")/basectl" caff "$@" +exec "$(dirname "$0")/basectl" example "$@" ``` Projects expose their own commands through `$PROJECT_ROOT/bin`. When @@ -1017,15 +1018,14 @@ Those defaults are intended to stay conservative: - prompt defaults - history behavior -## Bonus Utilities +## Optional Utility Tools -Base currently exposes a small number of convenience utilities through -`$BASE_HOME/bin`, including `caff` and `sort-in-place`. These are useful helper -commands that share Base's command conventions, but they are not the core -workspace orchestration surface. +Base no longer owns general-purpose utility CLIs such as `caff` and +`sort-in-place`. Those tools live in +[`codeforester/base-platform-tools`](https://github.com/codeforester/base-platform-tools), +which is the optional platform/SRE utility layer for Base-managed workspaces. -As Base matures, bonus utilities may stay documented as extras or move behind a -clearer namespace. The control-plane surface remains `basectl`. +The Base control-plane surface remains `basectl`. ## What Base Is Responsible For diff --git a/STANDARDS.md b/STANDARDS.md index 8552268..a29ab94 100644 --- a/STANDARDS.md +++ b/STANDARDS.md @@ -37,11 +37,12 @@ layer boundaries rather than placing logic wherever it is easiest to call. `PATH`. Public commands in `bin/` should be real launcher files, not symlinks. Keep them -small and delegate into the command implementation. For a Bash command: +small and delegate into the command implementation. For a hypothetical Bash +command: ```bash #!/usr/bin/env bash -exec "$(dirname "$0")/basectl" caff "$@" +exec "$(dirname "$0")/basectl" example "$@" ``` The implementation still belongs under: @@ -417,8 +418,8 @@ Base-owned Bash CLIs should live in per-command directories: ```text cli/bash/commands/ - caff/ - caff.sh + example/ + example.sh README.md tests/ ``` diff --git a/bin/caff b/bin/caff deleted file mode 100755 index 5fe284b..0000000 --- a/bin/caff +++ /dev/null @@ -1,2 +0,0 @@ -#!/usr/bin/env bash -exec "$(dirname "$0")/basectl" caff "$@" diff --git a/bin/sort-in-place b/bin/sort-in-place deleted file mode 100755 index 645b6f9..0000000 --- a/bin/sort-in-place +++ /dev/null @@ -1,2 +0,0 @@ -#!/usr/bin/env bash -exec "$(dirname "$0")/basectl" sort-in-place "$@" diff --git a/cli/bash/commands/basectl/README.md b/cli/bash/commands/basectl/README.md index 8645a58..45d6a46 100644 --- a/cli/bash/commands/basectl/README.md +++ b/cli/bash/commands/basectl/README.md @@ -16,10 +16,9 @@ The public entrypoint lives at `bin/basectl`. It establishes the Base runtime for command implementations, then sources this command implementation and calls `main`. -`basectl` also dispatches direct command names by convention. For example, -`basectl caff` loads `cli/bash/commands/caff/caff.sh`. Public convenience -commands in `$BASE_HOME/bin`, such as `bin/caff`, should remain tiny launchers -that delegate to `basectl`. +`basectl` also dispatches direct Base-owned command names by convention when +such command directories exist. Optional utility CLIs such as `caff` and +`sort-in-place` live in `codeforester/base-platform-tools` instead of Base core. ## Current subcommands diff --git a/cli/bash/commands/basectl/tests/runtime-dispatch.bats b/cli/bash/commands/basectl/tests/runtime-dispatch.bats index 9e19cb9..d53dddd 100644 --- a/cli/bash/commands/basectl/tests/runtime-dispatch.bats +++ b/cli/bash/commands/basectl/tests/runtime-dispatch.bats @@ -192,15 +192,8 @@ EOF [[ "$output" == *"ARGS=-m base_setup --dry-run demo"* ]] } -@test "basectl dispatches command implementations by command name" { - run_basectl sort-in-place --help - - [ "$status" -eq 0 ] - [[ "$output" == *"Sort text files in place."* ]] -} - @test "basectl treats path-like arguments as scripts before command names" { - local script_path="$TEST_TMPDIR/sort-in-place" + local script_path="$TEST_TMPDIR/demo-script" cat > "$script_path" <<'EOF' main() { @@ -234,16 +227,3 @@ EOF [[ "$output" == *"declare -rx BASE_BASH_COMMAND_DIR=\"$script_dir\""* ]] [[ "$output" == *"declare -rx BASE_BASH_COMMAND_SCRIPT=\"$script_dir/inspect-command-env.sh\""* ]] } - -@test "sort-in-place launcher delegates through basectl" { - local input_file="$TEST_TMPDIR/input.txt" - - printf 'b\na\nb\n' > "$input_file" - run env \ - HOME="$TEST_HOME" \ - PATH="$BASE_REPO_ROOT/bin:/usr/bin:/bin:/usr/sbin:/sbin" \ - sort-in-place -u "$input_file" - - [ "$status" -eq 0 ] - [ "$(cat "$input_file")" = $'a\nb' ] -} diff --git a/cli/bash/commands/caff/README.md b/cli/bash/commands/caff/README.md deleted file mode 100644 index 52f52c4..0000000 --- a/cli/bash/commands/caff/README.md +++ /dev/null @@ -1,9 +0,0 @@ -# `caff` - -Caffeinate a named process by finding its PID and running macOS `caffeinate` -against that process. - -Public invocation is exposed by the launcher at `bin/caff`; the implementation -lives here so command code, documentation, and future tests stay together. - -This is a Base bonus utility, not part of the core workspace orchestration surface. diff --git a/cli/bash/commands/caff/caff.sh b/cli/bash/commands/caff/caff.sh deleted file mode 100644 index e33af90..0000000 --- a/cli/bash/commands/caff/caff.sh +++ /dev/null @@ -1,136 +0,0 @@ -#!/usr/bin/env bash - -# -# caff: call caffeinate for a named process. -# - -caff_show_help() { - cat <<'EOF' -Caffeinate a named process. - -Usage: - caff [-s] - -Options: - -s Suppress the already-caffeinating message. - -h, --help Show this help text. -EOF -} - -caff_describe() { - printf '%s\n' "Caffeinate a named process" -} - -caff_first_pgrep_match() { - local pattern="$1" - local pgrep_output status - - pgrep_output="$(pgrep "$pattern" 2>/dev/null)" - status=$? - if [[ "$status" -eq 0 ]]; then - [[ -n "$pgrep_output" ]] || return 1 - printf '%s\n' "$pgrep_output" | sed -n '1p' - return 0 - fi - if [[ "$status" -eq 1 ]]; then - return 1 - fi - - print_error "Unable to query process list for '$pattern'." - return 2 -} - -caff_wait_pid_from_caffeinate_args() { - local args_line="$1" - local arg rest - local waiting_for_pid=false - local args=() - - read -r -a args <<< "$args_line" - for arg in "${args[@]:1}"; do - if [[ "$waiting_for_pid" == true ]]; then - printf '%s\n' "$arg" - return 0 - fi - - case "$arg" in - -w) - waiting_for_pid=true - ;; - -w[0-9]*) - printf '%s\n' "${arg#-w}" - return 0 - ;; - -*w*) - rest="${arg#*w}" - if [[ -n "$rest" ]]; then - printf '%s\n' "$rest" - return 0 - fi - waiting_for_pid=true - ;; - esac - done - - return 1 -} - -main() { - local silent=0 - local process_name - local target_pid - local caffeinate_pid - local caffeinated_pid - local pgrep_status - - case "${1:-}" in - -h|--help) - caff_show_help - return 0 - ;; - --describe) - caff_describe - return 0 - ;; - -s) - silent=1 - shift - ;; - esac - - if ! command -v caffeinate >/dev/null 2>&1; then - print_error "There is no caffeinate command on your system." - return 1 - fi - - if (($# != 1)); then - print_error "A process name is required." - caff_show_help >&2 - return 2 - fi - - process_name="$1" - target_pid="$(caff_first_pgrep_match "$process_name")" - pgrep_status=$? - if [[ "$pgrep_status" -eq 1 ]]; then - print_warn "'$process_name' process is not running." - return 1 - fi - [[ "$pgrep_status" -eq 0 ]] || return 1 - - caffeinate_pid="$(caff_first_pgrep_match caffeinate)" - pgrep_status=$? - if [[ "$pgrep_status" -eq 2 ]]; then - return 1 - fi - if [[ "$pgrep_status" -eq 0 ]]; then - caffeinated_pid="$(ps -o args= -p "$caffeinate_pid" | while IFS= read -r line; do caff_wait_pid_from_caffeinate_args "$line" && break; done)" - if [[ "$caffeinated_pid" == "$target_pid" ]]; then - ((silent)) || printf '%s\n' "Already caffeinating: $process_name pid=$target_pid, caffeinate pid=$caffeinate_pid" - return 0 - fi - fi - - printf '%s\n' "Caffeinating PID $target_pid" - caffeinate -iw "$target_pid" & disown -} diff --git a/cli/bash/commands/caff/tests/caff.bats b/cli/bash/commands/caff/tests/caff.bats deleted file mode 100644 index e6cb241..0000000 --- a/cli/bash/commands/caff/tests/caff.bats +++ /dev/null @@ -1,225 +0,0 @@ -#!/usr/bin/env bats - -load ../../../../../lib/bash/tests/test_helper.sh - -setup() { - setup_test_tmpdir - TEST_HOME="$TEST_TMPDIR/home" - TEST_MOCKBIN="$TEST_TMPDIR/mockbin" - TEST_STATE_DIR="$TEST_TMPDIR/state" - mkdir -p "$TEST_HOME" "$TEST_MOCKBIN" "$TEST_STATE_DIR" -} - -run_caff() { - run env \ - HOME="$TEST_HOME" \ - PATH="$TEST_MOCKBIN:/usr/bin:/bin:/usr/sbin:/sbin" \ - "$BASE_REPO_ROOT/bin/basectl" caff "$@" -} - -create_caffeinate_stub() { - cat > "$TEST_MOCKBIN/caffeinate" <<'EOF' -#!/usr/bin/env bash -if [[ -n "${CAFF_TEST_RECORD:-}" ]]; then - printf '%s\n' "$*" > "$CAFF_TEST_RECORD" -fi -sleep 0.2 -EOF - chmod +x "$TEST_MOCKBIN/caffeinate" -} - -create_pgrep_stub() { - cat > "$TEST_MOCKBIN/pgrep" <<'EOF' -#!/usr/bin/env bash -if [[ "${CAFF_TEST_PGREP_FAIL:-}" == "${1:-}" ]]; then - printf 'pgrep failed for %s\n' "$1" >&2 - exit 2 -fi -case "${1:-}" in - caffeinate) - [[ -n "${CAFF_TEST_CAFFEINATE_PID:-}" ]] && printf '%s\n' "$CAFF_TEST_CAFFEINATE_PID" - ;; - "${CAFF_TEST_PROCESS_NAME:-}") - [[ -n "${CAFF_TEST_TARGET_PID:-}" ]] && printf '%s\n' "$CAFF_TEST_TARGET_PID" - ;; -esac -EOF - chmod +x "$TEST_MOCKBIN/pgrep" -} - -create_ps_stub() { - cat > "$TEST_MOCKBIN/ps" <<'EOF' -#!/usr/bin/env bash -printf 'ARGS\n' -if [[ -n "${CAFF_TEST_PS_ARGS:-}" ]]; then - printf '%s\n' "$CAFF_TEST_PS_ARGS" -elif [[ -n "${CAFF_TEST_CAFFEINATED_PID:-}" ]]; then - printf 'caffeinate -iw %s\n' "$CAFF_TEST_CAFFEINATED_PID" -fi -EOF - chmod +x "$TEST_MOCKBIN/ps" -} - -create_core_tool_links_without_caffeinate() { - local tool - local tool_path - - for tool in uname dirname readlink basename; do - tool_path="$(command -v "$tool")" - ln -s "$tool_path" "$TEST_MOCKBIN/$tool" - done -} - -wait_for_record() { - local record_file="$1" - local attempt - - for attempt in 1 2 3 4 5; do - [[ -f "$record_file" ]] && return 0 - sleep 0.1 - done - - return 1 -} - -@test "caff prints help" { - run_caff --help - - [ "$status" -eq 0 ] - [[ "$output" == *"Usage:"* ]] - [[ "$output" == *"caff [-s] "* ]] -} - -@test "caff fails when caffeinate is unavailable" { - create_core_tool_links_without_caffeinate - - run env \ - HOME="$TEST_HOME" \ - PATH="$TEST_MOCKBIN:/bin:/usr/sbin:/sbin" \ - "$BASE_REPO_ROOT/bin/basectl" caff worker - - [ "$status" -eq 1 ] - [[ "$output" == *"There is no caffeinate command on your system."* ]] -} - -@test "caff requires exactly one process name" { - create_caffeinate_stub - - run_caff - - [ "$status" -eq 2 ] - [[ "$output" == *"A process name is required."* ]] - [[ "$output" == *"Usage:"* ]] -} - -@test "caff warns when the target process is not running" { - create_caffeinate_stub - create_pgrep_stub - - run_caff worker - - [ "$status" -eq 1 ] - [[ "$output" == *"'worker' process is not running."* ]] -} - -@test "caff starts caffeinate for the first matching process" { - local record_file="$TEST_STATE_DIR/caffeinate.args" - - create_caffeinate_stub - create_pgrep_stub - create_ps_stub - - run env \ - HOME="$TEST_HOME" \ - PATH="$TEST_MOCKBIN:/usr/bin:/bin:/usr/sbin:/sbin" \ - CAFF_TEST_PROCESS_NAME=worker \ - CAFF_TEST_TARGET_PID=1234 \ - CAFF_TEST_RECORD="$record_file" \ - "$BASE_REPO_ROOT/bin/basectl" caff worker - - [ "$status" -eq 0 ] - [[ "$output" == *"Caffeinating PID 1234"* ]] - wait_for_record "$record_file" - [ "$(cat "$record_file")" = "-iw 1234" ] -} - -@test "caff does not start another caffeinate for an already caffeinated process" { - local record_file="$TEST_STATE_DIR/caffeinate.args" - - create_caffeinate_stub - create_pgrep_stub - create_ps_stub - - run env \ - HOME="$TEST_HOME" \ - PATH="$TEST_MOCKBIN:/usr/bin:/bin:/usr/sbin:/sbin" \ - CAFF_TEST_PROCESS_NAME=worker \ - CAFF_TEST_TARGET_PID=1234 \ - CAFF_TEST_CAFFEINATE_PID=9999 \ - CAFF_TEST_CAFFEINATED_PID=1234 \ - CAFF_TEST_RECORD="$record_file" \ - "$BASE_REPO_ROOT/bin/basectl" caff worker - - [ "$status" -eq 0 ] - [[ "$output" == *"Already caffeinating: worker pid=1234, caffeinate pid=9999"* ]] - [ ! -e "$record_file" ] -} - -@test "caff recognizes already caffeinated process when -w is a separate option" { - local record_file="$TEST_STATE_DIR/caffeinate.args" - - create_caffeinate_stub - create_pgrep_stub - create_ps_stub - - run env \ - HOME="$TEST_HOME" \ - PATH="$TEST_MOCKBIN:/usr/bin:/bin:/usr/sbin:/sbin" \ - CAFF_TEST_PROCESS_NAME=worker \ - CAFF_TEST_TARGET_PID=1234 \ - CAFF_TEST_CAFFEINATE_PID=9999 \ - CAFF_TEST_PS_ARGS="caffeinate -i -w 1234" \ - CAFF_TEST_RECORD="$record_file" \ - "$BASE_REPO_ROOT/bin/basectl" caff worker - - [ "$status" -eq 0 ] - [[ "$output" == *"Already caffeinating: worker pid=1234, caffeinate pid=9999"* ]] - [ ! -e "$record_file" ] -} - -@test "caff recognizes already caffeinated process when -w appears before other options" { - local record_file="$TEST_STATE_DIR/caffeinate.args" - - create_caffeinate_stub - create_pgrep_stub - create_ps_stub - - run env \ - HOME="$TEST_HOME" \ - PATH="$TEST_MOCKBIN:/usr/bin:/bin:/usr/sbin:/sbin" \ - CAFF_TEST_PROCESS_NAME=worker \ - CAFF_TEST_TARGET_PID=1234 \ - CAFF_TEST_CAFFEINATE_PID=9999 \ - CAFF_TEST_PS_ARGS="caffeinate -w 1234 -i" \ - CAFF_TEST_RECORD="$record_file" \ - "$BASE_REPO_ROOT/bin/basectl" caff worker - - [ "$status" -eq 0 ] - [[ "$output" == *"Already caffeinating: worker pid=1234, caffeinate pid=9999"* ]] - [ ! -e "$record_file" ] -} - -@test "caff reports pgrep errors instead of treating them as not running" { - create_caffeinate_stub - create_pgrep_stub - - run env \ - HOME="$TEST_HOME" \ - PATH="$TEST_MOCKBIN:/usr/bin:/bin:/usr/sbin:/sbin" \ - CAFF_TEST_PGREP_FAIL=worker \ - "$BASE_REPO_ROOT/bin/basectl" caff worker - - [ "$status" -eq 1 ] - [[ "$output" == *"Unable to query process list for 'worker'."* ]] - [[ "$output" != *"'worker' process is not running."* ]] -} diff --git a/cli/bash/commands/sort-in-place/README.md b/cli/bash/commands/sort-in-place/README.md deleted file mode 100644 index 5e66e4c..0000000 --- a/cli/bash/commands/sort-in-place/README.md +++ /dev/null @@ -1,9 +0,0 @@ -# `sort-in-place` - -Sort one or more text files in place, optionally with `sort -u` behavior. - -Public invocation is exposed by the launcher at `bin/sort-in-place`; the -implementation lives here so command code, documentation, and future tests stay -together. - -This is a Base bonus utility, not part of the core workspace orchestration surface. diff --git a/cli/bash/commands/sort-in-place/sort-in-place.sh b/cli/bash/commands/sort-in-place/sort-in-place.sh deleted file mode 100644 index e1664fd..0000000 --- a/cli/bash/commands/sort-in-place/sort-in-place.sh +++ /dev/null @@ -1,76 +0,0 @@ -#!/usr/bin/env bash - -# -# sort-in-place: sort files in place. -# - -sort_in_place_show_help() { - cat <<'EOF' -Sort text files in place. - -Usage: - sort-in-place [-u] ... - -Options: - -u Remove duplicate lines while sorting. - -h, --help Show this help text. -EOF -} - -sort_in_place_describe() { - printf '%s\n' "Sort text files in place" -} - -main() { - local sort_args=() - local file - local temp_file - local rc=0 - - case "${1:-}" in - -h|--help) - sort_in_place_show_help - return 0 - ;; - --describe) - sort_in_place_describe - return 0 - ;; - -u) - sort_args=(-u) - shift - ;; - esac - - if (($# == 0)); then - print_error "At least one file is required." - sort_in_place_show_help >&2 - return 2 - fi - - for file in "$@"; do - if [[ ! -f "$file" ]]; then - print_warn "$file is not a regular file; skipping." - continue - fi - - temp_file="$file._tmp" - if [[ -f "$temp_file" ]]; then - print_warn "$temp_file already exists; skipping $file." - continue - fi - - if ! sort "${sort_args[@]}" "$file" > "$temp_file"; then - print_error "Can't write to '$temp_file'." - rc=1 - continue - fi - - if ! mv -- "$temp_file" "$file"; then - print_error "Can't move '$temp_file' to '$file'." - rc=1 - fi - done - - return "$rc" -} diff --git a/cli/bash/commands/sort-in-place/tests/sort-in-place.bats b/cli/bash/commands/sort-in-place/tests/sort-in-place.bats deleted file mode 100644 index 91956cd..0000000 --- a/cli/bash/commands/sort-in-place/tests/sort-in-place.bats +++ /dev/null @@ -1,78 +0,0 @@ -#!/usr/bin/env bats - -load ../../../../../lib/bash/tests/test_helper.sh - -setup() { - setup_test_tmpdir - TEST_HOME="$TEST_TMPDIR/home" - mkdir -p "$TEST_HOME" -} - -run_sort_in_place() { - run env \ - HOME="$TEST_HOME" \ - PATH="/usr/bin:/bin:/usr/sbin:/sbin" \ - "$BASE_REPO_ROOT/bin/basectl" sort-in-place "$@" -} - -@test "sort-in-place prints help" { - run_sort_in_place --help - - [ "$status" -eq 0 ] - [[ "$output" == *"Usage:"* ]] - [[ "$output" == *"sort-in-place [-u] ..."* ]] -} - -@test "sort-in-place requires at least one file" { - run_sort_in_place - - [ "$status" -eq 2 ] - [[ "$output" == *"At least one file is required."* ]] - [[ "$output" == *"Usage:"* ]] -} - -@test "sort-in-place sorts a file in place" { - local input_file="$TEST_TMPDIR/input.txt" - - printf 'b\na\nc\n' > "$input_file" - - run_sort_in_place "$input_file" - - [ "$status" -eq 0 ] - [ "$(cat "$input_file")" = $'a\nb\nc' ] -} - -@test "sort-in-place supports unique sorting" { - local input_file="$TEST_TMPDIR/input.txt" - - printf 'b\na\nb\na\n' > "$input_file" - - run_sort_in_place -u "$input_file" - - [ "$status" -eq 0 ] - [ "$(cat "$input_file")" = $'a\nb' ] -} - -@test "sort-in-place skips non-regular files" { - local missing_file="$TEST_TMPDIR/missing.txt" - - run_sort_in_place "$missing_file" - - [ "$status" -eq 0 ] - [[ "$output" == *"$missing_file is not a regular file; skipping."* ]] -} - -@test "sort-in-place skips a file when its temp file already exists" { - local input_file="$TEST_TMPDIR/input.txt" - local temp_file="$input_file._tmp" - - printf 'b\na\n' > "$input_file" - printf 'existing temp\n' > "$temp_file" - - run_sort_in_place "$input_file" - - [ "$status" -eq 0 ] - [[ "$output" == *"$temp_file already exists; skipping $input_file."* ]] - [ "$(cat "$input_file")" = $'b\na' ] - [ "$(cat "$temp_file")" = "existing temp" ] -} diff --git a/docs/execution-model.md b/docs/execution-model.md index b43e37c..9c808c9 100644 --- a/docs/execution-model.md +++ b/docs/execution-model.md @@ -138,13 +138,13 @@ $BASE_HOME/cli/bash/commands//.sh For example: ```bash -basectl caff +basectl example ``` loads: ```text -$BASE_HOME/cli/bash/commands/caff/caff.sh +$BASE_HOME/cli/bash/commands/example/example.sh ``` A command implementation is sourced as Bash and must define `main`. @@ -189,24 +189,24 @@ $BASE_HOME/cli/bash/commands/basectl/subcommands/ ## Public Command Launchers -Convenience commands in `$BASE_HOME/bin` should be tiny real launcher files, -not symlinks. They delegate to `basectl` and keep the public command surface in -one place. +Base-owned convenience commands in `$BASE_HOME/bin` should be tiny real launcher +files, not symlinks. They delegate to `basectl` and keep the public command +surface in one place. -Some launchers may expose bonus utilities such as `caff` or `sort-in-place`. -Those utilities follow the same command-layout convention, but they are extras, -not the core workspace control plane. - -Example: +Example for a hypothetical Base-owned Bash command: ```bash #!/usr/bin/env bash -exec "$(dirname "$0")/basectl" caff "$@" +exec "$(dirname "$0")/basectl" example "$@" ``` The implementation still lives under `cli/bash/commands//` with its local README and tests. +Optional utility CLIs such as `caff` and `sort-in-place` live in +[`codeforester/base-platform-tools`](https://github.com/codeforester/base-platform-tools) +instead of Base core. + ## Runtime Bootstrap `base_init.sh` is the runtime bootstrap layer. It is sourced after `basectl`