@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.
Files changed (39) hide show
  1. package/package.json +1 -1
  2. package/packages/cli/dist/commands/install.d.ts.map +1 -1
  3. package/packages/cli/dist/commands/install.js +15 -2
  4. package/packages/cli/dist/commands/install.js.map +1 -1
  5. package/packages/cli/dist/commands/skill.d.ts.map +1 -1
  6. package/packages/cli/dist/commands/skill.js +1 -9
  7. package/packages/cli/dist/commands/skill.js.map +1 -1
  8. package/packages/cli/dist/index.js +1 -1
  9. package/packages/cli/dist/index.js.map +1 -1
  10. package/skills/sdlc/discovery/SKILL.md +20 -5
  11. package/skills/sdlc/hotfix-analyze/SKILL.md +41 -17
  12. package/skills/sdlc/implement/SKILL.md +47 -14
  13. package/skills/sdlc/install-deps.mjs +7 -1
  14. package/skills/sdlc/pack.yaml +1 -1
  15. package/skills/sdlc/sdlc-health/SKILL.md +36 -36
  16. package/skills/vds-skill/_shared/credentials.sh +79 -0
  17. package/skills/vds-skill/create-bitbucket-pr/SKILL.md +65 -0
  18. package/skills/vds-skill/create-bitbucket-pr/scripts/create-pr.sh +105 -0
  19. package/skills/vds-skill/create-jira-epic/SKILL.md +65 -0
  20. package/skills/vds-skill/create-jira-epic/scripts/create-epic.sh +113 -0
  21. package/skills/vds-skill/install-deps.mjs +157 -0
  22. package/skills/vds-skill/pack.yaml +24 -0
  23. package/skills/vds-skill/pull/SKILL.md +38 -0
  24. package/skills/vds-skill/pull/scripts/pull.sh +52 -0
  25. package/skills/vds-skill/push-prd/SKILL.md +238 -0
  26. package/skills/vds-skill/push-srs/SKILL.md +128 -0
  27. package/skills/vds-skill/search-confluence/SKILL.md +66 -0
  28. package/skills/vds-skill/search-confluence/scripts/search.sh +128 -0
  29. package/skills/vds-skill/vds-scripts-skill/.openskills.json +6 -0
  30. package/skills/vds-skill/vds-scripts-skill/QUALITY.md +44 -0
  31. package/skills/vds-skill/vds-scripts-skill/SKILL.md +127 -0
  32. package/skills/vds-skill/vds-scripts-skill/references/audit-commands.md +171 -0
  33. package/skills/vds-skill/vds-scripts-skill/references/capability-index.md +34 -0
  34. package/skills/vds-skill/vds-scripts-skill/references/development-commands.md +12 -0
  35. package/skills/vds-skill/vds-scripts-skill/references/google-sheets.md +71 -0
  36. package/skills/vds-skill/vds-scripts-skill/references/integration-commands.md +17 -0
  37. package/skills/vds-skill/vds-scripts-skill/references/platform-bootstrap.md +31 -0
  38. package/skills/vds-skill/vds-scripts-skill/references/specialist-routing.md +14 -0
  39. package/skills/vds-skill/vds-scripts-skill/references/validation-commands.md +15 -0
