rhachet-roles-ehmpathy 1.17.14 → 1.17.16
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/dist/domain.roles/mechanic/briefs/practices/code.prod/evolvable.procedures/rule.require.hook-wrapper-pattern.md +109 -0
- package/dist/domain.roles/mechanic/briefs/practices/work.flow/refactor/rule.prefer.sedreplace-for-renames.md +19 -3
- package/dist/domain.roles/mechanic/skills/claude.tools/sedreplace.sh +3 -1
- package/package.json +1 -1
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
### .rule = require-hook-wrapper-pattern
|
|
2
|
+
|
|
3
|
+
#### .what
|
|
4
|
+
wrap procedures with hooks via composition, not inline decoration
|
|
5
|
+
|
|
6
|
+
#### .pattern
|
|
7
|
+
```ts
|
|
8
|
+
const _procedureName = (input: {...}, context: {...}) => {
|
|
9
|
+
// implementation
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export const procedureName = withHook(_procedureName);
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
#### .why
|
|
16
|
+
|
|
17
|
+
this pattern minimizes code diffs when hooks are added, changed, or removed:
|
|
18
|
+
|
|
19
|
+
- **add a hook** → 1 line changes (the export line)
|
|
20
|
+
- **remove a hook** → 1 line changes (the export line)
|
|
21
|
+
- **change hook order** → 1 line changes (the export line)
|
|
22
|
+
|
|
23
|
+
the alternative — inline wrapping at the function declaration — causes the entire function body to shift indentation, which produces noisy diffs that obscure the actual change.
|
|
24
|
+
|
|
25
|
+
#### .examples
|
|
26
|
+
|
|
27
|
+
##### ✅ good — wrapper pattern
|
|
28
|
+
|
|
29
|
+
```ts
|
|
30
|
+
/**
|
|
31
|
+
* .what = sends an invoice to the customer
|
|
32
|
+
* .why = triggers billing workflow
|
|
33
|
+
*/
|
|
34
|
+
const _sendInvoice = async (
|
|
35
|
+
input: { invoice: Invoice },
|
|
36
|
+
context: { log: LogMethods },
|
|
37
|
+
): Promise<{ sent: boolean }> => {
|
|
38
|
+
// implementation stays clean and unindented
|
|
39
|
+
context.log.info('sending invoice', { invoiceId: input.invoice.id });
|
|
40
|
+
return { sent: true };
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
export const sendInvoice = withLogTrail(_sendInvoice);
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
##### ✅ good — multiple hooks composed
|
|
47
|
+
|
|
48
|
+
```ts
|
|
49
|
+
const _processPayment = async (
|
|
50
|
+
input: { payment: Payment },
|
|
51
|
+
context: { log: LogMethods },
|
|
52
|
+
): Promise<{ success: boolean }> => {
|
|
53
|
+
// implementation
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
// hooks compose right-to-left: withRetry runs first, then withLogTrail
|
|
57
|
+
export const processPayment = withLogTrail(
|
|
58
|
+
withRetry(_processPayment, { maxAttempts: 3 }),
|
|
59
|
+
);
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
##### ⛔ bad — inline decoration
|
|
63
|
+
|
|
64
|
+
```ts
|
|
65
|
+
// ⛔ adding/removing the wrapper shifts the entire function body
|
|
66
|
+
export const sendInvoice = withLogTrail(async (
|
|
67
|
+
input: { invoice: Invoice },
|
|
68
|
+
context: { log: LogMethods },
|
|
69
|
+
): Promise<{ sent: boolean }> => {
|
|
70
|
+
// every line here will show as "changed" in the diff
|
|
71
|
+
// when the hook is added or removed
|
|
72
|
+
context.log.info('sending invoice', { invoiceId: input.invoice.id });
|
|
73
|
+
return { sent: true };
|
|
74
|
+
});
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
#### .diff comparison
|
|
78
|
+
|
|
79
|
+
when adding `withLogTrail` to an existing function:
|
|
80
|
+
|
|
81
|
+
**wrapper pattern diff (clean):**
|
|
82
|
+
```diff
|
|
83
|
+
- export const sendInvoice = _sendInvoice;
|
|
84
|
+
+ export const sendInvoice = withLogTrail(_sendInvoice);
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
**inline pattern diff (noisy):**
|
|
88
|
+
```diff
|
|
89
|
+
- export const sendInvoice = async (
|
|
90
|
+
+ export const sendInvoice = withLogTrail(async (
|
|
91
|
+
input: { invoice: Invoice },
|
|
92
|
+
context: { log: LogMethods },
|
|
93
|
+
): Promise<{ sent: boolean }> => {
|
|
94
|
+
context.log.info('sending invoice', { invoiceId: input.invoice.id });
|
|
95
|
+
return { sent: true };
|
|
96
|
+
- };
|
|
97
|
+
+ });
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
the wrapper pattern produces a 1-line diff; the inline pattern touches every line of the function.
|
|
101
|
+
|
|
102
|
+
#### .naming
|
|
103
|
+
|
|
104
|
+
- prefix the unwrapped procedure with `_` (e.g., `_sendInvoice`)
|
|
105
|
+
- export the wrapped version without prefix (e.g., `sendInvoice`)
|
|
106
|
+
- this signals that `_sendInvoice` is internal and should not be imported directly
|
|
107
|
+
|
|
108
|
+
#### .enforcement
|
|
109
|
+
inline hook decoration = **BLOCKER**
|
|
@@ -30,20 +30,36 @@ the same rename via sedreplace = 2 tool calls (dry-run + execute) = minimal toke
|
|
|
30
30
|
# dry-run first (default) - see what would change
|
|
31
31
|
npx rhachet run --skill sedreplace --old "oldName" --new "newName"
|
|
32
32
|
|
|
33
|
-
# filter to specific file types
|
|
34
|
-
npx rhachet run --skill sedreplace --old "oldName" --new "newName" --glob "
|
|
33
|
+
# filter to specific file types (recursive)
|
|
34
|
+
npx rhachet run --skill sedreplace --old "oldName" --new "newName" --glob "**/*.ts"
|
|
35
|
+
|
|
36
|
+
# filter to files in a specific directory
|
|
37
|
+
npx rhachet run --skill sedreplace --old "oldName" --new "newName" --glob "src/**/*.ts"
|
|
35
38
|
|
|
36
39
|
# apply changes after review of dry-run
|
|
37
40
|
npx rhachet run --skill sedreplace --old "oldName" --new "newName" --execute
|
|
38
41
|
```
|
|
39
42
|
|
|
43
|
+
### glob pattern semantics
|
|
44
|
+
|
|
45
|
+
the `--glob` option uses shell glob semantics:
|
|
46
|
+
|
|
47
|
+
| pattern | matches |
|
|
48
|
+
|---------|---------|
|
|
49
|
+
| `*.ts` | `.ts` files in root directory only |
|
|
50
|
+
| `**/*.ts` | all `.ts` files recursively |
|
|
51
|
+
| `src/*.ts` | `.ts` files directly in `src/` |
|
|
52
|
+
| `src/**/*.ts` | `.ts` files recursively in `src/` |
|
|
53
|
+
| `*.{ts,tsx}` | `.ts` and `.tsx` files in root |
|
|
54
|
+
| `**/*.{ts,tsx}` | all `.ts` and `.tsx` files recursively |
|
|
55
|
+
|
|
40
56
|
## .examples
|
|
41
57
|
|
|
42
58
|
### rename a function
|
|
43
59
|
|
|
44
60
|
```sh
|
|
45
61
|
# rename getUserById -> findUserByUuid across all .ts files
|
|
46
|
-
npx rhachet run --skill sedreplace --old "getUserById" --new "findUserByUuid" --glob "
|
|
62
|
+
npx rhachet run --skill sedreplace --old "getUserById" --new "findUserByUuid" --glob "**/*.ts" --execute
|
|
47
63
|
```
|
|
48
64
|
|
|
49
65
|
### update an import path
|
|
@@ -81,8 +81,10 @@ if ! git rev-parse --git-dir > /dev/null 2>&1; then
|
|
|
81
81
|
fi
|
|
82
82
|
|
|
83
83
|
# get git-tracked files, optionally filtered by glob
|
|
84
|
+
# note: use :(glob) magic pathspec for proper shell-like glob behavior
|
|
85
|
+
# without this, git ls-files uses pathspec matching where * matches /
|
|
84
86
|
if [[ -n "$GLOB_FILTER" ]]; then
|
|
85
|
-
FILES=$(git ls-files "$GLOB_FILTER")
|
|
87
|
+
FILES=$(git ls-files ":(glob)$GLOB_FILTER")
|
|
86
88
|
else
|
|
87
89
|
FILES=$(git ls-files)
|
|
88
90
|
fi
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "rhachet-roles-ehmpathy",
|
|
3
3
|
"author": "ehmpathy",
|
|
4
4
|
"description": "empathetic software construction roles and skills, via rhachet",
|
|
5
|
-
"version": "1.17.
|
|
5
|
+
"version": "1.17.16",
|
|
6
6
|
"repository": "ehmpathy/rhachet-roles-ehmpathy",
|
|
7
7
|
"homepage": "https://github.com/ehmpathy/rhachet-roles-ehmpathy",
|
|
8
8
|
"keywords": [
|