@maccesar/aiskills 1.16.0 → 1.17.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.
package/README.md CHANGED
@@ -177,16 +177,15 @@ Example prompts:
177
177
  ```
178
178
 
179
179
  Reference files:
180
- | File | Topics |
181
- | ----------------------- | ---------------------------------------------------------------------- |
182
- | 01-design-process.md | Feature-first workflow, low-fidelity, personality, pre-defined systems |
183
- | 02-visual-hierarchy.md | Weight, color, size hierarchy; labels; icons; button tiers |
184
- | 03-layout-spacing.md | White space, spacing scale, columns, law of proximity |
185
- | 04-typography.md | Type scale, line length, alignment, line-height, letter-spacing |
186
- | 05-color.md | HSL, shade systems, WCAG contrast, color signals |
187
- | 06-depth-shadows.md | Light source, raised/inset elements, shadow elevation |
188
- | 07-images.md | Stock photos, text over images, icons at scale, favicons |
189
- | 08-finishing-touches.md | Icons, borders, backgrounds, empty states, leveling up |
180
+ | File | Topics |
181
+ | ------------------------ | --------------------------------------------------------------------------------------- |
182
+ | 01-foundations.md | Project mindset: feature-first work, scope discipline, defining systems, picking a voice |
183
+ | 02-page-mechanics.md | Visual hierarchy, layout, white space, spacing scales, typography |
184
+ | 03-visual-treatment.md | Color systems (HSL, shades, greys, contrast), depth and shadows, image handling |
185
+ | 04-polish.md | Borders, accents, empty states, decorative defaults, sharpening design intuition |
186
+ | 05-motion.md | Motion system, hover/press states, loading patterns, `prefers-reduced-motion` |
187
+ | 06-dark-mode.md | Dark mode color tokens, text contrast, shadows, images, theme toggle |
188
+ | 07-component-patterns.md | Modals (focus, layout), forms (labels, validation), tables (density, alignment) |
190
189
 
191
190
  ---
192
191
 
@@ -298,12 +297,22 @@ Example prompts:
298
297
  ```
299
298
 
300
299
  Reference files:
301
- | File | Topics |
302
- | ---------------------- | -------------------------------------------------------------------- |
303
- | package-json-schema.md | contributes, activationEvents, engines, scripts, devDependencies |
304
- | api-patterns.md | TreeView, Webview, QuickPick, StatusBar, SecretStorage, withProgress |
305
- | architecture.md | Project structure, layered architecture, testing strategy |
306
- | publishing.md | vsce, .vscodeignore, CI/CD, Open VSX, versioning |
300
+ | File | Topics |
301
+ | ---------------------- | ------------------------------------------------------------------------------------------------------------ |
302
+ | api-treeview.md | TreeDataProvider, TreeView registration |
303
+ | api-webview.md | Webview Panel, CSP/nonce, postMessage, asWebviewUri |
304
+ | api-quickpick.md | Simple and async QuickPick with debounced search |
305
+ | api-statusbar.md | StatusBarItem, codicons, dynamic updates |
306
+ | api-secretstorage.md | Credential manager pattern, onDidChange |
307
+ | api-progress.md | withProgress (Notification + Window), cancellation tokens |
308
+ | api-additional.md | FileSystemWatcher, Disposable cleanup, Diagnostics, OutputChannel, ContextKeys, TextDocumentContentProvider |
309
+ | architecture.md | Project structure, layered architecture, testing strategy |
310
+ | package-json-schema.md | contributes, activationEvents, engines, scripts, devDependencies |
311
+ | publishing.md | vsce, .vscodeignore, CI/CD, Open VSX, versioning |
312
+ | lsp.md | LSP client setup, server lifecycle, capabilities, diagnostics |
313
+ | notebooks.md | Notebook serializers, controllers, renderers, output mime types |
314
+ | debugger.md | DAP: descriptor factory, configuration provider, adapter lifecycle |
315
+ | testing.md | Multi-suite test config, workspace fixtures, mocking `vscode`, CI, coverage |
307
316
 
308
317
  ---
309
318
 
