agentsclimarketplace

Behavior preserving refactor

Skill manman1414/frontend-refactor-skills/skills/behavior-preserving-refactor

React/Vue/TS 前端重构 Agent Skills(行为不变重构、拆文件、绞杀者迁移)

Install
npx -y skills add manman1414/frontend-refactor-skills --skill behavior-preserving-refactor

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its author says it does

Copied from the file, not written here

Performs behavior-preserving frontend refactors in React, Vue 2/3, and TypeScript without changing UI or runtime semantics. Use when renaming components, extracting hooks/composables, simplifying JSX/templates, deduplicating props logic, inlining or extracting functions, or when the user asks for a safe refactor with no behavior change.

The file declares its own license as MIT. That is the author’s claim about this one file, and it is not the same thing as the license GitHub reports for the repository, which is listed with the other numbers below.

SKILL.md

5.8 KB, as published. Nobody here has run it

Behavior-Preserving Refactor (Frontend)

<!-- Author: Administrator | Date: 2026-07-05 -->

Refactor structure only — zero behavior change. Applies to React (TS/TSX), Vue 2 (Options API), Vue 3 (Composition API + <script setup>).

<HARD-GATE> Do NOT change UI output, event timing, API calls, routing, or state transitions in the same step as a structural refactor. One refactor type per commit/PR slice. </HARD-GATE>

When to use

  • Rename component / prop / emit / slot
  • Extract custom hook (useXxx) or Vue composable (useXxx)
  • Simplify JSX or template conditions (equivalent logic only)
  • Inline or extract pure helper functions
  • Replace duplicated prop drilling with equivalent pattern (same data flow)
  • Convert between equivalent TS patterns (type alias ↔ interface) without runtime effect

Pre-flight checklist

- [ ] Existing tests / Storybook / manual repro path identified
- [ ] Baseline: run typecheck + relevant tests before editing
- [ ] Scope written in one sentence: "Refactor X without changing Y"
- [ ] Framework detected: React | Vue 2 | Vue 3

Workflow

Step 1 — Establish baseline

Run project commands (adapt to repo):

npm run typecheck   # or: vue-tsc --noEmit / tsc --noEmit
npm test -- --run   # or: vitest run / jest --bail

For Vue 2 without tests: note key user flows (click path, v-model binding, emit payload shape).

Step 2 — Pick ONE refactor type

TypeReactVue 2Vue 3
Rename symbolComponent, hook, propname, props, emitdefineProps, defineEmits
Extract logicuseXxx() hookmethods → mixin/composable*composable in composables/
Extract UI chunkChild component, same propsChild SFC, $attrs/$listenersChild SFC, defineOptions + attrs
Simplify conditionEquivalent ternary / early returnSame in computedSame in computed()
DedupeShared util or hookShared mixin/composableShared composable

* Prefer composable over mixin when touching Vue 2 — mixin only if project convention requires it.
† Vue 2.7+: prefer v-bind="$attrs" over $listeners.

Forbidden in this skill: API URL changes, v-ifv-show swaps, key changes on lists, adding/removing watch side effects, React useEffect dependency changes.

Step 3 — Apply minimal diff

Rules:

  1. Move, don't rewrite — copy-paste first, rename second
  2. Preserve public surface — exported component name, prop names, emit names, slot names unchanged unless rename IS the task
  3. Keep keys stable — React key, Vue :key on v-for unchanged
  4. Preserve reactivity — Vue: don't break ref/reactive unwrap; React: don't change hook call order

Step 4 — Verify equivalence

npm run typecheck
npm test -- --run

Manual spot-check (pick applicable):

  • Same DOM structure for default props/state
  • Same event payloads (emit / onXxx callback args)
  • Same loading / error / empty states
  • Same CSS class names on root element (if tests snapshot classes)

Step 5 — Diff self-review

For every changed line, answer: "Does this change runtime behavior?"

  • Yes → revert or split to a separate task
  • No → keep

Framework notes

React + TypeScript

// Extract hook — behavior identical
// Before: logic inside component
// After:
function useOrderFilters(orders: Order[]) {
  const [query, setQuery] = useState("");
  const filtered = useMemo(
    () => orders.filter((o) => o.name.includes(query)),
    [orders, query]
  );
  return { query, setQuery, filtered };
}
  • Do NOT add/remove hooks conditionally
  • Do NOT change useMemo/useCallback deps unless provably equivalent
  • Prefer type for props if file already uses type

Vue 2 (Options API)

  • Keep data() shape identical
  • computed getters must remain pure with same dependencies
  • When splitting components, pass equivalent props; use .sync / v-model same as before
  • Preserve $scopedSlots / slot API when extracting child

Vue 3 (Composition API)

<!-- Extract composable — same refs returned -->
<script setup lang="ts">
import { useOrderFilters } from "@/composables/useOrderFilters";
const props = defineProps<{ orders: Order[] }>();
const { query, filtered } = useOrderFilters(() => props.orders);
</script>
  • defineProps / defineEmits types must match previous runtime contract
  • watch / watchEffect — do not add or remove in behavior-preserving refactors
  • Prefer ref vs reactive consistency with surrounding code

Output format

When reporting to the user (中文):

## 改动
- [文件] 做了什么结构变更

## 为什么
- 一句话说明 refactor 类型与收益

## 行为保证
- 哪些入口验证过(测试 / 手动路径)

## 风险
- 无 / 或列出需人工确认的边界

Anti-patterns

  • ❌ "顺便"改样式、改文案、改 API 字段
  • ❌ 一次 PR 里 rename + extract + 逻辑优化
  • ❌ Vue 2 → Vue 3 语法迁移(用 strangler-refactor
  • ❌ 无测试的大组件拆分后不跑 typecheck

Related skills

  • File/package moves → extract-and-move
  • Replace legacy implementation → strangler-refactor
  • Tests before behavior change → test-driven-development

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.