habit-hooks 0.2.0 → 1.0.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.
- package/README.md +17 -226
- package/bin/habit-hooks.js +32 -0
- package/package.json +11 -44
- package/LICENSE.md +0 -21
- package/dist/baseline/auto-prune.d.ts +0 -4
- package/dist/baseline/auto-prune.js +0 -38
- package/dist/baseline/auto-prune.js.map +0 -1
- package/dist/baseline/commands.d.ts +0 -12
- package/dist/baseline/commands.js +0 -125
- package/dist/baseline/commands.js.map +0 -1
- package/dist/baseline/file-hash.d.ts +0 -2
- package/dist/baseline/file-hash.js +0 -25
- package/dist/baseline/file-hash.js.map +0 -1
- package/dist/baseline/filter.d.ts +0 -9
- package/dist/baseline/filter.js +0 -18
- package/dist/baseline/filter.js.map +0 -1
- package/dist/baseline/reap.d.ts +0 -6
- package/dist/baseline/reap.js +0 -22
- package/dist/baseline/reap.js.map +0 -1
- package/dist/baseline/snooze-index.d.ts +0 -5
- package/dist/baseline/snooze-index.js +0 -65
- package/dist/baseline/snooze-index.js.map +0 -1
- package/dist/baseline/store.d.ts +0 -18
- package/dist/baseline/store.js +0 -106
- package/dist/baseline/store.js.map +0 -1
- package/dist/checks/comment-check.d.ts +0 -3
- package/dist/checks/comment-check.js +0 -85
- package/dist/checks/comment-check.js.map +0 -1
- package/dist/checks/eslint-wrap.d.ts +0 -2
- package/dist/checks/eslint-wrap.js +0 -107
- package/dist/checks/eslint-wrap.js.map +0 -1
- package/dist/checks/jscpd-wrap.d.ts +0 -9
- package/dist/checks/jscpd-wrap.js +0 -159
- package/dist/checks/jscpd-wrap.js.map +0 -1
- package/dist/checks/knip-resolve.d.ts +0 -4
- package/dist/checks/knip-resolve.js +0 -49
- package/dist/checks/knip-resolve.js.map +0 -1
- package/dist/checks/knip-schema.d.ts +0 -32
- package/dist/checks/knip-schema.js +0 -24
- package/dist/checks/knip-schema.js.map +0 -1
- package/dist/checks/knip-wrap.d.ts +0 -4
- package/dist/checks/knip-wrap.js +0 -135
- package/dist/checks/knip-wrap.js.map +0 -1
- package/dist/cli/baseline-commands.d.ts +0 -2
- package/dist/cli/baseline-commands.js +0 -61
- package/dist/cli/baseline-commands.js.map +0 -1
- package/dist/cli/emit.d.ts +0 -7
- package/dist/cli/emit.js +0 -8
- package/dist/cli/emit.js.map +0 -1
- package/dist/cli/init/apply-recommendations.d.ts +0 -3
- package/dist/cli/init/apply-recommendations.js +0 -76
- package/dist/cli/init/apply-recommendations.js.map +0 -1
- package/dist/cli/init/command-runner.d.ts +0 -6
- package/dist/cli/init/command-runner.js +0 -22
- package/dist/cli/init/command-runner.js.map +0 -1
- package/dist/cli/init/completion-report.d.ts +0 -11
- package/dist/cli/init/completion-report.js +0 -98
- package/dist/cli/init/completion-report.js.map +0 -1
- package/dist/cli/init/ctx.d.ts +0 -12
- package/dist/cli/init/ctx.js +0 -16
- package/dist/cli/init/ctx.js.map +0 -1
- package/dist/cli/init/detect.d.ts +0 -10
- package/dist/cli/init/detect.js +0 -69
- package/dist/cli/init/detect.js.map +0 -1
- package/dist/cli/init/git-hook.d.ts +0 -7
- package/dist/cli/init/git-hook.js +0 -52
- package/dist/cli/init/git-hook.js.map +0 -1
- package/dist/cli/init/install-commands.d.ts +0 -8
- package/dist/cli/init/install-commands.js +0 -82
- package/dist/cli/init/install-commands.js.map +0 -1
- package/dist/cli/init/package-scripts.d.ts +0 -6
- package/dist/cli/init/package-scripts.js +0 -55
- package/dist/cli/init/package-scripts.js.map +0 -1
- package/dist/cli/init/prompts.d.ts +0 -9
- package/dist/cli/init/prompts.js +0 -33
- package/dist/cli/init/prompts.js.map +0 -1
- package/dist/cli/init/recommendations.d.ts +0 -14
- package/dist/cli/init/recommendations.js +0 -66
- package/dist/cli/init/recommendations.js.map +0 -1
- package/dist/cli/init/report-language.d.ts +0 -1
- package/dist/cli/init/report-language.js +0 -12
- package/dist/cli/init/report-language.js.map +0 -1
- package/dist/cli/init/reporters.d.ts +0 -12
- package/dist/cli/init/reporters.js +0 -44
- package/dist/cli/init/reporters.js.map +0 -1
- package/dist/cli/init/run.d.ts +0 -17
- package/dist/cli/init/run.js +0 -123
- package/dist/cli/init/run.js.map +0 -1
- package/dist/cli/init/scaffold-baseline.d.ts +0 -4
- package/dist/cli/init/scaffold-baseline.js +0 -11
- package/dist/cli/init/scaffold-baseline.js.map +0 -1
- package/dist/cli/init/scaffold-config.d.ts +0 -20
- package/dist/cli/init/scaffold-config.js +0 -59
- package/dist/cli/init/scaffold-config.js.map +0 -1
- package/dist/cli/init/scaffold-eslint-config.d.ts +0 -2
- package/dist/cli/init/scaffold-eslint-config.js +0 -12
- package/dist/cli/init/scaffold-eslint-config.js.map +0 -1
- package/dist/cli/init/scaffold-jscpd-config.d.ts +0 -3
- package/dist/cli/init/scaffold-jscpd-config.js +0 -12
- package/dist/cli/init/scaffold-jscpd-config.js.map +0 -1
- package/dist/cli/init/scaffold-knip-config.d.ts +0 -2
- package/dist/cli/init/scaffold-knip-config.js +0 -12
- package/dist/cli/init/scaffold-knip-config.js.map +0 -1
- package/dist/cli/init/scaffold-ruff-config.d.ts +0 -2
- package/dist/cli/init/scaffold-ruff-config.js +0 -12
- package/dist/cli/init/scaffold-ruff-config.js.map +0 -1
- package/dist/cli/init/skill.d.ts +0 -8
- package/dist/cli/init/skill.js +0 -39
- package/dist/cli/init/skill.js.map +0 -1
- package/dist/cli/init/snippet.d.ts +0 -2
- package/dist/cli/init/snippet.js +0 -31
- package/dist/cli/init/snippet.js.map +0 -1
- package/dist/cli/init/templates/eslint-config.d.ts +0 -2
- package/dist/cli/init/templates/eslint-config.js +0 -45
- package/dist/cli/init/templates/eslint-config.js.map +0 -1
- package/dist/cli/init/templates/jscpd-config.d.ts +0 -3
- package/dist/cli/init/templates/jscpd-config.js +0 -17
- package/dist/cli/init/templates/jscpd-config.js.map +0 -1
- package/dist/cli/init/templates/knip-config.d.ts +0 -2
- package/dist/cli/init/templates/knip-config.js +0 -8
- package/dist/cli/init/templates/knip-config.js.map +0 -1
- package/dist/cli/init/templates/ruff-config.d.ts +0 -2
- package/dist/cli/init/templates/ruff-config.js +0 -16
- package/dist/cli/init/templates/ruff-config.js.map +0 -1
- package/dist/cli/init/tool-steps.d.ts +0 -2
- package/dist/cli/init/tool-steps.js +0 -78
- package/dist/cli/init/tool-steps.js.map +0 -1
- package/dist/cli/init-command.d.ts +0 -2
- package/dist/cli/init-command.js +0 -59
- package/dist/cli/init-command.js.map +0 -1
- package/dist/cli.d.ts +0 -2
- package/dist/cli.js +0 -101
- package/dist/cli.js.map +0 -1
- package/dist/config/catalogue-unused.d.ts +0 -2
- package/dist/config/catalogue-unused.js +0 -39
- package/dist/config/catalogue-unused.js.map +0 -1
- package/dist/config/catalogue.d.ts +0 -2
- package/dist/config/catalogue.js +0 -163
- package/dist/config/catalogue.js.map +0 -1
- package/dist/config/defaults.d.ts +0 -4
- package/dist/config/defaults.js +0 -12
- package/dist/config/defaults.js.map +0 -1
- package/dist/config/jiti-loader.d.ts +0 -1
- package/dist/config/jiti-loader.js +0 -13
- package/dist/config/jiti-loader.js.map +0 -1
- package/dist/config/load.d.ts +0 -9
- package/dist/config/load.js +0 -54
- package/dist/config/load.js.map +0 -1
- package/dist/config/merge.d.ts +0 -3
- package/dist/config/merge.js +0 -91
- package/dist/config/merge.js.map +0 -1
- package/dist/config/schema.d.ts +0 -41
- package/dist/config/schema.js +0 -4
- package/dist/config/schema.js.map +0 -1
- package/dist/config/tool-smells.d.ts +0 -14
- package/dist/config/tool-smells.js +0 -69
- package/dist/config/tool-smells.js.map +0 -1
- package/dist/config/validate.d.ts +0 -2
- package/dist/config/validate.js +0 -159
- package/dist/config/validate.js.map +0 -1
- package/dist/detect/package-json.d.ts +0 -1
- package/dist/detect/package-json.js +0 -15
- package/dist/detect/package-json.js.map +0 -1
- package/dist/detect/path.d.ts +0 -1
- package/dist/detect/path.js +0 -14
- package/dist/detect/path.js.map +0 -1
- package/dist/detect/tool.d.ts +0 -10
- package/dist/detect/tool.js +0 -75
- package/dist/detect/tool.js.map +0 -1
- package/dist/discover.d.ts +0 -2
- package/dist/discover.js +0 -14
- package/dist/discover.js.map +0 -1
- package/dist/git/exec.d.ts +0 -8
- package/dist/git/exec.js +0 -48
- package/dist/git/exec.js.map +0 -1
- package/dist/git/resolve-scope.d.ts +0 -15
- package/dist/git/resolve-scope.js +0 -89
- package/dist/git/resolve-scope.js.map +0 -1
- package/dist/git/scope.d.ts +0 -7
- package/dist/git/scope.js +0 -58
- package/dist/git/scope.js.map +0 -1
- package/dist/guide/command.d.ts +0 -6
- package/dist/guide/command.js +0 -20
- package/dist/guide/command.js.map +0 -1
- package/dist/guide/guide.d.ts +0 -11
- package/dist/guide/guide.js +0 -82
- package/dist/guide/guide.js.map +0 -1
- package/dist/guide/render.d.ts +0 -3
- package/dist/guide/render.js +0 -14
- package/dist/guide/render.js.map +0 -1
- package/dist/index.d.ts +0 -1
- package/dist/index.js +0 -2
- package/dist/index.js.map +0 -1
- package/dist/mapper/mapper.d.ts +0 -34
- package/dist/mapper/mapper.js +0 -80
- package/dist/mapper/mapper.js.map +0 -1
- package/dist/prompts/loader.d.ts +0 -6
- package/dist/prompts/loader.js +0 -27
- package/dist/prompts/loader.js.map +0 -1
- package/dist/prompts/packaged-dir.d.ts +0 -1
- package/dist/prompts/packaged-dir.js +0 -10
- package/dist/prompts/packaged-dir.js.map +0 -1
- package/dist/prompts/registry.d.ts +0 -3
- package/dist/prompts/registry.js +0 -43
- package/dist/prompts/registry.js.map +0 -1
- package/dist/rule-files.d.ts +0 -2
- package/dist/rule-files.js +0 -22
- package/dist/rule-files.js.map +0 -1
- package/dist/rules/registry.d.ts +0 -3
- package/dist/rules/registry.js +0 -37
- package/dist/rules/registry.js.map +0 -1
- package/dist/runner.d.ts +0 -15
- package/dist/runner.js +0 -141
- package/dist/runner.js.map +0 -1
- package/dist/sensors/adapter.d.ts +0 -17
- package/dist/sensors/adapter.js +0 -81
- package/dist/sensors/adapter.js.map +0 -1
- package/dist/sensors/deptry-sensor.d.ts +0 -3
- package/dist/sensors/deptry-sensor.js +0 -43
- package/dist/sensors/deptry-sensor.js.map +0 -1
- package/dist/sensors/line-count-sensor.d.ts +0 -3
- package/dist/sensors/line-count-sensor.js +0 -50
- package/dist/sensors/line-count-sensor.js.map +0 -1
- package/dist/sensors/needs-extraction.d.ts +0 -3
- package/dist/sensors/needs-extraction.js +0 -57
- package/dist/sensors/needs-extraction.js.map +0 -1
- package/dist/sensors/preset.d.ts +0 -17
- package/dist/sensors/preset.js +0 -57
- package/dist/sensors/preset.js.map +0 -1
- package/dist/sensors/python-preset.d.ts +0 -7
- package/dist/sensors/python-preset.js +0 -34
- package/dist/sensors/python-preset.js.map +0 -1
- package/dist/sensors/runner.d.ts +0 -12
- package/dist/sensors/runner.js +0 -94
- package/dist/sensors/runner.js.map +0 -1
- package/dist/sensors/types.d.ts +0 -15
- package/dist/sensors/types.js +0 -5
- package/dist/sensors/types.js.map +0 -1
- package/dist/types.d.ts +0 -44
- package/dist/types.js +0 -2
- package/dist/types.js.map +0 -1
- package/dist/wrap/notices.d.ts +0 -19
- package/dist/wrap/notices.js +0 -38
- package/dist/wrap/notices.js.map +0 -1
- package/dist/wrap/resolve.d.ts +0 -5
- package/dist/wrap/resolve.js +0 -9
- package/dist/wrap/resolve.js.map +0 -1
- package/dist/wrap/run.d.ts +0 -17
- package/dist/wrap/run.js +0 -31
- package/dist/wrap/run.js.map +0 -1
- package/dist/wrap/shell.d.ts +0 -15
- package/dist/wrap/shell.js +0 -50
- package/dist/wrap/shell.js.map +0 -1
- package/src/prompts/deep-nesting.md +0 -13
- package/src/prompts/duplicated-code.md +0 -9
- package/src/prompts/high-complexity.md +0 -9
- package/src/prompts/needs-extraction.md +0 -9
- package/src/prompts/non-essential-comment.md +0 -7
- package/src/prompts/non-null-assertion.md +0 -10
- package/src/prompts/oversized-file.md +0 -9
- package/src/prompts/oversized-function.issues.njk +0 -5
- package/src/prompts/oversized-function.md +0 -9
- package/src/prompts/parse-error.md +0 -5
- package/src/prompts/too-many-parameters.md +0 -9
- package/src/prompts/uncoached.md +0 -4
- package/src/skills/habit-hooks-prompting/SKILL.md +0 -37
- package/src/skills/habit-hooks-review/SKILL.md +0 -108
package/dist/sensors/runner.js
DELETED
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
function assertUniqueIds(sensors) {
|
|
2
|
-
const seen = new Set();
|
|
3
|
-
for (const sensor of sensors) {
|
|
4
|
-
if (seen.has(sensor.id))
|
|
5
|
-
throw new Error(`duplicate sensor id: ${sensor.id}`);
|
|
6
|
-
seen.add(sensor.id);
|
|
7
|
-
}
|
|
8
|
-
}
|
|
9
|
-
function buildProducers(sensors) {
|
|
10
|
-
const producers = new Map();
|
|
11
|
-
for (const sensor of sensors) {
|
|
12
|
-
for (const smell of sensor.produces) {
|
|
13
|
-
const list = producers.get(smell) ?? [];
|
|
14
|
-
list.push(sensor);
|
|
15
|
-
producers.set(smell, list);
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
return producers;
|
|
19
|
-
}
|
|
20
|
-
function dependencyKeys(sensor) {
|
|
21
|
-
return sensor.dependsOn ?? [];
|
|
22
|
-
}
|
|
23
|
-
// Drop a multi sensor whose depended-on smells are not produced by any other
|
|
24
|
-
// sensor in the set — e.g. a composite whose inputs were gated out by config.
|
|
25
|
-
// This keeps SensorRunner construction (which throws on unproduced deps) from
|
|
26
|
-
// crashing a run when a dependency producer is simply inactive.
|
|
27
|
-
export function satisfiableSensors(sensors) {
|
|
28
|
-
return sensors.filter((sensor) => {
|
|
29
|
-
const others = sensors.filter((other) => other.id !== sensor.id);
|
|
30
|
-
const produced = new Set(others.flatMap((other) => other.produces));
|
|
31
|
-
return dependencyKeys(sensor).every((smell) => produced.has(smell));
|
|
32
|
-
});
|
|
33
|
-
}
|
|
34
|
-
function assertSatisfiable(sensor, producers) {
|
|
35
|
-
for (const smell of dependencyKeys(sensor)) {
|
|
36
|
-
const others = (producers.get(smell) ?? []).filter((p) => p.id !== sensor.id);
|
|
37
|
-
if (others.length === 0) {
|
|
38
|
-
throw new Error(`sensor '${sensor.id}' depends on unproduced smell: ${smell}`);
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
function predecessorsOf(sensor, producers) {
|
|
43
|
-
const seen = new Map();
|
|
44
|
-
for (const smell of dependencyKeys(sensor)) {
|
|
45
|
-
for (const producer of producers.get(smell) ?? []) {
|
|
46
|
-
if (producer.id !== sensor.id)
|
|
47
|
-
seen.set(producer.id, producer);
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
return [...seen.values()];
|
|
51
|
-
}
|
|
52
|
-
function isReady(sensor, placed, producers) {
|
|
53
|
-
return predecessorsOf(sensor, producers).every((p) => placed.has(p.id));
|
|
54
|
-
}
|
|
55
|
-
function orderSensors(sensors, producers) {
|
|
56
|
-
const order = [];
|
|
57
|
-
const placed = new Set();
|
|
58
|
-
while (order.length < sensors.length) {
|
|
59
|
-
const next = sensors.find((s) => !placed.has(s.id) && isReady(s, placed, producers));
|
|
60
|
-
if (next === undefined)
|
|
61
|
-
throw new Error('sensor dependency cycle detected');
|
|
62
|
-
order.push(next);
|
|
63
|
-
placed.add(next.id);
|
|
64
|
-
}
|
|
65
|
-
return order;
|
|
66
|
-
}
|
|
67
|
-
function gatherDeps(sensor, issues) {
|
|
68
|
-
const wanted = new Set(dependencyKeys(sensor));
|
|
69
|
-
if (wanted.size === 0)
|
|
70
|
-
return [];
|
|
71
|
-
return issues.filter((issue) => wanted.has(issue.smell));
|
|
72
|
-
}
|
|
73
|
-
export class SensorRunner {
|
|
74
|
-
ordered;
|
|
75
|
-
constructor(sensors) {
|
|
76
|
-
assertUniqueIds(sensors);
|
|
77
|
-
const producers = buildProducers(sensors);
|
|
78
|
-
for (const sensor of sensors)
|
|
79
|
-
assertSatisfiable(sensor, producers);
|
|
80
|
-
this.ordered = orderSensors(sensors, producers);
|
|
81
|
-
}
|
|
82
|
-
get sensors() {
|
|
83
|
-
return [...this.ordered];
|
|
84
|
-
}
|
|
85
|
-
async run(input) {
|
|
86
|
-
const issues = [];
|
|
87
|
-
for (const sensor of this.ordered) {
|
|
88
|
-
const ctx = { files: input.files, cwd: input.cwd, deps: gatherDeps(sensor, issues) };
|
|
89
|
-
issues.push(...(await sensor.run(ctx)));
|
|
90
|
-
}
|
|
91
|
-
return issues;
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
//# sourceMappingURL=runner.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"runner.js","sourceRoot":"","sources":["../../src/sensors/runner.ts"],"names":[],"mappings":"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"}
|
package/dist/sensors/types.d.ts
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
export interface Issue {
|
|
2
|
-
smell: string;
|
|
3
|
-
details: Record<string, unknown>;
|
|
4
|
-
}
|
|
5
|
-
export interface SensorContext {
|
|
6
|
-
files: string[];
|
|
7
|
-
cwd: string;
|
|
8
|
-
deps: Issue[];
|
|
9
|
-
}
|
|
10
|
-
export interface Sensor {
|
|
11
|
-
id: string;
|
|
12
|
-
produces: string[];
|
|
13
|
-
dependsOn?: string[];
|
|
14
|
-
run(_ctx: SensorContext): Promise<Issue[]>;
|
|
15
|
-
}
|
package/dist/sensors/types.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/sensors/types.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAChF,8EAA8E;AAC9E,mDAAmD"}
|
package/dist/types.d.ts
DELETED
|
@@ -1,44 +0,0 @@
|
|
|
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
|
-
fix?: string;
|
|
18
|
-
guidance?: string;
|
|
19
|
-
commentCheck?: CommentCheckThresholds;
|
|
20
|
-
}
|
|
21
|
-
export interface CoachingPrompt {
|
|
22
|
-
id: string;
|
|
23
|
-
title: string;
|
|
24
|
-
description: string;
|
|
25
|
-
severity: Severity;
|
|
26
|
-
guidancePath: string;
|
|
27
|
-
}
|
|
28
|
-
export interface Violation {
|
|
29
|
-
ruleId: string;
|
|
30
|
-
file: string;
|
|
31
|
-
line: number;
|
|
32
|
-
column?: number;
|
|
33
|
-
message: string;
|
|
34
|
-
source?: string;
|
|
35
|
-
}
|
|
36
|
-
export interface CheckOutcome {
|
|
37
|
-
violations: Violation[];
|
|
38
|
-
stderr?: string[];
|
|
39
|
-
failures?: string[];
|
|
40
|
-
}
|
|
41
|
-
export interface Check {
|
|
42
|
-
id: string;
|
|
43
|
-
run(_files: string[], _rules: Rule[], _cwd?: string): Promise<Violation[] | CheckOutcome>;
|
|
44
|
-
}
|
package/dist/types.js
DELETED
package/dist/types.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
|
package/dist/wrap/notices.d.ts
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
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 interface SensorSink {
|
|
13
|
-
notices: string[];
|
|
14
|
-
failures: string[];
|
|
15
|
-
}
|
|
16
|
-
export declare function spawnFailureOutcome(notices: string[], message: string): CheckOutcome;
|
|
17
|
-
export declare function recordSpawnFailure(sink: SensorSink, message: string): void;
|
|
18
|
-
export declare function noticesFor(tool: string, resolution: BinResolution, cwd: string): string[];
|
|
19
|
-
export declare function absolutize(cwd: string, file: string): string;
|
package/dist/wrap/notices.js
DELETED
|
@@ -1,38 +0,0 @@
|
|
|
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
|
-
// A sensor that could not spawn or timed out: the message is shown (stderr) and
|
|
24
|
-
// recorded as a failure so the run fails (exit 1), per docs/sensors.md.
|
|
25
|
-
export function spawnFailureOutcome(notices, message) {
|
|
26
|
-
return { violations: [], stderr: [...notices, message], failures: [message] };
|
|
27
|
-
}
|
|
28
|
-
export function recordSpawnFailure(sink, message) {
|
|
29
|
-
sink.notices.push(message);
|
|
30
|
-
sink.failures.push(message);
|
|
31
|
-
}
|
|
32
|
-
export function noticesFor(tool, resolution, cwd) {
|
|
33
|
-
return resolution.isFallback ? [fallbackNotice(tool, cwd)] : [];
|
|
34
|
-
}
|
|
35
|
-
export function absolutize(cwd, file) {
|
|
36
|
-
return isAbsolute(file) ? file : join(cwd, file);
|
|
37
|
-
}
|
|
38
|
-
//# sourceMappingURL=notices.js.map
|
package/dist/wrap/notices.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
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;AAUD,gFAAgF;AAChF,wEAAwE;AACxE,MAAM,UAAU,mBAAmB,CAAC,OAAiB,EAAE,OAAe;IACpE,OAAO,EAAE,UAAU,EAAE,EAAE,EAAE,MAAM,EAAE,CAAC,GAAG,OAAO,EAAE,OAAO,CAAC,EAAE,QAAQ,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC;AAChF,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,IAAgB,EAAE,OAAe;IAClE,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAC3B,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;AAC9B,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"}
|
package/dist/wrap/resolve.d.ts
DELETED
package/dist/wrap/resolve.js
DELETED
|
@@ -1,9 +0,0 @@
|
|
|
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
|
package/dist/wrap/resolve.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
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"}
|
package/dist/wrap/run.d.ts
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import { type ShellResult } from './shell.js';
|
|
2
|
-
import { type BinResolution } from './notices.js';
|
|
3
|
-
import type { CheckOutcome } from '../types.js';
|
|
4
|
-
interface SpawnSkip {
|
|
5
|
-
skipWarning: string;
|
|
6
|
-
}
|
|
7
|
-
export declare function skipOutcome(skip: SpawnSkip, notices: string[]): CheckOutcome;
|
|
8
|
-
interface SpawnWrappedArgs {
|
|
9
|
-
tool: string;
|
|
10
|
-
resolution: BinResolution;
|
|
11
|
-
cwd: string;
|
|
12
|
-
args: string[];
|
|
13
|
-
}
|
|
14
|
-
export declare function spawnWrapped(spec: SpawnWrappedArgs): Promise<ShellResult | SpawnSkip>;
|
|
15
|
-
export declare function isSpawnSkip(value: ShellResult | SpawnSkip): value is SpawnSkip;
|
|
16
|
-
export declare function parseJsonStdout<T>(stdout: string, startsWith: '{' | '['): T | null;
|
|
17
|
-
export {};
|
package/dist/wrap/run.js
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import { runTool } from './shell.js';
|
|
2
|
-
import { isSpawnFailure, spawnFailureOutcome, spawnFailureWarning } from './notices.js';
|
|
3
|
-
import { spawnTarget } from './resolve.js';
|
|
4
|
-
// A spawn/timeout skip becomes a failing outcome: shown to the user and recorded
|
|
5
|
-
// as a failure so the run fails (exit 1), per docs/sensors.md.
|
|
6
|
-
export function skipOutcome(skip, notices) {
|
|
7
|
-
return spawnFailureOutcome(notices, skip.skipWarning);
|
|
8
|
-
}
|
|
9
|
-
export async function spawnWrapped(spec) {
|
|
10
|
-
const { tool, resolution, cwd, args } = spec;
|
|
11
|
-
const target = spawnTarget(resolution.binPath, args);
|
|
12
|
-
const result = await runTool({ bin: target.bin, args: target.args, cwd });
|
|
13
|
-
if (isSpawnFailure(result))
|
|
14
|
-
return { skipWarning: spawnFailureWarning(tool, cwd, result.warnings) };
|
|
15
|
-
return result;
|
|
16
|
-
}
|
|
17
|
-
export function isSpawnSkip(value) {
|
|
18
|
-
return value.skipWarning !== undefined;
|
|
19
|
-
}
|
|
20
|
-
export function parseJsonStdout(stdout, startsWith) {
|
|
21
|
-
const trimmed = stdout.trim();
|
|
22
|
-
if (trimmed.length === 0 || !trimmed.startsWith(startsWith))
|
|
23
|
-
return null;
|
|
24
|
-
try {
|
|
25
|
-
return JSON.parse(trimmed);
|
|
26
|
-
}
|
|
27
|
-
catch {
|
|
28
|
-
return null;
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
//# sourceMappingURL=run.js.map
|
package/dist/wrap/run.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
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,EAAE,mBAAmB,EAAsB,MAAM,cAAc,CAAC;AAC5G,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAO3C,iFAAiF;AACjF,+DAA+D;AAC/D,MAAM,UAAU,WAAW,CAAC,IAAe,EAAE,OAAiB;IAC5D,OAAO,mBAAmB,CAAC,OAAO,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC;AACxD,CAAC;AASD,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"}
|
package/dist/wrap/shell.d.ts
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
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
|
-
stdin?: string;
|
|
13
|
-
}
|
|
14
|
-
export declare function runTool(opts: RunToolOptions): Promise<ShellResult>;
|
|
15
|
-
export {};
|
package/dist/wrap/shell.js
DELETED
|
@@ -1,50 +0,0 @@
|
|
|
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 writeStdin(child, stdin) {
|
|
13
|
-
if (stdin === undefined)
|
|
14
|
-
return;
|
|
15
|
-
child.stdin.on('error', () => undefined);
|
|
16
|
-
child.stdin.end(stdin);
|
|
17
|
-
}
|
|
18
|
-
function settleOnce(settler, result) {
|
|
19
|
-
if (settler.done.value)
|
|
20
|
-
return;
|
|
21
|
-
settler.done.value = true;
|
|
22
|
-
settler.clearTimer();
|
|
23
|
-
settler.resolve(result);
|
|
24
|
-
}
|
|
25
|
-
function closeResult(info) {
|
|
26
|
-
if (info.signal !== null)
|
|
27
|
-
return emptyFailure(`${info.bin} terminated by signal ${info.signal}`);
|
|
28
|
-
const stdout = Buffer.concat(info.buffers.stdout).toString('utf8');
|
|
29
|
-
const stderr = Buffer.concat(info.buffers.stderr).toString('utf8');
|
|
30
|
-
return { stdout, stderr, exitCode: info.code ?? -1, warnings: [] };
|
|
31
|
-
}
|
|
32
|
-
function startKillTimer(child, onTimeout, timeoutMs) {
|
|
33
|
-
return setTimeout(() => {
|
|
34
|
-
child.kill('SIGKILL');
|
|
35
|
-
onTimeout();
|
|
36
|
-
}, timeoutMs);
|
|
37
|
-
}
|
|
38
|
-
export function runTool(opts) {
|
|
39
|
-
const timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
40
|
-
return new Promise((resolve) => {
|
|
41
|
-
const child = spawn(opts.bin, opts.args, { cwd: opts.cwd });
|
|
42
|
-
const buffers = collectStreams(child);
|
|
43
|
-
writeStdin(child, opts.stdin);
|
|
44
|
-
const timer = startKillTimer(child, () => settleOnce(settler, emptyFailure(`${opts.bin} timed out after ${timeoutMs}ms`)), timeoutMs);
|
|
45
|
-
const settler = { resolve, done: { value: false }, clearTimer: () => clearTimeout(timer) };
|
|
46
|
-
child.on('error', (e) => settleOnce(settler, emptyFailure(`${opts.bin} failed to spawn: ${e.message}`)));
|
|
47
|
-
child.on('close', (code, signal) => settleOnce(settler, closeResult({ buffers, bin: opts.bin, code, signal })));
|
|
48
|
-
});
|
|
49
|
-
}
|
|
50
|
-
//# sourceMappingURL=shell.js.map
|
package/dist/wrap/shell.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"shell.js","sourceRoot":"","sources":["../../src/wrap/shell.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
Deeply nested blocks — `if` inside `for` inside `try` inside `if` — force the reader to hold every enclosing condition in their head at once. The smell is not the indentation; it is that the function is doing too much branching in one place.
|
|
2
|
-
|
|
3
|
-
Read the nesting from the inside out and ask what the innermost block actually needs. Usually most of the enclosing conditions are *guards* — preconditions that should be checked and bailed on early, not wrapped around the real work.
|
|
4
|
-
|
|
5
|
-
Prefer, in order:
|
|
6
|
-
|
|
7
|
-
1. **Guard clauses / early returns.** Invert a condition and `return` (or `continue`/`throw`) early so the happy path stays at the top indentation level. Each guard you lift removes one level of nesting from everything below it.
|
|
8
|
-
2. **Extract a helper.** When an inner block is a coherent sub-step, pull it into a named function. The name documents the intent and the nesting moves into a flat, separately-readable unit.
|
|
9
|
-
3. **Replace the structure.** A deep `if/else` ladder is often a lookup table or a polymorphic dispatch in disguise; a nested loop is often a `filter`/`map`/`flatMap` pipeline.
|
|
10
|
-
|
|
11
|
-
Avoid the mechanical fix of merging conditions with `&&` just to drop a level — that trades vertical nesting for an unreadable horizontal condition. The goal is a function whose shape you can take in at a glance, not one that merely passes the depth threshold.
|
|
12
|
-
|
|
13
|
-
If the nesting is genuinely irreducible (a real algorithm with interacting conditions), extracting the inner loops into well-named helpers is still the move: keep each function shallow even when the algorithm as a whole is deep.
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
Duplicated blocks are the cheapest visible sign of a missing abstraction. The right fix is rarely "extract a helper" — it's usually to name the concept that both copies are reaching for.
|
|
2
|
-
|
|
3
|
-
Read both occurrences side by side and ask: (1) Are these two copies of the same idea, or two different ideas that happen to look alike? (2) If they are the same idea, what is its name? (3) Does the duplicated block belong to a domain concept that does not exist yet — a value object, a strategy, a small class?
|
|
4
|
-
|
|
5
|
-
Avoid mechanical extraction. Pulling the matching lines into a function with five parameters and a couple of conditionals leaves the real shape untouched and adds a worse name on top. If extracting feels awkward, the underlying abstraction is wrong — step back and find the right one.
|
|
6
|
-
|
|
7
|
-
A concrete technique: try to write one sentence that names what both copies do. If the sentence reads naturally, that sentence is the name of the abstraction you are missing. If you can only describe the block as "the code that does X, Y, and Z", the duplication is hiding two or three smaller abstractions, not one.
|
|
8
|
-
|
|
9
|
-
If the two copies actually diverge in important ways and a shared abstraction would distort either side, document the decision (a short note or a test that pins both behaviours) and snooze the duplication — duplication is the lesser evil compared to a wrong abstraction.
|
|
@@ -1,9 +0,0 @@
|
|
|
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.
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
This file is **both** oversized and duplicated. Those two signals together point at one fix, and it is not "split the file in half."
|
|
2
|
-
|
|
3
|
-
When a long file also repeats a block, the length is usually a *symptom*: the file has accreted a concept that wants its own home, and the duplication is that concept leaking out in two places. Splitting the file at the line threshold would scatter the duplication further; deduplicating in place would leave the bloated file just as incohesive. Do both at once, in the right order.
|
|
4
|
-
|
|
5
|
-
Read the duplicated block first and name what it does in one sentence. That sentence is the missing module: extract the duplicated block into a shared unit (a small module, a class, a value object) with a focused interface — not a five-parameter helper that merely hides the lines.
|
|
6
|
-
|
|
7
|
-
Then let that extraction *carry weight out of the file*. Move the related types and helpers that belong with the new abstraction along with it. Re-check the file size afterwards: a good extraction usually brings the file back under the threshold on its own, because the thing you pulled out was the reason it was too big.
|
|
8
|
-
|
|
9
|
-
If the two copies are genuinely different ideas that only look alike, do not force a shared abstraction — extract along the real seams instead, and if the duplication is the lesser evil, snooze it with a note rather than distorting either side.
|
|
@@ -1,7 +0,0 @@
|
|
|
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.
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
A null check — and especially a `!` — is usually covering an underlying design problem, not handling a value that's legitimately sometimes-missing. Before you write one, say out loud what the null actually represents and how it relates to the other objects. That sentence almost always names the real fix.
|
|
2
|
-
|
|
3
|
-
Two design smells hide behind most null assertions:
|
|
4
|
-
|
|
5
|
-
- **A circular dependency with no clear owner.** Two objects reference each other, so neither can be built first and one sits briefly null. The fix is to give the dependency a direction: work out which object owns the other (operates on its state, is its exclusive consumer) and have the owner *create* the owned so they're initialised together — or, if it's really an upward event, give the emitter an opaque handler (no-op default) that the owner wires after construction. The mutual reference disappears.
|
|
6
|
-
- **Null standing in for a special case** — "outside the grid", "not found", "not loaded yet". Model that case explicitly: a subclass or a separate class with the same interface — a null object, like a `NullCell` that behaves as a blocked cell — resolved once at the boundary so no caller ever sees null.
|
|
7
|
-
|
|
8
|
-
Sometimes a null genuinely is unavoidable. Even then `!` is wrong: it swallows the failure at runtime. Replace it with an explicit check — an `if`-guard / early return, or a throw with a clear message — so a broken assumption fails loudly, where it happens.
|
|
9
|
-
|
|
10
|
-
The only exception: an extremely simple method where the nullable's scope is tiny, a wrong value genuinely cannot cause a silent error, and there's a real performance gain. This should be vanishingly rare — check in with a human before settling for it.
|
|
@@ -1,9 +0,0 @@
|
|
|
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.
|
|
@@ -1,9 +0,0 @@
|
|
|
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.
|
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
A fatal message means ESLint could not analyse the file at all, so every other rule went unchecked there too — real issues in this file are currently invisible.
|
|
2
|
-
|
|
3
|
-
Typical causes worth checking: a syntax error, a missing or misconfigured `tsconfig.json`, a plugin version mismatch, or a config that fails to import. Re-run the single file with `npx eslint <file>` to see the full stack the JSON output hides.
|
|
4
|
-
|
|
5
|
-
You've fixed it when a deliberate change to the file produces the ordinary lint error or warning you'd expect — that proves analysis is running again. Do not silence it with `eslint-disable` or by dropping the file from the lint set.
|
|
@@ -1,9 +0,0 @@
|
|
|
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.
|
package/src/prompts/uncoached.md
DELETED
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
General guidance: the issues listed are code smells. They tell you that there is likely something wrong with the code. Follow these steps:
|
|
2
|
-
- Ask yourself why the rule exists in the first place. What is it telling you about the code?
|
|
3
|
-
- Find a fix that improves maintainability, cuts cruft — doing the same with fewer statements where that lowers cognitive load — and/or improves security, scalability, and resilience.
|
|
4
|
-
- AVOID AT ALL COST: any fix that is designed to appease the reporting tool, but goes against the spirit of the warning.
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: habit-hooks-prompting
|
|
3
|
-
description: "Write or revise a habit-hooks coaching prompt. Use when a linter / knip / jscpd rule fires and the agent's default fix is wrong or shallow, or when adding a project-local override prompt. Keeps prompts short and outcome-focused using the ROSE pattern."
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Habit Hooks Prompting
|
|
7
|
-
|
|
8
|
-
Coaching prompts exist to **change behaviour the agent gets wrong by default** — nothing else.
|
|
9
|
-
|
|
10
|
-
The goal of every prompt is to drive the agent to **fix the root cause the violation points at, not to appease the reporting tool.** A fix that clears the report while going against the spirit of the rule is a failure, even when the tool goes green.
|
|
11
|
-
|
|
12
|
-
## When to write one
|
|
13
|
-
|
|
14
|
-
- The rule's own message already produces the right fix → **write nothing.** Let it fall through to the uncoached prompt. Don't coach what already works.
|
|
15
|
-
- The agent's default reaction is shallow or wrong — mechanical extraction that misses the real design problem, silencing instead of fixing, a reflexive `eslint-disable` → write a prompt that redirects it.
|
|
16
|
-
|
|
17
|
-
This is reactive: add a prompt the moment you catch the default behaviour being wrong, not pre-emptively for every rule.
|
|
18
|
-
|
|
19
|
-
## Keep it short (KISS)
|
|
20
|
-
|
|
21
|
-
A good prompt is the **minimum text that reliably nudges the right behaviour**. If it reads like an essay, cut it — direction beats explanation. The agent already has the violation in front of it.
|
|
22
|
-
|
|
23
|
-
## Use ROSE
|
|
24
|
-
|
|
25
|
-
ROSE structures feedback so the agent understands *why*, not just *what*, and fixes the cause rather than the symptom:
|
|
26
|
-
|
|
27
|
-
- **R**isk — the consequence of leaving it as-is.
|
|
28
|
-
- **O**bservation — what was seen. **The linter already supplies this** (file, line, rule); don't repeat it.
|
|
29
|
-
- **S**olution — the specific move you want, including the non-obvious one the agent tends to miss.
|
|
30
|
-
- **E**xpected outcome — what "done right" looks like, so the agent can check itself.
|
|
31
|
-
|
|
32
|
-
So a habit-hooks prompt writes **R, S, E** and lets the tool supply **O**. See `eslint-fatal.md` and `eslint-typescript-eslint-no-non-null-assertion.md` for prompts in this shape.
|
|
33
|
-
|
|
34
|
-
## Where prompts live
|
|
35
|
-
|
|
36
|
-
- Bundled: `src/prompts/<slugified-rule-id>.md` (`:` and `/` → `-`, `@` dropped).
|
|
37
|
-
- Project-local override: a file of the same name in the directory set by `prompts:` in `habit-hooks.config`.
|