@ngocsangairvds/vsaf 4.0.1 → 4.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/packages/cli/dist/commands/install.d.ts.map +1 -1
- package/packages/cli/dist/commands/install.js +15 -2
- package/packages/cli/dist/commands/install.js.map +1 -1
- package/packages/cli/dist/commands/skill.d.ts.map +1 -1
- package/packages/cli/dist/commands/skill.js +1 -9
- package/packages/cli/dist/commands/skill.js.map +1 -1
- package/packages/cli/dist/index.js +1 -1
- package/packages/cli/dist/index.js.map +1 -1
- package/skills/sdlc/discovery/SKILL.md +20 -5
- package/skills/sdlc/hotfix-analyze/SKILL.md +41 -17
- package/skills/sdlc/implement/SKILL.md +47 -14
- package/skills/sdlc/install-deps.mjs +7 -1
- package/skills/sdlc/pack.yaml +1 -1
- package/skills/sdlc/sdlc-health/SKILL.md +36 -36
- package/skills/vds-skill/_shared/credentials.sh +79 -0
- package/skills/vds-skill/create-bitbucket-pr/SKILL.md +65 -0
- package/skills/vds-skill/create-bitbucket-pr/scripts/create-pr.sh +105 -0
- package/skills/vds-skill/create-jira-epic/SKILL.md +65 -0
- package/skills/vds-skill/create-jira-epic/scripts/create-epic.sh +113 -0
- package/skills/vds-skill/install-deps.mjs +157 -0
- package/skills/vds-skill/pack.yaml +24 -0
- package/skills/vds-skill/pull/SKILL.md +38 -0
- package/skills/vds-skill/pull/scripts/pull.sh +52 -0
- package/skills/vds-skill/push-prd/SKILL.md +238 -0
- package/skills/vds-skill/push-srs/SKILL.md +128 -0
- package/skills/vds-skill/search-confluence/SKILL.md +66 -0
- package/skills/vds-skill/search-confluence/scripts/search.sh +128 -0
- package/skills/vds-skill/vds-scripts-skill/.openskills.json +6 -0
- package/skills/vds-skill/vds-scripts-skill/QUALITY.md +44 -0
- package/skills/vds-skill/vds-scripts-skill/SKILL.md +127 -0
- package/skills/vds-skill/vds-scripts-skill/references/audit-commands.md +171 -0
- package/skills/vds-skill/vds-scripts-skill/references/capability-index.md +34 -0
- package/skills/vds-skill/vds-scripts-skill/references/development-commands.md +12 -0
- package/skills/vds-skill/vds-scripts-skill/references/google-sheets.md +71 -0
- package/skills/vds-skill/vds-scripts-skill/references/integration-commands.md +17 -0
- package/skills/vds-skill/vds-scripts-skill/references/platform-bootstrap.md +31 -0
- package/skills/vds-skill/vds-scripts-skill/references/specialist-routing.md +14 -0
- package/skills/vds-skill/vds-scripts-skill/references/validation-commands.md +15 -0
|
@@ -18,22 +18,22 @@ Execute ALL checks below in order, then print the summary table.
|
|
|
18
18
|
Use the Read tool to check each of these 16 skill files (sdlc-health is excluded — if you're running this, it's installed). If the file exists, mark ✓. If Read returns an error, mark ✗.
|
|
19
19
|
|
|
20
20
|
```
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
21
|
+
.claude/skills/sdlc-init/SKILL.md
|
|
22
|
+
.claude/skills/sdlc-onboard-docs/SKILL.md
|
|
23
|
+
.claude/skills/sdlc-onboard-code/SKILL.md
|
|
24
|
+
.claude/skills/sdlc-discovery/SKILL.md
|
|
25
|
+
.claude/skills/sdlc-prd/SKILL.md
|
|
26
|
+
.claude/skills/sdlc-architecture/SKILL.md
|
|
27
|
+
.claude/skills/sdlc-srs/SKILL.md
|
|
28
|
+
.claude/skills/sdlc-test-design/SKILL.md
|
|
29
|
+
.claude/skills/sdlc-implement/SKILL.md
|
|
30
|
+
.claude/skills/sdlc-review/SKILL.md
|
|
31
|
+
.claude/skills/sdlc-feature-complete/SKILL.md
|
|
32
|
+
.claude/skills/sdlc-ship/SKILL.md
|
|
33
|
+
.claude/skills/sdlc-hotfix-analyze/SKILL.md
|
|
34
|
+
.claude/skills/sdlc-hotfix-red/SKILL.md
|
|
35
|
+
.claude/skills/sdlc-hotfix-green/SKILL.md
|
|
36
|
+
.claude/skills/sdlc-hotfix-review/SKILL.md
|
|
37
37
|
```
|
|
38
38
|
|
|
39
39
|
### Check 2: BMAD Skills (filesystem)
|
|
@@ -41,21 +41,21 @@ Use the Read tool to check each of these 16 skill files (sdlc-health is excluded
|
|
|
41
41
|
Check these 15 BMAD skills — required by discovery, prd, architecture, srs, test-design, review:
|
|
42
42
|
|
|
43
43
|
```
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
44
|
+
.claude/skills/bmad-advanced-elicitation/SKILL.md
|
|
45
|
+
.claude/skills/bmad-brainstorming/SKILL.md
|
|
46
|
+
.claude/skills/bmad-domain-research/SKILL.md
|
|
47
|
+
.claude/skills/bmad-prfaq/SKILL.md
|
|
48
|
+
.claude/skills/bmad-create-prd/SKILL.md
|
|
49
|
+
.claude/skills/bmad-validate-prd/SKILL.md
|
|
50
|
+
.claude/skills/bmad-review-adversarial-general/SKILL.md
|
|
51
|
+
.claude/skills/bmad-create-architecture/SKILL.md
|
|
52
|
+
.claude/skills/bmad-create-epics-and-stories/SKILL.md
|
|
53
|
+
.claude/skills/bmad-check-implementation-readiness/SKILL.md
|
|
54
|
+
.claude/skills/bmad-agent-analyst/SKILL.md
|
|
55
|
+
.claude/skills/bmad-review-edge-case-hunter/SKILL.md
|
|
56
|
+
.claude/skills/bmad-code-review/SKILL.md
|
|
57
|
+
.claude/skills/bmad-party-mode/SKILL.md
|
|
58
|
+
.claude/skills/bmad-qa-generate-e2e-tests/SKILL.md
|
|
59
59
|
```
|
|
60
60
|
|
|
61
61
|
### Check 3: mattpocock Skills (filesystem)
|
|
@@ -63,11 +63,11 @@ Check these 15 BMAD skills — required by discovery, prd, architecture, srs, te
|
|
|
63
63
|
Check these 5 skills — lean alternative for bugfix/known-domain flows:
|
|
64
64
|
|
|
65
65
|
```
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
66
|
+
.claude/skills/grill-me/SKILL.md
|
|
67
|
+
.claude/skills/diagnose/SKILL.md
|
|
68
|
+
.claude/skills/zoom-out/SKILL.md
|
|
69
|
+
.claude/skills/tdd/SKILL.md
|
|
70
|
+
.claude/skills/improve-codebase-architecture/SKILL.md
|
|
71
71
|
```
|
|
72
72
|
|
|
73
73
|
### Check 4: Plugins (Claude-only)
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Shared credential helper for vds-skill-* skills.
|
|
3
|
+
# Sourced by skill scripts to lazy-prompt + persist credentials.
|
|
4
|
+
# Config file: ~/.vds/sdlc-config.env (chmod 600)
|
|
5
|
+
#
|
|
6
|
+
# Portable across bash and zsh (uses eval for indirect expansion).
|
|
7
|
+
|
|
8
|
+
VSAF_CONFIG_FILE="${VSAF_CONFIG_FILE:-$HOME/.vds/sdlc-config.env}"
|
|
9
|
+
readonly VSAF_CONFIG_FILE
|
|
10
|
+
|
|
11
|
+
# Load existing config if present
|
|
12
|
+
if [[ -f "$VSAF_CONFIG_FILE" ]]; then
|
|
13
|
+
# shellcheck source=/dev/null
|
|
14
|
+
source "$VSAF_CONFIG_FILE"
|
|
15
|
+
fi
|
|
16
|
+
|
|
17
|
+
# Also load vds-cli's own env if present (for token reuse)
|
|
18
|
+
if [[ -f "$HOME/.vds/.env" ]]; then
|
|
19
|
+
set -a
|
|
20
|
+
# shellcheck source=/dev/null
|
|
21
|
+
source "$HOME/.vds/.env"
|
|
22
|
+
set +a
|
|
23
|
+
fi
|
|
24
|
+
|
|
25
|
+
# ensure_env VAR_NAME "Prompt message" [is_secret]
|
|
26
|
+
# Prompts user if VAR is empty, persists to config file.
|
|
27
|
+
# Re-persists if VAR is pre-set (supports token rotation).
|
|
28
|
+
ensure_env() {
|
|
29
|
+
local var="$1"
|
|
30
|
+
local prompt="${2:-Enter $var}"
|
|
31
|
+
local is_secret="${3:-true}"
|
|
32
|
+
|
|
33
|
+
# Portable indirect expansion (works in both bash and zsh)
|
|
34
|
+
local value
|
|
35
|
+
eval "value=\${$var:-}"
|
|
36
|
+
|
|
37
|
+
if [[ -z "$value" ]]; then
|
|
38
|
+
# Prompt user
|
|
39
|
+
if [[ "$is_secret" == "true" ]]; then
|
|
40
|
+
read -rsp "$prompt: " value
|
|
41
|
+
echo "" >&2
|
|
42
|
+
else
|
|
43
|
+
read -rp "$prompt: " value
|
|
44
|
+
fi
|
|
45
|
+
|
|
46
|
+
if [[ -z "$value" ]]; then
|
|
47
|
+
echo "ERROR: $var is required" >&2
|
|
48
|
+
return 1
|
|
49
|
+
fi
|
|
50
|
+
|
|
51
|
+
export "$var=$value"
|
|
52
|
+
fi
|
|
53
|
+
|
|
54
|
+
# Persist to config file (idempotent — replace existing entry or append)
|
|
55
|
+
mkdir -p "$(dirname "$VSAF_CONFIG_FILE")" || { echo "ERROR: Cannot create config dir $(dirname "$VSAF_CONFIG_FILE")" >&2; return 1; }
|
|
56
|
+
|
|
57
|
+
if grep -q "^${var}=" "$VSAF_CONFIG_FILE" 2>/dev/null; then
|
|
58
|
+
# Update in place (BSD/GNU sed compatible via tmp file)
|
|
59
|
+
local tmp
|
|
60
|
+
tmp=$(mktemp) || { echo "ERROR: mktemp failed" >&2; return 1; }
|
|
61
|
+
trap 'rm -f "$tmp"' EXIT INT TERM
|
|
62
|
+
grep -v "^${var}=" "$VSAF_CONFIG_FILE" > "$tmp"
|
|
63
|
+
printf '%s=%q\n' "$var" "$value" >> "$tmp"
|
|
64
|
+
mv "$tmp" "$VSAF_CONFIG_FILE"
|
|
65
|
+
trap - EXIT INT TERM
|
|
66
|
+
else
|
|
67
|
+
printf '%s=%q\n' "$var" "$value" >> "$VSAF_CONFIG_FILE"
|
|
68
|
+
fi
|
|
69
|
+
chmod 600 "$VSAF_CONFIG_FILE" || echo "WARN: Could not set chmod 600 on $VSAF_CONFIG_FILE" >&2
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
# require_command COMMAND_NAME — exits 127 if missing
|
|
73
|
+
require_command() {
|
|
74
|
+
if ! command -v "$1" >/dev/null 2>&1; then
|
|
75
|
+
echo "ERROR: Required command '$1' not found in PATH" >&2
|
|
76
|
+
echo " Install it before running this skill." >&2
|
|
77
|
+
return 127
|
|
78
|
+
fi
|
|
79
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: vds-skill-create-bitbucket-pr
|
|
3
|
+
description: Create a Bitbucket PR via `vds-cli bitbucket pr create`. Auto-extracts PROJECT/REPO from `git remote get-url origin`. Viettel-only — for GitHub use `gh pr create`. Run after /sdlc-ship when remote is bitbucket.digital.vn.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# /vds-skill-create-bitbucket-pr
|
|
7
|
+
|
|
8
|
+
Create a Bitbucket PR on Viettel internal Bitbucket via `vds-cli`.
|
|
9
|
+
|
|
10
|
+
## Config Check (run FIRST)
|
|
11
|
+
|
|
12
|
+
Before doing anything, run this check via Bash tool:
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
source .claude/skills/_shared/vds-skill/credentials.sh 2>/dev/null
|
|
16
|
+
MISSING=""
|
|
17
|
+
[[ -z "${VDS_BITBUCKET_TOKEN:-}" ]] && MISSING="$MISSING VDS_BITBUCKET_TOKEN"
|
|
18
|
+
if [[ -n "$MISSING" ]]; then
|
|
19
|
+
echo "BLOCKED — missing:$MISSING"
|
|
20
|
+
echo "Fix: edit ~/.vds/sdlc-config.env (or run: vsaf install vds-skill)"
|
|
21
|
+
else
|
|
22
|
+
echo "OK"
|
|
23
|
+
fi
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
If BLOCKED: tell the user exactly what's missing and how to fix, then STOP. Do NOT fabricate output. `--dry-run` mode skips credential + vds-cli checks.
|
|
27
|
+
|
|
28
|
+
## Prerequisites
|
|
29
|
+
|
|
30
|
+
- `vds-cli` installed (for non-dry-run)
|
|
31
|
+
- Inside a git repo with remote pointing to `bitbucket.digital.vn`
|
|
32
|
+
- On a named branch (not detached HEAD)
|
|
33
|
+
- `VDS_BITBUCKET_TOKEN` in `~/.vds/sdlc-config.env`
|
|
34
|
+
|
|
35
|
+
## Usage
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
/vds-skill-create-bitbucket-pr # interactive, target=master
|
|
39
|
+
/vds-skill-create-bitbucket-pr --target develop # different target
|
|
40
|
+
/vds-skill-create-bitbucket-pr --dry-run # preview without executing
|
|
41
|
+
/vds-skill-create-bitbucket-pr --description-file FILE # custom description
|
|
42
|
+
/vds-skill-create-bitbucket-pr --title "Custom title"
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Behavior
|
|
46
|
+
|
|
47
|
+
1. Verify remote URL matches `bitbucket.digital.vn` — abort if not
|
|
48
|
+
2. Extract PROJECT/REPO from remote URL
|
|
49
|
+
3. Determine source branch (current) + target (default `master`)
|
|
50
|
+
4. Auto-detect description from `.vsaf/docs/features/*/09-ship.md` or `.vsaf/docs/hotfixes/*/03-ship.md`
|
|
51
|
+
5. Lazy-prompt `VDS_BITBUCKET_TOKEN` if not set
|
|
52
|
+
6. Confirm with user before executing
|
|
53
|
+
7. Run `vds-cli bitbucket pr create $PROJECT/$REPO --source ... --target ... --title ... --description-file ... --yes --json-only`
|
|
54
|
+
|
|
55
|
+
## Implementation
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
bash .claude/skills/vds-skill-create-bitbucket-pr/scripts/create-pr.sh "$@"
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Notes
|
|
62
|
+
|
|
63
|
+
- For GitHub repos: use `gh pr create` instead (already standard in `/sdlc-ship`).
|
|
64
|
+
- PR description defaults to the latest ship report from `.vsaf/docs/`.
|
|
65
|
+
- Dry-run works without `vds-cli` installed (preview mode).
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# vds-skill-create-bitbucket-pr: create Bitbucket PR via vds-cli
|
|
3
|
+
# Usage: create-pr.sh [--dry-run] [--target BRANCH] [--description-file FILE] [--title TITLE]
|
|
4
|
+
# Auto-extracts project/repo from `git remote get-url origin`.
|
|
5
|
+
|
|
6
|
+
# NOTE: No `set -e` — we want full control of the exit-code path.
|
|
7
|
+
|
|
8
|
+
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
9
|
+
# shellcheck source=/dev/null
|
|
10
|
+
source "$SCRIPT_DIR/../../_shared/vds-skill/credentials.sh"
|
|
11
|
+
|
|
12
|
+
require_command git
|
|
13
|
+
# Note: require_command vds-cli moved AFTER dry-run check.
|
|
14
|
+
|
|
15
|
+
DRY_RUN=false
|
|
16
|
+
TARGET_BRANCH="master"
|
|
17
|
+
DESC_FILE=""
|
|
18
|
+
TITLE=""
|
|
19
|
+
|
|
20
|
+
while [[ $# -gt 0 ]]; do
|
|
21
|
+
case "$1" in
|
|
22
|
+
--dry-run) DRY_RUN=true; shift ;;
|
|
23
|
+
--target) TARGET_BRANCH="$2"; shift 2 ;;
|
|
24
|
+
--description-file) DESC_FILE="$2"; shift 2 ;;
|
|
25
|
+
--title) TITLE="$2"; shift 2 ;;
|
|
26
|
+
*) echo "Unknown arg: $1" >&2; exit 2 ;;
|
|
27
|
+
esac
|
|
28
|
+
done
|
|
29
|
+
|
|
30
|
+
REMOTE_URL=$(git remote get-url origin 2>/dev/null || true)
|
|
31
|
+
if [[ -z "$REMOTE_URL" ]]; then
|
|
32
|
+
echo "ERROR: No git remote 'origin' configured" >&2
|
|
33
|
+
exit 1
|
|
34
|
+
fi
|
|
35
|
+
|
|
36
|
+
if [[ ! "$REMOTE_URL" =~ bitbucket\.digital\.vn ]]; then
|
|
37
|
+
echo "WARNING: Remote URL does not match bitbucket.digital.vn pattern:" >&2
|
|
38
|
+
echo " $REMOTE_URL" >&2
|
|
39
|
+
echo " This skill is for Viettel Bitbucket. For GitHub, use 'gh pr create'." >&2
|
|
40
|
+
exit 1
|
|
41
|
+
fi
|
|
42
|
+
|
|
43
|
+
# Extract PROJECT/REPO from URL
|
|
44
|
+
# Handles:
|
|
45
|
+
# ssh://git@bitbucket.digital.vn/PROJECT/repo.git
|
|
46
|
+
# ssh://git@bitbucket.digital.vn:7999/PROJECT/repo.git
|
|
47
|
+
# https://bitbucket.digital.vn/scm/PROJECT/repo.git
|
|
48
|
+
if [[ "$REMOTE_URL" =~ /scm/([^/]+)/([^/]+)\.git$ ]]; then
|
|
49
|
+
PROJECT="${BASH_REMATCH[1]}"
|
|
50
|
+
REPO="${BASH_REMATCH[2]}"
|
|
51
|
+
elif [[ "$REMOTE_URL" =~ /([^/]+)/([^/]+)\.git$ ]]; then
|
|
52
|
+
PROJECT="${BASH_REMATCH[1]}"
|
|
53
|
+
REPO="${BASH_REMATCH[2]}"
|
|
54
|
+
else
|
|
55
|
+
echo "ERROR: Cannot parse PROJECT/REPO from remote URL: $REMOTE_URL" >&2
|
|
56
|
+
exit 1
|
|
57
|
+
fi
|
|
58
|
+
|
|
59
|
+
SOURCE_BRANCH=$(git rev-parse --abbrev-ref HEAD)
|
|
60
|
+
if [[ -z "$SOURCE_BRANCH" ]] || [[ "$SOURCE_BRANCH" == "HEAD" ]]; then
|
|
61
|
+
echo "ERROR: Not on a named branch" >&2
|
|
62
|
+
exit 1
|
|
63
|
+
fi
|
|
64
|
+
|
|
65
|
+
if [[ -z "$TITLE" ]]; then
|
|
66
|
+
TITLE=$(git log -1 --pretty=%s)
|
|
67
|
+
fi
|
|
68
|
+
|
|
69
|
+
if [[ -z "$DESC_FILE" ]]; then
|
|
70
|
+
DESC_FILE=$(find .vsaf/docs/features -name '09-ship.md' 2>/dev/null | head -1)
|
|
71
|
+
[[ -z "$DESC_FILE" ]] && DESC_FILE=$(find .vsaf/docs/hotfixes -name '03-ship.md' 2>/dev/null | head -1)
|
|
72
|
+
fi
|
|
73
|
+
|
|
74
|
+
CMD=(vds-cli bitbucket pr create "$PROJECT/$REPO"
|
|
75
|
+
--source "$SOURCE_BRANCH"
|
|
76
|
+
--target "$TARGET_BRANCH"
|
|
77
|
+
--title "$TITLE")
|
|
78
|
+
[[ -n "$DESC_FILE" ]] && [[ -f "$DESC_FILE" ]] && CMD+=(--description-file "$DESC_FILE")
|
|
79
|
+
|
|
80
|
+
if [[ "$DRY_RUN" == "true" ]]; then
|
|
81
|
+
echo "DRY-RUN — would execute:"
|
|
82
|
+
printf ' %q' "${CMD[@]}"
|
|
83
|
+
echo ""
|
|
84
|
+
echo " + --yes --json-only"
|
|
85
|
+
exit 0
|
|
86
|
+
fi
|
|
87
|
+
|
|
88
|
+
require_command vds-cli
|
|
89
|
+
|
|
90
|
+
ensure_env VDS_BITBUCKET_TOKEN "Enter VDS Bitbucket personal access token"
|
|
91
|
+
|
|
92
|
+
echo "About to create PR:"
|
|
93
|
+
echo " Project/Repo: $PROJECT/$REPO"
|
|
94
|
+
echo " Source: $SOURCE_BRANCH"
|
|
95
|
+
echo " Target: $TARGET_BRANCH"
|
|
96
|
+
echo " Title: $TITLE"
|
|
97
|
+
echo " Description: ${DESC_FILE:-<empty>}"
|
|
98
|
+
echo ""
|
|
99
|
+
read -rp "Proceed? [y/N]: " confirm
|
|
100
|
+
if [[ "$confirm" != "y" ]] && [[ "$confirm" != "Y" ]]; then
|
|
101
|
+
echo "Aborted"
|
|
102
|
+
exit 1
|
|
103
|
+
fi
|
|
104
|
+
|
|
105
|
+
"${CMD[@]}" --yes --json-only
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: vds-skill-create-jira-epic
|
|
3
|
+
description: Create a Jira Epic via `vds-cli jira create --issuetype Epic` from a PRD file. Use after /sdlc-prd has produced 02-prd.md. Auto-detects PRD location, lazy-prompts credentials. Viettel-only (requires vds-cli).
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# /vds-skill-create-jira-epic
|
|
7
|
+
|
|
8
|
+
Create a Jira Epic on Viettel Jira based on a PRD markdown file.
|
|
9
|
+
|
|
10
|
+
## Config Check (run FIRST)
|
|
11
|
+
|
|
12
|
+
Before doing anything, run this check via Bash tool:
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
source .claude/skills/_shared/vds-skill/credentials.sh 2>/dev/null
|
|
16
|
+
MISSING=""
|
|
17
|
+
[[ -z "${VDS_JIRA_TOKEN:-}" ]] && MISSING="$MISSING VDS_JIRA_TOKEN"
|
|
18
|
+
[[ -z "${VDS_JIRA_PROJECT_DEFAULT:-}" ]] && MISSING="$MISSING VDS_JIRA_PROJECT_DEFAULT"
|
|
19
|
+
if [[ -n "$MISSING" ]]; then
|
|
20
|
+
echo "BLOCKED — missing:$MISSING"
|
|
21
|
+
echo "Fix: edit ~/.vds/sdlc-config.env (or run: vsaf install vds-skill)"
|
|
22
|
+
else
|
|
23
|
+
echo "OK"
|
|
24
|
+
fi
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
If BLOCKED: tell the user exactly what's missing and how to fix, then STOP. Do NOT fabricate output. `--dry-run` mode skips credential + vds-cli checks.
|
|
28
|
+
|
|
29
|
+
## Prerequisites
|
|
30
|
+
|
|
31
|
+
- `vds-cli` installed (for non-dry-run)
|
|
32
|
+
- `python3` installed
|
|
33
|
+
- A PRD file exists at `.vsaf/docs/features/{feature}/02-prd.md`
|
|
34
|
+
- `VDS_JIRA_TOKEN` + `VDS_JIRA_PROJECT_DEFAULT` in `~/.vds/sdlc-config.env`
|
|
35
|
+
|
|
36
|
+
## Usage
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
/vds-skill-create-jira-epic # auto-detect latest PRD
|
|
40
|
+
/vds-skill-create-jira-epic --feature user-management # specific feature folder
|
|
41
|
+
/vds-skill-create-jira-epic --project NTTC # override project key
|
|
42
|
+
/vds-skill-create-jira-epic --dry-run # preview
|
|
43
|
+
/vds-skill-create-jira-epic --description-file path/to.md # custom description source
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Behavior
|
|
47
|
+
|
|
48
|
+
1. Locate PRD file (or use `--description-file`)
|
|
49
|
+
2. Extract summary from PRD's first H1 heading
|
|
50
|
+
3. Lazy-prompt `VDS_JIRA_TOKEN` + `VDS_JIRA_PROJECT_DEFAULT` if missing
|
|
51
|
+
4. Confirm with user
|
|
52
|
+
5. Run `vds-cli jira create --project KEY --issuetype Epic --summary ... --description-file ... --yes --json-only`
|
|
53
|
+
6. Save returned epic key to `<feature-dir>/jira-epic-key.txt`
|
|
54
|
+
|
|
55
|
+
## Implementation
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
bash .claude/skills/vds-skill-create-jira-epic/scripts/create-epic.sh "$@"
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Notes
|
|
62
|
+
|
|
63
|
+
- Dry-run prints constructed `vds-cli` command without executing
|
|
64
|
+
- Dry-run skips `vds-cli` availability check — works on machines without vds-cli installed
|
|
65
|
+
- Epic key written to `<feature-dir>/jira-epic-key.txt` for later cross-reference
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# vds-skill-create-jira-epic: create a Jira Epic via vds-cli
|
|
3
|
+
# Usage: create-epic.sh [--dry-run] [--project KEY] [--description-file FILE] [--feature NAME]
|
|
4
|
+
|
|
5
|
+
# NOTE: No `set -e` — we want full control of the exit-code path.
|
|
6
|
+
|
|
7
|
+
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
8
|
+
# shellcheck source=/dev/null
|
|
9
|
+
source "$SCRIPT_DIR/../../_shared/vds-skill/credentials.sh"
|
|
10
|
+
|
|
11
|
+
require_command python3
|
|
12
|
+
# Note: require_command vds-cli moved AFTER dry-run check so --dry-run
|
|
13
|
+
# works without vds-cli installed (preview mode).
|
|
14
|
+
|
|
15
|
+
DRY_RUN=false
|
|
16
|
+
PROJECT_KEY=""
|
|
17
|
+
DESC_FILE=""
|
|
18
|
+
FEATURE_NAME=""
|
|
19
|
+
|
|
20
|
+
while [[ $# -gt 0 ]]; do
|
|
21
|
+
case "$1" in
|
|
22
|
+
--dry-run) DRY_RUN=true; shift ;;
|
|
23
|
+
--project) PROJECT_KEY="$2"; shift 2 ;;
|
|
24
|
+
--description-file) DESC_FILE="$2"; shift 2 ;;
|
|
25
|
+
--feature) FEATURE_NAME="$2"; shift 2 ;;
|
|
26
|
+
*) echo "Unknown arg: $1" >&2; exit 2 ;;
|
|
27
|
+
esac
|
|
28
|
+
done
|
|
29
|
+
|
|
30
|
+
# Determine PRD file path
|
|
31
|
+
if [[ -z "$DESC_FILE" ]]; then
|
|
32
|
+
if [[ -n "$FEATURE_NAME" ]]; then
|
|
33
|
+
for path in ".vsaf/docs/features/$FEATURE_NAME/02-prd.md"; do
|
|
34
|
+
[[ -f "$path" ]] && DESC_FILE="$path" && break
|
|
35
|
+
done
|
|
36
|
+
fi
|
|
37
|
+
|
|
38
|
+
# Fallback: most recent 02-prd.md
|
|
39
|
+
if [[ -z "$DESC_FILE" ]]; then
|
|
40
|
+
DESC_FILE=$(find .vsaf/docs/features -name '02-prd.md' 2>/dev/null \
|
|
41
|
+
| xargs -I{} ls -t {} 2>/dev/null | head -1)
|
|
42
|
+
fi
|
|
43
|
+
fi
|
|
44
|
+
|
|
45
|
+
if [[ -z "$DESC_FILE" ]] || [[ ! -f "$DESC_FILE" ]]; then
|
|
46
|
+
echo "ERROR: PRD file not found. Run /sdlc-prd first or specify --description-file" >&2
|
|
47
|
+
exit 1
|
|
48
|
+
fi
|
|
49
|
+
|
|
50
|
+
# Derive feature name from PRD path if not given
|
|
51
|
+
if [[ -z "$FEATURE_NAME" ]]; then
|
|
52
|
+
FEATURE_NAME=$(basename "$(dirname "$DESC_FILE")")
|
|
53
|
+
fi
|
|
54
|
+
|
|
55
|
+
# Extract summary from PRD (first H1)
|
|
56
|
+
SUMMARY=$(grep -m1 '^# ' "$DESC_FILE" | sed 's/^# //' || true)
|
|
57
|
+
[[ -z "$SUMMARY" ]] && SUMMARY="$FEATURE_NAME"
|
|
58
|
+
|
|
59
|
+
if [[ "$DRY_RUN" == "true" ]]; then
|
|
60
|
+
DISPLAY_PROJECT="${PROJECT_KEY:-${VDS_JIRA_PROJECT_DEFAULT:-<VDS_JIRA_PROJECT_DEFAULT>}}"
|
|
61
|
+
CMD_DRY=(vds-cli jira create
|
|
62
|
+
--project "$DISPLAY_PROJECT"
|
|
63
|
+
--issuetype Epic
|
|
64
|
+
--summary "$SUMMARY"
|
|
65
|
+
--description-file "$DESC_FILE")
|
|
66
|
+
echo "DRY-RUN — would execute:"
|
|
67
|
+
printf ' %q' "${CMD_DRY[@]}"
|
|
68
|
+
echo ""
|
|
69
|
+
echo " + --yes --json-only"
|
|
70
|
+
exit 0
|
|
71
|
+
fi
|
|
72
|
+
|
|
73
|
+
require_command vds-cli
|
|
74
|
+
|
|
75
|
+
# Ensure credentials
|
|
76
|
+
ensure_env VDS_JIRA_TOKEN "Enter VDS Jira personal access token"
|
|
77
|
+
ensure_env VDS_JIRA_PROJECT_DEFAULT "Enter default Jira project key (e.g. NTTC)" false
|
|
78
|
+
|
|
79
|
+
PROJECT_KEY="${PROJECT_KEY:-$VDS_JIRA_PROJECT_DEFAULT}"
|
|
80
|
+
|
|
81
|
+
CMD=(vds-cli jira create
|
|
82
|
+
--project "$PROJECT_KEY"
|
|
83
|
+
--issuetype Epic
|
|
84
|
+
--summary "$SUMMARY"
|
|
85
|
+
--description-file "$DESC_FILE")
|
|
86
|
+
|
|
87
|
+
echo "About to create Jira Epic:"
|
|
88
|
+
echo " Project: $PROJECT_KEY"
|
|
89
|
+
echo " Summary: $SUMMARY"
|
|
90
|
+
echo " Description: $DESC_FILE"
|
|
91
|
+
echo " Feature: $FEATURE_NAME"
|
|
92
|
+
echo ""
|
|
93
|
+
read -rp "Proceed? [y/N]: " confirm
|
|
94
|
+
if [[ "$confirm" != "y" ]] && [[ "$confirm" != "Y" ]]; then
|
|
95
|
+
echo "Aborted"
|
|
96
|
+
exit 1
|
|
97
|
+
fi
|
|
98
|
+
|
|
99
|
+
RESULT=$("${CMD[@]}" --yes --json-only)
|
|
100
|
+
EPIC_KEY=$(echo "$RESULT" | python3 -c 'import json,sys; print(json.load(sys.stdin).get("key",""))')
|
|
101
|
+
|
|
102
|
+
if [[ -z "$EPIC_KEY" ]]; then
|
|
103
|
+
echo "ERROR: Failed to extract epic key from vds-cli response" >&2
|
|
104
|
+
echo "Response: $RESULT" >&2
|
|
105
|
+
exit 1
|
|
106
|
+
fi
|
|
107
|
+
|
|
108
|
+
EPIC_KEY_FILE="$(dirname "$DESC_FILE")/jira-epic-key.txt"
|
|
109
|
+
echo "$EPIC_KEY" > "$EPIC_KEY_FILE"
|
|
110
|
+
|
|
111
|
+
echo ""
|
|
112
|
+
echo "✓ Created Jira Epic: $EPIC_KEY"
|
|
113
|
+
echo " Saved key to: $EPIC_KEY_FILE"
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* VDS Skill Pack — credential & config setup
|
|
5
|
+
*
|
|
6
|
+
* Creates ~/.vds/sdlc-config.env template with all required
|
|
7
|
+
* environment variables for vds-skill-* skills.
|
|
8
|
+
*
|
|
9
|
+
* Usage:
|
|
10
|
+
* node install-deps.mjs [projectPath]
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'fs';
|
|
14
|
+
import { join } from 'path';
|
|
15
|
+
import { homedir } from 'os';
|
|
16
|
+
|
|
17
|
+
const projectPath = process.argv[2] || process.cwd();
|
|
18
|
+
|
|
19
|
+
// ── Config ──
|
|
20
|
+
|
|
21
|
+
const VDS_DIR = join(homedir(), '.vds');
|
|
22
|
+
const CONFIG_FILE = join(VDS_DIR, 'sdlc-config.env');
|
|
23
|
+
|
|
24
|
+
const REQUIRED_VARS = [
|
|
25
|
+
{ name: 'VDS_CONFLUENCE_TOKEN', desc: 'Confluence personal access token', skills: 'push-prd, push-srs, search-confluence' },
|
|
26
|
+
{ name: 'VDS_JIRA_TOKEN', desc: 'Jira personal access token', skills: 'create-jira-epic, search-confluence' },
|
|
27
|
+
{ name: 'VDS_BITBUCKET_TOKEN', desc: 'Bitbucket personal access token', skills: 'create-bitbucket-pr' },
|
|
28
|
+
{ name: 'VDS_CONFLUENCE_SPACE_DEFAULT', desc: 'Default Confluence space key (e.g. CEP)', skills: 'push-prd, push-srs, search-confluence' },
|
|
29
|
+
{ name: 'VDS_JIRA_PROJECT_DEFAULT', desc: 'Default Jira project key (e.g. NTTC)', skills: 'create-jira-epic, search-confluence' },
|
|
30
|
+
];
|
|
31
|
+
|
|
32
|
+
// ── Helpers ──
|
|
33
|
+
|
|
34
|
+
function log(icon, msg) {
|
|
35
|
+
console.log(` ${icon} ${msg}`);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function parseExistingConfig(filePath) {
|
|
39
|
+
if (!existsSync(filePath)) return {};
|
|
40
|
+
const vars = {};
|
|
41
|
+
for (const line of readFileSync(filePath, 'utf-8').split('\n')) {
|
|
42
|
+
const trimmed = line.trim();
|
|
43
|
+
if (!trimmed || trimmed.startsWith('#')) continue;
|
|
44
|
+
const eq = trimmed.indexOf('=');
|
|
45
|
+
if (eq > 0) {
|
|
46
|
+
const key = trimmed.slice(0, eq);
|
|
47
|
+
const val = trimmed.slice(eq + 1).replace(/^['"]|['"]$/g, '');
|
|
48
|
+
if (val) vars[key] = val;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return vars;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// ── Step 1: Ensure ~/.vds/ directory ──
|
|
55
|
+
|
|
56
|
+
console.log('\n[vds-skill] Setting up VDS credentials...\n');
|
|
57
|
+
|
|
58
|
+
mkdirSync(VDS_DIR, { recursive: true });
|
|
59
|
+
|
|
60
|
+
// ── Step 2: Create or merge config template ──
|
|
61
|
+
|
|
62
|
+
const existing = parseExistingConfig(CONFIG_FILE);
|
|
63
|
+
const missing = [];
|
|
64
|
+
const filled = [];
|
|
65
|
+
|
|
66
|
+
for (const v of REQUIRED_VARS) {
|
|
67
|
+
if (existing[v.name]) {
|
|
68
|
+
filled.push(v);
|
|
69
|
+
} else {
|
|
70
|
+
missing.push(v);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (!existsSync(CONFIG_FILE)) {
|
|
75
|
+
// Create fresh template
|
|
76
|
+
const lines = [
|
|
77
|
+
'# VDS Skill Pack — credential config',
|
|
78
|
+
'# File: ~/.vds/sdlc-config.env',
|
|
79
|
+
'# Permissions: 600 (auto-set by credentials.sh)',
|
|
80
|
+
'#',
|
|
81
|
+
'# Fill in the values below. Skills will use these automatically.',
|
|
82
|
+
'# To rotate a token: update the value here, skills pick it up next run.',
|
|
83
|
+
'',
|
|
84
|
+
];
|
|
85
|
+
|
|
86
|
+
for (const v of REQUIRED_VARS) {
|
|
87
|
+
lines.push(`# ${v.desc}`);
|
|
88
|
+
lines.push(`# Used by: ${v.skills}`);
|
|
89
|
+
lines.push(`${v.name}=`);
|
|
90
|
+
lines.push('');
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
writeFileSync(CONFIG_FILE, lines.join('\n'), { mode: 0o600 });
|
|
94
|
+
log('✅', `Config created: ${CONFIG_FILE}`);
|
|
95
|
+
} else if (missing.length > 0) {
|
|
96
|
+
// Append missing vars to existing config
|
|
97
|
+
const appendLines = ['\n# --- Added by vds-skill install ---\n'];
|
|
98
|
+
for (const v of missing) {
|
|
99
|
+
if (!readFileSync(CONFIG_FILE, 'utf-8').includes(v.name)) {
|
|
100
|
+
appendLines.push(`# ${v.desc}`);
|
|
101
|
+
appendLines.push(`# Used by: ${v.skills}`);
|
|
102
|
+
appendLines.push(`${v.name}=`);
|
|
103
|
+
appendLines.push('');
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
if (appendLines.length > 1) {
|
|
107
|
+
writeFileSync(CONFIG_FILE, readFileSync(CONFIG_FILE, 'utf-8') + appendLines.join('\n'), { mode: 0o600 });
|
|
108
|
+
log('✅', `Config updated: ${CONFIG_FILE}`);
|
|
109
|
+
}
|
|
110
|
+
} else {
|
|
111
|
+
log('✅', `Config already complete: ${CONFIG_FILE}`);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// ── Step 3: Print status ──
|
|
115
|
+
|
|
116
|
+
console.log('');
|
|
117
|
+
if (filled.length > 0) {
|
|
118
|
+
log('✅', `${filled.length} credential(s) already configured:`);
|
|
119
|
+
for (const v of filled) {
|
|
120
|
+
log(' ', `${v.name} — ${v.desc}`);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (missing.length > 0) {
|
|
125
|
+
console.log('');
|
|
126
|
+
log('⚠️', `${missing.length} credential(s) need to be filled:`);
|
|
127
|
+
console.log('');
|
|
128
|
+
console.log(' ┌────────────────────────────────────┬──────────────────────────────────────────────┐');
|
|
129
|
+
console.log(' │ Variable │ Description │');
|
|
130
|
+
console.log(' ├────────────────────────────────────┼──────────────────────────────────────────────┤');
|
|
131
|
+
for (const v of missing) {
|
|
132
|
+
const name = v.name.padEnd(36);
|
|
133
|
+
const desc = v.desc.padEnd(44);
|
|
134
|
+
console.log(` │ ${name}│ ${desc}│`);
|
|
135
|
+
}
|
|
136
|
+
console.log(' └────────────────────────────────────┴──────────────────────────────────────────────┘');
|
|
137
|
+
console.log('');
|
|
138
|
+
log('📝', `Edit config file:`);
|
|
139
|
+
console.log(` ${CONFIG_FILE}`);
|
|
140
|
+
console.log('');
|
|
141
|
+
log('💡', 'Skills will also lazy-prompt at runtime if values are empty.');
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// ── Step 4: Check vds-cli ──
|
|
145
|
+
|
|
146
|
+
console.log('');
|
|
147
|
+
try {
|
|
148
|
+
const { execSync } = await import('child_process');
|
|
149
|
+
const version = execSync('vds-cli --version', { stdio: 'pipe', encoding: 'utf-8' }).trim();
|
|
150
|
+
log('✅', `vds-cli ${version}`);
|
|
151
|
+
} catch {
|
|
152
|
+
log('⚠️', 'vds-cli not found — required for non-dry-run execution');
|
|
153
|
+
log(' ', 'Install vds-scripts from Viettel internal repo, then verify:');
|
|
154
|
+
log(' ', ' command -v vds-cli && vds-cli --version');
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
console.log('\n[vds-skill] Setup complete ✅\n');
|