@codyswann/lisa 2.226.2 → 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 (71) 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/rules/eager/history-audit.md +27 -0
  14. package/plugins/lisa/rules/reference/history-audit.md +42 -0
  15. package/plugins/lisa-agy/plugin.json +1 -1
  16. package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
  17. package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
  18. package/plugins/lisa-cdk-agy/plugin.json +1 -1
  19. package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
  20. package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
  21. package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
  22. package/plugins/lisa-copilot/rules/eager/history-audit.md +27 -0
  23. package/plugins/lisa-copilot/rules/reference/history-audit.md +42 -0
  24. package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
  25. package/plugins/lisa-cursor/rules/history-audit-reference.mdc +47 -0
  26. package/plugins/lisa-cursor/rules/history-audit.mdc +32 -0
  27. package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
  28. package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
  29. package/plugins/lisa-expo-agy/plugin.json +1 -1
  30. package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
  31. package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
  32. package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
  33. package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
  34. package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
  35. package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
  36. package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
  37. package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
  38. package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
  39. package/plugins/lisa-nestjs-agy/plugin.json +1 -1
  40. package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
  41. package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
  42. package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
  43. package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
  44. package/plugins/lisa-openclaw-agy/plugin.json +1 -1
  45. package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
  46. package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
  47. package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
  48. package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
  49. package/plugins/lisa-phaser-agy/plugin.json +1 -1
  50. package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
  51. package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
  52. package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
  53. package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
  54. package/plugins/lisa-rails-agy/plugin.json +1 -1
  55. package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
  56. package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
  57. package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
  58. package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
  59. package/plugins/lisa-typescript-agy/plugin.json +1 -1
  60. package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
  61. package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
  62. package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
  63. package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
  64. package/plugins/lisa-wiki-agy/plugin.json +1 -1
  65. package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
  66. package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
  67. package/plugins/src/base/rules/eager/history-audit.md +27 -0
  68. package/plugins/src/base/rules/reference/history-audit.md +42 -0
  69. package/scripts/install-claude-plugins.sh +89 -9
  70. package/ui/README.md +1 -0
  71. package/ui/index.html +143 -8
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-harper-fabric",
3
- "version": "2.226.2",
3
+ "version": "2.228.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.226.2",
3
+ "version": "2.228.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.226.2",
3
+ "version": "2.228.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.226.2",
3
+ "version": "2.228.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.226.2",
3
+ "version": "2.228.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.226.2",
3
+ "version": "2.228.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.226.2",
3
+ "version": "2.228.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.226.2",
3
+ "version": "2.228.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.226.2",
3
+ "version": "2.228.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.226.2",
3
+ "version": "2.228.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.226.2",
3
+ "version": "2.228.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.226.2",
3
+ "version": "2.228.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.226.2",
3
+ "version": "2.228.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.226.2",
3
+ "version": "2.228.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.226.2",
3
+ "version": "2.228.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.226.2",
3
+ "version": "2.228.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.226.2",
3
+ "version": "2.228.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.226.2",
3
+ "version": "2.228.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.226.2",
3
+ "version": "2.228.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.226.2",
3
+ "version": "2.228.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.226.2",
3
+ "version": "2.228.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.226.2",
3
+ "version": "2.228.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.226.2",
3
+ "version": "2.228.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.226.2",
3
+ "version": "2.228.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.226.2",
3
+ "version": "2.228.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.226.2",
3
+ "version": "2.228.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.226.2",
3
+ "version": "2.228.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.226.2",
3
+ "version": "2.228.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.226.2",
3
+ "version": "2.228.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"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-wiki",
3
- "version": "2.226.2",
3
+ "version": "2.228.0",
4
4
  "description": "Distributable LLM Wiki kernel — ingest, query, lint, and maintain a git-native markdown knowledge base across Claude and Codex.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-wiki",
