跳到正文
GitHub·· 7 天前AI 评分36

面向 Claude Code 的函数式设计 Agent Skills 包 functional-design-skills 发布 3.0.0

yutna/functional-design-skills

AI 导读

yutna/functional-design-skills 为 Claude Code 提供一套函数式代码设计规则的 Agent Skills,含一个索引与三十三个核心技能,另有九个语言包覆盖 JavaScript、TypeScript、React 与 Next.js、Elixir 与 Phoenix。

正文

Agent Skills carrying design rules for codebases written in a functional style. For Claude Code.

They cover:

  • Diagnosing complexity, and telling what a design costs from what it looks like
  • Modelling a domain in types so illegal values cannot be built
  • Composing workflows out of total functions, with errors as values
  • Module boundaries, information hiding, and interface depth
  • Immutability, state, effects at the edges, and reliability across a process boundary
  • Refactoring imperative or object-oriented code toward all of the above

The rules are language-agnostic. An index and thirty-three core skills describe designs in a neutral notation; nine language packs translate them into JavaScript, TypeScript, React and Next.js, and Elixir and Phoenix.

What it changes

The smallest example of the whole method. A type that permits states the business forbids:

type Shipment = {
  status: String,
  trackingNumber: Option<String>,
  shippedAt: Option<Date>,
  deliveredAt: Option<Date>,
  failureReason: Option<String>,
}

Five fields, four of them optional, so the type admits sixteen combinations of presence for any status. Reports break on the ones nobody meant. After:

type Shipment =
  | Pending
  | Shipped of { trackingNumber: TrackingNumber, shippedAt: Date }
  | Delivered of { trackingNumber: TrackingNumber, deliveredAt: Date }
  | Failed of { reason: FailureReason }

Each case carries exactly the data that case has, so there is nothing to check for absence. The gain is not tidiness: every consumer that used to branch on a status string and then test a field for null now handles four cases and cannot forget one.

Install

As a plugin

/plugin marketplace add yutna/functional-design-skills
/plugin install functional-design-skills@functional-design-skills

With the install script

Copies every skill into ~/.claude/skills/, where Claude Code finds it in any project:

git clone https://github.com/yutna/functional-design-skills.git
cd functional-design-skills
./scripts/install.sh

Options: --force to replace an existing copy, --dry-run to see what it would do. --force replaces this pack's own skills and nothing else; a name held by anything it did not install is reported and left alone.

On Windows, without needing a shell or WSL:

git clone https://github.com/yutna/functional-design-skills.git
cd functional-design-skills
.\scripts\install.ps1

Same options, as -Force and -DryRun. PowerShell 7 or later.

By hand

Copy any plugin/skills/<name>/ directory into ~/.claude/skills/ for every project, or into a project's .claude/skills/ for one repository. There is nothing to build and no dependency to install.

Start a new session afterwards, whichever route you took.

Upgrading from 2.x

Every skill was renamed in 3.0.0: the thirty-three core skills gained the functional- prefix the index and the language packs already had. Skills install flat into ~/.claude/skills/, where a name like hiding-information is one any pack might claim, and the second pack to claim it lost silently.

The plugin route replaces the old skills and needs nothing from you. The install script does not delete what it did not install, so remove the old directories first:

cd ~/.claude/skills
ls -d */ | grep -v '^functional-' | xargs rm -rf

Check what that would remove before running it if anything else lives there. From 3.0.0 on, each skill records the pack it came from, and the installers report a name another pack owns as conflict rather than skipping it. A directory that records no pack at all is reported as unmarked and is never replaced, --force included: the marker is this pack's own convention, so an unmarked skill is more likely to be someone else's than an old copy of this one. Remove it by hand to hand the name over.

Tell the agent the pack is there

Installing puts the rules within reach. It does not say what outranks what, and an agent with no stated precedence will read a deliberate convention as a defect. Paste this into the project's CLAUDE.md, with the last line naming the language pack that matches the project:

## Design rules

This codebase follows functional design. The `functional-design` skill
is the index; the rest of the pack carries one rule each.

- This project's own conventions win. The pack decides what they leave
  open, and a pack red flag is a place to look, not a finding.
- Before designing a new module, workflow, endpoint, or bounded
  context, read `functional-design` and follow its design loop.
- For anything smaller, use its calibration table to decide how much
  design the task warrants. For one function in a module that already
  exists, that is an honest signature and a precise name, nothing more.
- Before finishing a change that added or reshaped a type, a signature,
  or a module boundary, run the checklist in
  `functional-reviewing-functional-design`.
