@codyswann/lisa 2.338.4 → 2.339.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.
Files changed (72) hide show
  1. package/dist/core/upstream-evidence-manifest.d.ts.map +1 -1
  2. package/dist/core/upstream-evidence-manifest.js +5 -3
  3. package/dist/core/upstream-evidence-manifest.js.map +1 -1
  4. package/package.json +1 -1
  5. package/plugins/lisa/.claude-plugin/plugin.json +1 -1
  6. package/plugins/lisa/.codex-plugin/plugin.json +1 -1
  7. package/plugins/lisa/.codex-plugin/skills/lisa-secrets-access/scripts/providers.mjs +23 -0
  8. package/plugins/lisa/.codex-plugin/skills/lisa-secrets-access/scripts/surfaces.mjs +68 -4
  9. package/plugins/lisa/.codex-plugin/skills/lisa-setup-remote-env/scripts/setup-remote-env.mjs +117 -8
  10. package/plugins/lisa/skills/lisa-secrets-access/scripts/providers.mjs +23 -0
  11. package/plugins/lisa/skills/lisa-secrets-access/scripts/surfaces.mjs +68 -4
  12. package/plugins/lisa/skills/lisa-setup-remote-env/scripts/setup-remote-env.mjs +117 -8
  13. package/plugins/lisa-agy/plugin.json +1 -1
  14. package/plugins/lisa-agy/skills/lisa-secrets-access/scripts/providers.mjs +23 -0
  15. package/plugins/lisa-agy/skills/lisa-secrets-access/scripts/surfaces.mjs +68 -4
  16. package/plugins/lisa-agy/skills/lisa-setup-remote-env/scripts/setup-remote-env.mjs +117 -8
  17. package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
  18. package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
  19. package/plugins/lisa-cdk-agy/plugin.json +1 -1
  20. package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
  21. package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
  22. package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
  23. package/plugins/lisa-copilot/skills/lisa-secrets-access/scripts/providers.mjs +23 -0
  24. package/plugins/lisa-copilot/skills/lisa-secrets-access/scripts/surfaces.mjs +68 -4
  25. package/plugins/lisa-copilot/skills/lisa-setup-remote-env/scripts/setup-remote-env.mjs +117 -8
  26. package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
  27. package/plugins/lisa-cursor/skills/lisa-secrets-access/scripts/providers.mjs +23 -0
  28. package/plugins/lisa-cursor/skills/lisa-secrets-access/scripts/surfaces.mjs +68 -4
  29. package/plugins/lisa-cursor/skills/lisa-setup-remote-env/scripts/setup-remote-env.mjs +117 -8
  30. package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
  31. package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
  32. package/plugins/lisa-expo-agy/plugin.json +1 -1
  33. package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
  34. package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
  35. package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
  36. package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
  37. package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
  38. package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
  39. package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
  40. package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
  41. package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
  42. package/plugins/lisa-nestjs-agy/plugin.json +1 -1
  43. package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
  44. package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
  45. package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
  46. package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
  47. package/plugins/lisa-openclaw-agy/plugin.json +1 -1
  48. package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
  49. package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
  50. package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
  51. package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
  52. package/plugins/lisa-phaser-agy/plugin.json +1 -1
  53. package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
  54. package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
  55. package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
  56. package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
  57. package/plugins/lisa-rails-agy/plugin.json +1 -1
  58. package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
  59. package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
  60. package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
  61. package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
  62. package/plugins/lisa-typescript-agy/plugin.json +1 -1
  63. package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
  64. package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
  65. package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
  66. package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
  67. package/plugins/lisa-wiki-agy/plugin.json +1 -1
  68. package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
  69. package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
  70. package/plugins/src/base/skills/lisa-secrets-access/scripts/providers.mjs +23 -0
  71. package/plugins/src/base/skills/lisa-secrets-access/scripts/surfaces.mjs +68 -4
  72. package/plugins/src/base/skills/lisa-setup-remote-env/scripts/setup-remote-env.mjs +117 -8
