specweave 1.0.299 → 1.0.301

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 (49) hide show
  1. package/bin/specweave.js +25 -1
  2. package/dist/src/cli/commands/auto.js +1 -0
  3. package/dist/src/cli/commands/auto.js.map +1 -1
  4. package/dist/src/cli/commands/scan-plugins.d.ts +12 -0
  5. package/dist/src/cli/commands/scan-plugins.d.ts.map +1 -0
  6. package/dist/src/cli/commands/scan-plugins.js +80 -0
  7. package/dist/src/cli/commands/scan-plugins.js.map +1 -0
  8. package/dist/src/core/doctor/checkers/installation-health-checker.js +6 -6
  9. package/dist/src/core/doctor/checkers/installation-health-checker.js.map +1 -1
  10. package/dist/src/core/lazy-loading/llm-plugin-detector.d.ts +8 -27
  11. package/dist/src/core/lazy-loading/llm-plugin-detector.d.ts.map +1 -1
  12. package/dist/src/core/lazy-loading/llm-plugin-detector.js +12 -90
  13. package/dist/src/core/lazy-loading/llm-plugin-detector.js.map +1 -1
  14. package/dist/src/core/skill-security/index.d.ts +9 -0
  15. package/dist/src/core/skill-security/index.d.ts.map +1 -0
  16. package/dist/src/core/skill-security/index.js +5 -0
  17. package/dist/src/core/skill-security/index.js.map +1 -0
  18. package/dist/src/core/skill-security/parser.d.ts +27 -0
  19. package/dist/src/core/skill-security/parser.d.ts.map +1 -0
  20. package/dist/src/core/skill-security/parser.js +55 -0
  21. package/dist/src/core/skill-security/parser.js.map +1 -0
  22. package/dist/src/core/skill-security/reporter.d.ts +21 -0
  23. package/dist/src/core/skill-security/reporter.d.ts.map +1 -0
  24. package/dist/src/core/skill-security/reporter.js +121 -0
  25. package/dist/src/core/skill-security/reporter.js.map +1 -0
  26. package/dist/src/core/skill-security/rules.d.ts +25 -0
  27. package/dist/src/core/skill-security/rules.d.ts.map +1 -0
  28. package/dist/src/core/skill-security/rules.js +137 -0
  29. package/dist/src/core/skill-security/rules.js.map +1 -0
  30. package/dist/src/core/skill-security/scanner.d.ts +41 -0
  31. package/dist/src/core/skill-security/scanner.d.ts.map +1 -0
  32. package/dist/src/core/skill-security/scanner.js +78 -0
  33. package/dist/src/core/skill-security/scanner.js.map +1 -0
  34. package/package.json +1 -1
  35. package/plugins/specweave/hooks/lib/score-increment.sh +87 -0
  36. package/plugins/specweave/hooks/stop-auto-v5.sh +55 -9
  37. package/plugins/specweave/hooks/tests/test-auto-context-integration.sh +126 -0
  38. package/plugins/specweave/hooks/tests/test-stop-auto-enriched.sh +128 -0
  39. package/plugins/specweave/hooks/user-prompt-submit.sh +99 -150
  40. package/plugins/specweave/scripts/setup-auto.sh +58 -4
  41. package/plugins/specweave/scripts/tests/test-setup-auto-selection.sh +74 -0
  42. package/plugins/specweave/scripts/tests/test-setup-auto-usergoal.sh +83 -0
  43. package/plugins/specweave/skills/auto/SKILL.md +3 -1
  44. package/plugins/specweave/skills/do/SKILL.md +11 -0
  45. package/plugins/specweave/skills/increment/SKILL.md +8 -2
  46. package/plugins/specweave/skills/team-lead/SKILL.md +69 -5
  47. package/plugins/specweave-jira/skills/jira-mapper/SKILL.md +13 -14
  48. package/plugins/specweave-jira/skills/jira-resource-validator/SKILL.md +74 -4
  49. package/plugins/specweave-jira/skills/jira-sync/SKILL.md +18 -27
@@ -20,9 +20,15 @@ hooks:
20
20
 
21
21
  # Plan Product Increment
22
22
 