3
- "version": "2.226.2",
3
+ "version": "2.228.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"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-wiki",
3
- "version": "2.226.2",
3
+ "version": "2.228.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"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-wiki",
3
- "version": "2.226.2",
3
+ "version": "2.228.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"
@@ -0,0 +1,27 @@
1
+ # History Audit — Before Removing or Changing Existing Behavior (load-bearing)
2
+
3
+ When a proposed change would **remove, gate, skip, consolidate, weaken, or relax existing behavior** — and the motivation is your own assessment that the code looks redundant, wasteful, slow, or wrong (rather than a ticket, bug report, or spec) — audit the history **before presenting the change as safe or implementing it**.
4
+
5
+ ## How to apply
6
+
7
+ 1. `git log --follow` the affected files and `git log --grep` for related keywords.
8
+ 2. Read **full commit bodies** (never just subjects), code comments, and covering tests — the defense usually lives there, often naming the exact incident the behavior prevents.
9
+ 3. Deliver every such recommendation with an explicit verdict:
10
+ - **Defended by \<commit/comment/test\>** — adapt the proposal to preserve that reason.
11
+ - **No defense found** — genuinely accidental; safe to change, and say so.
12
+
13
+ ## Scope
14
+
15
+ - **Diagnosis doesn't need the audit; prescriptions do.** Analyzing what is slow or broken is free; recommending that existing behavior stop happening is what triggers the audit. If presenting unaudited ideas, label them as such.
16
+ - Changes driven by an external requirement may cite that requirement as their defense and move on.
17
+ - "Nothing was deleted" is not an exemption — a change that weakens a guarantee (e.g. adding a cache/stamp that skips a self-healing pass) alters existing behavior and needs the same audit.
18
+
19
+ ## Forbidden
20
+
21
+ - Recommending removal or weakening of existing behavior as "safe" without the verdict.
22
+ - Auditing subjects only (`--oneline` skims miss the evidence).
23
+ - Treating redundant-looking behavior as accidental by default — defended is the norm, not the exception.
24
+
25
+ This rule compounds with commit discipline: documenting the **why** in commit bodies is what makes future audits cheap.
26
+
27
+ Full prose: [reference/history-audit.md](../reference/history-audit.md).
@@ -0,0 +1,42 @@
1
+ # History Audit — Before Removing or Changing Existing Behavior
2
+
3
+ When a proposed change would remove, gate, skip, consolidate, weaken, or relax existing behavior — and the motivation is your own assessment that the code looks redundant, wasteful, slow, or wrong, rather than a ticket, bug report, or spec — **audit the history before presenting the change as safe or implementing it**. This is Chesterton's fence made operational: the fence's reason is usually written down; go read it.
4
+
5
+ This is the historical counterpart to the `empirical-inquiry` rule: `empirical-inquiry` establishes facts about how the system behaves *now* by probing it; this rule establishes *why the system behaves that way* by reading the record. Both reject confident-sounding reasoning as a substitute for evidence.
6
+
7
+ ## Why this rule exists
8
+
9
+ Optimization and cleanup recommendations fail in a characteristic way: the code genuinely *is* doing redundant-looking work, the diagnosis is correct, and the proposed fix would still ship a regression — because the redundancy is load-bearing and its reason lives in a commit body, a code comment, or a test the recommender never read. In a real Lisa incident, a performance audit of the postinstall pipeline produced five recommendations; the history audit walked back two of them (a "redundant" apply pass existed to fix a bootstrap chicken-and-egg; a "wasteful" detached re-run existed because the package manager clobbers `package.json` after lifecycle scripts finish) and confirmed the rest were genuinely accidental. Without the audit, the confident-sounding fixes would have re-shipped bugs that earlier commits had explicitly fixed.
10
+
11
+ ## When this rule applies
12
+
13
+ - A recommendation proposes that existing behavior stop happening: deleting a code path, gating an unconditional step, deduplicating repeated work, relaxing a guard or retry, skipping a pass "when nothing changed."
14
+ - The motivation is internal — the code *looks* redundant, wasteful, slow, dead, or overcautious — rather than an external requirement.
15
+ - **Nothing needs to be deleted to trigger it.** A change that weakens a guarantee alters existing behavior just as much: adding a cache or version-stamp that skips a self-healing pass, narrowing a matcher, batching what was deliberately serialized.
16
+
17
+ ## When it does not apply
18
+
19
+ - **Diagnosis is free.** Measuring, profiling, and explaining what is slow or broken needs no audit. The audit gates *prescriptions* — the moment an observation is about to become "so we should remove/skip/gate X."
20
+ - **Requirement-driven changes may cite the requirement.** Implementing a ticket, fixing a reproduced bug, or following a spec supplies external evidence; cite it as the defense and move on. Normal verification discipline covers those.
21
+ - **Spitballing is fine when labeled.** Presenting early ideas before the audit is acceptable if they are explicitly marked as not yet history-audited. What is forbidden is presenting them as safe.
22
+
23
+ ## How to apply it
24
+
25
+ 1. **Trace the artifact history.** `git log --follow` on each affected file; `git log --grep` for related keywords (feature names, flag names, error strings). Find the commits that introduced and last modified the behavior.
26
+ 2. **Read the full commit bodies.** Subjects rarely carry the defense; bodies do — often naming the exact incident, the affected projects, and the failure mode the behavior prevents. `--oneline` skims are how the evidence gets missed. Extend the same reading to code comments around the behavior and to tests whose names encode a philosophy (a test called `*-guard` or `*-preserves-*` is a defense).
27
+ 3. **Check for a prior removal.** Search for commits that removed the same behavior before — if it was removed and later restored, the restoration commit is the strongest defense on record, and your proposal must answer it specifically.
28
+ 4. **Issue a per-recommendation verdict.** Every prescription ships in one of two states:
29
+ - **Defended by \<commit/comment/test\>** — the behavior has a recorded reason. Adapt the proposal so that reason keeps holding (gate instead of delete, preserve the invariant, keep the escape hatch), and say what the defense was.
30
+ - **No defense found** — you searched and no commit, comment, or test justifies it. That absence is itself a finding: state it, and proceed.
31
+
32
+ The verdict is the enforcement mechanism — it cannot be filled in without doing the audit.
33
+
34
+ ## What this rule forbids
35
+
36
+ - Presenting the removal or weakening of existing behavior as safe without a verdict.
37
+ - Auditing by subject line only.
38
+ - Treating redundant-looking behavior as accidental by default. In a codebase with disciplined commit messages, *defended* is the norm; genuine accidents are the exception, and the audit is how you tell them apart.
39
+
40
+ ## The compounding loop
41
+
42
+ This rule only works in a repository whose commit messages record the **why** — which the commit-discipline rules already mandate. The two form a loop: writing down why a behavior exists makes the future audit a five-minute read instead of an archaeology project, and the audit is what makes writing it down pay off. When your own change survives an audit and ships, document its why with the same care — the next agent's audit depends on it.
@@ -55,9 +55,23 @@ detect_lisa_stack() {
55
55
  done
56
56
  }
