@codyswann/lisa 2.227.0 → 2.228.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 (63) hide show
  1. package/all/copy-contents/gitignore +2 -0
  2. package/dist/cli/ui-cmd.d.ts +21 -1
  3. package/dist/cli/ui-cmd.d.ts.map +1 -1
  4. package/dist/cli/ui-cmd.js +49 -3
  5. package/dist/cli/ui-cmd.js.map +1 -1
  6. package/dist/core/lisa.d.ts +74 -1
  7. package/dist/core/lisa.d.ts.map +1 -1
  8. package/dist/core/lisa.js +165 -25
  9. package/dist/core/lisa.js.map +1 -1
  10. package/package.json +1 -1
  11. package/plugins/lisa/.claude-plugin/plugin.json +1 -1
  12. package/plugins/lisa/.codex-plugin/plugin.json +1 -1
  13. package/plugins/lisa-agy/plugin.json +1 -1
  14. package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
  15. package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
  16. package/plugins/lisa-cdk-agy/plugin.json +1 -1
  17. package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
  18. package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
  19. package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
  20. package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
  21. package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
  22. package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
  23. package/plugins/lisa-expo-agy/plugin.json +1 -1
  24. package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
  25. package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
  26. package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
  27. package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
  28. package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
  29. package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
  30. package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
  31. package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
  32. package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
  33. package/plugins/lisa-nestjs-agy/plugin.json +1 -1
  34. package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
  35. package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
  36. package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
  37. package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
  38. package/plugins/lisa-openclaw-agy/plugin.json +1 -1
  39. package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
  40. package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
  41. package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
  42. package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
  43. package/plugins/lisa-phaser-agy/plugin.json +1 -1
  44. package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
  45. package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
  46. package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
  47. package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
  48. package/plugins/lisa-rails-agy/plugin.json +1 -1
  49. package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
  50. package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
  51. package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
  52. package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
  53. package/plugins/lisa-typescript-agy/plugin.json +1 -1
  54. package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
  55. package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
  56. package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
  57. package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
  58. package/plugins/lisa-wiki-agy/plugin.json +1 -1
  59. package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
  60. package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
  61. package/scripts/install-claude-plugins.sh +89 -9
  62. package/ui/README.md +1 -0
  63. package/ui/index.html +143 -8
@@ -170,6 +170,8 @@ tasks.json
170
170
  **/.claude/debug/
171
171
  **/.claude/worktrees/
172
172
  **/.claude/scheduled_tasks.lock
173
+ **/.claude/.lisa-plugins-synced
174
+ **/.claude/.lisa-marketplace-heal-v2
173
175
  .roo
174
176
  .roo/
175
177
  .roomodes
@@ -9,6 +9,25 @@ export interface UiCmdOptions {
9
9
  /** Set false (via --no-sync) to skip the config sync on startup */
10
10
  readonly sync?: boolean;
11
11
  }
12
+ /** Secret/variable and generated-artifact status exposed to the console. */
13
+ export interface RemoteEnvironmentStatus {
14
+ /** Whether each supported variable is visible to the `lisa ui` process. */
15
+ readonly variables: Readonly<Record<string, boolean>>;
16
+ /** Whether each remote-environment startup artifact exists in the project. */
17
+ readonly artifacts: Readonly<Record<string, boolean>>;
18
+ }
19
+ /** Variables the remote AWS bootstrap understands. Values are never exposed. */
20
+ export declare const REMOTE_ENVIRONMENT_VARIABLES: readonly ["LISA_AWS_BOOTSTRAP_JSON", "LISA_REMOTE_AGENT", "LISA_AWS_DEFAULT_PROFILE"];
21
+ /** Project-relative artifacts used by remote coding environments. */
22
+ export declare const REMOTE_ENVIRONMENT_ARTIFACTS: readonly ["scripts/remote-agent-aws-setup.sh", ".cursor/environment.json", ".github/workflows/copilot-setup-steps.yml"];
23
+ /**
24
+ * Inspect remote-environment readiness without ever reading a variable value
25
+ * into the browser payload.
26
+ * @param destDir - Project root
27
+ * @param environment - Environment visible to the Lisa process
28
+ * @returns Boolean-only variable and artifact status
29
+ */
30
+ export declare function inspectRemoteEnvironment(destDir: string, environment?: NodeJS.ProcessEnv): RemoteEnvironmentStatus;
12
31
  /**
13
32
  * Locate the packaged `ui/index.html` by walking up from this module until a
14
33
  * directory containing `ui/index.html` is found (works from both `src/` and
@@ -22,9 +41,10 @@ export declare function findUiHtml(startDir: string): string;
22
41
  * escaped so a value containing `</script>` cannot break out of the tag.
23
42
  * @param html - Packaged console HTML
24
43
  * @param config - Merged live config
44
+ * @param remoteEnvironment - Boolean-only secret and startup-artifact status
25
45
  * @returns HTML with the injected config
26
46
  */