@@ -51,6 +51,29 @@ const PROVIDER_BOOTSTRAP_ENV = {
51
51
  doppler: "DOPPLER_TOKEN",
52
52
  };
53
53
 
54
+ /**
55
+ * The tenant-scoped bootstrap variable a provider expects, by convention.
56
+ *
57
+ * Exists so the repo-less path cannot invent one. It used to compose
58
+ * `BWS_ACCESS_TOKEN_<namespace>` literally, ignoring the provider it had just
59
+ * resolved — so a Doppler tenant with no checkout was told to set a Bitwarden
60
+ * variable, and its CLI failed with "Missing access token". That is precisely
61
+ * the confusion the two questions above are separated to prevent, reintroduced
62
+ * on the one surface with no config file to correct it.
63
+ *
64
+ * `null` for a provider with no env-var bootstrap — 1Password, Vault and AWS
65
+ * authenticate by other means — because a name invented for them would be a
66
+ * variable nothing reads, which is worse than admitting there is none.
67
+ * `providerEnv` already treats an unmapped provider as "inject nothing".
68
+ * @param {string} provider Provider name.
69
+ * @param {string} namespace Tenant namespace.
70
+ * @returns {string|null} The variable to set, or null when the provider has none.
71
+ */
72
+ export function bootstrapKeyFor(provider, namespace) {
73
+ const canonical = PROVIDER_BOOTSTRAP_ENV[provider];
74
+ return canonical ? `${canonical}_${namespace}` : null;
75
+ }
76
+
54
77
  /**
55
78
  * Build the child environment for a provider CLI, injecting the bootstrap under
56
79
  * the name that CLI actually reads.
@@ -17,6 +17,8 @@ import { existsSync, readFileSync } from "node:fs";
17
17
  import { homedir } from "node:os";
18
18
  import { join } from "node:path";
19
19
 
20
+ import { bootstrapKeyFor } from "./providers.mjs";
21
+
20
22
  /**
21
23
  * Capabilities, per surface.
22
24
  *
@@ -79,6 +81,28 @@ export const SURFACES = {
79
81
  // recoverable; an absent one means the environment does not work.
80
82
  materializeAt: "both",
81
83
  },
84
+ // A container an operator builds and runs themselves — locally, or anywhere
85
+ // Lisa is not the one provisioning the host.
86
+ //
87
+ // It exists because `local` was standing in for it, and `local` is
88
+ // `materialized: false`. That default is right for a human at a keyboard: the
89
+ // provider CLI is authenticated, secrets resolve live, and writing them to
90
+ // disk would be a second copy to keep honest. A container has no keychain and
91
+ // dies with its filesystem, so the same default leaves it with the CLIs
92
+ // installed and nothing to authenticate them.
93
+ //
94
+ // The workaround was to claim `claude-web`, which materializes correctly and
95
+ // is a lie — it made a local container indistinguishable from a cloud session
96
+ // in every diagnostic and log.
97
+ //
98
+ // `setup` rather than `both`: a container has no session-start hook to run,
99
+ // and it is started fresh rather than resumed from a vendor's cache, so the
100
+ // start of the container IS the refresh.
101
+ container: {
102
+ materialized: true,
103
+ mayWriteValues: true,
104
+ materializeAt: "setup",
105
+ },
82
106
  };
83
107
 
84
108
  /** Config defaults when `.lisa.config.json` carries no `secrets` block. */
@@ -185,17 +209,49 @@ export function readConfig(cwd = process.cwd(), env = process.env) {
185
209
  throw new Error(`.lisa.config.json is not readable: ${err.message}`);
186
210
  }
187
211
  if (!cfg) return withSurface(DEFAULTS);
