@codyswann/lisa 4.33.4 → 4.33.6

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.
Files changed (88) hide show
  1. package/dist/configs/eslint/expo.d.ts.map +1 -1
  2. package/dist/configs/eslint/expo.js +4 -7
  3. package/dist/configs/eslint/expo.js.map +1 -1
  4. package/dist/configs/eslint/view-gate.d.ts +42 -0
  5. package/dist/configs/eslint/view-gate.d.ts.map +1 -0
  6. package/dist/configs/eslint/view-gate.js +110 -0
  7. package/dist/configs/eslint/view-gate.js.map +1 -0
  8. package/dist/core/lisa-owned-hash-ledger.d.ts.map +1 -1
  9. package/dist/core/lisa-owned-hash-ledger.js +8 -0
  10. package/dist/core/lisa-owned-hash-ledger.js.map +1 -1
  11. package/dist/core/nightly-e2e-guard-behavior-certificate.js +2 -2
  12. package/dist/core/upstream-evidence-manifest.d.ts.map +1 -1
  13. package/dist/core/upstream-evidence-manifest.js +20 -9
  14. package/dist/core/upstream-evidence-manifest.js.map +1 -1
  15. package/eslint-plugin-component-structure/README.md +85 -2
  16. package/eslint-plugin-component-structure/__tests__/no-hooks-in-view.test.js +229 -0
  17. package/eslint-plugin-component-structure/__tests__/no-return-in-view.test.js +185 -0
  18. package/eslint-plugin-component-structure/__tests__/plugin-index.test.js +8 -1
  19. package/eslint-plugin-component-structure/index.js +4 -1
  20. package/eslint-plugin-component-structure/lib/view-file-scope.js +62 -0
  21. package/eslint-plugin-component-structure/rules/no-hooks-in-view.js +101 -0
  22. package/eslint-plugin-component-structure/rules/no-return-in-view.js +133 -62
  23. package/expo/copy-overwrite/eslint.expo.ts +88 -1
  24. package/package.json +4 -4
  25. package/plugins/lisa/.claude-plugin/plugin.json +1 -1
  26. package/plugins/lisa/.codex-plugin/plugin.json +1 -1
  27. package/plugins/lisa-agy/plugin.json +1 -1
  28. package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
  29. package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
  30. package/plugins/lisa-cdk-agy/plugin.json +1 -1
  31. package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
  32. package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
  33. package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
  34. package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
  35. package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
  36. package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
  37. package/plugins/lisa-expo/.codex-plugin/skills/container-view-pattern/SKILL.md +80 -24
  38. package/plugins/lisa-expo/.codex-plugin/skills/container-view-pattern/scripts/validate_component.py +166 -43
  39. package/plugins/lisa-expo/skills/container-view-pattern/SKILL.md +80 -24
  40. package/plugins/lisa-expo/skills/container-view-pattern/scripts/validate_component.py +166 -43
  41. package/plugins/lisa-expo-agy/plugin.json +1 -1
  42. package/plugins/lisa-expo-agy/skills/container-view-pattern/SKILL.md +80 -24
  43. package/plugins/lisa-expo-agy/skills/container-view-pattern/scripts/validate_component.py +166 -43
  44. package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
  45. package/plugins/lisa-expo-copilot/skills/container-view-pattern/SKILL.md +80 -24
  46. package/plugins/lisa-expo-copilot/skills/container-view-pattern/scripts/validate_component.py +166 -43
  47. package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
  48. package/plugins/lisa-expo-cursor/skills/container-view-pattern/SKILL.md +80 -24
  49. package/plugins/lisa-expo-cursor/skills/container-view-pattern/scripts/validate_component.py +166 -43
  50. package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
  51. package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
  52. package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
  53. package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
  54. package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
  55. package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
  56. package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
  57. package/plugins/lisa-nestjs-agy/plugin.json +1 -1
  58. package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
  59. package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
  60. package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
  61. package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
  62. package/plugins/lisa-openclaw-agy/plugin.json +1 -1
  63. package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
  64. package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
  65. package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
  66. package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
  67. package/plugins/lisa-phaser-agy/plugin.json +1 -1
  68. package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
  69. package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
  70. package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
  71. package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
  72. package/plugins/lisa-rails-agy/plugin.json +1 -1
  73. package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
  74. package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
  75. package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
  76. package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
  77. package/plugins/lisa-typescript-agy/plugin.json +1 -1
  78. package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
  79. package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
  80. package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
  81. package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
  82. package/plugins/lisa-wiki-agy/plugin.json +1 -1
  83. package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
  84. package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
  85. package/plugins/src/expo/skills/container-view-pattern/SKILL.md +80 -24
  86. package/plugins/src/expo/skills/container-view-pattern/scripts/validate_component.py +166 -43
  87. package/typescript/copy-overwrite/scripts/check-skipped-required-checks.mjs +414 -6
  88. package/typescript/create-only/.github/workflows/review-evidence.yml +78 -3