27
- export declare function injectLiveConfig(html: string, config: JsonObject): string;
47
+ export declare function injectLiveConfig(html: string, config: JsonObject, remoteEnvironment?: RemoteEnvironmentStatus): string;
28
48
  /**
29
49
  * Run the `lisa ui` command: sync, then serve the console until interrupted.
30
50
  * @param targetPath - Project path argument (defaults to the current directory)
@@ -1 +1 @@
1
- {"version":3,"file":"ui-cmd.d.ts","sourceRoot":"","sources":["../../src/cli/ui-cmd.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAIlC,OAAO,EAAgB,KAAK,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAIrE,6CAA6C;AAC7C,eAAO,MAAM,eAAe,OAAO,CAAC;AAEpC,iCAAiC;AACjC,MAAM,WAAW,YAAY;IAC3B,4EAA4E;IAC5E,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,mEAAmE;IACnE,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC;CACzB;AAED;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAUnD;AAmBD;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,GAAG,MAAM,CAIzE;AAED;;;;;GAKG;AACH,wBAAsB,KAAK,CACzB,UAAU,EAAE,MAAM,GAAG,SAAS,EAC9B,OAAO,GAAE,YAAiB,GACzB,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAoCtB"}
1
+ {"version":3,"file":"ui-cmd.d.ts","sourceRoot":"","sources":["../../src/cli/ui-cmd.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAIlC,OAAO,EAAgB,KAAK,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAIrE,6CAA6C;AAC7C,eAAO,MAAM,eAAe,OAAO,CAAC;AAEpC,iCAAiC;AACjC,MAAM,WAAW,YAAY;IAC3B,4EAA4E;IAC5E,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,mEAAmE;IACnE,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC;CACzB;AAED,4EAA4E;AAC5E,MAAM,WAAW,uBAAuB;IACtC,2EAA2E;IAC3E,QAAQ,CAAC,SAAS,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IACtD,8EAA8E;IAC9E,QAAQ,CAAC,SAAS,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;CACvD;AAED,gFAAgF;AAChF,eAAO,MAAM,4BAA4B,uFAI/B,CAAC;AAEX,qEAAqE;AACrE,eAAO,MAAM,4BAA4B,yHAI/B,CAAC;AAYX;;;;;;GAMG;AACH,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,MAAM,EACf,WAAW,GAAE,MAAM,CAAC,UAAoC,GACvD,uBAAuB,CAezB;AAED;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAUnD;AAmBD;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,UAAU,EAClB,iBAAiB,GAAE,uBAGlB,GACA,MAAM,CAQR;AAED;;;;;GAKG;AACH,wBAAsB,KAAK,CACzB,UAAU,EAAE,MAAM,GAAG,SAAS,EAC9B,OAAO,GAAE,YAAiB,GACzB,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAqCtB"}
@@ -18,6 +18,46 @@ import { deepMerge, readJsonOrNull } from "../utils/index.js";
18
18
  import { printSyncReport } from "./sync-cmd.js";
19
19
  /** Default port for the settings console. */
20
20
  export const DEFAULT_UI_PORT = 4780;
