@codyswann/lisa 4.13.0 → 4.13.2

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 (125) hide show
  1. package/all/copy-overwrite/scripts/lisa-hooks/parity-safety-net.sh +7 -1
  2. package/dist/core/lisa-owned-hash-ledger.d.ts.map +1 -1
  3. package/dist/core/lisa-owned-hash-ledger.js +3 -0
  4. package/dist/core/lisa-owned-hash-ledger.js.map +1 -1
  5. package/dist/core/upstream-evidence-manifest.d.ts.map +1 -1
  6. package/dist/core/upstream-evidence-manifest.js +16 -14
  7. package/dist/core/upstream-evidence-manifest.js.map +1 -1
  8. package/dist/opencode/plugin-templates/parity-safety-net.sh +7 -1
  9. package/package.json +1 -1
  10. package/plugins/lisa/.claude-plugin/plugin.json +1 -1
  11. package/plugins/lisa/.codex-plugin/plugin.json +1 -1
  12. package/plugins/lisa/.codex-plugin/skills/lisa-atlassian-access/SKILL.md +48 -6
  13. package/plugins/lisa/.codex-plugin/skills/lisa-jam-access/SKILL.md +38 -2
  14. package/plugins/lisa/.codex-plugin/skills/lisa-notion-access/SKILL.md +47 -6
  15. package/plugins/lisa/.codex-plugin/skills/lisa-posthog-access/SKILL.md +38 -2
  16. package/plugins/lisa/.codex-plugin/skills/lisa-sentry-access/SKILL.md +38 -2
  17. package/plugins/lisa/.codex-plugin/skills/lisa-setup-atlassian/SKILL.md +53 -7
  18. package/plugins/lisa/.codex-plugin/skills/lisa-setup-notion/SKILL.md +53 -7
  19. package/plugins/lisa/.codex-plugin/skills/lisa-sonarcloud-access/SKILL.md +49 -7
  20. package/plugins/lisa/hooks/parity-safety-net.sh +7 -1
  21. package/plugins/lisa/rules/eager/falsifiable-checks.md +1 -1
  22. package/plugins/lisa/rules/reference/config-resolution.md +43 -6
  23. package/plugins/lisa/rules/reference/falsifiable-checks.md +1 -1
  24. package/plugins/lisa/skills/lisa-atlassian-access/SKILL.md +48 -6
  25. package/plugins/lisa/skills/lisa-jam-access/SKILL.md +38 -2
  26. package/plugins/lisa/skills/lisa-notion-access/SKILL.md +47 -6
  27. package/plugins/lisa/skills/lisa-posthog-access/SKILL.md +38 -2
  28. package/plugins/lisa/skills/lisa-sentry-access/SKILL.md +38 -2
  29. package/plugins/lisa/skills/lisa-setup-atlassian/SKILL.md +53 -7
  30. package/plugins/lisa/skills/lisa-setup-notion/SKILL.md +53 -7
  31. package/plugins/lisa/skills/lisa-sonarcloud-access/SKILL.md +49 -7
  32. package/plugins/lisa-agy/hooks/parity-safety-net.sh +7 -1
  33. package/plugins/lisa-agy/plugin.json +1 -1
  34. package/plugins/lisa-agy/skills/lisa-atlassian-access/SKILL.md +48 -6
  35. package/plugins/lisa-agy/skills/lisa-jam-access/SKILL.md +38 -2
  36. package/plugins/lisa-agy/skills/lisa-notion-access/SKILL.md +47 -6
  37. package/plugins/lisa-agy/skills/lisa-posthog-access/SKILL.md +38 -2
  38. package/plugins/lisa-agy/skills/lisa-sentry-access/SKILL.md +38 -2
  39. package/plugins/lisa-agy/skills/lisa-setup-atlassian/SKILL.md +53 -7
  40. package/plugins/lisa-agy/skills/lisa-setup-notion/SKILL.md +53 -7
  41. package/plugins/lisa-agy/skills/lisa-sonarcloud-access/SKILL.md +49 -7
  42. package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
  43. package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
  44. package/plugins/lisa-cdk-agy/plugin.json +1 -1
  45. package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
  46. package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
  47. package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
  48. package/plugins/lisa-copilot/hooks/parity-safety-net.sh +7 -1
  49. package/plugins/lisa-copilot/rules/eager/falsifiable-checks.md +1 -1
  50. package/plugins/lisa-copilot/rules/reference/config-resolution.md +43 -6
  51. package/plugins/lisa-copilot/rules/reference/falsifiable-checks.md +1 -1
  52. package/plugins/lisa-copilot/skills/lisa-atlassian-access/SKILL.md +48 -6
  53. package/plugins/lisa-copilot/skills/lisa-jam-access/SKILL.md +38 -2
  54. package/plugins/lisa-copilot/skills/lisa-notion-access/SKILL.md +47 -6
  55. package/plugins/lisa-copilot/skills/lisa-posthog-access/SKILL.md +38 -2
  56. package/plugins/lisa-copilot/skills/lisa-sentry-access/SKILL.md +38 -2
  57. package/plugins/lisa-copilot/skills/lisa-setup-atlassian/SKILL.md +53 -7
  58. package/plugins/lisa-copilot/skills/lisa-setup-notion/SKILL.md +53 -7
  59. package/plugins/lisa-copilot/skills/lisa-sonarcloud-access/SKILL.md +49 -7
  60. package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
  61. package/plugins/lisa-cursor/hooks/parity-safety-net.sh +7 -1
  62. package/plugins/lisa-cursor/rules/config-resolution-reference.mdc +43 -6
  63. package/plugins/lisa-cursor/rules/falsifiable-checks-reference.mdc +1 -1
  64. package/plugins/lisa-cursor/rules/falsifiable-checks.mdc +1 -1
  65. package/plugins/lisa-cursor/skills/lisa-atlassian-access/SKILL.md +48 -6
  66. package/plugins/lisa-cursor/skills/lisa-jam-access/SKILL.md +38 -2
  67. package/plugins/lisa-cursor/skills/lisa-notion-access/SKILL.md +47 -6
  68. package/plugins/lisa-cursor/skills/lisa-posthog-access/SKILL.md +38 -2
  69. package/plugins/lisa-cursor/skills/lisa-sentry-access/SKILL.md +38 -2
  70. package/plugins/lisa-cursor/skills/lisa-setup-atlassian/SKILL.md +53 -7
  71. package/plugins/lisa-cursor/skills/lisa-setup-notion/SKILL.md +53 -7
  72. package/plugins/lisa-cursor/skills/lisa-sonarcloud-access/SKILL.md +49 -7
  73. package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
  74. package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
  75. package/plugins/lisa-expo-agy/plugin.json +1 -1
  76. package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
  77. package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
  78. package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
  79. package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
  80. package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
  81. package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
  82. package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
  83. package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
  84. package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
  85. package/plugins/lisa-nestjs-agy/plugin.json +1 -1
  86. package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
  87. package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
  88. package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
  89. package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
  90. package/plugins/lisa-openclaw-agy/plugin.json +1 -1
  91. package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
  92. package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
  93. package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
  94. package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
  95. package/plugins/lisa-phaser-agy/plugin.json +1 -1
  96. package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
  97. package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
  98. package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
  99. package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
  100. package/plugins/lisa-rails-agy/plugin.json +1 -1
  101. package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
  102. package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
  103. package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
  104. package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
  105. package/plugins/lisa-typescript-agy/plugin.json +1 -1
  106. package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
  107. package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
  108. package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
  109. package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
  110. package/plugins/lisa-wiki-agy/plugin.json +1 -1
  111. package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
  112. package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
  113. package/plugins/src/base/hooks/parity-safety-net.sh +7 -1
  114. package/plugins/src/base/rules/eager/falsifiable-checks.md +1 -1
  115. package/plugins/src/base/rules/reference/config-resolution.md +43 -6
  116. package/plugins/src/base/rules/reference/falsifiable-checks.md +1 -1
  117. package/plugins/src/base/skills/lisa-atlassian-access/SKILL.md +48 -6
  118. package/plugins/src/base/skills/lisa-jam-access/SKILL.md +38 -2
  119. package/plugins/src/base/skills/lisa-notion-access/SKILL.md +47 -6
  120. package/plugins/src/base/skills/lisa-posthog-access/SKILL.md +38 -2
  121. package/plugins/src/base/skills/lisa-sentry-access/SKILL.md +38 -2
  122. package/plugins/src/base/skills/lisa-setup-atlassian/SKILL.md +53 -7
  123. package/plugins/src/base/skills/lisa-setup-notion/SKILL.md +53 -7
  124. package/plugins/src/base/skills/lisa-sonarcloud-access/SKILL.md +49 -7
  125. package/typescript/copy-overwrite/scripts/lisa-mutation.mjs +19 -20
