@ngocsangairvds/vsaf 3.1.24 → 3.1.26

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.
@@ -66,11 +66,13 @@ npx vsaf init
66
66
  - Use `/vsaf-onboard` for the structured onboarding sequence.
67
67
  - Do not modify code on day one.
68
68
 
69
- ### Step 2: Planning + Requirement -> PRD -> SRS
69
+ ### Step 2: Planning + Requirement -> PRD -> SRS -> Confluence
70
70
  ```
71
71
  /vsaf-plan <requirement> # scope + impact + approach
72
72
  /vsaf-doc-prd # write PRD from approved scope
73
+ /vsaf-push-prd <comment> # publish PRD to Confluence
73
74
  /vsaf-doc-srs # write SRS from PRD
75
+ /vsaf-push-srs <comment> # publish SRS to Confluence
74
76
  ```
75
77
  Quick Flow (bug fix) may use a mini-SRS, but impact analysis is still mandatory.
76
78
 
@@ -118,7 +120,9 @@ PR description must include: impact summary (from GitNexus), test results.
118
120
  | VSAF onboard | `/vsaf-onboard` |
119
121
  | VSAF plan | `/vsaf-plan <requirement>` |
120
122
  | VSAF doc PRD | `/vsaf-doc-prd` |
123
+ | VSAF push PRD → Confluence | `/vsaf-push-prd <comment>` |
121
124
  | VSAF doc SRS | `/vsaf-doc-srs` |
125
+ | VSAF push SRS → Confluence | `/vsaf-push-srs <comment>` |
122
126
  | VSAF testcase | `/vsaf-test <path/to/srs>` |
123
127
  | VSAF implement | `/vsaf-build <srs> <testcases>` |
124
128
  | VSAF ship | `/vsaf-ship` |
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@ngocsangairvds/vsaf",
3
- "version": "3.1.24",
4
- "description": "fix gitnexus skip git",
3
+ "version": "3.1.26",
4
+ "description": "fix confluence format",
5
5
  "keywords": ["claude", "claude-code", "ai", "sdlc", "framework", "bmad", "gitnexus", "superpowers"],
