Add branching-strategy skill with next and create actions
Version-aware branch naming (X.Y.Z.Q/{TASK_CODE}-{FEATURE}-{SEQ}),
branch creation from origin/test with origin/main fallback, and the
PR-into-test merge rule. Ships a TypeScript CLI (next, create) plus
shell modules (loggers, base, cli) run via Taskfile.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,2 +1,4 @@
|
|||||||
# branching-strategy
|
# branching-strategy
|
||||||
Git branching-strategy Agent Skill — version-aware branch naming, branches cut from origin/test, PRs into test. TypeScript CLI: next + create.
|
Git branching-strategy Agent Skill — version-aware branch naming, branches cut from origin/test, PRs into test. TypeScript CLI: next + create.
|
||||||
|
|
||||||
|
See [SKILL.md](SKILL.md).
|
||||||
|
|||||||
@@ -0,0 +1,104 @@
|
|||||||
|
---
|
||||||
|
name: branching-strategy
|
||||||
|
description: >
|
||||||
|
Principles and tooling for git branching in any repository — version-aware branch
|
||||||
|
naming, branch creation from origin/test, and the PR-into-test merge rule. Use this
|
||||||
|
skill whenever a new git branch must be named or created, or when committing, pushing,
|
||||||
|
or merging in any repository — even if the user only says "new branch", "commit this",
|
||||||
|
"push it", or "merge". Enforces the convention
|
||||||
|
X.Y.Z.Q/{TASK_MANAGEMENT_CODE}-{FEATURE_X}-{SEQ} with the version read from git tags
|
||||||
|
only. Also use when deciding what an agent may do autonomously in a repository versus
|
||||||
|
what requires explicit human approval.
|
||||||
|
license: Proprietary
|
||||||
|
metadata:
|
||||||
|
author: workspace-swiss-knife
|
||||||
|
version: "2.0"
|
||||||
|
spec: agentskills.io/specification
|
||||||
|
origin-repository: git@github.ibm.com:CTOTools-skills-code-agent/branching-strategy.git
|
||||||
|
origin-path: $HOME/projects-ibm/cognitive-architect/workspace-skills-code-agent/branching-strategy
|
||||||
|
---
|
||||||
|
|
||||||
|
# Branching Strategy
|
||||||
|
|
||||||
|
Git branching principles and tooling, for any repository.
|
||||||
|
|
||||||
|
## Main rules
|
||||||
|
|
||||||
|
### Rule 1 — All local work happens on a branch created from `origin/test`
|
||||||
|
|
||||||
|
- Working locally with **any** repository always requires a branch — never commit on
|
||||||
|
the base branch itself.
|
||||||
|
- The branch is created from **`origin/test`**; if the remote has no `test`, fall back
|
||||||
|
to **`origin/main`**; if the remote has neither, **ask the user** which base to use —
|
||||||
|
never guess.
|
||||||
|
- Fetch first (`git fetch --prune`) so the branch is cut from the current remote state.
|
||||||
|
- **Before creating the branch, present the computed branch name to the user and get
|
||||||
|
their confirmation** — run the `next` action, show the name, create only after a yes.
|
||||||
|
|
||||||
|
### Rule 2 — Branch naming convention
|
||||||
|
|
||||||
|
```
|
||||||
|
X.Y.Z.Q/{TASK_MANAGEMENT_CODE}-{FEATURE_X}-{SEQ}
|
||||||
|
```
|
||||||
|
|
||||||
|
Example: `4.0.0.0/IIAA-123-api-cleanup-001`
|
||||||
|
|
||||||
|
| Segment | Meaning | Source |
|
||||||
|
|---|---|---|
|
||||||
|
| `X.Y.Z.Q` | Version | **Git tags only** — latest semver tag of the target repo (`v` prefix allowed; 3-part tags get `Q=0`). No tags → stop and ask which version to tag, never guess. |
|
||||||
|
| `TASK_MANAGEMENT_CODE` | Task/ticket identifier, e.g. `IIAA-123` | Provided per task (`--task-key`) |
|
||||||
|
| `FEATURE_X` | Kebab-case feature/focus topic | Provided per task (`--topic`) |
|
||||||
|
| `SEQ` | 3-digit sequence, `001`-based | Auto-incremented by scanning existing local + remote branches with the same prefix |
|
||||||
|
|
||||||
|
The version is **never** taken from `package.json`, `VERSION` files, or guessed.
|
||||||
|
|
||||||
|
### Rule 3 — Merging into `test` is via Pull Request, by the user, always
|
||||||
|
|
||||||
|
- Every branch is merged into **`test`** through a **Pull Request** — never by direct
|
||||||
|
merge + push.
|
||||||
|
- **Merging always happens manually by the user.** The agent may prepare and push the
|
||||||
|
branch and hand over the PR compare URL; it never creates the merge.
|
||||||
|
|
||||||
|
## Supporting principles
|
||||||
|
|
||||||
|
- **Commit** only when the user asks; **push** only when the user asks, and only branches.
|
||||||
|
- Never delete or force-push branches without an explicit request.
|
||||||
|
- Git runs over **SSH** with the user's existing key — never install or configure API
|
||||||
|
tokens (`gh auth login`, PATs); real enforcement is server-side branch protection on `test`.
|
||||||
|
- Commits: imperative subject ≤ 72 chars; agent-generated commits carry
|
||||||
|
`Co-Authored-By: <agent name> <noreply@anthropic.com>`; never commit gitignored artifacts.
|
||||||
|
- Versions are marked with git tags — tags are the single source of truth.
|
||||||
|
|
||||||
|
## Available scripts
|
||||||
|
|
||||||
|
All commands run via `task` from the `scripts/` directory of this skill:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Compute the next branch name + detected base (no changes made) — show this to the user
|
||||||
|
task next -- --task-key="IIAA-123" --topic="api-cleanup" --repo-dir="/path/to/repo"
|
||||||
|
|
||||||
|
# After user confirmation: fetch, then create the branch from origin/test (or origin/main)
|
||||||
|
task create -- --task-key="IIAA-123" --topic="api-cleanup" --repo-dir="/path/to/repo"
|
||||||
|
|
||||||
|
# Build TypeScript to dist/
|
||||||
|
task build
|
||||||
|
```
|
||||||
|
|
||||||
|
TypeScript CLI actions (run via `cli` module → `src/`):
|
||||||
|
- **`next`** (`src/actions/next/action.ts`) — resolves version, sequence, and base branch; outputs the branch name and its parts as YAML
|
||||||
|
- **`create`** (`src/actions/create/action.ts`) — same resolution, then `git fetch --prune` + `git checkout -b <branch> <base>`; errors out if neither `origin/test` nor `origin/main` exists and no `--base` was given; never pushes
|
||||||
|
|
||||||
|
## Workflow
|
||||||
|
|
||||||
|
1. Confirm the target repo has at least one semver tag (`git tag --list`). If not, stop and ask which version to tag — do not invent one.
|
||||||
|
2. Run `task next` — it prints the computed branch name and the detected base branch.
|
||||||
|
3. **Show the branch name to the user and wait for confirmation.**
|
||||||
|
4. Run `task create` only after the user confirms. If it reports no `test`/`main` on the remote, ask the user and re-run with `--base`.
|
||||||
|
5. Pushing and the PR into `test` are the user's decision — hand over the compare URL, never merge.
|
||||||
|
|
||||||
|
## Gotchas
|
||||||
|
|
||||||
|
- Tags are compared with `--sort=-v:refname`; a malformed tag (e.g. `release-1`) is ignored, not an error.
|
||||||
|
- SEQ scans both local and `origin/` branches — `create` fetches first, but if using `next` alone on a stale repo, run `git fetch --prune` or the sequence can collide with an unseen remote branch.
|
||||||
|
- `task-key` must match `PROJECT-ID` shape (`^[A-Z][A-Z0-9]*-[A-Za-z0-9]+$`); `topic` must be kebab-case.
|
||||||
|
- `create` falls back to branching from the local ref when offline (fetch failure is non-fatal).
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
# Node / pnpm
|
||||||
|
node_modules/
|
||||||
|
dist/
|
||||||
|
.pnpm-store/
|
||||||
|
|
||||||
|
# Build artefacts
|
||||||
|
*.js.map
|
||||||
|
*.d.ts.map
|
||||||
|
|
||||||
|
# Environment
|
||||||
|
.env
|
||||||
|
.env.local
|
||||||
|
.env.*.local
|
||||||
|
|
||||||
|
# OS
|
||||||
|
.DS_Store
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
# https://taskfile.dev
|
||||||
|
|
||||||
|
version: "3"
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
environment:show:
|
||||||
|
desc: "Show environment vars"
|
||||||
|
cmds:
|
||||||
|
- |
|
||||||
|
./.scripts/base/api/environment-show.sh {{ .CLI_ARGS }}
|
||||||
|
silent: true
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# -------------------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# -------------------------------------------------------------------------------------
|
||||||
|
. ./.scripts/base/lib/--index.sh
|
||||||
|
|
||||||
|
_base_environment_show "$@"
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# -------------------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# -------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
export LOCAL_HOME_DIR=$(pwd)
|
||||||
|
export LOCAL_HOME_DIR_NAME=${PWD##*/}
|
||||||
|
export LOCAL_GIT_ROOT_DIR=$(dirname "${LOCAL_HOME_DIR}")
|
||||||
|
|
||||||
|
TMP_BASE_FILE_DOTENV=".env"
|
||||||
|
if
|
||||||
|
[ -e "${TMP_BASE_FILE_DOTENV}" ]
|
||||||
|
then
|
||||||
|
export $(
|
||||||
|
grep -v '^#' "${TMP_BASE_FILE_DOTENV}" | xargs
|
||||||
|
) >/dev/null 2>&1
|
||||||
|
fi
|
||||||
|
|
||||||
|
ALL_ARGS=("$@")
|
||||||
|
while [[ "$#" -gt 0 ]]; do
|
||||||
|
case $1 in
|
||||||
|
*) ;;
|
||||||
|
esac
|
||||||
|
shift
|
||||||
|
done
|
||||||
|
set -- "${ALL_ARGS[@]}"
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# -------------------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# -------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
. ./.scripts/base/lib/--index-api.sh
|
||||||
|
|
||||||
|
# No required environment variables for skill-manager base
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# -------------------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# -------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
. ./.scripts/base/lib/-ensure-environment-variable.sh
|
||||||
|
|
||||||
|
. ./.scripts/base/lib/-environment-show.sh
|
||||||
|
|
||||||
|
. ./.scripts/base/lib/-mask.sh
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# -------------------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# -------------------------------------------------------------------------------------
|
||||||
|
. ./.scripts/loggers/lib/--index.sh
|
||||||
|
#
|
||||||
|
# --> passed parameters are read & exported environment variables
|
||||||
|
#
|
||||||
|
. ./.scripts/base/lib/--env-vars-reader.sh
|
||||||
|
#
|
||||||
|
# --> required environment variables are validated for existence
|
||||||
|
#
|
||||||
|
. ./.scripts/base/lib/--env-vars-validator.sh
|
||||||
|
#
|
||||||
|
# --> available functions are imported/exported
|
||||||
|
#
|
||||||
|
. ./.scripts/base/lib/--index-api.sh
|
||||||
|
# -------------------------------------------------------------------------------------
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# -------------------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# -------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
. ./.scripts/loggers/lib/--index-api.sh
|
||||||
|
|
||||||
|
_base_ensureEnvironmentVariable() {
|
||||||
|
local FUNCTION_NAME="_base_ensureEnvironmentVariable"
|
||||||
|
local ENV_VAR_NAME="$1"
|
||||||
|
_loggers_info "${FUNCTION_NAME}" "ENV_VAR_NAME: ${ENV_VAR_NAME}"
|
||||||
|
if [ -z "${!ENV_VAR_NAME}" ]; then
|
||||||
|
_loggers_error "${FUNCTION_NAME}" "Missing required environment variable: ${ENV_VAR_NAME}. Check .env or .env-* files!"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# -------------------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# -------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
. ./.scripts/loggers/lib/--index-api.sh
|
||||||
|
|
||||||
|
_base_environment_show() {
|
||||||
|
local FUNCTION_NAME="_base_environment_show"
|
||||||
|
|
||||||
|
_loggers_info "${FUNCTION_NAME}" "LOCAL_HOME_DIR: ${LOCAL_HOME_DIR}"
|
||||||
|
_loggers_info "${FUNCTION_NAME}" "LOCAL_HOME_DIR_NAME: ${LOCAL_HOME_DIR_NAME}"
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# -------------------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# -------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
_base_mask() {
|
||||||
|
local original_value="$1"
|
||||||
|
local RET_VAL
|
||||||
|
if
|
||||||
|
[[ -n "$original_value" && ${#original_value} -gt 2 ]]
|
||||||
|
then
|
||||||
|
RET_VAL="${original_value:0:1}$(printf '%*s' $((${#original_value} - 2)) '' | tr ' ' '*')${original_value: -1}"
|
||||||
|
else
|
||||||
|
RET_VAL="$(printf '%*s' ${#original_value} '' | tr ' ' '*')"
|
||||||
|
fi
|
||||||
|
echo "${RET_VAL}"
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
# https://taskfile.dev
|
||||||
|
|
||||||
|
version: "3"
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
build:
|
||||||
|
desc: Compile TypeScript source to dist/
|
||||||
|
cmds:
|
||||||
|
- |
|
||||||
|
./.scripts/cli/api/build.sh {{ .CLI_ARGS }}
|
||||||
|
silent: true
|
||||||
|
|
||||||
|
next:
|
||||||
|
desc: |
|
||||||
|
Compute the next branch name (X.Y.Z.Q/{TASK_MANAGEMENT_CODE}-{FEATURE_X}-{SEQ}).
|
||||||
|
Usage: task cli:next -- --task-key="IIAA-123" --topic="api-cleanup" [--base=…] [--repo-dir=…]
|
||||||
|
cmds:
|
||||||
|
- |
|
||||||
|
./.scripts/cli/api/next.sh {{ .CLI_ARGS }}
|
||||||
|
silent: true
|
||||||
|
|
||||||
|
create:
|
||||||
|
desc: |
|
||||||
|
Compute and create the next branch locally (never pushes).
|
||||||
|
Usage: task cli:create -- --task-key="IIAA-123" --topic="api-cleanup" [--base=…] [--repo-dir=…]
|
||||||
|
cmds:
|
||||||
|
- |
|
||||||
|
./.scripts/cli/api/create.sh {{ .CLI_ARGS }}
|
||||||
|
silent: true
|
||||||
Executable
+11
@@ -0,0 +1,11 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# -------------------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# -------------------------------------------------------------------------------------
|
||||||
|
set -e
|
||||||
|
|
||||||
|
. ./.scripts/cli/lib/--index.sh
|
||||||
|
|
||||||
|
_cli__build "$@"
|
||||||
Executable
+11
@@ -0,0 +1,11 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# -------------------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# -------------------------------------------------------------------------------------
|
||||||
|
set -e
|
||||||
|
|
||||||
|
. ./.scripts/cli/lib/--index.sh
|
||||||
|
|
||||||
|
_cli__create "$@"
|
||||||
Executable
+11
@@ -0,0 +1,11 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# -------------------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# -------------------------------------------------------------------------------------
|
||||||
|
set -e
|
||||||
|
|
||||||
|
. ./.scripts/cli/lib/--index.sh
|
||||||
|
|
||||||
|
_cli__next "$@"
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# -------------------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# -------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
ALL_ARGS=("$@")
|
||||||
|
while [[ "$#" -gt 0 ]]; do
|
||||||
|
case $1 in
|
||||||
|
*) ;;
|
||||||
|
esac
|
||||||
|
shift
|
||||||
|
done
|
||||||
|
set -- "${ALL_ARGS[@]}"
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# -------------------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# -------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
. ./.scripts/cli/lib/--index-api.sh
|
||||||
|
|
||||||
|
# No required environment variables for cli module
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# -------------------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# -------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
. ./.scripts/cli/lib/-build.sh
|
||||||
|
|
||||||
|
. ./.scripts/cli/lib/-next.sh
|
||||||
|
|
||||||
|
. ./.scripts/cli/lib/-create.sh
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# -------------------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# -------------------------------------------------------------------------------------
|
||||||
|
. ./.scripts/loggers/lib/--index.sh
|
||||||
|
. ./.scripts/base/lib/--index.sh
|
||||||
|
#
|
||||||
|
# --> passed parameters are read & exported environment variables
|
||||||
|
#
|
||||||
|
. ./.scripts/cli/lib/--env-vars-reader.sh
|
||||||
|
#
|
||||||
|
# --> required environment variables are validated for existence
|
||||||
|
#
|
||||||
|
. ./.scripts/cli/lib/--env-vars-validator.sh
|
||||||
|
#
|
||||||
|
# --> available functions are imported/exported
|
||||||
|
#
|
||||||
|
. ./.scripts/cli/lib/--index-api.sh
|
||||||
|
# -------------------------------------------------------------------------------------
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# -------------------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# -------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
_cli__build() {
|
||||||
|
local FUNCTION_NAME="_cli__build"
|
||||||
|
_loggers_info "${FUNCTION_NAME}" "Compiling TypeScript source to dist/"
|
||||||
|
pnpm run build
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# -------------------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# -------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
_cli__create() {
|
||||||
|
local FUNCTION_NAME="_cli__create"
|
||||||
|
_loggers_info "${FUNCTION_NAME}" "Creating branch via TypeScript CLI"
|
||||||
|
npx tsx src/cli/commands.ts --action create "$@"
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# -------------------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# -------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
_cli__next() {
|
||||||
|
local FUNCTION_NAME="_cli__next"
|
||||||
|
_loggers_info "${FUNCTION_NAME}" "Computing next branch name via TypeScript CLI"
|
||||||
|
npx tsx src/cli/commands.ts --action next "$@"
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# -------------------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
# TOBE ADDED - Licence & Copyright Disclaimer
|
||||||
|
#
|
||||||
|
# -------------------------------------------------------------------------------------
|
||||||
|
export LOGGER_TRAILING_NEW_LINE_ENABLED="TRUE"
|
||||||
|
|
||||||
|
export LOGGER_IS_ENABLED_ERROR=true
|
||||||
|
export LOGGER_IS_ENABLED_INFO=true
|
||||||
|
export LOGGER_IS_ENABLED_WARN=true
|
||||||
|
export LOGGER_IS_ENABLED_DEBUG=true
|
||||||
|
export LOGGER_IS_ENABLED_TRACE=false
|
||||||
|
|
||||||
|
export LOCAL_STRING_005_SPACES=" "
|
||||||
|
export LOCAL_STRING_010_SPACES="${LOCAL_STRING_005_SPACES}${LOCAL_STRING_005_SPACES}"
|
||||||
|
export LOCAL_STRING_050_SPACES="${LOCAL_STRING_010_SPACES}${LOCAL_STRING_010_SPACES}${LOCAL_STRING_010_SPACES}${LOCAL_STRING_010_SPACES}${LOCAL_STRING_010_SPACES}"
|
||||||
|
export LOCAL_STRING_070_SPACES="${LOCAL_STRING_050_SPACES}${LOCAL_STRING_010_SPACES}${LOCAL_STRING_010_SPACES}"
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# -------------------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
# TOBE ADDED - Licence & Copyright Disclaimer
|
||||||
|
#
|
||||||
|
# -------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
. ./.scripts/loggers/lib/--env-vars-reader.sh
|
||||||
|
. ./.scripts/loggers/lib/--index.sh
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# -------------------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
# TOBE ADDED - Licence & Copyright Disclaimer
|
||||||
|
#
|
||||||
|
# -------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
. ./.scripts/loggers/lib/-enable-trailing-new-line.sh
|
||||||
|
|
||||||
|
. ./.scripts/loggers/lib/-info.sh
|
||||||
|
. ./.scripts/loggers/lib/-trace.sh
|
||||||
|
. ./.scripts/loggers/lib/-debug.sh
|
||||||
|
. ./.scripts/loggers/lib/-error.sh
|
||||||
|
. ./.scripts/loggers/lib/-warn.sh
|
||||||
|
|
||||||
|
. ./.scripts/loggers/lib/-printf--debug.sh
|
||||||
|
. ./.scripts/loggers/lib/-printf--info.sh
|
||||||
|
. ./.scripts/loggers/lib/-printf--trace.sh
|
||||||
|
. ./.scripts/loggers/lib/-printf--warn.sh
|
||||||
|
|
||||||
|
. ./.scripts/loggers/lib/-empty-line.sh
|
||||||
|
. ./.scripts/loggers/lib/-waiting-dot.sh
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# -------------------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
# TOBE ADDED - Licence & Copyright Disclaimer
|
||||||
|
#
|
||||||
|
# -------------------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
# --> passed parameters are read & exported environment variables
|
||||||
|
#
|
||||||
|
. ./.scripts/loggers/lib/--env-vars-reader.sh
|
||||||
|
#
|
||||||
|
# --> available functions are imported/exported
|
||||||
|
#
|
||||||
|
. ./.scripts/loggers/lib/--index-api.sh
|
||||||
|
# -------------------------------------------------------------------------------------
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# -------------------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
# TOBE ADDED - Licence & Copyright Disclaimer
|
||||||
|
#
|
||||||
|
# -------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
. ./.scripts/loggers/lib/-enable-trailing-new-line.sh
|
||||||
|
|
||||||
|
_loggers_debug() {
|
||||||
|
_loggers_enableLoggerTrailingNewLine
|
||||||
|
if
|
||||||
|
[ "${LOGGER_IS_ENABLED_DEBUG}" = true ]
|
||||||
|
then
|
||||||
|
local TMP_1ST_PARAM="${1}${LOCAL_STRING_070_SPACES}"
|
||||||
|
TMP_1ST_PARAM="${TMP_1ST_PARAM:0:70}"
|
||||||
|
local TMP_LINE="# DEBUG # ${TMP_1ST_PARAM} # ${2}"
|
||||||
|
echo -e "\033[1;36m${TMP_LINE}\033[0m" >&2
|
||||||
|
fi
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# -------------------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
# TOBE ADDED - Licence & Copyright Disclaimer
|
||||||
|
#
|
||||||
|
# -------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
. ./.scripts/loggers/lib/-enable-trailing-new-line.sh
|
||||||
|
|
||||||
|
_loggers_emptyLine() {
|
||||||
|
_loggers_enableLoggerTrailingNewLine
|
||||||
|
echo -e "" >&2
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# -------------------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
# TOBE ADDED - Licence & Copyright Disclaimer
|
||||||
|
#
|
||||||
|
# -------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
_loggers_enableLoggerTrailingNewLine() {
|
||||||
|
if
|
||||||
|
[ -z "$LOGGER_TRAILING_NEW_LINE_DISABLED" ]
|
||||||
|
then
|
||||||
|
echo "" >&2
|
||||||
|
export LOGGER_TRAILING_NEW_LINE_DISABLED="TRUE"
|
||||||
|
fi
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# -------------------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
# TOBE ADDED - Licence & Copyright Disclaimer
|
||||||
|
#
|
||||||
|
# -------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
. ./.scripts/loggers/lib/-enable-trailing-new-line.sh
|
||||||
|
|
||||||
|
_loggers_error() {
|
||||||
|
_loggers_enableLoggerTrailingNewLine
|
||||||
|
if
|
||||||
|
[ "${LOGGER_IS_ENABLED_ERROR}" = true ]
|
||||||
|
then
|
||||||
|
local TMP_1ST_PARAM="${1}${LOCAL_STRING_070_SPACES}"
|
||||||
|
TMP_1ST_PARAM="${TMP_1ST_PARAM:0:70}"
|
||||||
|
local TMP_LINE="# ERROR # ${TMP_1ST_PARAM} # $2"
|
||||||
|
echo -e "\033[1;31m${TMP_LINE}\033[0m" >&2
|
||||||
|
fi
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# -------------------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
# TOBE ADDED - Licence & Copyright Disclaimer
|
||||||
|
#
|
||||||
|
# -------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
. ./.scripts/loggers/lib/-enable-trailing-new-line.sh
|
||||||
|
|
||||||
|
_loggers_info() {
|
||||||
|
_loggers_enableLoggerTrailingNewLine
|
||||||
|
if
|
||||||
|
[ "${LOGGER_IS_ENABLED_INFO}" = true ]
|
||||||
|
then
|
||||||
|
local TMP_1ST_PARAM="${1}${LOCAL_STRING_070_SPACES}"
|
||||||
|
TMP_1ST_PARAM="${TMP_1ST_PARAM:0:70}"
|
||||||
|
local TMP_LINE="# INFO # ${TMP_1ST_PARAM} # ${2}"
|
||||||
|
echo -e "${TMP_LINE}" >&2
|
||||||
|
fi
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# -------------------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# -------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
_loggers_printf__debug() {
|
||||||
|
if
|
||||||
|
[ "${LOGGER_IS_ENABLED_DEBUG}" = true ]
|
||||||
|
then
|
||||||
|
printf '\033[1;36m%s\033[0m\n' "$*" >&2
|
||||||
|
fi
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# -------------------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# -------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
_loggers_printf__info() {
|
||||||
|
if
|
||||||
|
[ "${LOGGER_IS_ENABLED_INFO}" = true ]
|
||||||
|
then
|
||||||
|
printf '\033[1;32m%s\033[0m\n' "$*" >&2
|
||||||
|
fi
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# -------------------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# -------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
_loggers_printf__trace() {
|
||||||
|
if
|
||||||
|
[ "${LOGGER_IS_ENABLED_TRACE}" = true ]
|
||||||
|
then
|
||||||
|
printf '\033[0;90m%s\033[0m\n' "$*" >&2
|
||||||
|
fi
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# -------------------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# -------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
_loggers_printf__warn() {
|
||||||
|
if
|
||||||
|
[ "${LOGGER_IS_ENABLED_WARN}" = true ]
|
||||||
|
then
|
||||||
|
printf '\033[1;33m%s\033[0m\n' "$*" >&2
|
||||||
|
fi
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# -------------------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
# TOBE ADDED - Licence & Copyright Disclaimer
|
||||||
|
#
|
||||||
|
# -------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
. ./.scripts/loggers/lib/-enable-trailing-new-line.sh
|
||||||
|
|
||||||
|
_loggers_trace() {
|
||||||
|
_loggers_enableLoggerTrailingNewLine
|
||||||
|
if
|
||||||
|
[ "${LOGGER_IS_ENABLED_TRACE}" = true ]
|
||||||
|
then
|
||||||
|
local TMP_1ST_PARAM="${1}${LOCAL_STRING_070_SPACES}"
|
||||||
|
TMP_1ST_PARAM="${TMP_1ST_PARAM:0:70}"
|
||||||
|
local TMP_LINE="# TRACE # ${TMP_1ST_PARAM} # $2"
|
||||||
|
echo -e "\033[0;94m${TMP_LINE}\033[0m" >&2
|
||||||
|
fi
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# -------------------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
# TOBE ADDED - Licence & Copyright Disclaimer
|
||||||
|
#
|
||||||
|
# -------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
. ./.scripts/loggers/lib/-enable-trailing-new-line.sh
|
||||||
|
|
||||||
|
_loggers_waitingDot() {
|
||||||
|
echo -n "." >&2
|
||||||
|
export LOGGER_TRAILING_NEW_LINE_DISABLED=""
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# -------------------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
# TOBE ADDED - Licence & Copyright Disclaimer
|
||||||
|
#
|
||||||
|
# -------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
. ./.scripts/loggers/lib/-enable-trailing-new-line.sh
|
||||||
|
|
||||||
|
_loggers_warn() {
|
||||||
|
_loggers_enableLoggerTrailingNewLine
|
||||||
|
if
|
||||||
|
[ "${LOGGER_IS_ENABLED_WARN}" = true ]
|
||||||
|
then
|
||||||
|
local TMP_1ST_PARAM="${1}${LOCAL_STRING_070_SPACES}"
|
||||||
|
TMP_1ST_PARAM="${TMP_1ST_PARAM:0:70}"
|
||||||
|
local TMP_LINE="# WARN # ${TMP_1ST_PARAM} # $2"
|
||||||
|
echo -e "\033[1;33m${TMP_LINE}\033[0m" >&2
|
||||||
|
fi
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
# https://taskfile.dev
|
||||||
|
|
||||||
|
version: "3"
|
||||||
|
|
||||||
|
includes:
|
||||||
|
base: ./.scripts/base/Taskfile.yml
|
||||||
|
cli: ./.scripts/cli/Taskfile.yml
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
default:
|
||||||
|
cmds:
|
||||||
|
- task --list-all
|
||||||
|
silent: true
|
||||||
|
|
||||||
|
build:
|
||||||
|
desc: Compile TypeScript source to dist/
|
||||||
|
cmds:
|
||||||
|
- task: cli:build
|
||||||
|
silent: true
|
||||||
|
|
||||||
|
next:
|
||||||
|
desc: |
|
||||||
|
Compute the next branch name (X.Y.Z.Q/{TASK_MANAGEMENT_CODE}-{FEATURE_X}-{SEQ}).
|
||||||
|
Usage: task next -- --task-key="IIAA-123" --topic="api-cleanup" [--base=…] [--repo-dir=…]
|
||||||
|
cmds:
|
||||||
|
- task: cli:next
|
||||||
|
vars:
|
||||||
|
CLI_ARGS: "{{ .CLI_ARGS }}"
|
||||||
|
silent: true
|
||||||
|
|
||||||
|
create:
|
||||||
|
desc: |
|
||||||
|
Compute and create the next branch locally (never pushes).
|
||||||
|
Usage: task create -- --task-key="IIAA-123" --topic="api-cleanup" [--base=…] [--repo-dir=…]
|
||||||
|
cmds:
|
||||||
|
- task: cli:create
|
||||||
|
vars:
|
||||||
|
CLI_ARGS: "{{ .CLI_ARGS }}"
|
||||||
|
silent: true
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"name": "branch-tools",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"description": "CLI tools for version-aware git branch naming (branching-strategy skill)",
|
||||||
|
"type": "module",
|
||||||
|
"packageManager": "pnpm@9.15.0",
|
||||||
|
"bin": {
|
||||||
|
"branch-tools": "dist/cli/commands.js"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"build": "tsc",
|
||||||
|
"cli": "tsx src/cli/commands.ts"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/js-yaml": "^4.0.9",
|
||||||
|
"@types/node": "^22.0.0",
|
||||||
|
"tsx": "^4.19.0",
|
||||||
|
"typescript": "^5.7.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"js-yaml": "^4.1.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
Generated
+354
@@ -0,0 +1,354 @@
|
|||||||
|
lockfileVersion: '9.0'
|
||||||
|
|
||||||
|
settings:
|
||||||
|
autoInstallPeers: true
|
||||||
|
excludeLinksFromLockfile: false
|
||||||
|
|
||||||
|
importers:
|
||||||
|
|
||||||
|
.:
|
||||||
|
dependencies:
|
||||||
|
js-yaml:
|
||||||
|
specifier: ^4.1.0
|
||||||
|
version: 4.3.0
|
||||||
|
devDependencies:
|
||||||
|
'@types/js-yaml':
|
||||||
|
specifier: ^4.0.9
|
||||||
|
version: 4.0.9
|
||||||
|
'@types/node':
|
||||||
|
specifier: ^22.0.0
|
||||||
|
version: 22.20.1
|
||||||
|
tsx:
|
||||||
|
specifier: ^4.19.0
|
||||||
|
version: 4.23.1
|
||||||
|
typescript:
|
||||||
|
specifier: ^5.7.0
|
||||||
|
version: 5.9.3
|
||||||
|
|
||||||
|
packages:
|
||||||
|
|
||||||
|
'@esbuild/aix-ppc64@0.28.1':
|
||||||
|
resolution: {integrity: sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [ppc64]
|
||||||
|
os: [aix]
|
||||||
|
|
||||||
|
'@esbuild/android-arm64@0.28.1':
|
||||||
|
resolution: {integrity: sha512-34EGEbCIAgosYz6goLcopX6Mo7NyGv9tfwEM2/7Ce2VcVRk568iSvniGWcUXIy7wEDR1wzolcxcriFVrWYcwBg==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [android]
|
||||||
|
|
||||||
|
'@esbuild/android-arm@0.28.1':
|
||||||
|
resolution: {integrity: sha512-0k2F129Xdio1TdJfzJ8sy1Q47vUD2NnwdhiAf7drUN1EBTfPf4hsFCtmMgu/6m8JSzsBrlmVjudMBQqOfG8usQ==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [arm]
|
||||||
|
os: [android]
|
||||||
|
|
||||||
|
'@esbuild/android-x64@0.28.1':
|
||||||
|
resolution: {integrity: sha512-dbwY7ltSMDWsRatcRpCnES4F+im88OCUgGZjy52shC7GqHRE/cYlxNbB4Z4UpJswpcc4Qxd2oE/ufM0p61IKng==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [android]
|
||||||
|
|
||||||
|
'@esbuild/darwin-arm64@0.28.1':
|
||||||
|
resolution: {integrity: sha512-TZbWkQY7kvTAXbXUT7uVACR5cMHsDiSz9z7ZKAX/RTq/WJEk3QyRr0wZpNhBDX+/0CtdqUIJlOiodQcta6tY3Q==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [darwin]
|
||||||
|
|
||||||
|
'@esbuild/darwin-x64@0.28.1':
|
||||||
|
resolution: {integrity: sha512-zfdzgK9ACBNZLI/CyHTOx81SyNbM6YXn7rxSgX97VjyiPl9W1i4Ka4fgKECEoFCKGpvBj5qArWIGgQjOwkgskQ==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [darwin]
|
||||||
|
|
||||||
|
'@esbuild/freebsd-arm64@0.28.1':
|
||||||
|
resolution: {integrity: sha512-wG2EA8ENdEI0qhkSZMjfqrdY+ziCYCPMmtZjjIwOmXFjmyzEHn+UUxk5of+SYsjtfs3VpnlC7QLzSI5hY/rOAw==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [freebsd]
|
||||||
|
|
||||||
|
'@esbuild/freebsd-x64@0.28.1':
|
||||||
|
resolution: {integrity: sha512-i7dZ9vQgnvSCzi/rYCXNgtF/U+eKZNJBzu3eTQbRgHnM7tNSizLOkRFAl3qzVc/Op/u5YkHHa4pf/3DOYHthLQ==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [freebsd]
|
||||||
|
|
||||||
|
'@esbuild/linux-arm64@0.28.1':
|
||||||
|
resolution: {integrity: sha512-yHs+0uc8+nvEAfAfxrWQKK5peSNzBc4PegcMO0EJ2hT71uA7vB8Ihg2e77R2P7SG5uYjPbHlLLmve4LLLRCf0g==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [linux]
|
||||||
|
|
||||||
|
'@esbuild/linux-arm@0.28.1':
|
||||||
|
resolution: {integrity: sha512-qVXBOHQS+d5Y722GwJzJUtOLlX7km3CraOaGormF1pDtPd2C/l1SHRPgjLunLGe51Sh5YYWKMFDyV4SxgMQYTQ==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [arm]
|
||||||
|
os: [linux]
|
||||||
|
|
||||||
|
'@esbuild/linux-ia32@0.28.1':
|
||||||
|
resolution: {integrity: sha512-d1z4ZuP0ajrfz/FhGT4vv278rX8KnPPJx8i5+AtK7TYbx9Le9F1hyzurZpkEyjkGa9dUGhQow4C1NmeGvqxN2w==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [ia32]
|
||||||
|
os: [linux]
|
||||||
|
|
||||||
|
'@esbuild/linux-loong64@0.28.1':
|
||||||
|
resolution: {integrity: sha512-M5sRjUVZrkm1OAPR3dlOYzNmN+loZKGVi1VUQGrwuqLcbR6qeAz+famMhjASeH3YVKvZz+zT1jlh/keC3Rj/lg==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [loong64]
|
||||||
|
os: [linux]
|
||||||
|
|
||||||
|
'@esbuild/linux-mips64el@0.28.1':
|
||||||
|
resolution: {integrity: sha512-mRObBZeHh2OxcBFPWE/FjylkRgZdYuiTR3vaTozquCGOH14iP9oN4x4Ge81CoIDYQrXmIxpFumJBu5MtZpnQJQ==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [mips64el]
|
||||||
|
os: [linux]
|
||||||
|
|
||||||
|
'@esbuild/linux-ppc64@0.28.1':
|
||||||
|
resolution: {integrity: sha512-slScBsMAb3GFDcdrCgLwZtPYRoH2H/youv10QiZyRjmsP48fznoveWytSgCI/R0ZcUgpc0ZhIUEx6LHts8yrfQ==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [ppc64]
|
||||||
|
os: [linux]
|
||||||
|
|
||||||
|
'@esbuild/linux-riscv64@0.28.1':
|
||||||
|
resolution: {integrity: sha512-kw0owk1o0GFETUJyW0jc0G4Yzs0BHZn0JDZ8JRT088vjJYX777BAs1fDGxAC+q831qOs2DTC96mNsG2opdfyyQ==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [riscv64]
|
||||||
|
os: [linux]
|
||||||
|
|
||||||
|
'@esbuild/linux-s390x@0.28.1':
|
||||||
|
resolution: {integrity: sha512-/lAIjX8aYFRByhh6L5rYtPEDRqa9de/4V/juOXcta5frjvzXO4/sqEtyytse0g3zZFuWu5cDN0MkLz2qRDD2Ag==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [s390x]
|
||||||
|
os: [linux]
|
||||||
|
|
||||||
|
'@esbuild/linux-x64@0.28.1':
|
||||||
|
resolution: {integrity: sha512-u/anNYF2mmVOEDwLtnQ1wOr3EZ9sTNGLWrsYGYwHWzGA3Si84IOkHXlbWTD1NB+9/1lcnweYKO54uhxZydNzfA==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [linux]
|
||||||
|
|
||||||
|
'@esbuild/netbsd-arm64@0.28.1':
|
||||||
|
resolution: {integrity: sha512-oks0DYbLwWMmaakTsCb+zL4E+aHRVLom9IJZOAthMQEPiQmydXHkziYEsGYRx0uNV/IjEKGAV941JzH02pflqw==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [netbsd]
|
||||||
|
|
||||||
|
'@esbuild/netbsd-x64@0.28.1':
|
||||||
|
resolution: {integrity: sha512-aeL6lAnN89Hz43Mlh1G8ARasbuoYvSITDEx0tHh5b7jJnHcssqgjy9Yx430GDpmCa6OyrKoS0aNRjKundRizGg==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [netbsd]
|
||||||
|
|
||||||
|
'@esbuild/openbsd-arm64@0.28.1':
|
||||||
|
resolution: {integrity: sha512-MEFJe5C3R8pwXdZ5Y21oo6m7ePiS0d9pWucn99O/wvyJZChoIQKrQDxKrGeW8F5+T0okTHesAmDeiHDTIq0V/Q==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [openbsd]
|
||||||
|
|
||||||
|
'@esbuild/openbsd-x64@0.28.1':
|
||||||
|
resolution: {integrity: sha512-i/ZLIOafE0Z8cI/XANJAixoJL/uRAoS2xOA3rb0xN+KK0K177cMAsQYkzHtBrtMXAKuAc7HGgcWiZ/sRC1Nxgw==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [openbsd]
|
||||||
|
|
||||||
|
'@esbuild/openharmony-arm64@0.28.1':
|
||||||
|
resolution: {integrity: sha512-ge+Z7EXFNt2BO1oAMsVpiQ8EwndV9i1xXerAeTIK7AtPs3bKFXQM7nlRxDSIUIMeueR1CNXxqztLzdNeReKBJg==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [openharmony]
|
||||||
|
|
||||||
|
'@esbuild/sunos-x64@0.28.1':
|
||||||
|
resolution: {integrity: sha512-BEjgtECkL3vY+SaSQ6nzVfiALUeFxpawyp8Jmf5PtYhf1Ug40N1h/hxlhts+f1FvSvarEigdxS3BlSMI2PJLcQ==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [sunos]
|
||||||
|
|
||||||
|
'@esbuild/win32-arm64@0.28.1':
|
||||||
|
resolution: {integrity: sha512-lCv9eK/H6ZJWbE7bh2nw54CZ9M2nupBxJcTsdk/QQnWkdSjKGuxmmH8/GWrlT1eMmZfn4dGcCjRte397WqfQXA==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [win32]
|
||||||
|
|
||||||
|
'@esbuild/win32-ia32@0.28.1':
|
||||||
|
resolution: {integrity: sha512-zvb/mB2bSCoJOpoCBgYKKpX6YM6mJBlBUVUtVj41DlZJVEB6/0CKlRYxP5wWl1C1ILiCoAU5wZZ4q1P3qeS6Eg==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [ia32]
|
||||||
|
os: [win32]
|
||||||
|
|
||||||
|
'@esbuild/win32-x64@0.28.1':
|
||||||
|
resolution: {integrity: sha512-bm4Mowrv+GXMlpWX++EcXw/iLyd1o3+bJkC2DkWXYVvgZCqD/bSj9ctZeAMC3cIxgjRVR2Dufaiu4YPxr5gW1A==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [win32]
|
||||||
|
|
||||||
|
'@types/js-yaml@4.0.9':
|
||||||
|
resolution: {integrity: sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg==}
|
||||||
|
|
||||||
|
'@types/node@22.20.1':
|
||||||
|
resolution: {integrity: sha512-EANqOCF9QFyra+4pfxUcX9STKJpCLjMbObVzljIJomAWSnuSIEAvyzEU53GaajbXJEgdh0iEcPL+DGvpUd4k1Q==}
|
||||||
|
|
||||||
|
argparse@2.0.1:
|
||||||
|
resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
|
||||||
|
|
||||||
|
esbuild@0.28.1:
|
||||||
|
resolution: {integrity: sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
|
fsevents@2.3.3:
|
||||||
|
resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
|
||||||
|
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
|
||||||
|
os: [darwin]
|
||||||
|
|
||||||
|
js-yaml@4.3.0:
|
||||||
|
resolution: {integrity: sha512-1td788aAnnZ5qs7V2QIRl1owjtYpbKt749Y3xauqQgwIIGF/xXWz1wMTEBx5O3LK3lXLVuqXPdPxj2BoFHaW9Q==}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
|
tsx@4.23.1:
|
||||||
|
resolution: {integrity: sha512-GQHnkIfxyx1wYCOS/wonik5MVRZU9hi1TEZmzGZSCJB1y9YgoZ8H6itNE/u4suE+yLmOzuE4E5S4TZ/ZX2wcWQ==}
|
||||||
|
engines: {node: '>=18.0.0'}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
|
typescript@5.9.3:
|
||||||
|
resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==}
|
||||||
|
engines: {node: '>=14.17'}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
|
undici-types@6.21.0:
|
||||||
|
resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==}
|
||||||
|
|
||||||
|
snapshots:
|
||||||
|
|
||||||
|
'@esbuild/aix-ppc64@0.28.1':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/android-arm64@0.28.1':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/android-arm@0.28.1':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/android-x64@0.28.1':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/darwin-arm64@0.28.1':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/darwin-x64@0.28.1':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/freebsd-arm64@0.28.1':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/freebsd-x64@0.28.1':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/linux-arm64@0.28.1':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/linux-arm@0.28.1':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/linux-ia32@0.28.1':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/linux-loong64@0.28.1':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/linux-mips64el@0.28.1':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/linux-ppc64@0.28.1':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/linux-riscv64@0.28.1':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/linux-s390x@0.28.1':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/linux-x64@0.28.1':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/netbsd-arm64@0.28.1':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/netbsd-x64@0.28.1':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/openbsd-arm64@0.28.1':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/openbsd-x64@0.28.1':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/openharmony-arm64@0.28.1':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/sunos-x64@0.28.1':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/win32-arm64@0.28.1':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/win32-ia32@0.28.1':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/win32-x64@0.28.1':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@types/js-yaml@4.0.9': {}
|
||||||
|
|
||||||
|
'@types/node@22.20.1':
|
||||||
|
dependencies:
|
||||||
|
undici-types: 6.21.0
|
||||||
|
|
||||||
|
argparse@2.0.1: {}
|
||||||
|
|
||||||
|
esbuild@0.28.1:
|
||||||
|
optionalDependencies:
|
||||||
|
'@esbuild/aix-ppc64': 0.28.1
|
||||||
|
'@esbuild/android-arm': 0.28.1
|
||||||
|
'@esbuild/android-arm64': 0.28.1
|
||||||
|
'@esbuild/android-x64': 0.28.1
|
||||||
|
'@esbuild/darwin-arm64': 0.28.1
|
||||||
|
'@esbuild/darwin-x64': 0.28.1
|
||||||
|
'@esbuild/freebsd-arm64': 0.28.1
|
||||||
|
'@esbuild/freebsd-x64': 0.28.1
|
||||||
|
'@esbuild/linux-arm': 0.28.1
|
||||||
|
'@esbuild/linux-arm64': 0.28.1
|
||||||
|
'@esbuild/linux-ia32': 0.28.1
|
||||||
|
'@esbuild/linux-loong64': 0.28.1
|
||||||
|
'@esbuild/linux-mips64el': 0.28.1
|
||||||
|
'@esbuild/linux-ppc64': 0.28.1
|
||||||
|
'@esbuild/linux-riscv64': 0.28.1
|
||||||
|
'@esbuild/linux-s390x': 0.28.1
|
||||||
|
'@esbuild/linux-x64': 0.28.1
|
||||||
|
'@esbuild/netbsd-arm64': 0.28.1
|
||||||
|
'@esbuild/netbsd-x64': 0.28.1
|
||||||
|
'@esbuild/openbsd-arm64': 0.28.1
|
||||||
|
'@esbuild/openbsd-x64': 0.28.1
|
||||||
|
'@esbuild/openharmony-arm64': 0.28.1
|
||||||
|
'@esbuild/sunos-x64': 0.28.1
|
||||||
|
'@esbuild/win32-arm64': 0.28.1
|
||||||
|
'@esbuild/win32-ia32': 0.28.1
|
||||||
|
'@esbuild/win32-x64': 0.28.1
|
||||||
|
|
||||||
|
fsevents@2.3.3:
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
js-yaml@4.3.0:
|
||||||
|
dependencies:
|
||||||
|
argparse: 2.0.1
|
||||||
|
|
||||||
|
tsx@4.23.1:
|
||||||
|
dependencies:
|
||||||
|
esbuild: 0.28.1
|
||||||
|
optionalDependencies:
|
||||||
|
fsevents: 2.3.3
|
||||||
|
|
||||||
|
typescript@5.9.3: {}
|
||||||
|
|
||||||
|
undici-types@6.21.0: {}
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
/**
|
||||||
|
* Action: create
|
||||||
|
*
|
||||||
|
* Computes the next branch name (same rules as the `next` action) and creates
|
||||||
|
* the branch from the base branch:
|
||||||
|
*
|
||||||
|
* origin/test → origin/main → error (the caller must ask the user and pass --base)
|
||||||
|
*
|
||||||
|
* The caller MUST have confirmed the branch name with the user first (run `next`,
|
||||||
|
* show the name, get confirmation). Never pushes — publishing stays a human decision.
|
||||||
|
*
|
||||||
|
* Usage:
|
||||||
|
* branch-tools --action create --task-key IIAA-123 --topic api-cleanup [--base origin/develop] [--repo-dir /path]
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { resolve } from "node:path";
|
||||||
|
import { computeBranchName, detectBaseBranch, git } from "../next/action.js";
|
||||||
|
|
||||||
|
export function run(options: Record<string, string | undefined>): Record<string, unknown> {
|
||||||
|
const { branch, error, parts } = computeBranchName(options);
|
||||||
|
if (error) return { action: "create", success: false, error };
|
||||||
|
|
||||||
|
const repoDir = resolve(options["repo-dir"] ?? process.cwd());
|
||||||
|
|
||||||
|
const base = options["base"] ?? detectBaseBranch(repoDir);
|
||||||
|
if (!base) {
|
||||||
|
return {
|
||||||
|
action: "create",
|
||||||
|
success: false,
|
||||||
|
branch,
|
||||||
|
error: "Remote has neither origin/test nor origin/main — ask the user which base branch to use and pass it via --base",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
git(repoDir, ["fetch", "--prune"]);
|
||||||
|
} catch {
|
||||||
|
// no remote or offline — branch from the local ref instead
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
git(repoDir, ["checkout", "-b", branch!, base]);
|
||||||
|
} catch (err) {
|
||||||
|
return {
|
||||||
|
action: "create",
|
||||||
|
success: false,
|
||||||
|
branch,
|
||||||
|
base,
|
||||||
|
error: err instanceof Error ? err.message : String(err),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
action: "create",
|
||||||
|
success: true,
|
||||||
|
branch,
|
||||||
|
base,
|
||||||
|
...parts ? { parts } : {},
|
||||||
|
"repo-dir": repoDir,
|
||||||
|
note: "Branch created locally — push and PR into test are the user's decision",
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,121 @@
|
|||||||
|
/**
|
||||||
|
* Action: next
|
||||||
|
*
|
||||||
|
* Computes the next branch name for any git repository following the convention:
|
||||||
|
*
|
||||||
|
* X.Y.Z.Q/{TASK_MANAGEMENT_CODE}-{FEATURE_X}-{SEQ}
|
||||||
|
*
|
||||||
|
* - X.Y.Z.Q — version, read from git tags ONLY (latest semver tag;
|
||||||
|
* 3-part tags get Q=0). No tags → hard error.
|
||||||
|
* - TASK_MANAGEMENT_CODE — task/ticket identifier, e.g. IIAA-123 (--task-key)
|
||||||
|
* - FEATURE_X — kebab-case feature/focus topic (--topic)
|
||||||
|
* - SEQ — zero-padded 3-digit sequence, auto-incremented by scanning
|
||||||
|
* existing local + remote branches with the same prefix
|
||||||
|
*
|
||||||
|
* Repository-independent: operates on --repo-dir (default: current directory).
|
||||||
|
*
|
||||||
|
* Usage:
|
||||||
|
* branch-tools --action next --task-key IIAA-123 --topic api-cleanup [--repo-dir /path]
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { execFileSync } from "node:child_process";
|
||||||
|
import { resolve } from "node:path";
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Helpers
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
const TAG_PATTERN = /^v?(\d+)\.(\d+)\.(\d+)(?:\.(\d+))?$/;
|
||||||
|
|
||||||
|
export function git(repoDir: string, args: string[]): string {
|
||||||
|
return execFileSync("git", ["-C", repoDir, ...args], { encoding: "utf-8" }).trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
export function detectVersion(repoDir: string): string | undefined {
|
||||||
|
const tags = git(repoDir, ["tag", "--list", "--sort=-v:refname"])
|
||||||
|
.split("\n")
|
||||||
|
.map((t) => t.trim())
|
||||||
|
.filter((t) => TAG_PATTERN.test(t));
|
||||||
|
if (tags.length === 0) return undefined;
|
||||||
|
const m = tags[0].match(TAG_PATTERN)!;
|
||||||
|
return `${m[1]}.${m[2]}.${m[3]}.${m[4] ?? "0"}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function detectBaseBranch(repoDir: string): string | undefined {
|
||||||
|
for (const candidate of ["origin/test", "origin/main"]) {
|
||||||
|
try {
|
||||||
|
git(repoDir, ["show-ref", "--verify", "--quiet", `refs/remotes/${candidate}`]);
|
||||||
|
return candidate;
|
||||||
|
} catch {
|
||||||
|
// not present — try next
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function nextSequence(repoDir: string, prefix: string): string {
|
||||||
|
const branches = git(repoDir, ["branch", "-a", "--format=%(refname:short)"])
|
||||||
|
.split("\n")
|
||||||
|
.map((b) => b.trim().replace(/^origin\//, ""));
|
||||||
|
let max = 0;
|
||||||
|
for (const branch of branches) {
|
||||||
|
if (!branch.startsWith(`${prefix}-`)) continue;
|
||||||
|
const seq = branch.slice(prefix.length + 1);
|
||||||
|
if (/^\d+$/.test(seq)) max = Math.max(max, parseInt(seq, 10));
|
||||||
|
}
|
||||||
|
return String(max + 1).padStart(3, "0");
|
||||||
|
}
|
||||||
|
|
||||||
|
export function computeBranchName(
|
||||||
|
options: Record<string, string | undefined>
|
||||||
|
): { branch?: string; error?: string; parts?: Record<string, string> } {
|
||||||
|
const taskKey = options["task-key"];
|
||||||
|
if (!taskKey) return { error: "Missing required option: --task-key (e.g. IIAA-123)" };
|
||||||
|
if (!/^[A-Z][A-Z0-9]*-[A-Za-z0-9]+$/.test(taskKey)) {
|
||||||
|
return { error: `'task-key' must look like PROJECT-ID (e.g. IIAA-123). Got: '${taskKey}'` };
|
||||||
|
}
|
||||||
|
|
||||||
|
const topic = options["topic"];
|
||||||
|
if (!topic) return { error: "Missing required option: --topic (kebab-case feature topic)" };
|
||||||
|
if (!/^[a-z0-9]+(-[a-z0-9]+)*$/.test(topic)) {
|
||||||
|
return { error: `'topic' must be kebab-case (a-z, 0-9, single hyphens). Got: '${topic}'` };
|
||||||
|
}
|
||||||
|
|
||||||
|
const repoDir = resolve(options["repo-dir"] ?? process.cwd());
|
||||||
|
try {
|
||||||
|
git(repoDir, ["rev-parse", "--git-dir"]);
|
||||||
|
} catch {
|
||||||
|
return { error: `Not a git repository: ${repoDir}` };
|
||||||
|
}
|
||||||
|
|
||||||
|
const version = detectVersion(repoDir);
|
||||||
|
if (!version) {
|
||||||
|
return { error: `No version tag found in ${repoDir} — the version (X.Y.Z.Q) is read from git tags only. Create one first, e.g.: git tag 4.0.0.0` };
|
||||||
|
}
|
||||||
|
|
||||||
|
const prefix = `${version}/${taskKey}-${topic}`;
|
||||||
|
const seq = nextSequence(repoDir, prefix);
|
||||||
|
return {
|
||||||
|
branch: `${prefix}-${seq}`,
|
||||||
|
parts: { version, "task-key": taskKey, topic, seq },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Public API
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
export function run(options: Record<string, string | undefined>): Record<string, unknown> {
|
||||||
|
const { branch, error, parts } = computeBranchName(options);
|
||||||
|
if (error) return { action: "next", success: false, error };
|
||||||
|
|
||||||
|
const repoDir = resolve(options["repo-dir"] ?? process.cwd());
|
||||||
|
const base = detectBaseBranch(repoDir);
|
||||||
|
return {
|
||||||
|
action: "next",
|
||||||
|
success: true,
|
||||||
|
branch,
|
||||||
|
"base-branch": base ?? "NONE — remote has neither test nor main; ask the user",
|
||||||
|
...parts ? { parts } : {},
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,126 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
/**
|
||||||
|
* branch-tools CLI dispatcher
|
||||||
|
*
|
||||||
|
* Usage:
|
||||||
|
* branch-tools --action <action-name> [options]
|
||||||
|
*
|
||||||
|
* Actions:
|
||||||
|
* next — compute the next branch name (version from git tags only)
|
||||||
|
* create — compute and create the branch locally
|
||||||
|
*
|
||||||
|
* Output is always YAML to stdout. Exit 0 on success, 1 on error.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { parseArgs } from "node:util";
|
||||||
|
import * as yaml from "js-yaml";
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Action registry
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
type ActionModule = {
|
||||||
|
run: (options: Record<string, string | undefined>) => Record<string, unknown>;
|
||||||
|
};
|
||||||
|
|
||||||
|
const ACTIONS: Record<string, () => Promise<ActionModule>> = {
|
||||||
|
next: () => import("../actions/next/action.js"),
|
||||||
|
create: () => import("../actions/create/action.js"),
|
||||||
|
};
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// CLI
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
async function main(): Promise<void> {
|
||||||
|
let values: {
|
||||||
|
action?: string;
|
||||||
|
"task-key"?: string;
|
||||||
|
topic?: string;
|
||||||
|
base?: string;
|
||||||
|
"repo-dir"?: string;
|
||||||
|
help?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
({ values } = parseArgs({
|
||||||
|
args: process.argv.slice(2),
|
||||||
|
options: {
|
||||||
|
action: { type: "string", short: "a" },
|
||||||
|
"task-key": { type: "string" },
|
||||||
|
topic: { type: "string" },
|
||||||
|
base: { type: "string" },
|
||||||
|
"repo-dir": { type: "string" },
|
||||||
|
help: { type: "boolean", short: "h" },
|
||||||
|
},
|
||||||
|
strict: true,
|
||||||
|
}));
|
||||||
|
} catch (err) {
|
||||||
|
printError(String(err));
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (values.help || !values.action) {
|
||||||
|
console.log(`branch-tools --action <action> [options]
|
||||||
|
|
||||||
|
Branch convention: X.Y.Z.Q/{TASK_MANAGEMENT_CODE}-{FEATURE_X}-{SEQ}
|
||||||
|
Version (X.Y.Z.Q) is read from git tags ONLY. Branches are cut from origin/test (fallback origin/main).
|
||||||
|
|
||||||
|
Actions:
|
||||||
|
next Compute the next branch name
|
||||||
|
create Compute and create the branch locally (never pushes)
|
||||||
|
|
||||||
|
Options (both actions):
|
||||||
|
--task-key <key> Task identifier, e.g. IIAA-123 (required)
|
||||||
|
--topic <topic> Kebab-case focus topic, e.g. api-cleanup (required)
|
||||||
|
--base <branch> Base branch for create (default: origin/test, then origin/main)
|
||||||
|
--repo-dir <path> Target repository (default: current directory)
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
branch-tools --action next --task-key IIAA-123 --topic api-cleanup
|
||||||
|
branch-tools --action create --task-key IIAA-123 --topic api-cleanup --repo-dir /path/to/repo
|
||||||
|
`);
|
||||||
|
process.exit(values.help ? 0 : 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
const actionName = values.action;
|
||||||
|
|
||||||
|
if (!ACTIONS[actionName]) {
|
||||||
|
printError(
|
||||||
|
`Unknown action '${actionName}'. Available: ${Object.keys(ACTIONS).join(", ")}`
|
||||||
|
);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const mod = await ACTIONS[actionName]();
|
||||||
|
const result = mod.run({
|
||||||
|
"task-key": values["task-key"],
|
||||||
|
topic: values.topic,
|
||||||
|
base: values.base,
|
||||||
|
"repo-dir": values["repo-dir"],
|
||||||
|
});
|
||||||
|
process.stdout.write(
|
||||||
|
yaml.dump(result, { noRefs: true, sortKeys: false, lineWidth: 120 })
|
||||||
|
);
|
||||||
|
const success = (result["success"] as boolean | undefined) ?? true;
|
||||||
|
process.exit(success ? 0 : 1);
|
||||||
|
} catch (err) {
|
||||||
|
const errorOutput = {
|
||||||
|
success: false,
|
||||||
|
action: actionName,
|
||||||
|
error: err instanceof Error ? err.message : String(err),
|
||||||
|
};
|
||||||
|
process.stdout.write(yaml.dump(errorOutput, { noRefs: true, sortKeys: false }));
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function printError(msg: string): void {
|
||||||
|
process.stderr.write(`Error: ${msg}\n`);
|
||||||
|
process.stderr.write(
|
||||||
|
`Usage: branch-tools --action <${Object.keys(ACTIONS).join("|")}> [options]\n`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
main();
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ES2022",
|
||||||
|
"module": "NodeNext",
|
||||||
|
"moduleResolution": "NodeNext",
|
||||||
|
"outDir": "dist",
|
||||||
|
"rootDir": "src",
|
||||||
|
"strict": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"declaration": true,
|
||||||
|
"declarationMap": true,
|
||||||
|
"sourceMap": true
|
||||||
|
},
|
||||||
|
"include": ["src/**/*"],
|
||||||
|
"exclude": ["node_modules", "dist"]
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user