habit-hooks-typescript 1.0.0__tar.gz

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.
@@ -0,0 +1,20 @@
1
+ # Per-plugin Node tool deps (installed via npm ci; see plugins/*/package.json)
2
+ node_modules/
3
+ dist
4
+ coverage
5
+ .DS_Store
6
+ .idea
7
+ .claude-channel/
8
+ *.tgz
9
+ *.log
10
+ .vscode/
11
+ .venv/
12
+ __pycache__/
13
+ *.pyc
14
+ .pytest_cache/
15
+ .spec-runs/
16
+
17
+ /ruff.toml
18
+
19
+ # Workflow orchestration script (run from ~/.claude, never a repo deliverable)
20
+ .claude/workflows-build-overnight.js
@@ -0,0 +1,5 @@
1
+ Metadata-Version: 2.4
2
+ Name: habit-hooks-typescript
3
+ Version: 1.0.0
4
+ Summary: The TypeScript Habit Hooks plugin
5
+ Requires-Python: >=3.11
@@ -0,0 +1,178 @@
1
+ # The typescript plugin โ€” acceptance
2
+
3
+ The typescript plugin runs its sensors through the real `habit-sensors` pipeline.
4
+ These cases run the **actual** tools (`eslint`, `knip`, and a `ts-morph` comment
5
+ scan) against a fixture with a known smell and assert the canonical finding comes
6
+ out, mapped to the smell keys in [smell-vocabulary.md](smell-vocabulary.md).
7
+
8
+ `habit-sensors` is the installed CLI. The Node tools live in the typescript
9
+ plugin's own `node_modules` (`plugins/typescript/node_modules`). The intro
10
+ symlinks that into each case as `./node_modules` and puts its `.bin` on `PATH`
11
+ once โ€” the shipped `eslint.config.mjs` and `knip` resolve their plugin deps
12
+ through the normal `node_modules` walk (ESM `import` ignores `NODE_PATH`, which
13
+ is why we symlink rather than set it), and `ts-morph`'s `require` resolves the
14
+ same way. The cases share `finding.jq` to project each finding down to the
15
+ asserted fields.
16
+
17
+ ๐Ÿ“„.habit-hooks/config.toml
18
+ ```toml
19
+ plugins = ["typescript"]
20
+ ```
21
+
22
+ ๐Ÿ“„package.json
23
+ ```json
24
+ { "name": "demo", "version": "0.0.0" }
25
+ ```
26
+
27
+ ๐Ÿ“„finding.jq
28
+ ```jq
29
+ sort_by(.smell)[]
30
+ | {
31
+ smell,
32
+ language,
33
+ key: (.issues[0].key | sub(".*/"; "")),
34
+ line: .issues[0].details.line,
35
+ source: .issues[0].details.source
36
+ }
37
+ ```
38
+
39
+ ```bash
40
+ ln -s ../../plugins/typescript/node_modules node_modules
41
+ ```
42
+
43
+ โœ๏ธPATH
44
+ ```text
45
+ $PWD/node_modules/.bin:$PATH
46
+ ```
47
+
48
+ ## eslint adapter maps rule IDs to canonical smells
49
+
50
+ The `eslint` adapter runs eslint with the shipped flat config and a jq transform
51
+ in its command flattens the per-file `messages[]`, remaps each rule ID to a
52
+ canonical smell, and groups one finding per smell, stamping `source:
53
+ "eslint:<rule>"` on each issue. The config caps `max-params` at 3, so a
54
+ four-parameter function trips `max-params` โ†’ `too-many-parameters`.
55
+
56
+ ๐Ÿ“„eslint.config.mjs @plugins/typescript/src/habit_hooks_typescript/eslint.config.mjs
57
+
58
+ ๐Ÿ“„src/billing.ts
59
+ ```typescript
60
+ export function charge(a: number, b: number, c: number, d: number): number {
61
+ return a + b + c + d;
62
+ }
63
+ ```
64
+
65
+ ๐Ÿ“„.habit-hooks/config.toml
66
+ ```toml
67
+ plugins = ["typescript"]
68
+
69
+ [sensors.knip]
70
+ disabled = true
71
+
72
+ [sensors.comment]
73
+ disabled = true
74
+ ```
75
+
76
+ ```bash
77
+ habit-sensors --all | jq -f finding.jq
78
+ ```
79
+
80
+ ๐Ÿ–ฅ๏ธ โœ…
81
+ ```json
82
+ {
83
+ "smell": "too-many-parameters",
84
+ "language": "typescript",
85
+ "key": "billing.ts",
86
+ "line": 1,
87
+ "source": "eslint:max-params"
88
+ }
89
+ ```
90
+
91
+ ## knip sensor maps an unused export to unused-export
92
+
93
+ The `knip` sensor runs knip with the shipped `knip.json`, accepts its 0/1 exit
94
+ codes, and shapes each typed issue array into a finding โ€” `exports` โ†’
95
+ `unused-export`, one issue per symbol keyed by the symbol name. `helper.ts`
96
+ exports `neverUsed`, which nothing imports.
97
+
98
+ ๐Ÿ“„knip.json @plugins/typescript/src/habit_hooks_typescript/knip.json
99
+
100
+ ๐Ÿ“„src/cli.ts
101
+ ```typescript
102
+ import { used } from "./helper";
103
+
104
+ used();
105
+ ```
106
+
107
+ ๐Ÿ“„src/helper.ts
108
+ ```typescript
109
+ export function used(): void {}
110
+
111
+ export function neverUsed(): void {}
112
+ ```
113
+
114
+ ๐Ÿ“„.habit-hooks/config.toml
115
+ ```toml
116
+ plugins = ["typescript"]
117
+
118
+ [sensors.eslint]
119
+ disabled = true
120
+
121
+ [sensors.comment]
122
+ disabled = true
123
+ ```
124
+
125
+ ```bash
126
+ habit-sensors --all | jq '.[] | {smell, language, key: .issues[0].key, file: .issues[0].details.file, source: .issues[0].details.source}'
127
+ ```
128
+
129
+ ๐Ÿ–ฅ๏ธ โœ…
130
+ ```json
131
+ {
132
+ "smell": "unused-export",
133
+ "language": "typescript",
134
+ "key": "neverUsed",
135
+ "file": "src/helper.ts",
136
+ "source": "knip:exports"
137
+ }
138
+ ```
139
+
140
+ ## comment sensor maps a non-essential comment to non-essential-comment
141
+
142
+ The `comment` sensor scans the scoped files with ts-morph and reports comments
143
+ the reader could work out from the code, shaping each into a
144
+ `non-essential-comment` finding with `source: "comment:non-essential"`.
145
+
146
+ ๐Ÿ“„src/util.ts
147
+ ```typescript
148
+ export function add(a: number, b: number): number {
149
+ // this comment restates what the code already says clearly
150
+ return a + b;
151
+ }
152
+ ```
153
+
154
+ ๐Ÿ“„.habit-hooks/config.toml
155
+ ```toml
156
+ plugins = ["typescript"]
157
+
158
+ [sensors.eslint]
159
+ disabled = true
160
+
161
+ [sensors.knip]
162
+ disabled = true
163
+ ```
164
+
165
+ ```bash
166
+ habit-sensors --all | jq -f finding.jq
167
+ ```
168
+
169
+ ๐Ÿ–ฅ๏ธ โœ…
170
+ ```json
171
+ {
172
+ "smell": "non-essential-comment",
173
+ "language": "typescript",
174
+ "key": "util.ts",
175
+ "line": 2,
176
+ "source": "comment:non-essential"
177
+ }
178
+ ```