diff --git a/skill/code-release/SKILL.md b/command/release.md similarity index 67% rename from skill/code-release/SKILL.md rename to command/release.md index 2d33a13..6dca615 100644 --- a/skill/code-release/SKILL.md +++ b/command/release.md @@ -1,13 +1,12 @@ --- -name: code-release -description: > - Prepare and execute a release with version bumping, changelog updates, and tags. -use_when: > - REQUIRED: When the user asks to prepare or perform a release, - call skill({ name: "code-release" }) before changing any release artifacts. +description: Prepare and execute a release with version bumping, changelog updates, and tags --- -# Code Release Skill +## Context + +- Current version: !`node -p "require('./package.json').version"` +- Latest tag: !`git describe --tags --abbrev=0 2>/dev/null || echo "none"` +- Commits since last tag: !`git log $(git describe --tags --abbrev=0 2>/dev/null || echo "HEAD~10")..HEAD --oneline 2>/dev/null || git log --oneline -10` ## CRITICAL CONSTRAINT @@ -17,13 +16,15 @@ Any destructive or remote action requires confirmation, including: - `git tag` - `git push` -## Step 1: Determine last released version +## Your task + +### Step 1: Determine last released version 1. Prefer the latest Git tag that matches `v`. 2. If no matching tag exists, use the version in `package.json`. 3. Collect commits since the last version (tag to `HEAD`). -## Step 2: Propose version bump +### Step 2: Propose version bump Analyze commits since the last version and recommend a semver bump: - **major**: breaking changes or incompatible behavior changes. @@ -32,14 +33,14 @@ Analyze commits since the last version and recommend a semver bump: Present the recommendation and ask the user to confirm before changing any files. -## Step 3: Update release artifacts (after confirmation) +### Step 3: Update release artifacts (after confirmation) - Update the version in `package.json`. - Update `CHANGELOG` with a new section for the version. - Summarize changes based on the commit range. - Preserve the existing changelog format. -## Step 4: Tag and publish (after confirmation) +### Step 4: Tag and publish (after confirmation) - Commit release changes with a clear release message. - Create an annotated tag (for example, `vX.Y.Z`). diff --git a/skill/ask-questions-if-underspecified/SKILL.md b/skill/ask-questions-if-underspecified/SKILL.md new file mode 100644 index 0000000..00429e4 --- /dev/null +++ b/skill/ask-questions-if-underspecified/SKILL.md @@ -0,0 +1,59 @@ +--- +name: ask-questions-if-underspecified +description: Clarify requirements before implementing. Use when serious doubts arise. +use_when: > + When a request has multiple plausible interpretations or key details + (objective, scope, constraints, environment, or safety) are unclear, + load this skill before starting implementation. + Do NOT use when the request is already clear or when a quick, low-risk + discovery read can answer the missing details. +--- + +# Ask Questions If Underspecified + +## Goal + +Ask the minimum set of clarifying questions needed to avoid wrong work; do not start implementing until the must-have questions are answered (or the user explicitly approves proceeding with stated assumptions). + +## Workflow + +### 1) Decide whether the request is underspecified + +Treat a request as underspecified if after exploring how to perform the work, some or all of the following are not clear: +- Define the objective (what should change vs stay the same) +- Define "done" (acceptance criteria, examples, edge cases) +- Define scope (which files/components/users are in/out) +- Define constraints (compatibility, performance, style, deps, time) +- Identify environment (language/runtime versions, OS, build/test runner) +- Clarify safety/reversibility (data migration, rollout/rollback, risk) + +If multiple plausible interpretations exist, assume it is underspecified. + +### 2) Ask must-have questions first (keep it small) + +Ask 1-5 questions in the first pass. Prefer questions that eliminate whole branches of work. + +Use the `question` tool to present structured choices: +- Offer multiple-choice options with clear labels +- Mark recommended defaults with "(Recommended)" in the label +- Use `multiple: true` when the user can select several options +- The user can always select "Other" for custom input + +### 3) Pause before acting + +Until must-have answers arrive: +- Do not run commands, edit files, or produce a detailed plan that depends on unknowns +- Do perform a clearly labeled, low-risk discovery step only if it does not commit you to a direction (e.g., inspect repo structure, read relevant config files) + +If the user explicitly asks you to proceed without answers: +- State your assumptions as a short numbered list +- Ask for confirmation; proceed only after they confirm or correct them + +### 4) Confirm interpretation, then proceed + +Once you have answers, restate the requirements in 1-3 sentences (including key constraints and what success looks like), then start work. + +## Anti-patterns + +- Don't ask questions you can answer with a quick, low-risk discovery read (e.g., configs, existing patterns, docs). +- Don't ask open-ended questions if a tight multiple-choice or yes/no would eliminate ambiguity faster. diff --git a/src/index.ts b/src/index.ts index f3f6fa5..b27daa8 100644 --- a/src/index.ts +++ b/src/index.ts @@ -105,6 +105,7 @@ const SmartfrogPlugin: Plugin = async (ctx) => { const skillTool = createSkillTool({ pluginSkills: skills, pluginDir: PLUGIN_ROOT, + client: ctx.client, }) log("[init] Plugin loaded", { diff --git a/src/tools/skill.ts b/src/tools/skill.ts index 3f8121f..875d100 100644 --- a/src/tools/skill.ts +++ b/src/tools/skill.ts @@ -2,7 +2,11 @@ import { tool, type ToolContext } from "@opencode-ai/plugin" import { existsSync, readdirSync, readFileSync } from "node:fs" import { join, dirname } from "node:path" import { homedir } from "node:os" +import type { createOpencodeClient } from "@opencode-ai/sdk" import { parseFrontmatter, type LoadedSkill } from "../loaders" +import { log } from "../logger" + +type Client = ReturnType export interface SkillInfo { name: string @@ -118,18 +122,20 @@ function loadSkillContent(location: string): string { export interface CreateSkillToolOptions { pluginSkills: LoadedSkill[] pluginDir: string + client: Client } export function createSkillTool(options: CreateSkillToolOptions) { let cachedSkills: SkillInfo[] | null = null let cachedDescription: string | null = null + const { client, pluginDir, pluginSkills } = options const getSkills = (cwd: string): SkillInfo[] => { if (cachedSkills) return cachedSkills // Merge order: plugin defaults < global < project (later entries override earlier on name collision) const allSkills = [ - ...pluginSkillsToInfo(options.pluginSkills, options.pluginDir), + ...pluginSkillsToInfo(pluginSkills, pluginDir), ...discoverClaudeGlobalSkills(), ...discoverOpencodeGlobalSkills(), ...discoverClaudeProjectSkills(cwd), @@ -183,6 +189,18 @@ export function createSkillTool(options: CreateSkillToolOptions) { const body = loadSkillContent(skill.location) const dir = dirname(skill.location) + try { + await client.tui.showToast({ + body: { + message: `Skill "${skill.name}" loaded`, + variant: "info", + duration: 3000, + }, + }) + } catch (error) { + log("[skill] Failed to show toast", { error: String(error) }) + } + return [ `## Skill: ${skill.name}`, "",