@@ -80,10 +80,43 @@ read_token() {
80
80
  # Preferred path: the single secrets chokepoint. It owns the one-store rule
81
81
  # and the surface ladder, so anything it can answer must not be read out of an
82
82
  # OS keychain here — a second reader is how the same credential ends up living
83
- # in two places and drifting. Keep this identical to `atlassian-access`.
83
+ # in two places and drifting. Keep the LADDER below identical to
84
+ # `atlassian-access`; the tail may differ, because this skill falls through to a
85
+ # legacy keychain rung and that one does not. Saying "the ladder" rather
86
+ # than "this function" is deliberate — the old wording asked for something
87
+ # unachievable, and an unachievable rule is one that gets quietly dropped.
88
+ #
89
+ # Ordered repo-first, ending at the plugin's own copy. Repo-relative rungs
90
+ # lead because a project that vendors the resolver has declared which copy it
91
+ # wants used, and that decision must survive this change untouched. The plugin
92
+ # rungs are the floor: `resolve-secret.mjs` ships beside this skill, so a rung
93
+ # pointing at it is reachable from anywhere the plugin itself is installed.
94
+ # Without one, a consumer repository that vendors none of the leading paths
95
+ # never reaches a resolver at all — the ladder exits without having asked
96
+ # anything, which is what pushed agents into improvising their own credential
97
+ # lookups. This LADDER is identical in every skill that resolves a credential
98
+ # and `credential-resolver-ladder` fails if any copy diverges. Only what
99
+ # happens AFTER the ladder may differ between them.
100
+ local candidates=(
101
+ .claude/skills/lisa-secrets-access/scripts/resolve-secret.mjs
102
+ .agents/skills/lisa-secrets-access/scripts/resolve-secret.mjs
103
+ .opencode/skills/lisa/lisa-secrets-access/scripts/resolve-secret.mjs
104
+ .codex/skills/lisa/lisa-secrets-access/scripts/resolve-secret.mjs
105
+ )
106
+ if [ -n "${CLAUDE_PLUGIN_ROOT:-}" ]; then
107
+ candidates+=("$CLAUDE_PLUGIN_ROOT/skills/lisa-secrets-access/scripts/resolve-secret.mjs")
108
+ fi
109
+ if [ -n "${PLUGIN_ROOT:-}" ]; then
110
+ candidates+=("$PLUGIN_ROOT/skills/lisa-secrets-access/scripts/resolve-secret.mjs")
111
+ fi
112
+ # Last rung deliberately needs no environment variable: an agent that was
113
+ # never handed a plugin root still has the installed package to fall back on.
114
+ candidates+=(node_modules/@codyswann/lisa/plugins/lisa/skills/lisa-secrets-access/scripts/resolve-secret.mjs)
115
+
84
116
  local resolver
85
- for resolver in .claude/skills/lisa-secrets-access/scripts/resolve-secret.mjs \
86
- .agents/skills/lisa-secrets-access/scripts/resolve-secret.mjs; do
117
+ local tried=()
118
+ for resolver in "${candidates[@]}"; do
119
+ tried+=("$resolver")
87
120
  if [ -f "$resolver" ]; then
88
121
  local via_lisa
89
122
  via_lisa=$(node "$resolver" get ATLASSIAN_API_TOKEN 2>/dev/null) \
@@ -94,15 +127,17 @@ read_token() {
94
127
  # Legacy fallback: the OS keychain written by the guided flow below, for
95
128
  # projects that have not adopted a credentials provider. Reached only when the
96
129
  # chokepoint is absent or has no entry.
130
+ local from_keychain=""
97
131
  case "$(uname -s)" in
98
- Darwin) security find-generic-password -s lisa-atlassian -a "$email" -w 2>/dev/null ;;
99
- Linux) command -v secret-tool >/dev/null && secret-tool lookup service lisa-atlassian account "$email" 2>/dev/null ;;
132
+ Darwin) from_keychain=$(security find-generic-password -s lisa-atlassian -a "$email" -w 2>/dev/null) ;;
133
+ Linux) command -v secret-tool >/dev/null && \
134
+ from_keychain=$(secret-tool lookup service lisa-atlassian account "$email" 2>/dev/null) ;;
100
135
  MINGW*|MSYS*|CYGWIN*)