21
+ /** Variables the remote AWS bootstrap understands. Values are never exposed. */
22
+ export const REMOTE_ENVIRONMENT_VARIABLES = [
23
+ "LISA_AWS_BOOTSTRAP_JSON",
24
+ "LISA_REMOTE_AGENT",
25
+ "LISA_AWS_DEFAULT_PROFILE",
26
+ ];
27
+ /** Project-relative artifacts used by remote coding environments. */
28
+ export const REMOTE_ENVIRONMENT_ARTIFACTS = [
29
+ "scripts/remote-agent-aws-setup.sh",
30
+ ".cursor/environment.json",
31
+ ".github/workflows/copilot-setup-steps.yml",
32
+ ];
33
+ /**
34
+ * Read the CLI process environment through one explicit, reviewable exception.
35
+ * Only boolean presence checks derived from this map reach the browser.
36
+ * @returns Current process environment
37
+ */
38
+ function getProcessEnvironment() {
39
+ // eslint-disable-next-line no-restricted-syntax -- CLI status view must inspect externally supplied remote-environment variables once
40
+ return process.env;
41
+ }
42
+ /**
43
+ * Inspect remote-environment readiness without ever reading a variable value
44
+ * into the browser payload.
45
+ * @param destDir - Project root
46
+ * @param environment - Environment visible to the Lisa process
47
+ * @returns Boolean-only variable and artifact status
48
+ */
49
+ export function inspectRemoteEnvironment(destDir, environment = getProcessEnvironment()) {
50
+ return {
51
+ variables: Object.fromEntries(REMOTE_ENVIRONMENT_VARIABLES.map(name => [
52
+ name,
53
+ typeof environment[name] === "string" && environment[name].length > 0,
54
+ ])),
55
+ artifacts: Object.fromEntries(REMOTE_ENVIRONMENT_ARTIFACTS.map(relativePath => [
56
+ relativePath,
57
+ existsSync(path.join(destDir, relativePath)),
58
+ ])),
59
+ };
60
+ }
21
61
  /**
22
62
  * Locate the packaged `ui/index.html` by walking up from this module until a
23
63
  * directory containing `ui/index.html` is found (works from both `src/` and
@@ -53,11 +93,16 @@ async function readMergedConfig(destDir) {
53
93
  * escaped so a value containing `</script>` cannot break out of the tag.
54
94
  * @param html - Packaged console HTML
55
95
  * @param config - Merged live config
96
+ * @param remoteEnvironment - Boolean-only secret and startup-artifact status
56
97
  * @returns HTML with the injected config
57
98
  */