@@ -0,0 +1,417 @@
1
+ ---
2
+ allowed-tools: Bash(git:*), Bash(gh:*), Bash(npm version:*), Bash(npm pkg:*), Bash(cat:*), Bash(grep:*), Bash(test:*), Read, Edit, Write, Glob, Grep
3
+ description: Full release workflow — detect project, infer semver bump, update CHANGELOG+README, commit, push. Public repos get a tag + GitHub release (asks before the first ever tag or release); private repos skip both by default.
4
+ argument-hint: [patch|minor|major] (optional; inferred from semantic commits if omitted)
5
+ ---
6
+
7
+ ## Context (read-only — collected automatically)
8
+
9
+ - Working tree status: !`git status --porcelain`
10
+ - Current branch: !`git branch --show-current`
11
+ - Last tag: !`git describe --tags --abbrev=0 2>/dev/null || echo "<no tags>"`
12
+ - Commits since last tag: !`git log --pretty=format:"%h %s" $(git describe --tags --abbrev=0 2>/dev/null)..HEAD 2>/dev/null || git log --pretty=format:"%h %s"`
13
+ - Remote URL: !`git remote get-url origin 2>/dev/null || echo "<no remote>"`
14
+ - Recent commit style: !`git log --pretty=format:"%s" -20`
15
+ - Has gh CLI: !`command -v gh >/dev/null 2>&1 && echo "yes" || echo "no"`
16
+ - Repo visibility (GitHub only): !`gh repo view --json visibility -q .visibility 2>/dev/null || echo "<unknown>"`
17
+ - Versioned files present:
18
+ - package.json: !`test -f package.json && echo "yes" || echo "no"`
19
+ - tiapp.xml: !`test -f tiapp.xml && echo "yes" || echo "no"`
20
+ - composer.json: !`test -f composer.json && echo "yes" || echo "no"`
21
+ - Cargo.toml: !`test -f Cargo.toml && echo "yes" || echo "no"`
22
+ - podspec: !`ls *.podspec 2>/dev/null | head -1 || echo "<none>"`
23
+ - CHANGELOG.md present: !`test -f CHANGELOG.md && echo "yes" || echo "no"`
24
+ - README.md present: !`test -f README.md && echo "yes" || echo "no"`
25
+
26
+ ## Arguments
27
+
28
+ `$ARGUMENTS` may contain `patch`, `minor`, or `major` to override the inferred bump. If empty, infer from the commits above.
29
+
30
+ ## Your task
31
+
32
+ Execute the **release workflow** in five strict, ordered steps. **Do not skip Step 4** — it is the user's confirmation gate and is non-negotiable.
33
+
34
+ ### What this command actually does (read this first)
35
+
36
+ `/release` is **not** "make a release commit from already-staged work". It is the full janitor: it takes a project that has been worked on without commits (or with mixed work + a release intent) and turns it into a clean, shipped release.
37
+
38
+ **Working tree dirty is the expected starting state, not an anomaly.** When the user invokes `/release` with 12 modified files since the last tag, what they want is:
39
+
40
+ 1. Read every modified/untracked file, **understand the intent of each change**, and group them into **N semantic commits** (one feat per logical feature, one fix per logical bug, etc.) — NOT one giant "chore(release)" lump.
41
+ 2. Then bump the version, update CHANGELOG, commit the release metadata, tag, push, and create the GitHub release.
42
+
43
+ So the plan presented to the user is **N+1 commits**: N semantic commits derived from the working tree, plus 1 release commit at the end (bump + CHANGELOG). Both sets feed the CHANGELOG entry and the GitHub release notes.
44
+
45
+ If the working tree is **already clean** (everything is already committed since the last tag), skip the semantic-commit pass and just do the release commit (this is the "git tag + bump only" mode).
46
+
47
+ If the working tree is **completely empty** AND there are zero commits since the last tag → abort, "Nothing to release."
48
+
49
+ ### Verbosity discipline
50
+
51
+ 1. **Steps 1, 2, 3 are silent.** Do all detection, grouping, inference, and composition internally. **Do not print intermediate status, summary tables, or per-step headers.**
52
+ 2. **Step 4 is the only print before confirmation.** One compact block. No sub-section headers ("Files to modify / Commit message / Tag / Release notes / Push targets"). The N proposed commits go inside this block as a numbered list.
53
+ 3. **Step 5 runs the actions silently.** Do not narrate each commit as it lands. The final report is **one line plus the URL**.
54
+ 4. **Genuine anomalies** (branch is not main when expected to be, README/CHANGELOG language disagree, no remote, no `gh`, suspicious files like `.env` or large binaries about to be staged) go as `⚠️` lines **inside** the Step 4 block. A dirty working tree is **not** an anomaly — it is the input.
55
+
56
+ ### Step 0 — Lock interaction language (do this BEFORE printing anything)
57
+
58
+ Before you produce any user-facing output, scan the user's last 1–3 messages in this conversation and determine their language. **This file is in English for distribution; that does not mean you respond in English.** Lock that detected language and use it for **every** message you print to the user from this point until the command ends — Step 1 summary, Step 2 inferred bump, Step 4 plan preview, Step 5 progress, errors, final report.
59
+
60
+ If the user has not yet said anything in this session (rare — `/release` invoked as the very first message), default to the language of the repo's `README.md`. If that is also unclear, default to English.
61
+
62
+ If the user switches language mid-flow, switch with them on the next message.
63
+
64
+ This rule is **Axis 1** of the Language policy below and is non-negotiable. **Axis 2** (the language of the artifacts written to disk and pushed to GitHub) is detected separately in Step 1 from the project's README — see the Language policy section. The two axes are independent: you may speak Spanish to the user about a project whose artifacts are in English, or vice versa.
65
+
66
+ ---
67
+
68
+ ### Step 1 — Establish state (silent)
69
+
70
+ Do all of this **internally**. Do not print a status summary or any "Step 1" header. The detected facts feed Step 4.
71
+
72
+ 1. **Detect version source** (first match wins):
73
+ - `package.json` → npm (`npm version --no-git-tag-version`)
74
+ - `tiapp.xml` → Titanium. Edit only the **top-level** `<version>` element; never touch `<module version="...">` entries.
75
+ - `composer.json` → PHP (edit `"version"` if present; many composer projects are versionless)
76
+ - `Cargo.toml` → Rust (edit `[package] version = "X.Y.Z"`)
77
+ - `*.podspec` → CocoaPods (edit `s.version`)
78
+ - None → **versionless** mode (commit + push + optional tag).
79
+
80
+ 2. **Detect CHANGELOG strategy:** if `CHANGELOG.md` exists and has `## [Unreleased]` → **promote mode**; if exists without it → **generate mode**; if missing → **skip CHANGELOG entirely** (do not create one without asking).
81
+
82
+ 3. **Detect README version references:** grep `README.md` for the current version string and `v\d+\.\d+` badge patterns. Note locations that need updating, or note that none exist.
83
+
84
+ 4. **Detect project artifact language** (Axis 2): scan the first ~50 lines of `README.md` (skip code blocks, badges, identifier-only lines), classify the prose; tie-break with `CHANGELOG.md` then `git log`. Lock this language for the run.
85
+
86
+ 5. **Group the working tree into semantic commits** (the core janitor pass — do this carefully):
87
+
88
+ For each modified, deleted, and untracked file, read enough of the diff or content to understand the **intent** of the change. Then group the files by intent into a list of proposed commits, in the order they should land.
89
+
90
+ **Grouping heuristics:**
91
+ - Files in the same module that implement a single feature → one `feat(scope):` commit.
92
+ - Files that implement a single bug fix → one `fix(scope):` commit.
93
+ - Refactors that don't change behavior → `refactor(scope):` (or `chore` if mechanical).
94
+ - Renames, lint cleanups, formatting passes → `chore(scope):` or `style(scope):`.
95
+ - Documentation-only changes (README, CHANGELOG, docs/) → `docs(scope):`. **Exception:** the release-commit's CHANGELOG/README/version bumps stay in the final release commit, not in a docs commit.
96
+ - Test-only changes → `test(scope):`.
97
+ - Build/CI config → `build(scope):` or `ci(scope):`.
98
+ - Lockfiles (`package-lock.json`, `yarn.lock`, `Gemfile.lock`, `Cargo.lock`, `composer.lock`) follow whichever commit changed the corresponding manifest. If only the lockfile is dirty without a manifest change, group it into the release commit.
99
+ - Mixed-intent files: if a single file legitimately covers two unrelated changes, propose one combined commit and flag it in Step 4 so the user can split it manually if they want — do not silently mis-attribute.
100
+
101
+ **Files to exclude by default** (do not stage, mention them in Step 4 as "excluded"):
102
+ - Screenshots in repo root (`*.png`, `*.jpg`, `*.jpeg`, `Screenshot*.png`, `Captura*.png`) unless they live inside an `assets/`, `docs/`, or similarly-blessed directory.
103
+ - Editor scratch files: `*.swp`, `*.tmp`, `.DS_Store`, `Thumbs.db`.
104
+ - Local-only env files: `.env`, `.env.local`, `*.local.json` if the project's `.gitignore` already excludes them but they appear because of `git add -A`.
105
+ - Credentials, secret keys, large binaries that look unrelated to the project.
106
+ When in doubt, **list the file in the Step 4 plan as "excluded — confirm?"** instead of silently dropping or silently including it.
107
+
108
+ **Output of this step (held internally for Step 4):** an ordered list `[ {type, scope, subject, files[], body?}, ... ]` representing the N semantic commits, plus an "excluded" list.
109
+
110
+ 6. **Detect README documentation gaps** (do NOT skip this — README maintenance is part of the release, not optional):
111
+
112
+ For each proposed semantic commit (Step 1.5) and each existing commit since the last tag, ask: *does this change the project's user-visible surface?* Concretely, flag any of:
113
+
114
+ - A new slash command (`commands/*.md` added).
115
+ - A new CLI subcommand or flag (`bin/*` or `lib/commands/*` added/changed).
116
+ - A new public API export, hook, MCP tool, or skill.
117
+ - A new env var the user must set, a new config file the user must create.
118
+ - A new install path, distribution channel, or compatibility platform.
119
+ - A breaking change in any of the above.
120
+
121
+ For each surface change found, **grep `README.md`** for the new symbol/name/path. If it is **not** mentioned in README:
122
+ - Compose the documentation patch needed (a new row in an existing table, a new section under an existing heading, an updated bullet, etc.).
123
+ - Hold this patch internally for Step 4 — it becomes part of the release commit (along with the version bump and CHANGELOG insert), NOT a separate semantic commit. README updates that document this release ship with the release.
124
+
125
+ For surface changes that *are* already mentioned in README, do nothing.
126
+
127
+ For non-surface changes (internal refactors, fixes that don't change behavior, dependency bumps), do nothing.
128
+
129
+ **Also still update README** for: version badges, install snippets, or any reference to the old version that needs to bump to the new one.
130
+
131
+ 7. **Detect main-alignment state.** Capture: the name of the project's primary branch (`main` or `master` — check `git symbolic-ref refs/remotes/origin/HEAD` or fall back to `main` then `master`), whether the current branch matches it, and whether `main` (locally and on origin) is reachable. This drives the optional merge prompt in Step 4.
132
+
133
+ 8. **Detect repo visibility (drives tag + GitHub-release behavior).** Run `gh repo view --json visibility -q .visibility` if `gh` is available and the remote is GitHub.
134
+
135
+ - `PRIVATE` → **private mode**. **Skip both the git tag and the GitHub release by default.** For a private repo, tags and releases are distribution/marketing artifacts the maintainer typically doesn't need — version bumps live in `package.json`/`tiapp.xml`/etc. and the CHANGELOG. The user can opt back into a tag (still no GitHub release) by appending `con tag` / `with tag` to their Step 4 confirmation.
136
+ - `PUBLIC` or `INTERNAL` → **public mode**. Tag + GitHub release as usual.
137
+ - `gh` unavailable, command fails, or remote isn't GitHub → **unknown**. Behave as public mode for the tag, and skip the GitHub release if `gh` isn't there (existing fallback in Phase 3).
138
+
139
+ Hold the resolved mode (`private` / `public` / `unknown`) internally for Step 4 and Phase 3.
140
+
141
+ **Additionally, when the resolved mode is `public` (or `unknown` with `gh` available), detect first-time milestones** — they will surface as ⚠️ lines in Step 4 so the user can opt out before they're created:
142
+ - **First tag ever:** `git tag --list | head -1` → empty result sets the `first-tag` flag.
143
+ - **First GitHub release ever:** `gh release list --limit 1` → empty stdout sets the `first-release` flag. Only check when `gh` is available and the remote is GitHub.
144
+
145
+ In `private` mode these flags are **not** raised — tag and GitHub release are skipped by default anyway, so there is nothing to confirm.
146
+
147
+ 9. **Note genuine anomalies** for Step 4: no remote, no `gh`, README/CHANGELOG languages disagree, an excluded file is borderline. **Do not** flag a dirty working tree itself — that's expected. **Do not** flag "branch is not main" as an anomaly either — handle it via the dedicated merge prompt below. **Do not** flag "no prior tags" or "no prior releases" as an anomaly on a private repo — in private mode we're not creating either anyway, so it's not a question. On public mode the `first-tag` / `first-release` flags from Step 1.8 are surfaced as their own ⚠️ lines (see Step 4 Part C), not in the generic anomalies list.
148
+
149
+ ---
150
+
151
+ ### Step 2 — Infer the bump (silent)
152
+
153
+ Look at **both** sources of changes and take the highest level:
154
+
155
+ - Existing commits since the last tag (from `git log`).
156
+ - The N **proposed** semantic commits from Step 1.5.
157
+
158
+ Apply Conventional Commits rules across that combined set:
159
+
160
+ - Any `BREAKING CHANGE:` or `!:` → **major**
161
+ - Any `feat:` (no breaking) → **minor**
162
+ - Otherwise → **patch**
163
+
164
+ `$ARGUMENTS` (`patch` / `minor` / `major`) overrides the inference.
165
+
166
+ If there are zero existing commits AND zero proposed commits AND the working tree is clean → abort: "Nothing to release."
167
+
168
+ ---
169
+
170
+ ### Step 3 — Compose the CHANGELOG entry (silent)
171
+
172
+ The CHANGELOG entry covers **everything that will ship** — both the existing commits since the last tag and the N proposed semantic commits from Step 1.5.
173
+
174
+ **Promote mode**: take the existing `[Unreleased]` body, **append** any new bullets derived from the proposed commits that aren't already represented, then rename the heading to `## [X.Y.Z] - YYYY-MM-DD`. Insert a fresh empty `## [Unreleased]` above.
175
+
176
+ **Generate mode**: build the entry from the union of (existing commits since tag) + (proposed semantic commits), grouped by type, in the project artifact language detected in Step 1:
177
+
178
+ ```
179
+ ## [X.Y.Z] - YYYY-MM-DD
180
+
181
+ ### Added
182
+ - <feat: subjects from both sources, prefix stripped>
183
+
184
+ ### Fixed
185
+ - <fix: subjects from both sources>
186
+
187
+ ### Changed
188
+ - <refactor:, perf:, style: subjects>
189
+
190
+ ### Removed
191
+ - <only when explicitly mentioned>
192
+ ```
193
+
194
+ Skip empty sections. Use today's date. Don't double-count when an existing commit and a proposed commit describe the same work — pick the clearer wording.
195
+
196
+ ---
197
+
198
+ ### Language policy (applies to Steps 1–5)
199
+
200
+ There are **two independent language axes**. Do not mix them.
201
+
202
+ #### Axis 1 — Interaction language (your conversation with the user)
203
+
204
+ **Always match the user's language.** Detect from the user's last 1–3 messages in this conversation — not from this command file (which is in English for distribution), and not from the project files. If the user has been speaking Spanish, every status summary, plan preview, confirmation prompt, error message, and final report you print **must be in Spanish**. If the user switches mid-flow, switch with them.
205
+
206
+ This is about what the user **reads on screen**. It does **not** affect what gets written to disk or to GitHub.
207
+
208
+ #### Axis 2 — Project artifact language (everything that gets written or pushed)
209
+
210
+ **Detect the project's audience language from `README.md`. That single decision drives every artifact.** The README is the canonical signal because it defines who the project is for: a Spanish README means a Spanish-speaking audience (typical for private / local projects), an English README means an international audience (typical for open source). Match that language across the board.
211
+
212
+ **Detection procedure** (Step 1 must do this):
213
+
214
+ 1. Read the first ~50 lines of `README.md`, skipping code blocks, badges, shell commands, and identifier-only lines.
215
+ 2. Classify the prose: Spanish, English, or other.
216
+ 3. Tie-break with `CHANGELOG.md` (recent entries) if README is too short or ambiguous.
217
+ 4. Final tie-break: the recent `git log` subjects.
218
+ 5. If everything is ambiguous, default to English and tell the user in the Step 1 summary so they can correct you before Step 4.
219
+
220
+ The detected language applies to **all** of the following:
221
+
222
+ | Artifact | Language |
223
+ | --- | --- |
224
+ | New `CHANGELOG.md` entry | Project language |
225
+ | `README.md` edits (badges, version snippets) | Project language |
226
+ | Commit message (subject + body) | Project language |
227
+ | Tag annotation (`-m "..."`) | Project language |
228
+ | GitHub release title | Project language |
229
+ | GitHub release body / notes | Project language |
230
+
231
+ **No mixed-language releases.** If the README is in Spanish, the commit, tag, release title, and release body are all in Spanish. If the README is in English, all of them in English. The CHANGELOG entry being shipped also matches.
232
+
233
+ **Why this works:** the README is the project's "front door" and reflects the audience the maintainer chose. Aligning every public artifact (commits, tags, releases) to that same audience avoids the awkward private-project-with-English-commits or open-source-with-Spanish-tags split. Open source in English keeps coverage broad; private/local in Spanish keeps the team comfortable.
234
+
235
+ #### Spanish content rule (whenever writing Spanish, on either axis)
236
+
237
+ Preserve correct accents and eñes: sesión, electrónico, también, código, configuración, después, año, español, versión, añadir, corrección, contraseña. Do not strip or mangle tildes. The user has a standing rule about this and will catch it.
238
+
239
+ #### Overrides
240
+
241
+ If the user explicitly tells you to use a specific language for the artifacts (e.g. "haz los commits en inglés aunque el README esté en español"), that overrides Axis 2 detection for this run only.
242
+
243
+ ---
244
+
245
+ ### Step 4 — Present the plan in ONE block and STOP
246
+
247
+ This is your **only** print to the user before confirmation. One block. Compact. No tables, no per-section sub-headers. The block is shown in the user's language (Axis 1); the artifacts quoted inside use the project language (Axis 2).
248
+
249
+ The block has three parts (in this order, in one continuous block):
250
+
251
+ **Part A — header line + anomalies**
252
+ ```
253
+ **Release plan: vA.B.C → vX.Y.Z** (<bump> — <project type>, artifacts en <language>, branch <name>)
254
+
255
+ ⚠️ <one line per genuine anomaly> ← only if any; do NOT flag dirty working tree
256
+ ```
257
+
258
+ **Part B — semantic commits from the working tree** (omit this part entirely if the working tree was already clean)
259
+ ```
260
+ N commits semánticos antes del release:
261
+
262
+ 1. <type>(<scope>): <subject>
263
+ • <files>
264
+ 2. ...
265
+ N. ...
266
+
267
+ Excluded: <files the user should confirm or none>
268
+ ```
269
+
270
+ **Part C — release commit + tag + push**
271
+ ```
272
+ CHANGELOG entry:
273
+
274
+ ## [X.Y.Z] - YYYY-MM-DD
275
+ ### Added
276
+ - ...
277
+ ### Fixed
278
+ - ...
279
+
280
+ README updates (gaps found): <one-line per gap, e.g. "add /release row to Available commands table + new section"> ← omit this line if no gaps
281
+ Release commit: bumps <version-file> A.B.C→X.Y.Z, inserts CHANGELOG section, applies README updates. Subject: `<exact line>`.
282
+ Push: release commit to <branch>.
283
+ <one of the three rendering modes below — pick by visibility>
284
+ • Public / internal repo: `Tag + GitHub release: vX.Y.Z to <branch> + release with CHANGELOG notes.`
285
+ • Private repo (default): `Repo privado → omitiendo tag y GitHub release. (responde "con tag" si quieres crear el tag de todos modos)` — localize to user's language.
286
+ • Unknown (no gh / non-GitHub remote): `Tag + push: vX.Y.Z to <branch>. GitHub release: skipped (no gh / non-GitHub remote).`
287
+
288
+ ⚠️ First tag for this repo (no prior tags found). Confirming creates `vX.Y.Z` as the first ever tag. ← include ONLY when the `first-tag` flag from Step 1.8 is set (public mode); localize to user's language
289
+ ⚠️ First GitHub release for this repo (no prior releases found). Confirming creates the first ever release. ← include ONLY when the `first-release` flag from Step 1.8 is set (public mode, `gh` available); localize to user's language
290
+
291
+ Branch <branch> → <main-branch>? (default: no; responde "merge" para fast-forward, "PR" para pull request) ← include ONLY when current branch ≠ main/master; localize to user's language
292
+
293
+ ¿Procedo? ← in the user's language
294
+ ```
295
+
296
+ **Rules for this block:**
297
+
298
+ - The CHANGELOG entry is shown **once**. Don't paste it again under "Release notes" — say "notes = CHANGELOG section above".
299
+ - The N semantic commits are listed compactly: type + scope + subject on one line, files on the next. No separate body text per commit unless a commit truly needs one.
300
+ - "Excluded" lists files you decided not to stage (screenshots in root, scratch files, suspicious binaries) so the user can override your decision before confirming.
301
+ - A single bold "Release plan: ..." is the only header. No "Step 4 — Plan" preamble.
302
+ - The closing question is in the user's language: `¿Procedo?` (Spanish), `Proceed?` (English), etc.
303
+ - If there is something genuinely ambiguous (a file that could be either feat or fix, a possibly-sensitive file, a branch-vs-main question), put it in `⚠️` and ask the user to clarify in the same message — do not block on a separate round-trip if you can present the question alongside the plan.
304
+
305
+ Then **stop and wait**. Do **not** run any `git add`, `git commit`, `git push`, `git tag`, `gh release`, `npm version`, `Edit`, or `Write` calls in this step.
306
+
307
+ Acceptable confirmations:
308
+
309
+ - **Without merge** (default): `yes`, `sí`, `commitea`, `adelante`, `proceed`, `go`, `ship it`. → execute Phases 1–3 of Step 5 only.
310
+ - **With fast-forward merge** to main/master: `merge`, `sí merge`, `proceed and merge`, `merge yes`, `ship and merge`. → execute Phases 1–3, then Phase 4 with `mode=merge`.
311
+ - **With PR**: `pr`, `PR`, `abrir PR`, `proceed PR`, `release and PR`. → execute Phases 1–3, then Phase 4 with `mode=pr`. Requires `gh` available.
312
+ - **Force tag on a private repo** (modifier, combinable with any of the above): append `con tag` / `with tag` / `tag yes`. → flips the resolved mode for this run from `private` to `public` for tag-creation purposes only (still no GitHub release on a private repo). Example: `sí con tag`, `proceed with tag`, `merge con tag`.
313
+
314
+ Anything else (silence, partial yes, "let me check", questions) means **do not proceed**. If the user proposes changes (e.g. "merge commits 2 and 3", "split commit 5", "skip commit 7", "different subject for #4"), apply them and re-present a refreshed Step 4 block before executing. The merge-mode confirmation can also be revised — e.g. user says "yes but no merge" → mode=none; "merge actually" after an initial plain yes → re-confirm before doing it.
315
+
316
+ ---
317
+
318
+ ### Step 5 — Execute (silent until done)
319
+
320
+ Run the actions below **without** narrating each one. No "committing 1/7...", "staging files...", "pushing tag..." progress messages. The user sees one final report at the end.
321
+
322
+ If any step fails, **stop** and surface the failure with a diagnosis — do not attempt destructive recovery and do not skip ahead.
323
+
324
+ **Phase 1 — Land the N semantic commits from the working tree (skip if working tree was already clean).**
325
+
326
+ For each commit `i` in the proposed list, in order:
327
+
328
+ 1. `git add <exact files for commit i>` (use explicit paths — do **not** `git add -A` here; that would mix unrelated files into a single commit).
329
+ 2. `git commit -m "<type>(<scope>): <subject>"`. Use a HEREDOC if there's a multi-line body. **Never** `--no-verify`. If a pre-commit hook fails, fix the root cause, re-stage the same files, and retry the same commit (do not silently skip and do not `--amend`).
330
+
331
+ After Phase 1, the working tree should be clean except for files explicitly listed as "Excluded" in Step 4.
332
+
333
+ **Phase 2 — Release commit.**
334
+
335
+ 1. **Bump the version file:**
336
+ - npm: `npm version <patch|minor|major> --no-git-tag-version`
337
+ - tiapp.xml: `Edit` only the **top-level** `<version>` element. Verify no `<module version=...>` matches in your edit.
338
+ - composer.json: `Edit` `"version"` if present; skip if absent.
339
+ - Cargo.toml: `Edit` `[package] version = "X.Y.Z"`.
340
+ - podspec: `Edit` `s.version`.
341
+ - Versionless: skip.
342
+
343
+ 2. **Update `CHANGELOG.md`** with the Step 3 entry. Promote `[Unreleased]` or insert above the most recent version section.
344
+
345
+ 3. **Update `README.md`** with everything identified in Step 1.6:
346
+ - The documentation gap patches (new command rows, new sections, updated tables) so the README reflects the user-visible surface being shipped in this release.
347
+ - Plus any version badge / install snippet that referenced the old version.
348
+ Apply with `Edit` per location. Skip only if Step 1.6 found nothing.
349
+
350
+ 4. **Stage exactly the release-commit files** (the bumped version file + lockfile if `npm version` updated it + `CHANGELOG.md` + `README.md` if changed). Use explicit `git add <paths>`, not `git add -A`.
351
+
352
+ 5. **Commit:** `git commit -m "chore(release): vX.Y.Z"` (or the project's release-commit convention).
353
+
354
+ **Phase 3 — Push, tag, GitHub release.**
355
+
356
+ The behavior of steps 2 and 3 depends on the visibility resolved in Step 1.8:
357
+
358
+ 1. `git push origin <current-branch>` — always (regardless of visibility).
359
+ 2. Tag — **skip on private repos** unless the user appended `con tag` / `with tag` to their confirmation. Otherwise (public / internal / unknown / forced): only if a remote exists and a version was bumped (or user opted into a versionless tag):
360
+ - `git tag -a vX.Y.Z -m "Release vX.Y.Z"` ← annotation in project language
361
+ - `git push origin vX.Y.Z`
362
+ 3. GitHub release — **skip on private repos**, period (no override). For private repos, tags and releases aren't required and the user typically doesn't want them; the `con tag` modifier creates the tag only, never the GitHub release. Otherwise, if `gh` is available and remote is GitHub: `gh release create vX.Y.Z --title "vX.Y.Z" --notes "<CHANGELOG section>"` (HEREDOC for notes).
363
+
364
+ **Phase 4 — Optional main alignment** (only if user confirmed `merge` or `PR`).
365
+
366
+ For `mode=merge` (fast-forward only):
367
+
368
+ 1. `git fetch origin` to make sure local `main` knows about origin.
369
+ 2. `git checkout <main-branch>`.
370
+ 3. `git pull --ff-only origin <main-branch>` — bring local main up to date.
371
+ 4. `git merge --ff-only <feature-branch>` — must be fast-forward. If main has diverged (someone else merged in the meantime), this fails cleanly. **Do not** retry with `--no-ff` or any other strategy: abort, leave the user on `<main-branch>`, and tell them to resolve manually. The release on the feature branch is intact regardless.
372
+ 5. `git push origin <main-branch>`.
373
+ 6. **Stay on `<main-branch>`.** Do NOT `git checkout` back to the feature branch. Confirming `merge` is the user's signal that the feature branch is done — landing them on main matches that intent and avoids a silent branch-switch after a "ship it" action. The feature branch still exists locally; the user can `git branch -d <feature-branch>` when ready. (PR mode is different — see below — because the branch is still live.)
374
+
375
+ For `mode=pr` (open a PR via `gh`):
376
+
377
+ 1. Compose the PR title from the release: `Release vX.Y.Z` (in project artifact language).
378
+ 2. Compose the PR body from the CHANGELOG entry (the `## [X.Y.Z]` section).
379
+ 3. `gh pr create --base <main-branch> --head <feature-branch> --title "..." --body "..."` (HEREDOC for body).
380
+ 4. Capture the PR URL from `gh` stdout.
381
+
382
+ If `mode=none` (default), skip this phase entirely.
383
+
384
+ If a Phase 4 step fails, the release itself is already shipped (Phases 1–3 succeeded). Surface the merge/PR failure as a non-fatal note in the final report — do not unwind the release.
385
+
386
+ **Phase 5 — Final report (one line plus URL).**
387
+
388
+ ```
389
+ ✓ vX.Y.Z → <release URL>
390
+ N+1 commits on <feature-branch>, latest <short hash> · <optional one-line note>
391
+ ```
392
+
393
+ The "optional note" only appears if something was non-routine — examples:
394
+ - `merged to main; now on <main-branch>` (when Phase 4 ran successfully with mode=merge; include the new main HEAD short hash)
395
+ - `PR opened: <pr-url>` (when Phase 4 ran with mode=pr)
396
+ - `merge to main aborted: main has diverged — resolve manually`
397
+ - `tag is on feature branch; main not aligned`
398
+ - `GitHub release skipped (no gh)`
399
+ - `excluded file left in working tree: screenshot.png`
400
+
401
+ If everything was routine and no merge was requested, the second line is just the commit summary.
402
+
403
+ ---
404
+
405
+ ## Hard restrictions (never violate)
406
+
407
+ - **Never** `git push --force` or `git push -f`.
408
+ - **Never** `git commit --amend` on commits that may already be pushed.
409
+ - **Never** `--no-verify`, `--no-gpg-sign`, or any hook-skipping flag unless the user explicitly asks.
410
+ - **Never** delete tags or branches.
411
+ - **Never** merge to main with anything other than `--ff-only`. If fast-forward is not possible, abort and let the user resolve. Do not fall back to `--no-ff`, `-X theirs`, `-X ours`, or any rebase strategy.
412
+ - **Never** proceed past Step 4 without explicit confirmation. The slash command itself is consent to **invoke**, not consent to commit and push.
413
+ - If the working tree has merge conflicts or rebase-in-progress markers → **abort** with a diagnosis and let the user resolve manually.
414
+ - If the repo is **public/internal** and has **no prior tag** and/or **no prior GitHub release**, surface a ⚠️ line in the Step 4 plan (one per missing milestone) before creating the first ever tag or release — these are meaningful one-way actions. The user's plain confirmation in Step 4 (`yes` / `sí` / `proceed` / …) covers both ⚠️s in that single round-trip; do not require a separate confirmation prompt. On a **private repo** these confirmations are unnecessary and must NOT be raised — private mode skips both the tag and the GitHub release by default, so there's nothing to confirm. The user already opted in/out via the `con tag` modifier in Step 4 (and even then, the GitHub release stays skipped on private repos).
415
+ - If the repo has no remote → run Steps 5.1–5.5 only; skip push, tag, and release. Tell the user.
416
+ - If the repo has a remote but `gh` is not available → run through 5.7's tag step but skip the GitHub release; tell the user how to create the release manually if they want to.
417
+ - **Never** create a GitHub release on a private repo, even if the user added `con tag` to the confirmation. The `con tag` modifier only re-enables the git tag; the GitHub release stays skipped.
@@ -0,0 +1,105 @@
1
+ /**
2
+ * Detection of the maccesar-aiskills Claude Code marketplace plugin.
3
+ *
4
+ * When the plugin is installed, Claude Code already lists our skills and slash
5
+ * commands from its plugin cache, so the CLI must not also install its own copy
6
+ * into ~/.claude/ — that produces duplicate entries in the autocomplete.
7
+ *
8
+ * The subtlety, and the reason this lives in its own module: **the cache on disk
9
+ * does not mean the plugin is installed.** Uninstalling a plugin removes it from
10
+ * `enabledPlugins` in settings.json but leaves the cache directory behind.
11
+ * Treating that leftover directory as proof of installation makes the CLI skip
12
+ * work it should do, which leaves Claude Code with no skills at all and no way
13
+ * for the user to repair it by re-running install. So the question we answer here
14
+ * is "is the plugin enabled AND does it carry this file", never just the latter.
15
+ */
16
+
17
+ import { existsSync, readFileSync, readdirSync } from 'fs';
18
+ import { join } from 'path';
19
+ import {
20
+ CLAUDE_PLUGIN_KEY,
21
+ getClaudePluginSkillsPath,
22
+ getClaudeSettingsPaths,
23
+ } from './config.js';
24
+
25
+ /**
26
+ * Whether Claude Code currently has the aiskills plugin enabled.
27
+ * @param {string} baseDir - Optional base directory (defaults to homedir via config)
28
+ * @returns {boolean} True only when a settings file explicitly enables the plugin
29
+ */
30
+ export function isClaudePluginEnabled(baseDir) {
31
+ for (const settingsPath of getClaudeSettingsPaths(baseDir)) {
32
+ try {
33
+ const settings = JSON.parse(readFileSync(settingsPath, 'utf8'));
34
+ if (settings?.enabledPlugins?.[CLAUDE_PLUGIN_KEY] === true) {
35
+ return true;
36
+ }
37
+ } catch {
38
+ // Missing or malformed settings: nothing here says the plugin is enabled.
39
+ // Falling through to `false` is the safe direction — the cost of a wrong
40
+ // `false` is a duplicate entry, the cost of a wrong `true` is a user with
41
+ // no skills installed.
42
+ }
43
+ }
44
+ return false;
45
+ }
46
+
47
+ /**
48
+ * Whether a plugin cache directory exists at all, regardless of whether the
49
+ * plugin is enabled. An enabled plugin implies a cache; a cache implies nothing,
50
+ * because uninstalling leaves it behind. Diagnostics use this to tell "never
51
+ * installed" apart from "uninstalled, leftovers on disk".
52
+ * @param {string} baseDir - Optional base directory
53
+ * @returns {boolean}
54
+ */
55
+ export function hasClaudePluginCache(baseDir) {
56
+ return existsSync(getClaudePluginSkillsPath(baseDir));
57
+ }
58
+
59
+ /**
60
+ * Whether any cached version of the plugin carries the given file.
61
+ * @param {string} kind - Subdirectory inside the plugin ('skills' or 'commands')
62
+ * @param {string} entry - Entry to look for (skill directory or command file)
63
+ * @param {string} baseDir - Optional base directory
64
+ * @returns {boolean} True if a cached version contains it
65
+ */
66
+ function pluginCacheContains(kind, entry, baseDir) {
67
+ const pluginBase = getClaudePluginSkillsPath(baseDir);
68
+ if (!existsSync(pluginBase)) return false;
69
+ try {
70
+ return readdirSync(pluginBase).some((version) =>
71
+ existsSync(join(pluginBase, version, kind, entry))
72
+ );
73
+ } catch {
74
+ return false;
75
+ }
76
+ }
77
+
78
+ /**
79
+ * Whether the installed plugin already provides this skill to Claude Code.
80
+ * @param {string} skillName - Skill name (e.g. 'session-log')
81
+ * @param {string} baseDir - Optional base directory
82
+ * @returns {boolean}
83
+ */
84
+ export function pluginProvidesSkill(skillName, baseDir) {
85
+ return isClaudePluginEnabled(baseDir) && pluginCacheContains('skills', skillName, baseDir);
86
+ }
87
+
88
+ /**
89
+ * Whether the installed plugin already provides this slash command.
90
+ * @param {string} commandName - Command name without the .md extension
91
+ * @param {string} baseDir - Optional base directory
92
+ * @returns {boolean}
93
+ */
94
+ export function pluginProvidesCommand(commandName, baseDir) {
95
+ return (
96
+ isClaudePluginEnabled(baseDir) && pluginCacheContains('commands', `${commandName}.md`, baseDir)
97
+ );
98
+ }
99
+
100
+ export default {
101
+ isClaudePluginEnabled,
102
+ hasClaudePluginCache,
103
+ pluginProvidesSkill,
104
+ pluginProvidesCommand,
105
+ };