@jahia/agentic 0.1.1 → 0.3.0

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 (174) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/README.md +28 -0
  3. package/dist/claude/.claude/rules/jahia.md +3 -1
  4. package/dist/claude/.claude/skills/jahia/SKILL.md +18 -10
  5. package/dist/claude/.claude/skills/jahia-content/SKILL.md +102 -84
  6. package/dist/claude/.claude/skills/jahia-content-create-content/SKILL.md +255 -280
  7. package/dist/claude/.claude/skills/jahia-content-explore-structure/SKILL.md +187 -96
  8. package/dist/claude/.claude/skills/jahia-content-media-upload/SKILL.md +197 -0
  9. package/dist/claude/.claude/skills/jahia-content-move-content/SKILL.md +160 -165
  10. package/dist/claude/.claude/skills/jahia-content-organize/SKILL.md +209 -0
  11. package/dist/claude/.claude/skills/jahia-content-publish/SKILL.md +181 -0
  12. package/dist/claude/.claude/skills/jahia-content-query-content/SKILL.md +122 -92
  13. package/dist/claude/.claude/skills/jahia-content-translate-content/SKILL.md +154 -225
  14. package/dist/claude/.claude/skills/jahia-dev-build-component/SKILL.md +7 -22
  15. package/dist/claude/.claude/skills/jahia-dev-create-view/SKILL.md +58 -0
  16. package/dist/claude/.claude/skills/jahia-dev-cypress/SKILL.md +150 -330
  17. package/dist/claude/.claude/skills/jahia-dev-java/SKILL.md +7 -2
  18. package/dist/claude/.claude/skills/jahia-dev-query-content/SKILL.md +93 -296
  19. package/dist/claude/.claude/skills/jahia-java-concurrency/SKILL.md +308 -0
  20. package/dist/claude/.claude/skills/jahia-java-jcr/SKILL.md +153 -0
  21. package/dist/claude/.claude/skills/jahia-java-osgi/SKILL.md +134 -0
  22. package/dist/claude/.claude/skills/jahia-java-persistence/SKILL.md +177 -0
  23. package/dist/claude/.claude/skills/jahia-java-security/SKILL.md +84 -0
  24. package/dist/claude/.claude/skills/jahia-jcr-sql2/SKILL.md +258 -0
  25. package/dist/claude/.claude/skills/jahia-review-java/SKILL.md +131 -0
  26. package/dist/claude/.claude/skills/jahia-review-java/references/code-review-output.md +121 -0
  27. package/dist/claude/CLAUDE.md +4 -8
  28. package/dist/codex/.agents/skills/jahia/SKILL.md +18 -10
  29. package/dist/codex/.agents/skills/jahia-content/SKILL.md +102 -84
  30. package/dist/codex/.agents/skills/jahia-content-create-content/SKILL.md +255 -280
  31. package/dist/codex/.agents/skills/jahia-content-explore-structure/SKILL.md +187 -96
  32. package/dist/codex/.agents/skills/jahia-content-media-upload/SKILL.md +197 -0
  33. package/dist/codex/.agents/skills/jahia-content-move-content/SKILL.md +160 -165
  34. package/dist/codex/.agents/skills/jahia-content-organize/SKILL.md +209 -0
  35. package/dist/codex/.agents/skills/jahia-content-publish/SKILL.md +181 -0
  36. package/dist/codex/.agents/skills/jahia-content-query-content/SKILL.md +122 -92
  37. package/dist/codex/.agents/skills/jahia-content-translate-content/SKILL.md +154 -225
  38. package/dist/codex/.agents/skills/jahia-dev-build-component/SKILL.md +7 -22
  39. package/dist/codex/.agents/skills/jahia-dev-create-view/SKILL.md +58 -0
  40. package/dist/codex/.agents/skills/jahia-dev-cypress/SKILL.md +150 -330
  41. package/dist/codex/.agents/skills/jahia-dev-java/SKILL.md +7 -2
  42. package/dist/codex/.agents/skills/jahia-dev-query-content/SKILL.md +93 -296
  43. package/dist/codex/.agents/skills/jahia-java-concurrency/SKILL.md +308 -0
  44. package/dist/codex/.agents/skills/jahia-java-jcr/SKILL.md +153 -0
  45. package/dist/codex/.agents/skills/jahia-java-osgi/SKILL.md +134 -0
  46. package/dist/codex/.agents/skills/jahia-java-persistence/SKILL.md +177 -0
  47. package/dist/codex/.agents/skills/jahia-java-security/SKILL.md +84 -0
  48. package/dist/codex/.agents/skills/jahia-jcr-sql2/SKILL.md +258 -0
  49. package/dist/codex/.agents/skills/jahia-review-java/SKILL.md +131 -0
  50. package/dist/codex/.agents/skills/jahia-review-java/references/code-review-output.md +121 -0
  51. package/dist/codex/AGENTS.md +5 -5
  52. package/dist/copilot/.agents/skills/jahia/SKILL.md +18 -10
  53. package/dist/copilot/.agents/skills/jahia-content/SKILL.md +102 -84
  54. package/dist/copilot/.agents/skills/jahia-content-create-content/SKILL.md +255 -280
  55. package/dist/copilot/.agents/skills/jahia-content-explore-structure/SKILL.md +187 -96
  56. package/dist/copilot/.agents/skills/jahia-content-media-upload/SKILL.md +197 -0
  57. package/dist/copilot/.agents/skills/jahia-content-move-content/SKILL.md +160 -165
  58. package/dist/copilot/.agents/skills/jahia-content-organize/SKILL.md +209 -0
  59. package/dist/copilot/.agents/skills/jahia-content-publish/SKILL.md +181 -0
  60. package/dist/copilot/.agents/skills/jahia-content-query-content/SKILL.md +122 -92
  61. package/dist/copilot/.agents/skills/jahia-content-translate-content/SKILL.md +154 -225
  62. package/dist/copilot/.agents/skills/jahia-dev-build-component/SKILL.md +7 -22
  63. package/dist/copilot/.agents/skills/jahia-dev-create-view/SKILL.md +58 -0
  64. package/dist/copilot/.agents/skills/jahia-dev-cypress/SKILL.md +150 -330
  65. package/dist/copilot/.agents/skills/jahia-dev-java/SKILL.md +7 -2
  66. package/dist/copilot/.agents/skills/jahia-dev-query-content/SKILL.md +93 -296
  67. package/dist/copilot/.agents/skills/jahia-java-concurrency/SKILL.md +308 -0
  68. package/dist/copilot/.agents/skills/jahia-java-jcr/SKILL.md +153 -0
  69. package/dist/copilot/.agents/skills/jahia-java-osgi/SKILL.md +134 -0
  70. package/dist/copilot/.agents/skills/jahia-java-persistence/SKILL.md +177 -0
  71. package/dist/copilot/.agents/skills/jahia-java-security/SKILL.md +84 -0
  72. package/dist/copilot/.agents/skills/jahia-jcr-sql2/SKILL.md +258 -0
  73. package/dist/copilot/.agents/skills/jahia-review-java/SKILL.md +131 -0
  74. package/dist/copilot/.agents/skills/jahia-review-java/references/code-review-output.md +121 -0
  75. package/dist/copilot/AGENTS.md +5 -5
  76. package/dist/cursor/.agents/skills/jahia/SKILL.md +18 -10
  77. package/dist/cursor/.agents/skills/jahia-content/SKILL.md +102 -84
  78. package/dist/cursor/.agents/skills/jahia-content-create-content/SKILL.md +255 -280
  79. package/dist/cursor/.agents/skills/jahia-content-explore-structure/SKILL.md +187 -96
  80. package/dist/cursor/.agents/skills/jahia-content-media-upload/SKILL.md +197 -0
  81. package/dist/cursor/.agents/skills/jahia-content-move-content/SKILL.md +160 -165
  82. package/dist/cursor/.agents/skills/jahia-content-organize/SKILL.md +209 -0
  83. package/dist/cursor/.agents/skills/jahia-content-publish/SKILL.md +181 -0
  84. package/dist/cursor/.agents/skills/jahia-content-query-content/SKILL.md +122 -92
  85. package/dist/cursor/.agents/skills/jahia-content-translate-content/SKILL.md +154 -225
  86. package/dist/cursor/.agents/skills/jahia-dev-build-component/SKILL.md +7 -22
  87. package/dist/cursor/.agents/skills/jahia-dev-create-view/SKILL.md +58 -0
  88. package/dist/cursor/.agents/skills/jahia-dev-cypress/SKILL.md +150 -330
  89. package/dist/cursor/.agents/skills/jahia-dev-java/SKILL.md +7 -2
  90. package/dist/cursor/.agents/skills/jahia-dev-query-content/SKILL.md +93 -296
  91. package/dist/cursor/.agents/skills/jahia-java-concurrency/SKILL.md +308 -0
  92. package/dist/cursor/.agents/skills/jahia-java-jcr/SKILL.md +153 -0
  93. package/dist/cursor/.agents/skills/jahia-java-osgi/SKILL.md +134 -0
  94. package/dist/cursor/.agents/skills/jahia-java-persistence/SKILL.md +177 -0
  95. package/dist/cursor/.agents/skills/jahia-java-security/SKILL.md +84 -0
  96. package/dist/cursor/.agents/skills/jahia-jcr-sql2/SKILL.md +258 -0
  97. package/dist/cursor/.agents/skills/jahia-review-java/SKILL.md +131 -0
  98. package/dist/cursor/.agents/skills/jahia-review-java/references/code-review-output.md +121 -0
  99. package/dist/cursor/.cursor/rules/jahia.mdc +3 -1
  100. package/dist/gemini/.agents/skills/jahia/SKILL.md +18 -10
  101. package/dist/gemini/.agents/skills/jahia-content/SKILL.md +102 -84
  102. package/dist/gemini/.agents/skills/jahia-content-create-content/SKILL.md +255 -280
  103. package/dist/gemini/.agents/skills/jahia-content-explore-structure/SKILL.md +187 -96
  104. package/dist/gemini/.agents/skills/jahia-content-media-upload/SKILL.md +197 -0
  105. package/dist/gemini/.agents/skills/jahia-content-move-content/SKILL.md +160 -165
  106. package/dist/gemini/.agents/skills/jahia-content-organize/SKILL.md +209 -0
  107. package/dist/gemini/.agents/skills/jahia-content-publish/SKILL.md +181 -0
  108. package/dist/gemini/.agents/skills/jahia-content-query-content/SKILL.md +122 -92
  109. package/dist/gemini/.agents/skills/jahia-content-translate-content/SKILL.md +154 -225
  110. package/dist/gemini/.agents/skills/jahia-dev-build-component/SKILL.md +7 -22
  111. package/dist/gemini/.agents/skills/jahia-dev-create-view/SKILL.md +58 -0
  112. package/dist/gemini/.agents/skills/jahia-dev-cypress/SKILL.md +150 -330
  113. package/dist/gemini/.agents/skills/jahia-dev-java/SKILL.md +7 -2
  114. package/dist/gemini/.agents/skills/jahia-dev-query-content/SKILL.md +93 -296
  115. package/dist/gemini/.agents/skills/jahia-java-concurrency/SKILL.md +308 -0
  116. package/dist/gemini/.agents/skills/jahia-java-jcr/SKILL.md +153 -0
  117. package/dist/gemini/.agents/skills/jahia-java-osgi/SKILL.md +134 -0
  118. package/dist/gemini/.agents/skills/jahia-java-persistence/SKILL.md +177 -0
  119. package/dist/gemini/.agents/skills/jahia-java-security/SKILL.md +84 -0
  120. package/dist/gemini/.agents/skills/jahia-jcr-sql2/SKILL.md +258 -0
  121. package/dist/gemini/.agents/skills/jahia-review-java/SKILL.md +131 -0
  122. package/dist/gemini/.agents/skills/jahia-review-java/references/code-review-output.md +121 -0
  123. package/dist/gemini/AGENTS.md +5 -5
  124. package/dist/gemini/GEMINI.md +2 -2
  125. package/dist/opencode/.agents/skills/jahia/SKILL.md +18 -10
  126. package/dist/opencode/.agents/skills/jahia-content/SKILL.md +102 -84
  127. package/dist/opencode/.agents/skills/jahia-content-create-content/SKILL.md +255 -280
  128. package/dist/opencode/.agents/skills/jahia-content-explore-structure/SKILL.md +187 -96
  129. package/dist/opencode/.agents/skills/jahia-content-media-upload/SKILL.md +197 -0
  130. package/dist/opencode/.agents/skills/jahia-content-move-content/SKILL.md +160 -165
  131. package/dist/opencode/.agents/skills/jahia-content-organize/SKILL.md +209 -0
  132. package/dist/opencode/.agents/skills/jahia-content-publish/SKILL.md +181 -0
  133. package/dist/opencode/.agents/skills/jahia-content-query-content/SKILL.md +122 -92
  134. package/dist/opencode/.agents/skills/jahia-content-translate-content/SKILL.md +154 -225
  135. package/dist/opencode/.agents/skills/jahia-dev-build-component/SKILL.md +7 -22
  136. package/dist/opencode/.agents/skills/jahia-dev-create-view/SKILL.md +58 -0
  137. package/dist/opencode/.agents/skills/jahia-dev-cypress/SKILL.md +150 -330
  138. package/dist/opencode/.agents/skills/jahia-dev-java/SKILL.md +7 -2
  139. package/dist/opencode/.agents/skills/jahia-dev-query-content/SKILL.md +93 -296
  140. package/dist/opencode/.agents/skills/jahia-java-concurrency/SKILL.md +308 -0
  141. package/dist/opencode/.agents/skills/jahia-java-jcr/SKILL.md +153 -0
  142. package/dist/opencode/.agents/skills/jahia-java-osgi/SKILL.md +134 -0
  143. package/dist/opencode/.agents/skills/jahia-java-persistence/SKILL.md +177 -0
  144. package/dist/opencode/.agents/skills/jahia-java-security/SKILL.md +84 -0
  145. package/dist/opencode/.agents/skills/jahia-jcr-sql2/SKILL.md +258 -0
  146. package/dist/opencode/.agents/skills/jahia-review-java/SKILL.md +131 -0
  147. package/dist/opencode/.agents/skills/jahia-review-java/references/code-review-output.md +121 -0
  148. package/dist/opencode/AGENTS.md +5 -5
  149. package/dist/windsurf/.windsurf/rules/jahia.md +3 -1
  150. package/dist/windsurf/.windsurf/skills/jahia/SKILL.md +18 -10
  151. package/dist/windsurf/.windsurf/skills/jahia-content/SKILL.md +102 -84
  152. package/dist/windsurf/.windsurf/skills/jahia-content-create-content/SKILL.md +255 -280
  153. package/dist/windsurf/.windsurf/skills/jahia-content-explore-structure/SKILL.md +187 -96
  154. package/dist/windsurf/.windsurf/skills/jahia-content-media-upload/SKILL.md +197 -0
  155. package/dist/windsurf/.windsurf/skills/jahia-content-move-content/SKILL.md +160 -165
  156. package/dist/windsurf/.windsurf/skills/jahia-content-organize/SKILL.md +209 -0
  157. package/dist/windsurf/.windsurf/skills/jahia-content-publish/SKILL.md +181 -0
  158. package/dist/windsurf/.windsurf/skills/jahia-content-query-content/SKILL.md +122 -92
  159. package/dist/windsurf/.windsurf/skills/jahia-content-translate-content/SKILL.md +154 -225
  160. package/dist/windsurf/.windsurf/skills/jahia-dev-build-component/SKILL.md +7 -22
  161. package/dist/windsurf/.windsurf/skills/jahia-dev-create-view/SKILL.md +58 -0
  162. package/dist/windsurf/.windsurf/skills/jahia-dev-cypress/SKILL.md +150 -330
  163. package/dist/windsurf/.windsurf/skills/jahia-dev-java/SKILL.md +7 -2
  164. package/dist/windsurf/.windsurf/skills/jahia-dev-query-content/SKILL.md +93 -296
  165. package/dist/windsurf/.windsurf/skills/jahia-java-concurrency/SKILL.md +308 -0
  166. package/dist/windsurf/.windsurf/skills/jahia-java-jcr/SKILL.md +153 -0
  167. package/dist/windsurf/.windsurf/skills/jahia-java-osgi/SKILL.md +134 -0
  168. package/dist/windsurf/.windsurf/skills/jahia-java-persistence/SKILL.md +177 -0
  169. package/dist/windsurf/.windsurf/skills/jahia-java-security/SKILL.md +84 -0
  170. package/dist/windsurf/.windsurf/skills/jahia-jcr-sql2/SKILL.md +258 -0
  171. package/dist/windsurf/.windsurf/skills/jahia-review-java/SKILL.md +131 -0
  172. package/dist/windsurf/.windsurf/skills/jahia-review-java/references/code-review-output.md +121 -0
  173. package/dist/windsurf/AGENTS.md +5 -5
  174. package/package.json +7 -3