57
57
 
58
+ # One-time migration marker: once the user-wide Codex registrations are
59
+ # confirmed retired, skip the `codex plugin marketplace list` probe (a codex
60
+ # CLI spawn) on every subsequent install.
61
+ CODEX_RETIRE_MARKER="$HOME/.codex/.lisa-legacy-plugins-retired"
62
+
63
+ write_codex_retire_marker() {
64
+ mkdir -p "$HOME/.codex" 2>/dev/null && touch "$CODEX_RETIRE_MARKER" 2>/dev/null || true
65
+ }
66
+
58
67
  remove_user_wide_codex_lisa_plugins() {
59
68
  command -v codex >/dev/null 2>&1 || return 0
60
69
 
70
+ # The marker is written only after the cleanup actually ran or was confirmed
71
+ # unnecessary — never on the CODEX_THREAD_ID deferral below, which must
72
+ # retry on a later install (see fix/defer-codex-retirement-postinstall).
73
+ [ -f "$CODEX_RETIRE_MARKER" ] && return 0
74
+
61
75
  # Removing a plugin relocates its cache directory. A running Codex session
62
76
  # has already captured absolute hook paths, so removal inside that session
63
77
  # turns every later hook invocation into an EPIPE/broken-pipe failure. Defer
@@ -84,6 +98,7 @@ remove_user_wide_codex_lisa_plugins() {
84
98
  }
85
99
  });
