obsidian-dev-skills 1.2.0 → 1.2.2

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.
@@ -92,6 +92,7 @@ on:
92
92
  push:
93
93
  tags:
94
94
  - "*"
95
+ workflow_dispatch:
95
96
 
96
97
  permissions:
97
98
  contents: write
@@ -110,6 +111,8 @@ jobs:
110
111
  cache: "pnpm"
111
112
  - run: pnpm install --frozen-lockfile
112
113
  - run: pnpm build
114
+ - id: version
115
+ run: echo "version=$(jq -r .version manifest.json)" >> "$GITHUB_OUTPUT"
113
116
  - uses: actions/attest-build-provenance@v2
114
117
  with:
115
118
  subject-path: |
@@ -118,10 +121,10 @@ jobs:
118
121
  manifest.json
119
122
  - env:
120
123
  GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
121
- TAG: ${{ github.ref_name }}
124
+ VERSION: ${{ steps.version.outputs.version }}
122
125
  run: |
123
- gh release create "$TAG" \
124
- --title="$TAG" \
126
+ gh release create "$VERSION" \
127
+ --title="$VERSION" \
125
128
  --generate-notes \
126
129
  main.js styles.css manifest.json
127
130
  ```
@@ -134,6 +137,62 @@ Then cut releases by pushing a tag (e.g. `git tag 0.1.0 && git push origin 0.1.0
134
137
 
135
138
  **Fix**: merge the two rule blocks. If they target the same selector and have non-overlapping properties, combine into one block. If they target the same selector and one is meant to override the other (e.g. inside a media query), wrap the override in a more specific selector or use a CSS variable.
136
139
 
140
+ ### `!important` declarations
141
+
142
+ > Avoid `!important`. Override styles by increasing selector specificity or using CSS variables instead.
143
+
144
+ The fix is almost never to keep `!important` and hope the scorecard ignores it. Instead, scope the rule with a parent class so it wins the cascade naturally.
145
+
146
+ ```css
147
+ /* Wrong: leans on !important to beat Obsidian's defaults */
148
+ .my-plugin-btn {
149
+ border: none !important;
150
+ background: none !important;
151
+ }
152
+
153
+ /* Right: parent class boosts specificity, no !important needed */
154
+ .my-plugin-panel .my-plugin-btn {
155
+ border: none;
156
+ background: none;
157
+ }
158
+ ```
159
+
160
+ The parent class is whatever wrapper the plugin's UI lives inside (e.g. the panel root, settings container). For toggle classes that need to beat an element's natural display, chain the toggle class with the base class for the same specificity boost: `.my-plugin-panel .my-toggle-hidden { display: none; }`.
161
+
162
+ ### `:has()` selector
163
+
164
+ > Avoid `:has()`. It can cause significant performance issues due to broad selector invalidation.
165
+
166
+ Most uses of `:has()` are stylistic preferences that can be rewritten as descendant selectors targeting the child element directly.
167
+
168
+ ```css
169
+ /* Wrong: re-styles the parent based on a descendant */
170
+ .result:has(a[href*="http"]) {
171
+ word-break: break-all;
172
+ }
173
+
174
+ /* Right: targets the child directly */
175
+ .result a[href*="http"] {
176
+ word-break: break-all;
177
+ }
178
+ ```
179
+
180
+ If the rule genuinely needs to style a parent based on a child's presence, fall back to adding a marker class via TypeScript when the child is appended (e.g. `parentEl.addClass('has-external-link')`).
181
+
182
+ ### Don't override global Obsidian UI selectors
183
+
184
+ A plugin's `styles.css` is loaded globally. Rules targeting Obsidian's built-in classes affect every other plugin and Obsidian itself, not just yours.
185
+
186
+ ```css
187
+ /* Wrong: forces flex layout on every menu in Obsidian, not just this plugin's */
188
+ .menu .menu-item {
189
+ display: flex !important;
190
+ justify-content: space-between !important;
191
+ }
192
+ ```
193
+
194
+ **Fix**: either remove the rule and accept Obsidian's default styling, or scope it. Menus appended outside your plugin's panel (via `new Menu()`) can't be scoped via parent class, so the rule has to be removed, or a marker class added when the menu is opened. The scorecard does not specifically flag this leak, but it's a hygiene rule that prevents conflicts with other plugins.
195
+
137
196
  ### `obsidianmd` ESLint rules (Warnings and Risks)
138
197
 
139
198
  `eslint-plugin-obsidianmd` enforces Obsidian-idiomatic patterns. Common signals and fixes:
@@ -154,6 +213,18 @@ this.countEl.setText('0 Selected');
154
213
 
155
214
  **Never disable this rule.** The scorecard explicitly flags `eslint-disable-next-line obsidianmd/ui/sentence-case` as a Risk and counts each occurrence. If a string genuinely cannot be sentence case (e.g. it embeds a proper noun or product name), fix the casing instead of disabling.
156
215
 