@@ -0,0 +1,24 @@
1
+ name: vds-skill
2
+ version: 1.0.0
3
+ description: Viettel VDS automation skills — opt-in pack wrapping vds-cli for Confluence/Jira/Bitbucket integration with SDLC flow
4
+ author: "@ngocsangairvds/vsaf"
5
+
6
+ # Skills deployed WITH prefix: /vds-skill-<name>
7
+ skills:
8
+ # Knowledge sync
9
+ - pull # /vds-skill-pull — git pull + stale index detection
10
+
11
+ # Discovery enrichment (helper for Phase 1)
12
+ - search-confluence # /vds-skill-search-confluence — search Confluence + Jira for historical context
13
+
14
+ # PRD/SRS publication (helper for Phase 2/4)
15
+ - push-prd # /vds-skill-push-prd — push PRD to Confluence via vds-cli
16
+ - push-srs # /vds-skill-push-srs — push SRS to Confluence via vds-cli + link PRD
17
+
18
+ # Epic/PR creation (helper for Phase 2/9)
19
+ - create-jira-epic # /vds-skill-create-jira-epic — create Jira Epic from PRD
20
+ - create-bitbucket-pr # /vds-skill-create-bitbucket-pr — create Bitbucket PR after ship
21
+
22
+ # Bundled external skills — deployed WITHOUT prefix (as-is)
23
+ bundled:
24
+ - vds-scripts-skill # /vds-scripts-skill — platform-routing skill (imported from /Downloads/vds-scripts/)
@@ -0,0 +1,38 @@
1
+ ---
2
+ name: vds-skill-pull
3
+ description: Pull latest code via `git pull` then detect stale local knowledge index (`.gitnexus/`, `graphify-out/graph.json`) by comparing mtime with `.git/FETCH_HEAD`. Suggest `/sdlc-onboard-code` if stale. Use instead of bare `git pull` for projects using SDLC pack.
4
+ ---
5
+
6
+ # /vds-skill-pull
7
+
8
+ Wrapper for `git pull` that detects stale local knowledge index after pull and suggests rebuild.
9
+
10
+ ## Usage
11
+
12
+ ```bash
13
+ /vds-skill-pull # equivalent to `git pull`
14
+ /vds-skill-pull --rebase # forwards args to git pull
15
+ /vds-skill-pull origin main # also forwards
16
+ ```
17
+
18
+ ## Behavior
19
+
20
+ 1. Run `git pull` with all passed args
21
+ 2. If pull succeeds, check `.gitnexus/` and `graphify-out/graph.json` mtime vs `.git/FETCH_HEAD`
22
+ 3. If any stale → print suggestion to run `/sdlc-onboard-code`
23
+ 4. Exit 0 (advisory, does not block workflow)
24
+
25
+ ## Implementation
26
+
27
+ Invoke the bundled script:
28
+
29
+ ```bash
30
+ bash .claude/skills/vds-skill-pull/scripts/pull.sh "$@"
31
+ ```
32
+
33
+ ## Notes
34
+
35
+ - Works for both Viettel and non-Viettel projects (no vds-cli dependency).
36
+ - If `.gitnexus/` or `graphify-out/` does not exist, that's OK — only flagged if present-but-stale.
37
+ - Skill exits 0 even when knowledge is stale (advisory only — user decides whether to onboard now or later).
38
+ - `.gitnexus/` staleness uses directory mtime — this catches new files but may miss in-place edits. If GitNexus rewrites files without re-creating them, the check could give a false "fresh" reading. Acceptable for advisory mode.
@@ -0,0 +1,52 @@
1
+ #!/bin/bash
2
+ # vds-skill-pull: git pull + detect stale local knowledge index
3
+ # Exit codes: forwards `git pull` exit code (0 on success even if knowledge stale)
4
+
5
+ # Run git pull, forwarding all args. Capture exit code for explicit forwarding.
6
+ # (No `set -e` — we want full control of the exit-code path for the advisory stale check.)
7
+ git pull "$@"
8
+ EXIT=$?
9
+
10
+ if [[ $EXIT -ne 0 ]]; then
11
+ exit $EXIT
12
+ fi
13
+
14
+ # Skip stale check if not a fetch-based pull (e.g., --no-rebase --quiet might skip FETCH_HEAD)
15
+ if [[ ! -f .git/FETCH_HEAD ]]; then
16
+ exit 0
17
+ fi
18
+
19
+ FETCH_HEAD_MTIME=$(stat -c %Y .git/FETCH_HEAD 2>/dev/null || stat -f %m .git/FETCH_HEAD 2>/dev/null || echo 0)
20
+ STALE_REASONS=()
21
+
22
+ # Check .gitnexus/
23
+ if [[ -d .gitnexus ]]; then
24
+ GITNEXUS_MTIME=$(stat -c %Y .gitnexus 2>/dev/null || stat -f %m .gitnexus 2>/dev/null || echo 0)
25
+ if [[ $GITNEXUS_MTIME -lt $FETCH_HEAD_MTIME ]]; then
26
+ STALE_REASONS+=(".gitnexus/ index older than fetched commits")
27
+ fi
28
+ fi
29
+ # Note: .gitnexus missing is OK (project may not use GitNexus)
30
+
31
+ # Check graphify-out/graph.json
32
+ if [[ -f graphify-out/graph.json ]]; then
33
+ GRAPH_MTIME=$(stat -c %Y graphify-out/graph.json 2>/dev/null || stat -f %m graphify-out/graph.json 2>/dev/null || echo 0)
34
+ if [[ $GRAPH_MTIME -lt $FETCH_HEAD_MTIME ]]; then
35
+ STALE_REASONS+=("graphify-out/graph.json older than fetched commits")
36
+ fi
37
+ fi
38
+ # Note: graphify-out missing is OK
39
+
40
+ # Print suggestion if anything stale
41
+ if [[ ${#STALE_REASONS[@]} -gt 0 ]]; then
42
+ echo ""
43
+ echo "⚠️ Knowledge index có thể stale sau pull:"
44
+ for reason in "${STALE_REASONS[@]}"; do
45
+ echo " - $reason"
46
+ done
47
+ echo ""
48
+ echo " Khuyến nghị: chạy /sdlc-onboard-code để rebuild GitNexus + Graphify locally."
49
+ echo ""
50
+ fi
51
+
52
+ exit 0
@@ -0,0 +1,238 @@
1
+ ---
2
+ name: vds-skill-push-prd
3
+ description: Push PRD markdown to Viettel Confluence via vds-cli. Converts markdown → Confluence Storage Format (XHTML), searches for existing page, creates or updates. Use after /sdlc-prd has produced 02-prd.md. Viettel-only (requires vds-cli).
4
+ ---
5
+
6
+ # /vds-skill-push-prd
7
+
8
+ Publish the current PRD to Viettel Confluence via `vds-cli` — create a new page if it doesn't exist, or update the existing one. The optional `[comment]` becomes the Confluence version note.
9
+
10
+ ## Input
11
+
12
+ - `[file]` — optional path to a specific markdown file. Default: scan `.vsaf/docs/features/*/02-prd.md`
13
+ - `[comment]` — optional version note (e.g., "Initial draft", "Updated after stakeholder review")
14
+
15
+ ## Config Check (run FIRST)
16
+
17
+ Before doing anything, run this check via Bash tool:
18
+
19
+ ```bash
20
+ source .claude/skills/_shared/vds-skill/credentials.sh 2>/dev/null
21
+ MISSING=""
22
+ command -v vds-cli >/dev/null 2>&1 || MISSING="$MISSING vds-cli"
23
+ [[ -z "${VDS_CONFLUENCE_TOKEN:-}" ]] && MISSING="$MISSING VDS_CONFLUENCE_TOKEN"
24
+ [[ -z "${VDS_CONFLUENCE_SPACE_DEFAULT:-}" ]] && MISSING="$MISSING VDS_CONFLUENCE_SPACE_DEFAULT"
25
+ if [[ -n "$MISSING" ]]; then
26
+ echo "BLOCKED — missing:$MISSING"
27
+ echo "Fix: edit ~/.vds/sdlc-config.env (or run: vsaf install vds-skill)"
28
+ else
29
+ echo "OK"
30
+ fi
31
+ ```
32
+
33
+ If BLOCKED: tell the user exactly what's missing and how to fix, then STOP. Do NOT fabricate output.
34
+
35
+ ## Prerequisites
36
+
37
+ - `vds-cli` installed (`command -v vds-cli`)
38
+ - A PRD markdown file must exist
39
+ - `VDS_CONFLUENCE_TOKEN` + `VDS_CONFLUENCE_SPACE_DEFAULT` in `~/.vds/sdlc-config.env`
40
+ - Confluence parent page configured in `.vsaf/config.yaml` (or lazy-prompted)
41
+
42
+ ## Steps
43
+
44
+ ### Step 1 — Find PRD file
45
+
46
+ - If `[file]` given: use directly
47
+ - Otherwise: list `.vsaf/docs/features/*/02-prd.md`
48
+ - Multiple: ask user which one
49
+ - None: STOP — tell user to run `/sdlc-prd` first
50
+ - Read full file content
51
+
52
+ ### Step 2 — Load Confluence config
53
+
54
+ Read `.vsaf/config.yaml` (or `.vsaf/_bmad/bmm/config.yaml`) and extract:
55
+ - `confluence_space_key`
56
+ - `confluence_parent_page`
57
+
58
+ If missing: lazy-prompt + persist via `_shared/credentials.sh ensure_env`.
59
+
60
+ ### Step 3 — Determine page title
61
+
62
+ - For `02-prd.md` under `features/{feature-name}/`: title = `[PRD] {Feature Name}` (derived from folder name)
63
+ - Otherwise: use first `# Heading` in file, or filename stem
64
+
65
+ ### Step 4 — Convert markdown → Confluence storage format (XHTML)
66
+
67
+ **CRITICAL RULES:**
68
+
69
+ 1. **Never send raw markdown.** All content must be converted to Confluence Storage Format (XHTML).
70
+ 2. **Never wrap entire body in `<![CDATA[...]]>`.** CDATA is ONLY valid inside `<ac:plain-text-body>` within code macros.
71
+ 3. **All Confluence macro attributes MUST use `ac:` namespace prefix.** The tag is `<ac:parameter ac:name="...">`, never `<parameter name="...">`.
72
+ 4. **Escape HTML entities in regular text:** `&` → `&amp;`, `<` → `&lt;`, `>` → `&gt;`.
73
+ 5. **Every opened tag must be closed.** Confluence XML parser is strict.
74
+
75
+ **Block elements:**
76
+
77
+ ```
78
+ # Heading → <h1>Heading</h1>
79
+ ## Heading → <h2>Heading</h2>
80
+ ### Heading → <h3>Heading</h3>
81
+ #### Heading → <h4>Heading</h4>
82
+ paragraph → <p>paragraph</p>
83
+ --- → <hr/>
84
+ ```
85
+
86
+ **Inline elements:**
87
+
88
+ ```
89
+ **bold** → <strong>bold</strong>
90
+ *italic* → <em>italic</em>
91
+ `code` → <code>code</code>
92
+ [text](url) → <a href="url">text</a>
93
+ ```
94
+
95
+ **Lists:**
96
+
97
+ ```
98
+ - item → <ul><li>item</li></ul>
99
+ 1. item → <ol><li>item</li></ol>
100
+ ```
101
+
102
+ **Tables:**
103
+
104
+ ```
105
+ | H1 | H2 | → <table><tbody>
106
+ |----|----| <tr><th>H1</th><th>H2</th></tr>
107
+ | a | b | <tr><td>a</td><td>b</td></tr>
108
+ </tbody></table>
109
+ ```
110
+
111
+ **Code blocks (ONLY place for CDATA):**
112
+
113
+ ```
114
+ ```lang → <ac:structured-macro ac:name="code">
115
+ code here <ac:parameter ac:name="language">lang</ac:parameter>
116
+ ``` <ac:plain-text-body><![CDATA[code here]]></ac:plain-text-body>
117
+ </ac:structured-macro>
118
+ ```
119
+
120
+ **Info/warning/tip boxes:**
121
+
122
+ ```
123
+ > [!NOTE] → <ac:structured-macro ac:name="info">
124
+ > body <ac:parameter ac:name="title">title</ac:parameter>
125
+ <ac:rich-text-body><p>body</p></ac:rich-text-body>
126
+ </ac:structured-macro>
127
+
128
+ > [!WARNING] → ac:name="warning"
129
+ > [!TIP] → ac:name="tip"
130
+ ```
131
+
132
+ **Blockquote:**
133
+
134
+ ```
135
+ > text → <blockquote><p>text</p></blockquote>
136
+ ```
137
+
138
+ Save converted XHTML to a temp file: `BODY_FILE=$(mktemp --suffix=.html)`.
139
+
140
+ ### Step 5 — Search for existing page
141
+
142
+ Use Bash tool to run:
143
+
144
+ ```bash
145
+ source .claude/skills/_shared/vds-skill/credentials.sh
146
+ ensure_env VDS_CONFLUENCE_TOKEN "Enter VDS Confluence personal access token"
147
+
148
+ SPACE_KEY="<from config>"
149
+ PAGE_TITLE="<title from Step 3>"
150
+
151
+ SEARCH_RESULT=$(vds-cli confluence search \
152
+ --cql "space=$SPACE_KEY AND title = \"$PAGE_TITLE\"" \
153
+ --limit 1 --json-only)
154
+
155
+ EXISTING_PAGE_ID=$(echo "$SEARCH_RESULT" | python3 -c \
156
+ 'import json,sys; d=json.load(sys.stdin); print(d["results"][0]["id"] if d.get("results") else "")')
157
+ ```
158
+
159
+ If `$EXISTING_PAGE_ID` non-empty → Step 6 (update). Otherwise → Step 7 (create).
160
+
161
+ ### Step 6 — Update existing page (via vds-cli)
162
+
163
+ ```bash
164
+ EXISTING_VERSION=$(echo "$SEARCH_RESULT" | python3 -c \
165
+ 'import json,sys; d=json.load(sys.stdin); print(d["results"][0].get("version",{}).get("number",1))')
166
+
167
+ VERSION_COMMENT="${COMMENT:-Updated via vds-skill-push-prd}"
168
+
169
+ vds-cli confluence content update \
170
+ --page-id "$EXISTING_PAGE_ID" \
171
+ --body-file "$BODY_FILE" \
172
+ --version $((EXISTING_VERSION + 1)) \
173
+ --version-comment "$VERSION_COMMENT" \
174
+ --json-only
175
+
176
+ rm -f "$BODY_FILE"
177
+ ```
178
+
179
+ **Conflict handling:** if `vds-cli` returns version mismatch error, retry once with fresh version:
180
+
181
+ ```bash
182
+ LATEST_VERSION=$(vds-cli confluence content page "$EXISTING_PAGE_ID" --json-only \
183
+ | python3 -c 'import json,sys; print(json.load(sys.stdin)["version"]["number"])')
184
+
185
+ vds-cli confluence content update \
186
+ --page-id "$EXISTING_PAGE_ID" \
187
+ --body-file "$BODY_FILE" \
188
+ --version $((LATEST_VERSION + 1)) \
189
+ --version-comment "$VERSION_COMMENT" \
190
+ --json-only
191
+ ```
192
+
193
+ ### Step 7 — Create new page (via vds-cli)
194
+
195
+ ```bash
196
+ # Resolve parent page ID via search
197
+ PARENT_ID=""
198
+ if [[ -n "$CONFLUENCE_PARENT_PAGE" ]]; then
199
+ PARENT_ID=$(vds-cli confluence search \
200
+ --cql "space=$SPACE_KEY AND title = \"$CONFLUENCE_PARENT_PAGE\"" \
201
+ --limit 1 --json-only \
202
+ | python3 -c 'import json,sys; d=json.load(sys.stdin); print(d["results"][0]["id"] if d.get("results") else "")')
203
+ fi
204
+
205
+ CREATE_ARGS=(vds-cli confluence content create-page
206
+ --space "$SPACE_KEY"
207
+ --title "$PAGE_TITLE"
208
+ --body-file "$BODY_FILE"
209
+ --version-comment "${COMMENT:-Initial PRD push via vds-skill-push-prd}"
210
+ --json-only)
211
+ [[ -n "$PARENT_ID" ]] && CREATE_ARGS+=(--parent-id "$PARENT_ID")
212
+
213
+ "${CREATE_ARGS[@]}"
214
+ rm -f "$BODY_FILE"
215
+ ```
216
+
217
+ ### Step 8 — Output to user
218
+
219
+ ```
220
+ ## PRD pushed to Confluence
221
+
222
+ - Page: {page_title}
223
+ - Action: [Created / Updated]
224
+ - Space: {space_key}
225
+ - Comment: {comment}
226
+ - URL: {confluence_page_url}
227
+
228
+ ## Next step
229
+ Optionally run /vds-skill-create-jira-epic to create a Jira epic from this PRD,
230
+ or continue with /sdlc-architecture (Phase 3).
231
+ ```
232
+
233
+ ## Notes
234
+
235
+ - Never push a PRD that has failed validation
236
+ - If `vds-cli` is not installed, tell user to install vds-scripts first
237
+ - Do not modify the local file during this step
238
+ - Conflict handling: version mismatch → auto-retry once with fresh version number
@@ -0,0 +1,128 @@
1
+ ---
2
+ name: vds-skill-push-srs
3
+ description: Push SRS markdown to Viettel Confluence via vds-cli. Same flow as push-prd plus link to the corresponding PRD page. Use after /sdlc-srs has produced 05-srs.md. Viettel-only (requires vds-cli).
4
+ ---
5
+
6
+ # /vds-skill-push-srs
7
+
8
+ Publish the current SRS to Viettel Confluence via `vds-cli` — create or update the page, and link to the corresponding PRD page if it exists.
9
+
10
+ ## Input
11
+
12
+ - `[file]` — optional path to a specific markdown file. Default: scan `.vsaf/docs/features/*/05-srs.md`
13
+ - `[comment]` — optional version note
14
+
15
+ ## Config Check (run FIRST)
16
+
17
+ Before doing anything, run this check via Bash tool:
18
+
19
+ ```bash
20
+ source .claude/skills/_shared/vds-skill/credentials.sh 2>/dev/null
21
+ MISSING=""
22
+ command -v vds-cli >/dev/null 2>&1 || MISSING="$MISSING vds-cli"
23
+ [[ -z "${VDS_CONFLUENCE_TOKEN:-}" ]] && MISSING="$MISSING VDS_CONFLUENCE_TOKEN"
24
+ [[ -z "${VDS_CONFLUENCE_SPACE_DEFAULT:-}" ]] && MISSING="$MISSING VDS_CONFLUENCE_SPACE_DEFAULT"
25
+ if [[ -n "$MISSING" ]]; then
26
+ echo "BLOCKED — missing:$MISSING"
27
+ echo "Fix: edit ~/.vds/sdlc-config.env (or run: vsaf install vds-skill)"
28
+ else
29
+ echo "OK"
30
+ fi
31
+ ```
32
+
33
+ If BLOCKED: tell the user exactly what's missing and how to fix, then STOP. Do NOT fabricate output.
34
+
35
+ ## Prerequisites
36
+
37
+ - `vds-cli` installed
38
+ - An SRS markdown file must exist
39
+ - `VDS_CONFLUENCE_TOKEN` + `VDS_CONFLUENCE_SPACE_DEFAULT` in `~/.vds/sdlc-config.env`
40
+ - Confluence parent page in config or lazy-prompted
41
+
42
+ ## Steps
43
+
44
+ ### Step 1 — Find SRS file
45
+
46
+ - If `[file]` given: use directly
47
+ - Otherwise: list `.vsaf/docs/features/*/05-srs.md` (exclude `*-results.md`)
48
+ - Multiple: ask user
49
+ - None: STOP — tell user to run `/sdlc-srs` first
50
+ - Read full content
51
+
52
+ ### Step 2 — Load Confluence config
53
+
54
+ Same as `/vds-skill-push-prd` Step 2.
55
+
56
+ ### Step 3 — Determine page title
57
+
58
+ - For `05-srs.md` under `features/{feature-name}/`: title = `[SRS] {Feature Name}` (derived from folder name)
59
+ - Otherwise: first `# Heading` or filename stem
60
+
61
+ ### Step 4 — Convert markdown → Confluence storage format (XHTML)
62
+
63
+ Same conversion rules as `/vds-skill-push-prd` Step 4 (see that skill for full reference). Save converted body to `$BODY_FILE`.
64
+
65
+ ### Step 5 — Search for existing page
66
+
67
+ Same as `/vds-skill-push-prd` Step 5 — search for `[SRS] {Feature Name}` page in the space, store `EXISTING_PAGE_ID` and `SEARCH_RESULT`.
68
+
69
+ ### Step 6 — Update existing page (via vds-cli)
70
+
71
+ Same as `/vds-skill-push-prd` Step 6 (with auto-retry on conflict). Use `Updated via vds-skill-push-srs` as default version comment.
72
+
73
+ ### Step 7 — Create new page (via vds-cli)
74
+
75
+ Same as `/vds-skill-push-prd` Step 7. Use `Initial SRS push via vds-skill-push-srs` as default version comment.
76
+
77
+ ### Step 8 — Link SRS page to PRD page (if PRD exists)
78
+
79
+ ```bash
80
+ source .claude/skills/_shared/vds-skill/credentials.sh
81
+ ensure_env VDS_CONFLUENCE_TOKEN "Enter VDS Confluence personal access token"
82
+
83
+ # Derive PRD page title from feature name
84
+ FEATURE_NAME="<from folder>"
85
+ PRD_TITLE="[PRD] $FEATURE_NAME"
86
+
87
+ PRD_PAGE=$(vds-cli confluence search \
88
+ --cql "space=$SPACE_KEY AND title = \"$PRD_TITLE\"" \
89
+ --limit 1 --json-only)
90
+
91
+ PRD_URL=$(echo "$PRD_PAGE" | python3 -c \
92
+ 'import json,sys; d=json.load(sys.stdin); r=d.get("results"); print(r[0].get("url","") if r else "")')
93
+
94
+ if [[ -n "$PRD_URL" ]]; then
95
+ # Prepend "Related Documents" macro to SRS body BEFORE push (modify $BODY_FILE)
96
+ RELATED_BLOCK='<ac:structured-macro ac:name="info"><ac:parameter ac:name="title">Related Documents</ac:parameter><ac:rich-text-body><p><a href="'"$PRD_URL"'">'"$PRD_TITLE"'</a></p></ac:rich-text-body></ac:structured-macro>'
97
+
98
+ NEW_BODY=$(mktemp --suffix=.html)
99
+ { echo "$RELATED_BLOCK"; cat "$BODY_FILE"; } > "$NEW_BODY"
100
+ mv "$NEW_BODY" "$BODY_FILE"
101
+ fi
102
+ ```
103
+
104
+ This block runs BEFORE Step 6/7 actually executes the create/update — i.e., the Related Documents banner is part of the body content pushed to Confluence.
105
+
106
+ ### Step 9 — Output to user
107
+
108
+ ```
109
+ ## SRS pushed to Confluence
110
+
111
+ - Page: {page_title}
112
+ - Action: [Created / Updated]
113
+ - Space: {space_key}
114
+ - Comment: {comment}
115
+ - URL: {confluence_page_url}
116
+ - PRD link: [linked / not found]
117
+
118
+ ## Next step
119
+ Continue to Phase 5: /sdlc-test-design
120
+ ```
121
+
122
+ ## Notes
123
+
124
+ - If `vds-cli` is not installed, tell user to install vds-scripts first
125
+ - Do not modify the local file during this step
126
+ - SRS result files (`*-results.md`) are test outputs — push those separately if needed
127
+ - Conflict handling: version mismatch → auto-retry once with fresh version number
128
+ - PRD link is best-effort — if PRD page doesn't exist in Confluence yet, SRS pushes without the link
@@ -0,0 +1,66 @@
1
+ ---
2
+ name: vds-skill-search-confluence
3
+ description: Search Viettel Confluence + Jira for historical context related to a feature, write index to .vsaf/docs/features/{name}/01-discovery-historical.{md,json}. Run BEFORE /sdlc-discovery to enrich grill session context. Viettel-only (requires vds-cli).
4
+ ---
5
+
6
+ # /vds-skill-search-confluence
7
+
8
+ Search Viettel Confluence + Jira for pages/tickets related to a feature, then write a Markdown + JSON index into the feature folder. Output is consumed by `/sdlc-discovery` (Phase 1) to enrich the grill session.
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
+ command -v vds-cli >/dev/null 2>&1 || MISSING="$MISSING vds-cli"
18
+ [[ -z "${VDS_CONFLUENCE_TOKEN:-}" ]] && MISSING="$MISSING VDS_CONFLUENCE_TOKEN"
19
+ [[ -z "${VDS_JIRA_TOKEN:-}" ]] && MISSING="$MISSING VDS_JIRA_TOKEN"
20
+ [[ -z "${VDS_CONFLUENCE_SPACE_DEFAULT:-}" ]] && MISSING="$MISSING VDS_CONFLUENCE_SPACE_DEFAULT"
21
+ [[ -z "${VDS_JIRA_PROJECT_DEFAULT:-}" ]] && MISSING="$MISSING VDS_JIRA_PROJECT_DEFAULT"
22
+ if [[ -n "$MISSING" ]]; then
23
+ echo "BLOCKED — missing:$MISSING"
24
+ echo "Fix: edit ~/.vds/sdlc-config.env (or run: vsaf install vds-skill)"
25
+ else
26
+ echo "OK"
27
+ fi
28
+ ```
29
+
30
+ If BLOCKED: tell the user exactly what's missing and how to fix, then STOP. Do NOT fabricate output. `--dry-run` mode skips vds-cli check.
31
+
32
+ ## Prerequisites
33
+
34
+ - `vds-cli` installed
35
+ - `python3` installed
36
+ - Inside a project root with `.vsaf/docs/features/`
37
+ - `VDS_CONFLUENCE_TOKEN`, `VDS_JIRA_TOKEN`, `VDS_CONFLUENCE_SPACE_DEFAULT`, `VDS_JIRA_PROJECT_DEFAULT` in `~/.vds/sdlc-config.env`
38
+
39
+ ## Usage
40
+
41
+ ```bash
42
+ /vds-skill-search-confluence --feature user-management --query "auth jwt"
43
+ /vds-skill-search-confluence --feature payment --query "payment flow" --days 90 --limit 50
44
+ /vds-skill-search-confluence --feature billing --query "billing" --dry-run
45
+ ```
46
+
47
+ ## Behavior
48
+
49
+ 1. Build CQL: `space=<DEFAULT> AND text ~ "<QUERY>" AND lastmodified > now("-Nd")`
50
+ 2. Build JQL: `project = <DEFAULT> AND text ~ "<QUERY>" AND created > -Nd ORDER BY created DESC`
51
+ 3. Execute `vds-cli confluence search` + `vds-cli jira search` (sequential)
52
+ 4. Combine into JSON at `<feature-dir>/01-discovery-historical.json`
53
+ 5. Generate Markdown summary at `<feature-dir>/01-discovery-historical.md` (top 10 Confluence + top 15 Jira)
54
+
55
+ ## Implementation
56
+
57
+ ```bash
58
+ bash .claude/skills/vds-skill-search-confluence/scripts/search.sh "$@"
59
+ ```
60
+
61
+ ## Notes
62
+
63
+ - Run BEFORE `/sdlc-discovery` to enrich grill session.
64
+ - Default `--days 180`, `--limit 30`.
65
+ - Empty results are OK — Markdown still generated, sdlc-discovery proceeds normally.
66
+ - Dry-run skips `vds-cli` invocation — preview mode works without `vds-cli` installed.
@@ -0,0 +1,128 @@
1
+ #!/bin/bash
2
+ # vds-skill-search-confluence: search Confluence + Jira for feature discovery context
3
+ # Usage: search.sh --feature NAME --query KEYWORDS [--dry-run] [--days N] [--limit N]
4
+
5
+ # NOTE: No `set -e` — we want full control of the exit-code path.
6
+
7
+ # Source credentials helper (deployed path: .claude/skills/_shared/vds-skill/credentials.sh)
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 python3
13
+ # Note: require_command vds-cli moved AFTER dry-run check so --dry-run
14
+ # works without vds-cli installed (preview mode).
15
+
16
+ FEATURE=""
17
+ QUERY=""
18
+ DAYS=180
19
+ LIMIT=30
20
+ DRY_RUN=false
21
+
22
+ while [[ $# -gt 0 ]]; do
23
+ case "$1" in
24
+ --feature) FEATURE="$2"; shift 2 ;;
25
+ --query) QUERY="$2"; shift 2 ;;
26
+ --days) DAYS="$2"; shift 2 ;;
27
+ --limit) LIMIT="$2"; shift 2 ;;
28
+ --dry-run) DRY_RUN=true; shift ;;
29
+ *) echo "Unknown arg: $1" >&2; exit 2 ;;
30
+ esac
31
+ done
32
+
33
+ if [[ -z "$FEATURE" ]]; then
34
+ echo "ERROR: --feature is required" >&2
35
+ exit 2
36
+ fi
37
+ if [[ -z "$QUERY" ]]; then
38
+ echo "ERROR: --query (search keywords) is required" >&2
39
+ exit 2
40
+ fi
41
+
42
+ # Determine output dir
43
+ OUT_DIR=".vsaf/docs/features/$FEATURE"
44
+
45
+ OUT_JSON="$OUT_DIR/01-discovery-historical.json"
46
+ OUT_MD="$OUT_DIR/01-discovery-historical.md"
47
+
48
+ if [[ "$DRY_RUN" == "true" ]]; then
49
+ SPACE="${VDS_CONFLUENCE_SPACE_DEFAULT:-<VDS_CONFLUENCE_SPACE_DEFAULT>}"
50
+ PROJECT="${VDS_JIRA_PROJECT_DEFAULT:-<VDS_JIRA_PROJECT_DEFAULT>}"
51
+ CQL="space=$SPACE AND text ~ \"$QUERY\" AND lastmodified > now(\"-${DAYS}d\")"
52
+ JQL="project = $PROJECT AND text ~ \"$QUERY\" AND created > -${DAYS}d ORDER BY created DESC"
53
+ echo "DRY-RUN — would execute:"
54
+ echo " vds-cli confluence search --cql \"$CQL\" --limit $LIMIT --json-only"
55
+ echo " vds-cli jira search \"$JQL\" --limit $LIMIT --json-only"
56
+ echo " Write to: $OUT_JSON + $OUT_MD"
57
+ exit 0
58
+ fi
59
+
60
+ require_command vds-cli
61
+
62
+ # Ensure credentials
63
+ ensure_env VDS_CONFLUENCE_TOKEN "Enter VDS Confluence personal access token"
64
+ ensure_env VDS_JIRA_TOKEN "Enter VDS Jira personal access token"
65
+ ensure_env VDS_CONFLUENCE_SPACE_DEFAULT "Enter default Confluence space key (e.g. ENG)" false
66
+ ensure_env VDS_JIRA_PROJECT_DEFAULT "Enter default Jira project key (e.g. NTTC)" false
67
+
68
+ # Build CQL + JQL (now using real env vars)
69
+ CQL="space=$VDS_CONFLUENCE_SPACE_DEFAULT AND text ~ \"$QUERY\" AND lastmodified > now(\"-${DAYS}d\")"
70
+ JQL="project = $VDS_JIRA_PROJECT_DEFAULT AND text ~ \"$QUERY\" AND created > -${DAYS}d ORDER BY created DESC"
71
+
72
+ # Create output dir now (after dry-run exits early)
73
+ mkdir -p "$OUT_DIR"
74
+
75
+ # Execute searches
76
+ echo "Searching Confluence (limit $LIMIT, last ${DAYS}d)..."
77
+ CONF_RESULTS=$(vds-cli confluence search --cql "$CQL" --limit "$LIMIT" --json-only 2>/dev/null || echo '{"results":[]}')
78
+
79
+ echo "Searching Jira (limit $LIMIT, last ${DAYS}d)..."
80
+ JIRA_RESULTS=$(vds-cli jira search "$JQL" --limit "$LIMIT" --json-only 2>/dev/null || echo '{"issues":[]}')
81
+
82
+ # Combined JSON output + Markdown summary
83
+ python3 - "$CONF_RESULTS" "$JIRA_RESULTS" "$OUT_JSON" "$OUT_MD" "$QUERY" "$DAYS" <<'PYEOF'
84
+ import json, sys
85
+ conf_raw, jira_raw, out_json, out_md, query, days = sys.argv[1:7]
86
+ conf = json.loads(conf_raw) if conf_raw.strip() else {"results":[]}
87
+ jira = json.loads(jira_raw) if jira_raw.strip() else {"issues":[]}
88
+
89
+ combined = {
90
+ "query": query,
91
+ "days_back": int(days),
92
+ "confluence": conf.get("results", []),
93
+ "jira": jira.get("issues", []),
94
+ }
95
+ with open(out_json, "w") as f:
96
+ json.dump(combined, f, indent=2, ensure_ascii=False)
97
+
98
+ lines = [
99
+ f"# Discovery Historical Context — {query}",
100
+ f"",
101
+ f"> Generated by /vds-skill-search-confluence — searched last {days} days",
102
+ f"",
103
+ f"## Confluence pages ({len(combined['confluence'])} results)",
104
+ f"",
105
+ ]
106
+ for page in combined["confluence"][:10]:
107
+ title = page.get("title", "<no title>")
108
+ url = page.get("url", page.get("_links", {}).get("webui", ""))
109
+ modified = page.get("lastModified", page.get("version", {}).get("when", "?"))
110
+ lines.append(f"- [{title}]({url}) — modified {modified}")
111
+
112
+ lines += [
113
+ f"",
114
+ f"## Jira tickets ({len(combined['jira'])} results)",
115
+ f"",
116
+ ]
117
+ for issue in combined["jira"][:15]:
118
+ key = issue.get("key", "?")
119
+ summary = issue.get("fields", {}).get("summary", "<no summary>")
120
+ status = issue.get("fields", {}).get("status", {}).get("name", "?")
121
+ lines.append(f"- **{key}** — {summary} ({status})")
122
+
123
+ with open(out_md, "w") as f:
124
+ f.write("\n".join(lines) + "\n")
125
+
126
+ print(f"Wrote: {out_json}")
127
+ print(f"Wrote: {out_md}")
128
+ PYEOF
@@ -0,0 +1,6 @@
1
+ {
2
+ "source": "./.agent/skills/vds-scripts-skill",
3
+ "sourceType": "local",
4
+ "localPath": "/Users/vds-ai/Library/Mobile Documents/com~apple~CloudDocs/project/vds/.agent/skills/vds-scripts-skill",
5
+ "installedAt": "2026-01-26T07:07:42.597Z"
6
+ }