86
100
  '; then
101
+ write_codex_retire_marker
87
102
  return 0
88
103
  fi
89
104
 
@@ -102,6 +117,7 @@ remove_user_wide_codex_lisa_plugins() {
102
117
  codex plugin remove "${plugin}@lisa" </dev/null >/dev/null 2>&1 || true
103
118
  done
104
119
  codex plugin marketplace remove lisa </dev/null >/dev/null 2>&1 || true
120
+ write_codex_retire_marker
105
121
  }
106
122
 
107
123
  # Skip running Lisa's full template engine on itself — the Lisa repo IS the
@@ -144,8 +160,23 @@ fi
144
160
  # each path is written exactly once by its most-specific stack — there is no
145
161
  # intermediate clobbered state to be interrupted in. See src/core/lisa.ts
146
162
  # loadCopyOverwriteOwnership.
163
+ # Dedup guard: when the host's own package.json postinstall already invokes
164
+ # Lisa (wired by the ensure-lisa-postinstall migration), the package manager
165
+ # will run that root lifecycle script in this same install — running the full
166
+ # apply here too doubles the work for no benefit. The package-side apply
167
+ # exists for the bootstrap chicken-and-egg (#1017: a never-applied project
168
+ # has no postinstall to wire itself), so it still runs whenever the marker is
169
+ # absent. Detection matches LISA_MARKER in ensure-lisa-postinstall.ts.
170
+ HOST_HAS_LISA_POSTINSTALL="$(node -e "
171
+ const scripts = require('$PROJECT_ROOT/package.json').scripts || {};
172
+ const postinstall = scripts.postinstall || '';
173
+ process.stdout.write(postinstall.includes('node_modules/@codyswann/lisa/dist/index.js') ? 'true' : 'false');
174
+ " 2>/dev/null || echo false)"
175
+
147
176
  if [ "$IS_LISA_SELF" != "true" ] && [ -z "${CI:-}" ]; then
148
- if ! LISA_BOOTSTRAP=1 node "$LISA_DIR/dist/index.js" --yes --skip-git-check "$PROJECT_ROOT"; then
177
+ if [ "$HOST_HAS_LISA_POSTINSTALL" = "true" ]; then
178
+ echo "Lisa apply deferred to the host project's own postinstall script."
179
+ elif ! LISA_BOOTSTRAP=1 node "$LISA_DIR/dist/index.js" --yes --skip-git-check "$PROJECT_ROOT"; then
149
180
  echo "⚠️ Warning: Lisa template application failed. Migration may be incomplete." >&2
150
181
  fi
151
182
  fi
@@ -259,6 +290,38 @@ fi
259
290
  # Install plugins only when claude CLI is available
260
291
  if ! command -v claude &>/dev/null; then exit 0; fi
261
292
 