@@ -1,273 +1,208 @@
1
1
  ---
2
2
  name: jahia-content-translate-content
3
- description: Adds language support to a Jahia site and translates existing content nodes. Use when asked to add a new language, fill in missing translations, or audit which content lacks i18n values.
3
+ description: Adds site languages and translates existing Jahia content using MCP tools. Use when asked to enable a locale, fill in missing translations, or publish translated pages and content.
4
4
  ---
5
5
 
6
6
  # Skill: jahia-content-translate-content
7
7
 
8
- Adds languages to a Jahia site and populates i18n properties on existing content nodes via the GraphQL API.
8
+ Translates existing content and manages multi-language support using MCP tools via the `jahia` MCP server.
9
+
10
+ > **Never call Jahia's GraphQL API directly.** Use only MCP tools. If a capability is missing, report it — do not work around with curl/GraphQL.
9
11
 
10
12
  ---
11
13
 
12
14
  ## Prerequisites
13
15
 
14
- - Jahia running at `http://localhost:8080`
15
- - Credentials: `root` / `root1234` (default)
16
- - GraphQL endpoint: `http://localhost:8080/modules/graphql`
17
-
18
- **Always include both auth flags:**
19
- ```bash
20
- curl -s -u root:root1234 \
21
- -H "Content-Type: application/json" \
22
- -H "Origin: http://localhost:8080" \
23
- -X POST http://localhost:8080/modules/graphql \
24
- -d '{"query": "..."}'
25
- ```
26
-
27
- > ⚠️ The `Origin` header is **required** — omitting it returns `Permission denied`.
16
+ - MCP server `jahia` connected with a valid API token
17
+ - Know the target `siteKey` (call `site.list` if unsure)
18
+ - Content already exists in a source locale
28
19
 