216
+ **Configure the rule for domain-specific acronyms.** The rule accepts `acronyms`, `brands`, `ignoreWords`, and `ignoreRegex` options. Use these when your plugin legitimately uses acronyms or quoted button names that the rule otherwise rejects.
217
+
218
+ ```js
219
+ // eslint.config.mjs
220
+ "obsidianmd/ui/sentence-case": ["error", {
221
+ acronyms: ["SEO", "MDX", "URL", "CSV", "H1", "H2", "H3", "H4", "H5", "H6"],
222
+ ignoreRegex: ['"[^"]+"'], // ignore content inside double quotes (referenced button/control names)
223
+ }]
224
+ ```
225
+
226
+ This is the right answer when the autofix expected output is something like `'Open seo audit panel'` (downcased acronym) but the string genuinely should read `'Open SEO audit panel'`. Configuring the rule is preferred over per-line disables, which are forbidden, or awkward rephrasing.
227
+
157
228
  #### `obsidianmd/prefer-create-el-shorthand` (Warning)
158
229
 
159
230
  Use `createDiv()` and `createSpan()` instead of `createEl('div')` and `createEl('span')`.
@@ -31,6 +31,7 @@ on:
31
31
  push:
32
32
  tags:
33
33
  - "*"
34
+ workflow_dispatch:
34
35
 
35
36
  permissions:
36
37
  contents: write
@@ -49,6 +50,8 @@ jobs:
49
50
  cache: "pnpm"
50
51
  - run: pnpm install --frozen-lockfile
51
52
  - run: pnpm build
53
+ - id: version
54
+ run: echo "version=$(jq -r .version manifest.json)" >> "$GITHUB_OUTPUT"
52
55
  - uses: actions/attest-build-provenance@v2
53
56
  with:
54
57
  subject-path: |
@@ -57,18 +60,60 @@ jobs:
57
60
  manifest.json
58
61
  - env:
59
62
  GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
60
- TAG: ${{ github.ref_name }}
63
+ VERSION: ${{ steps.version.outputs.version }}
61
64
  run: |
62
- gh release create "$TAG" \
63
- --title="$TAG" \
64
- --generate-notes \
65
- main.js styles.css manifest.json
65
+ if gh release view "$VERSION" >/dev/null 2>&1; then
66
+ gh release upload "$VERSION" main.js styles.css manifest.json --clobber
67
+ else
68
+ gh release create "$VERSION" \
69
+ --title="$VERSION" \
70
+ --generate-notes \
71
+ main.js styles.css manifest.json
72
+ fi
66
73
  ```
67
74
 
75
+ **Why read the version from `manifest.json`**: the workflow runs from either a tag push (where `github.ref_name` is the tag) or a `workflow_dispatch` from a branch (where `github.ref_name` is the branch name, e.g. `master`). Using `github.ref_name` directly would title a manually-triggered release after the branch. Reading from `manifest.json` is consistent across both triggers and matches what Obsidian's plugin loader actually reads.
76
+
68
77
  Cut releases by pushing a tag (`git tag 0.1.0 && git push origin 0.1.0`). The workflow attaches the three required assets and registers the attestation. The scorecard's "Build verified" signal also fires once the workflow has run, because the attested artifacts can be reproduced byte-for-byte from source.
69
78
 
70
79
  For the full set of scorecard signals and their fixes, see [scorecard-compliance.md](scorecard-compliance.md).
71
80
 
81
+ ### Pushing tags so the workflow actually fires
82
+
83
+ Push order matters. Push commits to the default branch **first**, in a separate command, then push the tag. If you push both in a single batch (`git push --tags --follow-tags` or some GUIs), GitHub occasionally processes the tag webhook before indexing the workflow file at that ref, and the trigger silently drops.
84
+
85
+ ```bash
86
+ git push origin master # commits first
87
+ # pause a few seconds
88
+ git push origin 0.1.0 # tag in a separate command
89
+ ```
90
+
91
+ If the workflow does not fire on a tag push, diagnose with:
92
+
93
+ ```bash
94
+ gh api repos/:owner/:repo/actions/runs --jq '.workflow_runs[0:5] | .[] | {id, event, status, conclusion, head_branch, created_at}'
95
+ gh workflow list
96
+ gh api repos/:owner/:repo/actions/permissions/workflow
97
+ ```
98
+
99
+ If `gh api .../runs` is empty for the tag, the trigger was dropped. Recovery options:
100
+
101
+ - **Add `workflow_dispatch:` to the trigger** so you can re-run from the Actions tab without a re-push.
102
+ - **Re-push the tag**: `git push origin :refs/tags/0.1.0 && git push origin 0.1.0`.
103
+ - **Manually create the release** in the GitHub UI. The release will exist but it will not have the build provenance attestation, which costs scorecard points until the next release.
104
+
105
+ ### Recovery trigger
106
+
107
+ It is worth adding a manual trigger alongside the tag-push trigger so future "the workflow didn't fire" situations have a one-click fix:
108
+
109
+ ```yaml
110
+ on:
111
+ push:
112
+ tags:
113
+ - "*"
114
+ workflow_dispatch:
115
+ ```
116
+
72
117
  > [!NOTE]
73
118
  > Themes and plugins have different asset requirements and submission paths. Ensure you follow the correct flow for your project type.
74
119
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "obsidian-dev-skills",
3
- "version": "1.2.0",
3
+ "version": "1.2.2",
4
4
  "description": "Agent skills for Obsidian plugin and theme development, including community scorecard compliance, release workflow attestation, and dependency vulnerability hygiene.",
5
5
  "keywords": [
6
6
  "obsidian",