212
+ const provider = cfg.provider ?? DEFAULTS.provider;
213
+ const namespace = assertNamespace(cfg.namespace ?? DEFAULTS.namespace);
188
214
  return withSurface({
189
- provider: cfg.provider ?? DEFAULTS.provider,
190
- bootstrap: { ...DEFAULTS.bootstrap, ...(cfg.bootstrap ?? {}) },
215
+ provider,
216
+ bootstrap: resolveBootstrap(cfg.bootstrap, provider, namespace),
191
217
  require: cfg.require ?? null,
192
218
  rotating: cfg.rotating ?? [],
193
- namespace: assertNamespace(cfg.namespace ?? DEFAULTS.namespace),
219
+ namespace,
194
220
  narrow: { ...DEFAULTS.narrow, ...(cfg.narrow ?? {}) },
195
221
  surface: cfg.surface ?? null,
196
222
  });
197
223
  }
198
224
 
225
+ /**
226
+ * Fill in the bootstrap block a project did not spell out.
227
+ *
228
+ * Both defaults exist because a token stored on the machine was going unread.
229
+ *
230
+ * **The key** follows the same convention the repo-less path uses,
231
+ * `<PROVIDER_VAR>_<namespace>`, so a machine provisioned once serves every
232
+ * project of that tenant without each restating the name. A project that sets
233
+ * one keeps it: a name that is not derivable is a legitimate choice, and
234
+ * overriding it would point sessions at a variable nobody set.
235
+ *
236
+ * **The sources** gain `keychain` because `["env"]` alone meant a token in the
237
+ * OS keychain was never consulted — so the machine setup appeared to work and
238
+ * every project still failed until someone hand-edited its config. Env stays
239
+ * first, so a CI runner injecting the bootstrap never reaches for a local store.
240
+ * On a platform with no keychain the extra source reads as empty and costs
241
+ * nothing.
242
+ * @param {object|undefined} bootstrap The project's own block, if any.
243
+ * @param {string} provider Resolved provider name.
244
+ * @param {string} namespace Resolved namespace.
245
+ * @returns {{sources: string[], key: string|null}} The resolved block.
246
+ */
247
+ function resolveBootstrap(bootstrap, provider, namespace) {
248
+ const given = bootstrap ?? {};
249
+ return {
250
+ sources: given.sources ?? ["env", "keychain"],
251
+ key: given.key ?? bootstrapKeyFor(provider, namespace),
252
+ };
253
+ }
254
+
199
255
  /**
200
256
  * Build a configuration from the environment, for sessions with no checkout.
201
257
  *
@@ -237,10 +293,18 @@ function fromEnvironment(env) {
237
293
  namespace: assertNamespace(namespace),
238
294
  bootstrap: {
239
295
  ...DEFAULTS.bootstrap,
296
+ // Derived from the provider resolved just above, not from Bitwarden's
297
+ // name. Hardcoding it told a Doppler tenant to set BWS_ACCESS_TOKEN_<ns>,
298
+ // which its CLI has never heard of — on the one surface that has no
299
+ // config file to override the guess.
300
+ // Same two defaults as a project config gets, for the same reasons —
301
+ // see `resolveBootstrap`. Stated here too because this path never reads
302
+ // a file, so it cannot inherit them.
303
+ sources: ["env", "keychain"],
240
304
  key:
241
305
  env.LISA_BOOTSTRAP_KEY ??
242
306
  env.LISA_SECRETS_BOOTSTRAP_KEY ??
243
- `BWS_ACCESS_TOKEN_${namespace}`,
307
+ bootstrapKeyFor(provider, namespace),
244
308
  },
245
309
  };
246
310
  }
@@ -885,6 +885,52 @@ export function pinEnvironment(environmentId, cwd = process.cwd()) {
885
885
  return path;
886
886
  }
887
887
 
888
+ /**
889
+ * The lines an operator puts above the field, for a session with no checkout.
890
+ *
891
+ * Emitted as shell that runs, not as a template with holes: every hole is a
892
+ * hand substitution in a settings box with no review, which is the failure this
893
+ * whole file exists to prevent. So a provider that cannot be bootstrapped by an
894
+ * environment variable gets a sentence explaining that, not an `export` of a
895
+ * placeholder — a line that would be a syntax error if pasted.
896
+ *
897
+ * `LISA_PROVIDER` is exported even though the field defaults it, because the
898
+ * field's default is Bitwarden. A Doppler tenant that omits it gets `bws`
899
+ * installed and its own CLI missing, having configured everything correctly.
900
+ * @param {object} options Resolved identity and display strings.
901
+ * @returns {string[]} Lines to include in the emitted guidance.
902
+ */
903
+ function repoLessExports({ namespace, key, provider, bootstrapKey, tenant }) {
904
+ // Two different reasons the key can be missing, and telling them apart is the
905
+ // whole value of the message. "Nothing was named" is the operator's to fix by
906
+ // re-running; "this provider has no such variable" is not fixable at all and
907
+ // must not read as an omission.
908
+ if (!tenant) {
909
+ return [
910
+ " # Re-run with --tenant=<name> and these are filled in for you.",
911
+ ` export LISA_TENANT=${namespace}`,
912
+ " export <bootstrap variable>='<read from your credential manager>'",
913
+ " export LISA_SECRETS_SURFACE=claude-web",
914
+ ];
915
+ }
916
+ if (!bootstrapKey) {
917
+ return [
918
+ ` # ${provider} has no environment-variable bootstrap, so there is`,
919
+ " # nothing to export for it. Authenticate it the way its own CLI",
920
+ " # expects, then set the three below.",
921
+ ` export LISA_TENANT=${namespace}`,
922
+ ` export LISA_PROVIDER=${provider}`,
923
+ " export LISA_SECRETS_SURFACE=claude-web",
924
+ ];
925
+ }
926
+ return [
927
+ ` export LISA_TENANT=${namespace}`,
928
+ ` export ${key}='<the same value as above>'`,
929
+ ...(provider ? [` export LISA_PROVIDER=${provider}`] : []),
930
+ " export LISA_SECRETS_SURFACE=claude-web",
931
+ ];
932
+ }
933
+
888
934
  /**
889
935
  * Produce the configuration a human pastes to provision a Claude cloud surface.
890
936
  *
@@ -893,11 +939,16 @@ export function pinEnvironment(environmentId, cwd = process.cwd()) {
893
939
  * settings page, no direct URL, and no endpoint. Emit is not a degraded option
894
940
  * here, it is the only one — so the read-back in `verify-remote-env.mjs` is
895
941
  * what makes the result trustworthy, exactly as it would be at any other tier.
896
- * @param {{bootstrapKey: string|null}} options Project details.
942
+ * @param {{bootstrapKey: string|null, tenant?: string|null, provider?: string}} options Project details.
897
943
  * @returns {string} Text to show the operator.
898
944
  */