29
20
  ---
30
21
 
31
- ## Step 1 — Enable the new language on the site
32
-
33
- Before creating or querying translations, the language must be declared on the site node.
34
-
35
- ```bash
36
- # Check currently enabled languages
37
- curl -s -u root:root1234 \
38
- -H "Content-Type: application/json" \
39
- -H "Origin: http://localhost:8080" \
40
- -X POST http://localhost:8080/modules/graphql \
41
- -d '{
42
- "query": "{ jcr { nodeByPath(path: \"/sites/mySite\") { properties(names: [\"j:languages\",\"j:defaultLanguage\"]) { name values } } } }"
43
- }'
44
-
45
- # Add a language (e.g. "fr") — use setPropertiesBatch with the full new list
46
- curl -s -u root:root1234 \
47
- -H "Content-Type: application/json" \
48
- -H "Origin: http://localhost:8080" \
49
- -X POST http://localhost:8080/modules/graphql \
50
- -d '{
51
- "query": "mutation { jcr { mutateNode(pathOrId: \"/sites/mySite\") { setPropertiesBatch(properties: [{name: \"j:languages\", values: [\"en\", \"fr\"]}]) { path } } } }"
52
- }'
22
+ ## Step 1 — Check site languages
23
+
24
+ ```
25
+ tool: site.get
26
+ args: { "siteKey": "SITE_KEY" }
53
27
  ```
54
28
 
55
- > ⚠️ `j:languages` is a multi-valued property — always pass the **complete** list of languages, not just the new one. Passing only `["fr"]` would remove `"en"`.
29
+ Check `languages` and `defaultLanguage`.
56
30
 
57
- ---
31
+ - If the target locale is already present, continue.
32
+ - If the target locale is missing, add it to the site first before writing translations.
58
33
 
59
- ## Step 2 — Audit content missing translations
34
+ ---
60
35
 
61
- Query all i18n-bearing nodes and inspect which ones have empty values for the target language:
36
+ ## Step 2 Read the source content
62
37
 
63
- ```bash
64
- # Find all content nodes under /sites/mySite/contents
65
- curl -s -u root:root1234 \
66
- -H "Content-Type: application/json" \
67
- -H "Origin: http://localhost:8080" \
68
- -X POST http://localhost:8080/modules/graphql \
69
- -d '{
70
- "query": "{ jcr { nodesByQuery(query: \"SELECT * FROM [jmix:i18n] WHERE ISDESCENDANTNODE(\u0027/sites/mySite/contents\u0027) ORDER BY [jcr:path] ASC\", queryLanguage: SQL2) { nodes { path primaryNodeType { name } properties(language: \"fr\") { name value } } } } }"
71
- }'
38
+ ```
39
+ tool: content.get
40
+ args: {
41
+ "path": "/sites/SITE_KEY/home/about/main/intro-text",
42
+ "locale": "en"
43
+ }
72
44
  ```