101
136
  # `cmdkey /generic ... /pass:` stores the secret in Windows Credential Manager, but
102
137
  # `cmdkey /list` never prints stored passwords (by design). Read the CredentialBlob
103
138
  # back via the Win32 CredRead API through PowerShell; pass the target name via an env
104
139
  # var to dodge nested quoting, and strip the CRLF powershell.exe appends.
105
- LISA_CRED_TARGET="lisa-atlassian-${email}" powershell.exe -NoProfile -NonInteractive -Command '
140
+ from_keychain=$(LISA_CRED_TARGET="lisa-atlassian-${email}" powershell.exe -NoProfile -NonInteractive -Command '
106
141
  Add-Type -TypeDefinition @"
107
142
  using System;
108
143
  using System.Runtime.InteropServices;
@@ -128,8 +163,19 @@ public static class LisaCred {
128
163
  }
129
164
  }
130
165
  "@
131
- [LisaCred]::Read($env:LISA_CRED_TARGET)' 2>/dev/null | tr -d '\r' ;;
166
+ [LisaCred]::Read($env:LISA_CRED_TARGET)' 2>/dev/null | tr -d '\r') ;;
132
167
  esac
168
+ [ -n "$from_keychain" ] && { echo "$from_keychain"; return; }
169
+
170
+ # Name every path. A bare `return 1` sends the next reader hunting for a
171
+ # resolver they cannot see the absence of; the enumeration turns that into a
172
+ # seconds-long diagnosis. Paths and store coordinates only — never any
173
+ # resolved value, on any path.
174
+ echo "Error: could not resolve ATLASSIAN_API_TOKEN through lisa-secrets-access or the legacy keychain." >&2
175
+ echo "Tried, in order (relative paths are from $PWD):" >&2
176
+ printf ' %s\n' "${tried[@]}" >&2
177
+ echo " <OS keychain> service=lisa-atlassian account=$email" >&2
178
+ return 1
133
179
  }
