Ports generic-scripts-module-v1 into a routed skill: module anatomy, shared loggers/base modules, Taskfile integration, workflows, and pattern decisions. Fixes a latent defect in the loggers example that silenced all logging in the module scaffold: loggers/lib/--index-api.sh now sources --env-vars-reader.sh, and the reader preserves already-exported values so caller overrides still win. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
5.3 KiB
Shared Modules — loggers and base
The two foundation modules every other module depends on, and the bootstrap rule that decides whether logging works at all.
Working implementations are in assets/loggers/ and assets/base/.
1. loggers — support-only library
No api/ commands, no Taskfile.yml. It exists to be sourced.
.scripts/loggers/
lib/
--env-vars-reader.sh # level flags + padding strings
--index-api.sh # sources the reader, then every logger function
--index.sh
-debug.sh -error.sh -info.sh -trace.sh -warn.sh
-empty-line.sh -enable-trailing-new-line.sh -waiting-dot.sh
Consume it from any module:
. ./.scripts/loggers/lib/--index-api.sh
Available functions:
_loggers_info "${FUNCTION_NAME}" "message"
_loggers_debug "${FUNCTION_NAME}" "message"
_loggers_warn "${FUNCTION_NAME}" "message"
_loggers_error "${FUNCTION_NAME}" "message"
_loggers_trace "${FUNCTION_NAME}" "message"
_loggers_emptyLine
Levels are controlled by environment flags, defaulting to everything except trace:
LOGGER_IS_ENABLED_ERROR=true
LOGGER_IS_ENABLED_INFO=true
LOGGER_IS_ENABLED_WARN=true
LOGGER_IS_ENABLED_DEBUG=true
LOGGER_IS_ENABLED_TRACE=false
Export any of them before invoking a command to change verbosity for that run:
LOGGER_IS_ENABLED_DEBUG=false ./.scripts/<module>/api/<command>.sh
Rules:
- Do not add
api/wrappers tologgerswithout a real user-facing requirement. - Keep logger helpers small and side-effect-light.
- Log to
stderr—stdoutstays clean so command output remains pipeable. - Dependent modules source the logger API directly.
2. base — shared foundation
Has public api/ wrappers but no Taskfile.yml. Provides local environment handling and
local-storage helpers over .env files.
.scripts/base/
api/
local-storage-ensure-existance-file-dot-env.sh
local-storage-read-lines-from-file-dot-env.sh
local-storage-reset-file-dot-env.sh
local-storage-save-env-variable-2-file-dot-env.sh
local-storage-view-file-dot-env.sh
lib/
--env-vars-reader.sh --env-vars-validator.sh
--index-api.sh --index.sh
-export-env-variable.sh
-export-env-variables-from-file-dot-env.sh
-export-env-variables-from-file.sh
-get-env-variable-key-from-line.sh
-get-env-variable-value-from-line.sh
-log-env-vars.sh
-prompt-for-env-var.sh
local-storage/
--index-api.sh
-ensure-existance-file.sh -ensure-existance-file-dot-env.sh
-read-lines-from-file.sh -read-lines-from-file-dot-env.sh
-reset-file.sh -reset-file-dot-env.sh
-save-env-variable-2-file.sh -save-env-variable-2-file-dot-env.sh
-view-file.sh -view-file-dot-env.sh
Which entrypoint to source — this matters
# From another module, when you only need base's shared functions:
. ./.scripts/base/lib/--index-api.sh
# Only from base's own api/ wrappers:
. ./.scripts/base/lib/--index.sh
base/lib/--index.sh runs base's reader and validator, and reads .env and
environment-specific files as a side effect. Sourcing it from a library function pulls that
whole environment into an unrelated command — values appear that nobody set, and the failure
is hard to trace because nothing in the calling module mentions .env.
--index-api.sh defines functions and nothing else, which is why it is the correct dependency
edge between modules.
Rules:
- Prefer
base/lib/--index-api.shfrom other modules. - Nested lib packages such as
lib/local-storage/carry their own--index-api.shand are sourced from the parent's. - Never log secret values — redact tokens, API keys, passwords, kube config, credentials.
3. The logging bootstrap rule
A logger function whose level flag is unset prints nothing and returns 0.
Every _loggers_* function is guarded by its level flag:
if [ "${LOGGER_IS_ENABLED_INFO}" = true ]; then ... fi
An unset flag is not true, so the call is a silent no-op with a success exit code. Nothing
fails, nothing warns — the command simply produces no output, which reads like a bug in the
command rather than in its bootstrap.
Those flags are exported by loggers/lib/--env-vars-reader.sh. For logging to work, that
reader must have run before any logger call.
In the shipped assets/loggers/, --index-api.sh sources the reader itself, so any module
that sources the logger API gets working logging with no extra step. The reader uses
${VAR:-default} throughout, so it is safe to source repeatedly and a caller's override
still wins.
If you are adapting an existing loggers module rather than using the shipped one, check
this first. A --index-api.sh that sources only the logger function files, without the
reader, silences every module that depends on it — while bash -n passes and every command
exits 0.
Quick check in any repository:
grep -n 'env-vars-reader' .scripts/loggers/lib/--index-api.sh \
|| echo "WARNING: loggers API does not bootstrap its level flags — logging may be silent"
Direct test:
bash -c '. ./.scripts/loggers/lib/--index-api.sh; _loggers_info "check" "visible?"'
No output means the flags were never exported.