@rootreeweb/linting 5.2.0 → 5.4.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/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # @rootreeweb/linting
2
2
 
3
+ ## 5.4.0
4
+
5
+ ### Minor Changes
6
+
7
+ - Add `presetNetSuite`, and ship agent skills (format/lint and a SOLID audit) for `skills experimental_sync`.
8
+
9
+ ## 5.3.0
10
+
11
+ ### Minor Changes
12
+
13
+ - Add a preset for packages run directly by node.
14
+
3
15
  ## 5.2.0
4
16
 
5
17
  ### Minor Changes
package/README.md CHANGED
@@ -29,6 +29,7 @@ Available presets:
29
29
  - presetBase
30
30
  - presetReact
31
31
  - presetNext
32
+ - presetNetSuite (SuiteScript globals and `@N*` JSDoc tags)
32
33
 
33
34
  ```javascript
34
35
  import { presetBase } from "@rootreeweb/linting";
@@ -110,6 +111,20 @@ import { composeConfig, presetNext, turbo } from "@rootreeweb/linting";
110
111
  export default composeConfig(presetNext, turbo);
111
112
  ```
112
113
 
114
+ ## Agent Skills
115
+
116
+ This package ships agent skills (`format-and-lint-js` and `solid-audit`, plus `domain-modeling`, `grill-me`, `improve-codebase-architecture` and `dry-refactoring` from upstream) using the [`skills`](https://www.npmjs.com/package/skills) CLI's npm sync.
117
+
118
+ ```bash
119
+ yarn add -D skills
120
+ npm pkg set scripts.prepare="husky && skills experimental_sync -y"
121
+ yarn skills experimental_sync -y
122
+ ```
123
+
124
+ This links the skills into `.agents/skills` (and `.claude/skills` for Claude Code).
125
+ `dry-refactoring` expects [`jscpd`](https://www.npmjs.com/package/jscpd) to be installed.
126
+ `experimental_sync` is experimental in the `skills` CLI, so its flags may change.
127
+
113
128
  ## Stylelint
114
129
 
115
130
  `stylelint.config.js` example:
@@ -3,6 +3,7 @@ import { type TailwindOptions } from "./tailwind.js";
3
3
  export * from "./astro.js";
4
4
  export * from "./basic.js";
5
5
  export * from "./jsdoc.js";
6
+ export * from "./netsuite.js";
6
7
  export * from "./next.js";
7
8
  export * from "./packageJson.js";
8
9
  export * from "./react.js";
@@ -10,7 +11,9 @@ export * from "./tailwind.js";
10
11
  export * from "./turbo.js";
11
12
  export { composeConfig } from "./utils.js";
12
13
  export declare const presetBase: Linter.Config[];
13
- export declare const presetReactLite: Linter.Config[];
14
+ export declare const presetNode: Linter.Config<import("@eslint/core").RulesConfig>[];
15
+ export declare const presetNetSuite: Linter.Config<import("@eslint/core").RulesConfig>[];
16
+ export declare const presetReactLite: Linter.Config<import("@eslint/core").RulesConfig>[];
14
17
  export declare const getPresetReact: (options: TailwindOptions) => Linter.Config[];
15
18
  export declare const getPresetNext: (options: TailwindOptions) => Linter.Config[];
16
19
  export declare const getPresetAstro: (options: TailwindOptions) => Linter.Config[];
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/eslint/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAOrC,OAAO,EAAe,KAAK,eAAe,EAAE,MAAM,eAAe,CAAC;AAGlE,cAAc,YAAY,CAAC;AAC3B,cAAc,YAAY,CAAC;AAC3B,cAAc,YAAY,CAAC;AAC3B,cAAc,WAAW,CAAC;AAC1B,cAAc,kBAAkB,CAAC;AACjC,cAAc,YAAY,CAAC;AAC3B,cAAc,eAAe,CAAC;AAC9B,cAAc,YAAY,CAAC;AAC3B,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAE3C,eAAO,MAAM,UAAU,EAAE,MAAM,CAAC,MAAM,EAAyB,CAAC;AAChE,eAAO,MAAM,eAAe,EAAE,MAAM,CAAC,MAAM,EAG1C,CAAC;AACF,eAAO,MAAM,cAAc,YAAa,eAAe,KAAG,MAAM,CAAC,MAAM,EAChB,CAAC;AACxD,eAAO,MAAM,aAAa,YAAa,eAAe,KAAG,MAAM,CAAC,MAAM,EACzB,CAAC;AAC9C,eAAO,MAAM,cAAc,YAAa,eAAe,KAAG,MAAM,CAAC,MAAM,EACzB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/eslint/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AASrC,OAAO,EAAe,KAAK,eAAe,EAAE,MAAM,eAAe,CAAC;AAGlE,cAAc,YAAY,CAAC;AAC3B,cAAc,YAAY,CAAC;AAC3B,cAAc,YAAY,CAAC;AAC3B,cAAc,eAAe,CAAC;AAC9B,cAAc,WAAW,CAAC;AAC1B,cAAc,kBAAkB,CAAC;AACjC,cAAc,YAAY,CAAC;AAC3B,cAAc,eAAe,CAAC;AAC9B,cAAc,YAAY,CAAC;AAC3B,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAE3C,eAAO,MAAM,UAAU,EAAE,MAAM,CAAC,MAAM,EAAyB,CAAC;AAChE,eAAO,MAAM,UAAU,qDAAoC,CAAC;AAC5D,eAAO,MAAM,cAAc,qDAAsC,CAAC;AAClE,eAAO,MAAM,eAAe,qDAAmC,CAAC;AAChE,eAAO,MAAM,cAAc,YAAa,eAAe,KAAG,MAAM,CAAC,MAAM,EAChB,CAAC;AACxD,eAAO,MAAM,aAAa,YAAa,eAAe,KAAG,MAAM,CAAC,MAAM,EACzB,CAAC;AAC9C,eAAO,MAAM,cAAc,YAAa,eAAe,KAAG,MAAM,CAAC,MAAM,EACzB,CAAC"}
@@ -1,13 +1,16 @@
1
1
  import { astro } from "./astro.js";
2
2
  import { basic } from "./basic.js";
3
3
  import { jsdoc } from "./jsdoc.js";
4
+ import { netsuite } from "./netsuite.js";
4
5
  import { next } from "./next.js";
6
+ import { nodeTS } from "./nodeTS.js";
5
7
  import { react } from "./react.js";
6
8
  import { getTailwind } from "./tailwind.js";
7
9
  import { composeConfig } from "./utils.js";
8
10
  export * from "./astro.js";
9
11
  export * from "./basic.js";
10
12
  export * from "./jsdoc.js";
13
+ export * from "./netsuite.js";
11
14
  export * from "./next.js";
12
15
  export * from "./packageJson.js";
13
16
  export * from "./react.js";
@@ -15,6 +18,8 @@ export * from "./tailwind.js";
15
18
  export * from "./turbo.js";
16
19
  export { composeConfig } from "./utils.js";
17
20
  export const presetBase = [...basic, ...jsdoc];
21
+ export const presetNode = composeConfig(presetBase, nodeTS);
22
+ export const presetNetSuite = composeConfig(presetBase, netsuite);
18
23
  export const presetReactLite = composeConfig(presetBase, react);
19
24
  export const getPresetReact = (options) => composeConfig(presetBase, react, getTailwind(options));
20
25
  export const getPresetNext = (options) => composeConfig(getPresetReact(options), next);
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/eslint/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,OAAO,EAAE,WAAW,EAAwB,MAAM,eAAe,CAAC;AAClE,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAE3C,cAAc,YAAY,CAAC;AAC3B,cAAc,YAAY,CAAC;AAC3B,cAAc,YAAY,CAAC;AAC3B,cAAc,WAAW,CAAC;AAC1B,cAAc,kBAAkB,CAAC;AACjC,cAAc,YAAY,CAAC;AAC3B,cAAc,eAAe,CAAC;AAC9B,cAAc,YAAY,CAAC;AAC3B,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAE3C,MAAM,CAAC,MAAM,UAAU,GAAoB,CAAC,GAAG,KAAK,EAAE,GAAG,KAAK,CAAC,CAAC;AAChE,MAAM,CAAC,MAAM,eAAe,GAAoB,aAAa,CAC5D,UAAU,EACV,KAAK,CACL,CAAC;AACF,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,OAAwB,EAAmB,EAAE,CAC3E,aAAa,CAAC,UAAU,EAAE,KAAK,EAAE,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC;AACxD,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,OAAwB,EAAmB,EAAE,CAC1E,aAAa,CAAC,cAAc,CAAC,OAAO,CAAC,EAAE,IAAI,CAAC,CAAC;AAC9C,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,OAAwB,EAAmB,EAAE,CAC3E,aAAa,CAAC,cAAc,CAAC,OAAO,CAAC,EAAE,KAAK,CAAC,CAAC","sourcesContent":["import type { Linter } from \"eslint\";\n\nimport { astro } from \"./astro.js\";\nimport { basic } from \"./basic.js\";\nimport { jsdoc } from \"./jsdoc.js\";\nimport { next } from \"./next.js\";\nimport { react } from \"./react.js\";\nimport { getTailwind, type TailwindOptions } from \"./tailwind.js\";\nimport { composeConfig } from \"./utils.js\";\n\nexport * from \"./astro.js\";\nexport * from \"./basic.js\";\nexport * from \"./jsdoc.js\";\nexport * from \"./next.js\";\nexport * from \"./packageJson.js\";\nexport * from \"./react.js\";\nexport * from \"./tailwind.js\";\nexport * from \"./turbo.js\";\nexport { composeConfig } from \"./utils.js\";\n\nexport const presetBase: Linter.Config[] = [...basic, ...jsdoc];\nexport const presetReactLite: Linter.Config[] = composeConfig(\n\tpresetBase,\n\treact,\n);\nexport const getPresetReact = (options: TailwindOptions): Linter.Config[] =>\n\tcomposeConfig(presetBase, react, getTailwind(options));\nexport const getPresetNext = (options: TailwindOptions): Linter.Config[] =>\n\tcomposeConfig(getPresetReact(options), next);\nexport const getPresetAstro = (options: TailwindOptions): Linter.Config[] =>\n\tcomposeConfig(getPresetReact(options), astro);\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/eslint/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,OAAO,EAAE,WAAW,EAAwB,MAAM,eAAe,CAAC;AAClE,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAE3C,cAAc,YAAY,CAAC;AAC3B,cAAc,YAAY,CAAC;AAC3B,cAAc,YAAY,CAAC;AAC3B,cAAc,eAAe,CAAC;AAC9B,cAAc,WAAW,CAAC;AAC1B,cAAc,kBAAkB,CAAC;AACjC,cAAc,YAAY,CAAC;AAC3B,cAAc,eAAe,CAAC;AAC9B,cAAc,YAAY,CAAC;AAC3B,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAE3C,MAAM,CAAC,MAAM,UAAU,GAAoB,CAAC,GAAG,KAAK,EAAE,GAAG,KAAK,CAAC,CAAC;AAChE,MAAM,CAAC,MAAM,UAAU,GAAG,aAAa,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;AAC5D,MAAM,CAAC,MAAM,cAAc,GAAG,aAAa,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;AAClE,MAAM,CAAC,MAAM,eAAe,GAAG,aAAa,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;AAChE,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,OAAwB,EAAmB,EAAE,CAC3E,aAAa,CAAC,UAAU,EAAE,KAAK,EAAE,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC;AACxD,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,OAAwB,EAAmB,EAAE,CAC1E,aAAa,CAAC,cAAc,CAAC,OAAO,CAAC,EAAE,IAAI,CAAC,CAAC;AAC9C,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,OAAwB,EAAmB,EAAE,CAC3E,aAAa,CAAC,cAAc,CAAC,OAAO,CAAC,EAAE,KAAK,CAAC,CAAC","sourcesContent":["import type { Linter } from \"eslint\";\n\nimport { astro } from \"./astro.js\";\nimport { basic } from \"./basic.js\";\nimport { jsdoc } from \"./jsdoc.js\";\nimport { netsuite } from \"./netsuite.js\";\nimport { next } from \"./next.js\";\nimport { nodeTS } from \"./nodeTS.js\";\nimport { react } from \"./react.js\";\nimport { getTailwind, type TailwindOptions } from \"./tailwind.js\";\nimport { composeConfig } from \"./utils.js\";\n\nexport * from \"./astro.js\";\nexport * from \"./basic.js\";\nexport * from \"./jsdoc.js\";\nexport * from \"./netsuite.js\";\nexport * from \"./next.js\";\nexport * from \"./packageJson.js\";\nexport * from \"./react.js\";\nexport * from \"./tailwind.js\";\nexport * from \"./turbo.js\";\nexport { composeConfig } from \"./utils.js\";\n\nexport const presetBase: Linter.Config[] = [...basic, ...jsdoc];\nexport const presetNode = composeConfig(presetBase, nodeTS);\nexport const presetNetSuite = composeConfig(presetBase, netsuite);\nexport const presetReactLite = composeConfig(presetBase, react);\nexport const getPresetReact = (options: TailwindOptions): Linter.Config[] =>\n\tcomposeConfig(presetBase, react, getTailwind(options));\nexport const getPresetNext = (options: TailwindOptions): Linter.Config[] =>\n\tcomposeConfig(getPresetReact(options), next);\nexport const getPresetAstro = (options: TailwindOptions): Linter.Config[] =>\n\tcomposeConfig(getPresetReact(options), astro);\n"]}
@@ -0,0 +1,3 @@
1
+ import type { Linter } from "eslint";
2
+ export declare const netsuite: Linter.Config[];
3
+ //# sourceMappingURL=netsuite.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"netsuite.d.ts","sourceRoot":"","sources":["../../../src/eslint/netsuite.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAErC,eAAO,MAAM,QAAQ,EAAE,MAAM,CAAC,MAAM,EA6BnC,CAAC"}
@@ -0,0 +1,31 @@
1
+ export const netsuite = [
2
+ {
3
+ languageOptions: {
4
+ globals: {
5
+ define: "readonly",
6
+ log: "readonly",
7
+ },
8
+ },
9
+ rules: {
10
+ // N/* modules are provided by NetSuite at runtime.
11
+ "sonarjs/no-implicit-dependencies": "off",
12
+ },
13
+ },
14
+ {
15
+ files: ["**/*.ts"],
16
+ rules: {
17
+ "jsdoc/check-tag-names": [
18
+ "error",
19
+ {
20
+ definedTags: [
21
+ "NApiVersion",
22
+ "NScriptType",
23
+ "NModuleScope",
24
+ "NAmdConfig",
25
+ ],
26
+ },
27
+ ],
28
+ },
29
+ },
30
+ ];
31
+ //# sourceMappingURL=netsuite.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"netsuite.js","sourceRoot":"","sources":["../../../src/eslint/netsuite.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,QAAQ,GAAoB;IACxC;QACC,eAAe,EAAE;YAChB,OAAO,EAAE;gBACR,MAAM,EAAE,UAAU;gBAClB,GAAG,EAAE,UAAU;aACf;SACD;QACD,KAAK,EAAE;YACN,mDAAmD;YACnD,kCAAkC,EAAE,KAAK;SACzC;KACD;IACD;QACC,KAAK,EAAE,CAAC,SAAS,CAAC;QAClB,KAAK,EAAE;YACN,uBAAuB,EAAE;gBACxB,OAAO;gBACP;oBACC,WAAW,EAAE;wBACZ,aAAa;wBACb,aAAa;wBACb,cAAc;wBACd,YAAY;qBACZ;iBACD;aACD;SACD;KACD;CACD,CAAC","sourcesContent":["import type { Linter } from \"eslint\";\n\nexport const netsuite: Linter.Config[] = [\n\t{\n\t\tlanguageOptions: {\n\t\t\tglobals: {\n\t\t\t\tdefine: \"readonly\",\n\t\t\t\tlog: \"readonly\",\n\t\t\t},\n\t\t},\n\t\trules: {\n\t\t\t// N/* modules are provided by NetSuite at runtime.\n\t\t\t\"sonarjs/no-implicit-dependencies\": \"off\",\n\t\t},\n\t},\n\t{\n\t\tfiles: [\"**/*.ts\"],\n\t\trules: {\n\t\t\t\"jsdoc/check-tag-names\": [\n\t\t\t\t\"error\",\n\t\t\t\t{\n\t\t\t\t\tdefinedTags: [\n\t\t\t\t\t\t\"NApiVersion\",\n\t\t\t\t\t\t\"NScriptType\",\n\t\t\t\t\t\t\"NModuleScope\",\n\t\t\t\t\t\t\"NAmdConfig\",\n\t\t\t\t\t],\n\t\t\t\t},\n\t\t\t],\n\t\t},\n\t},\n];\n"]}
@@ -0,0 +1,3 @@
1
+ import type { Linter } from "eslint";
2
+ export declare const nodeTS: Linter.Config;
3
+ //# sourceMappingURL=nodeTS.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nodeTS.d.ts","sourceRoot":"","sources":["../../../src/eslint/nodeTS.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAErC,eAAO,MAAM,MAAM,EAAE,MAAM,CAAC,MAI3B,CAAC"}
@@ -0,0 +1,6 @@
1
+ export const nodeTS = {
2
+ rules: {
3
+ "import/extensions": ["error", "always", { ts: "always" }],
4
+ },
5
+ };
6
+ //# sourceMappingURL=nodeTS.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nodeTS.js","sourceRoot":"","sources":["../../../src/eslint/nodeTS.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,MAAM,GAAkB;IACpC,KAAK,EAAE;QACN,mBAAmB,EAAE,CAAC,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,EAAE,QAAQ,EAAE,CAAC;KAC1D;CACD,CAAC","sourcesContent":["import type { Linter } from \"eslint\";\n\nexport const nodeTS: Linter.Config = {\n\trules: {\n\t\t\"import/extensions\": [\"error\", \"always\", { ts: \"always\" }],\n\t},\n};\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rootreeweb/linting",
3
- "version": "5.2.0",
3
+ "version": "5.4.0",
4
4
  "description": "Shared configurations for ESLint and Stylelint",
5
5
  "license": "UNLICENSED",
6
6
  "repository": "github:rootreeweb/meta-tools",
@@ -34,7 +34,8 @@
34
34
  "test:watch": "vitest --coverage --ui"
35
35
  },