134
180
 
135
181
  EXISTING=$(read_token "$EMAIL")
@@ -167,10 +167,43 @@ read_notion_token() {
167
167
  # Preferred path: the single secrets chokepoint. It owns the one-store rule
168
168
  # and the surface ladder, so anything it can answer must not be read out of an
169
169
  # OS keychain here — a second reader is how the same credential ends up living
170
- # in two places and drifting. Keep this identical to `notion-access`.
170
+ # in two places and drifting. Keep the LADDER below identical to
171
+ # `notion-access`; the tail may differ, because this skill falls through to a
172
+ # legacy keychain rung and that one does not. Saying "the ladder" rather
173
+ # than "this function" is deliberate — the old wording asked for something
174
+ # unachievable, and an unachievable rule is one that gets quietly dropped.
175
+ #
176
+ # Ordered repo-first, ending at the plugin's own copy. Repo-relative rungs
177
+ # lead because a project that vendors the resolver has declared which copy it
178
+ # wants used, and that decision must survive this change untouched. The plugin
179
+ # rungs are the floor: `resolve-secret.mjs` ships beside this skill, so a rung
180
+ # pointing at it is reachable from anywhere the plugin itself is installed.
181
+ # Without one, a consumer repository that vendors none of the leading paths
182
+ # never reaches a resolver at all — the ladder exits without having asked
183
+ # anything, which is what pushed agents into improvising their own credential
184
+ # lookups. This LADDER is identical in every skill that resolves a credential
185
+ # and `credential-resolver-ladder` fails if any copy diverges. Only what
186
+ # happens AFTER the ladder may differ between them.
187
+ local candidates=(
188
+ .claude/skills/lisa-secrets-access/scripts/resolve-secret.mjs
189
+ .agents/skills/lisa-secrets-access/scripts/resolve-secret.mjs
190
+ .opencode/skills/lisa/lisa-secrets-access/scripts/resolve-secret.mjs
191
+ .codex/skills/lisa/lisa-secrets-access/scripts/resolve-secret.mjs
192
+ )
193
+ if [ -n "${CLAUDE_PLUGIN_ROOT:-}" ]; then
194
+ candidates+=("$CLAUDE_PLUGIN_ROOT/skills/lisa-secrets-access/scripts/resolve-secret.mjs")
195
+ fi
196
+ if [ -n "${PLUGIN_ROOT:-}" ]; then
197
+ candidates+=("$PLUGIN_ROOT/skills/lisa-secrets-access/scripts/resolve-secret.mjs")
198
+ fi
199
+ # Last rung deliberately needs no environment variable: an agent that was
200
+ # never handed a plugin root still has the installed package to fall back on.
201
+ candidates+=(node_modules/@codyswann/lisa/plugins/lisa/skills/lisa-secrets-access/scripts/resolve-secret.mjs)
202
+
171
203
  local resolver
172
- for resolver in .claude/skills/lisa-secrets-access/scripts/resolve-secret.mjs \
173
- .agents/skills/lisa-secrets-access/scripts/resolve-secret.mjs; do
204
+ local tried=()
205
+ for resolver in "${candidates[@]}"; do
206
+ tried+=("$resolver")
174
207
  if [ -f "$resolver" ]; then
175
208
  local via_lisa
176
209
  via_lisa=$(node "$resolver" get NOTION_API_TOKEN 2>/dev/null) \
@@ -181,15 +214,17 @@ read_notion_token() {
181
214
  # Legacy fallback: the OS keychain written by the guided flow below, for
182
215
  # projects that have not adopted a credentials provider. Reached only when the
183
216
  # chokepoint is absent or has no entry.
217
+ local from_keychain=""
184
218
  case "$(uname -s)" in
185
- Darwin) security find-generic-password -s lisa-notion -a "$workspace" -w 2>/dev/null ;;
186
- Linux) command -v secret-tool >/dev/null && secret-tool lookup service lisa-notion account "$workspace" 2>/dev/null ;;
219
+ Darwin) from_keychain=$(security find-generic-password -s lisa-notion -a "$workspace" -w 2>/dev/null) ;;
220
+ Linux) command -v secret-tool >/dev/null && \
221
+ from_keychain=$(secret-tool lookup service lisa-notion account "$workspace" 2>/dev/null) ;;
187
222
  MINGW*|MSYS*|CYGWIN*)