58
- export function injectLiveConfig(html, config) {
99
+ export function injectLiveConfig(html, config, remoteEnvironment = {
100
+ variables: {},
101
+ artifacts: {},
102
+ }) {
59
103
  const payload = JSON.stringify(config).replace(/<\//g, "<\\/");
60
- const tag = `<script>window.LISA_LIVE_CONFIG = ${payload};</script>`;
104
+ const remotePayload = JSON.stringify(remoteEnvironment).replace(/<\//g, "<\\/");
105
+ const tag = `<script>window.LISA_LIVE_CONFIG = ${payload}; window.LISA_REMOTE_ENVIRONMENT = ${remotePayload};</script>`;
61
106
  return html.replace("</body>", `${tag}\n</body>`);
62
107
  }
63
108
  /**
@@ -80,7 +125,8 @@ export async function runUi(targetPath, options = {}) {
80
125
  const htmlPath = findUiHtml(moduleDir);
81
126
  const html = await readFile(htmlPath, "utf8");
82
127
  const config = await readMergedConfig(destDir);
83
- const page = injectLiveConfig(html, config);
128
+ const remoteEnvironment = inspectRemoteEnvironment(destDir);
129
+ const page = injectLiveConfig(html, config, remoteEnvironment);
84
130
  const server = http.createServer((request, response) => {
85
131
  const url = request.url ?? "/";
86
132
  if (url === "/" || url.startsWith("/index.html") || url.startsWith("/#")) {
@@ -1 +1 @@
1
- {"version":3,"file":"ui-cmd.js","sourceRoot":"","sources":["../../src/cli/ui-cmd.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ui-cmd.js","sourceRoot":"","sources":["../../src/cli/ui-cmd.ts"],"names":[],"mappings":"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"}
@@ -226,11 +226,76 @@ export declare class Lisa {
226
226
  */
227
227
  private registerPlugins;
228
228
  /**
229
- * Install each plugin and refresh the Lisa marketplace cache
229
+ * Install each plugin and refresh the Lisa marketplace cache.
230
+ *
231
+ * Version-gated (perf): `claude plugin marketplace update` is a network git
232
+ * pull of the Lisa repo and every `claude plugin install` spawns the Claude
233
+ * CLI (seconds each); re-running all of it on every apply made a single
234
+ * `bun add` cost minutes across the repeated apply passes. A full sync
235
+ * (marketplace refresh + install every plugin) still runs whenever the
236
+ * installed Lisa version differs from the `.claude/.lisa-plugins-synced`
237
+ * marker; between version changes only plugins missing for this project are
238
+ * installed, so the self-heal property (a removed plugin comes back on the
239
+ * next install) survives at the cost of one `claude plugin list` call.
230
240
  * @param execAsync - Promisified exec function for running shell commands
231
241
  * @param plugins - Plugin identifiers from enabledPlugins (e.g. "lisa@lisa")
232
242
  */
233
243
  private installPluginsAndUpdateMarketplace;
244
+ /**
245
+ * Decide whether this apply needs a full plugin sync and which plugins to
246
+ * install.
247
+ * @param execAsync - Promisified exec function for running shell commands
248
+ * @param plugins - Plugin identifiers from enabledPlugins
249
+ * @returns Sync plan: fullSync (version changed), plugins to install, and
250
+ * the current Lisa version
251
+ */
252
+ private computePluginSyncPlan;
253
+ /**
254
+ * Refresh the cached Lisa marketplace via the Claude CLI.
255
+ * @param execAsync - Promisified exec function for running shell commands
256
+ * @param announce - Whether to log the outcome (silent for the pre-install
257
+ * best-effort refresh)
258
+ * @returns Promise that resolves when the refresh attempt completes
259
+ */
260
+ private updateLisaMarketplace;
261
+ /**
262
+ * Install a single plugin at project scope via the Claude CLI.
263
+ * @param execAsync - Promisified exec function for running shell commands
264
+ * @param plugin - Plugin identifier (already validated)
265
+ * @returns Promise that resolves when the install attempt completes
266
+ */
267
+ private installPlugin;
268
+ /**
269
+ * Exec options shared by all Claude CLI plugin commands.
270
+ * @returns Options object with the project cwd and POSIX shell
271
+ */
272
+ private pluginExecOptions;
273
+ /**
274
+ * Absolute path of the plugin sync marker recording the last fully-synced
275
+ * Lisa version for this project. Shared with install-claude-plugins.sh.
276
+ * @returns Marker path under the project's .claude directory
277
+ */
278
+ private pluginSyncMarkerPath;
279
+ /**
280
+ * Absolute path of the host project's package.json.
281
+ * @returns package.json path under the destination directory
282
+ */
283
+ private hostPackageJsonPath;
284
+ /**
285
+ * Read the plugin ids already installed for this project via
286
+ * `claude plugin list --json`.
287
+ * @param execAsync - Promisified exec function for running shell commands
288
+ * @returns Set of installed plugin ids, or null when the list cannot be
289
+ * obtained (older CLI, parse failure) so callers fall back to installing
290
+ * everything — the pre-existing behavior
291
+ */
292
+ private readInstalledPluginIds;
293
+ /**
294
+ * Record the Lisa version whose plugin set was last fully synced.
295
+ * @param version - Lisa package version to record
296
+ * @returns Promise that resolves when the marker is written (best-effort)
297
+ */
298
+ private writePluginSyncMarker;
234
299
  /**
235
300
  * Finalize operation
236
301
  * @returns Promise that resolves when finalization is complete
@@ -387,6 +452,14 @@ export declare class Lisa {
387
452
  * The trampoline is always fully detached so the package manager can exit
388
453
  * normally; the child then detects the exit and re-runs Lisa. See
389
454
  * utils/postinstall-trampoline.ts for details.
455
+ *
456
+ * Skipped when this apply did not mutate package.json: the trampoline exists
457
+ * solely to redo package.json changes that the parent package manager's
458
+ * end-of-command rewrite clobbers (#383). With no mutation there is nothing
459
+ * to clobber — the rewrite restores identical content — and scheduling a
460
+ * detached re-apply (plus its lockfile regen) is pure waste. File-based
461
+ * template writes are not clobbered and need no reconciliation.
462
+ * @param prePackageJsonHash - Hash of package.json captured before apply mutated anything (null if the file did not exist)
390
463
  * @returns Promise that resolves immediately after the detached child is spawned
391
464
  */
392
465
  private schedulePostinstallReconciliation;
@@ -1 +1 @@
1
- {"version":3,"file":"lisa.d.ts","sourceRoot":"","sources":["../../src/core/lisa.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"lisa.d.ts","sourceRoot":"","sources":["../../src/core/lisa.ts"],"names":[],"mappings":"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"}
package/dist/core/lisa.js CHANGED
@@ -4,6 +4,7 @@ import { existsSync } from "node:fs";
4
4
  import { readFile, stat } from "node:fs/promises";
5
5
  import * as path from "node:path";
6
6
  import pc from "picocolors";
7
+ import { getPackageVersion } from "../cli/version.js";
7
8
  import { installAgentsMd } from "../codex/agents-md-installer.js";
8
9
  import { installCodexProjectOverlay } from "../codex/project-overlay.js";
9
10
  import { installAgyPlugin } from "../agy/plugin-installer.js";
@@ -491,49 +492,171 @@ export class Lisa {
491
492
  await this.installPluginsAndUpdateMarketplace(execAsync, plugins);
492
493
  }
493
494
  /**
494
- * Install each plugin and refresh the Lisa marketplace cache
495
+ * Install each plugin and refresh the Lisa marketplace cache.
496
+ *
497
+ * Version-gated (perf): `claude plugin marketplace update` is a network git
498
+ * pull of the Lisa repo and every `claude plugin install` spawns the Claude
499
+ * CLI (seconds each); re-running all of it on every apply made a single
500
+ * `bun add` cost minutes across the repeated apply passes. A full sync
501
+ * (marketplace refresh + install every plugin) still runs whenever the
502
+ * installed Lisa version differs from the `.claude/.lisa-plugins-synced`
503
+ * marker; between version changes only plugins missing for this project are
504
+ * installed, so the self-heal property (a removed plugin comes back on the
505
+ * next install) survives at the cost of one `claude plugin list` call.
495
506
  * @param execAsync - Promisified exec function for running shell commands
496
507
  * @param plugins - Plugin identifiers from enabledPlugins (e.g. "lisa@lisa")
497
508
  */
498
509
  async installPluginsAndUpdateMarketplace(execAsync, plugins) {
499
510
  const { logger } = this.deps;
500
511
  const validPluginName = /^[\w@./-]+$/;
501
- logger.info("Registering plugins with Claude Code (project scope)...");
502
- try {
503
- await execAsync("claude plugin marketplace update lisa", {
504
- cwd: this.config.destDir,
505
- shell: "/bin/sh",
506
- });
512
+ const { fullSync, toInstall, version } = await this.computePluginSyncPlan(execAsync, plugins);
513
+ if (!fullSync && toInstall.length === 0) {
514
+ logger.info(pc.gray(`Plugins already in sync for Lisa ${version}; skipping`));
515
+ return;
507
516
  }
508
- catch {
517
+ logger.info("Registering plugins with Claude Code (project scope)...");
518
+ if (fullSync) {
509
519
  // Best-effort cache refresh. Individual plugin installs below will still
510
520
  // report whether the marketplace entry is available.
521
+ await this.updateLisaMarketplace(execAsync, false);
511
522
  }
512
- for (const plugin of plugins) {
523
+ for (const plugin of toInstall) {
513
524
  if (!validPluginName.test(plugin)) {
514
525
  logger.warn(`Skipping invalid plugin name: ${plugin}`);
515
526
  continue;
516
527
  }
517
- try {
518
- await execAsync(`claude plugin install ${plugin} --scope project`, {
519
- cwd: this.config.destDir,
520
- shell: "/bin/sh",
521
- });
522
- logger.success(`Registered plugin: ${plugin}`);
528
+ await this.installPlugin(execAsync, plugin);
529
+ }
530
+ // Post-install refresh must run whenever installs happened: installing can
531
+ // register the marketplace for the first time, and without a refresh newly
532
+ // added skills are not discovered ("Unknown skill" in nightly CI — #320).
533
+ await this.updateLisaMarketplace(execAsync, true);
534
+ if (fullSync) {
535
+ await this.writePluginSyncMarker(version);
536
+ }
537
+ }
538
+ /**
539
+ * Decide whether this apply needs a full plugin sync and which plugins to
540
+ * install.
541
+ * @param execAsync - Promisified exec function for running shell commands
542
+ * @param plugins - Plugin identifiers from enabledPlugins
543
+ * @returns Sync plan: fullSync (version changed), plugins to install, and
544
+ * the current Lisa version
545
+ */
546
+ async computePluginSyncPlan(execAsync, plugins) {
547
+ const version = getPackageVersion();
548
+ const syncedVersion = await readFile(this.pluginSyncMarkerPath(), "utf-8")
549
+ .then(content => content.trim())
550
+ .catch(() => null);
551
+ const fullSync = syncedVersion !== version;
552
+ const installed = fullSync
553
+ ? null
554
+ : await this.readInstalledPluginIds(execAsync);
555
+ const toInstall = installed === null
556
+ ? plugins
557
+ : plugins.filter(plugin => !installed.has(plugin));
558
+ return { fullSync, toInstall, version };
559
+ }
560
+ /**
561
+ * Refresh the cached Lisa marketplace via the Claude CLI.
562
+ * @param execAsync - Promisified exec function for running shell commands
563
+ * @param announce - Whether to log the outcome (silent for the pre-install
564
+ * best-effort refresh)
565
+ * @returns Promise that resolves when the refresh attempt completes
566
+ */
567
+ async updateLisaMarketplace(execAsync, announce) {
568
+ const { logger } = this.deps;
569
+ try {
570
+ await execAsync("claude plugin marketplace update lisa", this.pluginExecOptions());
571
+ if (announce) {
572
+ logger.success("Updated marketplace: lisa");
523
573
  }
524
- catch {
525
- logger.warn(`Could not register plugin: ${plugin}`);
574
+ }
575
+ catch {
576
+ if (announce) {
577
+ logger.warn("Could not update marketplace: lisa");
526
578
  }
527
579
  }
580
+ }
581
+ /**
582
+ * Install a single plugin at project scope via the Claude CLI.
583
+ * @param execAsync - Promisified exec function for running shell commands
584
+ * @param plugin - Plugin identifier (already validated)
585
+ * @returns Promise that resolves when the install attempt completes
586
+ */
587
+ async installPlugin(execAsync, plugin) {
588
+ const { logger } = this.deps;
589
+ try {
590
+ await execAsync(`claude plugin install ${plugin} --scope project`, this.pluginExecOptions());
591
+ logger.success(`Registered plugin: ${plugin}`);
592
+ }
593
+ catch {
594
+ logger.warn(`Could not register plugin: ${plugin}`);
595
+ }
596
+ }
597
+ /**
598
+ * Exec options shared by all Claude CLI plugin commands.
599
+ * @returns Options object with the project cwd and POSIX shell
600
+ */
601
+ pluginExecOptions() {
602
+ return { cwd: this.config.destDir, shell: "/bin/sh" };
603
+ }
604
+ /**
605
+ * Absolute path of the plugin sync marker recording the last fully-synced
606
+ * Lisa version for this project. Shared with install-claude-plugins.sh.
607
+ * @returns Marker path under the project's .claude directory
608
+ */
609
+ pluginSyncMarkerPath() {
610
+ return path.join(this.config.destDir, ".claude", ".lisa-plugins-synced");
611
+ }
612
+ /**
613
+ * Absolute path of the host project's package.json.
614
+ * @returns package.json path under the destination directory
615
+ */
616
+ hostPackageJsonPath() {
617
+ return path.join(this.config.destDir, "package.json");
618
+ }
619
+ /**
620
+ * Read the plugin ids already installed for this project via
621
+ * `claude plugin list --json`.
622
+ * @param execAsync - Promisified exec function for running shell commands
623
+ * @returns Set of installed plugin ids, or null when the list cannot be
624
+ * obtained (older CLI, parse failure) so callers fall back to installing
625
+ * everything — the pre-existing behavior
626
+ */
627
+ async readInstalledPluginIds(execAsync) {
528
628
  try {
529
- await execAsync("claude plugin marketplace update lisa", {
629
+ const result = (await execAsync("claude plugin list --json", {
530
630
  cwd: this.config.destDir,
531
631
  shell: "/bin/sh",
532
- });
533
- logger.success("Updated marketplace: lisa");
632
+ }));
633
+ const parsed = JSON.parse(String(result.stdout ?? ""));
634
+ if (!Array.isArray(parsed)) {
635
+ return null;
636
+ }
637
+ const destPath = path.resolve(this.config.destDir);
638
+ const ids = parsed
639
+ .filter((entry) => typeof entry.id === "string" &&
640
+ entry.projectPath === destPath)
641
+ .map(entry => entry.id);
642
+ return new Set(ids);
534
643
  }
535
644
  catch {
536
- logger.warn("Could not update marketplace: lisa");
645
+ return null;
646
+ }
647
+ }
648
+ /**
649
+ * Record the Lisa version whose plugin set was last fully synced.
650
+ * @param version - Lisa package version to record
651
+ * @returns Promise that resolves when the marker is written (best-effort)
652
+ */
653
+ async writePluginSyncMarker(version) {
654
+ try {
655
+ await fse.ensureDir(path.join(this.config.destDir, ".claude"));
656
+ await fse.writeFile(this.pluginSyncMarkerPath(), `${version}\n`, "utf-8");
657
+ }
658
+ catch {
659
+ // Best-effort: a missing marker only costs a redundant full sync next run.
537
660
  }
538
661
  }
539
662
  /* v8 ignore stop */
@@ -602,8 +725,12 @@ export class Lisa {
602
725
  await this.loadPendingDeletions();
603
726
  await this.loadCreateOnlyOwnership();
604
727
  await this.loadCopyOverwriteOwnership();
728
+ // Captured BEFORE any mutation (migrations included): the hash gates both
729
+ // the detached trampoline and the in-process lockfile reconcile, and
730
+ // before-strategies migrations can wire scripts.postinstall — a change the
731
+ // package manager's end-of-command rewrite would clobber (#383).
732
+ const prePackageJsonHash = hashFile(this.hostPackageJsonPath());
605
733
  await this.runMigrationsBeforeStrategies();
606
- const prePackageJsonHash = hashFile(path.join(this.config.destDir, "package.json"));
607
734
  await this.processConfigurations();
608
735
  if (this.selfApply) {
609
736
  this.deps.logger.info(pc.gray("Self-apply (Lisa source repo): skipped deletions.json processing"));
@@ -622,7 +749,7 @@ export class Lisa {
622
749
  await this.finalize();
623
750
  this.printSummary();
624
751
  await this.printMigrationNotices(this.config.destDir);
625
- await this.schedulePostinstallReconciliation();
752
+ await this.schedulePostinstallReconciliation(prePackageJsonHash);
626
753
  await this.reconcileLockfilesInProcess(prePackageJsonHash);
627
754
  return this.getSuccessResult();
628
755
  }
@@ -657,7 +784,7 @@ export class Lisa {
657
784
  return;
658
785
  if (shouldSchedulePostinstallReconciliation(this.config.dryRun))
659
786
  return;
660
- const pkgPath = path.join(this.config.destDir, "package.json");
787
+ const pkgPath = this.hostPackageJsonPath();
661
788
  const postHash = hashFile(pkgPath);
662
789
  if (prePackageJsonHash === null ||
663
790
  postHash === null ||
@@ -987,9 +1114,22 @@ export class Lisa {
987
1114
  * The trampoline is always fully detached so the package manager can exit
988
1115
  * normally; the child then detects the exit and re-runs Lisa. See
989
1116
  * utils/postinstall-trampoline.ts for details.
1117
+ *
1118
+ * Skipped when this apply did not mutate package.json: the trampoline exists
1119
+ * solely to redo package.json changes that the parent package manager's
1120
+ * end-of-command rewrite clobbers (#383). With no mutation there is nothing
1121
+ * to clobber — the rewrite restores identical content — and scheduling a
1122
+ * detached re-apply (plus its lockfile regen) is pure waste. File-based
1123
+ * template writes are not clobbered and need no reconciliation.
1124
+ * @param prePackageJsonHash - Hash of package.json captured before apply mutated anything (null if the file did not exist)
990
1125
  * @returns Promise that resolves immediately after the detached child is spawned
991
1126
  */
992
- async schedulePostinstallReconciliation() {
1127
+ async schedulePostinstallReconciliation(prePackageJsonHash) {
1128
+ const postHash = hashFile(this.hostPackageJsonPath());
1129
+ if (prePackageJsonHash === postHash) {
1130
+ this.deps.logger.info(pc.gray("Postinstall reconciliation skipped: package.json unchanged by this apply"));
1131
+ return;
1132
+ }
993
1133
  if (!shouldSchedulePostinstallReconciliation(this.config.dryRun)) {
994
1134
  return;
995
1135
  }