@nielspeter/eess-ts 0.1.1 → 0.2.1
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 +62 -11
- package/dist/cli/commands/explain.d.ts +2 -0
- package/dist/cli/commands/explain.d.ts.map +1 -1
- package/dist/cli/commands/explain.js +52 -1
- package/dist/cli/commands/explain.js.map +1 -1
- package/dist/cli/commands/init.d.ts +23 -0
- package/dist/cli/commands/init.d.ts.map +1 -0
- package/dist/cli/commands/init.js +387 -0
- package/dist/cli/commands/init.js.map +1 -0
- package/dist/cli/index.d.ts +5 -0
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +29 -5
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/resolve-config.d.ts.map +1 -1
- package/dist/cli/resolve-config.js +11 -1
- package/dist/cli/resolve-config.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/presets/agent-guardrails.d.ts +32 -0
- package/dist/presets/agent-guardrails.d.ts.map +1 -0
- package/dist/presets/agent-guardrails.js +87 -0
- package/dist/presets/agent-guardrails.js.map +1 -0
- package/dist/presets/index.d.ts +4 -0
- package/dist/presets/index.d.ts.map +1 -1
- package/dist/presets/index.js +2 -0
- package/dist/presets/index.js.map +1 -1
- package/dist/presets/recommended.d.ts +33 -0
- package/dist/presets/recommended.d.ts.map +1 -0
- package/dist/presets/recommended.js +81 -0
- package/dist/presets/recommended.js.map +1 -0
- package/dist/tsconfig/strict-family.d.ts +30 -0
- package/dist/tsconfig/strict-family.d.ts.map +1 -0
- package/dist/tsconfig/strict-family.js +45 -0
- package/dist/tsconfig/strict-family.js.map +1 -0
- package/dist/tsconfig/tsconfig-builder.d.ts +32 -0
- package/dist/tsconfig/tsconfig-builder.d.ts.map +1 -0
- package/dist/tsconfig/tsconfig-builder.js +149 -0
- package/dist/tsconfig/tsconfig-builder.js.map +1 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -13,6 +13,33 @@ Inspired by Java's [ArchUnit](https://www.archunit.org/). Powered by [ts-morph](
|
|
|
13
13
|
|
|
14
14
|
[Documentation](https://nielspeter.github.io/ts-archunit/) · [Getting Started](https://nielspeter.github.io/ts-archunit/getting-started) · [What Can It Check?](https://nielspeter.github.io/ts-archunit/what-to-check)
|
|
15
15
|
|
|
16
|
+
## Quickstart — zero to a red gate
|
|
17
|
+
|
|
18
|
+
A green check you've never seen fail proves nothing. This path takes you to a
|
|
19
|
+
**real, correct violation** — timed at seconds on a warm npm cache, minutes
|
|
20
|
+
cold:
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
npm install -D @nielspeter/eess-ts # Node >= 24; your project may be ESM or CJS
|
|
24
|
+
npx eess-ts init # scaffolds arch.rules.ts — editable guardrail rules
|
|
25
|
+
npx eess-ts check # runs them against your source
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
On most existing codebases the floor fires immediately (a silent `catch {}`,
|
|
29
|
+
an empty stub body, an `eval`). If yours comes up clean — good; now **prove
|
|
30
|
+
the gate is real** the same way eess proves its own gates in CI: introduce a
|
|
31
|
+
violation, watch it go red, revert.
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
echo 'export function probe(s: string): unknown { return eval(s) }' >> src/probe.ts
|
|
35
|
+
npx eess-ts check # ✗ red — preset/recommended/no-eval, with why + fix
|
|
36
|
+
rm src/probe.ts # green again — and now you trust the green
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Then wire the rules into your agent's loop — CI annotations, a Claude Code
|
|
40
|
+
hook, and an AGENTS.md block regenerated from the actual rules:
|
|
41
|
+
[agent integration recipes](../../docs/agent-integration.md).
|
|
42
|
+
|
|
16
43
|
## The Problem
|
|
17
44
|
|
|
18
45
|
AI coding agents don't know your architecture. They generate code that compiles, passes type checks, and looks correct in isolation — but violates the structural decisions your team spent months establishing.
|
|
@@ -141,14 +168,37 @@ layeredArchitecture(p, {
|
|
|
141
168
|
})
|
|
142
169
|
```
|
|
143
170
|
|
|
144
|
-
|
|
171
|
+
**Shape presets** (architecture patterns): `layeredArchitecture`, `dataLayerIsolation`, `strictBoundaries`.
|
|
172
|
+
|
|
173
|
+
**Floor presets** (universal safety, scoped by glob):
|
|
174
|
+
|
|
175
|
+
- `recommended(p)` — a thin, universal safety floor (no `eval`, no Function constructor, no silent catches, no empty bodies) that fires ~never on healthy code.
|
|
176
|
+
- `agentGuardrails(p, { src })` — the mistakes AI coding agents make most: generic errors, stub comments, empty bodies, copy-paste, and banned inline calls. Each rule carries `imperative` metadata for `explain --format agent`.
|
|
177
|
+
|
|
178
|
+
Every preset returns `ArchViolation[]` and, by default, throws on any error-severity violation. Pass `{ report: 'return' }` to own emission, `{ report: 'warn' }` to report without failing, or `{ format: 'json' }` for machine-readable output — the caller owns reporting (ADR-008). Per-rule severity is tunable via `overrides`.
|
|
179
|
+
|
|
180
|
+
### Scaffold a starter setup
|
|
181
|
+
|
|
182
|
+
`eess-ts init` writes a working `arch.rules.ts` (the floor preset expanded as editable builders), an `eess-ts.config.ts`, and `arch` / `arch:baseline` npm scripts:
|
|
183
|
+
|
|
184
|
+
```bash
|
|
185
|
+
npx eess-ts init # recommended floor (default)
|
|
186
|
+
npx eess-ts init --preset agent-guardrails
|
|
187
|
+
npx eess-ts init --dry-run # preview; writes nothing
|
|
188
|
+
```
|
|
145
189
|
|
|
146
190
|
## Feed Your Architecture to the Agent
|
|
147
191
|
|
|
148
192
|
The `explain` command dumps all active rules as structured JSON — pipe it into your agent's system prompt so it knows the constraints before writing code:
|
|
149
193
|
|
|
150
194
|
```bash
|
|
151
|
-
npx ts
|
|
195
|
+
npx eess-ts explain arch.rules.ts
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
For an agent's system prompt specifically, `--format agent` emits an imperative, sentinel-wrapped rules block (grouped by rule namespace) built from each rule's `imperative` metadata, with a check-in-the-loop preamble:
|
|
199
|
+
|
|
200
|
+
```bash
|
|
201
|
+
npx eess-ts explain arch.rules.ts --format agent
|
|
152
202
|
```
|
|
153
203
|
|
|
154
204
|
```json
|
|
@@ -300,15 +350,16 @@ smells
|
|
|
300
350
|
|
|
301
351
|
## Entry Points
|
|
302
352
|
|
|
303
|
-
| Function | Operates on | Use case
|
|
304
|
-
| -------------- | ----------------------------------------- |
|
|
305
|
-
| `modules(p)` | Source files | Import/dependency rules
|
|
306
|
-
| `classes(p)` | Class declarations | Inheritance, decorators, methods, body analysis
|
|
307
|
-
| `functions(p)` | Functions, arrow functions, class methods | Naming, parameters, body analysis
|
|
308
|
-
| `types(p)` | Interfaces + type aliases | Property types, type safety
|
|
309
|
-
| `slices(p)` | Groups of files | Cycles, layer ordering
|
|
310
|
-
| `calls(p)` | Call expressions | Framework-agnostic route/handler matching
|
|
311
|
-
| `within(sel)` | Scoped callbacks | Rules inside matched call callbacks
|
|
353
|
+
| Function | Operates on | Use case |
|
|
354
|
+
| -------------- | ----------------------------------------- | ----------------------------------------------------- |
|
|
355
|
+
| `modules(p)` | Source files | Import/dependency rules |
|
|
356
|
+
| `classes(p)` | Class declarations | Inheritance, decorators, methods, body analysis |
|
|
357
|
+
| `functions(p)` | Functions, arrow functions, class methods | Naming, parameters, body analysis |
|
|
358
|
+
| `types(p)` | Interfaces + type aliases | Property types, type safety |
|
|
359
|
+
| `slices(p)` | Groups of files | Cycles, layer ordering |
|
|
360
|
+
| `calls(p)` | Call expressions | Framework-agnostic route/handler matching |
|
|
361
|
+
| `within(sel)` | Scoped callbacks | Rules inside matched call callbacks |
|
|
362
|
+
| `tsconfig(p)` | Resolved compiler options | Assert `strict`, `target`, etc. via `.requires(spec)` |
|
|
312
363
|
|
|
313
364
|
## Compared to Other Tools
|
|
314
365
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"explain.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/explain.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,WAAW;IAC1B,SAAS,EAAE,MAAM,EAAE,CAAA;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAA;
|
|
1
|
+
{"version":3,"file":"explain.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/explain.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,WAAW;IAC1B,SAAS,EAAE,MAAM,EAAE,CAAA;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,uEAAuE;IACvE,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB;AAgBD;;GAEG;AACH,wBAAsB,UAAU,CAAC,IAAI,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAiBjE"}
|
|
@@ -17,13 +17,64 @@ export async function runExplain(args) {
|
|
|
17
17
|
descriptions.push(builder.describeRule());
|
|
18
18
|
}
|
|
19
19
|
}
|
|
20
|
-
if (args.
|
|
20
|
+
if (args.format === 'agent') {
|
|
21
|
+
outputAgent(descriptions);
|
|
22
|
+
}
|
|
23
|
+
else if (args.markdown || args.format === 'markdown') {
|
|
21
24
|
outputMarkdown(descriptions);
|
|
22
25
|
}
|
|
23
26
|
else {
|
|
24
27
|
outputJson(descriptions);
|
|
25
28
|
}
|
|
26
29
|
}
|
|
30
|
+
const AGENT_START = '<!-- eess-ts:start -->';
|
|
31
|
+
const AGENT_END = '<!-- eess-ts:end -->';
|
|
32
|
+
/**
|
|
33
|
+
* Emit imperative markdown for an AI-agent system prompt / project instructions.
|
|
34
|
+
* The block is wrapped in sentinel markers so tooling can replace it idempotently.
|
|
35
|
+
*/
|
|
36
|
+
function outputAgent(descriptions) {
|
|
37
|
+
const lines = [
|
|
38
|
+
AGENT_START,
|
|
39
|
+
'',
|
|
40
|
+
'## Architecture Rules (auto-generated by eess-ts)',
|
|
41
|
+
'',
|
|
42
|
+
'The following rules are enforced by CI. Violations will block your PR.',
|
|
43
|
+
'',
|
|
44
|
+
'**Verify before you continue:** after writing or changing code, run ' +
|
|
45
|
+
'`eess-ts check --format json`, read the `violations` array, and fix ' +
|
|
46
|
+
'each one using its `suggestion`. Do this in your edit loop — do not wait for CI.',
|
|
47
|
+
'',
|
|
48
|
+
];
|
|
49
|
+
if (descriptions.length === 0) {
|
|
50
|
+
lines.push('_No rules found._', '', AGENT_END);
|
|
51
|
+
process.stdout.write(lines.join('\n') + '\n');
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
// Group by the rule id's first namespace segment (e.g. `preset/agent/...` →
|
|
55
|
+
// "preset"), or "General" for rules without an id.
|
|
56
|
+
const groups = new Map();
|
|
57
|
+
for (const d of descriptions) {
|
|
58
|
+
const key = d.id ? (d.id.split('/')[0] ?? 'General') : 'General';
|
|
59
|
+
const group = groups.get(key) ?? [];
|
|
60
|
+
group.push(d);
|
|
61
|
+
groups.set(key, group);
|
|
62
|
+
}
|
|
63
|
+
for (const [group, rules] of groups) {
|
|
64
|
+
lines.push(`### ${titleCase(group)}`, '');
|
|
65
|
+
for (const d of rules) {
|
|
66
|
+
// The imperative is the self-contained rule statement; `because`/`suggestion`
|
|
67
|
+
// live in the check --format json payload, so don't double them up here.
|
|
68
|
+
lines.push(`- ${d.imperative ?? (d.rule || 'Follow the architecture rule.')}`);
|
|
69
|
+
}
|
|
70
|
+
lines.push('');
|
|
71
|
+
}
|
|
72
|
+
lines.push(AGENT_END);
|
|
73
|
+
process.stdout.write(lines.join('\n') + '\n');
|
|
74
|
+
}
|
|
75
|
+
function titleCase(s) {
|
|
76
|
+
return s.length === 0 ? s : (s[0] ?? '').toUpperCase() + s.slice(1);
|
|
77
|
+
}
|
|
27
78
|
function outputJson(descriptions) {
|
|
28
79
|
const output = {
|
|
29
80
|
rules: descriptions,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"explain.js","sourceRoot":"","sources":["../../../src/cli/commands/explain.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAA;
|
|
1
|
+
{"version":3,"file":"explain.js","sourceRoot":"","sources":["../../../src/cli/commands/explain.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export interface InitArgs {
|
|
2
|
+
/** Directory to scaffold into. Defaults to `process.cwd()`. */
|
|
3
|
+
cwd?: string;
|
|
4
|
+
/** Starter preset: `recommended` (default) | `agent-guardrails`. */
|
|
5
|
+
preset?: string;
|
|
6
|
+
/** tsconfig path written into the generated files. Default `tsconfig.json`. */
|
|
7
|
+
tsconfig?: string;
|
|
8
|
+
/** Overwrite existing files instead of refusing. */
|
|
9
|
+
force?: boolean;
|
|
10
|
+
/** Print the plan; write nothing. */
|
|
11
|
+
dryRun?: boolean;
|
|
12
|
+
/** Skip `arch-baseline.json` (and omit the `baseline` config field). */
|
|
13
|
+
noBaseline?: boolean;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Scaffold a working eess-ts setup in `cwd`. Returns the process exit code
|
|
17
|
+
* (0 success, 1 on a recoverable error — bad `--preset`, missing tsconfig, a
|
|
18
|
+
* file conflict without `--force`, or a write failure). All reads, parses, and
|
|
19
|
+
* conflict checks run before any file is written, so a validation failure never
|
|
20
|
+
* leaves a partial scaffold; a raw I/O failure mid-write is caught and reported.
|
|
21
|
+
*/
|
|
22
|
+
export declare function runInit(args: InitArgs): number;
|
|
23
|
+
//# sourceMappingURL=init.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/init.ts"],"names":[],"mappings":"AAkBA,MAAM,WAAW,QAAQ;IACvB,+DAA+D;IAC/D,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,oEAAoE;IACpE,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,+EAA+E;IAC/E,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,oDAAoD;IACpD,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,qCAAqC;IACrC,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,wEAAwE;IACxE,UAAU,CAAC,EAAE,OAAO,CAAA;CACrB;AAUD;;;;;;GAMG;AACH,wBAAgB,OAAO,CAAC,IAAI,EAAE,QAAQ,GAAG,MAAM,CAyE9C"}
|
|
@@ -0,0 +1,387 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
/**
|
|
4
|
+
* "Floor" presets — a universal rule set scoped to your source glob. eess only
|
|
5
|
+
* scaffolds the floor presets: unlike ts-archunit, eess presets return eager
|
|
6
|
+
* `ArchViolation[]` (ADR-008), which a CLI rule file cannot spread. So `init`
|
|
7
|
+
* emits the floor rules **expanded as builders** (CLI-loadable, editable), and
|
|
8
|
+
* leaves the shape presets (layered / boundaries / data-layer) to test-file use.
|
|
9
|
+
*/
|
|
10
|
+
const FLOOR_PRESETS = ['recommended', 'agent-guardrails'];
|
|
11
|
+
const VALID_PRESETS = [...FLOOR_PRESETS];
|
|
12
|
+
const VALID_PRESET_SET = new Set(VALID_PRESETS);
|
|
13
|
+
/**
|
|
14
|
+
* Scaffold a working eess-ts setup in `cwd`. Returns the process exit code
|
|
15
|
+
* (0 success, 1 on a recoverable error — bad `--preset`, missing tsconfig, a
|
|
16
|
+
* file conflict without `--force`, or a write failure). All reads, parses, and
|
|
17
|
+
* conflict checks run before any file is written, so a validation failure never
|
|
18
|
+
* leaves a partial scaffold; a raw I/O failure mid-write is caught and reported.
|
|
19
|
+
*/
|
|
20
|
+
export function runInit(args) {
|
|
21
|
+
const cwd = args.cwd ?? process.cwd();
|
|
22
|
+
const tsconfig = args.tsconfig ?? 'tsconfig.json';
|
|
23
|
+
const preset = args.preset ?? 'recommended';
|
|
24
|
+
if (!isValidPreset(preset)) {
|
|
25
|
+
console.error(`Error: unknown --preset '${preset}'. Valid presets: ${VALID_PRESETS.join(', ')}.`);
|
|
26
|
+
return 1;
|
|
27
|
+
}
|
|
28
|
+
// tsconfig must exist — the generated project() call points at it.
|
|
29
|
+
if (!fs.existsSync(path.join(cwd, tsconfig))) {
|
|
30
|
+
console.error(`Error: eess-ts needs a ${tsconfig} — run \`tsc --init\` first or pass --tsconfig <path>.`);
|
|
31
|
+
return 1;
|
|
32
|
+
}
|
|
33
|
+
const sourceRoot = detectSourceRoot(cwd, tsconfig);
|
|
34
|
+
const writeBaseline = args.noBaseline !== true;
|
|
35
|
+
// Stage the generated files in memory (no writes yet).
|
|
36
|
+
const staged = [
|
|
37
|
+
stage(cwd, 'eess-ts.config.ts', configTemplate(writeBaseline)),
|
|
38
|
+
stage(cwd, 'arch.rules.ts', rulesTemplate(preset, tsconfig, sourceRoot)),
|
|
39
|
+
];
|
|
40
|
+
if (writeBaseline) {
|
|
41
|
+
staged.push(stage(cwd, 'arch-baseline.json', baselineTemplate()));
|
|
42
|
+
}
|
|
43
|
+
// Read + parse package.json up front so a parse failure never crashes mid-write.
|
|
44
|
+
const pkgPlan = planPackageJson(cwd);
|
|
45
|
+
// A dry run writes nothing, so it previews regardless of existing files.
|
|
46
|
+
if (args.dryRun === true) {
|
|
47
|
+
printDryRun(staged, pkgPlan, cwd, sourceRoot);
|
|
48
|
+
return 0;
|
|
49
|
+
}
|
|
50
|
+
// Conflict detection (before any write).
|
|
51
|
+
if (args.force !== true) {
|
|
52
|
+
const conflicts = staged.filter((f) => fs.existsSync(f.path)).map((f) => f.name);
|
|
53
|
+
if (conflicts.length > 0) {
|
|
54
|
+
console.error(`Error: refusing to overwrite existing file(s): ${conflicts.join(', ')}.\n` +
|
|
55
|
+
`Re-run with --force to overwrite or --dry-run to preview.`);
|
|
56
|
+
return 1;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
// Flush. Every read/parse/validate is already done, so a validation failure
|
|
60
|
+
// can't leave a partial scaffold; the `wx` flag (unless --force) also fails
|
|
61
|
+
// fast if a file appeared after the conflict check rather than clobbering it.
|
|
62
|
+
const writeFlag = args.force === true ? 'w' : 'wx';
|
|
63
|
+
try {
|
|
64
|
+
for (const file of staged) {
|
|
65
|
+
fs.writeFileSync(file.path, file.content, { flag: writeFlag });
|
|
66
|
+
}
|
|
67
|
+
if (pkgPlan.action === 'write') {
|
|
68
|
+
fs.writeFileSync(pkgPlan.path, pkgPlan.content);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
catch (err) {
|
|
72
|
+
console.error(`Error: could not write files — ${err instanceof Error ? err.message : String(err)}`);
|
|
73
|
+
return 1;
|
|
74
|
+
}
|
|
75
|
+
printClosing(staged, pkgPlan, cwd, sourceRoot);
|
|
76
|
+
return 0;
|
|
77
|
+
}
|
|
78
|
+
function isValidPreset(value) {
|
|
79
|
+
return VALID_PRESET_SET.has(value);
|
|
80
|
+
}
|
|
81
|
+
function stage(cwd, name, content) {
|
|
82
|
+
return { path: path.join(cwd, name), name, content };
|
|
83
|
+
}
|
|
84
|
+
// --- Templates ---------------------------------------------------------------
|
|
85
|
+
function configTemplate(writeBaseline) {
|
|
86
|
+
const baselineLine = writeBaseline ? `\n baseline: 'arch-baseline.json',` : '';
|
|
87
|
+
return `import { defineConfig } from '@nielspeter/eess-ts'
|
|
88
|
+
|
|
89
|
+
export default defineConfig({
|
|
90
|
+
// The active tsconfig is set in arch.rules.ts via project('...').
|
|
91
|
+
rules: ['arch.rules.ts'],${baselineLine}
|
|
92
|
+
format: 'auto',
|
|
93
|
+
})
|
|
94
|
+
`;
|
|
95
|
+
}
|
|
96
|
+
/** The source-file glob for the floor rules, sourceRoot-aware. */
|
|
97
|
+
function includeGlob(sourceRoot) {
|
|
98
|
+
return `**/${sourceRoot}/**`;
|
|
99
|
+
}
|
|
100
|
+
function rulesTemplate(preset, tsconfig, sourceRoot) {
|
|
101
|
+
return preset === 'recommended'
|
|
102
|
+
? recommendedRulesTemplate(tsconfig, sourceRoot)
|
|
103
|
+
: agentGuardrailsRulesTemplate(tsconfig, sourceRoot);
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Header shared by both floor templates: the CLI runs a rule file's default
|
|
107
|
+
* export (an array of builders). eess presets return violations for a test
|
|
108
|
+
* harness, so `init` expands the floor into builders here — visible and editable.
|
|
109
|
+
*/
|
|
110
|
+
function rulesHeader(tsconfig) {
|
|
111
|
+
return `// Architecture rules for eess-ts. Run with \`eess-ts check\` (or \`npm run arch\`).
|
|
112
|
+
//
|
|
113
|
+
// This file expands the 'floor' preset into individual builders. eess presets
|
|
114
|
+
// (recommended / agentGuardrails) return violations for a test harness; a CLI
|
|
115
|
+
// rule file is an array of builders, so the rules are inlined below — edit,
|
|
116
|
+
// remove, or add freely. \`eess-ts explain --format agent\` turns the imperative
|
|
117
|
+
// metadata into a rules block for an AI agent's system prompt.
|
|
118
|
+
|
|
119
|
+
const p = project('${tsconfig}')`;
|
|
120
|
+
}
|
|
121
|
+
function recommendedRulesTemplate(tsconfig, sourceRoot) {
|
|
122
|
+
const include = includeGlob(sourceRoot);
|
|
123
|
+
return `import { project, functions } from '@nielspeter/eess-ts'
|
|
124
|
+
import { functionNoEval, functionNoFunctionConstructor } from '@nielspeter/eess-ts/rules/security'
|
|
125
|
+
import { functionNoSilentCatch } from '@nielspeter/eess-ts/rules/errors'
|
|
126
|
+
import { noEmptyBodies } from '@nielspeter/eess-ts/rules/hygiene'
|
|
127
|
+
|
|
128
|
+
${rulesHeader(tsconfig)}
|
|
129
|
+
const include = '${include}'
|
|
130
|
+
|
|
131
|
+
// Thin universal safety floor — dangerous regardless of project shape.
|
|
132
|
+
export default [
|
|
133
|
+
functions(p).that().resideInFile(include).should().satisfy(functionNoEval()).rule({
|
|
134
|
+
id: 'preset/recommended/no-eval',
|
|
135
|
+
because: 'eval() executes arbitrary code — a code-injection risk',
|
|
136
|
+
suggestion: 'remove eval(); parse or dispatch explicitly',
|
|
137
|
+
imperative: 'Do NOT call eval()',
|
|
138
|
+
}),
|
|
139
|
+
functions(p).that().resideInFile(include).should().satisfy(functionNoFunctionConstructor()).rule({
|
|
140
|
+
id: 'preset/recommended/no-function-constructor',
|
|
141
|
+
because: 'the Function constructor is eval() in disguise',
|
|
142
|
+
suggestion: 'define the function directly instead of building it from a string',
|
|
143
|
+
imperative: 'Do NOT use the Function constructor',
|
|
144
|
+
}),
|
|
145
|
+
functions(p).that().resideInFile(include).should().satisfy(functionNoSilentCatch()).rule({
|
|
146
|
+
id: 'preset/recommended/no-silent-catch',
|
|
147
|
+
because: 'a silent catch hides failures',
|
|
148
|
+
suggestion: 'handle or rethrow the caught error (reference it in the catch)',
|
|
149
|
+
imperative: 'Do NOT swallow errors in an empty catch',
|
|
150
|
+
}),
|
|
151
|
+
functions(p).that().resideInFile(include).should().satisfy(noEmptyBodies()).rule({
|
|
152
|
+
id: 'preset/recommended/no-empty-bodies',
|
|
153
|
+
because: 'an empty function body is usually an unfinished stub',
|
|
154
|
+
suggestion: 'implement the body or remove the function',
|
|
155
|
+
imperative: 'Do NOT leave a function body empty',
|
|
156
|
+
}),
|
|
157
|
+
|
|
158
|
+
// Add project-specific rules below — builders, no .check().
|
|
159
|
+
// modules(p).that().resideInFolder('${sourceRoot}/core/**')
|
|
160
|
+
// .should().notImportFrom('${sourceRoot}/app/**'),
|
|
161
|
+
]
|
|
162
|
+
`;
|
|
163
|
+
}
|
|
164
|
+
function agentGuardrailsRulesTemplate(tsconfig, sourceRoot) {
|
|
165
|
+
const include = includeGlob(sourceRoot);
|
|
166
|
+
return `import { project, functions, smells } from '@nielspeter/eess-ts'
|
|
167
|
+
import { functionNoGenericErrors } from '@nielspeter/eess-ts/rules/errors'
|
|
168
|
+
import { noStubComments, noEmptyBodies } from '@nielspeter/eess-ts/rules/hygiene'
|
|
169
|
+
|
|
170
|
+
${rulesHeader(tsconfig)}
|
|
171
|
+
const include = '${include}'
|
|
172
|
+
|
|
173
|
+
// Guardrails for the mistakes AI coding agents make most.
|
|
174
|
+
export default [
|
|
175
|
+
functions(p).that().resideInFile(include).should().satisfy(functionNoGenericErrors()).rule({
|
|
176
|
+
id: 'preset/agent/no-generic-errors',
|
|
177
|
+
because: 'a generic Error loses the type/context callers need to handle it',
|
|
178
|
+
suggestion: 'throw a domain-specific error (NotFoundError, ValidationError, …)',
|
|
179
|
+
imperative: 'Do NOT throw new Error() — throw a domain-specific error class',
|
|
180
|
+
}),
|
|
181
|
+
functions(p).that().resideInFile(include).should().satisfy(noStubComments()).rule({
|
|
182
|
+
id: 'preset/agent/no-stubs',
|
|
183
|
+
because: 'stub comments (TODO/FIXME/"not implemented") ship unfinished work',
|
|
184
|
+
suggestion: 'implement the body or remove the stub before committing',
|
|
185
|
+
imperative: 'Do NOT leave stub comments in a function body',
|
|
186
|
+
}),
|
|
187
|
+
functions(p).that().resideInFile(include).should().satisfy(noEmptyBodies()).rule({
|
|
188
|
+
id: 'preset/agent/no-empty-bodies',
|
|
189
|
+
because: 'an empty function body is almost always an unfinished stub',
|
|
190
|
+
suggestion: 'implement the body — every function must have at least one statement',
|
|
191
|
+
imperative: 'Do NOT leave a function body empty',
|
|
192
|
+
}),
|
|
193
|
+
smells.duplicateBodies(p).withMinSimilarity(0.9).rule({
|
|
194
|
+
id: 'preset/agent/no-copy-paste',
|
|
195
|
+
because: 'near-identical bodies are copy-paste instead of reuse',
|
|
196
|
+
suggestion: 'extract the shared logic into one function',
|
|
197
|
+
imperative: 'Do NOT duplicate a function body — extract the shared logic',
|
|
198
|
+
}),
|
|
199
|
+
|
|
200
|
+
// Ban a specific call from being inlined (one rule per API):
|
|
201
|
+
// import { call } from '@nielspeter/eess-ts'
|
|
202
|
+
// functions(p).that().resideInFile(include).should().notContain(call('parseInt')),
|
|
203
|
+
]
|
|
204
|
+
`;
|
|
205
|
+
}
|
|
206
|
+
function baselineTemplate() {
|
|
207
|
+
// Inert seed — an empty baseline filters nothing. Timestamp is fixed so the
|
|
208
|
+
// seed is deterministic; `eess-ts baseline` restamps it when populated.
|
|
209
|
+
return JSON.stringify({ generatedAt: null, count: 0, violations: [] }, null, 2) + '\n';
|
|
210
|
+
}
|
|
211
|
+
// --- Source-root detection ---------------------------------------------------
|
|
212
|
+
/**
|
|
213
|
+
* Best-effort project source root from the tsconfig `include` globs or
|
|
214
|
+
* `compilerOptions.rootDir`. Falls back to `src`. tsconfig files are often JSONC
|
|
215
|
+
* (comments / trailing commas), so parse failures fall back rather than throw.
|
|
216
|
+
*/
|
|
217
|
+
function detectSourceRoot(cwd, tsconfig) {
|
|
218
|
+
const parsed = readJsonc(path.join(cwd, tsconfig));
|
|
219
|
+
if (!isRecord(parsed))
|
|
220
|
+
return 'src';
|
|
221
|
+
const include = parsed['include'];
|
|
222
|
+
if (Array.isArray(include)) {
|
|
223
|
+
for (const entry of include) {
|
|
224
|
+
if (typeof entry !== 'string')
|
|
225
|
+
continue;
|
|
226
|
+
const root = leadingDir(entry);
|
|
227
|
+
if (root !== undefined)
|
|
228
|
+
return root;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
const compilerOptions = parsed['compilerOptions'];
|
|
232
|
+
if (isRecord(compilerOptions) && typeof compilerOptions['rootDir'] === 'string') {
|
|
233
|
+
const root = leadingDir(compilerOptions['rootDir']);
|
|
234
|
+
if (root !== undefined)
|
|
235
|
+
return root;
|
|
236
|
+
}
|
|
237
|
+
return 'src';
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* The first path segment of a glob, if it is a plausible literal source
|
|
241
|
+
* directory. Rejects wildcards, `.`/`..`, and file-like segments (a dotted
|
|
242
|
+
* extension, e.g. `vite.config.ts`) — a common `include` lists config files
|
|
243
|
+
* before the source glob (`["vite.config.ts", "src/**\/*.ts"]`), and picking
|
|
244
|
+
* the file would scaffold a glob that matches nothing (a silently vacuous rule
|
|
245
|
+
* set — exactly what eess exists to prevent). Callers keep scanning past an
|
|
246
|
+
* `undefined` to the next entry.
|
|
247
|
+
*/
|
|
248
|
+
function leadingDir(glob) {
|
|
249
|
+
const first = glob.replace(/^\.\//, '').split('/')[0];
|
|
250
|
+
if (first === undefined ||
|
|
251
|
+
first === '' ||
|
|
252
|
+
first === '.' ||
|
|
253
|
+
first === '..' ||
|
|
254
|
+
first.includes('*') ||
|
|
255
|
+
/\.[a-z0-9]+$/i.test(first) // looks like a file (has an extension), not a dir
|
|
256
|
+
) {
|
|
257
|
+
return undefined;
|
|
258
|
+
}
|
|
259
|
+
return first;
|
|
260
|
+
}
|
|
261
|
+
function readJsonc(filePath) {
|
|
262
|
+
if (!fs.existsSync(filePath))
|
|
263
|
+
return undefined;
|
|
264
|
+
const raw = fs.readFileSync(filePath, 'utf-8');
|
|
265
|
+
// Strip block and line comments and trailing commas, then parse best-effort.
|
|
266
|
+
const stripped = raw
|
|
267
|
+
.replace(/\/\*[\s\S]*?\*\//g, '')
|
|
268
|
+
.replace(/(^|[^:])\/\/.*$/gm, '$1')
|
|
269
|
+
.replace(/,(\s*[}\]])/g, '$1');
|
|
270
|
+
try {
|
|
271
|
+
return JSON.parse(stripped);
|
|
272
|
+
}
|
|
273
|
+
catch (err) {
|
|
274
|
+
// Best-effort JSONC parse: a malformed tsconfig falls back to `src` root
|
|
275
|
+
// detection rather than crashing init. The parse error carries no fix here.
|
|
276
|
+
void err;
|
|
277
|
+
return undefined;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
/**
|
|
281
|
+
* Plan the `package.json` script merge without writing. Reads and parses up
|
|
282
|
+
* front; any problem (missing / unparseable / scripts already present) resolves
|
|
283
|
+
* to a graceful skip rather than a mid-run crash. Preserves the file's indent,
|
|
284
|
+
* EOL, and trailing-newline state so the diff stays to the two added scripts.
|
|
285
|
+
*/
|
|
286
|
+
function planPackageJson(cwd) {
|
|
287
|
+
const pkgPath = path.join(cwd, 'package.json');
|
|
288
|
+
if (!fs.existsSync(pkgPath)) {
|
|
289
|
+
return { action: 'skip', reason: 'no package.json (run `npx eess-ts check` directly)' };
|
|
290
|
+
}
|
|
291
|
+
const raw = fs.readFileSync(pkgPath, 'utf-8');
|
|
292
|
+
let parsed;
|
|
293
|
+
try {
|
|
294
|
+
parsed = JSON.parse(raw);
|
|
295
|
+
}
|
|
296
|
+
catch (err) {
|
|
297
|
+
// The skip reason IS the error handling — we surface it and leave the file
|
|
298
|
+
// untouched rather than risk corrupting an unparseable package.json.
|
|
299
|
+
void err;
|
|
300
|
+
return { action: 'skip', reason: 'package.json is not valid JSON — skipped script entry' };
|
|
301
|
+
}
|
|
302
|
+
if (!isRecord(parsed)) {
|
|
303
|
+
return { action: 'skip', reason: 'package.json is not an object — skipped script entry' };
|
|
304
|
+
}
|
|
305
|
+
const existingScripts = parsed['scripts'];
|
|
306
|
+
const scripts = isRecord(existingScripts) ? { ...existingScripts } : {};
|
|
307
|
+
if (scripts['arch'] !== undefined || scripts['arch:baseline'] !== undefined) {
|
|
308
|
+
return { action: 'skip', reason: 'an `arch` or `arch:baseline` script already exists' };
|
|
309
|
+
}
|
|
310
|
+
scripts['arch'] = 'eess-ts check';
|
|
311
|
+
scripts['arch:baseline'] = 'eess-ts baseline';
|
|
312
|
+
parsed['scripts'] = scripts;
|
|
313
|
+
const indent = detectIndent(raw);
|
|
314
|
+
const eol = raw.includes('\r\n') ? '\r\n' : '\n';
|
|
315
|
+
const trailingNewline = /\n$/.test(raw);
|
|
316
|
+
let content = JSON.stringify(parsed, null, indent);
|
|
317
|
+
if (eol === '\r\n')
|
|
318
|
+
content = content.replace(/\n/g, '\r\n');
|
|
319
|
+
if (trailingNewline)
|
|
320
|
+
content += eol;
|
|
321
|
+
return { action: 'write', path: pkgPath, content };
|
|
322
|
+
}
|
|
323
|
+
/**
|
|
324
|
+
* Detect the indentation (a tab or N spaces) of an existing JSON file. A
|
|
325
|
+
* minified single-line file (no newline) returns `''` so it stays compact
|
|
326
|
+
* rather than being re-expanded to 2-space — keeping the write to the two added
|
|
327
|
+
* scripts. Multi-line files default to 2 spaces when no indent is detected.
|
|
328
|
+
*/
|
|
329
|
+
function detectIndent(raw) {
|
|
330
|
+
if (!raw.includes('\n'))
|
|
331
|
+
return '';
|
|
332
|
+
const match = raw.match(/\n(\t+|[ ]+)"/);
|
|
333
|
+
return match?.[1] ?? ' ';
|
|
334
|
+
}
|
|
335
|
+
// --- Messaging ---------------------------------------------------------------
|
|
336
|
+
function printDryRun(staged, pkg, cwd, sourceRoot) {
|
|
337
|
+
process.stdout.write('Dry run — would create:\n');
|
|
338
|
+
for (const file of staged) {
|
|
339
|
+
const exists = fs.existsSync(file.path) ? ' (exists — needs --force)' : '';
|
|
340
|
+
process.stdout.write(` ${file.name}${exists}\n`);
|
|
341
|
+
}
|
|
342
|
+
if (pkg.action === 'write') {
|
|
343
|
+
process.stdout.write(' package.json (add `arch` + `arch:baseline` scripts)\n');
|
|
344
|
+
}
|
|
345
|
+
else {
|
|
346
|
+
process.stdout.write(` (package.json script entry skipped — ${pkg.reason})\n`);
|
|
347
|
+
}
|
|
348
|
+
if (hasSource(cwd, sourceRoot)) {
|
|
349
|
+
process.stdout.write(`\nNote: this codebase already has source under ${sourceRoot}/ — errors fail the ` +
|
|
350
|
+
`build, warnings don't. Run \`eess-ts baseline\` before gating CI on \`arch\`.\n`);
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
function printClosing(staged, pkg, cwd, sourceRoot) {
|
|
354
|
+
const scriptsAdded = pkg.action === 'write';
|
|
355
|
+
const runCmd = scriptsAdded ? 'npm run arch' : 'npx eess-ts check';
|
|
356
|
+
const baselineCmd = scriptsAdded ? 'npm run arch:baseline' : 'npx eess-ts baseline';
|
|
357
|
+
process.stdout.write(`Created ${String(staged.length)} file(s).\n`);
|
|
358
|
+
if (!scriptsAdded && pkg.action === 'skip') {
|
|
359
|
+
process.stdout.write(`Note: package.json script entry skipped — ${pkg.reason}.\n`);
|
|
360
|
+
}
|
|
361
|
+
if (hasSource(cwd, sourceRoot)) {
|
|
362
|
+
process.stdout.write(`\nThis codebase already has source under ${sourceRoot}/. Errors fail the build; ` +
|
|
363
|
+
`warnings are advisory and never fail CI.\n` +
|
|
364
|
+
`To accept current violations as tracked legacy debt before gating CI, run ` +
|
|
365
|
+
`\`${baselineCmd}\` and commit the result, then: \`${runCmd}\`.\n`);
|
|
366
|
+
}
|
|
367
|
+
else {
|
|
368
|
+
process.stdout.write(`\nNext: \`${runCmd}\`.\n`);
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
/** Does the source root directory exist and contain at least one entry? */
|
|
372
|
+
function hasSource(cwd, sourceRoot) {
|
|
373
|
+
const dir = path.join(cwd, sourceRoot);
|
|
374
|
+
try {
|
|
375
|
+
return fs.statSync(dir).isDirectory() && fs.readdirSync(dir).length > 0;
|
|
376
|
+
}
|
|
377
|
+
catch (err) {
|
|
378
|
+
// A missing/unreadable source dir simply means "no existing source" — the
|
|
379
|
+
// greenfield closing message applies. Nothing to report.
|
|
380
|
+
void err;
|
|
381
|
+
return false;
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
function isRecord(value) {
|
|
385
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
386
|
+
}
|
|
387
|
+
//# sourceMappingURL=init.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"init.js","sourceRoot":"","sources":["../../../src/cli/commands/init.ts"],"names":[],"mappings":"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"}
|
package/dist/cli/index.d.ts
CHANGED
package/dist/cli/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"AAoDA,UAAU,UAAU;IAClB,MAAM,EAAE;QACN,QAAQ,CAAC,EAAE,MAAM,CAAA;QACjB,MAAM,CAAC,EAAE,MAAM,CAAA;QACf,OAAO,CAAC,EAAE,OAAO,CAAA;QACjB,IAAI,CAAC,EAAE,MAAM,CAAA;QACb,MAAM,CAAC,EAAE,MAAM,CAAA;QACf,MAAM,CAAC,EAAE,MAAM,CAAA;QACf,IAAI,CAAC,EAAE,OAAO,CAAA;QACd,OAAO,CAAC,EAAE,OAAO,CAAA;QACjB,KAAK,CAAC,EAAE,OAAO,CAAA;QACf,QAAQ,CAAC,EAAE,OAAO,CAAA;QAClB,GAAG,CAAC,EAAE,OAAO,CAAA;QACb,KAAK,CAAC,EAAE,OAAO,CAAA;QACf,MAAM,CAAC,EAAE,MAAM,CAAA;QACf,QAAQ,CAAC,EAAE,MAAM,CAAA;QACjB,KAAK,CAAC,EAAE,OAAO,CAAA;QACf,SAAS,CAAC,EAAE,OAAO,CAAA;QACnB,aAAa,CAAC,EAAE,OAAO,CAAA;KACxB,CAAA;IACD,WAAW,EAAE,MAAM,EAAE,CAAA;CACtB;AAGD,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,UAAU,CAyBvD;AA2ED,wBAAsB,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAgEvD"}
|