@maccesar/aiskills 1.21.0 → 1.22.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
@@ -8,7 +8,7 @@
8
8
 
9
9
  `aiskills` is a toolkit of curated skills for AI coding assistants. It provides skill files for Claude Code, Gemini CLI, or Codex CLI.
10
10
 
11
- Each skill is a small knowledge package: a `SKILL.md` file with YAML frontmatter plus a set of reference files. When a prompt matches the skill, the assistant reads those files and answers from the source material.
11
+ Each skill is a small knowledge package: a `SKILL.md` file with YAML frontmatter plus optional references, scripts, and invocation policy. Most activate when a prompt matches; sensitive workflows such as `release` can require explicit invocation.
12
12
 
13
13
  ---
14
14
 
@@ -73,6 +73,7 @@ All three platforms use the same Agent Skills format: a `SKILL.md` file with YAM
73
73
  | vscode-extension-dev | VS Code | VS Code Extension API docs | 14 files |
74
74
  | stitch-showcase | Design Tools | Google Stitch export workflow | 16 files |
75
75
  | session-log | Project | Convention + 3 A/B rounds | 2 files |
76
+ | release | Publishing | Portable, confirmation-gated workflow | 1 file |
76
77
  | seo-launch | Web / SEO | Head tags, share cards, server files | 5 files |
77
78
  | npm-supply-chain | npm / CI | npm and GitHub changelogs, 2025–2026 | 5 files |
78
79
 
@@ -80,20 +81,22 @@ Use `aiskills list` to see available skills from the command line. Pull requests
80
81
 
81
82
  ---
82
83
 
83
- ## Available commands
84
+ ## Explicit-only release skill
84
85
 
85
- Slash commands are Claude Code-only. They ship with the plugin (Option A). The CLI distribution (Option B) installs skills only.
86
+ End-to-end release janitor that works across project types: npm, Titanium (`tiapp.xml`), Composer, Cargo, CocoaPods, or versionless (git-tag-only) repos. **Designed for a dirty working tree** it groups your uncommitted work into semantic commits, then ships the release on top.
86
87
 
87
- | Command | Purpose |
88
- | ---------- | ------------------------------------------------------------------------------ |
89
- | `/release` | Full release workflow: detect project, bump semver, update CHANGELOG + README, commit, push, tag, GitHub release |
88
+ Unlike the other skills, `release` is never supposed to activate merely because a repository looks ready. Name it explicitly:
90
89
 
91
- ### /release
90
+ | Platform | Invocation |
91
+ | --- | --- |
92
+ | Claude Code | `/release [patch\|minor\|major]` |
93
+ | Codex CLI | `$release [patch\|minor\|major]` |
94
+ | Gemini CLI | `Use the release skill [with a patch\|minor\|major bump]` |
92
95
 
93
- End-to-end release janitor that works across project types: npm, Titanium (`tiapp.xml`), Composer, Cargo, CocoaPods, or versionless (git-tag-only) repos. **Designed for a dirty working tree** it groups your uncommitted work into semantic commits, then ships the release on top.
96
+ Codex carries platform-specific metadata that disables implicit invocation. Claude Code has its own equivalent frontmatter field, but adding it to this universal `SKILL.md` makes Codex reject the skill; the common body therefore enforces the boundary for Claude and Gemini: the prompt must name `release` before the workflow may proceed. Gemini still asks for activation consent. Every platform then stops again at the release plan and requires a second explicit confirmation before any mutation.
94
97
 
95
98
  When to use it:
96
- - You have weeks of work in the working tree (with maybe a few interim commits you made along the way) and want one command to clean everything into proper semantic commits and ship a release.
99
+ - You have weeks of work in the working tree (with maybe a few interim commits you made along the way) and want one workflow to clean everything into proper semantic commits and ship a release.
97
100
  - You maintain `CHANGELOG.md` in Keep-a-Changelog format and want the `[Unreleased]` section promoted automatically.
98
101
  - You want the bump level inferred from Conventional Commits across both your existing commits and the proposed new ones, with the option to override.
99
102
 
@@ -101,7 +104,8 @@ Example prompts:
101
104
  ```
102
105
  /release
103
106
  /release minor
104
- /release major
107
+ $release major
108
+ Use the release skill with a patch bump
105
109
  ```
106
110
 
107
111
  How it works:
@@ -128,15 +132,16 @@ Language policy (two independent axes):
128
132
  Hard restrictions:
129
133
  - Never `--force-push`, `--amend` published commits, or `--no-verify`.
130
134
  - Aborts on merge conflicts or rebase-in-progress.
131
- - Asks before creating the **first** tag on `main` / `master`.
135
+ - Warns before creating the first tag or GitHub release in a public/internal repository.
132
136
  - Skips push / tag / release gracefully when the repo has no remote or `gh` is not installed.
133
137
 
134
138
  Distribution note:
135
- - Available via the plugin install (Option A above). Slash commands are not distributed by the npm CLI (Option B) because they are a Claude Code feature.
139
+ - The release workflow now ships through both installation options as one Agent Skill. The former Claude-only `commands/release.md` is removed automatically on the next `aiskills update` or `aiskills install`.
140
+ - If npm updates before an enabled Claude marketplace cache, the CLI keeps the old plugin command as the temporary `/release` provider and suppresses the new same-name mirror rather than creating a duplicate. Refresh with `/plugin marketplace update maccesar-aiskills`, run `aiskills install`, then `/reload-plugins` to finish the handoff to the skill.
136
141
 
137
142
  ## How skills work
138
143
 
139
- Skills activate based on what you ask. You can write prompts normally:
144
+ Most skills activate based on what you ask. You can write prompts normally:
140
145
 
141
146
  ```
142
147
  "How do I create better visual hierarchy in this UI?"
@@ -146,7 +151,7 @@ Skills activate based on what you ask. You can write prompts normally:
146
151
 
147
152
  The assistant reads the skill's `SKILL.md`, checks whether the request fits, and then loads the reference files for that skill. That keeps the answer tied to the source material.
148
153
 
149
- You do not need to name a skill explicitly, though you still can if you want to force a specific one.
154
+ You do not normally need to name a skill explicitly. `release` is the deliberate exception because it can commit, tag, push, and publish; invoke it using the platform-specific form documented above.
150
155
 
151
156
  ---
152
157
 
@@ -9,8 +9,10 @@ import { join } from 'path';
9
9
  import os from 'os';
10
10
  import {
11
11
  SKILLS,
12
+ COMMANDS,
12
13
  PACKAGE_VERSION,
13
14
  getAgentsSkillsDir,
15
+ getClaudeCommandsDir,
14
16
  getConfigDir,
15
17
  getPlatforms,
16
18
  } from '../config.js';
@@ -20,6 +22,7 @@ import {
20
22
  isClaudePluginEnabled,
21
23
  hasClaudePluginCache,
22
24
  pluginProvidesSkill,
25
+ pluginProvidesCommand,
23
26
  } from '../claude-plugin.js';
24
27
 
25
28
  const CHECK = chalk.green('✓');
@@ -30,6 +33,7 @@ export async function doctorCommand() {
30
33
  const homeDir = os.homedir();
31
34
  const skillsDir = getAgentsSkillsDir(homeDir);
32
35
  const claudeDir = join(homeDir, '.claude');
36
+ const commandsDir = getClaudeCommandsDir(homeDir);
33
37
  const cacheDir = getConfigDir();
34
38
 
35
39
  let issues = 0;
@@ -59,6 +63,29 @@ export async function doctorCommand() {
59
63
  issues += missingSkills.length;
60
64
  }
61
65
 
66
+ // Slash commands the enabled marketplace plugin serves are intentionally
67
+ // absent from ~/.claude/commands/ to avoid duplicate autocomplete entries.
68
+ const missingCommands = [];
69
+ const pluginCommands = [];
70
+ for (const command of COMMANDS) {
71
+ if (pluginProvidesCommand(command, homeDir)) {
72
+ pluginCommands.push(command);
73
+ } else if (!existsSync(join(commandsDir, `${command}.md`))) {
74
+ missingCommands.push(command);
75
+ }
76
+ }
77
+ const expectedCommands = COMMANDS.length - pluginCommands.length;
78
+ if (COMMANDS.length === 0) {
79
+ console.log(` ${CHECK} Slash commands: none (workflows ship as cross-agent skills)`);
80
+ } else if (pluginCommands.length === COMMANDS.length) {
81
+ console.log(` ${CHECK} Slash commands: all ${COMMANDS.length} served by the marketplace plugin`);
82
+ } else if (missingCommands.length === 0) {
83
+ console.log(` ${CHECK} Slash commands: ${expectedCommands}/${expectedCommands} installed in ~/.claude/commands/`);
84
+ } else {
85
+ console.log(` ${CROSS} Slash commands: ${expectedCommands - missingCommands.length}/${expectedCommands} installed (missing: ${missingCommands.join(', ')})`);
86
+ issues += missingCommands.length;
87
+ }
88
+
62
89
  // Hook check
63
90
  if (hasHook(claudeDir)) {
64
91
  console.log(` ${CHECK} Hook: SessionStart configured`);
@@ -88,10 +115,13 @@ export async function doctorCommand() {
88
115
 
89
116
  for (const skill of SKILLS) {
90
117
  // A skill the marketplace plugin provides is *supposed* to have no mirror
91
- // here — the CLI removes it on purpose to avoid a duplicate entry. Counting
92
- // it as missing turns a healthy marketplace install into a wall of errors
93
- // telling the user to run a command that will correctly do nothing.
94
- if (platform.name === 'claude' && pluginProvidesSkill(skill, homeDir)) {
118
+ // here — the CLI removes it on purpose to avoid a duplicate entry. During
119
+ // command-to-skill migrations, a same-name command in an older enabled
120
+ // cache also suppresses the mirror until the marketplace refreshes.
121
+ if (
122
+ platform.name === 'claude' &&
123
+ (pluginProvidesSkill(skill, homeDir) || pluginProvidesCommand(skill, homeDir))
124
+ ) {
95
125
  servedByPlugin.push(skill);
96
126
  continue;
97
127
  }
@@ -304,11 +304,21 @@ export async function skillsCommand(options) {
304
304
  skillsToInstall,
305
305
  baseDir
306
306
  );
307
- if (symlinkResult.linked.length === skillsToInstall.length) {
308
- spinner.succeed(`${platform.displayName}: Skills linked`);
307
+ const expected = skillsToInstall.length - symlinkResult.skipped.length;
308
+ const pluginNote =
309
+ symlinkResult.skipped.length > 0
310
+ ? ` (${symlinkResult.skipped.length} served by the marketplace plugin)`
311
+ : '';
312
+
313
+ if (expected === 0) {
314
+ spinner.info(
315
+ `${platform.displayName}: All ${symlinkResult.skipped.length} skills served by the marketplace plugin`
316
+ );
317
+ } else if (symlinkResult.linked.length === expected) {
318
+ spinner.succeed(`${platform.displayName}: Skills linked${pluginNote}`);
309
319
  } else {
310
320
  spinner.warn(
311
- `${platform.displayName}: ${symlinkResult.linked.length}/${skillsToInstall.length} skills linked`
321
+ `${platform.displayName}: ${symlinkResult.linked.length}/${expected} skills linked${pluginNote}`
312
322
  );
313
323
  }
314
324
  }
package/lib/config.js CHANGED
@@ -31,6 +31,7 @@ export const SKILLS = [
31
31
  'humaniza',
32
32
  'npm-supply-chain',
33
33
  'refactoring-ui',
34
+ 'release',
34
35
  'seo-launch',
35
36
  'session-log',
36
37
  'stitch-showcase',
@@ -41,18 +42,17 @@ export const SKILLS = [
41
42
  export const LEGACY_SKILLS = [];
42
43
 
43
44
  // Slash commands to install (Claude Code only — copied to ~/.claude/commands/)
44
- export const COMMANDS = [
45
- 'release',
46
- ];
45
+ export const COMMANDS = [];
47
46
 
48
47
  // Legacy commands to remove during updates/uninstall
49
- export const LEGACY_COMMANDS = [];
48
+ export const LEGACY_COMMANDS = ['release'];
50
49
 
51
50
  // Cache/config directory
52
51
  export const getConfigDir = () => path.join(os.homedir(), '.aiskills');
53
52
 
54
53
  // Directory paths
55
- export const getAgentsSkillsDir = (baseDir = os.homedir()) => path.join(baseDir, '.agents', 'skills');
54
+ export const getAgentsDir = (baseDir = os.homedir()) => path.join(baseDir, '.agents');
55
+ export const getAgentsSkillsDir = (baseDir = os.homedir()) => path.join(getAgentsDir(baseDir), 'skills');
56
56
  export const getClaudeSkillsDir = (baseDir = os.homedir()) => path.join(baseDir, '.claude', 'skills');
57
57
  export const getClaudeCommandsDir = (baseDir = os.homedir()) => path.join(baseDir, '.claude', 'commands');
58
58
  export const getGeminiSkillsDir = (baseDir = os.homedir()) => path.join(baseDir, '.gemini', 'skills');
@@ -111,11 +111,17 @@ export default {
111
111
  COMMANDS,
112
112
  LEGACY_COMMANDS,
113
113
  getConfigDir,
114
+ getAgentsDir,
114
115
  getAgentsSkillsDir,
115
116
  getClaudeSkillsDir,
116
117
  getClaudeCommandsDir,
117
118
  getGeminiSkillsDir,
118
119
  getCodexSkillsDir,
120
+ CLAUDE_PLUGIN_MARKETPLACE,
121
+ CLAUDE_PLUGIN_NAME,
122
+ CLAUDE_PLUGIN_KEY,
123
+ getClaudePluginSkillsPath,
124
+ getClaudeSettingsPaths,
119
125
  getPlatforms,
120
126
  GITHUB_API_HEADERS,
121
127
  };
package/lib/installer.js CHANGED
@@ -18,6 +18,7 @@ import {
18
18
  getClaudeCommandsDir,
19
19
  } from './config.js';
20
20
  import { removeSkills, removeCommands } from './cleanup.js';
21
+ import { createSymlinkOrCopy } from './symlink.js';
21
22
 
22
23
  /**
23
24
  * Recursively copy a directory
@@ -37,6 +38,8 @@ export async function copyDirectory(src, dest) {
37
38
  * @param {string} repoDir - Repository directory
38
39
  * @param {string} skillName - Name of the skill
39
40
  * @param {string} baseDir - Base directory for installation
41
+ * Development checkouts are linked so `npm link` users see edits immediately.
42
+ * Published npm packages are copied so installed skills remain independent.
40
43
  * @returns {Promise<boolean>} True if installed successfully
41
44
  */
42
45
  export async function installSkill(repoDir, skillName, baseDir = os.homedir()) {
@@ -52,6 +55,10 @@ export async function installSkill(repoDir, skillName, baseDir = os.homedir()) {
52
55
  return false;
53
56
  }
54
57
 
58
+ if (existsSync(join(repoDir, '.git'))) {
59
+ return createSymlinkOrCopy(skillSrc, skillDest, true);
60
+ }
61
+
55
62
  if (existsSync(skillDest)) {
56
63
  await remove(skillDest);
57
64
  }
package/lib/symlink.js CHANGED
@@ -83,8 +83,8 @@ async function removePath(path) {
83
83
  * @returns {Promise<boolean>} True if the plugin provides this skill
84
84
  */
85
85
  export async function isClaudePluginSkillInstalled(skillName, baseDir) {
86
- const { pluginProvidesSkill } = await import('./claude-plugin.js');
87
- return pluginProvidesSkill(skillName, baseDir);
86
+ const { pluginProvidesSkill, pluginProvidesCommand } = await import('./claude-plugin.js');
87
+ return pluginProvidesSkill(skillName, baseDir) || pluginProvidesCommand(skillName, baseDir);
88
88
  }
89
89
 
90
90
  /**
@@ -118,8 +118,9 @@ export async function createSkillSymlinks(platformSkillsDir, skills, baseDir) {
118
118
  const useRelative = !!baseDir;
119
119
 
120
120
  for (const skill of skills) {
121
- // Skip Claude when the marketplace plugin already provides this skill;
122
- // clean up any stale symlink left from a previous CLI install.
121
+ // Skip Claude when the marketplace plugin already provides this skill or
122
+ // still provides a same-name legacy command during a command-to-skill
123
+ // migration; clean up any stale symlink left from a previous CLI install.
123
124
  if (isClaudePlatform && await isClaudePluginSkillInstalled(skill, baseDir)) {
124
125
  const stalePath = join(platformSkillsDir, skill);
125
126
  if (existsSync(stalePath)) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@maccesar/aiskills",
3
- "version": "1.21.0",
3
+ "version": "1.22.0",
4
4
  "description": "AI coding assistant skills for Claude Code, Gemini CLI, and Codex CLI",
5
5
  "type": "module",
6
6
  "bin": {
@@ -20,6 +20,7 @@
20
20
  "codex",
21
21
  "llm",
22
22
  "agents",
23
+ "release",
23
24
  "refactoring-ui",
24
25
  "design",
25
26
  "ui",
@@ -56,6 +57,8 @@
56
57
  "bin/",
57
58
  "lib/",
58
59
  "skills/",
60
+ "!skills/**/__pycache__/",
61
+ "!skills/**/*.py[cod]",
59
62
  "commands/"
60
63
  ],
61
64
  "publishConfig": {
@@ -0,0 +1,26 @@
1
+ ---
2
+ name: release
3
+ description: 'Use only when the user explicitly invokes `$release`, `/release`, or asks to use the release skill by name. Never activate merely because work appears ready or the user discusses releases. Handles the full repository release workflow: semantic commits, semver, CHANGELOG and README updates, confirmation, push, tag, GitHub release, and publication verification.'
4
+ ---
5
+
6
+ # Release
7
+
8
+ Publish a repository only through an explicit, reviewable authorization sequence.
9
+
10
+ ## Invocation boundary
11
+
12
+ This is an explicit-only skill.
13
+
14
+ - Proceed only when the user's current request names `$release`, `/release`, or the `release` skill itself.
15
+ - A generic request that merely discusses releases, versions, tags, publishing, or a repository that looks ready is not an invocation. Stop and tell the user how to invoke the skill.
16
+ - Invoking the skill authorizes read-only analysis and a release plan. It does not authorize commits, tags, pushes, package publication, GitHub releases, merges, or pull requests.
17
+ - Accept an optional `patch`, `minor`, or `major` override from the invocation. Otherwise infer the bump from the repository changes.
18
+
19
+ ## Required workflow
20
+
21
+ 1. Read [references/workflow.md](references/workflow.md) completely before running repository checks.
22
+ 2. Follow its Steps 0–4 in order and present the single confirmation block.
23
+ 3. Stop. Do not perform any mutation until the user explicitly confirms that exact plan.
24
+ 4. After confirmation, follow Step 5 exactly and verify every publication channel triggered by the release.
25
+
26
+ Repository instructions remain authoritative. If they impose stricter release checks or extra synchronized version files, include those requirements in the plan and execution.
@@ -0,0 +1,6 @@
1
+ interface:
2
+ display_name: "Release"
3
+ short_description: "Plan, confirm, and publish a repository release"
4
+ default_prompt: "Use $release to prepare and publish this repository after showing me the confirmation plan."
5
+ policy:
6
+ allow_implicit_invocation: false
@@ -1,39 +1,46 @@
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.
1
+ # Release workflow
2
+
3
+ <!-- TOC-START -->
4
+ ## Contents
5
+
6
+ - [Read-only context to collect](#read-only-context-to-collect)
7
+ - [Your task](#your-task)
8
+ - [What this skill actually does (read this first)](#what-this-skill-actually-does-read-this-first)
9
+ - [Verbosity discipline](#verbosity-discipline)
10
+ - [Step 0 Lock interaction language (do this BEFORE printing anything)](#step-0--lock-interaction-language-do-this-before-printing-anything)
11
+ - [Step 1 Establish state (silent)](#step-1--establish-state-silent)
12
+ - [Step 2 Infer the bump (silent)](#step-2--infer-the-bump-silent)
13
+ - [Step 3 Compose the CHANGELOG entry (silent)](#step-3--compose-the-changelog-entry-silent)
14
+ - [Language policy (applies to Steps 1–5)](#language-policy-applies-to-steps-15)
15
+ - [Step 4 Present the plan in ONE block and STOP](#step-4--present-the-plan-in-one-block-and-stop)
16
+ - [Step 5 Execute (silent until done)](#step-5--execute-silent-until-done)
17
+ - [Hard restrictions (never violate)](#hard-restrictions-never-violate)
18
+
19
+ <!-- TOC-END -->
20
+
21
+ ## Read-only context to collect
22
+
23
+ Collect this state with read-only tools before planning anything:
24
+
25
+ - Working tree status: `git status --porcelain`
26
+ - Current branch: `git branch --show-current`
27
+ - Last tag: `git describe --tags --abbrev=0`
28
+ - Commits since the last tag, or the full log when no tag exists
29
+ - Remote URL and primary branch
30
+ - Recent commit subjects and conventions
31
+ - Whether `gh` is installed and, for GitHub remotes, repository visibility
32
+ - Presence of `package.json`, `tiapp.xml`, `composer.json`, `Cargo.toml`, a root `*.podspec`, `CHANGELOG.md`, and `README.md`
33
+ - Merge or rebase state and unresolved conflicts
34
+
35
+ The explicit invocation may include `patch`, `minor`, or `major` to override the inferred bump. If none is present, infer it from the work being released.
29
36
 
30
37
  ## Your task
31
38
 
32
39
  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
40
 
34
- ### What this command actually does (read this first)
41
+ ### What this skill actually does (read this first)
35
42
 
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.
43
+ The release skill 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
44
 
38
45
  **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
46
 
@@ -55,9 +62,9 @@ If the working tree is **completely empty** AND there are zero commits since the
55
62
 
56
63
  ### Step 0 — Lock interaction language (do this BEFORE printing anything)
57
64
 
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.
65
+ 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 until the skill ends: the Step 4 plan, errors, and final report.
59
66
 
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.
67
+ If the user has not yet said anything in this session (rare when the skill is invoked as the first message), default to the language of the repo's `README.md`. If that is also unclear, default to English.
61
68
 
62
69
  If the user switches language mid-flow, switch with them on the next message.
63
70
 
@@ -168,7 +175,7 @@ Apply Conventional Commits rules across that combined set:
168
175
  - Any `feat:` (no breaking) → **minor**
169
176
  - Otherwise → **patch**
170
177
 
171
- `$ARGUMENTS` (`patch` / `minor` / `major`) overrides the inference.
178
+ An explicit invocation override (`patch` / `minor` / `major`) takes precedence over the inference.
172
179
 
173
180
  If there are zero existing commits AND zero proposed commits AND the working tree is clean → abort: "Nothing to release."
174
181
 
@@ -208,7 +215,7 @@ There are **two independent language axes**. Do not mix them.
208
215
 
209
216
  #### Axis 1 — Interaction language (your conversation with the user)
210
217
 
211
- **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.
218
+ **Always match the user's language.** Detect from the user's last 1–3 messages in this conversation — not from this workflow file (which is in English for distribution), and not from the project files. If the user has been speaking Spanish, every plan preview, confirmation prompt, error message, and final report you print **must be in Spanish**. If the user switches mid-flow, switch with them.
212
219
 
213
220
  This is about what the user **reads on screen**. It does **not** affect what gets written to disk or to GitHub.
214
221
 
@@ -222,7 +229,7 @@ This is about what the user **reads on screen**. It does **not** affect what get
222
229
  2. Classify the prose: Spanish, English, or other.
223
230
  3. Tie-break with `CHANGELOG.md` (recent entries) if README is too short or ambiguous.
224
231
  4. Final tie-break: the recent `git log` subjects.
225
- 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.
232
+ 5. If everything is ambiguous, default to English and surface that assumption in the Step 4 block so the user can correct it before confirming.
226
233
 
227
234
  The detected language applies to **all** of the following:
228
235
 
@@ -284,7 +291,7 @@ CHANGELOG entry:
284
291
  ### Fixed
285
292
  - ...
286
293
 
287
- 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
294
+ README updates (gaps found): <one-line per gap, e.g. "add the new public skill to the available-skills table"> ← omit this line if no gaps
288
295
  Release commit: bumps <version-file> A.B.C→X.Y.Z (+ <secondary version file> to the same number), inserts CHANGELOG section, applies README updates. Subject: `<exact line>`.
289
296
  Push: release commit to <branch>.
290
297
  Publishing: pushing the tag triggers `<workflow>.yml`, which publishes to <registry>. ← include ONLY when Step 1.9 found a tag-triggered publishing workflow; localize to user's language
@@ -427,9 +434,9 @@ If everything was routine and no merge was requested, the second line is just th
427
434
  - **Never** `--no-verify`, `--no-gpg-sign`, or any hook-skipping flag unless the user explicitly asks.
428
435
  - **Never** delete tags or branches.
429
436
  - **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.
430
- - **Never** proceed past Step 4 without explicit confirmation. The slash command itself is consent to **invoke**, not consent to commit and push.
437
+ - **Never** proceed past Step 4 without explicit confirmation. Explicitly invoking the skill is consent to **plan**, not consent to commit and push.
431
438
  - If the working tree has merge conflicts or rebase-in-progress markers → **abort** with a diagnosis and let the user resolve manually.
432
439
  - 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).
433
- - If the repo has no remote → run Steps 5.1–5.5 only; skip push, tag, and release. Tell the user.
434
- - 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.
440
+ - If the repo has no remote → land the semantic and release commits only; skip push, tag, and GitHub release. Tell the user.
441
+ - If the repo has a remote but `gh` is not available → push the branch and tag, but skip the GitHub release; tell the user how to create it manually if they want to.
435
442
  - **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.