@iceinvein/agent-skills 0.1.7 → 0.1.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +19 -3
- package/package.json +1 -1
- package/skills/complexity-accountant/SKILL.md +137 -0
- package/skills/complexity-accountant/skill.json +26 -0
- package/skills/contract-enforcer/SKILL.md +139 -0
- package/skills/contract-enforcer/skill.json +26 -0
- package/skills/index.json +30 -0
- package/skills/module-secret-auditor/SKILL.md +140 -0
- package/skills/module-secret-auditor/skill.json +26 -0
- package/skills/seam-finder/SKILL.md +141 -0
- package/skills/seam-finder/skill.json +26 -0
- package/skills/simplicity-razor/SKILL.md +166 -0
- package/skills/simplicity-razor/skill.json +26 -0
package/README.md
CHANGED
|
@@ -25,18 +25,30 @@ bunx @iceinvein/agent-skills install design-review
|
|
|
25
25
|
|
|
26
26
|
# Install for a specific tool
|
|
27
27
|
bunx @iceinvein/agent-skills install design-review --tool claude
|
|
28
|
+
|
|
29
|
+
# Install globally (available in all projects)
|
|
30
|
+
bunx @iceinvein/agent-skills install design-review -g
|
|
28
31
|
```
|
|
29
32
|
|
|
33
|
+
If no tools are detected in your directory, the CLI will prompt you to choose which tools you use.
|
|
34
|
+
|
|
30
35
|
## Commands
|
|
31
36
|
|
|
32
37
|
| Command | Description |
|
|
33
38
|
|---------|-------------|
|
|
34
|
-
| `install <skill> [--tool <tool>]` | Install a skill |
|
|
35
|
-
| `remove <skill
|
|
36
|
-
| `update <skill
|
|
39
|
+
| `install <skill> [--tool <tool>] [-g]` | Install a skill |
|
|
40
|
+
| `remove <skill> [-g]` | Remove a skill |
|
|
41
|
+
| `update <skill> [-g]` | Update to latest version |
|
|
37
42
|
| `list` | List all available skills |
|
|
38
43
|
| `info <skill>` | Show skill details |
|
|
39
44
|
|
|
45
|
+
### Flags
|
|
46
|
+
|
|
47
|
+
| Flag | Description |
|
|
48
|
+
|------|-------------|
|
|
49
|
+
| `--tool <tool>` | Install for a specific tool (claude, cursor, codex, gemini) |
|
|
50
|
+
| `-g, --global` | Install to home directory instead of current project |
|
|
51
|
+
|
|
40
52
|
## Available Skills
|
|
41
53
|
|
|
42
54
|
### design-review
|
|
@@ -66,6 +78,10 @@ Skills live in this repo under `skills/`. The CLI fetches them from GitHub and i
|
|
|
66
78
|
- **Codex**: Appended to `AGENTS.md`
|
|
67
79
|
- **Gemini CLI**: `.gemini/skills/` + `.gemini/settings.json`
|
|
68
80
|
|
|
81
|
+
### Local vs Global
|
|
82
|
+
|
|
83
|
+
By default, skills install to the **current project directory** (e.g., `./claude/skills/`). Use `-g` to install to your **home directory** (e.g., `~/.claude/skills/`) so the skill is available in every project.
|
|
84
|
+
|
|
69
85
|
A `.agent-skills.lock` file tracks installations for update and remove.
|
|
70
86
|
|
|
71
87
|
## License
|
package/package.json
CHANGED
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: complexity-accountant
|
|
3
|
+
description: Use when the agent creates a new abstraction, splits code into multiple files, introduces a library or framework, or when code feels over-engineered. Trigger on "this is too complicated", "should I extract this?", "do we need this layer?", or when reviewing structural changes. NOT for initial scaffolding or established team patterns.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Complexity Accountant
|
|
7
|
+
|
|
8
|
+
A structured complexity analysis framework based on John Ousterhout's *A Philosophy of Software Design*. Treats complexity as a finite budget — every abstraction, layer, and indirection must justify the complexity it adds by reducing complexity elsewhere. Modules should be deep (simple interface, rich functionality), not shallow (interface as complex as the implementation).
|
|
9
|
+
|
|
10
|
+
**Core principle:** Complexity is the root cause of most software difficulty. It manifests as change amplification, cognitive load, and unknown unknowns. The goal is not zero complexity — it's justified complexity where every cost buys a specific benefit.
|
|
11
|
+
|
|
12
|
+
## When to Use
|
|
13
|
+
|
|
14
|
+
- Creating a new abstraction (class, module, helper function, wrapper)
|
|
15
|
+
- Splitting code into multiple files or layers
|
|
16
|
+
- Introducing a library or framework to replace inline code
|
|
17
|
+
- "This is too complicated" / "Should I extract this?" / "Do we need this layer?"
|
|
18
|
+
- Reviewing structural changes or refactoring proposals
|
|
19
|
+
|
|
20
|
+
**Not for:** Initial project scaffolding with established frameworks, patterns the team has already committed to, trivial organizational changes (renaming, moving a constant).
|
|
21
|
+
|
|
22
|
+
## The Process
|
|
23
|
+
|
|
24
|
+
### 1. Measure Complexity Added
|
|
25
|
+
|
|
26
|
+
For every structural change, evaluate impact across Ousterhout's three dimensions:
|
|
27
|
+
|
|
28
|
+
**Change amplification** — How many places must change for a single logical change?
|
|
29
|
+
- Count: if you add a field to a data type, how many files need updating?
|
|
30
|
+
- If the answer is > 2, the structure is amplifying changes. Name the specific coupling that causes this.
|
|
31
|
+
|
|
32
|
+
**Cognitive load** — How much must a developer hold in their head to work here?
|
|
33
|
+
- Count: modules involved, implicit ordering, non-obvious conventions, state to track
|
|
34
|
+
- A function that requires reading 3 other files to understand has high cognitive load
|
|
35
|
+
- Beware: more lines of code ≠ more cognitive load. A single clear 50-line function often has less cognitive load than five 10-line functions spread across files.
|
|
36
|
+
|
|
37
|
+
**Unknown unknowns** — Things a developer needs to know that aren't apparent from reading the code.
|
|
38
|
+
- Hidden dependencies ("this only works if X runs first")
|
|
39
|
+
- Implicit initialization order
|
|
40
|
+
- Convention-based behavior ("files named *.handler.ts are auto-registered")
|
|
41
|
+
- Side effects that aren't visible in the function signature
|
|
42
|
+
|
|
43
|
+
### 2. Depth Test
|
|
44
|
+
|
|
45
|
+
For every new module, function, or class, evaluate its depth:
|
|
46
|
+
|
|
47
|
+
**Deep module** = Simple interface, lots of functionality behind it. The caller's life is genuinely easier because the module absorbs complexity.
|
|
48
|
+
- Good example: `fs.readFile(path)` — one call hides file descriptors, buffering, encoding, OS syscalls
|
|
49
|
+
- Good example: `db.query(sql, params)` — one call hides connection pooling, parameterization, result mapping
|
|
50
|
+
|
|
51
|
+
**Shallow module** = Interface nearly as complex as its implementation. The abstraction doesn't earn its keep.
|
|
52
|
+
- Bad example: `UserValidator` class that wraps three `if` statements
|
|
53
|
+
- Bad example: `ConfigManager` that just reads `process.env` with a method per variable
|
|
54
|
+
- Bad example: `ApiClient` where every method is a one-liner calling `fetch` with different URLs
|
|
55
|
+
|
|
56
|
+
**The inlining test:** Would removing this abstraction and inlining the code make things simpler or more complex? If simpler — the abstraction is shallow. Kill it.
|
|
57
|
+
|
|
58
|
+
### 3. Justify or Kill
|
|
59
|
+
|
|
60
|
+
If a new abstraction doesn't meaningfully reduce at least one complexity dimension, remove it.
|
|
61
|
+
|
|
62
|
+
**Justifications that DO count:**
|
|
63
|
+
- "This absorbs the complexity of [specific thing] so callers don't need to know about it"
|
|
64
|
+
- "Without this boundary, changing [X] would require modifying [N] files instead of 1"
|
|
65
|
+
- "This eliminates an unknown-unknown: callers no longer need to know about [hidden constraint]"
|
|
66
|
+
|
|
67
|
+
**Justifications that do NOT count:**
|
|
68
|
+
- "Might be useful later" — YAGNI. Add it when it's needed, not when it's imagined.
|
|
69
|
+
- "Separation of concerns" — without naming the specific concerns being separated and why they need separation.
|
|
70
|
+
- "It's the standard pattern" — patterns serve purposes. Name the purpose in this codebase. If the purpose doesn't apply, the pattern doesn't apply.
|
|
71
|
+
- "Makes it more testable" — only valid if you're actually writing those tests right now. Hypothetical testability is not a benefit.
|
|
72
|
+
- "Clean architecture says so" — architecture serves the system, not the reverse.
|
|
73
|
+
|
|
74
|
+
### 4. Complexity Budget Report
|
|
75
|
+
|
|
76
|
+
Produce a brief annotation with every non-trivial structural change:
|
|
77
|
+
|
|
78
|
+
```
|
|
79
|
+
COMPLEXITY: [what changed]
|
|
80
|
+
Added: [specific complexity cost]
|
|
81
|
+
Bought: [specific benefit — what's easier now?]
|
|
82
|
+
Net: [worth it / not worth it / marginal — with reasoning]
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Example:
|
|
86
|
+
|
|
87
|
+
```
|
|
88
|
+
COMPLEXITY: Extract PricingEngine from OrderProcessor
|
|
89
|
+
Added: new module boundary (+1 file to understand), interface between them (PricingInput/PricingResult types)
|
|
90
|
+
Bought: pricing rules can change without touching order flow, pricing is independently testable with known inputs
|
|
91
|
+
Net: worth it — pricing is a likely change axis and the boundary matches a real domain separation
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Counter-example:
|
|
95
|
+
|
|
96
|
+
```
|
|
97
|
+
COMPLEXITY: Extract validateEmail into utils/validation.ts
|
|
98
|
+
Added: new file, new import, function is now in a different directory from its only caller
|
|
99
|
+
Bought: nothing — this function has one caller and the validation logic is 4 lines
|
|
100
|
+
Net: not worth it — inline it. If a second caller appears, extract then.
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## Interaction Model
|
|
104
|
+
|
|
105
|
+
Decision engine. The agent runs this analysis on its own work before presenting code to the human. The complexity report appears alongside the diff — the human sees what complexity was added, what it bought, and whether it's justified. The human can challenge the assessment or override the verdict.
|
|
106
|
+
|
|
107
|
+
## Red Flags
|
|
108
|
+
|
|
109
|
+
| Signal | Diagnosis |
|
|
110
|
+
|--------|-----------|
|
|
111
|
+
| A wrapper that adds no logic, just delegates | Shallow module — remove the middleman |
|
|
112
|
+
| A file with one exported function under 10 lines | Doesn't need its own file — put it near its caller |
|
|
113
|
+
| An interface/type with only one implementation | Premature abstraction — inline it, extract when a second implementation appears |
|
|
114
|
+
| A "utils" or "helpers" directory | Complexity junk drawer — each utility should live near its caller |
|
|
115
|
+
| A class where most methods delegate to another class | Pass-through layer adding indirection without value |
|
|
116
|
+
| Configuration object with 15+ fields | Interface is as complex as the problem — the abstraction isn't simplifying anything |
|
|
117
|
+
| "Manager", "Handler", "Processor" suffix with vague responsibility | Name doesn't describe a clear, bounded responsibility |
|
|
118
|
+
| Three files created for one feature (type, implementation, barrel export) | Ceremony without benefit — does the feature need all three? |
|
|
119
|
+
|
|
120
|
+
## Guard Rails
|
|
121
|
+
|
|
122
|
+
**Don't punish good abstractions.** Deep modules that genuinely simplify their callers' lives are worth their cost. The goal isn't minimal code — it's minimal accidental complexity.
|
|
123
|
+
|
|
124
|
+
**Respect existing architecture.** If the team uses a repository pattern and it's working, don't argue against it in the middle of a feature. The complexity accountant evaluates *new* additions, not relitigates committed decisions.
|
|
125
|
+
|
|
126
|
+
**Acknowledge uncertainty.** Sometimes it's genuinely unclear whether an abstraction earns its keep. Say so: "This is marginal — it could go either way. Here's what I'd watch for to decide later."
|
|
127
|
+
|
|
128
|
+
**Scale the analysis.** A 3-line helper doesn't need a complexity report. A new service layer does. Match the rigor to the stakes.
|
|
129
|
+
|
|
130
|
+
## The Ousterhout Heuristics (Reference)
|
|
131
|
+
|
|
132
|
+
1. **Classes should be deep, not shallow.** A deep class has a simple interface relative to the functionality it provides.
|
|
133
|
+
2. **Define errors out of existence.** Handle common error cases as part of the normal code path rather than throwing exceptions. An API that can't fail is simpler than one that handles 5 error types.
|
|
134
|
+
3. **Pull complexity downward.** It's better for a module to be internally complex with a simple interface than to push complexity onto its callers.
|
|
135
|
+
4. **General-purpose modules are deeper.** Making a module slightly more general often makes it significantly deeper — the same interface handles more cases.
|
|
136
|
+
5. **Different layer, different abstraction.** If two adjacent layers use the same abstraction, one layer is probably not adding value. Each layer should transform or enrich the abstraction.
|
|
137
|
+
6. **Complexity is incremental.** No single change makes a system complex — it's the accumulation of many "small" additions that each seemed harmless. This is why you account for every one.
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "complexity-accountant",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Ousterhout-inspired complexity analysis — deep vs shallow modules, complexity budget, justified abstractions",
|
|
5
|
+
"author": "iceinvein",
|
|
6
|
+
"type": "prompt",
|
|
7
|
+
"tools": ["claude", "cursor", "codex", "gemini"],
|
|
8
|
+
"files": {
|
|
9
|
+
"prompt": "SKILL.md"
|
|
10
|
+
},
|
|
11
|
+
"install": {
|
|
12
|
+
"claude": {
|
|
13
|
+
"prompt": ".claude/skills/complexity-accountant/SKILL.md"
|
|
14
|
+
},
|
|
15
|
+
"cursor": {
|
|
16
|
+
"prompt": ".cursor/rules/complexity-accountant.mdc"
|
|
17
|
+
},
|
|
18
|
+
"codex": {
|
|
19
|
+
"prompt": "AGENTS.md",
|
|
20
|
+
"append": true
|
|
21
|
+
},
|
|
22
|
+
"gemini": {
|
|
23
|
+
"prompt": ".gemini/skills/complexity-accountant.md"
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: contract-enforcer
|
|
3
|
+
description: Use when writing or modifying functions with non-trivial logic — especially functions that handle external input, state transitions, or cross-module boundaries. Trigger on "is this correct?", "what are the edge cases?", "what does this function guarantee?", or when reviewing boundary behavior. NOT for trivial getters, simple mappings, or configuration code.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Contract Enforcer
|
|
7
|
+
|
|
8
|
+
A structured correctness framework based on Bertrand Meyer's *Design by Contract*. Before writing or modifying non-trivial functions, the agent articulates preconditions, postconditions, invariants, and failure contracts — then verifies the implementation satisfies them.
|
|
9
|
+
|
|
10
|
+
**Core principle:** Every function participates in a contract. The caller promises preconditions, the function promises postconditions, and both rely on invariants. Code without an explicit contract has an implicit one — and implicit contracts breed bugs.
|
|
11
|
+
|
|
12
|
+
## When to Use
|
|
13
|
+
|
|
14
|
+
- Writing or modifying a function with non-trivial logic
|
|
15
|
+
- Functions that handle external input, state transitions, or cross-module boundaries
|
|
16
|
+
- "Is this function correct?" / "What are the edge cases?"
|
|
17
|
+
- "What does this function guarantee?"
|
|
18
|
+
- Reviewing boundary behavior or error handling
|
|
19
|
+
|
|
20
|
+
**Not for:** Trivial getters, simple property access, configuration code, pure mappings with obvious behavior.
|
|
21
|
+
|
|
22
|
+
## The Process
|
|
23
|
+
|
|
24
|
+
Before presenting code for any non-trivial function, articulate the contract. This is not optional overhead — it's how you discover bugs before writing them.
|
|
25
|
+
|
|
26
|
+
### 1. Preconditions
|
|
27
|
+
|
|
28
|
+
What must be true about inputs for this function to behave correctly?
|
|
29
|
+
|
|
30
|
+
Go beyond types. Types catch shape errors; preconditions catch semantic errors:
|
|
31
|
+
- "Array must be sorted" (not just "array of numbers")
|
|
32
|
+
- "userId must correspond to an existing user in the database" (not just "string")
|
|
33
|
+
- "Amount must be positive and in a supported currency" (not just "number")
|
|
34
|
+
|
|
35
|
+
For each precondition, answer: **If this is violated, does the function fail loudly or silently corrupt state?** Silent corruption is the worst outcome — it means the contract exists but isn't enforced.
|
|
36
|
+
|
|
37
|
+
### 2. Postconditions
|
|
38
|
+
|
|
39
|
+
What does this function guarantee about its output?
|
|
40
|
+
|
|
41
|
+
- What relationship holds between input and output?
|
|
42
|
+
- What state changes are guaranteed to have happened?
|
|
43
|
+
- What side effects are promised? What side effects are promised NOT to occur?
|
|
44
|
+
- Is the guarantee conditional (e.g., "returns sorted array IF input was non-empty")?
|
|
45
|
+
|
|
46
|
+
A postcondition should be specific enough to write a test from. "Returns the correct result" is not a postcondition — it's a wish.
|
|
47
|
+
|
|
48
|
+
### 3. Invariants
|
|
49
|
+
|
|
50
|
+
What must remain true throughout execution?
|
|
51
|
+
|
|
52
|
+
- **Class/module invariants** this function must preserve (e.g., "the balance is never negative")
|
|
53
|
+
- **Data structure invariants** (e.g., "the heap property is maintained")
|
|
54
|
+
- **Resource invariants** (e.g., "the database connection is returned to the pool, even on error")
|
|
55
|
+
- **Concurrency invariants** (e.g., "the lock is held during the critical section")
|
|
56
|
+
|
|
57
|
+
If the function temporarily violates an invariant (e.g., rebalancing a tree), note where it's restored.
|
|
58
|
+
|
|
59
|
+
### 4. Failure Contract
|
|
60
|
+
|
|
61
|
+
When preconditions are violated, what happens? The answer must be explicit:
|
|
62
|
+
|
|
63
|
+
- **Throw** a specific, named error type — not a generic `Error("something went wrong")`
|
|
64
|
+
- **Return** an error union or result type — with the error case documented
|
|
65
|
+
- **Never** return a magic value (null, -1, empty string) without documenting what it means
|
|
66
|
+
|
|
67
|
+
"Undefined behavior" is not a contract. If you can't state what happens on bad input, you don't understand the function yet.
|
|
68
|
+
|
|
69
|
+
### 5. Verify Implementation
|
|
70
|
+
|
|
71
|
+
After articulating the contract, evaluate the actual code:
|
|
72
|
+
- Are preconditions **enforced** at the function boundary, or merely **assumed**?
|
|
73
|
+
- Does the implementation actually satisfy the postconditions for all valid inputs?
|
|
74
|
+
- Are invariants maintained across all code paths, including error paths?
|
|
75
|
+
- Does the failure behavior match the failure contract?
|
|
76
|
+
|
|
77
|
+
If the implementation doesn't match the contract, either fix the implementation or revise the contract — but never leave them misaligned.
|
|
78
|
+
|
|
79
|
+
## Output Format
|
|
80
|
+
|
|
81
|
+
Present contracts as a structured block alongside the code:
|
|
82
|
+
|
|
83
|
+
```
|
|
84
|
+
CONTRACT: functionName(param1, param2)
|
|
85
|
+
Requires: [preconditions — semantic, not just types]
|
|
86
|
+
Ensures: [postconditions — what the caller can rely on]
|
|
87
|
+
Invariant: [what is preserved across the call]
|
|
88
|
+
On violation: [specific failure behavior]
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Example:
|
|
92
|
+
|
|
93
|
+
```
|
|
94
|
+
CONTRACT: calculateShippingCost(order, destination)
|
|
95
|
+
Requires: order.items.length > 0, all items have weight > 0, destination is valid ISO 3166-1 alpha-2
|
|
96
|
+
Ensures: returns { cost: number > 0, currency: order.currency }, does not modify order
|
|
97
|
+
Invariant: shipping rules table is not mutated
|
|
98
|
+
On violation: throws InvalidOrderError (bad order) or UnsupportedDestinationError (bad destination)
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## Interaction Model
|
|
102
|
+
|
|
103
|
+
Produce contracts independently for anything derivable from code. But **ask the human** for semantic preconditions you can't determine alone:
|
|
104
|
+
|
|
105
|
+
- "Should this accept negative quantities, or is that a precondition violation?"
|
|
106
|
+
- "When the user isn't found, should this throw or return null? That's a contract decision — it affects every caller."
|
|
107
|
+
- "Is an empty array a valid input here, or should it be rejected?"
|
|
108
|
+
|
|
109
|
+
These are design decisions, not implementation details. The human owns them.
|
|
110
|
+
|
|
111
|
+
## Anti-Patterns
|
|
112
|
+
|
|
113
|
+
| Pattern | Problem |
|
|
114
|
+
|---------|---------|
|
|
115
|
+
| `if (!x) return null` without documentation | Silent failure — caller doesn't know null means "contract violated" vs "legitimate absence" |
|
|
116
|
+
| Defensive checks buried deep inside the function | Preconditions should be checked at the boundary, not scattered through logic |
|
|
117
|
+
| Return type is `any` or overly broad union | Postcondition is too vague to be useful — what does the caller actually get? |
|
|
118
|
+
| Function modifies its inputs without declaring it | Hidden side effect — violates caller's assumption of immutability |
|
|
119
|
+
| "Works for all inputs" | Almost certainly false — name the actual domain |
|
|
120
|
+
| Catching all exceptions and returning a default | Converts loud failure into silent corruption — the worst trade |
|
|
121
|
+
| Error messages that don't identify which precondition failed | "Invalid input" tells the caller nothing — name the violated condition |
|
|
122
|
+
|
|
123
|
+
## Guard Rails
|
|
124
|
+
|
|
125
|
+
**Scale to the function.** A 3-line pure function doesn't need a formal contract block. Use judgment — the overhead should be proportional to the function's complexity and the consequences of getting it wrong.
|
|
126
|
+
|
|
127
|
+
**Contracts are for humans, not compilers.** Write contracts that a developer reading the code can understand and verify. Don't write contracts in formal logic unless the team reads formal logic.
|
|
128
|
+
|
|
129
|
+
**Don't gold-plate.** If the function has one obvious precondition and one obvious postcondition, state them briefly. The format is a tool, not a ritual.
|
|
130
|
+
|
|
131
|
+
**Contracts evolve.** When requirements change, update the contract first, then the implementation. A stale contract is worse than no contract — it's actively misleading.
|
|
132
|
+
|
|
133
|
+
## The Meyer Principles (Reference)
|
|
134
|
+
|
|
135
|
+
1. **Separate commands from queries.** Functions that return values should not have side effects. Functions with side effects should not return values. When you must violate this, document it in the contract.
|
|
136
|
+
2. **Demand no more, promise no less.** Preconditions should be as weak as possible (accept the widest reasonable input). Postconditions should be as strong as possible (guarantee the most specific output).
|
|
137
|
+
3. **The client is responsible for preconditions.** The function does not need to "handle" invalid input gracefully — it needs to fail clearly and immediately when preconditions are violated.
|
|
138
|
+
4. **Inheritance respects contracts.** Subtypes may weaken preconditions (accept more) and strengthen postconditions (guarantee more), but never the reverse.
|
|
139
|
+
5. **Contracts are documentation that compiles.** The best contracts are checked at runtime (assertions, type narrowing, validation) — not just written in comments.
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "contract-enforcer",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Meyer-inspired Design by Contract — preconditions, postconditions, invariants, and failure contracts for non-trivial functions",
|
|
5
|
+
"author": "iceinvein",
|
|
6
|
+
"type": "prompt",
|
|
7
|
+
"tools": ["claude", "cursor", "codex", "gemini"],
|
|
8
|
+
"files": {
|
|
9
|
+
"prompt": "SKILL.md"
|
|
10
|
+
},
|
|
11
|
+
"install": {
|
|
12
|
+
"claude": {
|
|
13
|
+
"prompt": ".claude/skills/contract-enforcer/SKILL.md"
|
|
14
|
+
},
|
|
15
|
+
"cursor": {
|
|
16
|
+
"prompt": ".cursor/rules/contract-enforcer.mdc"
|
|
17
|
+
},
|
|
18
|
+
"codex": {
|
|
19
|
+
"prompt": "AGENTS.md",
|
|
20
|
+
"append": true
|
|
21
|
+
},
|
|
22
|
+
"gemini": {
|
|
23
|
+
"prompt": ".gemini/skills/contract-enforcer.md"
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
package/skills/index.json
CHANGED
|
@@ -16,5 +16,35 @@
|
|
|
16
16
|
"description": "Semantic code search, call hierarchy, dependency graphs, and impact analysis via MCP",
|
|
17
17
|
"type": "code",
|
|
18
18
|
"version": "1.0.0"
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"name": "contract-enforcer",
|
|
22
|
+
"description": "Meyer-inspired Design by Contract — preconditions, postconditions, invariants, and failure contracts for non-trivial functions",
|
|
23
|
+
"type": "prompt",
|
|
24
|
+
"version": "1.0.0"
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
"name": "complexity-accountant",
|
|
28
|
+
"description": "Ousterhout-inspired complexity analysis — deep vs shallow modules, complexity budget, justified abstractions",
|
|
29
|
+
"type": "prompt",
|
|
30
|
+
"version": "1.0.0"
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
"name": "module-secret-auditor",
|
|
34
|
+
"description": "Parnas-inspired information hiding analysis — module boundaries drawn by change-reason, not by noun or technical layer",
|
|
35
|
+
"type": "prompt",
|
|
36
|
+
"version": "1.0.0"
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
"name": "seam-finder",
|
|
40
|
+
"description": "Feathers-inspired legacy code modification — find seams, make minimal incisions, preserve existing behavior",
|
|
41
|
+
"type": "prompt",
|
|
42
|
+
"version": "1.0.0"
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
"name": "simplicity-razor",
|
|
46
|
+
"description": "Hickey-inspired simplicity analysis — simple vs easy, complecting detection, strand decomposition",
|
|
47
|
+
"type": "prompt",
|
|
48
|
+
"version": "1.0.0"
|
|
19
49
|
}
|
|
20
50
|
]
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: module-secret-auditor
|
|
3
|
+
description: Use when creating new files, directories, or modules, when reviewing project structure, when a change ripples across 3+ directories, or when the user asks "where should this code live?" or "how should I structure this?". Trigger on structural decisions and reorganizations.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Module Secret Auditor
|
|
7
|
+
|
|
8
|
+
A structural analysis framework based on David Parnas' information hiding principle. Every module should hide exactly one design decision that is likely to change. Module boundaries exist to isolate the impact of change — not to group related nouns or match technical layers.
|
|
9
|
+
|
|
10
|
+
**Core principle:** The question isn't "what is this module?" but "what secret does this module hide?" If you can't name the secret, the boundary is wrong.
|
|
11
|
+
|
|
12
|
+
## When to Use
|
|
13
|
+
|
|
14
|
+
- Creating new files, directories, or modules
|
|
15
|
+
- Reviewing or questioning existing project structure
|
|
16
|
+
- A change to one feature requires modifying files in 3+ directories
|
|
17
|
+
- "Where should this code live?" / "How should I structure this?"
|
|
18
|
+
- Proposing a refactoring that reorganizes code
|
|
19
|
+
- Post-mortem on a change that rippled further than expected
|
|
20
|
+
|
|
21
|
+
**Not for:** Naming conventions, code style, adding code to existing well-bounded modules, or trivial file organization (moving a constant).
|
|
22
|
+
|
|
23
|
+
## The Process
|
|
24
|
+
|
|
25
|
+
### 1. Identify the Secrets
|
|
26
|
+
|
|
27
|
+
For each module (file, directory, package), answer: **"What design decision does this module hide from the rest of the system?"**
|
|
28
|
+
|
|
29
|
+
**Good secrets** (specific, likely to change):
|
|
30
|
+
- "How authentication tokens are validated and refreshed"
|
|
31
|
+
- "The algorithm for calculating shipping costs"
|
|
32
|
+
- "How we communicate with the payment provider's API"
|
|
33
|
+
- "The format and schema of the analytics event payload"
|
|
34
|
+
|
|
35
|
+
**Bad secrets** (vague, organized by noun or layer):
|
|
36
|
+
- "All the models" — grouped by technical role, not by change-reason
|
|
37
|
+
- "Utility functions" — no shared secret, just a junk drawer
|
|
38
|
+
- "Things related to users" — too broad. Which *decision* about users?
|
|
39
|
+
- "The controller layer" — a technical layer isn't a secret
|
|
40
|
+
|
|
41
|
+
If you can't name the secret in one specific sentence, the module is hiding nothing — or hiding too many things.
|
|
42
|
+
|
|
43
|
+
### 2. One Change, One Module Test
|
|
44
|
+
|
|
45
|
+
For each likely change the system will face, trace the blast radius:
|
|
46
|
+
|
|
47
|
+
1. Name the change: "We switch from Stripe to a different payment provider"
|
|
48
|
+
2. List every module that must be modified
|
|
49
|
+
3. If > 1 module must change for a single business decision, the secret is leaked
|
|
50
|
+
|
|
51
|
+
Run this test for 3-5 realistic changes. The changes that matter most are the ones the human identifies as likely — ask them:
|
|
52
|
+
|
|
53
|
+
> "What are the things most likely to change in this system over the next 6 months?"
|
|
54
|
+
|
|
55
|
+
This is domain knowledge the code doesn't reveal. Without it, you're guessing at change-likelihood based on patterns.
|
|
56
|
+
|
|
57
|
+
### 3. Leak Detection
|
|
58
|
+
|
|
59
|
+
Scan for information hiding violations — places where a module's internal decisions are visible to the outside:
|
|
60
|
+
|
|
61
|
+
- **Exported internals:** A module exports data structures that encode its implementation choices (e.g., a database module exports raw row types instead of domain types)
|
|
62
|
+
- **Caller knowledge:** A caller must understand the internal state machine or initialization order of another module to use it correctly
|
|
63
|
+
- **Configuration leaks:** File formats, protocol details, or algorithm parameters visible across module boundaries
|
|
64
|
+
- **Connector types:** Types that exist solely to shuttle data between modules that should be independent — these are often symptoms of a missing or misdrawn boundary
|
|
65
|
+
|
|
66
|
+
### 4. Recommend by Secret
|
|
67
|
+
|
|
68
|
+
When proposing structure, organize around change-reasons, not around nouns or technical layers.
|
|
69
|
+
|
|
70
|
+
Instead of:
|
|
71
|
+
```
|
|
72
|
+
models/user.ts
|
|
73
|
+
services/userService.ts
|
|
74
|
+
controllers/userController.ts
|
|
75
|
+
repositories/userRepository.ts
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Propose:
|
|
79
|
+
```
|
|
80
|
+
auth/ — secret: how identity is verified and sessions are managed
|
|
81
|
+
pricing/ — secret: how costs are calculated and discounts applied
|
|
82
|
+
notifications/ — secret: how and when users are notified, via which channels
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Each directory answers: **"If this decision changes, only this directory is affected."**
|
|
86
|
+
|
|
87
|
+
This doesn't mean you can't have shared infrastructure (database client, HTTP framework, logging). Infrastructure modules hide *infrastructure secrets* — "how we talk to Postgres" or "how we structure HTTP responses." The test still applies: when the infrastructure decision changes, only the infrastructure module changes.
|
|
88
|
+
|
|
89
|
+
## Interaction Model
|
|
90
|
+
|
|
91
|
+
The agent analyzes code structure independently but must ask the human one critical question:
|
|
92
|
+
|
|
93
|
+
> "What are the things most likely to change in this system over the next 6 months?"
|
|
94
|
+
|
|
95
|
+
This determines what the secrets should be. The agent can assess *structural* quality on its own (leak detection, coupling analysis), but *change-likelihood* is a domain judgment.
|
|
96
|
+
|
|
97
|
+
If the human isn't sure, offer concrete prompts:
|
|
98
|
+
- "Will you likely switch any external services (payment, email, auth provider)?"
|
|
99
|
+
- "Are there business rules that are still being figured out?"
|
|
100
|
+
- "Is there a part of the system that gets changed every sprint?"
|
|
101
|
+
|
|
102
|
+
## Output Format
|
|
103
|
+
|
|
104
|
+
```
|
|
105
|
+
MODULE AUDIT: [module or directory name]
|
|
106
|
+
Secret: [the design decision this module hides — one sentence]
|
|
107
|
+
Leak check: [leaked? where does the secret escape?]
|
|
108
|
+
Change test: [for the most likely change — how many modules would be affected?]
|
|
109
|
+
Verdict: [well-bounded / leaking / misdrawn / no clear secret]
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## The Parnas Litmus Tests (Reference)
|
|
113
|
+
|
|
114
|
+
| Question | Good answer | Bad answer |
|
|
115
|
+
|----------|-------------|------------|
|
|
116
|
+
| What secret does this module hide? | A specific design decision | "User-related things" |
|
|
117
|
+
| If the secret changes, what else breaks? | Nothing outside this module | Multiple other modules |
|
|
118
|
+
| Can you describe the module's interface without revealing the secret? | Yes — the interface is abstract | No — callers need to know internals |
|
|
119
|
+
| Why are these two things in the same module? | They share a secret | They share a noun |
|
|
120
|
+
| Why are these two things in different modules? | They hide different secrets | They're in different technical layers |
|
|
121
|
+
|
|
122
|
+
## Guard Rails
|
|
123
|
+
|
|
124
|
+
**Don't reorganize for its own sake.** If the current structure works and changes don't ripple, the boundaries are fine — even if they don't match textbook information hiding. Parnas' principle is a tool for evaluating structure, not a mandate to restructure everything.
|
|
125
|
+
|
|
126
|
+
**Respect team conventions.** If the team uses MVC and it's working, don't unilaterally propose feature-based organization. Present the analysis and let the team decide.
|
|
127
|
+
|
|
128
|
+
**Infrastructure is a valid secret.** "How we talk to the database" is a real secret that deserves its own module. Not everything needs to be a business domain boundary.
|
|
129
|
+
|
|
130
|
+
**Small projects get simple boundaries.** A 10-file project doesn't need 10 modules with formal interfaces. The secret principle scales — apply it at whatever granularity fits the codebase.
|
|
131
|
+
|
|
132
|
+
## Common Mistakes
|
|
133
|
+
|
|
134
|
+
| Mistake | Fix |
|
|
135
|
+
|---------|-----|
|
|
136
|
+
| Organizing by technical layer (models/, services/, controllers/) | Organize by change-reason. Ask: "what secret does each layer hide?" |
|
|
137
|
+
| Creating a module per entity (UserModule, OrderModule, ProductModule) | Entities aren't secrets. Ask: "which *decisions* about users change independently?" |
|
|
138
|
+
| "Utils" or "shared" directories | Each utility should live near its caller. If two callers need it, that's a signal of a shared secret — name it. |
|
|
139
|
+
| Assuming current structure is wrong | Always evaluate before prescribing. Working code with imperfect boundaries beats reorganized code that breaks. |
|
|
140
|
+
| Ignoring the human's change-likelihood input | Domain knowledge trumps structural analysis. If the human says "pricing never changes," don't optimize for pricing changes. |
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "module-secret-auditor",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Parnas-inspired information hiding analysis — module boundaries drawn by change-reason, not by noun or technical layer",
|
|
5
|
+
"author": "iceinvein",
|
|
6
|
+
"type": "prompt",
|
|
7
|
+
"tools": ["claude", "cursor", "codex", "gemini"],
|
|
8
|
+
"files": {
|
|
9
|
+
"prompt": "SKILL.md"
|
|
10
|
+
},
|
|
11
|
+
"install": {
|
|
12
|
+
"claude": {
|
|
13
|
+
"prompt": ".claude/skills/module-secret-auditor/SKILL.md"
|
|
14
|
+
},
|
|
15
|
+
"cursor": {
|
|
16
|
+
"prompt": ".cursor/rules/module-secret-auditor.mdc"
|
|
17
|
+
},
|
|
18
|
+
"codex": {
|
|
19
|
+
"prompt": "AGENTS.md",
|
|
20
|
+
"append": true
|
|
21
|
+
},
|
|
22
|
+
"gemini": {
|
|
23
|
+
"prompt": ".gemini/skills/module-secret-auditor.md"
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: seam-finder
|
|
3
|
+
description: Use when modifying existing code the agent didn't write, when the agent's instinct is to rewrite or heavily refactor, when adding tests to untested code, or when working in a codebase with minimal coverage. Trigger on "change this behavior", "add a feature to this", or any modification of legacy or unfamiliar code. NOT for greenfield code or code the agent just wrote in this session.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Seam Finder
|
|
7
|
+
|
|
8
|
+
A structured approach to modifying existing code based on Michael Feathers' *Working Effectively with Legacy Code*. Before changing existing code, find the seams — places where behavior can be altered without editing the code at that point. Seams enable safe modification: you can sense what code does, separate dependencies, and change behavior through the narrowest possible incision.
|
|
9
|
+
|
|
10
|
+
**Core principle:** Existing code is not an obstacle to be rewritten. It's a system with embedded knowledge — implicit behavior, battle-tested edge case handling, and hard-won correctness. The default is *preserve*, not *replace*. Find the seam, make the minimal incision.
|
|
11
|
+
|
|
12
|
+
## When to Use
|
|
13
|
+
|
|
14
|
+
- Modifying existing code you didn't write (or wrote long ago)
|
|
15
|
+
- "Change this behavior" / "Add a feature to this existing code"
|
|
16
|
+
- When your first instinct is to rewrite or heavily refactor
|
|
17
|
+
- Adding tests to untested code
|
|
18
|
+
- Working in a codebase with minimal test coverage
|
|
19
|
+
- When a modification feels risky and you want a safe approach
|
|
20
|
+
|
|
21
|
+
**Not for:** Greenfield code, code you just wrote in this session, throwaway prototypes, or code the team has explicitly decided to rewrite.
|
|
22
|
+
|
|
23
|
+
## The Process
|
|
24
|
+
|
|
25
|
+
### 1. Identify the Seam Types
|
|
26
|
+
|
|
27
|
+
Before modifying existing code, survey the available seams:
|
|
28
|
+
|
|
29
|
+
**Object seam** — Can behavior be changed by passing a different object or dependency?
|
|
30
|
+
- Look for: constructor parameters, function arguments that accept interfaces, injected services, callback parameters, strategy patterns already in place
|
|
31
|
+
- This is the most common seam in object-oriented code. If a dependency is passed in, you can substitute it.
|
|
32
|
+
|
|
33
|
+
**Preprocessing seam** — Can the inputs be transformed before they reach this code?
|
|
34
|
+
- Look for: middleware chains, interceptors, data transformation layers, event handlers, input normalization steps
|
|
35
|
+
- The code doesn't change — its inputs do. This is safe because the original code path is untouched.
|
|
36
|
+
|
|
37
|
+
**Link seam** — Can a different implementation be substituted at the module or import level?
|
|
38
|
+
- Look for: imports that could be aliased, environment-based module resolution, plugin architectures, dependency injection containers, test mocks at the module level
|
|
39
|
+
- In dynamic languages, this is powerful. In static languages, it requires more planning.
|
|
40
|
+
|
|
41
|
+
If no seams exist, you may need to create one — but creating a seam is a smaller change than rewriting the code. Introduce one parameter, extract one function, add one interface.
|
|
42
|
+
|
|
43
|
+
### 2. Classify the Goal
|
|
44
|
+
|
|
45
|
+
What are you trying to do? The answer determines which seam to use.
|
|
46
|
+
|
|
47
|
+
**Sensing** — You need to observe what the code does.
|
|
48
|
+
- Goal: understand behavior, add tests, verify assumptions before changing anything
|
|
49
|
+
- Strategy: find a seam that lets you intercept outputs or side effects
|
|
50
|
+
- Example: pass a test double that records calls instead of making real network requests
|
|
51
|
+
|
|
52
|
+
**Separation** — You need to break a dependency so one side can change independently.
|
|
53
|
+
- Goal: modify one part without affecting another
|
|
54
|
+
- Strategy: find a seam that decouples the parts
|
|
55
|
+
- Example: extract a function parameter so the algorithm can be tested without its data source
|
|
56
|
+
|
|
57
|
+
### 3. Minimal Incision
|
|
58
|
+
|
|
59
|
+
Identify the **smallest change** that achieves the goal:
|
|
60
|
+
|
|
61
|
+
- NOT "refactor the function" — "extract this 4-line block behind a function parameter so we can vary its behavior"
|
|
62
|
+
- NOT "rewrite with better patterns" — "add one parameter that lets us substitute this dependency in tests"
|
|
63
|
+
- NOT "clean up while we're here" — "change only what we were asked to change"
|
|
64
|
+
|
|
65
|
+
**The one-sentence test:** Can you describe the change in one sentence? If it takes a paragraph, the incision is too large. Break it down.
|
|
66
|
+
|
|
67
|
+
### 4. Preserve the Unknown
|
|
68
|
+
|
|
69
|
+
Existing code has behavior you don't fully understand. Treat it with respect:
|
|
70
|
+
|
|
71
|
+
- **Assume load-bearing until proven otherwise.** That weird null check? Probably caught a production bug. That redundant-looking condition? Probably handles a case you haven't seen.
|
|
72
|
+
- **Side effects are features.** Logging, metrics, cache warming, audit trails — if you don't understand why a side effect exists, don't remove it.
|
|
73
|
+
- **Timing and ordering matter.** Reordering operations can break subtle invariants. If the original code does A before B, keep that order unless you can prove it doesn't matter.
|
|
74
|
+
- **Error swallowing may be intentional.** A catch-all that silently ignores errors looks wrong, but it might be preventing a cascade failure that took days to debug.
|
|
75
|
+
|
|
76
|
+
When in doubt: preserve, annotate with a question, and ask the human.
|
|
77
|
+
|
|
78
|
+
### 5. Present the Surgical Plan
|
|
79
|
+
|
|
80
|
+
Before making any changes, present the plan:
|
|
81
|
+
|
|
82
|
+
```
|
|
83
|
+
SEAM ANALYSIS: [what needs to change]
|
|
84
|
+
Seam type: [object / preprocessing / link / new seam needed]
|
|
85
|
+
Location: [file:line where behavior can be altered]
|
|
86
|
+
Incision: [the minimal change — one sentence]
|
|
87
|
+
Preserved: [what existing behavior is explicitly kept]
|
|
88
|
+
Risk: [what could break — be specific]
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Example:
|
|
92
|
+
|
|
93
|
+
```
|
|
94
|
+
SEAM ANALYSIS: Add retry logic to payment processing
|
|
95
|
+
Seam type: object — PaymentProcessor accepts a gateway parameter
|
|
96
|
+
Location: src/payments/processor.ts:42 (constructor accepts PaymentGateway)
|
|
97
|
+
Incision: Wrap the existing gateway in a RetryingGateway decorator that retries on transient errors
|
|
98
|
+
Preserved: all existing payment logic, error handling, and logging untouched
|
|
99
|
+
Risk: retry could cause duplicate charges if the gateway doesn't support idempotency keys — verify before implementing
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## Interaction Model
|
|
103
|
+
|
|
104
|
+
Decision engine. When the agent encounters existing code it needs to modify, it produces the seam analysis before making any changes. The human sees the surgical plan — seam type, incision point, what's preserved, what's at risk — and approves before cuts are made.
|
|
105
|
+
|
|
106
|
+
## Red Flags
|
|
107
|
+
|
|
108
|
+
| Agent impulse | Better approach |
|
|
109
|
+
|---------------|----------------|
|
|
110
|
+
| "Let me rewrite this function" | Find a seam. Make a minimal change. |
|
|
111
|
+
| "I'll refactor this to be cleaner" | Were you asked to refactor? If not — preserve and extend. |
|
|
112
|
+
| "This code is messy, let me fix it" | Messy code that works has value. Messy code that you break has negative value. |
|
|
113
|
+
| "I'll wrap this in a new abstraction" | Does the abstraction help the change, or does it just hide code you don't understand? |
|
|
114
|
+
| "Let me add types to make this safer" | Adding types to code you don't fully understand can introduce false confidence — the types might be wrong. |
|
|
115
|
+
| "I'll extract a class for this" | Is extraction the minimal incision, or are you redesigning while the patient is on the table? |
|
|
116
|
+
| "Let me clean up these comments/formatting" | Unrelated formatting changes make the diff noisy and hide the real change. Don't. |
|
|
117
|
+
| "This would be simpler if I just started over" | Simpler to write, maybe. Simpler to be correct? Almost never. |
|
|
118
|
+
|
|
119
|
+
## The Feathers Techniques (Reference)
|
|
120
|
+
|
|
121
|
+
1. **Sprout Method** — Need new behavior? Write a new function and call it from the existing code. Don't modify the existing logic — add alongside it.
|
|
122
|
+
|
|
123
|
+
2. **Wrap Method** — Need behavior before or after existing code? Rename the original, create a new function with the old name that calls the original plus the new behavior.
|
|
124
|
+
|
|
125
|
+
3. **Sprout Class** — Same as Sprout Method but at the class level. Create a new class for the new behavior and instantiate it from the existing code.
|
|
126
|
+
|
|
127
|
+
4. **Scratch Refactoring** — Need to understand code? Refactor it aggressively in a throwaway branch. Delete the branch. Now make your real, minimal change with the understanding you gained.
|
|
128
|
+
|
|
129
|
+
5. **Characterization Tests** — Before changing code, write tests that document its current behavior (even if that behavior seems wrong). These tests protect against accidental changes.
|
|
130
|
+
|
|
131
|
+
6. **The Legacy Code Dilemma** — To change code safely, you need tests. To add tests, you often need to change code. Seams break this cycle by creating safe points of intervention.
|
|
132
|
+
|
|
133
|
+
## Guard Rails
|
|
134
|
+
|
|
135
|
+
**Don't moralize about code quality.** The skill is about making safe, effective changes — not about judging the code you're working with.
|
|
136
|
+
|
|
137
|
+
**Small seams first.** If you need to create a seam, start with the smallest possible one. You can always widen it later.
|
|
138
|
+
|
|
139
|
+
**Characterize before changing.** If the code has no tests and you're about to modify it, write at least one characterization test first. Even a rough test that asserts current output is better than changing code with no safety net.
|
|
140
|
+
|
|
141
|
+
**Ask about the unknown.** When you encounter behavior you don't understand, ask the human before removing or modifying it. "I see this null check on line 47 — do you know if there's a case where this value is actually null in production?"
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "seam-finder",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Feathers-inspired legacy code modification — find seams, make minimal incisions, preserve existing behavior",
|
|
5
|
+
"author": "iceinvein",
|
|
6
|
+
"type": "prompt",
|
|
7
|
+
"tools": ["claude", "cursor", "codex", "gemini"],
|
|
8
|
+
"files": {
|
|
9
|
+
"prompt": "SKILL.md"
|
|
10
|
+
},
|
|
11
|
+
"install": {
|
|
12
|
+
"claude": {
|
|
13
|
+
"prompt": ".claude/skills/seam-finder/SKILL.md"
|
|
14
|
+
},
|
|
15
|
+
"cursor": {
|
|
16
|
+
"prompt": ".cursor/rules/seam-finder.mdc"
|
|
17
|
+
},
|
|
18
|
+
"codex": {
|
|
19
|
+
"prompt": "AGENTS.md",
|
|
20
|
+
"append": true
|
|
21
|
+
},
|
|
22
|
+
"gemini": {
|
|
23
|
+
"prompt": ".gemini/skills/seam-finder.md"
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: simplicity-razor
|
|
3
|
+
description: Use when the agent recommends a library, framework, or pattern, when proposing architecture or technology choices, when the user asks "should I use X?", or when code has many dependencies for its scope. Trigger on technology decisions and dependency introductions. NOT for choices the team has already committed to.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Simplicity Razor
|
|
7
|
+
|
|
8
|
+
A decision framework based on Rich Hickey's *Simple Made Easy*. Distinguishes between "simple" (not interleaved — one concern per construct) and "easy" (near at hand — familiar, convenient). These are orthogonal properties. Easy things can be complex. Hard things can be simple. AI agents and developers consistently reach for easy over simple, accumulating *complecting* — the braiding together of independent concerns until the system becomes impossible to reason about.
|
|
9
|
+
|
|
10
|
+
**Core principle:** Before recommending any tool, library, pattern, or approach, name its strands. If they're braided together such that you can't change one without affecting the others, the solution is complex — regardless of how easy it is to use. Simplicity is a choice, not an accident.
|
|
11
|
+
|
|
12
|
+
## When to Use
|
|
13
|
+
|
|
14
|
+
- Recommending a library, framework, or pattern
|
|
15
|
+
- Proposing an architecture or technology choice
|
|
16
|
+
- "Should I use X?" where X is a popular tool or approach
|
|
17
|
+
- Code has many dependencies relative to its scope
|
|
18
|
+
- A solution feels "enterprise-grade" for a small problem
|
|
19
|
+
- Evaluating competing approaches
|
|
20
|
+
|
|
21
|
+
**Not for:** Choices the team has already committed to (don't relitigate the framework), trivial utility selections (date formatting library), or ecosystem-mandated tools (your CI provider's required CLI).
|
|
22
|
+
|
|
23
|
+
## The Process
|
|
24
|
+
|
|
25
|
+
### 1. Name the Strands
|
|
26
|
+
|
|
27
|
+
For any proposed solution, identify the independent concerns it handles:
|
|
28
|
+
|
|
29
|
+
- A function that does validation AND transformation AND logging: **3 strands**
|
|
30
|
+
- A library that couples data fetching with caching with state management: **3 strands**
|
|
31
|
+
- A pattern that ties request handling to serialization to error formatting: **3 strands**
|
|
32
|
+
|
|
33
|
+
Each strand should be nameable in 2-3 words. If you can't name the strands, you don't understand the solution well enough to recommend it.
|
|
34
|
+
|
|
35
|
+
**Naming exercise:** For a proposed ORM:
|
|
36
|
+
- Strand 1: query construction
|
|
37
|
+
- Strand 2: result mapping
|
|
38
|
+
- Strand 3: schema migration
|
|
39
|
+
- Strand 4: connection management
|
|
40
|
+
- Strand 5: query caching
|
|
41
|
+
- Strand 6: change tracking
|
|
42
|
+
|
|
43
|
+
That's 6 strands. Are they all braided? Can you use query construction without change tracking? If not, you've bought 6 concerns to solve 1 problem.
|
|
44
|
+
|
|
45
|
+
### 2. Complecting Test
|
|
46
|
+
|
|
47
|
+
Are any strands braided together such that you can't change one without affecting the others?
|
|
48
|
+
|
|
49
|
+
Ask for each pair:
|
|
50
|
+
- Can you change the validation logic without touching the transformation? **If no: complected.**
|
|
51
|
+
- Can you swap the data fetching strategy without reconfiguring the cache? **If no: complected.**
|
|
52
|
+
- Can you modify error formatting without changing request handling? **If no: complected.**
|
|
53
|
+
|
|
54
|
+
**The substitution test:** Can you replace one strand with a different implementation without modifying the code that handles the other strands? If you can, the strands are composed (good). If you can't, they're complected (investigate).
|
|
55
|
+
|
|
56
|
+
### 3. Easy vs. Simple Audit
|
|
57
|
+
|
|
58
|
+
For the proposed solution, evaluate both dimensions independently:
|
|
59
|
+
|
|
60
|
+
**Easy signals** (not inherently bad, but suspicious when used as justification):
|
|
61
|
+
|
|
62
|
+
| Signal | Why it's suspicious |
|
|
63
|
+
|--------|-------------------|
|
|
64
|
+
| "Everyone uses it" | Popularity ≠ simplicity. Popular tools are often complex tools with good marketing. |
|
|
65
|
+
| "It has a nice API" | Nice APIs can hide enormous complecting behind convenience methods. |
|
|
66
|
+
| "It's one line to add" | One line to add, but how many concepts to understand? |
|
|
67
|
+
| "It's the standard approach" | Standards emerge from momentum, not from analysis. |
|
|
68
|
+
| "There's a plugin for that" | Plugin ecosystems are dependency graphs you'll inherit. |
|
|
69
|
+
| "It handles everything" | "Everything" means braided concerns. What if you don't need everything? |
|
|
70
|
+
|
|
71
|
+
**Simple signals** (what to aim for):
|
|
72
|
+
|
|
73
|
+
| Signal | Why it's good |
|
|
74
|
+
|--------|-------------|
|
|
75
|
+
| "Each piece does one thing" | Single-strand constructs compose freely. |
|
|
76
|
+
| "I can replace this part without touching that part" | Strands are separated, not braided. |
|
|
77
|
+
| "The data flows in one direction" | Unidirectional flow is inherently less complected. |
|
|
78
|
+
| "There's no hidden state" | State that you can see is state you can reason about. |
|
|
79
|
+
| "I can understand this without reading my dependencies' source" | The abstraction boundary holds. |
|
|
80
|
+
| "I chose each piece deliberately" | Composition by choice, not by inheritance. |
|
|
81
|
+
|
|
82
|
+
### 4. Decomplect or Justify
|
|
83
|
+
|
|
84
|
+
If the solution complects, the agent must either:
|
|
85
|
+
|
|
86
|
+
**Decomplect** — Use separate, composable pieces:
|
|
87
|
+
- Replace the all-in-one library with focused tools that each handle one strand
|
|
88
|
+
- Split the multi-concern function into single-purpose functions that compose
|
|
89
|
+
- Use data-oriented designs that separate data from behavior
|
|
90
|
+
|
|
91
|
+
**Justify** — Explain *specifically* why the complecting is worth it:
|
|
92
|
+
- A measured performance requirement that the composed version can't meet
|
|
93
|
+
- The team has standardized on it and switching would be more disruptive than the complecting
|
|
94
|
+
- The braided concerns genuinely change together — not hypothetically, but *actually*, based on real change history
|
|
95
|
+
|
|
96
|
+
**Justifications that do NOT count:**
|
|
97
|
+
- "It's industry standard" — standards can be complex
|
|
98
|
+
- "Everyone uses it" — popularity is not simplicity
|
|
99
|
+
- "It's easier" — that is *literally the trap this skill exists to catch*
|
|
100
|
+
- "It's well-maintained" — maintenance quality doesn't affect complecting
|
|
101
|
+
- "It has good docs" — documented complexity is still complexity
|
|
102
|
+
|
|
103
|
+
### When the Human Requests Something Complex
|
|
104
|
+
|
|
105
|
+
Don't refuse. Present the analysis and ask:
|
|
106
|
+
|
|
107
|
+
> "Here's what [X] braids together: [strands]. Is that tradeoff worth it for your situation?"
|
|
108
|
+
|
|
109
|
+
The human may have context you don't — team familiarity, contractual obligations, performance constraints. Respect their decision. The skill ensures the decision is *informed*, not that it's *yours*.
|
|
110
|
+
|
|
111
|
+
## Output Format
|
|
112
|
+
|
|
113
|
+
```
|
|
114
|
+
SIMPLICITY: [proposed solution]
|
|
115
|
+
Strands: [list independent concerns involved]
|
|
116
|
+
Complected: [which strands are braided — or "none"]
|
|
117
|
+
Easy score: [high / medium / low]
|
|
118
|
+
Simple score: [high / medium / low]
|
|
119
|
+
Verdict: [use it / decomplect / justify]
|
|
120
|
+
Alternative: [if complected — what's the simpler composition?]
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
Example:
|
|
124
|
+
|
|
125
|
+
```
|
|
126
|
+
SIMPLICITY: Using Redux + Redux Toolkit + RTK Query for app state
|
|
127
|
+
Strands: UI state, server cache, async data fetching, optimistic updates, devtools
|
|
128
|
+
Complected: server cache is braided with UI state (same store, same reducers)
|
|
129
|
+
Easy score: high (popular, well-documented, team knows it)
|
|
130
|
+
Simple score: low (5 braided concerns behind one store abstraction)
|
|
131
|
+
Verdict: decomplect
|
|
132
|
+
Alternative: React state for UI + TanStack Query for server cache — each strand handled by a focused tool
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
## The Hickey Vocabulary (Reference)
|
|
136
|
+
|
|
137
|
+
| Term | Meaning | Example |
|
|
138
|
+
|------|---------|---------|
|
|
139
|
+
| **Simple** | Not interleaved; one braid | A pure function: data in, data out |
|
|
140
|
+
| **Easy** | Near at hand; familiar | An ORM with convention-over-configuration |
|
|
141
|
+
| **Complex** | Interleaved; multiple braids | A function that fetches, caches, transforms, and logs |
|
|
142
|
+
| **Complecting** | The act of braiding together | Adding "just one more concern" to an existing module |
|
|
143
|
+
| **Decomplecting** | The act of separating braids | Splitting a god module into composable parts |
|
|
144
|
+
| **Compose** | Combining simple things | Piping focused functions together |
|
|
145
|
+
| **Artifact** | What you build | Code, modules, services |
|
|
146
|
+
| **Construct** | Tools you use to build | Languages, libraries, patterns |
|
|
147
|
+
|
|
148
|
+
## Guard Rails
|
|
149
|
+
|
|
150
|
+
**Simple ≠ minimal.** A well-composed system of 10 focused tools can be simpler than a 3-tool system where each tool braids 5 concerns. Simplicity is about separation, not about having fewer things.
|
|
151
|
+
|
|
152
|
+
**Don't relitigate committed decisions.** If the team has chosen React, you don't run the simplicity razor on React. You run it on *new* choices being made within the React ecosystem.
|
|
153
|
+
|
|
154
|
+
**Acknowledge the tradeoff.** Sometimes easy IS the right choice — when time is the binding constraint, when the team's familiarity reduces the effective complexity, when the complecting is genuinely harmless for this project's expected lifetime. Say so when it's true.
|
|
155
|
+
|
|
156
|
+
**Composition has costs too.** Gluing together 8 tiny libraries has its own complexity — version management, interface adaptation, debugging across boundaries. The simplicity razor checks for braided concerns, not for an absolute count of dependencies.
|
|
157
|
+
|
|
158
|
+
## Common Mistakes
|
|
159
|
+
|
|
160
|
+
| Mistake | Fix |
|
|
161
|
+
|---------|-----|
|
|
162
|
+
| Treating "popular" as "proven simple" | Popularity signals ease, not simplicity. Evaluate strands. |
|
|
163
|
+
| Decomplecting into too many tiny pieces | Composition has costs. Find the right granularity. |
|
|
164
|
+
| Rejecting all libraries as "complex" | Libraries aren't inherently bad. Libraries that braid unrelated concerns are bad. |
|
|
165
|
+
| Running this analysis on every 3-line decision | Scale to stakes. A date formatting library doesn't need strand analysis. |
|
|
166
|
+
| Refusing the human's choice without explanation | Present the analysis. Respect the decision. You inform, you don't veto. |
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "simplicity-razor",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Hickey-inspired simplicity analysis — simple vs easy, complecting detection, strand decomposition",
|
|
5
|
+
"author": "iceinvein",
|
|
6
|
+
"type": "prompt",
|
|
7
|
+
"tools": ["claude", "cursor", "codex", "gemini"],
|
|
8
|
+
"files": {
|
|
9
|
+
"prompt": "SKILL.md"
|
|
10
|
+
},
|
|
11
|
+
"install": {
|
|
12
|
+
"claude": {
|
|
13
|
+
"prompt": ".claude/skills/simplicity-razor/SKILL.md"
|
|
14
|
+
},
|
|
15
|
+
"cursor": {
|
|
16
|
+
"prompt": ".cursor/rules/simplicity-razor.mdc"
|
|
17
|
+
},
|
|
18
|
+
"codex": {
|
|
19
|
+
"prompt": "AGENTS.md",
|
|
20
|
+
"append": true
|
|
21
|
+
},
|
|
22
|
+
"gemini": {
|
|
23
|
+
"prompt": ".gemini/skills/simplicity-razor.md"
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|