Files
development-scripts-3darch/SKILL.md
T

12 KiB

name, description, license, metadata, compatibility
name description license metadata compatibility
development-scripts-3darch Create, refactor, review, document, and maintain shell script modules under a repository's .scripts/ directory — the api/ + lib/ split, the --index.sh / --index-api.sh / --env-vars-reader.sh / --env-vars-validator.sh bootstrap chain, module and root Taskfile.yml integration, logging through the shared loggers module, and shared helpers from the base module. Use when adding a new script module or a new command to an existing one; writing an api/ wrapper or a lib/ implementation function; adding a CLI flag or a required environment variable; wiring a module into task; debugging a script that produces no output, cannot find a function, or unexpectedly loads .env; or reviewing shell module work for naming, sourcing order, and secret-safety — even when the user only says ".scripts", "Taskfile", "task runner", "shell module", or names a specific module like loggers or base. Proprietary
author version spec
workspace-skills-code-agent 1.0 agentskills.io/specification
Requires bash. Taskfile (task) needed only for task-runner integration. Designed for Cline, Claude Code, GitHub Copilot, OpenAI Codex, and other compatible agent environments

Repository Script Modules

3D Architecture Wizzard Project Adoption

This is the primary project-adopted skill for 3D Architecture Wizzard (3darch).

Project engineering peers:

Work rules for shell automation organised as modules under a repository's .scripts/ directory, with a strict public/private split and a fixed sourcing order.

The whole model rests on one idea: api/ is what people run, lib/ is what the code calls, and a single bootstrap chain guarantees that by the time an implementation function runs, its environment has been read, validated, and its dependencies sourced.

Scope routing

Pick the reference that matches the change; do not load all of them.

The change touches Read
A reader, validator, implementation function, or api/ wrapper references/module-anatomy.md
The loggers or base module, or logging that produces no output references/shared-modules.md
.env files, per-environment layers, or shipping .scripts/ into a rendered repo references/environment-model.md
A module Taskfile.yml or the root Taskfile.yml references/taskfile.md
Creating a module, changing one, or checking work before delivery references/workflows.md
Deciding what kind of module something should be references/module-patterns.md

Copy-ready modules live in assets/: module-example-v1/ is the scaffold for a new module; loggers/ and base/ are working reference implementations.

1. Repository model

<target-repository>/
  Taskfile.yml                  # root task runner, includes module Taskfiles
  .scripts/
    <module>/
      api/                      # public command wrappers — what users run
      lib/                      # implementation functions — what the code calls
      Taskfile.yml              # only when the module is exposed through task

Commands always run from the repository root, never from inside the module:

./.scripts/<module>/api/<command>.sh [args]
task <module-alias>:<task-name> -- [args]

Every . source path in every file is written relative to the repository root (. ./.scripts/loggers/lib/--index-api.sh). This is why running a script from inside its own directory fails to find its dependencies — that is the convention working as designed, not a bug to patch with dirname "$0".

2. Module anatomy

.scripts/<module>/
  api/
    <command-name>.sh
  lib/
    --env-vars-reader.sh        # defaults + CLI flag parsing
    --env-vars-validator.sh     # required-input checks
    --index-api.sh              # sources dependencies + this module's implementations
    --index.sh                  # full runtime bootstrap
    -<command-name>.sh          # one implementation function per command
  Taskfile.yml                  # optional
File Responsibility
api/<command>.sh Thin public entrypoint. Sources lib/--index.sh, calls one function.
lib/-<command>.sh The command implementation function.
lib/--index-api.sh Sources upstream module APIs, then this module's implementation files.
lib/--index.sh Runtime bootstrap: reader, then validator, then API index.
lib/--env-vars-reader.sh Exports defaults and parses CLI arguments into environment variables.
lib/--env-vars-validator.sh Validates required variables, failing with an actionable log.
Taskfile.yml Maps task names to api/ scripts, forwarding {{.CLI_ARGS}}.

Not every module has every file — a support-only library like loggers has no api/ and no Taskfile.yml. See references/module-patterns.md.

3. Standard runtime flow