23
- ## CRITICAL: Plan Mode Required
23
+ ## CRITICAL: Plan Mode Required (BLOCKING)
24
24
 
25
- **Before executing this skill, you MUST be in plan mode.** If you are not currently in plan mode, call `EnterPlanMode` first. Increment planning is ALWAYS a planning activity never skip straight to implementation.
25
+ **You MUST be in plan mode before proceeding.** If not, call `EnterPlanMode` now and wait for confirmation before continuing to Step 0A.
26
+
27
+ 1. Call `EnterPlanMode` immediately
28
+ 2. Wait for plan mode confirmation
29
+ 3. Then proceed to Step 0A
30
+
31
+ Increment planning produces specs, plans, and task breakdowns that require user review. Do not skip plan mode or defer it — the user must approve the plan before any implementation begins.
26
32
 
27
33
  ## Project Overrides
28
34
 
@@ -27,7 +27,9 @@ description: Orchestrate multi-agent parallel development with domain-specialize
27
27
  | Action | Tool | Parameters |
28
28
  |--------|------|------------|
29
29
  | Create team | `TeamCreate` | `team_name`, `description` |
30
- | Spawn agent | `Task` | `team_name`, `name`, `subagent_type`, `prompt` |
30
+ | Spawn agent | `Task` | `team_name`, `name`, `subagent_type`, `prompt`, `mode` |
31
+ | Spawn agent (plan mode) | `Task` | `mode: "plan"` — agent must submit plan for team lead review |
32
+ | Approve/reject plan | `SendMessage` | `type: "plan_approval_response"`, `request_id`, `recipient`, `approve`, `content` |
31
33
  | Send message | `SendMessage` | `type`, `recipient`, `content`, `summary` |
32
34
  | Shutdown agent | `SendMessage` | `type: "shutdown_request"`, `recipient` |
33
35
 
@@ -191,6 +193,58 @@ Analyze domains
191
193
 
192
194
  ---
193
195
 
196
+ ## 3b. Plan Review Workflow
197
+
198
+ The team lead acts as **architectural reviewer** for all sub-agent plans. Do NOT auto-accept plans.
199
+
200
+ ### Why Review
201
+
202
+ Without review, agents may duplicate work across domains, misinterpret scope, make conflicting architectural decisions, or produce plans misaligned with the spec.
203
+
204
+ ### Protocol
205
+
206
+ **Spawn all agents with `mode: "plan"`.** This forces agents to call `ExitPlanMode` before implementing, which sends a `plan_approval_request` to the team lead.
207
+
208
+ When you receive a plan approval request:
209
+
210
+ 1. **Read the plan** — check the agent's spec.md, plan.md, and tasks.md
211
+ 2. **Evaluate**:
212
+ - Does it align with the feature spec and ACs?
213
+ - Is the architecture consistent with existing codebase patterns?
214
+ - Does the agent stay within its file ownership boundaries?
215
+ - Are there conflicts with other agents' plans?
216
+ - Is scope correct — not too broad, not too narrow?
217
+ 3. **Approve or reject**:
218
+
219
+ ```
220
+ // Approve
221
+ SendMessage({
222
+ type: "plan_approval_response",
223
+ request_id: "<from plan_approval_request>",
224
+ recipient: "database-agent",
225
+ approve: true
226
+ });
227
+
228
+ // Reject with feedback
229
+ SendMessage({
230
+ type: "plan_approval_response",
231
+ request_id: "<from plan_approval_request>",
232
+ recipient: "database-agent",
233
+ approve: false,
234
+ content: "Revise: 1) Add index on user_id for sessions. 2) Missing migration for AC-US1-03."
235
+ });
236
+ ```
237
+
238
+ ### Non-Blocking Review
239
+
240
+ Plan review MUST NOT block other agents. Review plans as they arrive — agents waiting for approval are idle, but other agents continue working normally.
241
+
242
+ ### Multi-Increment Consideration
243
+
244
+ For very large features, the team lead MAY split work into multiple increments per domain for better tracking and independent closure. Decide this during initial analysis (Step 1), before spawning agents.
245
+
246
+ ---
247
+
194
248
  ## 4. Agent Spawn Prompt Templates
195
249
 
