@konvert7/klint 0.1.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.
@@ -0,0 +1,112 @@
1
+ ---
2
+ name: klint-rules
3
+ description: Add, modify, or explain klint architecture rules for this repo. Use when asked to enforce a new structural constraint, adjust rule scope, or explain why a violation fired.
4
+ argument-hint: <constraint to enforce>
5
+ ---
6
+
7
+ ## Workflow
8
+
9
+ 1. **Understand the constraint** — if scope is ambiguous, ask: which files/layers are involved? should it block or warn? are there legitimate exceptions?
10
+
11
+ 2. **Grep first** — verify the pattern exists and count current occurrences before touching `klint.yaml`:
12
+ ```sh
13
+ grep -rn "the-pattern" src/ --include="*.ts"
14
+ ```
15
+
16
+ 3. **Choose the right primitive:**
17
+
18
+ | The constraint is... | Primitive |
19
+ |---|---|
20
+ | Layer X must not import from layer Y | `arch.imports` + `deny` |
21
+ | Layer X may only import from Y and Z | `arch.imports` + `allow` |
22
+ | Pattern P must only appear in one designated file | `arch.singleton` |
23
+ | Pattern P must never appear inside a scoped layer | `arch.forbidden` |
24
+
25
+ 4. **Read `klint.yaml`** — check existing `arch.layers` and rules before adding anything. Add a new named layer to `arch.layers` if the file group doesn't exist yet.
26
+
27
+ 5. **Write the stanza** in `klint.yaml` under the correct `arch:` section.
28
+
29
+ 6. **Verify zero new violations on the current codebase:**
30
+ ```sh
31
+ bun klint/cli.ts # in-repo
32
+ npx klint # after npm install
33
+ ```
34
+ If new violations appear on existing code, fix them first or adjust the rule scope — then land the rule.
35
+
36
+ 7. **Write a break test** — prove the rule fires on a deliberate violation, and doesn't fire on clean code. Run the test suite to confirm.
37
+
38
+ ## Primitive reference
39
+
40
+ ### Layers
41
+ ```yaml
42
+ arch:
43
+ layers:
44
+ core: ["src/lib/**", "src/hooks/**"]
45
+ ui: ["src/components/**"]
46
+ targets: ["src/targets/**"]
47
+ ```
48
+
49
+ ### Import boundaries
50
+ ```yaml
51
+ arch:
52
+ imports:
53
+ # deny: block imports from one layer into another
54
+ - from: core
55
+ deny: targets
56
+ message: "Core must not depend on agent-specific code"
57
+ severity: error # optional — default is error; use warn to record without blocking
58
+
59
+ # allow: whitelist — anything not listed is denied (npm + node: builtins always pass)
60
+ - from: ["src/dao/**"]
61
+ allow: ["src/dao/**", "src/prisma/**"]
62
+ message: "DAO may only import from dao or prisma"
63
+
64
+ # type-only: allow — import type {} is permitted even when value imports are denied
65
+ - from: core
66
+ deny: targets
67
+ type-only: allow
68
+ message: "Core must not depend on agent-specific code"
69
+ ```
70
+
71
+ ### Singleton — one designated location
72
+ ```yaml
73
+ arch:
74
+ singleton:
75
+ - pattern: "process.env.API_KEY"
76
+ only: "src/lib/auth.ts"
77
+ in: ["src/**"] # optional: limit scan scope (default: all files)
78
+ message: "Use the auth module instead of reading API_KEY directly"
79
+ ```
80
+
81
+ `pattern` is a **literal string match**. It fires if the pattern appears in any in-scope file except the `only` file.
82
+
83
+ ### Forbidden — banned pattern in scope
84
+ ```yaml
85
+ arch:
86
+ forbidden:
87
+ - pattern: "console.log("
88
+ in: ["src/lib/**"]
89
+ message: "Use the logger — console.log leaks into the agent event stream"
90
+ ```
91
+
92
+ ## Pitfalls
93
+
94
+ - `pattern` in `singleton` and `forbidden` is a **literal string** — will not catch `process.env["KEY"]` (bracket notation). Grep for both forms if both are possible.
95
+ - `only` in `singleton` is relative to the project root — use forward slashes on all platforms.
96
+ - `severity: warn` records the violation but does not block the session. Use `error` (the default) to block.
97
+ - Path aliases (`@/*`) resolve via `tsconfig.json` `compilerOptions.paths`, including `extends` chains.
98
+ - Adding a rule that fires on existing code is not a blocker — fix the violations first, then add the rule.
99
+
100
+ ## Output format
101
+
102
+ After adding or modifying a rule, report:
103
+ - The YAML stanza added
104
+ - The grep evidence that confirmed the pattern is real
105
+ - The test written and its result
106
+ - The output of `bun run klint` (or `npx klint`) confirming zero new violations
107
+
108
+ ## Do NOT use
109
+
110
+ - To fix TypeScript type errors, lint issues, or Biome violations — those are separate tools
111
+ - To enforce naming conventions (hooks must start with `use`, components must be PascalCase) — use ESLint with the appropriate plugin
112
+ - To write custom TypeScript AST rules in `klint.rules.ts` — that is a separate, code-level workflow
@@ -0,0 +1,41 @@
1
+ #!/usr/bin/env bun
2
+ import { writeFileSync } from "node:fs";
3
+ import { resolve } from "node:path";
4
+ import { stringify as toYaml } from "yaml";
5
+ import { KlintConfigSchema } from "../core/config.schema";
6
+ import { BUILT_IN_PLUGINS } from "../plugins/index";
7
+ import { BUILT_IN_RULES } from "../rules/index";
8
+
9
+ const allKnownRuleNames = [
10
+ ...Object.keys(BUILT_IN_RULES),
11
+ ...Object.values(BUILT_IN_PLUGINS).flatMap((p) => Object.keys(p.rules)),
12
+ ];
13
+
14
+ const raw = KlintConfigSchema.toJSONSchema() as Record<string, unknown>;
15
+
16
+ // VS Code reads `properties` for key autocomplete, not `propertyNames`.
17
+ // Expand known rule names into explicit property entries so editors suggest them.
18
+ // `additionalProperties` stays to allow custom rule names without validation errors.
19
+ const rulesSchema = (raw.properties as Record<string, Record<string, unknown>>).rules;
20
+ const valueSchema = rulesSchema.additionalProperties;
21
+ rulesSchema.properties = Object.fromEntries(
22
+ allKnownRuleNames.map((name) => [name, valueSchema])
23
+ );
24
+ delete rulesSchema.propertyNames;
25
+
26
+ // Remove the 2020-12 $schema key; set Draft-07 for broad editor compatibility
27
+ delete raw.$schema;
28
+
29
+ const schema = {
30
+ $schema: "http://json-schema.org/draft-07/schema#",
31
+ $id: "https://klint.dev/schema.json",
32
+ ...raw,
33
+ };
34
+
35
+ const jsonOutPath = resolve(import.meta.dir, "../../klint.schema.json");
36
+ writeFileSync(jsonOutPath, `${JSON.stringify(schema, null, 2)}\n`);
37
+ process.stdout.write(`Generated ${jsonOutPath}\n`);
38
+
39
+ const yamlOutPath = resolve(import.meta.dir, "../../klint.schema.yaml");
40
+ writeFileSync(yamlOutPath, toYaml(schema, { lineWidth: 120 }));
41
+ process.stdout.write(`Generated ${yamlOutPath}\n`);