73
45
 
74
- Or query a specific content type:
46
+ Note the properties that actually contain human-readable text.
75
47
 
76
- ```bash
77
- curl -s -u root:root1234 \
78
- -H "Content-Type: application/json" \
79
- -H "Origin: http://localhost:8080" \
80
- -X POST http://localhost:8080/modules/graphql \
81
- -d '{
82
- "query": "{ jcr { nodesByQuery(query: \"SELECT * FROM [namespace:article] WHERE ISDESCENDANTNODE(\u0027/sites/mySite/contents\u0027)\", queryLanguage: SQL2) { nodes { path properties(language: \"fr\") { name value } } } } }"
83
- }'
84
- ```
48
+ To find a batch of nodes to translate:
85
49
 
86
- Look for nodes where i18n fields (`jcr:title`, `body`, etc.) have empty or null `value`.
50
+ ```
51
+ tool: content.search
52
+ args: {
53
+ "siteKey": "SITE_KEY",
54
+ "nodeType": "jmix:droppableContent",
55
+ "locale": "en",
56
+ "limit": 50
57
+ }
58
+ ```
87
59
 
88
60
  ---
89
61
 
90
- ## Step 3 — Set i18n properties
91
-
92
- ### Single node
62
+ ## Step 3 — Identify which properties are i18n
93
63
 