api/<command>.sh
  └── source lib/--index.sh
        ├── source lib/--env-vars-reader.sh      # defaults + CLI flags → env vars
        ├── source lib/--env-vars-validator.sh   # required vars present?
        └── source lib/--index-api.sh
              ├── source dependencies (loggers, base, …)
              └── source lib/-<command>.sh       # defines the function
  └── call _<moduleNamespace>_<commandFunction>

The order is load-bearing. The reader runs before the validator so the validator has something to check; both run before the API index so implementation functions see a fully-prepared environment. Reordering produces failures that look like missing variables.

Arguments reach the reader because sourcing happens with the wrapper's own arguments still in scope — this is why api/ wrappers need no argument forwarding of their own.

4. Naming conventions

Kind Convention Example
Module directory lowercase kebab-case module-example-v1, repositories-nodejs
Public command lowercase kebab-case, action-oriented initialize-one.sh, find-many-by-query.sh
Implementation file single leading dash lib/-initialize-one.sh
Index / lifecycle file double leading dash lib/--index.sh, lib/--env-vars-reader.sh
Function _<moduleNamespace>_<actionName> _moduleExampleV1_initializeOne
Environment variable uppercase module prefix MODULE_EXAMPLE_V1_OUTPUT_DIR

Kebab-case module names convert to camelCase function namespaces:

Module Function prefix Env prefix
module-example-v1 _moduleExampleV1_ MODULE_EXAMPLE_V1_
repositories-nodejs _repositoriesNodejs_ REPOSITORIES_NODEJS_
operation-utilities _operationUtilities_ OPERATION_UTILITIES_

The dash count is not decoration — it is how you tell at a glance whether a file defines a command (-name.sh) or wires the module together (--name.sh).

5. Non-negotiable rules

  • api/ wrappers stay thin. Source lib/--index.sh, call one function, nothing else. No implementation, no argument parsing, no validation.
  • CLI flags are parsed only in --env-vars-reader.sh. Never in an implementation file.
  • Every new lib/-*.sh gets added to lib/--index-api.sh. Forgetting this is the most common cause of "command not found" for a function that plainly exists.
  • Readers preserve already-exported values — export X="${X:-default}" — so a caller can override any setting before invoking the module. A module reader never sources .env files itself; the base module owns that. See references/environment-model.md.
  • Log through _loggers_* helpers, never raw echo. Loggers write to stderr, keeping stdout clean for real output.
  • Never log secrets. Redact tokens, API keys, passwords, and kube config.
  • Quote variable expansions unless word splitting is intended.
  • return non-zero from implementation functions; reserve exit 1 for validators and bootstrap. An exit inside a sourced library kills the caller's shell.

6. Creating a module

Full walkthrough in references/workflows.md. The short path:

  1. Copy assets/module-example-v1/ to .scripts/<new-module>/.
  2. Replace every module-example-v1 path, the MODULE_EXAMPLE_V1_ env prefix, and the _moduleExampleV1_ function prefix.
  3. Add implementation files as lib/-<command>.sh and source them from lib/--index-api.sh.
  4. Add api/<command>.sh wrappers for user-facing commands only.
  5. Add a module Taskfile.yml, and a root include only if the namespace is stable.
  6. Validate (section 7).

7. Validation

# Syntax-check every script in the module
find .scripts/<module> -name '*.sh' -print0 | xargs -0 -n1 bash -n

# Confirm task exposure resolves
task --list

# Run the command itself, from the repository root
./.scripts/<module>/api/<command>.sh --dry-run

bash -n catches syntax errors but not a missing entry in --index-api.sh — that only surfaces at call time. Always run the command once, from the repository root, before delivering. A dry-run flag is worth adding precisely so this check is cheap.

8. Mistakes to avoid

  • Putting implementation logic into api/*.sh.
  • Adding a lib/-*.sh file and forgetting to source it from lib/--index-api.sh.
  • Parsing CLI flags in an implementation function instead of the reader.
  • Sourcing base/lib/--index.sh from a library function — it reads .env and environment-specific files as a side effect. Use base/lib/--index-api.sh when you only need shared functions.
  • Writing source paths relative to the script instead of the repository root.
  • Using exit inside a sourced implementation function.
  • Printing secrets, or documenting their values.
  • Adding a root Taskfile.yml include for an experimental module.
  • Assuming logging works because the code compiles — a logger whose level flags were never exported is silent and returns 0. See references/shared-modules.md §3.