36
36
  "files": [
37
- "dist/**/*"
37
+ "dist/**/*",
38
+ "skills/**/*"
38
39
  ],
39
40
  "keywords": [
40
41
  "eslint",
@@ -46,17 +47,17 @@
46
47
  "dependencies": {
47
48
  "@eslint/js": "^10.0.1",
48
49
  "@ianvs/prettier-plugin-sort-imports": "^4.7.1",
49
- "@next/eslint-plugin-next": "^16.4.0",
50
- "@rootreeweb/js-lib": "^4.0.19",
51
- "@tanstack/eslint-plugin-query": "^5.104.2",
50
+ "@next/eslint-plugin-next": "^16.4.2",
51
+ "@rootreeweb/js-lib": "^4.0.20",
52
+ "@tanstack/eslint-plugin-query": "^5.104.5",
52
53
  "@vitest/eslint-plugin": "^1.6.27",
53
54
  "eslint-import-resolver-typescript": "^4.4.5",
54
55
  "eslint-package-json": "^3.0.0",
55
56
  "eslint-plugin-astro": "^3.2.1",
56
57
  "eslint-plugin-import": "^2.32.0",
57
- "eslint-plugin-jsdoc": "^65.3.2",
58
+ "eslint-plugin-jsdoc": "^65.4.0",
58
59
  "eslint-plugin-jsx-a11y": "^6.10.2",
59
- "eslint-plugin-perfectionist": "^5.12.1",
60
+ "eslint-plugin-perfectionist": "^5.13.0",
60
61
  "eslint-plugin-promise": "^7.3.0",
61
62
  "eslint-plugin-react": "^7.37.5",
62
63
  "eslint-plugin-react-compiler": "^19.1.0-rc.2",
@@ -66,9 +67,9 @@
66
67
  "eslint-plugin-sonarjs": "^4.2.2",
67
68
  "eslint-plugin-tailwindcss": "^4.4.0",
68
69
  "eslint-plugin-testing-library": "^7.16.2",
69
- "eslint-plugin-turbo": "^2.11.7",
70
+ "eslint-plugin-turbo": "^2.11.9",
70
71
  "globals": "^17.13.0",
71
- "postcss": "^8.5.30",
72
+ "postcss": "^8.5.31",
72
73
  "prettier-plugin-astro": "^1.1.1",
73
74
  "prettier-plugin-jsdoc": "^1.8.1",
74
75
  "prettier-plugin-tailwindcss": "^0.8.1",
@@ -77,33 +78,35 @@
77
78
  "stylelint-config-tailwindcss": "^1.0.1",
78
79
  "stylelint-prettier": "^5.0.3",
79
80
  "tailwindcss": "^4.3.3",
80
- "typescript-eslint": "^8.71.2"
81
+ "typescript-eslint": "^8.72.0"
81
82
  },
82
83
  "devDependencies": {
83
- "@rootreeweb/tsconfigs": "^3.3.24",
84
+ "@rootreeweb/tsconfigs": "^3.4.1",
84
85
  "@types/eslint-plugin-jsx-a11y": "^6.10.1",
85
86
  "@types/eslint-plugin-security": "^3.0.1",
86
87
  "@types/node": "^26.6.5",
87
88
  "@types/react": "^19.3.0",
88
89
  "@typescript/native-preview": "^7.0.0-dev.20260707.2",
89
- "@vitest/coverage-v8": "^5.0.4",
90
- "@vitest/ui": "^5.0.4",
90
+ "@vitest/coverage-v8": "^5.0.5",
91
+ "@vitest/ui": "^5.0.5",
91
92
  "del-cli": "^8.0.1",
92
- "eslint": "^10.12.0",
93
+ "eslint": "^10.13.0",
93
94
  "husky": "^9.1.7",
94
- "knip": "^6.41.0",
95
+ "jscpd": "^5.4.1",
96
+ "knip": "^6.42.1",
95
97
  "lint-staged": "^17.6.0",
96
98
  "prettier": "~3.8.5",
97
- "react": "^19.3.0",
99
+ "react": "^19.3.1",
98
100
  "stylelint": "~17.16.0",
99
- "turbo": "~2.11.7",
101
+ "turbo": "~2.11.9",
100
102
  "typescript": "^6.0.3",
101
103
  "vite": "^8.3.4",
102
- "vitest": "^5.0.4"
104
+ "vitest": "^5.0.5"
103
105
  },
104
106
  "peerDependencies": {
105
107
  "eslint": "^10",
106
108
  "husky": "^9.1.7",
109
+ "jscpd": "*",
107
110
  "lint-staged": "^17.2.0",
108
111
  "prettier": "~3.8.0",
109
112
  "stylelint": "~17.16.0",
@@ -116,5 +119,11 @@
116
119
  "turbo": {
117
120
  "optional": true
118
121
  }
119
- }
122
+ },
123
+ "skills": [
124
+ "mattpocock/skills@domain-modeling",
125
+ "mattpocock/skills@grill-me",
126
+ "mattpocock/skills@improve-codebase-architecture",
127
+ "kucherenko/jscpd@dry-refactoring"
128
+ ]
120
129
  }
