面向 Claude Code 的函数式设计 Agent Skills 包 functional-design-skills 发布 3.0.0
yutna/functional-design-skills
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.shOptions: --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:
- Say what outranks what, as in the snippet above. Most over-application is an agent with no stated precedence treating every rule as binding.
- 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.
- Scope the rules to the code they apply to. A rule file under
.claude/rules/takes apaths: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.jsonpins, 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