188
223
  # `cmdkey /generic ... /pass:` stores the secret in Windows Credential Manager, but
189
224
  # `cmdkey /list` never prints stored passwords (by design). Read the CredentialBlob
190
225
  # back via the Win32 CredRead API through PowerShell; pass the target name via an env
191
226
  # var to dodge nested quoting, and strip the CRLF powershell.exe appends.
192
- LISA_CRED_TARGET="lisa-notion-${workspace}" powershell.exe -NoProfile -NonInteractive -Command '
227
+ from_keychain=$(LISA_CRED_TARGET="lisa-notion-${workspace}" powershell.exe -NoProfile -NonInteractive -Command '
193
228
  Add-Type -TypeDefinition @"
194
229
  using System;
195
230
  using System.Runtime.InteropServices;
@@ -215,8 +250,19 @@ public static class LisaCred {
215
250
  }
216
251
  }
217
252
  "@
218
- [LisaCred]::Read($env:LISA_CRED_TARGET)' 2>/dev/null | tr -d '\r' ;;
253
+ [LisaCred]::Read($env:LISA_CRED_TARGET)' 2>/dev/null | tr -d '\r') ;;
219
254
  esac
255
+ [ -n "$from_keychain" ] && { echo "$from_keychain"; return; }
256
+
257
+ # Name every path. A bare `return 1` sends the next reader hunting for a
258
+ # resolver they cannot see the absence of; the enumeration turns that into a
259
+ # seconds-long diagnosis. Paths and store coordinates only — never any
260
+ # resolved value, on any path.
261
+ echo "Error: could not resolve NOTION_API_TOKEN through lisa-secrets-access or the legacy keychain." >&2
262
+ echo "Tried, in order (relative paths are from $PWD):" >&2
263
+ printf ' %s\n' "${tried[@]}" >&2
264
+ echo " <OS keychain> service=lisa-notion account=$workspace" >&2
265
+ return 1
220
266
  }