293
+ # Version-gated plugin sync (perf). `claude plugin marketplace update` is a
294
+ # network git pull of the whole Lisa repo and every `claude plugin install`
295
+ # spawns the Claude CLI (seconds each); re-running all of it on every install
296
+ # made a single `bun add` cost minutes. A full sync still runs whenever the
297
+ # installed Lisa version differs from the .claude/.lisa-plugins-synced marker
298
+ # (shared with Lisa.apply's registerPlugins); between version changes only
299
+ # plugins missing for this project are installed, so the self-heal property
300
+ # (a removed plugin comes back on the next install) survives at the cost of a
301
+ # single `claude plugin list` call.
302
+ LISA_VERSION="$(node -e "console.log(require('$LISA_DIR/package.json').version || '')" 2>/dev/null || true)"
303
+ PLUGIN_SYNC_MARKER="$PROJECT_ROOT/.claude/.lisa-plugins-synced"
304
+ FORCE_PLUGIN_SYNC=true
305
+ if [ -n "$LISA_VERSION" ] && [ -f "$PLUGIN_SYNC_MARKER" ] \
306
+ && [ "$(cat "$PLUGIN_SYNC_MARKER" 2>/dev/null)" = "$LISA_VERSION" ]; then
307
+ FORCE_PLUGIN_SYNC=false
308
+ fi
309
+
310
+ INSTALLED_PLUGINS_FOR_PROJECT=""
311
+ if command -v jq >/dev/null 2>&1; then
312
+ INSTALLED_PLUGINS_FOR_PROJECT="$(claude plugin list --json 2>/dev/null \
313
+ | jq -r --arg cwd "$PROJECT_ROOT" '.[] | select(.projectPath == $cwd) | .id' 2>/dev/null || true)"
314
+ fi
315
+
316
+ install_plugin_if_missing() {
317
+ local plugin="$1"
318
+ if [ "$FORCE_PLUGIN_SYNC" != "true" ] && [ -n "$INSTALLED_PLUGINS_FOR_PROJECT" ] \
319
+ && printf '%s\n' "$INSTALLED_PLUGINS_FOR_PROJECT" | grep -qxF "$plugin"; then
320
+ return 0
321
+ fi
322
+ claude plugin install "$plugin" --scope project </dev/null 2>&1 || true
323
+ }
324
+
262
325
  # The Lisa marketplace is registered via extraKnownMarketplaces in .claude/settings.json
263
326
  # pointing to the GitHub repo (CodySwannGT/lisa). Built plugins are committed to the repo
264
327
  # so relative paths in marketplace.json resolve correctly.
@@ -272,7 +335,10 @@ if ! command -v claude &>/dev/null; then exit 0; fi
272
335
  # "Update now" action with: "Local plugins cannot be updated remotely."
273
336
  # If we detect a non-github marketplace named "lisa", uninstall the plugins
274
337
  # sourced from it and remove the registration so the github source can take over.
275
- if command -v jq >/dev/null 2>&1; then
338
+ # Gated on version change: the stale registration can only exist in state
339
+ # written by an older Lisa, so re-probing it on every same-version install
340
+ # spends a claude CLI spawn to re-confirm a fact that cannot have changed.
341
+ if [ "$FORCE_PLUGIN_SYNC" = "true" ] && command -v jq >/dev/null 2>&1; then
276
342
  STALE_LISA_SOURCE=$(claude plugin marketplace list --json 2>/dev/null \
277
343
  | jq -r '.[] | select(.name == "lisa" and .source != "github") | .source' 2>/dev/null \
278
344
  | head -n 1)
@@ -339,7 +405,12 @@ heal_local_classification() {
339
405
 
340
406
  if command -v jq >/dev/null 2>&1; then
341
407
  # Refresh the cached marketplace.json so we're reading the latest schema.
342
- claude plugin marketplace update lisa </dev/null >/dev/null 2>&1 || true
408
+ # Gated: the pull is a network git fetch of the whole Lisa repo; once this
409
+ # version's plugins are synced and the project's heal-v2 marker exists, a
410
+ # fresh pull cannot change the outcome of the schema check below.
411
+ if [ "$FORCE_PLUGIN_SYNC" = "true" ] || [ ! -f "$PROJECT_ROOT/.claude/$HEAL_V2_MARKER_NAME" ]; then
412
+ claude plugin marketplace update lisa </dev/null >/dev/null 2>&1 || true
413
+ fi
343
414
 
344
415
  MARKETPLACE_JSON_PATH="$HOME/.claude/plugins/marketplaces/lisa/.claude-plugin/marketplace.json"
345
416
  NEW_SCHEMA="false"
@@ -364,8 +435,8 @@ if command -v jq >/dev/null 2>&1; then
364
435
  fi
365
436
  fi
366
437
 
367
- # Always install the base plugin (universal governance for all projects)
368
- claude plugin install "lisa@lisa" --scope project </dev/null 2>&1 || true
438
+ # Always ensure the base plugin (universal governance for all projects)
439
+ install_plugin_if_missing "lisa@lisa"
369
440
 
370
441
  # Detect which stack plugin to install from .claude/settings.json
371
442
  LISA_STACK="$(detect_lisa_stack "$SETTINGS_FILE")"
@@ -374,13 +445,13 @@ LISA_STACK="$(detect_lisa_stack "$SETTINGS_FILE")"
374
445
  case "$LISA_STACK" in
375
446
  rails) ;; # Rails doesn't get typescript plugin