- Use `functional-typescript` for concrete syntax.

The first bullet matters more than it looks. It is what stops the pack arguing with a decision the team already made.

Check it took

Start a session in the target project and ask one question whose right answer names a skill:

we have four nullable fields that only make sense in some combinations

Expect the agent to reach functional-making-illegal-states-unrepresentable. If it answers from general knowledge without naming a skill, the files are in the wrong directory.

If it over-engineers

The symptom is a wrapper type on a throwaway script, a new error union for a one-line change, or a review finding against something the team decided on purpose. The cause is the agent applying a heavier rule than the task warrants.

Every core rule says how strictly it is meant, as the first word of the item. Rule means breaking it is a defect. Default means do this unless the stated condition holds. Judgement means two answers are defensible and the skill says what tips it. An agent that quotes a Default as though it were a Rule is misreading the pack, and saying so is usually enough.

Three more things fix it, in order of how much they buy:

  1. Say what outranks what, as in the snippet above. Most over-application is an agent with no stated precedence treating every rule as binding.
  2. Name the calibration row out loud in the session: "this is one function in a module that already exists". The row is the pack's own answer and the agent will take it.
  3. Scope the rules to the code they apply to. A rule file under .claude/rules/ takes a paths: key in its frontmatter, so a codebase that is only partly functional can point these rules at the part that is.

Which skills there are

functional-design is the index: the calibration table, the symptom-to-skill routing, and the notation the core skills use. Start there.

Thirty-three core skills carry one rule each, grouped roughly as: complexity and module boundaries; domain modelling and types; workflows, composition and errors; effects, state and reliability; naming, comments, testing, review and refactoring.

Nine language packs give the concrete syntax: functional-javascript, functional-typescript, functional-typescript-effect, functional-typescript-ts-pattern, functional-typescript-xstate, functional-typescript-react, functional-typescript-react-nextjs, functional-elixir, functional-elixir-phoenix.

Each skill's description states the situations that should trigger it, and only those; the body is read when the skill is selected. Reference files behind each one hold the depth, and load only when a skill links into them.

Layout

plugin/skills/<name>/       what ships
  SKILL.md                  the rule, short enough to always read
  references/*.md           depth, loaded only when needed
plugin/.claude-plugin/      the plugin manifest
.claude-plugin/             the marketplace manifest
evals/                      routing cases and agent scenarios
scripts/                    validation, routing, install, link

Only plugin/ ships. Everything else maintains the pack.

Further reading

  • CONTRIBUTING.md is how to change the pack, and the constraints a change has to keep.
  • CLAUDE.md is the same thing addressed to an agent working in this repository.
  • evals/README.md is what the routing check does and does not prove, and the scenarios to run against a real agent.
  • CHANGELOG.md is what changed in each release.

What this does not establish

Worth knowing before relying on it.

  • The routing check is a keyword lint, not an oracle. It scores word overlap and has no idea what any word means. A green run means every description contains the vocabulary people use for that problem; it does not mean an agent routes correctly. The scenarios in evals/ cover that question, as a sample rather than a proof.
  • Only the first place in a ranking is meaningful. A median of 36 of the 43 skills score exactly zero on any given case, and ties break by name, so second and third places are often filled alphabetically. The gate uses the top three, which is deliberately generous.
  • The prose checks know only the domains they are told about. They catch a domain noun standing where a structural word belongs, and one buried inside a longer word, for the nouns listed in scripts/validate-prose.mjs. A new example domain that is not added to that list is unchecked.
  • The example check is a type checker, not a test. Every fenced example in a real language is parsed, and every TypeScript, TSX and JavaScript one is type-checked against the libraries package.json pins, so a member that does not exist or a value of the wrong type fails the build. An example is a fragment, so the names it assumes are declared in a prelude outside the plugin, and a prelude is an assumption: an example can type-check against one that is wrong, or type-check and still be wrong. Nothing runs an example or knows whether a library behaves as shown. Elixir fences are parsed, not compiled, in continuous integration, which installs the toolchain for that step; locally they are parsed only when you pass --with-elixir. The notation below has no checker.
  • The neutral notation is checked by nobody. Around three quarters of the code blocks in the pack are text, the notation the core skills share, and no parser exists for it. Those blocks are held to the same standard by review alone.

Licence

MIT, in LICENSE. Use the skills in any project, including commercial work; keep the copyright notice.

The licence covers the wording in this repository and nothing else. The design principles it states are long-established and belong to nobody here.

来源:GitHub · github.com