@@ -0,0 +1,101 @@
1
+ /**
2
+ * This file is managed by Lisa.
3
+ * Do not edit directly — changes will be overwritten on the next `lisa` run.
4
+ */
5
+
6
+ /**
7
+ * ESLint rule banning hook calls anywhere in a View component file.
8
+ *
9
+ * The Container/View skill has said "View should not contain hooks" since it was
10
+ * written, and for that whole time nothing enforced it — the skill's own table
11
+ * of "ESLint rules that enforce the pattern" listed four rules, none of which
12
+ * looked at a hook. This rule is the missing half.
13
+ *
14
+ * The matcher is a SHAPE — `use` followed by an uppercase letter, called — and
15
+ * deliberately never a list of names. The call that made the gap expensive was a
16
+ * project-local custom hook, so any rule built on an enumerated list would have
17
+ * missed the one that mattered. That is also why there is no exemption list:
18
+ * a custom data hook and a render helper are indistinguishable by name, so
19
+ * `useMemo` and `useCallback` are caught too. The skill already forbids
20
+ * `useMemo` in a View, so exempting it would contradict the document.
21
+ *
22
+ * Scope is the file, not the component body: a hook called from a local render
23
+ * helper still runs during that View's render.
24
+ * @module eslint-plugin-component-structure/rules/no-hooks-in-view
25
+ */
26
+
27
+ const { isEnforcedViewFile } = require("../lib/view-file-scope");
28
+
29
+ /** `use` plus an uppercase letter — React's own definition of a hook name. */
30
+ const HOOK_NAME = /^use[A-Z]\w*$/u;
31
+
32
+ /**
33
+ * The hook a call expression invokes, if it invokes one.
34
+ *
35
+ * Covers the bare call (`useFlag()`) and the namespaced one
36
+ * (`React.useMemo()`); a computed member access is not a statically knowable
37
+ * name and is left to `no-restricted-imports` and review.
38
+ * @param {object} callee - The callee of a CallExpression.
39
+ * @returns {string|null} The hook name, or null when this is not a hook call.
40
+ */
41
+ function hookName(callee) {
42
+ if (callee.type === "Identifier" && HOOK_NAME.test(callee.name)) {
43
+ return callee.name;
44
+ }
45
+
46
+ if (
47
+ callee.type === "MemberExpression" &&
48
+ !callee.computed &&
49
+ callee.property.type === "Identifier" &&
50
+ HOOK_NAME.test(callee.property.name)
51
+ ) {
52
+ return callee.property.name;
53
+ }
54
+
55
+ return null;
56
+ }
57
+
58
+ module.exports = {
59
+ meta: {
60
+ type: "problem",
61
+ docs: {
62
+ description:
63
+ "Disallow hook calls in View components - hooks belong in the Container",
64
+ category: "Best Practices",
65
+ recommended: true,
66
+ },
67
+ fixable: null,
68
+ schema: [],
69
+ messages: {
70
+ // The message names the escape route on purpose. "No hooks in Views" with
71
+ // no way out is what makes a developer — or an agent — reach for a disable
72
+ // comment, and there is no disable this rule will accept.
73
+ //
74
+ // The Container is always a legitimate home: Containers may hold logic,
75
+ // including a callback that returns JSX. The row-component alternative is
76
+ // named for the `renderItem` shape, where lifting the callback moves JSX
77
+ // rather than removing it — it is an OPTION, never a requirement.
78
+ noHooksInView:
79
+ "View components must not call hooks. Move {{hook}}() into the corresponding Container and pass its result down as a prop — Containers may hold logic, including a callback that returns JSX. If {{hook}}() builds a row (a renderItem or render* callback), extracting that row into its own memoised component is an equally valid alternative.",
80
+ },
81
+ },
82
+
83
+ create(context) {
84
+ if (!isEnforcedViewFile(context.getFilename())) {
85
+ return {};
86
+ }
87
+
88
+ return {
89
+ CallExpression(node) {
90
+ const hook = hookName(node.callee);
91
+ if (hook !== null) {
92
+ context.report({
93
+ node,
94
+ messageId: "noHooksInView",
95
+ data: { hook },
96
+ });
97
+ }
98
+ },
99
+ };
100
+ },
101
+ };
@@ -3,12 +3,85 @@
3
3
  * Do not edit directly — changes will be overwritten on the next `lisa` run.