376
447
  *)
377
- claude plugin install "lisa-typescript@lisa" --scope project </dev/null 2>&1 || true
448
+ install_plugin_if_missing "lisa-typescript@lisa"
378
449
  ;;
379
450
  esac
380
451
 
381
452
  # Install stack-specific plugin if not plain typescript
382
453
  if [ -n "$LISA_STACK" ]; then
383
- claude plugin install "lisa-${LISA_STACK}@lisa" --scope project </dev/null 2>&1 || true
454
+ install_plugin_if_missing "lisa-${LISA_STACK}@lisa"
384
455
  fi
385
456
 
386
457
  # Uninstall old monolithic plugins during migration
@@ -403,7 +474,7 @@ for plugin in \
403
474
  "skill-creator@claude-plugins-official" \
404
475
  "atlassian@claude-plugins-official" \
405
476
  "safety-net@cc-marketplace"; do
406
- claude plugin install "$plugin" --scope project </dev/null 2>&1 || true
477
+ install_plugin_if_missing "$plugin"
407
478
  done
408
479
 
409
480
  # Install stack-specific third-party plugins
@@ -411,6 +482,15 @@ if [ "$LISA_STACK" = "expo" ] || [ "$LISA_STACK" = "harper-fabric" ]; then
411
482
  for plugin in \
412
483
  "playwright@claude-plugins-official" \
413
484
  "posthog@claude-plugins-official"; do
414
- claude plugin install "$plugin" --scope project </dev/null 2>&1 || true
485
+ install_plugin_if_missing "$plugin"
415
486
  done
416
487
  fi
488
+
489
+ # Record the fully-synced Lisa version so same-version installs skip the
490
+ # marketplace pulls and forced reinstalls above. Written last so any earlier
491
+ # failure exits (set -e) without recording a sync that did not finish.
492
+ if [ -n "$LISA_VERSION" ]; then
493
+ mkdir -p "$PROJECT_ROOT/.claude" 2>/dev/null \
494
+ && printf '%s' "$LISA_VERSION" > "$PLUGIN_SYNC_MARKER" 2>/dev/null \
495
+ || true
496
+ fi
package/ui/README.md CHANGED
@@ -153,6 +153,7 @@ visible in the section.
153
153
  | General (`harness`, `tracker`, `source`, `repo`, package manager) | `src/core/config.ts`, `plugins/src/base/rules/reference/config-resolution.md` |
154
154
  | Project types (8 stacks + template strategies) | `src/detection/`, `src/strategies/`, `<stack>/` template dirs |
155
155
  | Coding agents (claude/codex/cursor/agy/copilot/opencode/fleet) | `src/core/lisa.ts`, `scripts/generate-*-plugin-artifacts.mjs` |
156
+ | Remote Environment (variable presence + active-agent startup scripts) | `src/cli/ui-cmd.ts`, `plugins/src/base/scripts/remote-agent-aws-setup.sh` |
156
157
  | Work tracker (JIRA / GitHub Issues / Linear) | `config-resolution.md`, `lisa-setup-*` skills |
157
158
  | PRD source (Notion / Confluence / Linear / GitHub) | `config-resolution.md`, `lisa-setup-*` skills |
158
159
  | Deploy & environments (`deploy.*`, `github.environments`) | `scripts/lisa-github-environments.sh` |