6
6
  "bin": {
7
7
  "vsaf": "./bin/vsaf.js"
package/src/global.js CHANGED
@@ -1,6 +1,7 @@
1
1
  'use strict';
2
2
  const path = require('path');
3
3
  const fs = require('fs');
4
+ const readline = require('readline');
4
5
  const { ok, info, warn, step, hasCommand, exec, copyDir, copyFile, CLAUDE_HOME, CODEX_HOME } = require('./utils');
5
6
 
6
7
  const PKG_ROOT = path.join(__dirname, '..');
@@ -16,6 +17,7 @@ async function installGlobal() {
16
17
  installSkills();
17
18
  installBinary('gitnexus', () => exec('npm install -g gitnexus@1.6.4-rc.79'));
18
19
  setupGitnexusMcp();
20
+ await setupConfluenceMcp();
19
21
 
20
22
  console.log('\n\x1b[32m\x1b[1m✓ Global infra ready.\x1b[0m\n');
21
23
  }
@@ -69,4 +71,95 @@ function setupGitnexusMcp() {
69
71
  : warn('MCP setup failed — run manually: claude mcp add gitnexus -- npx -y gitnexus@1.6.4-rc.79 mcp');
70
72
  }
71
73
 
74
+ async function setupConfluenceMcp() {
75
+ step('Confluence MCP');
76
+
77
+ if (!hasCommand('claude')) {
78
+ info('Claude Code CLI not found — skip Confluence MCP setup');
79
+ return;
80
+ }
81
+
82
+ const url = await promptInput(
83
+ ' Confluence URL (blank to skip, e.g. http://confluence.company.com): '
84
+ );
85
+
86
+ if (!url) {
87
+ warn('Confluence MCP skipped — run manually later:\n' +
88
+ ' claude mcp add confluence -e CONF_MODE=server \\\n' +
89
+ ' -e CONF_BASE_URL=<your-url> \\\n' +
90
+ ' -e CONF_AUTH_MODE=bearer \\\n' +
91
+ ' -e CONF_TOKEN=<your-token> \\\n' +
92
+ ' -- npx -y confluence-mcp-server');
93
+ return;
94
+ }
95
+
96
+ const token = await promptSecret(
97
+ ' Confluence Personal Access Token: '
98
+ );
99
+
100
+ if (!token) {
101
+ warn('Confluence MCP skipped — token is required');
102
+ return;
103
+ }
104
+
105
+ const cmd = [
106
+ 'claude mcp add confluence',
107
+ '-e CONF_MODE=server',
108
+ `-e CONF_BASE_URL=${url}`,
109
+ '-e CONF_AUTH_MODE=bearer',
110
+ `-e CONF_TOKEN=${token}`,
111
+ '-- npx -y confluence-mcp-server',
112
+ ].join(' ');
113
+
114
+ exec(cmd)
115
+ ? ok('Confluence MCP configured')
116
+ : warn('Confluence MCP setup failed — run manually (see above)');
117
+ }
118
+
119
+ function promptInput(question) {
120
+ return new Promise((resolve) => {
121
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
122
+ rl.question(question, (answer) => { rl.close(); resolve(answer.trim()); });
123
+ });
124
+ }
125
+
126
+ function promptSecret(question) {
127
+ return new Promise((resolve) => {
128
+ // Non-TTY (CI, pipe): fall back to plain readline without hiding
129
+ if (!process.stdin.isTTY) {
130
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
131
+ rl.question(question, (answer) => { rl.close(); resolve(answer.trim()); });
132
+ return;
133
+ }
134
+
135
+ process.stdout.write(question);
136
+ process.stdin.setRawMode(true);
137
+ process.stdin.resume();
138
+
139
+ let input = '';
140
+ const onData = (buf) => {
141
+ const ch = buf.toString('utf8');
142
+ if (ch === '\r' || ch === '\n' || ch === '\x00') {
143
+ // Enter — done
144
+ process.stdin.setRawMode(false);
145
+ process.stdin.pause();
146
+ process.stdout.write('\n');
147
+ process.stdin.removeListener('data', onData);
148
+ resolve(input.trim());
149
+ } else if (ch === '\x03') {
150
+ // Ctrl+C
151
+ process.stdout.write('\n');
152
+ process.exit(0);
153
+ } else if (ch === '\x7f' || ch === '\b') {
154
+ // Backspace
155
+ input = input.slice(0, -1);
156
+ } else {
157
+ input += ch;
158
+ }
159
+ };
160
+
161
+ process.stdin.on('data', onData);
162
+ });
163
+ }
164
+
72
165
  module.exports = { installGlobal };
@@ -14,3 +14,7 @@ user_name: DuongOT
14
14
  communication_language: English
15
15
  document_output_language: English
16
16
  output_folder: "{project-root}/_bmad-output"
17
+
18
+ # Confluence Integration (set by /vsaf-push-prd or /vsaf-push-srs on first use)
19
+ confluence_space_key: ""
20
+ confluence_parent_page: ""
@@ -48,7 +48,8 @@ Must have run `/vsaf-plan` and have its output before running this skill.
48
48
  - Validation: [PASS / FAIL — details if fail]
49
49
 
50
50
  ## Next step
51
- Run /vsaf-doc-srs to write SRS from this PRD
51
+ - Run /vsaf-push-prd <comment> to publish this PRD to Confluence
52
+ - Or run /vsaf-doc-srs to write SRS from this PRD
52
53
  ```
53
54
 
54
55
  ## Notes
@@ -159,7 +159,8 @@ Save the SRS document to: `.vsaf/docs/srs/SRS-[project-name]-[FR-ID]-[feature-na
159
159
  - [SRS sections still containing {{...}} that need additional input]
160
160
 
161
161
  ## Next step
162
- Run /vsaf-test .vsaf/docs/srs/SRS-[...].md to generate testcases
162
+ - Run /vsaf-push-srs <comment> to publish this SRS to Confluence
163
+ - Or run /vsaf-test .vsaf/docs/srs/SRS-[...].md to generate testcases
163
164
  ```
164
165
 
165
166
  ## Notes
@@ -0,0 +1,151 @@
1
+ ---
2
+ name: vsaf-push-prd
3
+ description: Push PRD document to Confluence. Use after /vsaf-doc-prd or /vsaf-doc-prd (edit) to publish or update the PRD page on Confluence. Accepts an optional comment as version note.
4
+ ---
5
+
6
+ # VSAF Push PRD
7
+
8
+ ## Objective
9
+ Publish the current PRD to Confluence — create a new page if it doesn't exist, or update the existing one. The comment becomes the Confluence version note.
10
+
11
+ ## Input
12
+ - `[file]` — optional path to a specific markdown file (defaults to scanning `.vsaf/docs/planning-artifacts/prd-*.md`)
13
+ - `[comment]` — optional version note (e.g. "Initial draft", "Updated scope after stakeholder review")
14
+
15
+ ## Prerequisites
16
+ - Confluence MCP must be configured (`vsaf init` sets this up)
17
+ - A PRD markdown file must exist
18
+
19
+ ## Steps
20
+
21
+ ### Step 1 — Find PRD file
22
+ - If a file path was given as argument, use that file directly
23
+ - Otherwise list files in `.vsaf/docs/planning-artifacts/` matching `prd-*.md`
24
+ - If multiple found: ask user which one to push
25
+ - If none found: STOP — tell user to run `/vsaf-doc-prd` first
26
+ - Read full file content
27
+
28
+ ### Step 2 — Load Confluence config
29
+ Read `.vsaf/_bmad/bmm/config.yaml` and extract:
30
+ - `confluence_space_key` — Confluence space (e.g. `PROJ`)
31
+ - `confluence_parent_page` — parent page title (e.g. `Project Documentation`)
32
+
33
+ If either field is missing or empty:
34
+ - Ask user: "Enter Confluence space key (e.g. PROJ):"
35
+ - Ask user: "Enter parent page title in Confluence (blank = root of space):"
36
+ - Save answers back to config so the user is not asked again
37
+
38
+ ### Step 3 — Determine page title
39
+ - If file is from `prd-*.md`: title = `[PRD] {Feature Name}` (derive from filename: `prd-user-management.md` → `[PRD] User Management`)
40
+ - Otherwise: use the first `# Heading` in the file as the page title, or the filename stem if no heading found
41
+
42
+ ### Step 4 — Convert markdown → Confluence storage format (HTML)
43
+
44
+ **CRITICAL: Never send raw markdown or CDATA-wrapped content to Confluence.**
45
+ **CRITICAL: Do NOT wrap the entire page body in `<![CDATA[...]]>`. CDATA is only valid inside `<ac:plain-text-body>` for code blocks.**
46
+
47
+ Convert the full file content to Confluence storage format using these rules:
48
+
49
+ **Block elements:**
50
+ ```
51
+ # Heading → <h1>Heading</h1>
52
+ ## Heading → <h2>Heading</h2>
53
+ ### Heading → <h3>Heading</h3>
54
+ #### Heading → <h4>Heading</h4>
55
+ paragraph text → <p>paragraph text</p>
56
+ --- → <hr/>
57
+ ```
58
+
59
+ **Inline elements:**
60
+ ```
61
+ **bold** → <strong>bold</strong>
62
+ *italic* → <em>italic</em>
63
+ `inline code` → <code>inline code</code>
64
+ [text](url) → <a href="url">text</a>
65
+ ```
66
+
67
+ **Lists:**
68
+ ```
69
+ - item → <ul><li>item</li></ul>
70
+ 1. item → <ol><li>item</li></ol>
71
+ ```
72
+
73
+ **Tables:**
74
+ ```
75
+ | H1 | H2 | → <table><tbody>
76
+ |----|----| <tr><th>H1</th><th>H2</th></tr>
77
+ | a | b | <tr><td>a</td><td>b</td></tr>
78
+ </tbody></table>
79
+ ```
80
+
81
+ **Code blocks — the ONLY place CDATA is correct:**
82
+ ````
83
+ ```lang → <ac:structured-macro ac:name="code">
84
+ code here <ac:parameter ac:name="language">lang</ac:parameter>
85
+ ``` <ac:plain-text-body><![CDATA[code here]]></ac:plain-text-body>
86
+ </ac:structured-macro>
87
+ ````
88
+
89
+ **Info/warning/tip boxes:**
90
+ ```
91
+ > [!NOTE] title → <ac:structured-macro ac:name="info">
92
+ > body <ac:parameter ac:name="title">title</ac:parameter>
93
+ <ac:rich-text-body><p>body</p></ac:rich-text-body>
94
+ </ac:structured-macro>
95
+
96
+ > [!WARNING] → ac:name="warning"
97
+ > [!TIP] → ac:name="tip"
98
+ ```
99
+
100
+ **Blockquote:**
101
+ ```
102
+ > text → <blockquote><p>text</p></blockquote>
103
+ ```
104
+
105
+ When calling the Confluence MCP create/update tool, set the content representation to **`storage`**.
106
+
107
+ ### Step 5 — Check if page exists
108
+ Use the `confluence` MCP to search for a page with that exact title in the configured space:
109
+ ```
110
+ CQL: title = "{page_title}" AND space = "{space_key}"
111
+ ```
112
+ - **Found** → proceed to Step 6 (update)
113
+ - **Not found** → proceed to Step 7 (create)
114
+
115
+ ### Step 6 — Update existing page
116
+ Call the `confluence` MCP update tool with:
117
+ - Page ID from search result
118
+ - Content: the **converted wiki markup** from Step 4
119
+ - Representation: `wiki`
120
+ - Version comment: the `[comment]` argument (or "Updated via vsaf-push-prd" if blank)
121
+
122
+ Confirm the two-stage update prompt if the MCP requires it.
123
+
124
+ ### Step 7 — Create new page
125
+ Call the `confluence` MCP create tool with:
126
+ - Space key from config
127
+ - Parent page title from config (find its ID first if needed)
128
+ - Title: determined in Step 3
129
+ - Content: the **converted wiki markup** from Step 4
130
+ - Representation: `wiki`
131
+ - Version comment: the `[comment]` argument (or "Created via vsaf-push-prd" if blank)
132
+
133
+ ### Step 8 — Output to user
134
+ ```
135
+ ## PRD pushed to Confluence
136
+
137
+ - Page: {page_title}
138
+ - Action: [Created / Updated]
139
+ - Space: {space_key}
140
+ - Comment: {comment}
141
+ - URL: {confluence_page_url}
142
+
143
+ ## Next step
144
+ Run /vsaf-push-srs to also publish the SRS, or continue with /vsaf-build
145
+ ```
146
+
147
+ ## Notes
148
+ - Never push a PRD that has failed `/vsaf-validate-prd` validation
149
+ - If the Confluence MCP is not configured, tell the user to run `vsaf init` and enter their token
150
+ - Do not modify the local file during this step
151
+ - The file argument accepts any markdown file path, not just files under `.vsaf/`
@@ -0,0 +1,163 @@
1
+ ---
2
+ name: vsaf-push-srs
3
+ description: Push SRS document to Confluence. Use after /vsaf-doc-srs or SRS edits to publish or update the SRS page on Confluence. Accepts an optional comment as version note.
4
+ ---
5
+
6
+ # VSAF Push SRS
7
+
8
+ ## Objective
9
+ Publish the current SRS to Confluence — create a new page if it doesn't exist, or update the existing one. The comment becomes the Confluence version note.
10
+
11
+ ## Input
12
+ - `[file]` — optional path to a specific markdown file (defaults to scanning `.vsaf/docs/srs/*.md`)
13
+ - `[comment]` — optional version note (e.g. "Initial draft", "Revised after tech review")
14
+
15
+ ## Prerequisites
16
+ - Confluence MCP must be configured (`vsaf init` sets this up)
17
+ - An SRS markdown file must exist
18
+
19
+ ## Steps
20
+
21
+ ### Step 1 — Find SRS file
22
+ - If a file path was given as argument, use that file directly
23
+ - Otherwise list files in `.vsaf/docs/srs/` matching `*.md` (exclude `*-results.md`)
24
+ - If multiple found: ask user which one to push
25
+ - If none found: STOP — tell user to run `/vsaf-doc-srs` first
26
+ - Read full file content
27
+
28
+ ### Step 2 — Load Confluence config
29
+ Read `.vsaf/_bmad/bmm/config.yaml` and extract:
30
+ - `confluence_space_key` — Confluence space (e.g. `PROJ`)
31
+ - `confluence_parent_page` — parent page title (e.g. `Project Documentation`)
32
+
33
+ If either field is missing or empty:
34
+ - Ask user: "Enter Confluence space key (e.g. PROJ):"
35
+ - Ask user: "Enter parent page title in Confluence (blank = root of space):"
36
+ - Save answers back to config so the user is not asked again
37
+
38
+ ### Step 3 — Determine page title
39
+ - If file is from `srs-*.md` or `SRS-*.md`: title = `[SRS] {Feature Name}` (derive from filename)
40
+ - Otherwise: use the first `# Heading` in the file as the page title, or the filename stem if no heading found
41
+
42
+ ### Step 4 — Convert markdown → Confluence storage format (HTML)
43
+
44
+ **CRITICAL: Never send raw markdown or CDATA-wrapped content to Confluence.**
45
+ **CRITICAL: Do NOT wrap the entire page body in `<![CDATA[...]]>`. CDATA is only valid inside `<ac:plain-text-body>` for code blocks.**
46
+
47
+ Convert the full file content to Confluence storage format using these rules:
48
+
49
+ **Block elements:**
50
+ ```
51
+ # Heading → <h1>Heading</h1>
52
+ ## Heading → <h2>Heading</h2>
53
+ ### Heading → <h3>Heading</h3>
54
+ #### Heading → <h4>Heading</h4>
55
+ paragraph text → <p>paragraph text</p>
56
+ --- → <hr/>
57
+ ```
58
+
59
+ **Inline elements:**
60
+ ```
61
+ **bold** → <strong>bold</strong>
62
+ *italic* → <em>italic</em>
63
+ `inline code` → <code>inline code</code>
64
+ [text](url) → <a href="url">text</a>
65
+ ```
66
+
67
+ **Lists:**
68
+ ```
69
+ - item → <ul><li>item</li></ul>
70
+ 1. item → <ol><li>item</li></ol>
71
+ ```
72
+
73
+ **Tables:**
74
+ ```
75
+ | H1 | H2 | → <table><tbody>
76
+ |----|----| <tr><th>H1</th><th>H2</th></tr>
77
+ | a | b | <tr><td>a</td><td>b</td></tr>
78
+ </tbody></table>
79
+ ```
80
+
81
+ **Code blocks — the ONLY place CDATA is correct:**
82
+ ````
83
+ ```lang → <ac:structured-macro ac:name="code">
84
+ code here <ac:parameter ac:name="language">lang</ac:parameter>
85
+ ``` <ac:plain-text-body><![CDATA[code here]]></ac:plain-text-body>
86
+ </ac:structured-macro>
87
+ ````
88
+
89
+ **Info/warning/tip boxes:**
90
+ ```
91
+ > [!NOTE] title → <ac:structured-macro ac:name="info">
92
+ > body <ac:parameter ac:name="title">title</ac:parameter>
93
+ <ac:rich-text-body><p>body</p></ac:rich-text-body>
94
+ </ac:structured-macro>
95
+
96
+ > [!WARNING] → ac:name="warning"
97
+ > [!TIP] → ac:name="tip"
98
+ ```
99
+
100
+ **Blockquote:**
101
+ ```
102
+ > text → <blockquote><p>text</p></blockquote>
103
+ ```
104
+
105
+ When calling the Confluence MCP create/update tool, set the content representation to **`storage`**.
106
+
107
+ ### Step 5 — Check if page exists
108
+ Use the `confluence` MCP to search for a page with that exact title in the configured space:
109
+ ```
110
+ CQL: title = "{page_title}" AND space = "{space_key}"
111
+ ```
112
+ - **Found** → proceed to Step 6 (update)
113
+ - **Not found** → proceed to Step 7 (create)
114
+
115
+ ### Step 6 — Update existing page
116
+ Call the `confluence` MCP update tool with:
117
+ - Page ID from search result
118
+ - Content: the **converted wiki markup** from Step 4
119
+ - Representation: `wiki`
120
+ - Version comment: the `[comment]` argument (or "Updated via vsaf-push-srs" if blank)
121
+
122
+ Confirm the two-stage update prompt if the MCP requires it.
123
+
124
+ ### Step 7 — Create new page
125
+ Call the `confluence` MCP create tool with:
126
+ - Space key from config
127
+ - Parent page title from config (find its ID first if needed)
128
+ - Title: determined in Step 3
129
+ - Content: the **converted wiki markup** from Step 4
130
+ - Representation: `wiki`
131
+ - Version comment: the `[comment]` argument (or "Created via vsaf-push-srs" if blank)
132
+
133
+ ### Step 8 — Link SRS page to PRD page (if PRD page exists)
134
+ - Search Confluence for the corresponding `[PRD] {feature-name}` page
135
+ - If found: prepend this storage-format block at the top of the SRS content before pushing:
136
+ ```xml
137
+ <ac:structured-macro ac:name="info">
138
+ <ac:parameter ac:name="title">Related Documents</ac:parameter>
139
+ <ac:rich-text-body><p><a href="{prd_url}">[PRD] {feature-name}</a></p></ac:rich-text-body>
140
+ </ac:structured-macro>
141
+ ```
142
+ - This creates traceability in Confluence
143
+
144
+ ### Step 9 — Output to user
145
+ ```
146
+ ## SRS pushed to Confluence
147
+
148
+ - Page: {page_title}
149
+ - Action: [Created / Updated]
150
+ - Space: {space_key}
151
+ - Comment: {comment}
152
+ - URL: {confluence_page_url}
153
+ - PRD link: [linked / not found]
154
+
155
+ ## Next step
156
+ Run /vsaf-test to generate testcases from this SRS, or /vsaf-build to implement
157
+ ```
158
+
159
+ ## Notes
160
+ - If the Confluence MCP is not configured, tell the user to run `vsaf init` and enter their token
161
+ - Do not modify the local file during this step
162
+ - SRS result files (`*-results.md`) are test outputs — push those separately if needed
163
+ - The file argument accepts any markdown file path, not just files under `.vsaf/`