196
250
  Each agent receives a detailed prompt that includes its skill invocations, file ownership, and workflow instructions.
@@ -528,11 +582,14 @@ TeamCreate({
528
582
 
529
583
  ### Step 2: Spawn Upstream Agents (Phase 1)
530
584
 
585
+ All agents are spawned with `mode: "plan"` so the team lead reviews their plans before implementation (see Section 3b).
586
+
531
587
  ```typescript
532
588
  Task({
533
589
  team_name: "feature-checkout",
534
590
  name: "database-agent",
535
591
  subagent_type: "general-purpose",
592
+ mode: "plan",
536
593
  prompt: `[DATABASE AGENT PROMPT - see template in Section 4c]`,
537
594
  });
538
595
 
@@ -540,6 +597,7 @@ Task({
540
597
  team_name: "feature-checkout",
541
598
  name: "shared-types-agent",
542
599
  subagent_type: "general-purpose",
600
+ mode: "plan",
543
601
  prompt: `[SHARED/TYPES AGENT PROMPT]`,
544
602
  });
545
603
  ```
@@ -555,6 +613,7 @@ Task({
555
613
  team_name: "feature-checkout",
556
614
  name: "backend-agent",
557
615
  subagent_type: "general-purpose",
616
+ mode: "plan",
558
617
  prompt: `[BACKEND AGENT PROMPT - see template in Section 4b]`,
559
618
  });
560
619
 
@@ -562,6 +621,7 @@ Task({
562
621
  team_name: "feature-checkout",
563
622
  name: "frontend-agent",
564
623
  subagent_type: "general-purpose",
624
+ mode: "plan",
565
625
  prompt: `[FRONTEND AGENT PROMPT - see template in Section 4a]`,
566
626
  });
567
627
 
@@ -569,6 +629,7 @@ Task({
569
629
  team_name: "feature-checkout",
570
630
  name: "testing-agent",
571
631
  subagent_type: "general-purpose",
632
+ mode: "plan",
572
633
  prompt: `[TESTING AGENT PROMPT - see template in Section 4d]`,
573
634
  });
574
635
  ```
@@ -625,12 +686,15 @@ Orchestrator Final Check:
625
686
  ```
626
687
  /sw:team-lead "Build checkout flow"
627
688
 
628
- ├── Step 1: Analyze feature -> identify domains
689
+ ├── Step 1: Analyze feature -> identify domains -> decide increment split
629
690
  ├── Step 2: Create team via TeamCreate
630
691
  ├── Step 3: Create per-domain increments
631
- ├── Step 4: Contract-first spawning
632
- │ ├── Phase 1: Spawn shared + database -> wait for CONTRACT_READY
633
- │ └── Phase 2: Spawn backend + frontend + testing (parallel)
692
+ ├── Step 4: Contract-first spawning (all agents with mode: "plan")
693
+ │ ├── Phase 1: Spawn shared + database
694
+ └── Review & approve each agent's plan (Section 3b)
695
+ │ │ └── Wait for CONTRACT_READY after approval
696
+ │ └── Phase 2: Spawn backend + frontend + testing
697
+ │ └── Review & approve each agent's plan
634
698
  ├── Step 5: Monitor progress via SendMessage
635
699
  ├── Step 6: Quality gates (each agent runs /sw:grill)
636
700
  └── Step 7: Merge and close (/sw:team-merge)
@@ -87,29 +87,28 @@ JIRA_DOMAIN="$(grep '^JIRA_DOMAIN=' .env | head -1 | cut -d '=' -f2-)"
87
87
  ### Domain Validation (before ANY API call)
88
88
 
89
89
  ```bash
90
- # Must be a valid hostname no special chars, no consecutive dots
91
- if [[ ! "$JIRA_DOMAIN" =~ ^[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?(\.[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?)*$ ]]; then
92
- echo "Error: JIRA_DOMAIN contains invalid characters"
90
+ # Reject IP addresses FIRSTIPv4, IPv6 brackets, hex-encoded (SSRF prevention)
91
+ if [[ "$JIRA_DOMAIN" =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+ ]] || [[ "$JIRA_DOMAIN" =~ ^\[.*\]$ ]] || [[ "$JIRA_DOMAIN" =~ ^0x ]]; then
92
+ echo "Error: IP addresses not allowed — use a hostname"
93
93
  exit 1
94
94
  fi
95
95
 
96
- # Cloud JIRA: must match <subdomain>.atlassian.net
97
- if [[ ! "$JIRA_DOMAIN" =~ ^[a-zA-Z0-9-]+\.atlassian\.net$ ]]; then
98
- echo "Error: Domain does not match <subdomain>.atlassian.net pattern"
99
- echo "Self-hosted JIRA requires explicit user confirmation"
96
+ # Reject localhost and private networks
97
+ if [[ "$JIRA_DOMAIN" =~ ^(localhost|127\.|10\.|172\.(1[6-9]|2[0-9]|3[01])\.|192\.168\.) ]]; then
98
+ echo "Error: Internal/localhost addresses not allowed"
100
99
  exit 1
101
- # Agent: use AskUserQuestion to confirm non-standard domain before retrying
102
100
  fi
103
101
 
104
- # Reject IP addresses (SSRF prevention)
105
- if [[ "$JIRA_DOMAIN" =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+ ]] || [[ "$JIRA_DOMAIN" =~ ^\[.*\]$ ]] || [[ "$JIRA_DOMAIN" =~ ^0x ]]; then
106
- echo "Error: IP addresses not allowed — use a hostname"
102
+ # Must be a valid hostname — no special chars, no consecutive dots
103
+ if [[ ! "$JIRA_DOMAIN" =~ ^[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?(\.[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?)*$ ]]; then
104
+ echo "Error: JIRA_DOMAIN contains invalid characters"
107
105
  exit 1
108
106
  fi
109
107
 
110
- # Reject localhost and private networks
111
- if [[ "$JIRA_DOMAIN" =~ ^(localhost|127\.|10\.|172\.(1[6-9]|2[0-9]|3[01])\.|192\.168\.) ]]; then
112
- echo "Error: Internal/localhost addresses not allowed"
108
+ # Cloud JIRA: must match <subdomain>.atlassian.net
109
+ # Agent: use AskUserQuestion to confirm non-standard domain before retrying
110
+ if [[ ! "$JIRA_DOMAIN" =~ ^[a-zA-Z0-9-]+\.atlassian\.net$ ]]; then
111
+ echo "Error: Domain does not match <subdomain>.atlassian.net pattern"
113
112
  exit 1
114
113
  fi
115
114
  ```
@@ -9,6 +9,76 @@ allowed-tools: Read, Bash, Write, Edit
9
9
 
10
10
  **Auto-Activation**: Triggers when Jira setup or validation is needed.
11
11
 
12
+ ## Security Rules (MANDATORY)
13
+
14
+ These rules apply to ALL JIRA API operations in this skill.
15
+
16
+ ### Credential Handling
17
+
18
+ 1. **Never collect credentials** — this skill reads from `.env` only, never prompts the user
19
+ 2. **Never log secrets** — never echo token values, auth headers, or base64 credentials
20
+ 3. **Never write credentials** — the user configures `.env` themselves (this skill may update non-secret keys like `JIRA_BOARDS` and `JIRA_PROJECT`)
21
+
22
+ ### Credential Loading
23
+
24
+ ```bash
25
+ # 1. Validate presence FIRST (before reading any values)
26
+ for KEY in JIRA_API_TOKEN JIRA_EMAIL JIRA_DOMAIN; do
27
+ if ! grep -qE "^${KEY}=.+" .env; then
28
+ echo "Error: ${KEY} missing or empty in .env"
29
+ exit 1
30
+ fi
31
+ done
32
+
33
+ # 2. Load credentials ONLY after validation passes (never display values)
34
+ # head -1 ensures only first match used if .env has duplicate keys
35
+ JIRA_API_TOKEN="$(grep '^JIRA_API_TOKEN=' .env | head -1 | cut -d '=' -f2-)"
36
+ JIRA_EMAIL="$(grep '^JIRA_EMAIL=' .env | head -1 | cut -d '=' -f2-)"
37
+ JIRA_DOMAIN="$(grep '^JIRA_DOMAIN=' .env | head -1 | cut -d '=' -f2-)"
38
+ ```
39
+
40
+ ### Domain Validation (before ANY API call)
41
+
42
+ ```bash
43
+ # Reject IP addresses first (SSRF prevention)
44
+ if [[ "$JIRA_DOMAIN" =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+ ]] || [[ "$JIRA_DOMAIN" =~ ^\[.*\]$ ]] || [[ "$JIRA_DOMAIN" =~ ^0x ]]; then
45
+ echo "Error: IP addresses not allowed — use a hostname"
46
+ exit 1
47
+ fi
48
+
49
+ # Reject localhost and private networks
50
+ if [[ "$JIRA_DOMAIN" =~ ^(localhost|127\.|10\.|172\.(1[6-9]|2[0-9]|3[01])\.|192\.168\.) ]]; then
51
+ echo "Error: Internal/localhost addresses not allowed"
52
+ exit 1
53
+ fi
54
+
55
+ # Must be a valid hostname — no special chars, no consecutive dots
56
+ if [[ ! "$JIRA_DOMAIN" =~ ^[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?(\.[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?)*$ ]]; then
57
+ echo "Error: JIRA_DOMAIN contains invalid characters"
58
+ exit 1
59
+ fi
60
+
61
+ # Cloud JIRA: must match <subdomain>.atlassian.net
62
+ if [[ ! "$JIRA_DOMAIN" =~ ^[a-zA-Z0-9-]+\.atlassian\.net$ ]]; then
63
+ echo "Error: Domain does not match <subdomain>.atlassian.net pattern"
64
+ exit 1
65
+ fi
66
+ ```
67
+
68
+ ### API Call Pattern (HTTPS only, quoted variables)
69
+
70
+ ```bash
71
+ AUTH="$(printf '%s:%s' "$JIRA_EMAIL" "$JIRA_API_TOKEN" | base64)"
72
+
73
+ # All API calls MUST use https://, double-quote all variables
74
+ curl -s -f \
75
+ -H "Authorization: Basic $AUTH" \
76
+ -H "Content-Type: application/json" \
77
+ "https://${JIRA_DOMAIN}/rest/api/3/..."
78
+ ```
79
+
80
+ ---
81
+
12
82
  ## What This Skill Does
13
83
 
14
84
  This skill ensures your Jira configuration in `.env` is valid and all resources exist. It's **smart enough** to:
@@ -31,10 +101,10 @@ This skill ensures your Jira configuration in `.env` is valid and all resources
31
101
 
32
102
  ### Required .env Variables
33
103
 
34
- ```bash
35
- JIRA_API_TOKEN=your_token_here
36
- JIRA_EMAIL=your_email@company.com
37
- JIRA_DOMAIN=yourcompany.atlassian.net
104
+ ```
105
+ JIRA_API_TOKEN=<your-token>
106
+ JIRA_EMAIL=<your-email>
107
+ JIRA_DOMAIN=<your-company>.atlassian.net
38
108
  JIRA_STRATEGY=board-based
39
109
  JIRA_PROJECT=PROJECTKEY
40
110
  JIRA_BOARDS=1,2,3 # IDs (if exist) OR names (if creating)
@@ -106,29 +106,28 @@ if [ -z "$JIRA_DOMAIN" ]; then
106
106
  exit 1
107
107
  fi
108
108
 
109
- # Must be a valid hostname each label: alphanumeric, hyphens allowed mid-label, no consecutive dots
110
- if [[ ! "$JIRA_DOMAIN" =~ ^[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?(\.[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?)*$ ]]; then
111
- echo "Error: JIRA_DOMAIN is not a valid hostname"
109
+ # Reject IP addresses FIRSTIPv4, IPv6 brackets, hex-encoded (SSRF prevention)
110
+ if [[ "$JIRA_DOMAIN" =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+ ]] || [[ "$JIRA_DOMAIN" =~ ^\[.*\]$ ]] || [[ "$JIRA_DOMAIN" =~ ^0x ]]; then
111
+ echo "Error: IP addresses not allowed — use a hostname"
112
112
  exit 1
113
113
  fi
114
114
 
115
- # Must end with .atlassian.net for cloud JIRA
116
- if [[ ! "$JIRA_DOMAIN" =~ ^[a-zA-Z0-9-]+\.atlassian\.net$ ]]; then
117
- echo "Error: Domain does not match <subdomain>.atlassian.net pattern"
118
- echo "Self-hosted JIRA requires explicit user confirmation"
115
+ # Reject localhost and internal hostnames
116
+ if [[ "$JIRA_DOMAIN" =~ ^(localhost|127\.|10\.|172\.(1[6-9]|2[0-9]|3[01])\.|192\.168\.) ]]; then
117
+ echo "Error: Internal/localhost addresses not allowed"
119
118
  exit 1
120
- # Agent: use AskUserQuestion to confirm non-standard domain before retrying
121
119
  fi
122
120
 
123
- # Reject IP addressesIPv4, IPv6 brackets, hex-encoded (SSRF prevention)
124
- if [[ "$JIRA_DOMAIN" =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+ ]] || [[ "$JIRA_DOMAIN" =~ ^\[.*\]$ ]] || [[ "$JIRA_DOMAIN" =~ ^0x ]]; then
125
- echo "Error: IP addresses not allowed use a hostname"
121
+ # Must be a valid hostname each label: alphanumeric, hyphens allowed mid-label, no consecutive dots
122
+ if [[ ! "$JIRA_DOMAIN" =~ ^[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?(\.[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?)*$ ]]; then
123
+ echo "Error: JIRA_DOMAIN is not a valid hostname"
126
124
  exit 1
127
125
  fi
128
126
 
129
- # Reject localhost and internal hostnames
130
- if [[ "$JIRA_DOMAIN" =~ ^(localhost|127\.|10\.|172\.(1[6-9]|2[0-9]|3[01])\.|192\.168\.) ]]; then
131
- echo "Error: Internal/localhost addresses not allowed"
127
+ # Must end with .atlassian.net for cloud JIRA
128
+ # Agent: use AskUserQuestion to confirm non-standard domain before retrying
129
+ if [[ ! "$JIRA_DOMAIN" =~ ^[a-zA-Z0-9-]+\.atlassian\.net$ ]]; then
130
+ echo "Error: Domain does not match <subdomain>.atlassian.net pattern"
132
131
  exit 1
133
132
  fi
134
133
  ```
@@ -168,7 +167,7 @@ JIRA and Confluence are both Atlassian products and often used together. This sk
168
167
 
169
168
  ### Confluence Credentials
170
169
 
171
- Same authentication and security rules as JIRA — user configures `.env`, skill only validates presence. Same domain validation applies (must be `<subdomain>.atlassian.net`, HTTPS only, no IPs).
170
+ Same authentication and security rules as JIRA — user configures `.env`, skill only validates presence. Same domain validation applies (must be `<subdomain>.atlassian.net`, HTTPS only, no IPs). `CONFLUENCE_DOMAIN` MUST pass the same validation as `JIRA_DOMAIN` (Step 4) before any Confluence API call.
172
171
 
173
172
  Required `.env` keys (configured by the user, NOT by this skill):
174
173
  ```
@@ -190,20 +189,12 @@ CONFLUENCE_SPACE_KEY=<space-key>
190
189
 
191
190
  **Every page update MUST increment the version number**:
192
191
 
193
- ```bash
194
- # 1. Get current version
195
- curl -s GET ".../pages/{id}" | jq '.version.number'
196
- # Returns: 5
192
+ 1. GET current page to retrieve `version.number` (e.g., returns 5)
193
+ 2. PUT update with `version.number` set to current + 1 (e.g., 6)
197
194
 
198
- # 2. Update with version + 1
199
- PUT ".../pages/{id}"
200
- { "version": { "number": 6 } }
201
- ```
195
+ If version is not incremented, the API returns `409 Conflict`.
202
196
 
203
- **Error if version not incremented**:
204
- ```
205
- 409 Conflict: "Version must be incremented on update. Current version is: 5"
206
- ```
197
+ All Confluence API calls follow the same security rules as JIRA (HTTPS only, domain validation, credential handling). See the `jira-mapper` skill's **Security Rules** section for implementation patterns.
207
198
 
208
199
  ### Reference Documentation
209
200