4
4
  */
5
5
 
6
+ /**
7
+ * ESLint rule requiring a View component to be an expression-bodied arrow.
8
+ *
9
+ * Stated that way on purpose. The rule used to be stated as "no return
10
+ * statements", registered exactly one `ArrowFunctionExpression` visitor, and
11
+ * nothing anywhere required a View to be an arrow function — so
12
+ * `const XView = (...) => { ... }` was blocked while `function XView(...) { ... }`
13
+ * was never visited and reported nothing, with `require-memo-in-view` still
14
+ * passing because it checks the export. Writing `function` instead of `const`
15
+ * was a silent, complete, permanent opt-out of the only statement gate on Views.
16
+ *
17
+ * An expression body is a property a `FunctionDeclaration` cannot have, so
18
+ * requiring it bans declaration form by construction — no prose scanning, no
19
+ * substring match on the word "function" (which fires on JSDoc, on local render
20
+ * helpers, and on disable-comment justifications), and no false-positive
21
+ * surface.
22
+ *
23
+ * What this rule guarantees is "no statement list". It is NOT a purity gate:
24
+ * `{Date.now()}` and `{Math.random() > 0.5 ? … : …}` sit happily in an
25
+ * expression body. Hooks are the other half and are `no-hooks-in-view`'s job.
26
+ * @module eslint-plugin-component-structure/rules/no-return-in-view
27
+ */
28
+
29
+ const { isEnforcedViewFile } = require("../lib/view-file-scope");
30
+
31
+ /**
32
+ * Whether an identifier names a View component by spelling.
33
+ * @param {string} name - The declared identifier.
34
+ * @returns {boolean} True for a PascalCase name containing "View".
35
+ */
36
+ const isViewComponentName = name =>
37
+ /^[A-Z]/u.test(name) && name.includes("View");
38
+
39
+ /**
40
+ * The binding a function expression or arrow is assigned to, if any.
41
+ * @param {object} node - The function node.
42
+ * @returns {string|null} The declared name, or null when there is none.
43
+ */
44
+ function declaredName(node) {
45
+ const parent = node.parent;
46
+ return parent &&
47
+ parent.type === "VariableDeclarator" &&
48
+ parent.id.type === "Identifier"
49
+ ? parent.id.name
50
+ : null;
51
+ }
52
+
53
+ /**
54
+ * The identifier a default export ultimately names.
55
+ *
56
+ * Unwraps `memo(X)` and `React.memo(X)` so the component the file actually
57
+ * exports is recognised whatever it is called. Without this the rule would
58
+ * depend on the identifier containing the word "View", and a file named
59
+ * `HomeScreenView.tsx` exporting `HomeScreen` would slip through — the same
60
+ * class of spelling-dependent hole the rule is being fixed for.
61
+ * @param {object} declaration - The `export default` declaration node.
62
+ * @returns {string|null} The exported identifier, or null.
63
+ */
64
+ function defaultExportedName(declaration) {
65
+ if (declaration.type === "Identifier") {
66
+ return declaration.name;
67
+ }
68
+
69
+ if (declaration.type !== "CallExpression") {
70
+ return null;
71
+ }
72
+
73
+ const [firstArgument] = declaration.arguments;
74
+ return firstArgument && firstArgument.type === "Identifier"
75
+ ? firstArgument.name
76
+ : null;
77
+ }
78
+
6
79
  module.exports = {
7
80
  meta: {
8
81
  type: "problem",
9
82
  docs: {
10
83
  description:
11
- "Disallow return statements in View components - use arrow function shorthand",
84
+ "Require View components to be arrow functions with an expression body (no statements)",
12
85
  category: "Best Practices",
13
86
  recommended: true,
14
87
  },
@@ -16,81 +89,79 @@ module.exports = {
16
89
  schema: [],
17
90
  messages: {
18
91
  noReturnInView:
19
- "View components should use arrow function shorthand: () => (...) instead of () => { return (...) }. Hoist any definitions outside of the arrow function body or into the corresponding Container.",
92
+ "View components must use an expression body: () => (...) instead of () => { return (...) }. Hoist any definitions outside of the arrow function body or into the corresponding Container.",
93
+ mustBeArrowFunction:
94
+ "View components must be an arrow function with an expression body: const {{name}} = (props) => (...). A function declaration or function expression cannot have one, so it always carries a statement list — move that logic into the corresponding Container.",
20
95
  },
21
96
  },
22
97
 
23
98
  create(context) {
24
- const filename = context.getFilename();
25
- const normalizedPath = filename.replace(/\\/g, "/");
26
-
27
- // Only check View.tsx and View.jsx files
28
- if (!filename.endsWith("View.tsx") && !filename.endsWith("View.jsx")) {
99
+ if (!isEnforcedViewFile(context.getFilename())) {
29
100
  return {};
30
101
  }
31
102
 
32
- // Check if file is in features/**/components, features/**/screens, or components directory
33
- const isFeatureComponent =
34
- normalizedPath.includes("features/") &&
35
- normalizedPath.includes("/components/");
36
- const isFeatureScreen =
37
- normalizedPath.includes("features/") &&
38
- normalizedPath.includes("/screens/");
39
- const isComponentsDir =
40
- normalizedPath.includes("/components/") &&
41
- !normalizedPath.includes("/components/ui/") &&
42
- !normalizedPath.includes("/components/custom/ui/");
43
-
44
- if (!isFeatureComponent && !isFeatureScreen && !isComponentsDir) {
45
- return {};
46
- }
103
+ /** Statement-bodied functions that are not arrows, pending a verdict. */
104
+ const nonArrowCandidates = [];
105
+ /** Names the default export resolves to. */
106
+ const exportedNames = new Set();
107
+
108
+ /**
109
+ * Record a non-arrow function that may be this file's View component.
110
+ * @param {object} node - The FunctionDeclaration or FunctionExpression.
111
+ * @param {string|null} name - Its declared name, when it has one.
112
+ * @returns {void}
113
+ */
114
+ const considerNonArrow = (node, name) => {
115
+ const isDefaultExported =
116
+ node.parent && node.parent.type === "ExportDefaultDeclaration";
117
+ nonArrowCandidates.push({ node, name, isDefaultExported });
118
+ };
47
119
 
48
120
  return {
49
121
  ArrowFunctionExpression(node) {
50
- // Check if this is a component (starts with capital letter or is exported)
51
122
  const parent = node.parent;
123
+ const name = declaredName(node);
124
+ const isComponent =
125
+ (parent && parent.type === "ExportDefaultDeclaration") ||
126
+ (name !== null && isViewComponentName(name));
52
127
 
53
- // Helper to check if variable has a View component name
54
- const isViewComponent = name =>
55
- /^[A-Z]/.test(name) && name.includes("View");
56
-
57
- // Determine if this is a component
58
- const isComponent = (() => {
59
- // Check if it's a default export
60
- if (parent.type === "ExportDefaultDeclaration") {
61
- return true;
62
- }
63
-
64
- // Check if it's a variable declaration with PascalCase name
65
- if (
66
- parent.type === "VariableDeclarator" &&
67
- parent.id.type === "Identifier"
68
- ) {
69
- return isViewComponent(parent.id.name);
70
- }
71
-
72
- // Check if it's part of an export statement
73
- if (
74
- parent.type === "VariableDeclarator" &&
75
- parent.parent.type === "VariableDeclaration" &&
76
- parent.parent.parent.type === "ExportNamedDeclaration"
77
- ) {
78
- return isViewComponent(parent.id.name);
79
- }
80
-
81
- return false;
82
- })();
83
-
84
- if (!isComponent) return;
85
-
86
- // Check if the arrow function has a block body (any BlockStatement is forbidden)
87
- if (node.body.type === "BlockStatement") {
88
- context.report({
89
- node,
90
- messageId: "noReturnInView",
91
- });
128
+ if (isComponent && node.body.type === "BlockStatement") {
129
+ context.report({ node, messageId: "noReturnInView" });
92
130
  }
93
131
  },
132
+
133
+ FunctionDeclaration(node) {
134
+ considerNonArrow(node, node.id ? node.id.name : null);
135
+ },
136
+
137
+ FunctionExpression(node) {
138
+ considerNonArrow(node, declaredName(node));
139
+ },
140
+
141
+ ExportDefaultDeclaration(node) {
142
+ const name = defaultExportedName(node.declaration);
143
+ if (name !== null) {
144
+ exportedNames.add(name);
145
+ }
146
+ },
147
+
148
+ "Program:exit"() {
149
+ nonArrowCandidates
150
+ .filter(
151
+ candidate =>
152
+ candidate.isDefaultExported ||
153
+ (candidate.name !== null &&
154
+ (isViewComponentName(candidate.name) ||
155
+ exportedNames.has(candidate.name)))
156
+ )
157
+ .forEach(candidate => {
158
+ context.report({
159
+ node: candidate.node,
160
+ messageId: "mustBeArrowFunction",
161
+ data: { name: candidate.name ?? "ComponentView" },
162
+ });
163
+ });
164
+ },
94
165
  };
95
166
  },
96
167
  };
@@ -59,6 +59,39 @@ const componentStructure = require("@codyswann/eslint-plugin-component-structure
59
59
  const uiStandards = require("@codyswann/eslint-plugin-ui-standards");
60
60
  const expoConfig = require("eslint-config-expo/flat");
61
61
 
62
+ /**
63
+ * React's hook named exports, banned from a View's import list.
64
+ *
65
+ * An enumeration is correct HERE and nowhere else: React's hook set is fixed and
66
+ * published, and this is the import-line axis. The call-site axis
67
+ * (`component-structure/no-hooks-in-view`) matches by shape precisely because a
68
+ * project-local custom hook can never appear on a list like this one.
69
+ */
70
+ const REACT_HOOK_IMPORT_NAMES = [
71
+ "useActionState",
72
+ "useCallback",
73
+ "useContext",
74
+ "useDebugValue",
75
+ "useDeferredValue",
76
+ "useEffect",
77
+ "useEffectEvent",
78
+ "useId",
79
+ "useImperativeHandle",
80
+ "useInsertionEffect",
81
+ "useLayoutEffect",
82
+ "useMemo",
83
+ "useOptimistic",
84
+ "useReducer",
85
+ "useRef",
86
+ "useState",
87
+ "useSyncExternalStore",
88
+ "useTransition",
89
+ ];
90
+
91
+ /** What a developer is told when a View imports a hook. */
92
+ const VIEW_HOOK_IMPORT_MESSAGE =
93
+ "View components must not use hooks. Move the hook into the corresponding Container and pass its result down as a prop.";
94
+
62
95
  /**
63
96
  * A project's `.lisa.config.json`, when it has one.
64
97
  *
@@ -371,7 +404,13 @@ export function getExpoConfig({
371
404
  },
372
405
  },
373
406
 
374
- // View component rules
407
+ // View component rules: no statements, no hooks.
408
+ //
409
+ // Stated as those two properties rather than as "Views are pure functions",
410
+ // which is a guarantee neither rule delivers — `{Date.now()}` and
411
+ // `{Math.random() > 0.5 ? … : …}` sit happily in an expression body. The
412
+ // skill claiming an enforcement it did not have is what let a hook live in
413
+ // a View for so long; the fix must not repeat the shape of the defect.
375
414
  {
376
415
  files: ["**/*View.tsx", "**/*View.jsx"],
377
416
  rules: {
@@ -379,6 +418,54 @@ export function getExpoConfig({
379
418
  },
380
419
  },
381
420
 
421
+ // View hook ban, on two independent axes.
422
+ //
423
+ // `no-hooks-in-view` matches the CALL by shape (`use` + uppercase), so a
424
+ // project-local custom hook is caught where an enumerated name list would
425
+ // miss it. `no-restricted-imports` catches the IMPORT LINE, before a call
426
+ // site exists. Neither subsumes the other: a hook re-exported through a
427
+ // barrel escapes the import patterns, and an import with no call yet
428
+ // escapes the call matcher.
429
+ //
430
+ // `paths` may enumerate here because React's hook set is fixed and
431
+ // published; the generic half is the rule, not this list. Scope matches the
432
+ // Container/View pattern's own applicability table, and the base
433
+ // `@/features/*/*` restriction is carried forward because a flat-config
434
+ // override replaces a rule's options wholesale.
435
+ {
436
+ files: ["**/*View.tsx", "**/*View.jsx"],
437
+ ignores: [
438
+ "components/ui/**",
439
+ "components/shared/**",
440
+ "components/icons/**",
441
+ ],
442
+ rules: {
443
+ "component-structure/no-hooks-in-view": "error",
444
+ "no-restricted-imports": [
445
+ "error",
446
+ {
447
+ paths: [
448
+ {
449
+ name: "react",
450
+ importNames: REACT_HOOK_IMPORT_NAMES,
451
+ message: VIEW_HOOK_IMPORT_MESSAGE,
452
+ },
453
+ ],
454
+ patterns: [
455
+ // Objects, not a mix: `no-restricted-imports` accepts an array of
456
+ // strings OR an array of objects, never both, and a mixed array
457
+ // is a schema error that disables the whole override.
458
+ { group: ["@/features/*/*"] },
459
+ {
460
+ group: ["**/hooks/**", "@/hooks/**", "~/hooks/**"],
461
+ message: VIEW_HOOK_IMPORT_MESSAGE,
462
+ },
463
+ ],
464
+ },
465
+ ],
466
+ },
467
+ },
468
+
382
469
  // View memo requirement (excluding UI components)
383
470
  {
384
471
  files: ["**/*View.tsx", "**/*View.jsx"],
package/package.json CHANGED
@@ -165,7 +165,7 @@
165
165
  "zod-validation-error": "^4.0.0"
166
166
  },
167
167
  "name": "@codyswann/lisa",
168
- "version": "4.33.4",
168
+ "version": "4.33.6",
169
169
  "description": "Claude Code governance framework that applies guardrails, guidance, and automated enforcement to projects",
170
170
  "main": "dist/index.js",
171
171
  "exports": {
@@ -313,7 +313,7 @@
313
313
  "test": "tests"
314
314
  },
315
315
  "types": "./dist/index.d.ts",
316
- "lisaReleaseCommit": "e48bd2085b8b7e9a93defcede3adda0984be0172",
317
- "gitHead": "e48bd2085b8b7e9a93defcede3adda0984be0172",
318
- "lisaReleaseTag": "v4.33.4"
316
+ "lisaReleaseCommit": "384b68c7af30a0c6a27ea50978b723b470f6aaaa",
317
+ "gitHead": "384b68c7af30a0c6a27ea50978b723b470f6aaaa",
318
+ "lisaReleaseTag": "v4.33.6"
319
319
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa",
3
- "version": "4.33.4",
3
+ "version": "4.33.6",
4
4
  "description": "Universal governance — agents, skills, commands, hooks, and rules for all projects",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa",
3
- "version": "4.33.4",
3
+ "version": "4.33.6",
4
4
  "description": "Universal governance: agents, skills, commands, hooks, and rules for all projects.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa",
3
- "version": "4.33.4",
3
+ "version": "4.33.6",
4
4
  "description": "Universal governance — agents, skills, commands, hooks, and rules for all projects",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-cdk",
3
- "version": "4.33.4",
3
+ "version": "4.33.6",
4
4
  "description": "AWS CDK-specific plugin",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-cdk",
3
- "version": "4.33.4",
3
+ "version": "4.33.6",
4
4
  "description": "AWS CDK-specific Lisa plugin.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-cdk",
3
- "version": "4.33.4",
3
+ "version": "4.33.6",
4
4
  "description": "AWS CDK-specific plugin",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-cdk",
3
- "version": "4.33.4",
3
+ "version": "4.33.6",
4
4
  "description": "AWS CDK-specific plugin",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-cdk",
3
- "version": "4.33.4",
3
+ "version": "4.33.6",
4
4
  "description": "AWS CDK-specific plugin",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa",
3
- "version": "4.33.4",
3
+ "version": "4.33.6",
4
4
  "description": "Universal governance — agents, skills, commands, hooks, and rules for all projects",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa",
3
- "version": "4.33.4",
3
+ "version": "4.33.6",
4
4
  "description": "Universal governance — agents, skills, commands, hooks, and rules for all projects",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-expo",
3
- "version": "4.33.4",
3
+ "version": "4.33.6",
4
4
  "description": "Expo/React Native-specific skills, agents, rules, and MCP servers",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-expo",
3
- "version": "4.33.4",
3
+ "version": "4.33.6",
4
4
  "description": "Expo and React Native-specific skills, agents, rules, and MCP servers.",
5
5
  "author": {
6
6
  "name": "Cody Swann"