94
- ```bash
95
- curl -s -u root:root1234 \
96
- -H "Content-Type: application/json" \
97
- -H "Origin: http://localhost:8080" \
98
- -X POST http://localhost:8080/modules/graphql \
99
- -d '{
100
- "query": "mutation { jcr { mutateNode(pathOrId: \"/sites/mySite/contents/articles/my-article\") { setPropertiesBatch(properties: [{name: \"jcr:title\", value: \"Mon article\", language: \"fr\"}, {name: \"body\", value: \"<p>Contenu ici</p>\", language: \"fr\"}]) { path } } } }"
101
- }'
64
+ ```
65
+ tool: content.type
66
+ args: { "name": "jnt:bigText" }
102
67
  ```
103
68
 
104
- > ⚠️ Use `setPropertiesBatch` (plural), not `setProperty` (singular — does not exist in the Jahia GraphQL API).
69
+ Translate only properties marked as internationalized.
105
70
 
106
- ### Critical ordering rule
71
+ Common i18n properties:
72
+ - `jcr:title`
73
+ - `text`
74
+ - `body`
75
+ - `description`
76
+ - `subtitle`
107
77
 
108
- When a content type has **mandatory** i18n fields (other than `jcr:title`), set those **before** `jcr:title`. Setting `jcr:title` first can trigger a constraint check before all mandatory fields are present.
78
+ Common non-i18n properties:
79
+ - `j:view`
80
+ - `j:templateName`
81
+ - `jcr:primaryType`
82
+ - references such as `j:node`
83
+ - numeric or technical settings
109
84
 
110
- The safe pattern — set all mandatory i18n fields in a single `setPropertiesBatch` call:
85
+ ---
111
86
 
112
- ```bash
113
- curl -s -u root:root1234 \
114
- -H "Content-Type: application/json" \
115
- -H "Origin: http://localhost:8080" \
116
- -X POST http://localhost:8080/modules/graphql \
117
- -d '{
118
- "query": "mutation { jcr { mutateNode(pathOrId: \"/sites/mySite/contents/articles/my-article\") { setPropertiesBatch(properties: [{name: \"body\", value: \"<p>Contenu ici</p>\", language: \"fr\"}, {name: \"jcr:title\", value: \"Mon article\", language: \"fr\"}]) { path } } } }"
119
- }'
120
- ```
87
+ ## Step 4 — Write translated properties
121
88
 
122
- ---
89
+ Use `content.update` with the target locale:
123
90
 
124
- ## Step 4 — Bulk translation with Python
91
+ ```
92
+ tool: content.update
93
+ args: {
94
+ "path": "/sites/SITE_KEY/home/about/main/intro-text",
95
+ "locale": "fr",
96
+ "properties": {
97
+ "jcr:title": "À propos",
98
+ "text": "<p>Bienvenue sur notre site.</p>"
99
+ }
100
+ }
101
+ ```
125
102
 
126
- For translating many nodes at once:
103
+ Key rules:
104
+ - Set all mandatory i18n properties in the same call.
105
+ - Translate only i18n properties.
106
+ - Preserve the HTML structure for rich text.
107
+ - Do not translate technical choicelist values.
127
108
 
128
- ```python
129
- import json
130
- from urllib.request import Request, urlopen
109
+ ---
131
110
 
132
- JAHIA = "http://localhost:8080"
133
- AUTH = ("root", "root1234")
111
+ ## Step 5 — Translate page titles
134
112
 
135
- import base64
136
- token = base64.b64encode(f"{AUTH[0]}:{AUTH[1]}".encode()).decode()
137
- HEADERS = {
138
- "Content-Type": "application/json",
139
- "Origin": JAHIA,
140
- "Authorization": f"Basic {token}",
141
- }
113
+ Page titles are also i18n:
142
114
 
