habit-hooks 0.1.0-beta.0

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.
Files changed (192) hide show
  1. package/LICENSE.md +21 -0
  2. package/README.md +233 -0
  3. package/dist/baseline/commands.d.ts +12 -0
  4. package/dist/baseline/commands.js +131 -0
  5. package/dist/baseline/commands.js.map +1 -0
  6. package/dist/baseline/file-hash.d.ts +2 -0
  7. package/dist/baseline/file-hash.js +25 -0
  8. package/dist/baseline/file-hash.js.map +1 -0
  9. package/dist/baseline/filter.d.ts +8 -0
  10. package/dist/baseline/filter.js +31 -0
  11. package/dist/baseline/filter.js.map +1 -0
  12. package/dist/baseline/store.d.ts +18 -0
  13. package/dist/baseline/store.js +106 -0
  14. package/dist/baseline/store.js.map +1 -0
  15. package/dist/checks/comment-check.d.ts +3 -0
  16. package/dist/checks/comment-check.js +83 -0
  17. package/dist/checks/comment-check.js.map +1 -0
  18. package/dist/checks/eslint-wrap.d.ts +2 -0
  19. package/dist/checks/eslint-wrap.js +104 -0
  20. package/dist/checks/eslint-wrap.js.map +1 -0
  21. package/dist/checks/jscpd-wrap.d.ts +6 -0
  22. package/dist/checks/jscpd-wrap.js +158 -0
  23. package/dist/checks/jscpd-wrap.js.map +1 -0
  24. package/dist/checks/knip-resolve.d.ts +4 -0
  25. package/dist/checks/knip-resolve.js +49 -0
  26. package/dist/checks/knip-resolve.js.map +1 -0
  27. package/dist/checks/knip-schema.d.ts +32 -0
  28. package/dist/checks/knip-schema.js +24 -0
  29. package/dist/checks/knip-schema.js.map +1 -0
  30. package/dist/checks/knip-wrap.d.ts +4 -0
  31. package/dist/checks/knip-wrap.js +127 -0
  32. package/dist/checks/knip-wrap.js.map +1 -0
  33. package/dist/cli/baseline-commands.d.ts +2 -0
  34. package/dist/cli/baseline-commands.js +61 -0
  35. package/dist/cli/baseline-commands.js.map +1 -0
  36. package/dist/cli/emit.d.ts +7 -0
  37. package/dist/cli/emit.js +8 -0
  38. package/dist/cli/emit.js.map +1 -0
  39. package/dist/cli/init/detect.d.ts +8 -0
  40. package/dist/cli/init/detect.js +20 -0
  41. package/dist/cli/init/detect.js.map +1 -0
  42. package/dist/cli/init/git-hook.d.ts +6 -0
  43. package/dist/cli/init/git-hook.js +48 -0
  44. package/dist/cli/init/git-hook.js.map +1 -0
  45. package/dist/cli/init/install-commands.d.ts +6 -0
  46. package/dist/cli/init/install-commands.js +55 -0
  47. package/dist/cli/init/install-commands.js.map +1 -0
  48. package/dist/cli/init/package-scripts.d.ts +6 -0
  49. package/dist/cli/init/package-scripts.js +55 -0
  50. package/dist/cli/init/package-scripts.js.map +1 -0
  51. package/dist/cli/init/prompts.d.ts +9 -0
  52. package/dist/cli/init/prompts.js +33 -0
  53. package/dist/cli/init/prompts.js.map +1 -0
  54. package/dist/cli/init/reporters.d.ts +11 -0
  55. package/dist/cli/init/reporters.js +40 -0
  56. package/dist/cli/init/reporters.js.map +1 -0
  57. package/dist/cli/init/run.d.ts +12 -0
  58. package/dist/cli/init/run.js +159 -0
  59. package/dist/cli/init/run.js.map +1 -0
  60. package/dist/cli/init/scaffold-baseline.d.ts +4 -0
  61. package/dist/cli/init/scaffold-baseline.js +11 -0
  62. package/dist/cli/init/scaffold-baseline.js.map +1 -0
  63. package/dist/cli/init/scaffold-config.d.ts +13 -0
  64. package/dist/cli/init/scaffold-config.js +42 -0
  65. package/dist/cli/init/scaffold-config.js.map +1 -0
  66. package/dist/cli/init/scaffold-eslint-config.d.ts +2 -0
  67. package/dist/cli/init/scaffold-eslint-config.js +12 -0
  68. package/dist/cli/init/scaffold-eslint-config.js.map +1 -0
  69. package/dist/cli/init/scaffold-jscpd-config.d.ts +2 -0
  70. package/dist/cli/init/scaffold-jscpd-config.js +12 -0
  71. package/dist/cli/init/scaffold-jscpd-config.js.map +1 -0
  72. package/dist/cli/init/scaffold-knip-config.d.ts +2 -0
  73. package/dist/cli/init/scaffold-knip-config.js +12 -0
  74. package/dist/cli/init/scaffold-knip-config.js.map +1 -0
  75. package/dist/cli/init/skill.d.ts +7 -0
  76. package/dist/cli/init/skill.js +36 -0
  77. package/dist/cli/init/skill.js.map +1 -0
  78. package/dist/cli/init/snippet.d.ts +1 -0
  79. package/dist/cli/init/snippet.js +18 -0
  80. package/dist/cli/init/snippet.js.map +1 -0
  81. package/dist/cli/init/templates/eslint-config.d.ts +2 -0
  82. package/dist/cli/init/templates/eslint-config.js +35 -0
  83. package/dist/cli/init/templates/eslint-config.js.map +1 -0
  84. package/dist/cli/init/templates/jscpd-config.d.ts +2 -0
  85. package/dist/cli/init/templates/jscpd-config.js +9 -0
  86. package/dist/cli/init/templates/jscpd-config.js.map +1 -0
  87. package/dist/cli/init/templates/knip-config.d.ts +2 -0
  88. package/dist/cli/init/templates/knip-config.js +8 -0
  89. package/dist/cli/init/templates/knip-config.js.map +1 -0
  90. package/dist/cli/init-command.d.ts +2 -0
  91. package/dist/cli/init-command.js +33 -0
  92. package/dist/cli/init-command.js.map +1 -0
  93. package/dist/cli.d.ts +2 -0
  94. package/dist/cli.js +101 -0
  95. package/dist/cli.js.map +1 -0
  96. package/dist/config/defaults.d.ts +4 -0
  97. package/dist/config/defaults.js +172 -0
  98. package/dist/config/defaults.js.map +1 -0
  99. package/dist/config/jiti-loader.d.ts +1 -0
  100. package/dist/config/jiti-loader.js +13 -0
  101. package/dist/config/jiti-loader.js.map +1 -0
  102. package/dist/config/load.d.ts +8 -0
  103. package/dist/config/load.js +53 -0
  104. package/dist/config/load.js.map +1 -0
  105. package/dist/config/merge.d.ts +3 -0
  106. package/dist/config/merge.js +90 -0
  107. package/dist/config/merge.js.map +1 -0
  108. package/dist/config/schema.d.ts +32 -0
  109. package/dist/config/schema.js +4 -0
  110. package/dist/config/schema.js.map +1 -0
  111. package/dist/config/validate.d.ts +2 -0
  112. package/dist/config/validate.js +128 -0
  113. package/dist/config/validate.js.map +1 -0
  114. package/dist/detect/package-json.d.ts +1 -0
  115. package/dist/detect/package-json.js +15 -0
  116. package/dist/detect/package-json.js.map +1 -0
  117. package/dist/detect/tool.d.ts +10 -0
  118. package/dist/detect/tool.js +73 -0
  119. package/dist/detect/tool.js.map +1 -0
  120. package/dist/eslint-runner.d.ts +7 -0
  121. package/dist/eslint-runner.js +34 -0
  122. package/dist/eslint-runner.js.map +1 -0
  123. package/dist/git/exec.d.ts +8 -0
  124. package/dist/git/exec.js +48 -0
  125. package/dist/git/exec.js.map +1 -0
  126. package/dist/git/resolve-scope.d.ts +15 -0
  127. package/dist/git/resolve-scope.js +89 -0
  128. package/dist/git/resolve-scope.js.map +1 -0
  129. package/dist/git/scope.d.ts +7 -0
  130. package/dist/git/scope.js +58 -0
  131. package/dist/git/scope.js.map +1 -0
  132. package/dist/index.d.ts +1 -0
  133. package/dist/index.js +2 -0
  134. package/dist/index.js.map +1 -0
  135. package/dist/prompts/loader.d.ts +6 -0
  136. package/dist/prompts/loader.js +27 -0
  137. package/dist/prompts/loader.js.map +1 -0
  138. package/dist/prompts/packaged-dir.d.ts +1 -0
  139. package/dist/prompts/packaged-dir.js +10 -0
  140. package/dist/prompts/packaged-dir.js.map +1 -0
  141. package/dist/prompts/registry.d.ts +3 -0
  142. package/dist/prompts/registry.js +68 -0
  143. package/dist/prompts/registry.js.map +1 -0
  144. package/dist/reporter.d.ts +7 -0
  145. package/dist/reporter.js +114 -0
  146. package/dist/reporter.js.map +1 -0
  147. package/dist/rules/registry.d.ts +3 -0
  148. package/dist/rules/registry.js +37 -0
  149. package/dist/rules/registry.js.map +1 -0
  150. package/dist/runner.d.ts +15 -0
  151. package/dist/runner.js +151 -0
  152. package/dist/runner.js.map +1 -0
  153. package/dist/types.d.ts +41 -0
  154. package/dist/types.js +2 -0
  155. package/dist/types.js.map +1 -0
  156. package/dist/wrap/notices.d.ts +13 -0
  157. package/dist/wrap/notices.js +29 -0
  158. package/dist/wrap/notices.js.map +1 -0
  159. package/dist/wrap/resolve.d.ts +5 -0
  160. package/dist/wrap/resolve.js +9 -0
  161. package/dist/wrap/resolve.js.map +1 -0
  162. package/dist/wrap/run.d.ts +15 -0
  163. package/dist/wrap/run.js +26 -0
  164. package/dist/wrap/run.js.map +1 -0
  165. package/dist/wrap/shell.d.ts +14 -0
  166. package/dist/wrap/shell.js +44 -0
  167. package/dist/wrap/shell.js.map +1 -0
  168. package/package.json +46 -0
  169. package/src/prompts/comment-non-essential.md +7 -0
  170. package/src/prompts/eslint-boundaries-dependencies.md +9 -0
  171. package/src/prompts/eslint-complexity.md +9 -0
  172. package/src/prompts/eslint-eqeqeq.md +3 -0
  173. package/src/prompts/eslint-fatal.md +7 -0
  174. package/src/prompts/eslint-max-lines-per-function.md +9 -0
  175. package/src/prompts/eslint-max-lines.md +9 -0
  176. package/src/prompts/eslint-max-params.md +9 -0
  177. package/src/prompts/eslint-no-duplicate-imports.md +1 -0
  178. package/src/prompts/eslint-no-unused-vars.md +3 -0
  179. package/src/prompts/eslint-no-var.md +1 -0
  180. package/src/prompts/eslint-no-warning-comments.md +3 -0
  181. package/src/prompts/eslint-prefer-const.md +3 -0
  182. package/src/prompts/eslint-typescript-eslint-no-explicit-any.md +3 -0
  183. package/src/prompts/eslint-typescript-eslint-no-inferrable-types.md +3 -0
  184. package/src/prompts/eslint-typescript-eslint-no-non-null-assertion.md +3 -0
  185. package/src/prompts/jscpd-duplication.md +9 -0
  186. package/src/prompts/knip-classMembers.md +7 -0
  187. package/src/prompts/knip-dependencies.md +9 -0
  188. package/src/prompts/knip-exports.md +9 -0
  189. package/src/prompts/knip-files.md +9 -0
  190. package/src/prompts/knip-types.md +9 -0
  191. package/src/prompts/uncoached.md +3 -0
  192. package/src/skills/habit-hooks-review/SKILL.md +108 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reporter.js","sourceRoot":"","sources":["../src/reporter.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { Rule } from '../types.js';
