@miroshnik/elves 2026.1026.0 → 2026.1026.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.
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { readdirSync } from "node:fs";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { pathToFileURL } from "node:url";
|
|
5
|
-
var FRAGMENT =
|
|
5
|
+
var FRAGMENT = /^(?:[^.]+\.)?eslint\.(?:ts|mts|mjs|js)$/;
|
|
6
6
|
var SKIP = new Set(["node_modules", ".git"]);
|
|
7
7
|
var SKIP_REL = new Set(["tests/lib"]);
|
|
8
8
|
function eslintFragments(root, dirs = ["tests"]) {
|
|
@@ -19,7 +19,7 @@ function eslintFragments(root, dirs = ["tests"]) {
|
|
|
19
19
|
if (e.isDirectory()) {
|
|
20
20
|
if (!SKIP.has(e.name) && !SKIP_REL.has(path.relative(root, full).split(path.sep).join("/")))
|
|
21
21
|
walk(full);
|
|
22
|
-
} else if (FRAGMENT.
|
|
22
|
+
} else if (FRAGMENT.test(e.name))
|
|
23
23
|
out.push(full);
|
|
24
24
|
}
|
|
25
25
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@miroshnik/elves",
|
|
3
|
-
"version": "2026.1026.
|
|
3
|
+
"version": "2026.1026.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Elves — a development flow with AI agents: shared rules and skills for any agent; install — bunx @miroshnik/elves install -g; the installer and skill scripts — TypeScript under Bun, tests — bun test in tests/",
|
|
6
6
|
"repository": {
|
|
@@ -72,5 +72,5 @@
|
|
|
72
72
|
"eslint-plugin-boundaries": "^7.2.0",
|
|
73
73
|
"knip": "^6.41.0"
|
|
74
74
|
},
|
|
75
|
-
"gitHead": "
|
|
75
|
+
"gitHead": "6c5389f526d10b2b11b78bd6a8bdb6d31a6821a9"
|
|
76
76
|
}
|
package/skills/spec/SKILL.md
CHANGED
|
@@ -183,7 +183,7 @@ each one — `checks.md` next to this file:
|
|
|
183
183
|
|
|
184
184
|
- a convention on every item of a registry from the code — `invariant`
|
|
185
185
|
(`standards/<name>/`);
|
|
186
|
-
- a lint rule — `examples`, `eslintLinter`,
|
|
186
|
+
- a lint rule — `examples`, `eslintLinter`, an `eslint.ts` or `<section>.eslint.ts` fragment
|
|
187
187
|
collected by `collectEslint`;
|
|
188
188
|
- out of scope and exceptions — `fileOf`, marks `spec-outside(<decision>)` /
|
|
189
189
|
`spec-exception(<decision>) #N`, `exceptionsIn()`, `lintExceptions`;
|
package/skills/spec/canon.md
CHANGED
|
@@ -44,8 +44,8 @@ no decision claims is dead and is deleted.
|
|
|
44
44
|
- a topic of one section has the same anatomy — `<topic>.md` and a section
|
|
45
45
|
with a name of its own; a test file named after its folder
|
|
46
46
|
(`<name>/<name>.test.ts`) is a decision not regrouped yet;
|
|
47
|
-
- the mechanism — in the same folder: an
|
|
48
|
-
`rule.yml`. The project's tool configs (`eslint.config.*`, `sgconfig.yml`)
|
|
47
|
+
- the mechanism — in the same folder: an ESLint fragment (the topic's
|
|
48
|
+
`eslint.ts`, a section's `<section>.eslint.ts`), an ast-grep `rule.yml`. The project's tool configs (`eslint.config.*`, `sgconfig.yml`)
|
|
49
49
|
only collect them from the tree (the harness's `collectEslint`) — a rule
|
|
50
50
|
has one home;
|
|
51
51
|
- `exceptions/<section>/` — exceptions for elements without a file of their
|
package/skills/spec/checks.md
CHANGED
|
@@ -7,8 +7,8 @@ Which check for what:
|
|
|
7
7
|
| What to check | With what | Where in `tests/` |
|
|
8
8
|
|---|---|---|
|
|
9
9
|
| A convention on every item of a registry from the code ("every mutation writes an audit record") | `invariant` | `standards/<name>/` |
|
|
10
|
-
| A lint rule: «not allowed», «allowed», «outside scope» | `examples`, `eslintLinter` | `standards/<
|
|
11
|
-
| An ESLint rule in a standard's folder | `collectEslint` in `eslint.config.*` | the tree's `eslint.ts` fragments |
|
|
10
|
+
| A lint rule: «not allowed», «allowed», «outside scope» | `examples`, `eslintLinter` | `standards/<topic>/` + an ESLint fragment (`eslint.ts`, `<section>.eslint.ts`) |
|
|
11
|
+
| An ESLint rule in a standard's folder | `collectEslint` in `eslint.config.*` | the tree's `eslint.ts` and `<section>.eslint.ts` fragments |
|
|
12
12
|
| Out of scope and an item's exception with its own file | `fileOf` in `invariant`; a mark `spec-outside(<decision>)` / `spec-exception(<decision>) #N` in the file (`marksIn`), in JSON — the value of the `"//"` key | the item's file (a route, a script, a migration, a workspace package's `package.json`) |
|
|
13
13
|
| Exceptions with a ratchet | `exceptionsIn()` → `exceptions` in `invariant`; a disable `-- #N reason` + `lintExceptions` | the decision folder's `exceptions/<item>.json`; migration — `spec-exceptions` |
|
|
14
14
|
| Architecture model: module boundaries, directories, packages | `boundariesConfig`, `architecture` | `architecture/model.ts`, `architecture/<name>/` |
|
|
@@ -142,7 +142,8 @@ Biome will be added when a project needs them (the core doesn't depend on the
|
|
|
142
142
|
linter, `Linter` is an interface with one required method, `lint`).
|
|
143
143
|
|
|
144
144
|
**A rule lives in its standard's folder.** A flat config fragment is
|
|
145
|
-
`eslint.ts`
|
|
145
|
+
the topic's `eslint.ts` or a section's `<section>.eslint.ts` — each standard
|
|
146
|
+
of a topic keeps its own — next to the test (`export default [{ files: ["src/domain/**"],
|
|
146
147
|
rules: {…} }]`, paths from the project root); the project's `eslint.config.*`
|
|
147
148
|
only collects the tree's fragments — `lint` and the editor see the rule as
|
|
148
149
|
usual:
|
|
@@ -168,7 +169,9 @@ export default restrict({ id: "no-alert", selectors: ["CallExpression[callee.nam
|
|
|
168
169
|
|
|
169
170
|
A fragment is `eslint.ts`, `eslint.mts` (ESM even in a project without
|
|
170
171
|
`"type": "module"`, without the `MODULE_TYPELESS_PACKAGE_JSON` warning),
|
|
171
|
-
`eslint.mjs` or `eslint.js
|
|
172
|
+
`eslint.mjs` or `eslint.js`, a section's — the same under its prefix
|
|
173
|
+
(`<section>.eslint.mts`); a knip entry for the fragments —
|
|
174
|
+
`tests/**/*eslint.{ts,mts}`; `tests/lib` holds helpers, not fragments.
|
|
172
175
|
|
|
173
176
|
**Exceptions are explicit, have a task, and go away when no longer needed.**
|
|
174
177
|
|
package/skills/spec/migration.md
CHANGED
|
@@ -143,7 +143,7 @@ a flat decision as a topic of one section. One task, one PR, in this order:
|
|
|
143
143
|
`<topic>/<name>.md` (the other `<name>.*` files alongside), another test
|
|
144
144
|
file of the folder `<name>/<file>.test.ts` → `<topic>/<name>.<file>.test.ts`
|
|
145
145
|
(by its own name it would be a section of its own), a helper file under the
|
|
146
|
-
section's prefix (`<name>.preload.ts`), `<name>/exceptions/*.json` → `<topic>/exceptions/<name>/`, a package
|
|
146
|
+
section's prefix (`<name>.preload.ts`, the ESLint fragment `<name>/eslint.ts` → `<topic>/<name>.eslint.ts`), `<name>/exceptions/*.json` → `<topic>/exceptions/<name>/`, a package
|
|
147
147
|
subfolder `<name>/<package>/` → `<topic>/<package>/`. The moves go in a
|
|
148
148
|
commit of their own, without content edits, so history follows the files
|
|
149
149
|
and `pr labels` reads them as mechanical. Each topic gets `<topic>.md`: its
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* eslint-config — assembling ESLint rules from the spec tree: a standard's rule lives as
|
|
3
|
-
*
|
|
4
|
-
*
|
|
2
|
+
* eslint-config — assembling ESLint rules from the spec tree: a standard's rule lives as a fragment (default — a flat
|
|
3
|
+
* config object or array) in its topic folder, next to the examples and the reason — the topic's `eslint.ts` or a
|
|
4
|
+
* section's `<section>.eslint.ts`, so each standard of a topic keeps its own —, and the project's `eslint.config.*`
|
|
5
|
+
* only collects the fragments — `lint` and the editor see the rule as usual:
|
|
5
6
|
*
|
|
6
7
|
* import { collectEslint } from "@miroshnik/elves/spec/eslint-config";
|
|
7
8
|
* export default [...base, ...(await collectEslint(import.meta.dirname))];
|
|
@@ -20,12 +21,13 @@ import { readdirSync } from "node:fs";
|
|
|
20
21
|
import path from "node:path";
|
|
21
22
|
import { pathToFileURL } from "node:url";
|
|
22
23
|
|
|
23
|
-
|
|
24
|
+
/** A topic's fragment `eslint.ts` or a section's `<section>.eslint.ts` (`.mts`, `.mjs`, `.js` too). */
|
|
25
|
+
const FRAGMENT = /^(?:[^.]+\.)?eslint\.(?:ts|mts|mjs|js)$/;
|
|
24
26
|
const SKIP = new Set(["node_modules", ".git"]);
|
|
25
27
|
// test helpers, not the spec: `spec-doc` skips them too
|
|
26
28
|
const SKIP_REL = new Set(["tests/lib"]);
|
|
27
29
|
|
|
28
|
-
/** Paths of `eslint.ts` fragments in the directories (by default `tests/`), in order. */
|
|
30
|
+
/** Paths of the topics' `eslint.ts` and the sections' `<section>.eslint.ts` fragments in the directories (by default `tests/`), in order. */
|
|
29
31
|
export function eslintFragments(root: string, dirs: string[] = ["tests"]): string[] {
|
|
30
32
|
const out: string[] = [];
|
|
31
33
|
const walk = (dir: string): void => {
|
|
@@ -39,7 +41,7 @@ export function eslintFragments(root: string, dirs: string[] = ["tests"]): strin
|
|
|
39
41
|
const full = path.join(dir, e.name);
|
|
40
42
|
if (e.isDirectory()) {
|
|
41
43
|
if (!SKIP.has(e.name) && !SKIP_REL.has(path.relative(root, full).split(path.sep).join("/"))) walk(full);
|
|
42
|
-
} else if (FRAGMENT.
|
|
44
|
+
} else if (FRAGMENT.test(e.name)) out.push(full);
|
|
43
45
|
}
|
|
44
46
|
};
|
|
45
47
|
for (const d of dirs) walk(path.join(root, d));
|
|
@@ -67,7 +69,7 @@ const SPEC_PLUGIN: { meta: { name: string }; rules: Record<string, object> } = {
|
|
|
67
69
|
|
|
68
70
|
/**
|
|
69
71
|
* A standard's ban by AST selectors (as in `no-restricted-syntax`) — as its own rule `spec/<id>` in the shared `spec`
|
|
70
|
-
* plugin: bans of different standards on the same files all apply. A standard's `eslint.ts`
|
|
72
|
+
* plugin: bans of different standards on the same files all apply. A standard's fragment (`eslint.ts`, `<section>.eslint.ts`):
|
|
71
73
|
*
|
|
72
74
|
* export default restrict({ id: "no-alert", selectors: ["CallExpression[callee.name='alert']"], message: "…", files: ["src/**"] });
|
|
73
75
|
*/
|