143
- def gql(query):
144
- body = json.dumps({"query": query}).encode()
145
- req = Request(f"{JAHIA}/modules/graphql", data=body, headers=HEADERS, method="POST")
146
- with urlopen(req) as r:
147
- d = json.loads(r.read())
148
- if "errors" in d:
149
- print("ERR:", d["errors"][0]["message"][:120])
150
- return d
151
-
152
- # 1. List all articles missing French title
153
- result = gql(
154
- '{ jcr { nodesByQuery(query: "SELECT * FROM [namespace:article] '
155
- "WHERE ISDESCENDANTNODE('/sites/mySite/contents') "
156
- 'ORDER BY [jcr:path] ASC", queryLanguage: SQL2) '
157
- '{ nodes { path properties(language: "fr") { name value } } } } }'
158
- )
159
-
160
- nodes = result["data"]["jcr"]["nodesByQuery"]["nodes"]
161
- missing_fr = [
162
- n["path"]
163
- for n in nodes
164
- if not any(p["name"] == "jcr:title" and p["value"] for p in n["properties"])
165
- ]
166
-
167
- # 2. Set French translations (replace with actual translated values)
168
- translations = {
169
- "/sites/mySite/contents/articles/article-1": ("Titre FR 1", "<p>Corps FR 1</p>"),
170
- "/sites/mySite/contents/articles/article-2": ("Titre FR 2", "<p>Corps FR 2</p>"),
115
+ ```
116
+ tool: content.update
117
+ args: {
118
+ "path": "/sites/SITE_KEY/home/about",
119
+ "locale": "fr",
120
+ "properties": {
121
+ "jcr:title": "À propos"
122
+ }
171
123
  }
172
-
173
- for path, (title, body) in translations.items():
174
- r = gql(
175
- f'mutation {{ jcr {{ mutateNode(pathOrId: "{path}") {{'
176
- f' setPropertiesBatch(properties: ['
177
- f' {{name: "body", value: {json.dumps(body)}, language: "fr"}},'
178
- f' {{name: "jcr:title", value: {json.dumps(title)}, language: "fr"}}'
179
- f' ]) {{ path }} }} }} }}'
180
- )
181
- ok = "errors" not in r
182
- print(f" {'✓' if ok else '✗'} {path.split('/')[-1]}")
183
124
  ```
184
125
 
185
126
  ---
186
127
 
187
- ## Step 5Choicelist fields and view-level translations
128
+ ## Step 6Verify translations
188
129
 
189
- ### Choicelist fields should NOT be i18n
130
+ Check the translated locale directly:
190
131
 
191
- If a CND property uses a choicelist (e.g. `category`, `status`), its stored values are language-agnostic keys like `"featured"` or `"draft"`. The display label is translated **in the view**, not in the JCR.
132
+ ```
133
+ tool: content.get
134
+ args: {
135
+ "path": "/sites/SITE_KEY/home/about/main/intro-text",
136
+ "locale": "fr"
137
+ }
138
+ ```
192
139
 
193
- ```tsx
194
- // In the view component — translate the stored key to a display label
195
- const STATUS_LABELS: Record<string, Record<string, string>> = {
196
- en: { featured: "Featured", draft: "Draft" },
197
- fr: { featured: "En vedette", draft: "Brouillon" },
198
- };
140
+ Or search in the target locale:
199
141
 
200
- const lang = renderContext.getMainResourceLocale().getLanguage();
201
- const statusKey = properties.status as string;
202
- const statusLabel = STATUS_LABELS[lang]?.[statusKey] ?? statusKey;
142
+ ```
143
+ tool: content.search
144
+ args: {
145
+ "siteKey": "SITE_KEY",
146
+ "nodeType": "jmix:droppableContent",
147
+ "locale": "fr",
148
+ "limit": 20,
149
+ "projectProperties": ["jcr:title"]
150
+ }
203
151
  ```
204
152
 
205
- Do **not** add `i18n` to the CND property for choicelists — the key should be the same in all languages.
206
-
207
- ### Hardcoded UI strings
153
+ ---
208
154
 
209
- View-level strings that are not stored in JCR (button labels, headings, placeholder text) need locale-keyed label maps in the component:
155
+ ## Step 7 Publish the translated locale
210
156
 
211
- ```tsx
212
- const LABELS = {
213
- en: { readMore: "Read more", by: "By" },
214
- fr: { readMore: "Lire la suite", by: "Par" },
215
- };
157
+ Publish the page or subtree in the target language:
216
158
 
217
- const t = LABELS[renderContext.getMainResourceLocale().getLanguage()] ?? LABELS.en;
218
- // Usage: <a>{t.readMore}</a>
219
159
  ```
220
-
221
- ---
222
-
223
- ## Step 6 — Clean up orphaned translation nodes
224
-
225
- When you remove the `i18n` flag from a CND property (or delete a language from the site), orphaned `j:translation_XX` child nodes may remain. Inspect and remove them if needed:
226
-
227
- ```bash
228
- # Find translation sub-nodes for a content node
229
- curl -s -u root:root1234 \
230
- -H "Content-Type: application/json" \
231
- -H "Origin: http://localhost:8080" \
232
- -X POST http://localhost:8080/modules/graphql \
233
- -d '{
234
- "query": "{ jcr { nodeByPath(path: \"/sites/mySite/contents/articles/my-article\") { children { nodes { name primaryNodeType { name } } } } } }"
235
- }'
236
-
237
- # Delete an orphaned translation node
238
- curl -s -u root:root1234 \
239
- -H "Content-Type: application/json" \
240
- -H "Origin: http://localhost:8080" \
241
- -X POST http://localhost:8080/modules/graphql \
242
- -d '{
243
- "query": "mutation { jcr { deleteNode(pathOrId: \"/sites/mySite/contents/articles/my-article/j:translation_fr\") }"
244
- }'
160
+ tool: publication.publish
161
+ args: {
162
+ "path": "/sites/SITE_KEY/home/about",
163
+ "languages": ["fr"]
164
+ }
245
165
  ```
246
166
 
167
+ If the translation touches both a page and child content, publish the page path so the subtree is included.
168
+
247
169
  ---
248
170
 
249
- ## Step 7 — Publish translations
250
-
251
- After setting i18n properties, republish to make them live:
252
-
253
- ```bash
254
- # Publish a single node for a specific language
255
- curl -s -u root:root1234 \
256
- -H "Content-Type: application/json" \
257
- -H "Origin: http://localhost:8080" \
258
- -X POST http://localhost:8080/modules/graphql \
259
- -d '{
260
- "query": "mutation { jcr { mutateNode(pathOrId: \"/sites/mySite/contents/articles/my-article\") { publish(languages: [\"fr\"]) } } }"
261
- }'
262
-
263
- # Publish all articles in both languages
264
- curl -s -u root:root1234 \
265
- -H "Content-Type: application/json" \
266
- -H "Origin: http://localhost:8080" \
267
- -X POST http://localhost:8080/modules/graphql \
268
- -d '{
269
- "query": "mutation { jcr { mutateNodesByQuery(query: \"SELECT * FROM [namespace:article] WHERE ISDESCENDANTNODE(\u0027/sites/mySite/contents\u0027)\", queryLanguage: SQL2) { publish(languages: [\"en\", \"fr\"]) } } }"
270
- }'
171
+ ## Common patterns
172
+
173
+ ### Translate all content under a page
174
+
175
+ 1. Discover the page structure:
176
+ ```
177
+ tool: page.structure
178
+ args: { "path": "/sites/SITE_KEY/home/about" }
179
+ ```
180
+ 2. Read each child node in the source locale:
181
+ ```
182
+ tool: content.get
183
+ args: { "path": "CHILD_PATH", "locale": "en" }
184
+ ```
185
+ 3. Write translated values to the target locale:
186
+ ```
187
+ tool: content.update
188
+ args: {
189
+ "path": "CHILD_PATH",
190
+ "locale": "fr",
191
+ "properties": { "jcr:title": "...", "text": "..." }
192
+ }
193
+ ```
194
+ 4. Publish the page in the new language.
195
+
196
+ ### Audit whether a page is publish-ready in the new locale
197
+
198
+ ```
199
+ tool: publication.status
200
+ args: {
201
+ "path": "/sites/SITE_KEY/home/about",
202
+ "language": "fr",
203
+ "subNodes": true,
204
+ "references": true
205
+ }
271
206
  ```