221
267
 
222
268
  TOKEN=$(read_notion_token "$WORKSPACE")
@@ -62,9 +62,38 @@ not the only one:
62
62
  read_sonar_secret() {
63
63
  local name="$1"
64
64
  [ -n "${!name:-}" ] && { echo "${!name}"; return; }
65
+ #
66
+ # Ordered repo-first, ending at the plugin's own copy. Repo-relative rungs
67
+ # lead because a project that vendors the resolver has declared which copy it
68
+ # wants used, and that decision must survive this change untouched. The plugin
69
+ # rungs are the floor: `resolve-secret.mjs` ships beside this skill, so a rung
70
+ # pointing at it is reachable from anywhere the plugin itself is installed.
71
+ # Without one, a consumer repository that vendors none of the leading paths
72
+ # never reaches a resolver at all — the ladder exits without having asked
73
+ # anything, which is what pushed agents into improvising their own credential
74
+ # lookups. This LADDER is identical in every skill that resolves a credential
75
+ # and `credential-resolver-ladder` fails if any copy diverges. Only what
76
+ # happens AFTER the ladder may differ between them.
77
+ local candidates=(
78
+ .claude/skills/lisa-secrets-access/scripts/resolve-secret.mjs
79
+ .agents/skills/lisa-secrets-access/scripts/resolve-secret.mjs
80
+ .opencode/skills/lisa/lisa-secrets-access/scripts/resolve-secret.mjs
81
+ .codex/skills/lisa/lisa-secrets-access/scripts/resolve-secret.mjs
82
+ )
83
+ if [ -n "${CLAUDE_PLUGIN_ROOT:-}" ]; then
84
+ candidates+=("$CLAUDE_PLUGIN_ROOT/skills/lisa-secrets-access/scripts/resolve-secret.mjs")
85
+ fi
86
+ if [ -n "${PLUGIN_ROOT:-}" ]; then
87
+ candidates+=("$PLUGIN_ROOT/skills/lisa-secrets-access/scripts/resolve-secret.mjs")
88
+ fi
89
+ # Last rung deliberately needs no environment variable: an agent that was
90
+ # never handed a plugin root still has the installed package to fall back on.
91
+ candidates+=(node_modules/@codyswann/lisa/plugins/lisa/skills/lisa-secrets-access/scripts/resolve-secret.mjs)
92
+
65
93
  local resolver
66
- for resolver in .claude/skills/lisa-secrets-access/scripts/resolve-secret.mjs \
67
- .agents/skills/lisa-secrets-access/scripts/resolve-secret.mjs; do
94
+ local tried=()
95
+ for resolver in "${candidates[@]}"; do
96
+ tried+=("$resolver")
68
97
  if [ -f "$resolver" ]; then
69
98
  local via_lisa
70
99
  via_lisa=$(node "$resolver" get "$name" 2>/dev/null) \
@@ -72,6 +101,13 @@ read_sonar_secret() {
72
101
  break
73
102
  fi
74
103
  done
104
+ # Name every path. A bare `return 1` sends the next reader hunting for a
105
+ # resolver they cannot see the absence of; the enumeration turns that into a
106
+ # seconds-long diagnosis. Paths and store coordinates only — never any
107
+ # resolved value, on any path.
108
+ echo "Error: could not resolve $name through lisa-secrets-access." >&2
109
+ echo "Tried, in order (relative paths are from $PWD):" >&2
110
+ printf ' %s\n' "${tried[@]}" >&2
75
111
  return 1
76
112
  }
77
113
 
@@ -81,11 +117,17 @@ SONARQUBE_CLI_TOKEN=$(read_sonar_secret SONARQUBE_CLI_TOKEN) && export SONARQUBE
81
117
  SONARQUBE_CLI_ORG=$(read_sonar_secret SONARQUBE_CLI_ORG) && export SONARQUBE_CLI_ORG
82
118
  ```
83
119
 
84
- `sonar-secrets.sh` runs the same resolution with a longer search path (it also
85
- probes `.codex/skills/…` and `node_modules/@codyswann/lisa/plugins/lisa/…`,
86
- because a hook fires in checkouts that never installed an agent plugin) and with
87
- a timeout, because it sits in front of every prompt. Use its search order
88
- verbatim when this skill runs inside a hook.
120
+ `sonar-secrets.sh` runs the same resolution with two differences that belong to
121
+ a hook rather than to a skill: it anchors every candidate at `$repo_root` (a
122
+ hook fires from whatever directory the agent happens to be in) and it wraps the
123
+ `resolve-secret.mjs` call in a timeout, because it sits in front of every
124
+ prompt. Use its search order verbatim when this skill runs inside a hook.
125
+
126
+ This paragraph used to claim the hook searched a *longer* path than the ladder
127
+ above. It did — the ladder above stopped after `.agents/`, so in a checkout
128
+ that vendored neither leading path it reached no resolver at all and returned
129
+ without a word. The two now cover the same layouts, and the fix that closed
130
+ that gap is guarded by `credential-resolver-ladder`.
89
131
 
90
132
  Wiring is performed once by `/lisa:setup:sonar` (which drives `sonar integrate
91
133
  <agent>`); this access layer assumes the MCP is already wired. This is distinct
@@ -347,7 +347,6 @@ export const STRYKER_PARSED_EXTENSIONS = Object.freeze([
347
347
  ".jsx",
348
348
  ".cjs",
349
349
  ".mjs",
350
- ".cjsx",
351
350
  ".ts",
352
351
  ".tsx",
353
352
  ".cts",
@@ -387,11 +386,14 @@ export const UNINSTRUMENTABLE_GUARD_EXTENSIONS = Object.freeze([
387
386
  * @returns {string[]} Those whose extension no mutation tool here reaches.
388
387
  */
389
388
  export const uninstrumentableGuards = files =>
390
- files.filter(file =>
391
- UNINSTRUMENTABLE_GUARD_EXTENSIONS.includes(
392
- path.extname(normalizePath(file)).toLowerCase()
393
- )
394
- );
389
+ files.filter(file => {
390
+ const normalized = normalizePath(file);
391
+ const extension = path.extname(normalized).toLowerCase();
392
+ return (
393
+ UNINSTRUMENTABLE_GUARD_EXTENSIONS.includes(extension) ||
394
+ (extension === "" && normalized.startsWith(".husky/"))
395
+ );
396
+ });
395
397
 
396
398
  /**
397
399
  * Whether Stryker can parse a file at all, by extension.
@@ -1313,7 +1315,7 @@ export const selectChangedTargets = (cwd, base, patterns) => {
1313
1315
  const changed = git(cwd, [
1314
1316
  "diff",
1315
1317
  "--name-only",
1316
- "--diff-filter=ACMR",
1318
+ "--diff-filter=ACMRD",
1317
1319
  `${base}...HEAD`,
1318
1320
  ])
1319
1321
  .split("\n")
@@ -1326,9 +1328,7 @@ export const selectChangedTargets = (cwd, base, patterns) => {
1326
1328
  .filter(file => fs.existsSync(path.join(cwd, file))),
1327
1329
  // Reported from the diff, not from the selection, because by construction
1328
1330
  // these can never BE in the selection — that is the whole point of them.
1329
- uninstrumentable: uninstrumentableGuards(changed).filter(file =>
1330
- fs.existsSync(path.join(cwd, file))
1331
- ),
1331
+ uninstrumentable: uninstrumentableGuards(changed),
1332
1332
  };
1333
1333
  };
1334
1334
 
@@ -2090,27 +2090,26 @@ export const runGate = (cwd = process.cwd(), argv = []) => {
2090
2090
  return 1;
2091
2091
  }
2092
2092
 
2093
- if (scope.selected.length === 0 && scope.uninstrumentable.length > 0) {
2094
- // Exit 0, like `nothing-to-mutate`, because nothing here is a test failure
2095
- // — but under its own marker, because the claim is the opposite one. That
2096
- // branch says nothing this gate cares about changed. This one says a guard
2097
- // changed and this gate is structurally blind to it, so the absence of red
2098
- // is a property of the toolchain rather than of the tests.
2093
+ if (scope.uninstrumentable.length > 0) {
2094
+ // Always name the blind part of the diff, even when another selected target
2095
+ // lets Stryker continue. A mixed run measures only the selected files; its
2096
+ // score is not evidence about a shell guard changed beside them.
2099
2097
  const blind = scope.uninstrumentable.map(file => ` • ${file}`).join("\n");
2100
2098
  console.log(
2101
2099
  `⚪ ${OUTCOMES.uninstrumentableLanguage}\n` +
2102
- ` ${scope.changed} file(s) changed vs ${since}; 0 of them are mutate targets\n` +
2103
- ` under the patterns from ${declaration.source} — but ${scope.uninstrumentable.length} of them\n` +
2100
+ ` ${scope.changed} file(s) changed vs ${since}; ${scope.selected.length} of them are mutate targets\n` +
2101
+ ` under the patterns from ${declaration.source}, and ${scope.uninstrumentable.length} of them\n` +
2104
2102
  ` ${scope.uninstrumentable.length === 1 ? "is" : "are"} in a language Stryker cannot instrument in ANY configuration:\n` +
2105
2103
  `${blind}\n` +
2106
- ' This is NOT "nothing relevant changed". NO mutant COULD be generated\n' +
2104
+ " NO mutant COULD be generated for these files. The mutation result\n" +
2105
+ " below, if one runs, covers only the selected targets and says nothing\n" +
2107
2106
  " for these files, so this gate is silent about them by construction —\n" +
2108
2107
  " adding them to `mutate` would abort the run, not measure them.\n" +
2109
2108
  " Their only evidence is a driving test that runs the script against a\n" +
2110
2109
  " payload table and asserts the blocked/allowed verdict, with a control\n" +
2111
2110
  " on both sides. Check that one exists; nothing here did."
2112
2111
  );
2113
- return 0;
2112
+ if (scope.selected.length === 0) return 0;
2114
2113
  }
2115
2114
 
2116
2115
  if (scope.selected.length === 0) {