@ngocsangairvds/vsaf 3.1.26 → 3.1.27

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@ngocsangairvds/vsaf",
3
- "version": "3.1.26",
4
- "description": "fix confluence format",
3
+ "version": "3.1.27",
4
+ "description": "improve confluence format",
5
5
  "keywords": ["claude", "claude-code", "ai", "sdlc", "framework", "bmad", "gitnexus", "superpowers"],
6
6
  "bin": {
7
7
  "vsaf": "./bin/vsaf.js"
@@ -9,7 +9,7 @@ description: Push PRD document to Confluence. Use after /vsaf-doc-prd or /vsaf-d
9
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
10
 
11
11
  ## Input
12
- - `[file]` — optional path to a specific markdown file (defaults to scanning `.vsaf/docs/planning-artifacts/prd-*.md`)
12
+ - `[file]` — optional path to a specific markdown file or directory (defaults to scanning `.vsaf/docs/planning-artifacts/prd-*.md`)
13
13
  - `[comment]` — optional version note (e.g. "Initial draft", "Updated scope after stakeholder review")
14
14
 
15
15
  ## Prerequisites
@@ -18,11 +18,12 @@ Publish the current PRD to Confluence — create a new page if it doesn't exist,
18
18
 
19
19
  ## Steps
20
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
21
+ ### Step 1 — Find PRD file(s)
22
+ - If a **directory path** was given (e.g. `@docs/architecture/`): read all `*.md` files in that directory and combine them into a single page body, separated by `<hr/>`. Use the directory name as the page title base.
23
+ - If a **specific file path** was given: use that file directly.
24
+ - Otherwise: list files in `.vsaf/docs/planning-artifacts/` matching `prd-*.md`
25
+ - If multiple found: ask user which one to push
26
+ - If none found: STOP — tell user to run `/vsaf-doc-prd` first
26
27
  - Read full file content
27
28
 
28
29
  ### Step 2 — Load Confluence config
@@ -35,16 +36,23 @@ If either field is missing or empty:
35
36
  - Ask user: "Enter parent page title in Confluence (blank = root of space):"
36
37
  - Save answers back to config so the user is not asked again
37
38
 
38
- ### Step 3 — Determine page title
39
+ ### Step 3 — Determine page title and parent page ID
39
40
  - If file is from `prd-*.md`: title = `[PRD] {Feature Name}` (derive from filename: `prd-user-management.md` → `[PRD] User Management`)
41
+ - If input was a directory: title = the numbered section label provided by the user (e.g. `3.4.2.12 VRoute — Architecture Design`)
40
42
  - Otherwise: use the first `# Heading` in the file as the page title, or the filename stem if no heading found
41
43
 
42
- ### Step 4 — Convert markdown → Confluence storage format (HTML)
44
+ **Resolve parent page ID:**
45
+ Use `confluence_search_pages` to search for the parent page title in the configured space. Extract the `id` field from the result — this is the `parentId` needed for page creation. Do NOT skip this step.
43
46
 
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.**
47
+ ### Step 4 Convert markdown Confluence storage format
46
48
 
47
- Convert the full file content to Confluence storage format using these rules:
49
+ **CRITICAL RULES read before writing a single tag:**
50
+
51
+ 1. **Never send raw markdown.** All content must be converted to Confluence Storage Format (XHTML).
52
+ 2. **Never wrap the entire body in `<![CDATA[...]]>`.** CDATA is ONLY valid inside `<ac:plain-text-body>` within code macros.
53
+ 3. **All Confluence macro attributes MUST use the `ac:` namespace prefix.** The tag is `<ac:parameter ac:name="...">`, never `<parameter name="...">`. Wrong namespace = XML parse error.
54
+ 4. **Escape HTML entities in regular text:** `&` → `&amp;`, `<` → `&lt;`, `>` → `&gt;`.
55
+ 5. **Every opened tag must be closed.** Confluence's XML parser is strict — an unclosed tag aborts the entire page push.
48
56
 
49
57
  **Block elements:**
50
58
  ```
@@ -72,19 +80,20 @@ paragraph text → <p>paragraph text</p>
72
80
 
73
81
  **Tables:**
74
82
  ```
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>
83
+ | H1 | H2 | → <table><tbody>
84
+ |----|----|<tr><th>H1</th><th>H2</th></tr>
85
+ | a | b | <tr><td>a</td><td>b</td></tr>
86
+ </tbody></table>
79
87
  ```
80
88
 
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
- ````
89
+ **Code blocks — the ONLY correct place for CDATA:**
90
+ ```
91
+ ```lang → <ac:structured-macro ac:name="code">
92
+ code here <ac:parameter ac:name="language">lang</ac:parameter>
93
+ ``` <ac:plain-text-body><![CDATA[code here]]></ac:plain-text-body>
94
+ </ac:structured-macro>
95
+ ```
96
+ ⚠️ The attribute is `ac:name` not `name`. Using `<ac:parameter name="language">` will cause an XML parse error.
88
97
 
89
98
  **Info/warning/tip boxes:**
90
99
  ```
@@ -102,33 +111,27 @@ code here <ac:parameter ac:name="language">lang</ac:parameter>
102
111
  > text → <blockquote><p>text</p></blockquote>
103
112
  ```
104
113
 
105
- When calling the Confluence MCP create/update tool, set the content representation to **`storage`**.
106
-
107
114
  ### 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
- ```
115
+ Use `confluence_search_pages` to search for a page with that exact title in the configured space.
112
116
  - **Found** → proceed to Step 6 (update)
113
117
  - **Not found** → proceed to Step 7 (create)
114
118
 
115
119
  ### 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
+ Call `confluence_update_page` with:
121
+ - `pageId`: the ID from the search result
122
+ - `bodyStorageValue`: the **storage format HTML** converted in Step 4
120
123
  - Version comment: the `[comment]` argument (or "Updated via vsaf-push-prd" if blank)
121
124
 
122
125
  Confirm the two-stage update prompt if the MCP requires it.
123
126
 
124
127
  ### 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)
128
+ Call `confluence_create_page` with:
129
+ - `spaceKey`: the space key from config
130
+ - `parentId`: the parent page ID resolved in Step 3
131
+ - `title`: the page title determined in Step 3
132
+ - `bodyStorageValue`: the **storage format HTML** converted in Step 4
133
+
134
+ > The `bodyStorageValue` parameter takes Confluence Storage Format (XHTML) directly do not add a separate `representation` field.
132
135
 
133
136
  ### Step 8 — Output to user
134
137
  ```
@@ -148,4 +151,4 @@ Run /vsaf-push-srs to also publish the SRS, or continue with /vsaf-build
148
151
  - Never push a PRD that has failed `/vsaf-validate-prd` validation
149
152
  - If the Confluence MCP is not configured, tell the user to run `vsaf init` and enter their token
150
153
  - Do not modify the local file during this step
151
- - The file argument accepts any markdown file path, not just files under `.vsaf/`
154
+ - The file argument accepts any markdown file path or directory, not just files under `.vsaf/`
@@ -9,7 +9,7 @@ description: Push SRS document to Confluence. Use after /vsaf-doc-srs or SRS edi
9
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
10
 
11
11
  ## Input
12
- - `[file]` — optional path to a specific markdown file (defaults to scanning `.vsaf/docs/srs/*.md`)
12
+ - `[file]` — optional path to a specific markdown file or directory (defaults to scanning `.vsaf/docs/srs/*.md`)
13
13
  - `[comment]` — optional version note (e.g. "Initial draft", "Revised after tech review")
14
14
 
15
15
  ## Prerequisites
@@ -18,11 +18,12 @@ Publish the current SRS to Confluence — create a new page if it doesn't exist,
18
18
 
19
19
  ## Steps
20
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
21
+ ### Step 1 — Find SRS file(s)
22
+ - If a **directory path** was given (e.g. `@docs/srs/`): read all `*.md` files in that directory (excluding `*-results.md`) and combine them into a single page body, separated by `<hr/>`. Use the directory name as the page title base.
23
+ - If a **specific file path** was given: use that file directly.
24
+ - Otherwise: list files in `.vsaf/docs/srs/` matching `*.md` (exclude `*-results.md`)
25
+ - If multiple found: ask user which one to push
26
+ - If none found: STOP — tell user to run `/vsaf-doc-srs` first
26
27
  - Read full file content
27
28
 
28
29
  ### Step 2 — Load Confluence config
@@ -35,16 +36,23 @@ If either field is missing or empty:
35
36
  - Ask user: "Enter parent page title in Confluence (blank = root of space):"
36
37
  - Save answers back to config so the user is not asked again
37
38
 
38
- ### Step 3 — Determine page title
39
+ ### Step 3 — Determine page title and parent page ID
39
40
  - If file is from `srs-*.md` or `SRS-*.md`: title = `[SRS] {Feature Name}` (derive from filename)
41
+ - If input was a directory: title = the numbered section label provided by the user
40
42
  - Otherwise: use the first `# Heading` in the file as the page title, or the filename stem if no heading found
41
43
 
42
- ### Step 4 — Convert markdown → Confluence storage format (HTML)
44
+ **Resolve parent page ID:**
45
+ Use `confluence_search_pages` to search for the parent page title in the configured space. Extract the `id` field from the result — this is the `parentId` needed for page creation. Do NOT skip this step.
43
46
 
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.**
47
+ ### Step 4 Convert markdown Confluence storage format
46
48
 
47
- Convert the full file content to Confluence storage format using these rules:
49
+ **CRITICAL RULES read before writing a single tag:**
50
+
51
+ 1. **Never send raw markdown.** All content must be converted to Confluence Storage Format (XHTML).
52
+ 2. **Never wrap the entire body in `<![CDATA[...]]>`.** CDATA is ONLY valid inside `<ac:plain-text-body>` within code macros.
53
+ 3. **All Confluence macro attributes MUST use the `ac:` namespace prefix.** The tag is `<ac:parameter ac:name="...">`, never `<parameter name="...">`. Wrong namespace = XML parse error.
54
+ 4. **Escape HTML entities in regular text:** `&` → `&amp;`, `<` → `&lt;`, `>` → `&gt;`.
55
+ 5. **Every opened tag must be closed.** Confluence's XML parser is strict — an unclosed tag aborts the entire page push.
48
56
 
49
57
  **Block elements:**
50
58
  ```
@@ -72,19 +80,20 @@ paragraph text → <p>paragraph text</p>
72
80
 
73
81
  **Tables:**
74
82
  ```
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>
83
+ | H1 | H2 | → <table><tbody>
84
+ |----|----|<tr><th>H1</th><th>H2</th></tr>
85
+ | a | b | <tr><td>a</td><td>b</td></tr>
86
+ </tbody></table>
79
87
  ```
80
88
 
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
- ````
89
+ **Code blocks — the ONLY correct place for CDATA:**
90
+ ```
91
+ ```lang → <ac:structured-macro ac:name="code">
92
+ code here <ac:parameter ac:name="language">lang</ac:parameter>
93
+ ``` <ac:plain-text-body><![CDATA[code here]]></ac:plain-text-body>
94
+ </ac:structured-macro>
95
+ ```
96
+ ⚠️ The attribute is `ac:name` not `name`. Using `<ac:parameter name="language">` will cause an XML parse error.
88
97
 
89
98
  **Info/warning/tip boxes:**
90
99
  ```
@@ -102,37 +111,31 @@ code here <ac:parameter ac:name="language">lang</ac:parameter>
102
111
  > text → <blockquote><p>text</p></blockquote>
103
112
  ```
104
113
 
105
- When calling the Confluence MCP create/update tool, set the content representation to **`storage`**.
106
-
107
114
  ### 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
- ```
115
+ Use `confluence_search_pages` to search for a page with that exact title in the configured space.
112
116
  - **Found** → proceed to Step 6 (update)
113
117
  - **Not found** → proceed to Step 7 (create)
114
118
 
115
119
  ### 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
+ Call `confluence_update_page` with:
121
+ - `pageId`: the ID from the search result
122
+ - `bodyStorageValue`: the **storage format HTML** converted in Step 4
120
123
  - Version comment: the `[comment]` argument (or "Updated via vsaf-push-srs" if blank)
121
124
 
122
125
  Confirm the two-stage update prompt if the MCP requires it.
123
126
 
124
127
  ### 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)
128
+ Call `confluence_create_page` with:
129
+ - `spaceKey`: the space key from config
130
+ - `parentId`: the parent page ID resolved in Step 3
131
+ - `title`: the page title determined in Step 3
132
+ - `bodyStorageValue`: the **storage format HTML** converted in Step 4
133
+
134
+ > The `bodyStorageValue` parameter takes Confluence Storage Format (XHTML) directly do not add a separate `representation` field.
132
135
 
133
136
  ### Step 8 — Link SRS page to PRD page (if PRD page exists)
134
137
  - 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:
138
+ - If found: prepend this block at the top of the SRS content before pushing:
136
139
  ```xml
137
140
  <ac:structured-macro ac:name="info">
138
141
  <ac:parameter ac:name="title">Related Documents</ac:parameter>
@@ -160,4 +163,4 @@ Run /vsaf-test to generate testcases from this SRS, or /vsaf-build to implement
160
163
  - If the Confluence MCP is not configured, tell the user to run `vsaf init` and enter their token
161
164
  - Do not modify the local file during this step
162
165
  - 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/`
166
+ - The file argument accepts any markdown file path or directory, not just files under `.vsaf/`