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.
- package/LICENSE.md +21 -0
- package/README.md +233 -0
- package/dist/baseline/commands.d.ts +12 -0
- package/dist/baseline/commands.js +131 -0
- package/dist/baseline/commands.js.map +1 -0
- package/dist/baseline/file-hash.d.ts +2 -0
- package/dist/baseline/file-hash.js +25 -0
- package/dist/baseline/file-hash.js.map +1 -0
- package/dist/baseline/filter.d.ts +8 -0
- package/dist/baseline/filter.js +31 -0
- package/dist/baseline/filter.js.map +1 -0
- package/dist/baseline/store.d.ts +18 -0
- package/dist/baseline/store.js +106 -0
- package/dist/baseline/store.js.map +1 -0
- package/dist/checks/comment-check.d.ts +3 -0
- package/dist/checks/comment-check.js +83 -0
- package/dist/checks/comment-check.js.map +1 -0
- package/dist/checks/eslint-wrap.d.ts +2 -0
- package/dist/checks/eslint-wrap.js +104 -0
- package/dist/checks/eslint-wrap.js.map +1 -0
- package/dist/checks/jscpd-wrap.d.ts +6 -0
- package/dist/checks/jscpd-wrap.js +158 -0
- package/dist/checks/jscpd-wrap.js.map +1 -0
- package/dist/checks/knip-resolve.d.ts +4 -0
- package/dist/checks/knip-resolve.js +49 -0
- package/dist/checks/knip-resolve.js.map +1 -0
- package/dist/checks/knip-schema.d.ts +32 -0
- package/dist/checks/knip-schema.js +24 -0
- package/dist/checks/knip-schema.js.map +1 -0
- package/dist/checks/knip-wrap.d.ts +4 -0
- package/dist/checks/knip-wrap.js +127 -0
- package/dist/checks/knip-wrap.js.map +1 -0
- package/dist/cli/baseline-commands.d.ts +2 -0
- package/dist/cli/baseline-commands.js +61 -0
- package/dist/cli/baseline-commands.js.map +1 -0
- package/dist/cli/emit.d.ts +7 -0
- package/dist/cli/emit.js +8 -0
- package/dist/cli/emit.js.map +1 -0
- package/dist/cli/init/detect.d.ts +8 -0
- package/dist/cli/init/detect.js +20 -0
- package/dist/cli/init/detect.js.map +1 -0
- package/dist/cli/init/git-hook.d.ts +6 -0
- package/dist/cli/init/git-hook.js +48 -0
- package/dist/cli/init/git-hook.js.map +1 -0
- package/dist/cli/init/install-commands.d.ts +6 -0
- package/dist/cli/init/install-commands.js +55 -0
- package/dist/cli/init/install-commands.js.map +1 -0
- package/dist/cli/init/package-scripts.d.ts +6 -0
- package/dist/cli/init/package-scripts.js +55 -0
- package/dist/cli/init/package-scripts.js.map +1 -0
- package/dist/cli/init/prompts.d.ts +9 -0
- package/dist/cli/init/prompts.js +33 -0
- package/dist/cli/init/prompts.js.map +1 -0
- package/dist/cli/init/reporters.d.ts +11 -0
- package/dist/cli/init/reporters.js +40 -0
- package/dist/cli/init/reporters.js.map +1 -0
- package/dist/cli/init/run.d.ts +12 -0
- package/dist/cli/init/run.js +159 -0
- package/dist/cli/init/run.js.map +1 -0
- package/dist/cli/init/scaffold-baseline.d.ts +4 -0
- package/dist/cli/init/scaffold-baseline.js +11 -0
- package/dist/cli/init/scaffold-baseline.js.map +1 -0
- package/dist/cli/init/scaffold-config.d.ts +13 -0
- package/dist/cli/init/scaffold-config.js +42 -0
- package/dist/cli/init/scaffold-config.js.map +1 -0
- package/dist/cli/init/scaffold-eslint-config.d.ts +2 -0
- package/dist/cli/init/scaffold-eslint-config.js +12 -0
- package/dist/cli/init/scaffold-eslint-config.js.map +1 -0
- package/dist/cli/init/scaffold-jscpd-config.d.ts +2 -0
- package/dist/cli/init/scaffold-jscpd-config.js +12 -0
- package/dist/cli/init/scaffold-jscpd-config.js.map +1 -0
- package/dist/cli/init/scaffold-knip-config.d.ts +2 -0
- package/dist/cli/init/scaffold-knip-config.js +12 -0
- package/dist/cli/init/scaffold-knip-config.js.map +1 -0
- package/dist/cli/init/skill.d.ts +7 -0
- package/dist/cli/init/skill.js +36 -0
- package/dist/cli/init/skill.js.map +1 -0
- package/dist/cli/init/snippet.d.ts +1 -0
- package/dist/cli/init/snippet.js +18 -0
- package/dist/cli/init/snippet.js.map +1 -0
- package/dist/cli/init/templates/eslint-config.d.ts +2 -0
- package/dist/cli/init/templates/eslint-config.js +35 -0
- package/dist/cli/init/templates/eslint-config.js.map +1 -0
- package/dist/cli/init/templates/jscpd-config.d.ts +2 -0
- package/dist/cli/init/templates/jscpd-config.js +9 -0
- package/dist/cli/init/templates/jscpd-config.js.map +1 -0
- package/dist/cli/init/templates/knip-config.d.ts +2 -0
- package/dist/cli/init/templates/knip-config.js +8 -0
- package/dist/cli/init/templates/knip-config.js.map +1 -0
- package/dist/cli/init-command.d.ts +2 -0
- package/dist/cli/init-command.js +33 -0
- package/dist/cli/init-command.js.map +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +101 -0
- package/dist/cli.js.map +1 -0
- package/dist/config/defaults.d.ts +4 -0
- package/dist/config/defaults.js +172 -0
- package/dist/config/defaults.js.map +1 -0
- package/dist/config/jiti-loader.d.ts +1 -0
- package/dist/config/jiti-loader.js +13 -0
- package/dist/config/jiti-loader.js.map +1 -0
- package/dist/config/load.d.ts +8 -0
- package/dist/config/load.js +53 -0
- package/dist/config/load.js.map +1 -0
- package/dist/config/merge.d.ts +3 -0
- package/dist/config/merge.js +90 -0
- package/dist/config/merge.js.map +1 -0
- package/dist/config/schema.d.ts +32 -0
- package/dist/config/schema.js +4 -0
- package/dist/config/schema.js.map +1 -0
- package/dist/config/validate.d.ts +2 -0
- package/dist/config/validate.js +128 -0
- package/dist/config/validate.js.map +1 -0
- package/dist/detect/package-json.d.ts +1 -0
- package/dist/detect/package-json.js +15 -0
- package/dist/detect/package-json.js.map +1 -0
- package/dist/detect/tool.d.ts +10 -0
- package/dist/detect/tool.js +73 -0
- package/dist/detect/tool.js.map +1 -0
- package/dist/eslint-runner.d.ts +7 -0
- package/dist/eslint-runner.js +34 -0
- package/dist/eslint-runner.js.map +1 -0
- package/dist/git/exec.d.ts +8 -0
- package/dist/git/exec.js +48 -0
- package/dist/git/exec.js.map +1 -0
- package/dist/git/resolve-scope.d.ts +15 -0
- package/dist/git/resolve-scope.js +89 -0
- package/dist/git/resolve-scope.js.map +1 -0
- package/dist/git/scope.d.ts +7 -0
- package/dist/git/scope.js +58 -0
- package/dist/git/scope.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/prompts/loader.d.ts +6 -0
- package/dist/prompts/loader.js +27 -0
- package/dist/prompts/loader.js.map +1 -0
- package/dist/prompts/packaged-dir.d.ts +1 -0
- package/dist/prompts/packaged-dir.js +10 -0
- package/dist/prompts/packaged-dir.js.map +1 -0
- package/dist/prompts/registry.d.ts +3 -0
- package/dist/prompts/registry.js +68 -0
- package/dist/prompts/registry.js.map +1 -0
- package/dist/reporter.d.ts +7 -0
- package/dist/reporter.js +114 -0
- package/dist/reporter.js.map +1 -0
- package/dist/rules/registry.d.ts +3 -0
- package/dist/rules/registry.js +37 -0
- package/dist/rules/registry.js.map +1 -0
- package/dist/runner.d.ts +15 -0
- package/dist/runner.js +151 -0
- package/dist/runner.js.map +1 -0
- package/dist/types.d.ts +41 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/wrap/notices.d.ts +13 -0
- package/dist/wrap/notices.js +29 -0
- package/dist/wrap/notices.js.map +1 -0
- package/dist/wrap/resolve.d.ts +5 -0
- package/dist/wrap/resolve.js +9 -0
- package/dist/wrap/resolve.js.map +1 -0
- package/dist/wrap/run.d.ts +15 -0
- package/dist/wrap/run.js +26 -0
- package/dist/wrap/run.js.map +1 -0
- package/dist/wrap/shell.d.ts +14 -0
- package/dist/wrap/shell.js +44 -0
- package/dist/wrap/shell.js.map +1 -0
- package/package.json +46 -0
- package/src/prompts/comment-non-essential.md +7 -0
- package/src/prompts/eslint-boundaries-dependencies.md +9 -0
- package/src/prompts/eslint-complexity.md +9 -0
- package/src/prompts/eslint-eqeqeq.md +3 -0
- package/src/prompts/eslint-fatal.md +7 -0
- package/src/prompts/eslint-max-lines-per-function.md +9 -0
- package/src/prompts/eslint-max-lines.md +9 -0
- package/src/prompts/eslint-max-params.md +9 -0
- package/src/prompts/eslint-no-duplicate-imports.md +1 -0
- package/src/prompts/eslint-no-unused-vars.md +3 -0
- package/src/prompts/eslint-no-var.md +1 -0
- package/src/prompts/eslint-no-warning-comments.md +3 -0
- package/src/prompts/eslint-prefer-const.md +3 -0
- package/src/prompts/eslint-typescript-eslint-no-explicit-any.md +3 -0
- package/src/prompts/eslint-typescript-eslint-no-inferrable-types.md +3 -0
- package/src/prompts/eslint-typescript-eslint-no-non-null-assertion.md +3 -0
- package/src/prompts/jscpd-duplication.md +9 -0
- package/src/prompts/knip-classMembers.md +7 -0
- package/src/prompts/knip-dependencies.md +9 -0
- package/src/prompts/knip-exports.md +9 -0
- package/src/prompts/knip-files.md +9 -0
- package/src/prompts/knip-types.md +9 -0
- package/src/prompts/uncoached.md +3 -0
- package/src/skills/habit-hooks-review/SKILL.md +108 -0
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
export const TOOL_CONFIG_FILENAMES = {
|
|
4
|
+
eslint: ['eslint.config.js', 'eslint.config.mjs', 'eslint.config.ts', 'eslint.config.cjs'],
|
|
5
|
+
knip: ['knip.json', 'knip.jsonc', 'knip.ts'],
|
|
6
|
+
jscpd: ['.jscpd.json', 'jscpd.json'],
|
|
7
|
+
};
|
|
8
|
+
export const TOOL_PACKAGE_JSON_KEYS = {
|
|
9
|
+
eslint: 'eslintConfig',
|
|
10
|
+
knip: 'knip',
|
|
11
|
+
jscpd: 'jscpd',
|
|
12
|
+
};
|
|
13
|
+
function readJson(path) {
|
|
14
|
+
if (!existsSync(path))
|
|
15
|
+
return null;
|
|
16
|
+
try {
|
|
17
|
+
return JSON.parse(readFileSync(path, 'utf8'));
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
function resolveBinFromPackageJson(packageDir, name) {
|
|
24
|
+
const pkg = readJson(join(packageDir, 'package.json'));
|
|
25
|
+
if (pkg === null)
|
|
26
|
+
return null;
|
|
27
|
+
const bin = pkg.bin;
|
|
28
|
+
if (typeof bin === 'string')
|
|
29
|
+
return join(packageDir, bin);
|
|
30
|
+
if (bin !== null && typeof bin === 'object' && !Array.isArray(bin)) {
|
|
31
|
+
const entry = bin[name];
|
|
32
|
+
if (typeof entry === 'string')
|
|
33
|
+
return join(packageDir, entry);
|
|
34
|
+
}
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
function findBinPath(cwd, name) {
|
|
38
|
+
const packageDir = join(cwd, 'node_modules', name);
|
|
39
|
+
const fromPkg = resolveBinFromPackageJson(packageDir, name);
|
|
40
|
+
if (fromPkg !== null && existsSync(fromPkg))
|
|
41
|
+
return fromPkg;
|
|
42
|
+
const fromBin = join(cwd, 'node_modules', '.bin', name);
|
|
43
|
+
if (existsSync(fromBin))
|
|
44
|
+
return fromBin;
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
function filenamesFor(name) {
|
|
48
|
+
return TOOL_CONFIG_FILENAMES[name] ?? [];
|
|
49
|
+
}
|
|
50
|
+
function packageJsonKeyFor(name) {
|
|
51
|
+
return TOOL_PACKAGE_JSON_KEYS[name];
|
|
52
|
+
}
|
|
53
|
+
function findConfigFile(cwd, name) {
|
|
54
|
+
for (const filename of filenamesFor(name)) {
|
|
55
|
+
const candidate = join(cwd, filename);
|
|
56
|
+
if (existsSync(candidate))
|
|
57
|
+
return candidate;
|
|
58
|
+
}
|
|
59
|
+
const packageJsonPath = join(cwd, 'package.json');
|
|
60
|
+
const pkg = readJson(packageJsonPath);
|
|
61
|
+
const key = packageJsonKeyFor(name);
|
|
62
|
+
if (pkg !== null && key !== undefined && pkg[key] !== undefined)
|
|
63
|
+
return packageJsonPath;
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
export function detectTool(cwd, name) {
|
|
67
|
+
const binPath = findBinPath(cwd, name);
|
|
68
|
+
if (binPath === null)
|
|
69
|
+
return null;
|
|
70
|
+
const configPath = findConfigFile(cwd, name);
|
|
71
|
+
return { available: true, binPath, configPath };
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=tool.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool.js","sourceRoot":"","sources":["../../src/detect/tool.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { Check, CheckOutcome, Rule } from './types.js';
|
|
2
|
+
interface EslintFilterContext {
|
|
3
|
+
resolveFilesForRule(_rule: Rule): string[];
|
|
4
|
+
cwd: string;
|
|
5
|
+
}
|
|
6
|
+
export declare function runEslintSource(rules: Rule[], ctx: EslintFilterContext, check: Check): Promise<CheckOutcome>;
|
|
7
|
+
export {};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
function unionFiles(rules, ctx) {
|
|
2
|
+
const seen = new Set();
|
|
3
|
+
for (const rule of rules) {
|
|
4
|
+
for (const file of ctx.resolveFilesForRule(rule))
|
|
5
|
+
seen.add(file);
|
|
6
|
+
}
|
|
7
|
+
return [...seen];
|
|
8
|
+
}
|
|
9
|
+
function ruleAllowsViolation(rule, file, ctx) {
|
|
10
|
+
return ctx.resolveFilesForRule(rule).includes(file);
|
|
11
|
+
}
|
|
12
|
+
function filterEslintViolations(violations, rules, ctx) {
|
|
13
|
+
const byId = new Map(rules.map((r) => [r.id, r]));
|
|
14
|
+
return violations.filter((v) => {
|
|
15
|
+
const rule = byId.get(v.ruleId);
|
|
16
|
+
if (rule === undefined)
|
|
17
|
+
return true;
|
|
18
|
+
return ruleAllowsViolation(rule, v.file, ctx);
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
function normalizeOutcome(result) {
|
|
22
|
+
if (Array.isArray(result))
|
|
23
|
+
return { violations: result };
|
|
24
|
+
return result;
|
|
25
|
+
}
|
|
26
|
+
export async function runEslintSource(rules, ctx, check) {
|
|
27
|
+
const selected = rules.filter((rule) => rule.source === 'eslint');
|
|
28
|
+
const files = unionFiles(selected, ctx);
|
|
29
|
+
if (files.length === 0)
|
|
30
|
+
return { violations: [], stderr: [] };
|
|
31
|
+
const outcome = normalizeOutcome(await check.run(files, selected, ctx.cwd));
|
|
32
|
+
return { violations: filterEslintViolations(outcome.violations, selected, ctx), stderr: outcome.stderr ?? [] };
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=eslint-runner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"eslint-runner.js","sourceRoot":"","sources":["../src/eslint-runner.ts"],"names":[],"mappings":"AAOA,SAAS,UAAU,CAAC,KAAa,EAAE,GAAwB;IACzD,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAC/B,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,KAAK,MAAM,IAAI,IAAI,GAAG,CAAC,mBAAmB,CAAC,IAAI,CAAC;YAAE,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IACnE,CAAC;IACD,OAAO,CAAC,GAAG,IAAI,CAAC,CAAC;AACnB,CAAC;AAED,SAAS,mBAAmB,CAAC,IAAU,EAAE,IAAY,EAAE,GAAwB;IAC7E,OAAO,GAAG,CAAC,mBAAmB,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;AACtD,CAAC;AAED,SAAS,sBAAsB,CAAC,UAAuB,EAAE,KAAa,EAAE,GAAwB;IAC9F,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAU,CAAC,CAAC,CAAC;IAC3D,OAAO,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE;QAC7B,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;QAChC,IAAI,IAAI,KAAK,SAAS;YAAE,OAAO,IAAI,CAAC;QACpC,OAAO,mBAAmB,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;IAChD,CAAC,CAAC,CAAC;AACL,CAAC;AAED,SAAS,gBAAgB,CAAC,MAAkC;IAC1D,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC;QAAE,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,CAAC;IACzD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,KAAa,EACb,GAAwB,EACxB,KAAY;IAEZ,MAAM,QAAQ,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAC;IAClE,MAAM,KAAK,GAAG,UAAU,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;IACxC,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,UAAU,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;IAC9D,MAAM,OAAO,GAAG,gBAAgB,CAAC,MAAM,KAAK,CAAC,GAAG,CAAC,KAAK,EAAE,QAAQ,EAAE,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC;IAC5E,OAAO,EAAE,UAAU,EAAE,sBAAsB,CAAC,OAAO,CAAC,UAAU,EAAE,QAAQ,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,IAAI,EAAE,EAAE,CAAC;AACjH,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export declare class GitError extends Error {
|
|
2
|
+
readonly command: string;
|
|
3
|
+
readonly stderr: string;
|
|
4
|
+
readonly cause?: unknown | undefined;
|
|
5
|
+
constructor(command: string, stderr: string, cause?: unknown | undefined);
|
|
6
|
+
}
|
|
7
|
+
export declare function gitExec(args: string[], cwd: string): string;
|
|
8
|
+
export declare function isGitRepo(cwd: string): boolean;
|
package/dist/git/exec.js
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { execFileSync } from 'node:child_process';
|
|
2
|
+
export class GitError extends Error {
|
|
3
|
+
command;
|
|
4
|
+
stderr;
|
|
5
|
+
cause;
|
|
6
|
+
constructor(command, stderr, cause) {
|
|
7
|
+
super(`git ${command} failed: ${stderr.trim() || (cause instanceof Error ? cause.message : '')}`);
|
|
8
|
+
this.command = command;
|
|
9
|
+
this.stderr = stderr;
|
|
10
|
+
this.cause = cause;
|
|
11
|
+
this.name = 'GitError';
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
function readStderr(err) {
|
|
15
|
+
if (err && typeof err === 'object' && 'stderr' in err) {
|
|
16
|
+
const stderr = err.stderr;
|
|
17
|
+
if (typeof stderr === 'string')
|
|
18
|
+
return stderr;
|
|
19
|
+
if (stderr instanceof Buffer)
|
|
20
|
+
return stderr.toString('utf8');
|
|
21
|
+
}
|
|
22
|
+
return '';
|
|
23
|
+
}
|
|
24
|
+
export function gitExec(args, cwd) {
|
|
25
|
+
try {
|
|
26
|
+
return execFileSync('git', args, {
|
|
27
|
+
cwd,
|
|
28
|
+
encoding: 'utf8',
|
|
29
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
catch (err) {
|
|
33
|
+
throw new GitError(args.join(' '), readStderr(err), err);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
export function isGitRepo(cwd) {
|
|
37
|
+
try {
|
|
38
|
+
execFileSync('git', ['rev-parse', '--is-inside-work-tree'], {
|
|
39
|
+
cwd,
|
|
40
|
+
stdio: ['ignore', 'ignore', 'ignore'],
|
|
41
|
+
});
|
|
42
|
+
return true;
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=exec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"exec.js","sourceRoot":"","sources":["../../src/git/exec.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAElD,MAAM,OAAO,QAAS,SAAQ,KAAK;IAEf;IACA;IACA;IAHlB,YACkB,OAAe,EACf,MAAc,EACd,KAAe;QAE/B,KAAK,CAAC,OAAO,OAAO,YAAY,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;QAJlF,YAAO,GAAP,OAAO,CAAQ;QACf,WAAM,GAAN,MAAM,CAAQ;QACd,UAAK,GAAL,KAAK,CAAU;QAG/B,IAAI,CAAC,IAAI,GAAG,UAAU,CAAC;IACzB,CAAC;CACF;AAED,SAAS,UAAU,CAAC,GAAY;IAC9B,IAAI,GAAG,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,QAAQ,IAAI,GAAG,EAAE,CAAC;QACtD,MAAM,MAAM,GAAI,GAA2B,CAAC,MAAM,CAAC;QACnD,IAAI,OAAO,MAAM,KAAK,QAAQ;YAAE,OAAO,MAAM,CAAC;QAC9C,IAAI,MAAM,YAAY,MAAM;YAAE,OAAO,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IAC/D,CAAC;IACD,OAAO,EAAE,CAAC;AACZ,CAAC;AAED,MAAM,UAAU,OAAO,CAAC,IAAc,EAAE,GAAW;IACjD,IAAI,CAAC;QACH,OAAO,YAAY,CAAC,KAAK,EAAE,IAAI,EAAE;YAC/B,GAAG;YACH,QAAQ,EAAE,MAAM;YAChB,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC;SAClC,CAAC,CAAC;IACL,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,IAAI,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,UAAU,CAAC,GAAG,CAAC,EAAE,GAAG,CAAC,CAAC;IAC3D,CAAC;AACH,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,GAAW;IACnC,IAAI,CAAC;QACH,YAAY,CAAC,KAAK,EAAE,CAAC,WAAW,EAAE,uBAAuB,CAAC,EAAE;YAC1D,GAAG;YACH,KAAK,EAAE,CAAC,QAAQ,EAAE,QAAQ,EAAE,QAAQ,CAAC;SACtC,CAAC,CAAC;QACH,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { ScopeConfig } from '../config/schema.js';
|
|
2
|
+
export type ScopeMode = 'uncommitted' | 'last' | 'branch' | 'since' | 'all';
|
|
3
|
+
export interface ScopeFlags {
|
|
4
|
+
last?: number;
|
|
5
|
+
branch?: string;
|
|
6
|
+
since?: string;
|
|
7
|
+
all?: boolean;
|
|
8
|
+
}
|
|
9
|
+
export interface ResolvedScope {
|
|
10
|
+
mode: ScopeMode;
|
|
11
|
+
changedFiles: Set<string> | null;
|
|
12
|
+
}
|
|
13
|
+
export declare class GitScopeError extends Error {
|
|
14
|
+
}
|
|
15
|
+
export declare function resolveScope(flags: ScopeFlags, scope: ScopeConfig | undefined, cwd: string): ResolvedScope;
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { isGitRepo } from './exec.js';
|
|
2
|
+
import { getChangedVsBranch, getChangedVsCommit, getCurrentBranch, getLastNCommitsChanges, getUncommittedFiles, unionFiles, } from './scope.js';
|
|
3
|
+
const DEFAULT_BRANCH_BASE = 'origin/main';
|
|
4
|
+
const DEFAULT_MAIN_BRANCH = 'main';
|
|
5
|
+
export class GitScopeError extends Error {
|
|
6
|
+
}
|
|
7
|
+
function allScope() {
|
|
8
|
+
return { mode: 'all', changedFiles: null };
|
|
9
|
+
}
|
|
10
|
+
function toSet(files) {
|
|
11
|
+
return new Set(files);
|
|
12
|
+
}
|
|
13
|
+
function requireRepo(cwd, mode) {
|
|
14
|
+
if (!isGitRepo(cwd)) {
|
|
15
|
+
throw new GitScopeError(`--${mode} requires a git repository (run inside a git working tree)`);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
function resolveFromFlags(flags, scope, cwd) {
|
|
19
|
+
if (flags.all === true)
|
|
20
|
+
return allScope();
|
|
21
|
+
if (flags.last !== undefined)
|
|
22
|
+
return resolveLastFlag(flags.last, cwd);
|
|
23
|
+
if (flags.since !== undefined)
|
|
24
|
+
return resolveSinceFlag(flags.since, cwd);
|
|
25
|
+
if (flags.branch !== undefined)
|
|
26
|
+
return resolveBranchFlag(flags.branch, scope, cwd);
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
function resolveLastFlag(n, cwd) {
|
|
30
|
+
requireRepo(cwd, 'last');
|
|
31
|
+
const files = unionFiles(getLastNCommitsChanges(cwd, n), getUncommittedFiles(cwd));
|
|
32
|
+
return { mode: 'last', changedFiles: toSet(files) };
|
|
33
|
+
}
|
|
34
|
+
function resolveSinceFlag(hash, cwd) {
|
|
35
|
+
requireRepo(cwd, 'since');
|
|
36
|
+
const files = unionFiles(getChangedVsCommit(cwd, hash), getUncommittedFiles(cwd));
|
|
37
|
+
return { mode: 'since', changedFiles: toSet(files) };
|
|
38
|
+
}
|
|
39
|
+
function resolveBranchFlag(value, scope, cwd) {
|
|
40
|
+
requireRepo(cwd, 'branch');
|
|
41
|
+
const base = value !== '' ? value : (scope.branchBase ?? DEFAULT_BRANCH_BASE);
|
|
42
|
+
const files = unionFiles(getChangedVsBranch(cwd, base), getUncommittedFiles(cwd));
|
|
43
|
+
return { mode: 'branch', changedFiles: toSet(files) };
|
|
44
|
+
}
|
|
45
|
+
function resolveUncommitted(cwd) {
|
|
46
|
+
if (!isGitRepo(cwd))
|
|
47
|
+
return allScope();
|
|
48
|
+
return { mode: 'uncommitted', changedFiles: toSet(getUncommittedFiles(cwd)) };
|
|
49
|
+
}
|
|
50
|
+
function tryGetCurrentBranch(cwd) {
|
|
51
|
+
try {
|
|
52
|
+
return getCurrentBranch(cwd);
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
function tryGetBranchScope(cwd, branchBase) {
|
|
59
|
+
try {
|
|
60
|
+
const files = unionFiles(getChangedVsBranch(cwd, branchBase), getUncommittedFiles(cwd));
|
|
61
|
+
return { mode: 'branch', changedFiles: toSet(files) };
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
return allScope();
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
function resolveAutoBranch(scope, cwd) {
|
|
68
|
+
if (!isGitRepo(cwd))
|
|
69
|
+
return allScope();
|
|
70
|
+
const current = tryGetCurrentBranch(cwd);
|
|
71
|
+
if (current === null)
|
|
72
|
+
return allScope();
|
|
73
|
+
if (current === (scope.mainBranch ?? DEFAULT_MAIN_BRANCH))
|
|
74
|
+
return null;
|
|
75
|
+
return tryGetBranchScope(cwd, scope.branchBase ?? DEFAULT_BRANCH_BASE);
|
|
76
|
+
}
|
|
77
|
+
function resolveFromConfig(scopeCfg, cwd) {
|
|
78
|
+
if (scopeCfg.onlyChangedFiles === true)
|
|
79
|
+
return resolveUncommitted(cwd);
|
|
80
|
+
if (scopeCfg.autoBranchOffMain === true) {
|
|
81
|
+
return resolveAutoBranch(scopeCfg, cwd) ?? allScope();
|
|
82
|
+
}
|
|
83
|
+
return allScope();
|
|
84
|
+
}
|
|
85
|
+
export function resolveScope(flags, scope, cwd) {
|
|
86
|
+
const scopeCfg = scope ?? {};
|
|
87
|
+
return resolveFromFlags(flags, scopeCfg, cwd) ?? resolveFromConfig(scopeCfg, cwd);
|
|
88
|
+
}
|
|
89
|
+
//# sourceMappingURL=resolve-scope.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolve-scope.js","sourceRoot":"","sources":["../../src/git/resolve-scope.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export declare function getUncommittedFiles(cwd: string): string[];
|
|
2
|
+
export declare function getChangedVsCommit(cwd: string, hash: string): string[];
|
|
3
|
+
export declare function getLastNCommitsChanges(cwd: string, n: number): string[];
|
|
4
|
+
export declare function getMergeBase(cwd: string, base: string): string;
|
|
5
|
+
export declare function getChangedVsBranch(cwd: string, base: string): string[];
|
|
6
|
+
export declare function getCurrentBranch(cwd: string): string;
|
|
7
|
+
export declare function unionFiles(...lists: string[][]): string[];
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { resolve } from 'node:path';
|
|
2
|
+
import { gitExec } from './exec.js';
|
|
3
|
+
function parseLines(stdout) {
|
|
4
|
+
return stdout
|
|
5
|
+
.split('\n')
|
|
6
|
+
.map((line) => line.trim())
|
|
7
|
+
.filter((line) => line.length > 0);
|
|
8
|
+
}
|
|
9
|
+
function toAbsolute(cwd, files) {
|
|
10
|
+
return files.map((file) => resolve(cwd, file));
|
|
11
|
+
}
|
|
12
|
+
function extractTargetFromRenameOrCopy(rest) {
|
|
13
|
+
const arrowIndex = rest.indexOf(' -> ');
|
|
14
|
+
return arrowIndex === -1 ? rest : rest.slice(arrowIndex + 4);
|
|
15
|
+
}
|
|
16
|
+
function parseStatusLine(line) {
|
|
17
|
+
if (line.length < 4)
|
|
18
|
+
return null;
|
|
19
|
+
const status = line.slice(0, 2);
|
|
20
|
+
const rest = line.slice(3);
|
|
21
|
+
if (status === '??')
|
|
22
|
+
return rest;
|
|
23
|
+
return extractTargetFromRenameOrCopy(rest);
|
|
24
|
+
}
|
|
25
|
+
export function getUncommittedFiles(cwd) {
|
|
26
|
+
const stdout = gitExec(['status', '--porcelain'], cwd);
|
|
27
|
+
const files = stdout
|
|
28
|
+
.split('\n')
|
|
29
|
+
.map(parseStatusLine)
|
|
30
|
+
.filter((entry) => entry !== null && entry.length > 0);
|
|
31
|
+
return toAbsolute(cwd, files);
|
|
32
|
+
}
|
|
33
|
+
export function getChangedVsCommit(cwd, hash) {
|
|
34
|
+
const stdout = gitExec(['diff', '--name-only', hash, 'HEAD'], cwd);
|
|
35
|
+
return toAbsolute(cwd, parseLines(stdout));
|
|
36
|
+
}
|
|
37
|
+
export function getLastNCommitsChanges(cwd, n) {
|
|
38
|
+
return getChangedVsCommit(cwd, `HEAD~${String(n)}`);
|
|
39
|
+
}
|
|
40
|
+
export function getMergeBase(cwd, base) {
|
|
41
|
+
return gitExec(['merge-base', 'HEAD', base], cwd).trim();
|
|
42
|
+
}
|
|
43
|
+
export function getChangedVsBranch(cwd, base) {
|
|
44
|
+
const mergeBase = getMergeBase(cwd, base);
|
|
45
|
+
return getChangedVsCommit(cwd, mergeBase);
|
|
46
|
+
}
|
|
47
|
+
export function getCurrentBranch(cwd) {
|
|
48
|
+
return gitExec(['rev-parse', '--abbrev-ref', 'HEAD'], cwd).trim();
|
|
49
|
+
}
|
|
50
|
+
export function unionFiles(...lists) {
|
|
51
|
+
const seen = new Set();
|
|
52
|
+
for (const list of lists) {
|
|
53
|
+
for (const file of list)
|
|
54
|
+
seen.add(file);
|
|
55
|
+
}
|
|
56
|
+
return [...seen];
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=scope.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scope.js","sourceRoot":"","sources":["../../src/git/scope.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export type { HabitHooksConfig, RuleDefinition, RuleOverride, ScopeConfig, } from './config/schema.js';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { resolvePackagedDir } from './packaged-dir.js';
|
|
4
|
+
function slugify(ruleId) {
|
|
5
|
+
return ruleId.replace(/[:/]/g, '-').replace(/@/g, '');
|
|
6
|
+
}
|
|
7
|
+
function tryRead(path) {
|
|
8
|
+
return existsSync(path) ? readFileSync(path, 'utf8').trimEnd() : null;
|
|
9
|
+
}
|
|
10
|
+
function candidatePaths(slug, opts) {
|
|
11
|
+
const packagedDir = opts.packagedDir ?? resolvePackagedDir();
|
|
12
|
+
const paths = [];
|
|
13
|
+
if (opts.overrideDir !== undefined)
|
|
14
|
+
paths.push(join(opts.overrideDir, `${slug}.md`));
|
|
15
|
+
paths.push(join(packagedDir, `${slug}.md`));
|
|
16
|
+
return paths;
|
|
17
|
+
}
|
|
18
|
+
export function loadGuidance(ruleId, opts = {}) {
|
|
19
|
+
const attempts = candidatePaths(slugify(ruleId), opts);
|
|
20
|
+
for (const path of attempts) {
|
|
21
|
+
const text = tryRead(path);
|
|
22
|
+
if (text !== null)
|
|
23
|
+
return text;
|
|
24
|
+
}
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=loader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loader.js","sourceRoot":"","sources":["../../src/prompts/loader.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAEvD,SAAS,OAAO,CAAC,MAAc;IAC7B,OAAO,MAAM,CAAC,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;AACxD,CAAC;AAED,SAAS,OAAO,CAAC,IAAY;IAC3B,OAAO,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;AACxE,CAAC;AAOD,SAAS,cAAc,CAAC,IAAY,EAAE,IAAyB;IAC7D,MAAM,WAAW,GAAG,IAAI,CAAC,WAAW,IAAI,kBAAkB,EAAE,CAAC;IAC7D,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,IAAI,IAAI,CAAC,WAAW,KAAK,SAAS;QAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,GAAG,IAAI,KAAK,CAAC,CAAC,CAAC;IACrF,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,GAAG,IAAI,KAAK,CAAC,CAAC,CAAC;IAC5C,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,MAAc,EAAE,OAA4B,EAAE;IACzE,MAAM,QAAQ,GAAG,cAAc,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,IAAI,CAAC,CAAC;IACvD,KAAK,MAAM,IAAI,IAAI,QAAQ,EAAE,CAAC;QAC5B,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;QAC3B,IAAI,IAAI,KAAK,IAAI;YAAE,OAAO,IAAI,CAAC;IACjC,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function resolvePackagedDir(): string;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { existsSync } from 'node:fs';
|
|
2
|
+
import { fileURLToPath } from 'node:url';
|
|
3
|
+
import { dirname, join } from 'node:path';
|
|
4
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
5
|
+
const PROBE_FILE = 'eslint-max-params.md';
|
|
6
|
+
const SRC_PROMPTS_RELATIVE = join('..', '..', 'src', 'prompts');
|
|
7
|
+
export function resolvePackagedDir() {
|
|
8
|
+
return existsSync(join(here, PROBE_FILE)) ? here : join(here, SRC_PROMPTS_RELATIVE);
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=packaged-dir.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"packaged-dir.js","sourceRoot":"","sources":["../../src/prompts/packaged-dir.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAE1C,MAAM,IAAI,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AACrD,MAAM,UAAU,GAAG,sBAAsB,CAAC;AAC1C,MAAM,oBAAoB,GAAG,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,SAAS,CAAC,CAAC;AAEhE,MAAM,UAAU,kBAAkB;IAChC,OAAO,UAAU,CAAC,IAAI,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,oBAAoB,CAAC,CAAC;AACtF,CAAC"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { join } from 'node:path';
|
|
2
|
+
import { defaultRules } from '../config/defaults.js';
|
|
3
|
+
import { resolvePackagedDir } from './packaged-dir.js';
|
|
4
|
+
const supplementalSeeds = [
|
|
5
|
+
{
|
|
6
|
+
id: 'eslint:fatal',
|
|
7
|
+
title: 'ESLint fatal parse/config error',
|
|
8
|
+
description: 'ESLint could not analyze the file — a parse error, unresolvable config, or a plugin threw.',
|
|
9
|
+
severity: 'enforced',
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
id: 'eslint:boundaries/dependencies',
|
|
13
|
+
title: 'Architectural layering violated',
|
|
14
|
+
description: 'An upper layer reached into a lower one; restore the boundary at the seam.',
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
id: 'knip:files',
|
|
18
|
+
title: 'Unused file',
|
|
19
|
+
description: 'A file no consumer imports — either an entry point knip cannot see, or orphan code.',
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
id: 'knip:exports',
|
|
23
|
+
title: 'Unused export',
|
|
24
|
+
description: 'An export no consumer references — either internal-by-accident or undeclared public surface.',
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
id: 'knip:types',
|
|
28
|
+
title: 'Unused type export',
|
|
29
|
+
description: 'A type export no consumer references — either internal-by-accident or undeclared public surface.',
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
id: 'knip:dependencies',
|
|
33
|
+
title: 'Unused dependency',
|
|
34
|
+
description: 'A package.json dependency with no detected import — uninstall, or teach knip about the config that loads it.',
|
|
35
|
+
},
|
|
36
|
+
];
|
|
37
|
+
function slugify(ruleId) {
|
|
38
|
+
return ruleId.replace(/[:/]/g, '-').replace(/@/g, '');
|
|
39
|
+
}
|
|
40
|
+
function buildPrompt(seed, packagedDir) {
|
|
41
|
+
return {
|
|
42
|
+
id: seed.id,
|
|
43
|
+
title: seed.title,
|
|
44
|
+
description: seed.description,
|
|
45
|
+
severity: seed.severity ?? 'suggested',
|
|
46
|
+
guidancePath: join(packagedDir, `${slugify(seed.id)}.md`),
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
function addSeedToMap(map, seed, packagedDir) {
|
|
50
|
+
map.set(seed.id, buildPrompt(seed, packagedDir));
|
|
51
|
+
}
|
|
52
|
+
function buildRegistry() {
|
|
53
|
+
const packagedDir = resolvePackagedDir();
|
|
54
|
+
const map = new Map();
|
|
55
|
+
for (const rule of defaultRules)
|
|
56
|
+
addSeedToMap(map, rule, packagedDir);
|
|
57
|
+
for (const seed of supplementalSeeds)
|
|
58
|
+
addSeedToMap(map, seed, packagedDir);
|
|
59
|
+
return map;
|
|
60
|
+
}
|
|
61
|
+
const registry = buildRegistry();
|
|
62
|
+
export function lookupPrompt(ruleId) {
|
|
63
|
+
return registry.get(ruleId) ?? null;
|
|
64
|
+
}
|
|
65
|
+
export function listPrompts() {
|
|
66
|
+
return [...registry.values()];
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/prompts/registry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AACrD,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AASvD,MAAM,iBAAiB,GAAe;IACpC;QACE,EAAE,EAAE,cAAc;QAClB,KAAK,EAAE,iCAAiC;QACxC,WAAW,EAAE,4FAA4F;QACzG,QAAQ,EAAE,UAAU;KACrB;IACD;QACE,EAAE,EAAE,gCAAgC;QACpC,KAAK,EAAE,iCAAiC;QACxC,WAAW,EAAE,4EAA4E;KAC1F;IACD;QACE,EAAE,EAAE,YAAY;QAChB,KAAK,EAAE,aAAa;QACpB,WAAW,EAAE,qFAAqF;KACnG;IACD;QACE,EAAE,EAAE,cAAc;QAClB,KAAK,EAAE,eAAe;QACtB,WAAW,EAAE,8FAA8F;KAC5G;IACD;QACE,EAAE,EAAE,YAAY;QAChB,KAAK,EAAE,oBAAoB;QAC3B,WAAW,EAAE,kGAAkG;KAChH;IACD;QACE,EAAE,EAAE,mBAAmB;QACvB,KAAK,EAAE,mBAAmB;QAC1B,WAAW,EAAE,8GAA8G;KAC5H;CACF,CAAC;AAEF,SAAS,OAAO,CAAC,MAAc;IAC7B,OAAO,MAAM,CAAC,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;AACxD,CAAC;AAED,SAAS,WAAW,CAAC,IAAc,EAAE,WAAmB;IACtD,OAAO;QACL,EAAE,EAAE,IAAI,CAAC,EAAE;QACX,KAAK,EAAE,IAAI,CAAC,KAAK;QACjB,WAAW,EAAE,IAAI,CAAC,WAAW;QAC7B,QAAQ,EAAE,IAAI,CAAC,QAAQ,IAAI,WAAW;QACtC,YAAY,EAAE,IAAI,CAAC,WAAW,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,KAAK,CAAC;KAC1D,CAAC;AACJ,CAAC;AAED,SAAS,YAAY,CAAC,GAAgC,EAAE,IAAc,EAAE,WAAmB;IACzF,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,EAAE,WAAW,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC,CAAC;AACnD,CAAC;AAED,SAAS,aAAa;IACpB,MAAM,WAAW,GAAG,kBAAkB,EAAE,CAAC;IACzC,MAAM,GAAG,GAAG,IAAI,GAAG,EAA0B,CAAC;IAC9C,KAAK,MAAM,IAAI,IAAI,YAAY;QAAE,YAAY,CAAC,GAAG,EAAE,IAAI,EAAE,WAAW,CAAC,CAAC;IACtE,KAAK,MAAM,IAAI,IAAI,iBAAiB;QAAE,YAAY,CAAC,GAAG,EAAE,IAAI,EAAE,WAAW,CAAC,CAAC;IAC3E,OAAO,GAAG,CAAC;AACb,CAAC;AAED,MAAM,QAAQ,GAAG,aAAa,EAAE,CAAC;AAEjC,MAAM,UAAU,YAAY,CAAC,MAAc;IACzC,OAAO,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,IAAI,CAAC;AACtC,CAAC;AAED,MAAM,UAAU,WAAW;IACzB,OAAO,CAAC,GAAG,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;AAChC,CAAC"}
|
package/dist/reporter.js
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { loadGuidance } from './prompts/loader.js';
|
|
2
|
+
import { lookupPrompt } from './prompts/registry.js';
|
|
3
|
+
function ruleGuidance(rule) {
|
|
4
|
+
if (rule.guidance !== undefined)
|
|
5
|
+
return rule.guidance;
|
|
6
|
+
return loadGuidance(rule.id);
|
|
7
|
+
}
|
|
8
|
+
function promptToRule(prompt) {
|
|
9
|
+
return {
|
|
10
|
+
id: prompt.id,
|
|
11
|
+
source: 'custom',
|
|
12
|
+
severity: 'suggested',
|
|
13
|
+
changedFilesOnly: false,
|
|
14
|
+
title: prompt.title,
|
|
15
|
+
description: prompt.description,
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
const MAX_PER_GROUP = 10;
|
|
19
|
+
const UNCOACHED_RULE_ID = 'uncoached';
|
|
20
|
+
function groupByRule(violations) {
|
|
21
|
+
const groups = new Map();
|
|
22
|
+
for (const v of violations) {
|
|
23
|
+
const list = groups.get(v.ruleId) ?? [];
|
|
24
|
+
list.push(v);
|
|
25
|
+
groups.set(v.ruleId, list);
|
|
26
|
+
}
|
|
27
|
+
return groups;
|
|
28
|
+
}
|
|
29
|
+
function renderRuleHeader(rule) {
|
|
30
|
+
const guidance = ruleGuidance(rule);
|
|
31
|
+
const tail = guidance === null ? '' : `\n${guidance}`;
|
|
32
|
+
return `❌ ${rule.title}\n${rule.description}${tail}`;
|
|
33
|
+
}
|
|
34
|
+
function formatViolation(v) {
|
|
35
|
+
return `- ${v.file}:${v.line} - ${v.message}`;
|
|
36
|
+
}
|
|
37
|
+
function renderViolationsList(rule, violations) {
|
|
38
|
+
const shown = violations.slice(0, MAX_PER_GROUP);
|
|
39
|
+
const lines = ['Violations:', ...shown.map(formatViolation)];
|
|
40
|
+
const remaining = violations.length - shown.length;
|
|
41
|
+
if (remaining > 0) {
|
|
42
|
+
lines.push(`(${remaining} more ${rule.id} violations)`);
|
|
43
|
+
}
|
|
44
|
+
return lines.join('\n');
|
|
45
|
+
}
|
|
46
|
+
function renderGroup(rule, violations) {
|
|
47
|
+
return `${renderRuleHeader(rule)}\n\n${renderViolationsList(rule, violations)}`;
|
|
48
|
+
}
|
|
49
|
+
function renderHeader(total) {
|
|
50
|
+
if (total === 0) {
|
|
51
|
+
return '✅ Habit Hooks: automated checks passed.\n\nHabit Hooks catches structural smells, not correctness or design. If no reviewer sub-agent has reviewed this change set, run one before declaring done.';
|
|
52
|
+
}
|
|
53
|
+
const noun = total === 1 ? 'violation' : 'violations';
|
|
54
|
+
return `❌ Habit Hooks: ${total} ${noun}`;
|
|
55
|
+
}
|
|
56
|
+
function appendRuleSection(acc, rule, group) {
|
|
57
|
+
if (!group || group.length === 0)
|
|
58
|
+
return;
|
|
59
|
+
acc.sections.push('');
|
|
60
|
+
acc.sections.push(renderGroup(rule, group));
|
|
61
|
+
acc.consumed.add(rule.id);
|
|
62
|
+
if (rule.severity === 'enforced')
|
|
63
|
+
acc.exitCode = 1;
|
|
64
|
+
}
|
|
65
|
+
function appendKnownRules(acc, rules, groups) {
|
|
66
|
+
for (const rule of rules)
|
|
67
|
+
appendRuleSection(acc, rule, groups.get(rule.id));
|
|
68
|
+
}
|
|
69
|
+
function appendPromptOnlyGroups(acc, groups) {
|
|
70
|
+
for (const [ruleId, group] of groups) {
|
|
71
|
+
if (acc.consumed.has(ruleId))
|
|
72
|
+
continue;
|
|
73
|
+
const prompt = lookupPrompt(ruleId);
|
|
74
|
+
if (prompt === null)
|
|
75
|
+
continue;
|
|
76
|
+
appendRuleSection(acc, promptToRule(prompt), group);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
function collectUncoached(acc, groups) {
|
|
80
|
+
const out = [];
|
|
81
|
+
for (const [ruleId, group] of groups) {
|
|
82
|
+
if (acc.consumed.has(ruleId))
|
|
83
|
+
continue;
|
|
84
|
+
out.push(...group);
|
|
85
|
+
}
|
|
86
|
+
return out;
|
|
87
|
+
}
|
|
88
|
+
function formatUncoachedLine(v) {
|
|
89
|
+
return `- ${v.ruleId}: ${v.message} (${v.file}:${v.line})`;
|
|
90
|
+
}
|
|
91
|
+
function renderUncoachedBody(violations) {
|
|
92
|
+
const header = loadGuidance(UNCOACHED_RULE_ID);
|
|
93
|
+
const intro = header === null ? '' : `${header}\n\n`;
|
|
94
|
+
return `${intro}${violations.map(formatUncoachedLine).join('\n')}`;
|
|
95
|
+
}
|
|
96
|
+
function appendUncoachedSection(acc, uncoached) {
|
|
97
|
+
if (uncoached.length === 0)
|
|
98
|
+
return;
|
|
99
|
+
acc.sections.push('');
|
|
100
|
+
acc.sections.push(`⚠️ Uncoached rules\n\n${renderUncoachedBody(uncoached)}`);
|
|
101
|
+
}
|
|
102
|
+
export function report(violations, rules) {
|
|
103
|
+
const groups = groupByRule(violations);
|
|
104
|
+
const acc = {
|
|
105
|
+
sections: [renderHeader(violations.length)],
|
|
106
|
+
exitCode: 0,
|
|
107
|
+
consumed: new Set(),
|
|
108
|
+
};
|
|
109
|
+
appendKnownRules(acc, rules, groups);
|
|
110
|
+
appendPromptOnlyGroups(acc, groups);
|
|
111
|
+
appendUncoachedSection(acc, collectUncoached(acc, groups));
|
|
112
|
+
return { stdout: `${acc.sections.join('\n')}\n`, exitCode: acc.exitCode };
|
|
113
|
+
}
|
|
114
|
+
//# sourceMappingURL=reporter.js.map
|