272
207
 
273
208
  ---
@@ -276,22 +211,16 @@ curl -s -u root:root1234 \
276
211
 
277
212
  | Error | Cause | Fix |
278
213
  |-------|-------|-----|
279
- | `Permission denied` | Missing `Origin` header | Add `-H "Origin: http://localhost:8080"` |
280
- | i18n property returned empty after set | Missing `language:` in `properties()` query | Add `language: "fr"` to read call |
281
- | `ConstraintViolationException` on title set | Mandatory i18n field not set first | Use `setPropertiesBatch` with all mandatory fields in one call |
282
- | Language not appearing in site | `j:languages` mutation only had the new language | Pass the full list: `["en", "fr"]` |
283
- | Choicelist key changed per language | Property incorrectly declared `i18n` in CND | Remove `i18n` from the CND property; translate keys in the view |
214
+ | `MANDATORY_PROPERTY_MISSING` | Not all required i18n properties were set | Set all mandatory translated properties in one `content.update` call |
215
+ | Language not available | Locale not enabled on the site | Add the locale to the site first |
216
+ | Properties appear empty | Wrong locale was used | Verify configured locales with `site.get` |
217
+ | Translation not visible on the public site | It is still only in EDIT | Publish with `publication.publish` |
284
218
 
285
219
  ---
286
220
 
287
- ## Workflow summary
221
+ ## Related skills
222
+
223
+ - `/jahia-content-explore-structure` — find the right pages, nodes, and types first
224
+ - `/jahia-content-publish` — publish translated content and inspect language-scoped status
225
+ - `/jahia-content-create-content` — create new pages or content that will later need translation
288
226
 
289
- ```
290
- 1. Enable language → mutateNode j:languages with full list
291
- 2. Audit → nodesByQuery with properties(language: "XX") to find gaps
292
- 3. Translate → setPropertiesBatch with language: "XX" for each i18n field
293
- 4. View strings → add locale-keyed label maps in .server.tsx
294
- 5. Choicelists → translate keys in the view, not the JCR
295
- 6. Publish → publish(languages: ["XX"]) for all affected nodes
296
- 7. Verify → query back with language: "XX" to confirm values
297
- ```
@@ -16,17 +16,17 @@ Run these steps in order. Do not skip to the view before the content type is def
16
16
 
17
17
  ---
18
18
 
19
- ## Step 0 — Optional: capture reference screenshot
19
+ ## Step 0 — Optional: capture reference screenshot (interactive sessions only)
20
20
 
21
21
  If the user provides a reference URL (or mentions a site to model after), invoke `/jahia-dev-screenshot` **before writing any code** to capture the visual spec.
22
22
 
23
- The screenshot gives the view implementation visual context: layout, colors, typography, and component anatomy. Reference it throughout Steps 2–3.
23
+ > Skip this step in automated / autopilot contexts screenshot comparison requires human review and adds significant time.
24
24
 
25
25
  ---
26
26
 
27
- ## Step 1 — Write and confirm the content spec
27
+ ## Step 1 — Write the content spec
28
28
 
29
- Before writing any code, fill out this template and confirm with the user:
29
+ Before writing any code, draft this spec internally. In an interactive session, confirm with the user; in autopilot mode, proceed with your best judgement and document what you chose:
30
30
 
31
31
  ```
32
32
  Name: <ComponentName>
@@ -41,8 +41,6 @@ Used where: <Area on page / nested in <Parent> / listing item>
41
41
  Has children: <yes: ChildType / no>
42
42
  ```