899
- export function emitClaudeWeb({ bootstrapKey }) {
900
- const key = bootstrapKey ?? "<secrets.bootstrap.key is not configured>";
945
+ export function emitClaudeWeb({
946
+ bootstrapKey,
947
+ tenant = null,
948
+ provider = null,
949
+ }) {
950
+ const key = bootstrapKey ?? "<not resolved>";
951
+ const namespace = tenant ?? "<your namespace>";
901
952
  return [
902
953
  "Provisioning tier: EMIT — and for this surface that is the only tier.",
903
954
  " A Claude cloud environment is account-scoped configuration edited in the",
@@ -924,11 +975,9 @@ export function emitClaudeWeb({ bootstrapKey }) {
924
975
  " while the image is being built, before the environment exists. So a",
925
976
  " session with no checkout has to be told its tenant inside the script",
926
977
  " itself, or the field finds nothing to prepare and exits 0 looking like",
927
- " success. Put these three lines FIRST, then the line after them:",
978
+ " success. Put the lines below FIRST, then the field after them:",
928
979
  "",
929
- " export LISA_TENANT=<your namespace>",
930
- ` export ${key}='<the same value as above>'`,
931
- " export LISA_SECRETS_SURFACE=claude-web",
980
+ ...repoLessExports({ namespace, key, provider, bootstrapKey, tenant }),
932
981
  "",
933
982
  " LISA_SECRETS_SURFACE is set explicitly rather than left to detection:",
934
983
  " detection keys off CLAUDE_CODE_REMOTE, and a surface that does not set",
@@ -1096,6 +1145,66 @@ function readBootstrapKey(cwd = process.cwd()) {
1096
1145
  return JSON.parse(readFileSync(path, "utf8")).secrets?.bootstrap?.key ?? null;
1097
1146
  }
1098
1147
 
1148
+ /**
1149
+ * Work out what to tell the operator to export, for a surface that has no repo.
1150
+ *
1151
+ * Emitting used to read the key out of `.lisa.config.json` in the working
1152
+ * directory, which made the command that configures a REPO-LESS environment
1153
+ * require a repository — so it was run from a checkout that happened to be
1154
+ * nearby, or it printed a placeholder the operator substituted by hand.
1155
+ *
1156
+ * Explicit flags win, then the environment, then a checkout if the caller
1157
+ * happens to be standing in one, then the convention. The provider matters
1158
+ * because it decides the variable's name: `bws` reads `BWS_ACCESS_TOKEN` and
1159
+ * `doppler` reads `DOPPLER_TOKEN`, so a tenant on one told to export the
1160
+ * other's name gets "Missing access token" from a CLI it did configure.
1161
+ * @param {string[]} argv Process arguments.
1162
+ * @param {Record<string, string|undefined>} [env] Environment to read.
1163
+ * @param {string} [cwd] Directory to look for a config in.
1164
+ * @returns {Promise<{tenant: string|null, provider: string, bootstrapKey: string|null}>} Resolved identity.
1165
+ */
1166
+ export async function resolveEmitTarget(
1167
+ argv,
1168
+ env = process.env,
1169
+ cwd = process.cwd()
1170
+ ) {
1171
+ const flag = name =>
1172
+ argv.find(a => a.startsWith(`--${name}=`))?.slice(name.length + 3) ?? null;
1173
+
1174
+ const tenant =
1175
+ flag("tenant") ||
1176
+ (env.LISA_TENANT ?? env.LISA_SECRETS_NAMESPACE ?? "").trim() ||
1177
+ null;
1178
+ const provider =
1179
+ flag("provider") ||
1180
+ (env.LISA_PROVIDER ?? env.LISA_SECRETS_PROVIDER ?? "").trim() ||
1181
+ "bitwarden";
1182
+
1183
+ // Validated before it can reach the output, by the same rule the runtime path
1184
+ // applies. This one becomes `export LISA_TENANT=<value>` in a block an
1185
+ // operator pastes verbatim, so `my tenant` or `has$var` would emit shell that
1186
+ // breaks — or worse, expands — where nothing reviews it. Refusing here costs
1187
+ // a re-run; emitting it costs a container nobody can explain.
1188
+ if (tenant) {
1189
+ const { assertNamespace } = await import(
1190
+ pathToFileURL(siblingScript("lisa-secrets-access", "surfaces.mjs")).href
1191
+ );
1192
+ assertNamespace(tenant);
1193
+ }
1194
+
1195
+ // A configured key outranks the convention: a project may legitimately use a
1196
+ // name that is not derivable, and this command must not tell its operator to
1197
+ // set a different one from the one their sessions actually read.
1198
+ const configured = readBootstrapKey(cwd);
1199
+ if (configured) return { tenant, provider, bootstrapKey: configured };
1200
+ if (!tenant) return { tenant, provider, bootstrapKey: null };
1201
+
1202
+ const { bootstrapKeyFor } = await import(
1203
+ pathToFileURL(siblingScript("lisa-secrets-access", "providers.mjs")).href
1204
+ );
1205
+ return { tenant, provider, bootstrapKey: bootstrapKeyFor(provider, tenant) };
1206
+ }
1207
+
1099
1208
  async function main() {
1100
1209
  // `--print-tools` answers "which CLIs does this secret set imply", reading
1101
1210
  // the notes materialized alongside the values.
@@ -1170,7 +1279,7 @@ async function main() {
1170
1279
  `Emitting is implemented for claude-web, which has no other tier.`
1171
1280
  );
1172
1281
  }
1173
- console.log(emitClaudeWeb({ bootstrapKey: readBootstrapKey() }));
1282
+ console.log(emitClaudeWeb(await resolveEmitTarget(process.argv)));
1174
1283
  return;
1175
1284
  }
1176
1285
 
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-expo",
3
- "version": "2.338.4",
3
+ "version": "2.339.0",
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": "2.338.4",
3
+ "version": "2.339.0",
4
4
  "description": "Expo and 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": "2.338.4",
3
+ "version": "2.339.0",
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": "2.338.4",
3
+ "version": "2.339.0",
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": "2.338.4",
3
+ "version": "2.339.0",
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-harper-fabric",
3
- "version": "2.338.4",
3
+ "version": "2.339.0",
4
4
  "description": "Harper/Fabric-specific rules for TypeScript component apps",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-harper-fabric",
3
- "version": "2.338.4",
3
+ "version": "2.339.0",
4
4
  "description": "Harper/Fabric-specific Lisa rules for TypeScript component apps.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-harper-fabric",
3
- "version": "2.338.4",
3
+ "version": "2.339.0",
4
4
  "description": "Harper/Fabric-specific rules for TypeScript component apps",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-harper-fabric",
3
- "version": "2.338.4",
3
+ "version": "2.339.0",
4
4
  "description": "Harper/Fabric-specific rules for TypeScript component apps",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-harper-fabric",
3
- "version": "2.338.4",
3
+ "version": "2.339.0",
4
4
  "description": "Harper/Fabric-specific rules for TypeScript component apps",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-nestjs",
3
- "version": "2.338.4",
3
+ "version": "2.339.0",
4
4
  "description": "NestJS-specific skills (GraphQL, TypeORM) and hooks (migration write-protection)",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-nestjs",
3
- "version": "2.338.4",
3
+ "version": "2.339.0",
4
4
  "description": "NestJS-specific skills and migration write-protection hooks.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-nestjs",
3
- "version": "2.338.4",
3
+ "version": "2.339.0",
4
4
  "description": "NestJS-specific skills (GraphQL, TypeORM) and hooks (migration write-protection)",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-nestjs",
3
- "version": "2.338.4",
3
+ "version": "2.339.0",
4
4
  "description": "NestJS-specific skills (GraphQL, TypeORM) and hooks (migration write-protection)",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-nestjs",
3
- "version": "2.338.4",
3
+ "version": "2.339.0",
4
4
  "description": "NestJS-specific skills (GraphQL, TypeORM) and hooks (migration write-protection)",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-openclaw",
3
- "version": "2.338.4",
3
+ "version": "2.339.0",
4
4
  "description": "Connect staff roles to Telegram or Slack via OpenClaw — facilitator/specialist hub-and-spoke routing and repo-coding topics, for Claude Code and Codex",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-openclaw",
3
- "version": "2.338.4",
3
+ "version": "2.339.0",
4
4
  "description": "Connect staff roles to Telegram or Slack via OpenClaw — facilitator/specialist hub-and-spoke routing and repo-coding topics, across Claude and Codex.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-openclaw",
3
- "version": "2.338.4",
3
+ "version": "2.339.0",
4
4
  "description": "Connect staff roles to Telegram or Slack via OpenClaw — facilitator/specialist hub-and-spoke routing and repo-coding topics, for Claude Code and Codex",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-openclaw",
3
- "version": "2.338.4",
3
+ "version": "2.339.0",
4
4
  "description": "Connect staff roles to Telegram or Slack via OpenClaw — facilitator/specialist hub-and-spoke routing and repo-coding topics, for Claude Code and Codex",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-openclaw",
3
- "version": "2.338.4",
3
+ "version": "2.339.0",
4
4
  "description": "Connect staff roles to Telegram or Slack via OpenClaw — facilitator/specialist hub-and-spoke routing and repo-coding topics, for Claude Code and Codex",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-phaser",
3
- "version": "2.338.4",
3
+ "version": "2.339.0",
4
4
  "description": "Phaser 4 game-development rules for TypeScript projects",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-phaser",
3
- "version": "2.338.4",
3
+ "version": "2.339.0",
4
4
  "description": "Phaser 4 game-development rules for TypeScript projects",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-phaser",
3
- "version": "2.338.4",
3
+ "version": "2.339.0",
4
4
  "description": "Phaser 4 game-development rules for TypeScript projects",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-phaser",
3
- "version": "2.338.4",
3
+ "version": "2.339.0",
4
4
  "description": "Phaser 4 game-development rules for TypeScript projects",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-phaser",
3
- "version": "2.338.4",
3
+ "version": "2.339.0",
4
4
  "description": "Phaser 4 game-development rules for TypeScript projects",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-rails",
3
- "version": "2.338.4",
3
+ "version": "2.339.0",
4
4
  "description": "Ruby on Rails-specific hooks — RuboCop linting/formatting and ast-grep scanning on edit",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-rails",
3
- "version": "2.338.4",
3
+ "version": "2.339.0",
4
4
  "description": "Ruby on Rails-specific skills and hooks for RuboCop and ast-grep scanning on edit.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-rails",
3
- "version": "2.338.4",
3
+ "version": "2.339.0",
4
4
  "description": "Ruby on Rails-specific hooks — RuboCop linting/formatting and ast-grep scanning on edit",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-rails",
3
- "version": "2.338.4",
3
+ "version": "2.339.0",
4
4
  "description": "Ruby on Rails-specific hooks — RuboCop linting/formatting and ast-grep scanning on edit",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-rails",
3
- "version": "2.338.4",
3
+ "version": "2.339.0",
4
4
  "description": "Ruby on Rails-specific hooks — RuboCop linting/formatting and ast-grep scanning on edit",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-typescript",
3
- "version": "2.338.4",
3
+ "version": "2.339.0",
4
4
  "description": "TypeScript-specific hooks — Prettier formatting, ESLint linting, ast-grep scanning, and error-suppression blocking on edit",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-typescript",
3
- "version": "2.338.4",
3
+ "version": "2.339.0",
4
4
  "description": "TypeScript-specific hooks for formatting, linting, and ast-grep scanning on edit.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-typescript",
3
- "version": "2.338.4",
3
+ "version": "2.339.0",
4
4
  "description": "TypeScript-specific hooks — Prettier formatting, ESLint linting, ast-grep scanning, and error-suppression blocking on edit",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-typescript",
3
- "version": "2.338.4",
3
+ "version": "2.339.0",
4
4
  "description": "TypeScript-specific hooks — Prettier formatting, ESLint linting, ast-grep scanning, and error-suppression blocking on edit",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-typescript",
3
- "version": "2.338.4",
3
+ "version": "2.339.0",
4
4
  "description": "TypeScript-specific hooks — Prettier formatting, ESLint linting, ast-grep scanning, and error-suppression blocking on edit",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-wiki",
3
- "version": "2.338.4",
3
+ "version": "2.339.0",
4
4
  "description": "LLM Wiki — a distributable, git-native markdown knowledge base for Claude Code and Codex",
5
5
  "author": {
6
6
  "name": "Cody Swann"