finishing-a-development-branch

安装量: 17.7K
排名: #140

安装

npx skills add https://github.com/obra/superpowers --skill finishing-a-development-branch

Finishing a Development Branch Overview Guide completion of development work by presenting clear options and handling chosen workflow. Core principle: Verify tests → Present options → Execute choice → Clean up. Announce at start: "I'm using the finishing-a-development-branch skill to complete this work." The Process Step 1: Verify Tests Before presenting options, verify tests pass:

Run project's test suite

npm test / cargo test / pytest / go test ./ .. . If tests fail: Tests failing ( failures). Must fix before completing: [Show failures] Cannot proceed with merge/PR until tests pass. Stop. Don't proceed to Step 2. If tests pass: Continue to Step 2. Step 2: Determine Base Branch

Try common base branches

git merge-base HEAD main 2

/dev/null || git merge-base HEAD master 2

/dev/null Or ask: "This branch split from main - is that correct?" Step 3: Present Options Present exactly these 4 options: Implementation complete. What would you like to do? 1. Merge back to locally 2. Push and create a Pull Request 3. Keep the branch as-is (I'll handle it later) 4. Discard this work Which option? Don't add explanation - keep options concise. Step 4: Execute Choice Option 1: Merge Locally

Switch to base branch

git checkout < base-branch

Pull latest

git pull

Merge feature branch

git merge < feature-branch

Verify tests on merged result

< test command

If tests pass

git branch -d < feature-branch

Then: Cleanup worktree (Step 5) Option 2: Push and Create PR

Push branch

git push -u origin < feature-branch

Create PR

gh pr create --title "" --body " $( cat << 'EOF'</p> <h2 id="summary">Summary</h2> <p><2-3 bullets of what changed></p> <h2 id="test-plan">Test Plan</h2> <ul> <li>[ ] <verification steps> EOF ) " Then: Cleanup worktree (Step 5) Option 3: Keep As-Is Report: "Keeping branch . Worktree preserved at ." Don't cleanup worktree. Option 4: Discard Confirm first: This will permanently delete:</li> <li>Branch <name></li> <li>All commits: <commit-list></li> <li>Worktree at <path> Type 'discard' to confirm. Wait for exact confirmation. If confirmed: git checkout < base-branch<blockquote> <p>git branch -D < feature-branch</p> <p>Then: Cleanup worktree (Step 5) Step 5: Cleanup Worktree For Options 1, 2, 4: Check if in worktree: git worktree list | grep $( git branch --show-current ) If yes: git worktree remove < worktree-path</p> <p>For Option 3: Keep worktree. Quick Reference Option Merge Push Keep Worktree Cleanup Branch</p> </blockquote> </li> <li>Merge locally ✓ - - ✓</li> <li> <h2 id="create-pr_1">Create PR</h2> ✓ ✓ -</li> <li> <h2 id="keep-as-is">Keep as-is</h2> - ✓ -</li> <li> <h2 id="discard">Discard</h2> <h2 id="-">-</h2> ✓ (force) Common Mistakes Skipping test verification Problem: Merge broken code, create failing PR Fix: Always verify tests before offering options Open-ended questions Problem: "What should I do next?" → ambiguous Fix: Present exactly 4 structured options Automatic worktree cleanup Problem: Remove worktree when might need it (Option 2, 3) Fix: Only cleanup for Options 1 and 4 No confirmation for discard Problem: Accidentally delete work Fix: Require typed "discard" confirmation Red Flags Never: Proceed with failing tests Merge without verifying tests on result Delete work without confirmation Force-push without explicit request Always: Verify tests before offering options Present exactly 4 options Get typed confirmation for Option 4 Clean up worktree for Options 1 & 4 only Integration Called by: subagent-driven-development (Step 7) - After all tasks complete executing-plans (Step 5) - After all batches complete Pairs with: using-git-worktrees</li> <li>Cleans up worktree created by that skill</li> </ul> </article> <a href="/" class="back-link">← <span data-i18n="detail.backToLeaderboard">返回排行榜</span></a> </div> <aside class="sidebar"> <section class="related-skills" id="relatedSkillsSection"> <h2 class="related-title" data-i18n="detail.relatedSkills">相关 Skills</h2> <div class="related-list" id="relatedSkillsList"> <div class="skeleton-card"></div> <div class="skeleton-card"></div> <div class="skeleton-card"></div> </div> </section> </aside> </div> </div> <script src="https://unpkg.com/i18next@23.11.5/i18next.min.js" defer></script> <script src="https://unpkg.com/i18next-browser-languagedetector@7.2.1/i18nextBrowserLanguageDetector.min.js" defer></script> <script defer> // Language resources - same pattern as index page const resources = { 'zh-CN': null, 'en': null, 'ja': null, 'ko': null, 'zh-TW': null, 'es': null, 'fr': null }; // Load language files (only current + fallback for performance) async function loadLanguageResources() { const savedLang = localStorage.getItem('i18nextLng') || 'en'; const langsToLoad = new Set([savedLang, 'en']); // current + fallback await Promise.all([...langsToLoad].map(async (lang) => { try { const response = await fetch(`/locales/${lang}.json`); if (response.ok) { resources[lang] = { translation: await response.json() }; } } catch (error) { console.warn(`Failed to load ${lang} language file:`, error); } })); } // Load a single language on demand (for language switching) async function loadLanguage(lang) { if (resources[lang]) return; try { const response = await fetch(`/locales/${lang}.json`); if (response.ok) { resources[lang] = { translation: await response.json() }; i18next.addResourceBundle(lang, 'translation', resources[lang].translation); } } catch (error) { console.warn(`Failed to load ${lang} language file:`, error); } } // Initialize i18next async function initI18n() { try { await loadLanguageResources(); // Filter out null values from resources const validResources = {}; for (const [lang, data] of Object.entries(resources)) { if (data !== null) { validResources[lang] = data; } } console.log('Loaded languages:', Object.keys(validResources)); console.log('zh-CN resource:', validResources['zh-CN']); console.log('detail.home in resource:', validResources['zh-CN']?.translation?.detail?.home); // 检查是否有保存的语言偏好 const savedLang = localStorage.getItem('i18nextLng'); // 如果没有保存的语言偏好,默认使用英文 const defaultLang = savedLang && ['zh-CN', 'en', 'ja', 'ko', 'zh-TW', 'es', 'fr'].includes(savedLang) ? savedLang : 'en'; await i18next .use(i18nextBrowserLanguageDetector) .init({ lng: defaultLang, // 强制设置初始语言 fallbackLng: 'en', supportedLngs: ['zh-CN', 'en', 'ja', 'ko', 'zh-TW', 'es', 'fr'], resources: validResources, detection: { order: ['localStorage'], // 只使用 localStorage,不检测浏览器语言 caches: ['localStorage'], lookupLocalStorage: 'i18nextLng' }, interpolation: { escapeValue: false } }); console.log('i18next initialized, language:', i18next.language); console.log('Test translation:', i18next.t('detail.home')); // Set initial language in selector const langSwitcher = document.getElementById('langSwitcher'); langSwitcher.value = i18next.language; // Update page language updatePageLanguage(); // Language switch event langSwitcher.addEventListener('change', async (e) => { await loadLanguage(e.target.value); // load on demand i18next.changeLanguage(e.target.value).then(() => { updatePageLanguage(); localStorage.setItem('i18nextLng', e.target.value); }); }); } catch (error) { console.error('i18next init failed:', error); } } // Translation helper function t(key, options = {}) { return i18next.t(key, options); } // Update all translatable elements function updatePageLanguage() { // Update HTML lang attribute document.documentElement.lang = i18next.language; // Update elements with data-i18n attribute document.querySelectorAll('[data-i18n]').forEach(el => { const key = el.getAttribute('data-i18n'); el.textContent = t(key); }); } // Copy command function function copyCommand() { const command = document.getElementById('installCommand').textContent; const btn = document.getElementById('copyBtn'); navigator.clipboard.writeText(command).then(() => { btn.textContent = t('copied'); btn.classList.add('copied'); setTimeout(() => { btn.textContent = t('copy'); btn.classList.remove('copied'); }, 2000); }).catch(() => { // Fallback for non-HTTPS const textArea = document.createElement('textarea'); textArea.value = command; textArea.style.position = 'fixed'; textArea.style.left = '-9999px'; document.body.appendChild(textArea); textArea.select(); document.execCommand('copy'); document.body.removeChild(textArea); btn.textContent = t('copied'); btn.classList.add('copied'); setTimeout(() => { btn.textContent = t('copy'); btn.classList.remove('copied'); }, 2000); }); } // Initialize document.getElementById('copyBtn').addEventListener('click', copyCommand); initI18n(); // 异步加载相关 Skills async function loadRelatedSkills() { const owner = 'obra'; const skillName = 'finishing-a-development-branch'; const currentLang = 'es'; const listContainer = document.getElementById('relatedSkillsList'); const section = document.getElementById('relatedSkillsSection'); try { const response = await fetch(`/api/related-skills/${encodeURIComponent(owner)}/${encodeURIComponent(skillName)}?limit=6`); if (!response.ok) { throw new Error('Failed to load'); } const data = await response.json(); const relatedSkills = data.related_skills || []; if (relatedSkills.length === 0) { // 没有相关推荐时隐藏整个区域 section.style.display = 'none'; return; } // 渲染相关 Skills listContainer.innerHTML = relatedSkills.map(skill => { const desc = skill.description || ''; const truncatedDesc = desc.length > 60 ? desc.substring(0, 60) + '...' : desc; return ` <a href="${currentLang === 'en' ? '' : '/' + currentLang}/skill/${skill.owner}/${skill.repo}/${skill.skill_name}" class="related-card"> <div class="related-name">${escapeHtml(skill.skill_name)}</div> <div class="related-meta"> <span class="related-owner">${escapeHtml(skill.owner)}</span> <span class="related-installs">${skill.installs}</span> </div> <div class="related-desc">${escapeHtml(truncatedDesc)}</div> </a> `; }).join(''); } catch (error) { console.error('Failed to load related skills:', error); // 加载失败时显示提示或隐藏 listContainer.innerHTML = '<div class="related-empty">暂无相关推荐</div>'; } } // HTML 转义 function escapeHtml(text) { const div = document.createElement('div'); div.textContent = text; return div.innerHTML; } // 页面加载完成后异步加载相关 Skills if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', loadRelatedSkills); } else { loadRelatedSkills(); } </script> </body> </html>