43
43
 
44
- Only proceed once the spec is confirmed.
45
-
46
44
  ---
47
45
 
48
46
  ## Step 2 — Invoke `jahia-dev-define-content-type`
@@ -69,25 +67,12 @@ Use the instructions from the `jahia-dev-create-view` skill to:
69
67
 
70
68
  ---
71
69
 
72
- ## Step 4 — Validate and compare
70
+ ## Step 4 — Validate
73
71
 
74
72
  1. In Jahia Page Builder, click **New content** and select the new type
75
73
  2. Fill in the content form and click **Save**
76
74
  3. Verify the component renders correctly on the page
77
75
  4. Check for the error `No rendering set for node: <type>` — if seen, re-run `yarn build && yarn jahia-deploy`
78
- 5. **If a reference screenshot was taken in Step 0**, invoke `/jahia-dev-screenshot` again to capture the Jahia render and compare against the reference.
79
-
80
- ---
81
-
82
- ## Step 5 — Accessibility check
83
-
84
- After the component is live, invoke `/jahia-dev-accessibility` to audit it:
85
-
86
- 1. Run the axe-core audit against the page(s) containing the new component
87
- 2. Fix any `critical` or `serious` violations before considering the component done
88
- 3. Report remaining violations to the user
89
-
90
- A component is not complete until it has no critical or serious accessibility violations.
91
76
 
92
77
  ---
93
78
 
@@ -128,13 +113,13 @@ If the component has child nodes (e.g. a hero with CTA buttons), repeat Steps 2
128
113
  ---
129
114
 
130
115
  ## Validation checklist
131
- - [ ] Spec confirmed before writing any code
116
+ - [ ] Spec written before any code
132
117
  - [ ] Component count within scale of thumbs (1–4 templates, 5–10 types, 2–5 mixins, 1–4 views/type)
133
118
  - [ ] `definition.cnd` created with correct namespace and mixins
134
119
  - [ ] `types.ts` reflects all CND properties
135
120
  - [ ] `default.server.tsx` renders without errors
136
121
  - [ ] Views handle null/missing fields gracefully (mandatory does not guarantee a value at runtime)
137
122
  - [ ] `component.module.css` applied and visible
123
+ - [ ] Semantic HTML used: correct heading level, `alt` text on images, sufficient colour contrast
138
124
  - [ ] Component appears in Page Builder content picker
139
125
  - [ ] Content can be created and renders on the page
140
- - [ ] No critical or serious accessibility violations (`/jahia-dev-accessibility`)
@@ -78,6 +78,64 @@ When you have a source HTML fragment to translate (e.g. from `/jahia-dev-import-
78
78
 
79
79
  ---
80
80
 
81
+ ## Step 1b — Accessibility and SEO rules (apply to every view)
82
+
83
+ Build these requirements in from the start — retrofitting them later is more expensive.
84
+
85
+ ### Semantic HTML structure
86
+
87
+ | Element | Rule |
88
+ |---|---|
89
+ | `<section>`, `<article>` | Wrap every self-contained block of content |
90
+ | `<header>` / `<footer>` | Use for the page header and footer in page templates |
91
+ | `<nav>` | Wrap navigation menus; add `aria-label` when there are multiple navs |
92
+ | `<main>` | Exactly one per page, wrapping all page body content (already enforced by the Layout component for page templates) |
93
+ | Headings | Each page must have exactly one `<h1>`; section headings use `<h2>`; sub-section headings use `<h3>`. Never skip levels. |
94
+
95
+ ### Images
96
+
97
+ Every `<img>` must have an `alt` attribute. Decorative images use `alt=""`. Informational images use a descriptive string from a CND property:
98
+
99
+ ```tsx
100
+ // ❌ Missing alt
101
+ <img src={buildNodeUrl(props.image)} />
102
+
103
+ // ✅ Descriptive alt from content
104
+ <img src={buildNodeUrl(props.image)} alt={props.imageAlt ?? ""} />
105
+ ```
106
+
107
+ Add `- imageAlt (string) i18n` to the CND and `imageAlt?: string` to `types.ts` for any type with an image field.
108
+
109
+ ### Colour contrast
110
+
111
+ Use colours with a contrast ratio ≥ 4.5:1 for body text and ≥ 3:1 for large text (18px+ or bold 14px+) against the background. Avoid light grey on white. When in doubt, use near-black (`#333333` or `#1a1a1a`) for body text.
112
+
113
+ ### Link and button names
114
+
115
+ Every `<a>` and `<button>` must have an accessible name — either visible text or `aria-label`. Icon-only buttons must have `aria-label`:
116
+
117
+ ```tsx
118
+ // ❌ No accessible name
119
+ <button><svg>…</svg></button>
120
+
121
+ // ✅ Accessible name via aria-label
122
+ <button aria-label="Close menu"><svg aria-hidden="true">…</svg></button>
123
+ ```
124
+
125
+ ### Focus styles
126
+
127
+ Never suppress focus indicators globally. Use `:focus-visible` to style keyboard focus:
128
+
129
+ ```css
130
+ /* ❌ Never do this */
131
+ * { outline: none; }
132
+
133
+ /* ✅ Style keyboard focus without affecting mouse users */
134
+ :focus-visible { outline: 2px solid #0969da; outline-offset: 2px; }
135
+ ```
136
+
137
+ ---
138
+
81
139
  ## Step 2 — Import Props from types.ts
82
140
 
83
141
  Always import `Props` from `./types.js` (not `./types.ts` — use `.js` extension at import time):