2
+ import type { HabitHooksConfig } from '../config/schema.js';
3
+ export declare function buildRules(config: HabitHooksConfig, configDir: string): Rule[];
@@ -0,0 +1,37 @@
1
+ import { isAbsolute, resolve } from 'node:path';
2
+ import { DEFAULT_COMMENT_CHECK_THRESHOLDS } from '../checks/comment-check.js';
3
+ import { defaultConfig, defaultRules } from '../config/defaults.js';
4
+ import { mergeRules } from '../config/merge.js';
5
+ import { loadGuidance } from '../prompts/loader.js';
6
+ const COMMENT_RULE_ID = 'comment:non-essential';
7
+ function resolvePromptsDir(config, configDir) {
8
+ if (config.prompts === undefined)
9
+ return undefined;
10
+ return isAbsolute(config.prompts) ? config.prompts : resolve(configDir, config.prompts);
11
+ }
12
+ function attachGuidanceToRule(rule, overrideDir) {
13
+ const guidance = loadGuidance(rule.id, { overrideDir });
14
+ if (guidance === null)
15
+ return rule;
16
+ return { ...rule, guidance };
17
+ }
18
+ function attachGuidance(rules, overrideDir) {
19
+ return rules.map((rule) => attachGuidanceToRule(rule, overrideDir));
20
+ }
21
+ function resolveCommentThresholds(config) {
22
+ return {
23
+ maxSingleLineChars: config?.maxSingleLineChars ?? DEFAULT_COMMENT_CHECK_THRESHOLDS.maxSingleLineChars,
24
+ maxBlockChars: config?.maxBlockChars ?? DEFAULT_COMMENT_CHECK_THRESHOLDS.maxBlockChars,
25
+ };
26
+ }
27
+ function attachCommentThresholds(rules, config) {
28
+ const thresholds = resolveCommentThresholds(config);
29
+ return rules.map((rule) => (rule.id === COMMENT_RULE_ID ? { ...rule, commentCheck: thresholds } : rule));
30
+ }
31
+ export function buildRules(config, configDir) {
32
+ const merged = mergeRules(defaultRules, defaultConfig.rules, config.rules);
33
+ const overrideDir = resolvePromptsDir(config, configDir);
34
+ const withGuidance = attachGuidance(merged, overrideDir);
35
+ return attachCommentThresholds(withGuidance, config.commentCheck);
36
+ }
37
+ //# sourceMappingURL=registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/rules/registry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEhD,OAAO,EAAE,gCAAgC,EAAE,MAAM,4BAA4B,CAAC;AAC9E,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AACpE,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAEhD,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAEpD,MAAM,eAAe,GAAG,uBAAuB,CAAC;AAEhD,SAAS,iBAAiB,CAAC,MAAwB,EAAE,SAAiB;IACpE,IAAI,MAAM,CAAC,OAAO,KAAK,SAAS;QAAE,OAAO,SAAS,CAAC;IACnD,OAAO,UAAU,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,SAAS,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC;AAC1F,CAAC;AAED,SAAS,oBAAoB,CAAC,IAAU,EAAE,WAA+B;IACvE,MAAM,QAAQ,GAAG,YAAY,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,WAAW,EAAE,CAAC,CAAC;IACxD,IAAI,QAAQ,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IACnC,OAAO,EAAE,GAAG,IAAI,EAAE,QAAQ,EAAE,CAAC;AAC/B,CAAC;AAED,SAAS,cAAc,CAAC,KAAa,EAAE,WAA+B;IACpE,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,oBAAoB,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC,CAAC;AACtE,CAAC;AAED,SAAS,wBAAwB,CAAC,MAAsC;IACtE,OAAO;QACL,kBAAkB,EAAE,MAAM,EAAE,kBAAkB,IAAI,gCAAgC,CAAC,kBAAkB;QACrG,aAAa,EAAE,MAAM,EAAE,aAAa,IAAI,gCAAgC,CAAC,aAAa;KACvF,CAAC;AACJ,CAAC;AAED,SAAS,uBAAuB,CAAC,KAAa,EAAE,MAAsC;IACpF,MAAM,UAAU,GAAG,wBAAwB,CAAC,MAAM,CAAC,CAAC;IACpD,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,eAAe,CAAC,CAAC,CAAC,EAAE,GAAG,IAAI,EAAE,YAAY,EAAE,UAAU,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;AAC3G,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,MAAwB,EAAE,SAAiB;IACpE,MAAM,MAAM,GAAG,UAAU,CAAC,YAAY,EAAE,aAAa,CAAC,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC;IAC3E,MAAM,WAAW,GAAG,iBAAiB,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;IACzD,MAAM,YAAY,GAAG,cAAc,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IACzD,OAAO,uBAAuB,CAAC,YAAY,EAAE,MAAM,CAAC,YAAY,CAAC,CAAC;AACpE,CAAC"}
@@ -0,0 +1,15 @@
1
+ import { type ScopeFlags } from './git/resolve-scope.js';
2
+ import type { Violation } from './types.js';
3
+ interface RunResult {
4
+ stdout: string;
5
+ stderr: string[];
6
+ exitCode: number;
7
+ violations: Violation[];
8
+ }
9
+ interface RunOptions {
10
+ configPath?: string;
11
+ scopeFlags?: ScopeFlags;
12
+ applyBaseline?: boolean;
13
+ }
14
+ export declare function run(cwd: string, options?: RunOptions): Promise<RunResult>;
15
+ export {};
package/dist/runner.js ADDED
@@ -0,0 +1,151 @@
1
+ import { dirname, relative } from 'node:path';
2
+ import fg from 'fast-glob';
3
+ import picomatch from 'picomatch';
4
+ import { eslintWrap } from './checks/eslint-wrap.js';
5
+ import { commentCheck } from './checks/comment-check.js';
6
+ import { jscpdWrap } from './checks/jscpd-wrap.js';
7
+ import { knipWrap } from './checks/knip-wrap.js';
8
+ import { loadConfig, loadConfigFromPath } from './config/load.js';
9
+ import { buildRules } from './rules/registry.js';
10
+ import { report } from './reporter.js';
11
+ import { resolveScope } from './git/resolve-scope.js';
12
+ import { loadBaseline } from './baseline/store.js';
13
+ import { partitionBySnooze } from './baseline/filter.js';
14
+ import { runEslintSource } from './eslint-runner.js';
15
+ async function discoverFiles(cwd) {
16
+ return fg(['**/*.{ts,tsx,js,mjs,cjs}'], {
17
+ cwd,
18
+ absolute: true,
19
+ ignore: ['**/node_modules/**', '**/dist/**', '**/coverage/**'],
20
+ dot: false,
21
+ });
22
+ }
23
+ function buildMatcher(patterns) {
24
+ if (!patterns || patterns.length === 0)
25
+ return null;
26
+ return picomatch(patterns);
27
+ }
28
+ function filterFilesForRule(rule, files, cwd) {
29
+ const includeMatcher = buildMatcher(rule.include);
30
+ const excludeMatcher = buildMatcher(rule.exclude);
31
+ if (!includeMatcher && !excludeMatcher)
32
+ return files;
33
+ return files.filter((file) => {
34
+ const rel = relative(cwd, file);
35
+ if (includeMatcher && !includeMatcher(rel))
36
+ return false;
37
+ if (excludeMatcher && excludeMatcher(rel))
38
+ return false;
39
+ return true;
40
+ });
41
+ }
42
+ function applyScopeToRule(rule, files, scope) {
43
+ if (!rule.changedFilesOnly)
44
+ return files;
45
+ const changed = scope.changedFiles;
46
+ if (changed === null)
47
+ return files;
48
+ return files.filter((file) => changed.has(file));
49
+ }
50
+ function applyBaselineToRule(files, ctx) {
51
+ if (ctx.baseline === null)
52
+ return files;
53
+ return partitionBySnooze(files, ctx.baseline, ctx.cwd).active;
54
+ }
55
+ function resolveFilesForRule(rule, ctx) {
56
+ const filtered = filterFilesForRule(rule, ctx.files, ctx.cwd);
57
+ const scoped = applyScopeToRule(rule, filtered, ctx.scope);
58
+ return applyBaselineToRule(scoped, ctx);
59
+ }
60
+ function addRuleToGroup(groups, rule, files) {
61
+ const key = files.join('\0');
62
+ const existing = groups.get(key);
63
+ if (existing)
64
+ existing.rules.push(rule);
65
+ else
66
+ groups.set(key, { rules: [rule], files });
67
+ }
68
+ function groupByFileSet(rules, ctx) {
69
+ const groups = new Map();
70
+ for (const rule of rules) {
71
+ addRuleToGroup(groups, rule, resolveFilesForRule(rule, ctx));
72
+ }
73
+ return [...groups.values()];
74
+ }
75
+ function normalizeOutcome(result) {
76
+ if (Array.isArray(result))
77
+ return { violations: result };
78
+ return result;
79
+ }
80
+ async function runGroup(check, group, ctx) {
81
+ const raw = await check.run(group.files, group.rules, ctx.cwd);
82
+ return normalizeOutcome(raw);
83
+ }
84
+ async function runGroups(groups, ctx, check) {
85
+ const stderr = [];
86
+ const violations = [];
87
+ for (const group of groups) {
88
+ if (group.files.length === 0)
89
+ continue;
90
+ const outcome = await runGroup(check, group, ctx);
91
+ violations.push(...outcome.violations);
92
+ if (outcome.stderr)
93
+ stderr.push(...outcome.stderr);
94
+ }
95
+ return { violations, stderr };
96
+ }
97
+ function eslintFilterCtx(ctx) {
98
+ return { resolveFilesForRule: (rule) => resolveFilesForRule(rule, ctx), cwd: ctx.cwd };
99
+ }
100
+ async function runCheckForSource(rules, ctx, binding) {
101
+ if (binding.source === 'eslint')
102
+ return runEslintSource(rules, eslintFilterCtx(ctx), binding.check);
103
+ const selected = rules.filter((rule) => rule.source === binding.source);
104
+ return runGroups(groupByFileSet(selected, ctx), ctx, binding.check);
105
+ }
106
+ async function resolveConfig(cwd, options) {
107
+ if (options.configPath !== undefined) {
108
+ const loaded = await loadConfigFromPath(options.configPath);
109
+ return { config: loaded.config, configDir: dirname(options.configPath) };
110
+ }
111
+ const loaded = await loadConfig(cwd);
112
+ const configDir = loaded.sourcePath ? dirname(loaded.sourcePath) : cwd;
113
+ return { config: loaded.config, configDir };
114
+ }
115
+ function resolveBaseline(cwd, options) {
116
+ if (options.applyBaseline === false)
117
+ return null;
118
+ return loadBaseline(cwd);
119
+ }
120
+ async function buildContext(cwd, options) {
121
+ const { config, configDir } = await resolveConfig(cwd, options);
122
+ const rules = buildRules(config, configDir);
123
+ const files = await discoverFiles(cwd);
124
+ const scope = resolveScope(options.scopeFlags ?? {}, config.scope, cwd);
125
+ const baseline = resolveBaseline(cwd, options);
126
+ return { ctx: { cwd, files, scope, baseline }, rules };
127
+ }
128
+ const CHECK_BINDINGS = [
129
+ { source: 'eslint', check: eslintWrap },
130
+ { source: 'custom', check: commentCheck },
131
+ { source: 'jscpd', check: jscpdWrap },
132
+ { source: 'knip', check: knipWrap },
133
+ ];
134
+ async function collectOutcomes(rules, ctx) {
135
+ const violations = [];
136
+ const stderr = [];
137
+ for (const binding of CHECK_BINDINGS) {
138
+ const outcome = await runCheckForSource(rules, ctx, binding);
139
+ violations.push(...outcome.violations);
140
+ if (outcome.stderr)
141
+ stderr.push(...outcome.stderr);
142
+ }
143
+ return { violations, stderr };
144
+ }
145
+ export async function run(cwd, options = {}) {
146
+ const { ctx, rules } = await buildContext(cwd, options);
147
+ const outcome = await collectOutcomes(rules, ctx);
148
+ const reported = report(outcome.violations, rules);
149
+ return { ...reported, violations: outcome.violations, stderr: outcome.stderr ?? [] };
150
+ }
151
+ //# sourceMappingURL=runner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runner.js","sourceRoot":"","sources":["../src/runner.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,41 @@
1
+ export type Severity = 'enforced' | 'suggested';
2
+ export type RuleSource = 'eslint' | 'jscpd' | 'knip' | 'custom';
3
+ export interface CommentCheckThresholds {
4
+ maxSingleLineChars: number;
5
+ maxBlockChars: number;
6
+ }
7
+ export interface Rule {
8
+ id: string;
9
+ source: RuleSource;
10
+ sourceRuleId?: string;
11
+ severity: Severity;
12
+ changedFilesOnly: boolean;
13
+ title: string;
14
+ description: string;
15
+ include?: string[];
16
+ exclude?: string[];
17
+ guidance?: string;
18
+ commentCheck?: CommentCheckThresholds;
19
+ }
20
+ export interface CoachingPrompt {
21
+ id: string;
22
+ title: string;
23
+ description: string;
24
+ severity: Severity;
25
+ guidancePath: string;
26
+ }
27
+ export interface Violation {
28
+ ruleId: string;
29
+ file: string;
30
+ line: number;
31
+ column?: number;
32
+ message: string;
33
+ }
34
+ export interface CheckOutcome {
35
+ violations: Violation[];
36
+ stderr?: string[];
37
+ }
38
+ export interface Check {
39
+ id: string;
40
+ run(_files: string[], _rules: Rule[], _cwd?: string): Promise<Violation[] | CheckOutcome>;
41
+ }
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,13 @@
1
+ import type { ShellResult } from './shell.js';
2
+ import type { CheckOutcome } from '../types.js';
3
+ export interface BinResolution {
4
+ binPath: string;
5
+ isFallback: boolean;
6
+ }
7
+ export declare function fallbackNotice(tool: string, cwd: string): string;
8
+ export declare function spawnFailureWarning(tool: string, cwd: string, warnings: string[]): string;
9
+ export declare function firstLine(text: string): string;
10
+ export declare function isSpawnFailure(result: ShellResult): boolean;
11
+ export declare function emptyOutcome(stderr: string[]): CheckOutcome;
12
+ export declare function noticesFor(tool: string, resolution: BinResolution, cwd: string): string[];
13
+ export declare function absolutize(cwd: string, file: string): string;
@@ -0,0 +1,29 @@
1
+ import { isAbsolute, join } from 'node:path';
2
+ export function fallbackNotice(tool, cwd) {
3
+ return `habit-hooks: using bundled ${tool} (no ${tool} installation found in ${cwd})`;
4
+ }
5
+ export function spawnFailureWarning(tool, cwd, warnings) {
6
+ const detail = warnings.length > 0 ? warnings.join('; ') : 'spawn failure';
7
+ return `habit-hooks: ${tool} skipped in ${cwd} (${detail})`;
8
+ }
9
+ export function firstLine(text) {
10
+ for (const line of text.split('\n')) {
11
+ const trimmed = line.trim();
12
+ if (trimmed.length > 0)
13
+ return trimmed;
14
+ }
15
+ return '';
16
+ }
17
+ export function isSpawnFailure(result) {
18
+ return result.exitCode === -1;
19
+ }
20
+ export function emptyOutcome(stderr) {
21
+ return { violations: [], stderr };
22
+ }
23
+ export function noticesFor(tool, resolution, cwd) {
24
+ return resolution.isFallback ? [fallbackNotice(tool, cwd)] : [];
25
+ }
26
+ export function absolutize(cwd, file) {
27
+ return isAbsolute(file) ? file : join(cwd, file);
28
+ }
29
+ //# sourceMappingURL=notices.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"notices.js","sourceRoot":"","sources":["../../src/wrap/notices.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAS7C,MAAM,UAAU,cAAc,CAAC,IAAY,EAAE,GAAW;IACtD,OAAO,8BAA8B,IAAI,QAAQ,IAAI,0BAA0B,GAAG,GAAG,CAAC;AACxF,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,IAAY,EAAE,GAAW,EAAE,QAAkB;IAC/E,MAAM,MAAM,GAAG,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,eAAe,CAAC;IAC3E,OAAO,gBAAgB,IAAI,eAAe,GAAG,KAAK,MAAM,GAAG,CAAC;AAC9D,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,IAAY;IACpC,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;QACpC,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;QAC5B,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC;YAAE,OAAO,OAAO,CAAC;IACzC,CAAC;IACD,OAAO,EAAE,CAAC;AACZ,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,MAAmB;IAChD,OAAO,MAAM,CAAC,QAAQ,KAAK,CAAC,CAAC,CAAC;AAChC,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,MAAgB;IAC3C,OAAO,EAAE,UAAU,EAAE,EAAE,EAAE,MAAM,EAAE,CAAC;AACpC,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,IAAY,EAAE,UAAyB,EAAE,GAAW;IAC7E,OAAO,UAAU,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,cAAc,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;AAClE,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,GAAW,EAAE,IAAY;IAClD,OAAO,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;AACnD,CAAC"}
@@ -0,0 +1,5 @@
1
+ export declare function requiresNodeRuntime(binPath: string): boolean;
2
+ export declare function spawnTarget(binPath: string, args: string[]): {
3
+ bin: string;
4
+ args: string[];
5
+ };
@@ -0,0 +1,9 @@
1
+ export function requiresNodeRuntime(binPath) {
2
+ return binPath.endsWith('.js');
3
+ }
4
+ export function spawnTarget(binPath, args) {
5
+ if (requiresNodeRuntime(binPath))
6
+ return { bin: process.execPath, args: [binPath, ...args] };
7
+ return { bin: binPath, args };
8
+ }
9
+ //# sourceMappingURL=resolve.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolve.js","sourceRoot":"","sources":["../../src/wrap/resolve.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,mBAAmB,CAAC,OAAe;IACjD,OAAO,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;AACjC,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,OAAe,EAAE,IAAc;IACzD,IAAI,mBAAmB,CAAC,OAAO,CAAC;QAAE,OAAO,EAAE,GAAG,EAAE,OAAO,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,EAAE,CAAC;IAC7F,OAAO,EAAE,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;AAChC,CAAC"}
@@ -0,0 +1,15 @@
1
+ import { type ShellResult } from './shell.js';
2
+ import { type BinResolution } from './notices.js';
3
+ interface SpawnSkip {
4
+ skipWarning: string;
5
+ }
6
+ interface SpawnWrappedArgs {
7
+ tool: string;
8
+ resolution: BinResolution;
9
+ cwd: string;
10
+ args: string[];
11
+ }
12
+ export declare function spawnWrapped(spec: SpawnWrappedArgs): Promise<ShellResult | SpawnSkip>;
13
+ export declare function isSpawnSkip(value: ShellResult | SpawnSkip): value is SpawnSkip;
14
+ export declare function parseJsonStdout<T>(stdout: string, startsWith: '{' | '['): T | null;
15
+ export {};
@@ -0,0 +1,26 @@
1
+ import { runTool } from './shell.js';
2
+ import { isSpawnFailure, spawnFailureWarning } from './notices.js';
3
+ import { spawnTarget } from './resolve.js';
4
+ export async function spawnWrapped(spec) {
5
+ const { tool, resolution, cwd, args } = spec;
6
+ const target = spawnTarget(resolution.binPath, args);
7
+ const result = await runTool({ bin: target.bin, args: target.args, cwd });
8
+ if (isSpawnFailure(result))
9
+ return { skipWarning: spawnFailureWarning(tool, cwd, result.warnings) };
10
+ return result;
11
+ }
12
+ export function isSpawnSkip(value) {
13
+ return value.skipWarning !== undefined;
14
+ }
15
+ export function parseJsonStdout(stdout, startsWith) {
16
+ const trimmed = stdout.trim();
17
+ if (trimmed.length === 0 || !trimmed.startsWith(startsWith))
18
+ return null;
19
+ try {
20
+ return JSON.parse(trimmed);
21
+ }
22
+ catch {
23
+ return null;
24
+ }
25
+ }
26
+ //# sourceMappingURL=run.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run.js","sourceRoot":"","sources":["../../src/wrap/run.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAoB,MAAM,YAAY,CAAC;AACvD,OAAO,EAAE,cAAc,EAAE,mBAAmB,EAAsB,MAAM,cAAc,CAAC;AACvF,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAa3C,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,IAAsB;IACvD,MAAM,EAAE,IAAI,EAAE,UAAU,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,IAAI,CAAC;IAC7C,MAAM,MAAM,GAAG,WAAW,CAAC,UAAU,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IACrD,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,EAAE,GAAG,EAAE,MAAM,CAAC,GAAG,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC;IAC1E,IAAI,cAAc,CAAC,MAAM,CAAC;QAAE,OAAO,EAAE,WAAW,EAAE,mBAAmB,CAAC,IAAI,EAAE,GAAG,EAAE,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC;IACpG,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,KAA8B;IACxD,OAAQ,KAAmB,CAAC,WAAW,KAAK,SAAS,CAAC;AACxD,CAAC;AAED,MAAM,UAAU,eAAe,CAAI,MAAc,EAAE,UAAqB;IACtE,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC;IAC9B,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,UAAU,CAAC;QAAE,OAAO,IAAI,CAAC;IACzE,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,CAAM,CAAC;IAClC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC"}
@@ -0,0 +1,14 @@
1
+ export interface ShellResult {
2
+ stdout: string;
3
+ stderr: string;
4
+ exitCode: number;
5
+ warnings: string[];
6
+ }
7
+ interface RunToolOptions {
8
+ bin: string;
9
+ args: string[];
10
+ cwd: string;
11
+ timeoutMs?: number;
12
+ }
13
+ export declare function runTool(opts: RunToolOptions): Promise<ShellResult>;
14
+ export {};
@@ -0,0 +1,44 @@
1
+ import { spawn } from 'node:child_process';
2
+ const DEFAULT_TIMEOUT_MS = 5 * 60 * 1000;
3
+ function emptyFailure(warning) {
4
+ return { stdout: '', stderr: '', exitCode: -1, warnings: [warning] };
5
+ }
6
+ function collectStreams(child) {
7
+ const buffers = { stdout: [], stderr: [] };
8
+ child.stdout.on('data', (chunk) => buffers.stdout.push(chunk));
9
+ child.stderr.on('data', (chunk) => buffers.stderr.push(chunk));
10
+ return buffers;
11
+ }
12
+ function settleOnce(settler, result) {
13
+ if (settler.done.value)
14
+ return;
15
+ settler.done.value = true;
16
+ settler.clearTimer();
17
+ settler.resolve(result);
18
+ }
19
+ function closeResult(info) {
20
+ if (info.signal !== null)
21
+ return emptyFailure(`${info.bin} terminated by signal ${info.signal}`);
22
+ const stdout = Buffer.concat(info.buffers.stdout).toString('utf8');
23
+ const stderr = Buffer.concat(info.buffers.stderr).toString('utf8');
24
+ return { stdout, stderr, exitCode: info.code ?? -1, warnings: [] };
25
+ }
26
+ function startKillTimer(child, onTimeout, timeoutMs) {
27
+ return setTimeout(() => {
28
+ child.kill('SIGKILL');
29
+ onTimeout();
30
+ }, timeoutMs);
31
+ }
32
+ export function runTool(opts) {
33
+ const timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS;
34
+ return new Promise((resolve) => {
35
+ const child = spawn(opts.bin, opts.args, { cwd: opts.cwd });
36
+ const buffers = collectStreams(child);
37
+ const done = { value: false };
38
+ const timer = startKillTimer(child, () => settleOnce(settler, emptyFailure(`${opts.bin} timed out after ${timeoutMs}ms`)), timeoutMs);
39
+ const settler = { resolve, done, clearTimer: () => clearTimeout(timer) };
40
+ child.on('error', (e) => settleOnce(settler, emptyFailure(`${opts.bin} failed to spawn: ${e.message}`)));
41
+ child.on('close', (code, signal) => settleOnce(settler, closeResult({ buffers, bin: opts.bin, code, signal })));
42
+ });
43
+ }
44
+ //# sourceMappingURL=shell.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shell.js","sourceRoot":"","sources":["../../src/wrap/shell.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,46 @@
1
+ {
2
+ "name": "habit-hooks",
3
+ "version": "0.1.0-beta.0",
4
+ "type": "module",
5
+ "engines": {
6
+ "node": ">=22.13"
7
+ },
8
+ "bin": {
9
+ "habit-hooks": "./dist/cli.js"
10
+ },
11
+ "main": "./dist/index.js",
12
+ "types": "./dist/index.d.ts",
13
+ "files": [
14
+ "dist",
15
+ "src/prompts/*.md",
16
+ "!src/prompts/REVIEW.md",
17
+ "src/skills"
18
+ ],
19
+ "devDependencies": {
20
+ "@eslint/js": "^10.0.1",
21
+ "@types/node": "^25.9.1",
22
+ "@types/picomatch": "^4.0.3",
23
+ "prettier": "^3.8.3",
24
+ "typescript": "^6.0.3",
25
+ "vitest": "^4.1.7"
26
+ },
27
+ "dependencies": {
28
+ "commander": "^14.0.3",
29
+ "eslint": "^10.4.0",
30
+ "fast-glob": "^3.3.3",
31
+ "jiti": "^2.7.0",
32
+ "jscpd": "4.2.4",
33
+ "knip": "5.88.1",
34
+ "picomatch": "^4.0.4",
35
+ "ts-morph": "^28.0.0",
36
+ "typescript-eslint": "^8.60.0"
37
+ },
38
+ "scripts": {
39
+ "build": "rm -rf dist && tsc -p . && chmod +x dist/cli.js",
40
+ "test": "vitest run",
41
+ "test:watch": "vitest",
42
+ "lint": "eslint .",
43
+ "format": "prettier -w .",
44
+ "typecheck": "tsc --noEmit"
45
+ }
46
+ }
@@ -0,0 +1,7 @@
1
+ Comments indicate code that is not self-documenting. The smell is the *need* for the comment — the reader could not work out what the code does from the names and structure alone.
2
+
3
+ Extract complex logic into well-named functions instead of explaining with a comment. A function called `applyDiscountForLoyalCustomers` does not need a header explaining what it does.
4
+
5
+ Remove comments unless they impact functionality (executable annotations) or explain *why* something non-obvious was chosen (a workaround for a specific library bug, a reference to a spec). Comments that explain *what* the code does are almost always redundant — or worse, drift out of sync with the code and start lying.
6
+
7
+ Do not delete an `eslint-disable` or shebang on autopilot — those are flagged separately and exempted from this rule.
@@ -0,0 +1,9 @@
1
+ An "upper" layer reached into a "lower" one — or a peer reached sideways into something it should not know about. The smell is a leak in the layering, not the specific import line that fired.
2
+
3
+ Re-state the violating layer's responsibility in one sentence. If that sentence does not actually need the thing it just imported, the dependency is wrong and the import is the symptom. The fix lives at the seam, not at the import.
4
+
5
+ Ask: (1) Does the upper layer want a *capability* that the lower layer happens to expose, in which case the capability belongs in an abstraction the upper layer owns and the lower layer implements? (2) Did a piece of behaviour drift into the wrong layer over time, in which case it should move rather than be imported across? (3) Is the layering itself wrong for what the code actually does — sometimes the boundary is the bug.
6
+
7
+ Avoid mechanical fixes. Re-exporting the offending symbol from a "neutral" module, or adding a thin pass-through wrapper, leaves the same coupling and adds a misleading name on top. Suppressing the rule for one file teaches the next reader that the boundary is negotiable.
8
+
9
+ A concrete technique: imagine the lower layer is a third-party package you cannot modify. What interface would you wish it exposed? Define that interface in the upper layer (or in a shared seam) and have the lower layer satisfy it. The dependency now points the right way and the layers stay independently understandable.
@@ -0,0 +1,9 @@
1
+ High cyclomatic complexity means a function carries too many decisions — harder to understand, harder to test exhaustively, a frequent home for bugs. The smell is mixed concerns, not the number itself.
2
+
3
+ High complexity often indicates multiple responsibilities. Look for: (1) decision trees that could be strategy patterns or a lookup table, (2) multiple concerns that belong in separate methods, (3) state machines that could be explicit classes with named states.
4
+
5
+ Focus on extracting *meaningful* abstractions, not just shaving complexity metrics. Splitting one `if/else` chain into three nested helpers usually moves the complexity around without making the code clearer.
6
+
7
+ If responsibilities are tangled you may need to first *inline* methods to see the whole picture before redistributing. Think of this when reducing complexity seems particularly hard — stepping backwards often opens up better possibilities.
8
+
9
+ A concrete technique: name each branch by the responsibility it handles. If two branches resolve to the same one-sentence description, they belong together; if one branch has no clear name, that path probably belongs in a separate function or class.
@@ -0,0 +1,3 @@
1
+ `==` triggers silent type coercion (`0 == ''`, `null == undefined`, etc.) that hides real bugs. Use `===` and `!==`.
2
+
3
+ If you genuinely need `null`-or-`undefined` semantics, say so explicitly: `value == null` is sometimes argued for, but a `value === null || value === undefined` (or a dedicated nullish check) reads more honestly.
@@ -0,0 +1,7 @@
1
+ A "fatal" message means ESLint could not analyze the file at all — a parse error, an unresolvable config, or a plugin that threw. There is no rule judgement here; the linter never got far enough to make one.
2
+
3
+ Common causes: a real syntax error in the file; a missing or misnamed `tsconfig.json`; typescript-eslint requiring an explicit `tsconfigRootDir` when sample / fixture folders create multiple candidate roots; a plugin whose major version no longer matches its peers; a config file that imports a module that fails to load.
4
+
5
+ How to diagnose: re-run the failing file directly with `npx eslint <file>` to see the full stack — the JSON output strips it. If the file lives under a fixtures, samples, or vendored folder, check `eslint.config.*`'s `ignores` list; analysing those paths is almost never what you want. For typescript-eslint, set `languageOptions.parserOptions.tsconfigRootDir` (often `import.meta.dirname`) so the parser stops guessing.
6
+
7
+ Do not silence this with `eslint-disable` or by removing the file from the lint set. A fatal means broken analysis, not a style smell — every other rule is also unchecked for that file until you fix it.
@@ -0,0 +1,9 @@
1
+ Functions over 12 lines almost always carry more than one responsibility, and that is the smell to chase — not the line count itself.
2
+
3
+ Analyse responsibilities first: what distinct concerns does this function handle? Ask: (1) Are these separate responsibilities that belong in different methods? (2) Should this become a class with multiple methods? (3) Can you group cohesive data into objects to reduce local variables?
4
+
5
+ Avoid mechanical extraction. Pulling out a `helperA` / `helperB` purely to satisfy the threshold often hides the smell behind worse names and leaves the real shape untouched. Find true responsibility boundaries.
6
+
7
+ If responsibilities are tangled you may need to first *inline* methods to see the whole picture before redistributing. Think of this when reducing line count seems particularly hard — stepping backwards often opens up better possibilities.
8
+
9
+ A concrete technique: write what the method does in one short sentence. Refactor until the code reads as close to that sentence as possible. If you cannot say what it does in one sentence, it almost certainly has more than one responsibility.
@@ -0,0 +1,9 @@
1
+ Files over 200 lines accumulate unrelated concerns. The smell is poor cohesion — a file that asks the reader to hold too many ideas at once — not the raw line count.
2
+
3
+ First identify the seams: which exports, types, or helper clusters actually belong together? A long file usually splits cleanly along one of: a data type and its operations, a feature pipeline, or one concern per file.
4
+
5
+ Avoid mechanical splits. Carving the file at line 200 into `foo-1.ts` and `foo-2.ts`, or moving every private helper into a `utils.ts`, satisfies the threshold without making anything clearer — the cohesion problem just hops to a new place.
6
+
7
+ If the file's structure resists splitting, that is itself the signal: responsibilities are tangled. Look for a missing abstraction (a class, a small module with a focused interface) that would let related pieces move together as a unit.
8
+
9
+ A concrete technique: write a one-sentence description of what each emerging seam *would* be responsible for. If you cannot, you have not found the seam yet — do not split.
@@ -0,0 +1,9 @@
1
+ Long parameter lists violate single responsibility — the function is doing too much, or it is operating on data that belongs together as an object the function should live near.
2
+
3
+ Before grouping parameters into a bag-of-fields object, ask: (1) Should this method actually belong *on* the parameter object as a class method? A free function taking five fields from the same record is often a class method waiting to happen. (2) For static methods with many parameters — this is very often a class waiting to happen. (3) Group cohesive data into meaningful objects and pass those around, even if some methods do not need every field.
4
+
5
+ Favour a declarative style over many locals — once data clusters into objects, control flow usually simplifies too.
6
+
7
+ A literal `{ ...everything }` object that just renames the parameter list does not address the smell; it hides it.
8
+
9
+ A concrete technique: write the calling sites you wish existed (one line each). Make them real — the parameter shape usually falls out of the call sites you want.
@@ -0,0 +1 @@
1
+ Multiple `import` statements from the same module fragment the dependency picture and invite drift (one set of named imports gets updated, the other does not). Merge into a single statement.
@@ -0,0 +1,3 @@
1
+ Unused bindings are noise — they make the reader wonder whether something is missing. Delete the dead code, or if the parameter is required by a signature you cannot change, prefix with `_` to mark it intentional.
2
+
3
+ Do not silence this rule with a no-op reference (`void unused`); that just hides the problem.
@@ -0,0 +1 @@
1
+ `var` hoists to the enclosing function and has no block scope, which produces confusing closures and surprising temporal behaviour. Use `const` by default, `let` only when the binding genuinely needs to be reassigned.
@@ -0,0 +1,3 @@
1
+ TODO / FIXME / XXX / HACK markers are a confession that something is unfinished — and confessions written into code tend to outlive the person who wrote them.
2
+
3
+ Either do the work now, or capture it somewhere that can actually be triaged (issue tracker, task list). A marker in the source is a poor backlog.
@@ -0,0 +1,3 @@
1
+ A `let` that is never reassigned signals to the reader that reassignment is possible — that is a lie. Switch to `const` so the binding's immutability is visible at the declaration site.
2
+
3
+ If you almost reassigned but found a cleaner way (early return, ternary, separate binding), keep it as `const` rather than reverting to `let`.