@@ -0,0 +1,18 @@
1
+ ---
2
+ name: format-and-lint-js
3
+ description: Type-checks, lints and formats a JavaScript/TypeScript project using the @rootreeweb/linting setup. Use after making changes to JS or TS code.
4
+ ---
5
+
6
+ # Format and Lint JS
7
+
8
+ ## When to use this skill
9
+
10
+ - Use this after making changes to the JavaScript and TypeScript in the project
11
+
12
+ ## How to use it
13
+
14
+ 1. Execute `yarn check` to check for type errors.
15
+ 2. Execute `yarn lint` to check for lint errors.
16
+ - If the repo contains the `@rootreeweb/linting` package itself, run `yarn build` first so the rules are up to date.
17
+ 3. Execute `yarn format` to format the code.
18
+ 4. Fix any errors that arise and then repeat until there are no errors.
@@ -0,0 +1,83 @@
1
+ ---
2
+ name: solid-audit
3
+ description: Audits code or a design against SOLID and Clean Code (Uncle Bob) principles and reports concrete, prioritised refactors. Use on demand when asked for a SOLID, clean code, Uncle Bob, design or code-quality audit/review. Also use passively whenever planning or designing an implementation (plan mode, "how should we build X", new modules, refactors) to sanity-check the proposed structure before code is written.
4
+ ---
5
+
6
+ # SOLID Audit
7
+
8
+ Judge code by how cheaply it can change. Every finding must name a concrete cost (what change becomes hard, what breaks, what gets duplicated) and a concrete fix. A principle being "violated" with no cost is not a finding.
9
+
10
+ ## Modes
11
+
12
+ ### Planning (passive)
13
+
14
+ Run silently while drafting any implementation plan. Do not announce the skill or produce a report.
15
+
16
+ 1. Check the proposed modules, functions and data flow against the principles and house preferences below.
17
+ 2. Adjust the plan itself where a problem is cheap to avoid now (split a responsibility, invert a dependency, narrow an interface).
18
+ 3. Only if a trade-off remains worth the user's attention, add at most 3 bullets under a short **Design notes** heading in the plan. Otherwise add nothing.
19
+ 4. Never add abstractions to the plan "for SOLID". Prefer the simplest design that does not create one of the costs listed below.
20
+
21
+ ### Audit (on demand)
22
+
23
+ 1. **Scope**: use what the user named (files, package, diff, PR). If nothing is named, audit the current diff against the main branch; if there is no diff, ask.
24
+ 2. **Read**: read the code in scope fully, plus its direct callers and dependencies when needed to judge coupling. Don't audit files you haven't read.
25
+ 3. **Assess** each principle, heuristic and house preference below. Skip what doesn't apply.
26
+ 4. **Report** using the output format below, most severe first. Cap at ~10 findings unless asked for more.
27
+ 5. **Don't edit** unless the user asks. When asked to fix, do one finding at a time with tests/type checks green between steps.
28
+
29
+ ## Principles
30
+
31
+ Interpret for idiomatic TypeScript: modules and functions count as units, not just classes. Types and function parameters are interfaces.
32
+
33
+ | Principle | Signals of a real problem | Typical fix |
34
+ | --------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------- |
35
+ | **S**ingle Responsibility: one reason to change | A unit mixes I/O, business rules and formatting; edits for unrelated features touch the same function; name needs "and"/"Manager"/"Utils"/"Helper"; file well over ~100 lines | Split by reason to change; keep pure logic separate from side effects |
36
+ | **O**pen/Closed: extend without editing | Growing `switch`/`if` chains on a type tag repeated in several places; every new variant edits core code | Discriminated union + lookup map/strategy object; only once there are ≥2 real variants |
37
+ | **L**iskov Substitution: substitutes honour the contract | Overrides/implementations that throw "not supported", ignore params, narrow accepted inputs or widen errors; callers check concrete type | Fix the contract or stop pretending it's the same type |
38
+ | **I**nterface Segregation: depend only on what you use | Functions take a whole config/entity but read one field; optional-property soup; mocks in tests need many unused stubs | Accept a narrow type (`Pick<>`, specific params) |
39
+ | **D**ependency Inversion: policy doesn't depend on detail | Business logic imports fs, network, env, SDKs or global singletons directly; tests need module mocking to run | Pass the dependency in (parameter, factory arg); keep the edge thin |
40
+
41
+ ## Clean Code heuristics
42
+
43
+ - **Functions**: do one thing at one level of abstraction; >3 params or boolean flag params usually mean two functions or an options object.
44
+ - **Names**: reveal intent; same concept, same word; no type-encoded or abbreviated names.
45
+ - **Side effects**: hidden mutation of args/globals; functions named as queries that also write.
46
+ - **Errors**: swallowed errors, `catch` that logs and continues into invalid state, error handling mixed into happy-path logic.
47
+ - **Law of Demeter**: long `a.b().c().d` chains reaching into another module's internals.
48
+ - **Comments**: inline comments that restate code or compensate for a bad name. Prefer renaming/extracting. Comments that explain non-obvious _why_ are fine. JSDoc blocks are exempt: never flag them as over-commenting.
49
+ - **Tests**: untested branches in changed logic; tests coupled to implementation details.
50
+ - **Design smells**: rigidity (one change → many edits), fragility (changes break distant code), immobility (can't reuse without dragging deps), needless complexity (speculative abstraction), opacity.
51
+
52
+ ## House preferences
53
+
54
+ These are this team's defaults. Apply them in both modes alongside SOLID.
55
+
56
+ - **Files under ~100 lines.** A longer file is a signal of more than one responsibility; propose how to split it (by responsibility, not arbitrarily). JSDoc lines count less: don't split a file just because its docs are thorough.
57
+ - **Use folder structure.** Group related files in a folder named for the concept (e.g. `eslint/netsuite.ts`, `presets/core.ts`) rather than prefixing file names or growing a flat directory.
58
+ - **Minimise churn in existing files.** Prefer designs where new behaviour lands in new files and existing files change little; in particular avoid plans that rewrite import lists across many consumers.
59
+ - **Barrels where they don't complicate things.** A folder's `index.ts` re-exporting its public surface lets consumers import from the folder, so adding or splitting files inside it doesn't touch their imports. Skip a barrel when it would create an import cycle, re-export across package boundaries for no gain, or hide which module owns a side effect.
60
+
61
+ ## Guardrails
62
+
63
+ - YAGNI beats OCP: don't recommend an abstraction for a single implementation or a hypothetical future variant.
64
+ - Don't recommend classes/interfaces where plain functions and types suffice.
65
+ - Style, formatting and lint-enforced rules are out of scope; the linter owns them.
66
+ - Duplication findings: hand off to the `dry-refactoring` skill. Module-boundary/deep-module redesigns: hand off to `improve-codebase-architecture`.
67
+ - Say so plainly when code is in good shape; an empty or short report is a valid result.
68
+
69
+ ## Output format (audit mode)
70
+
71
+ ```markdown
72
+ ## SOLID audit: <scope>
73
+
74
+ <one-sentence overall verdict>
75
+
76
+ | # | Severity | Principle | Location | Problem (cost) | Suggested fix |
77
+ | --- | -------- | --------- | --------------- | ------------------------------------------------------------------------------------------- | -------------------------------- |
78
+ | 1 | High | DIP | `src/foo.ts:42` | `syncOrders` calls the NetSuite SDK directly, so it can't be tested without mocking modules | Pass a `fetchOrders` function in |
79
+
80
+ **Quick wins:** <1–3 low-effort, high-value items by #>
81
+ ```
82
+
83
+ Severity: **High** = actively causing bugs or blocking likely changes; **Medium** = real cost on the next change in this area; **Low** = worth doing when touching the code anyway.