@codyswann/lisa 4.27.0 → 4.27.1

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 (59) hide show
  1. package/dist/core/nightly-e2e-guard-behavior-certificate.js +2 -2
  2. package/dist/core/upstream-evidence-manifest.js +1 -1
  3. package/package.json +3 -3
  4. package/plugins/lisa/.claude-plugin/plugin.json +1 -1
  5. package/plugins/lisa/.codex-plugin/plugin.json +1 -1
  6. package/plugins/lisa/.codex-plugin/skills/lisa-linear-access/SKILL.md +39 -4
  7. package/plugins/lisa/skills/lisa-linear-access/SKILL.md +39 -4
  8. package/plugins/lisa-agy/plugin.json +1 -1
  9. package/plugins/lisa-agy/skills/lisa-linear-access/SKILL.md +39 -4
  10. package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
  11. package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
  12. package/plugins/lisa-cdk-agy/plugin.json +1 -1
  13. package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
  14. package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
  15. package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
  16. package/plugins/lisa-copilot/skills/lisa-linear-access/SKILL.md +39 -4
  17. package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
  18. package/plugins/lisa-cursor/skills/lisa-linear-access/SKILL.md +39 -4
  19. package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
  20. package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
  21. package/plugins/lisa-expo-agy/plugin.json +1 -1
  22. package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
  23. package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
  24. package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
  25. package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
  26. package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
  27. package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
  28. package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
  29. package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
  30. package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
  31. package/plugins/lisa-nestjs-agy/plugin.json +1 -1
  32. package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
  33. package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
  34. package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
  35. package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
  36. package/plugins/lisa-openclaw-agy/plugin.json +1 -1
  37. package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
  38. package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
  39. package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
  40. package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
  41. package/plugins/lisa-phaser-agy/plugin.json +1 -1
  42. package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
  43. package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
  44. package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
  45. package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
  46. package/plugins/lisa-rails-agy/plugin.json +1 -1
  47. package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
  48. package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
  49. package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
  50. package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
  51. package/plugins/lisa-typescript-agy/plugin.json +1 -1
  52. package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
  53. package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
  54. package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
  55. package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
  56. package/plugins/lisa-wiki-agy/plugin.json +1 -1
  57. package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
  58. package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
  59. package/plugins/src/base/skills/lisa-linear-access/SKILL.md +39 -4
@@ -20,9 +20,9 @@ export const NIGHTLY_E2E_GUARD_BEHAVIOR_CERTIFICATES = Object.freeze({
20
20
  }),
21
21
  "312e8962e21be2f3c6c6c0235b0c6159b8dc39425508b4965c7df87fb1333380": Object.freeze({
22
22
  contractVersion: "1.9.0",
23
- packageVersions: Object.freeze(["4.27.0"]),
23
+ packageVersions: Object.freeze(["4.27.1"]),
24
24
  provenances: Object.freeze([
25
- "workspace package @codyswann/lisa@4.27.0 (typescript/copy-overwrite/scripts/check-nightly-e2e-health.mjs)",
25
+ "workspace package @codyswann/lisa@4.27.1 (typescript/copy-overwrite/scripts/check-nightly-e2e-health.mjs)",
26
26
  ]),
27
27
  }),
28
28
  "898ee0247806c7aa6e98328662d80bcff22ff067999298865462a58daef8bb22": Object.freeze({
@@ -589,7 +589,7 @@ export const UPSTREAM_EVIDENCE_MANIFEST = Object.freeze({
589
589
  "plugins/src/base/skills/lisa-jsdoc-best-practices/SKILL.md": "7cf0729966ac0afcab6e2c043f192772a9e9e7c72fae61e55b300e97308f970e",
590
590
  "plugins/src/base/skills/lisa-kane-browser/SKILL.md": "e082517cb42f437eeb22b3935aa0fef09299874736e55184e88d2b64f7860a7d",
591
591
  "plugins/src/base/skills/lisa-learnings-audit/SKILL.md": "98a1c4916f376a3f85008c43a4dde033c374ed0a6111a8094f72296973c5d21b",
592
- "plugins/src/base/skills/lisa-linear-access/SKILL.md": "b4d1121a7d1938ccd1f24516eec12955bd6131523337bc1432ed7152750b9260",
592
+ "plugins/src/base/skills/lisa-linear-access/SKILL.md": "f6644b11ad2d730795471acf826e52b284e310de70923b65a78f14b58242cf15",
593
593
  "plugins/src/base/skills/lisa-linear-add-journey/SKILL.md": "0a9f2bd0e19fbaa4794537e436c22748c7621644253a91923d84bc77ca43f793",
594
594
  "plugins/src/base/skills/lisa-linear-build-intake/SKILL.md": "9c2d59a903ef2778ccf249136d8b561a6c381596631b6d35e6f469fd74497cee",
595
595
  "plugins/src/base/skills/lisa-linear-claim/SKILL.md": "d3fee9a0435e93402be53769313f971c77b847521826fbaee0d19a0bdbc34b93",
package/package.json CHANGED
@@ -161,7 +161,7 @@
161
161
  "zod-validation-error": "^4.0.0"
162
162
  },
163
163
  "name": "@codyswann/lisa",
164
- "version": "4.27.0",
164
+ "version": "4.27.1",
165
165
  "description": "Claude Code governance framework that applies guardrails, guidance, and automated enforcement to projects",
166
166
  "main": "dist/index.js",
167
167
  "exports": {
@@ -305,6 +305,6 @@
305
305
  "test": "tests"
306
306
  },
307
307
  "types": "./dist/index.d.ts",
308
- "lisaReleaseCommit": "f6c60240380f0355137d58b67b745dfda9dcf06e",
309
- "gitHead": "f6c60240380f0355137d58b67b745dfda9dcf06e"
308
+ "lisaReleaseCommit": "eafbdc0e2ad81b0b4a7a64024475332e0a06df95",
309
+ "gitHead": "eafbdc0e2ad81b0b4a7a64024475332e0a06df95"
310
310
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa",
3
- "version": "4.27.0",
3
+ "version": "4.27.1",
4
4
  "description": "Universal governance — agents, skills, commands, hooks, and rules for all projects",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa",
3
- "version": "4.27.0",
3
+ "version": "4.27.1",
4
4
  "description": "Universal governance: agents, skills, commands, hooks, and rules for all projects.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -82,9 +82,43 @@ All GraphQL calls use:
82
82
  # or AWS has no path to the key at all — the ladder stopped at rung one, which
83
83
  # is also what left `/lisa:setup:linear` reading an OS keychain directly with
84
84
  # nowhere to migrate to. Mirrors `atlassian-access` and `notion-access`.
85
- read_linear_key() {
85
+ read_linear_key() { # $1=workspace slug; defaults to $WORKSPACE read from config
86
86
  [ -n "${LINEAR_API_KEY:-}" ] && { echo "$LINEAR_API_KEY"; return; }
87
87
 
88
+ # Per-workspace rung, matching `atlassian-access` and `notion-access` — and
89
+ # matching what `setup-linear` has always told the reader to export. This
90
+ # copy lacked it, so a key set the documented way validated during setup (the
91
+ # setup skill reads it) and was then ignored by every real Linear call, which
92
+ # silently fell through to whatever other credential it found. Nothing
93
+ # errored; the operation just ran as a different identity. The rung is also
94
+ # load-bearing headlessly: `analyze-claude-remote` reports Linear's env as
95
+ # `LINEAR_API_KEY` "or per-account `LINEAR_API_KEY_<ws-slug>`", and
96
+ # `generate-claude-remote-build-script` emits that suffixed name into cloud
97
+ # routine env templates — so a routine provisioned from those skills had no
98
+ # reachable key at all.
99
+ #
100
+ # An unset variable changes nothing: an empty workspace skips the rung
101
+ # entirely and the ladder below runs exactly as it did before.
102
+ local ws="${1:-${WORKSPACE:-}}"
103
+ if [ -n "$ws" ]; then
104
+ local slug; slug=$(echo "$ws" | tr '[:upper:]-' '[:lower:]_')
105
+ # Constrain the slug before it reaches `${!varname}`. Indirect expansion
106
+ # evaluates an array subscript, so a workspace carrying `[$(...)]` would
107
+ # RUN that command here — and `tr` above only folds case and dashes, it
108
+ # strips nothing. The slug's source is `.lisa.config.json`, which is
109
+ # repository-controlled input: exactly what this ladder already refuses to
110
+ # trust when it picks resolvers. A real workspace slug is lowercase
111
+ # alphanumerics and underscores, so anything else is not a slug we should
112
+ # be expanding — skip the rung and fall through rather than guessing.
113
+ case "$slug" in
114
+ ""|*[!a-z0-9_]*) ;;
115
+ *)
116
+ local varname="LINEAR_API_KEY_${slug}"
117
+ [ -n "${!varname:-}" ] && { echo "${!varname}"; return; }
118
+ ;;
119
+ esac
120
+ fi
121
+
88
122
  # The ladder is ordered across trusted machine-managed substrates and ends at
89
123
  # the installed package. Checkout-local paths are deliberately absent: a
90
124
  # familiar generated destination is still repository-controlled executable
@@ -132,9 +166,10 @@ linear_graphql() {
132
166
  local query="$1"
133
167
  local variables="${2:-{}}"
134
168
  local key
135
- key=$(read_linear_key) || {
136
- echo "Error: no Linear API key. Set LINEAR_API_KEY, or store it as" >&2
137
- echo "LINEAR_API_KEY in this project's secrets provider." >&2
169
+ key=$(read_linear_key "${WORKSPACE:-}") || {
170
+ echo "Error: no Linear API key. Set LINEAR_API_KEY (or the per-workspace" >&2
171
+ echo "LINEAR_API_KEY_<slug>), or store it as LINEAR_API_KEY in this" >&2
172
+ echo "project's secrets provider." >&2
138
173
  return 1
139
174
  }
140
175
  jq -n --arg query "$query" --argjson variables "$variables" \
@@ -82,9 +82,43 @@ All GraphQL calls use:
82
82
  # or AWS has no path to the key at all — the ladder stopped at rung one, which
83
83
  # is also what left `/lisa:setup:linear` reading an OS keychain directly with
84
84
  # nowhere to migrate to. Mirrors `atlassian-access` and `notion-access`.
85
- read_linear_key() {
85
+ read_linear_key() { # $1=workspace slug; defaults to $WORKSPACE read from config
86
86
  [ -n "${LINEAR_API_KEY:-}" ] && { echo "$LINEAR_API_KEY"; return; }
87
87
 
88
+ # Per-workspace rung, matching `atlassian-access` and `notion-access` — and
89
+ # matching what `setup-linear` has always told the reader to export. This
90
+ # copy lacked it, so a key set the documented way validated during setup (the
91
+ # setup skill reads it) and was then ignored by every real Linear call, which
92
+ # silently fell through to whatever other credential it found. Nothing
93
+ # errored; the operation just ran as a different identity. The rung is also
94
+ # load-bearing headlessly: `analyze-claude-remote` reports Linear's env as
95
+ # `LINEAR_API_KEY` "or per-account `LINEAR_API_KEY_<ws-slug>`", and
96
+ # `generate-claude-remote-build-script` emits that suffixed name into cloud
97
+ # routine env templates — so a routine provisioned from those skills had no
98
+ # reachable key at all.
99
+ #
100
+ # An unset variable changes nothing: an empty workspace skips the rung
101
+ # entirely and the ladder below runs exactly as it did before.
102
+ local ws="${1:-${WORKSPACE:-}}"
103
+ if [ -n "$ws" ]; then
104
+ local slug; slug=$(echo "$ws" | tr '[:upper:]-' '[:lower:]_')
105
+ # Constrain the slug before it reaches `${!varname}`. Indirect expansion
106
+ # evaluates an array subscript, so a workspace carrying `[$(...)]` would
107
+ # RUN that command here — and `tr` above only folds case and dashes, it
108
+ # strips nothing. The slug's source is `.lisa.config.json`, which is
109
+ # repository-controlled input: exactly what this ladder already refuses to
110
+ # trust when it picks resolvers. A real workspace slug is lowercase
111
+ # alphanumerics and underscores, so anything else is not a slug we should
112
+ # be expanding — skip the rung and fall through rather than guessing.
113
+ case "$slug" in
114
+ ""|*[!a-z0-9_]*) ;;
115
+ *)
116
+ local varname="LINEAR_API_KEY_${slug}"
117
+ [ -n "${!varname:-}" ] && { echo "${!varname}"; return; }
118
+ ;;
119
+ esac
120
+ fi
121
+
88
122
  # The ladder is ordered across trusted machine-managed substrates and ends at
89
123
  # the installed package. Checkout-local paths are deliberately absent: a
90
124
  # familiar generated destination is still repository-controlled executable
@@ -132,9 +166,10 @@ linear_graphql() {
132
166
  local query="$1"
133
167
  local variables="${2:-{}}"
134
168
  local key
135
- key=$(read_linear_key) || {
136
- echo "Error: no Linear API key. Set LINEAR_API_KEY, or store it as" >&2
137
- echo "LINEAR_API_KEY in this project's secrets provider." >&2
169
+ key=$(read_linear_key "${WORKSPACE:-}") || {
170
+ echo "Error: no Linear API key. Set LINEAR_API_KEY (or the per-workspace" >&2
171
+ echo "LINEAR_API_KEY_<slug>), or store it as LINEAR_API_KEY in this" >&2
172
+ echo "project's secrets provider." >&2
138
173
  return 1
139
174
  }
140
175
  jq -n --arg query "$query" --argjson variables "$variables" \
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa",
3
- "version": "4.27.0",
3
+ "version": "4.27.1",
4
4
  "description": "Universal governance — agents, skills, commands, hooks, and rules for all projects",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -82,9 +82,43 @@ All GraphQL calls use:
82
82
  # or AWS has no path to the key at all — the ladder stopped at rung one, which
83
83
  # is also what left `/lisa:setup:linear` reading an OS keychain directly with
84
84
  # nowhere to migrate to. Mirrors `atlassian-access` and `notion-access`.
85
- read_linear_key() {
85
+ read_linear_key() { # $1=workspace slug; defaults to $WORKSPACE read from config
86
86
  [ -n "${LINEAR_API_KEY:-}" ] && { echo "$LINEAR_API_KEY"; return; }
87
87
 
88
+ # Per-workspace rung, matching `atlassian-access` and `notion-access` — and
89
+ # matching what `setup-linear` has always told the reader to export. This
90
+ # copy lacked it, so a key set the documented way validated during setup (the
91
+ # setup skill reads it) and was then ignored by every real Linear call, which
92
+ # silently fell through to whatever other credential it found. Nothing
93
+ # errored; the operation just ran as a different identity. The rung is also
94
+ # load-bearing headlessly: `analyze-claude-remote` reports Linear's env as
95
+ # `LINEAR_API_KEY` "or per-account `LINEAR_API_KEY_<ws-slug>`", and
96
+ # `generate-claude-remote-build-script` emits that suffixed name into cloud
97
+ # routine env templates — so a routine provisioned from those skills had no
98
+ # reachable key at all.
99
+ #
100
+ # An unset variable changes nothing: an empty workspace skips the rung
101
+ # entirely and the ladder below runs exactly as it did before.
102
+ local ws="${1:-${WORKSPACE:-}}"
103
+ if [ -n "$ws" ]; then
104
+ local slug; slug=$(echo "$ws" | tr '[:upper:]-' '[:lower:]_')
105
+ # Constrain the slug before it reaches `${!varname}`. Indirect expansion
106
+ # evaluates an array subscript, so a workspace carrying `[$(...)]` would
107
+ # RUN that command here — and `tr` above only folds case and dashes, it
108
+ # strips nothing. The slug's source is `.lisa.config.json`, which is
109
+ # repository-controlled input: exactly what this ladder already refuses to
110
+ # trust when it picks resolvers. A real workspace slug is lowercase
111
+ # alphanumerics and underscores, so anything else is not a slug we should
112
+ # be expanding — skip the rung and fall through rather than guessing.
113
+ case "$slug" in
114
+ ""|*[!a-z0-9_]*) ;;
115
+ *)
116
+ local varname="LINEAR_API_KEY_${slug}"
117
+ [ -n "${!varname:-}" ] && { echo "${!varname}"; return; }
118
+ ;;
119
+ esac
120
+ fi
121
+
88
122
  # The ladder is ordered across trusted machine-managed substrates and ends at
89
123
  # the installed package. Checkout-local paths are deliberately absent: a
90
124
  # familiar generated destination is still repository-controlled executable
@@ -132,9 +166,10 @@ linear_graphql() {
132
166
  local query="$1"
133
167
  local variables="${2:-{}}"
134
168
  local key
135
- key=$(read_linear_key) || {
136
- echo "Error: no Linear API key. Set LINEAR_API_KEY, or store it as" >&2
137
- echo "LINEAR_API_KEY in this project's secrets provider." >&2
169
+ key=$(read_linear_key "${WORKSPACE:-}") || {
170
+ echo "Error: no Linear API key. Set LINEAR_API_KEY (or the per-workspace" >&2
171
+ echo "LINEAR_API_KEY_<slug>), or store it as LINEAR_API_KEY in this" >&2
172
+ echo "project's secrets provider." >&2
138
173
  return 1
139
174
  }
140
175
  jq -n --arg query "$query" --argjson variables "$variables" \
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-cdk",
3
- "version": "4.27.0",
3
+ "version": "4.27.1",
4
4
  "description": "AWS CDK-specific plugin",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-cdk",
3
- "version": "4.27.0",
3
+ "version": "4.27.1",
4
4
  "description": "AWS CDK-specific Lisa plugin.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-cdk",
3
- "version": "4.27.0",
3
+ "version": "4.27.1",
4
4
  "description": "AWS CDK-specific plugin",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-cdk",
3
- "version": "4.27.0",
3
+ "version": "4.27.1",
4
4
  "description": "AWS CDK-specific plugin",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-cdk",
3
- "version": "4.27.0",
3
+ "version": "4.27.1",
4
4
  "description": "AWS CDK-specific plugin",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa",
3
- "version": "4.27.0",
3
+ "version": "4.27.1",
4
4
  "description": "Universal governance — agents, skills, commands, hooks, and rules for all projects",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -82,9 +82,43 @@ All GraphQL calls use:
82
82
  # or AWS has no path to the key at all — the ladder stopped at rung one, which
83
83
  # is also what left `/lisa:setup:linear` reading an OS keychain directly with
84
84
  # nowhere to migrate to. Mirrors `atlassian-access` and `notion-access`.
85
- read_linear_key() {
85
+ read_linear_key() { # $1=workspace slug; defaults to $WORKSPACE read from config
86
86
  [ -n "${LINEAR_API_KEY:-}" ] && { echo "$LINEAR_API_KEY"; return; }
87
87
 
88
+ # Per-workspace rung, matching `atlassian-access` and `notion-access` — and
89
+ # matching what `setup-linear` has always told the reader to export. This
90
+ # copy lacked it, so a key set the documented way validated during setup (the
91
+ # setup skill reads it) and was then ignored by every real Linear call, which
92
+ # silently fell through to whatever other credential it found. Nothing
93
+ # errored; the operation just ran as a different identity. The rung is also
94
+ # load-bearing headlessly: `analyze-claude-remote` reports Linear's env as
95
+ # `LINEAR_API_KEY` "or per-account `LINEAR_API_KEY_<ws-slug>`", and
96
+ # `generate-claude-remote-build-script` emits that suffixed name into cloud
97
+ # routine env templates — so a routine provisioned from those skills had no
98
+ # reachable key at all.
99
+ #
100
+ # An unset variable changes nothing: an empty workspace skips the rung
101
+ # entirely and the ladder below runs exactly as it did before.
102
+ local ws="${1:-${WORKSPACE:-}}"
103
+ if [ -n "$ws" ]; then
104
+ local slug; slug=$(echo "$ws" | tr '[:upper:]-' '[:lower:]_')
105
+ # Constrain the slug before it reaches `${!varname}`. Indirect expansion
106
+ # evaluates an array subscript, so a workspace carrying `[$(...)]` would
107
+ # RUN that command here — and `tr` above only folds case and dashes, it
108
+ # strips nothing. The slug's source is `.lisa.config.json`, which is
109
+ # repository-controlled input: exactly what this ladder already refuses to
110
+ # trust when it picks resolvers. A real workspace slug is lowercase
111
+ # alphanumerics and underscores, so anything else is not a slug we should
112
+ # be expanding — skip the rung and fall through rather than guessing.
113
+ case "$slug" in
114
+ ""|*[!a-z0-9_]*) ;;
115
+ *)
116
+ local varname="LINEAR_API_KEY_${slug}"
117
+ [ -n "${!varname:-}" ] && { echo "${!varname}"; return; }
118
+ ;;
119
+ esac
120
+ fi
121
+
88
122
  # The ladder is ordered across trusted machine-managed substrates and ends at
89
123
  # the installed package. Checkout-local paths are deliberately absent: a
90
124
  # familiar generated destination is still repository-controlled executable
@@ -132,9 +166,10 @@ linear_graphql() {
132
166
  local query="$1"
133
167
  local variables="${2:-{}}"
134
168
  local key
135
- key=$(read_linear_key) || {
136
- echo "Error: no Linear API key. Set LINEAR_API_KEY, or store it as" >&2
137
- echo "LINEAR_API_KEY in this project's secrets provider." >&2
169
+ key=$(read_linear_key "${WORKSPACE:-}") || {
170
+ echo "Error: no Linear API key. Set LINEAR_API_KEY (or the per-workspace" >&2
171
+ echo "LINEAR_API_KEY_<slug>), or store it as LINEAR_API_KEY in this" >&2
172
+ echo "project's secrets provider." >&2
138
173
  return 1
139
174
  }
140
175
  jq -n --arg query "$query" --argjson variables "$variables" \
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa",
3
- "version": "4.27.0",
3
+ "version": "4.27.1",
4
4
  "description": "Universal governance — agents, skills, commands, hooks, and rules for all projects",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -82,9 +82,43 @@ All GraphQL calls use:
82
82
  # or AWS has no path to the key at all — the ladder stopped at rung one, which
83
83
  # is also what left `/lisa:setup:linear` reading an OS keychain directly with
84
84
  # nowhere to migrate to. Mirrors `atlassian-access` and `notion-access`.
85
- read_linear_key() {
85
+ read_linear_key() { # $1=workspace slug; defaults to $WORKSPACE read from config
86
86
  [ -n "${LINEAR_API_KEY:-}" ] && { echo "$LINEAR_API_KEY"; return; }
87
87
 
88
+ # Per-workspace rung, matching `atlassian-access` and `notion-access` — and
89
+ # matching what `setup-linear` has always told the reader to export. This
90
+ # copy lacked it, so a key set the documented way validated during setup (the
91
+ # setup skill reads it) and was then ignored by every real Linear call, which
92
+ # silently fell through to whatever other credential it found. Nothing
93
+ # errored; the operation just ran as a different identity. The rung is also
94
+ # load-bearing headlessly: `analyze-claude-remote` reports Linear's env as
95
+ # `LINEAR_API_KEY` "or per-account `LINEAR_API_KEY_<ws-slug>`", and
96
+ # `generate-claude-remote-build-script` emits that suffixed name into cloud
97
+ # routine env templates — so a routine provisioned from those skills had no
98
+ # reachable key at all.
99
+ #
100
+ # An unset variable changes nothing: an empty workspace skips the rung
101
+ # entirely and the ladder below runs exactly as it did before.
102
+ local ws="${1:-${WORKSPACE:-}}"
103
+ if [ -n "$ws" ]; then
104
+ local slug; slug=$(echo "$ws" | tr '[:upper:]-' '[:lower:]_')
105
+ # Constrain the slug before it reaches `${!varname}`. Indirect expansion
106
+ # evaluates an array subscript, so a workspace carrying `[$(...)]` would
107
+ # RUN that command here — and `tr` above only folds case and dashes, it
108
+ # strips nothing. The slug's source is `.lisa.config.json`, which is
109
+ # repository-controlled input: exactly what this ladder already refuses to
110
+ # trust when it picks resolvers. A real workspace slug is lowercase
111
+ # alphanumerics and underscores, so anything else is not a slug we should
112
+ # be expanding — skip the rung and fall through rather than guessing.
113
+ case "$slug" in
114
+ ""|*[!a-z0-9_]*) ;;
115
+ *)
116
+ local varname="LINEAR_API_KEY_${slug}"
117
+ [ -n "${!varname:-}" ] && { echo "${!varname}"; return; }
118
+ ;;
119
+ esac
120
+ fi
121
+
88
122
  # The ladder is ordered across trusted machine-managed substrates and ends at
89
123
  # the installed package. Checkout-local paths are deliberately absent: a
90
124
  # familiar generated destination is still repository-controlled executable
@@ -132,9 +166,10 @@ linear_graphql() {
132
166
  local query="$1"
133
167
  local variables="${2:-{}}"
134
168
  local key
135
- key=$(read_linear_key) || {
136
- echo "Error: no Linear API key. Set LINEAR_API_KEY, or store it as" >&2
137
- echo "LINEAR_API_KEY in this project's secrets provider." >&2
169
+ key=$(read_linear_key "${WORKSPACE:-}") || {
170
+ echo "Error: no Linear API key. Set LINEAR_API_KEY (or the per-workspace" >&2
171
+ echo "LINEAR_API_KEY_<slug>), or store it as LINEAR_API_KEY in this" >&2
172
+ echo "project's secrets provider." >&2
138
173
  return 1
139
174
  }
140
175
  jq -n --arg query "$query" --argjson variables "$variables" \
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-expo",
3
- "version": "4.27.0",
3
+ "version": "4.27.1",
4
4
  "description": "Expo/React Native-specific skills, agents, rules, and MCP servers",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-expo",
3
- "version": "4.27.0",
3
+ "version": "4.27.1",
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": "4.27.0",
3
+ "version": "4.27.1",
4
4
  "description": "Expo/React Native-specific skills, agents, rules, and MCP servers",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-expo",
3
- "version": "4.27.0",
3
+ "version": "4.27.1",
4
4
  "description": "Expo/React Native-specific skills, agents, rules, and MCP servers",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-expo",
3
- "version": "4.27.0",
3
+ "version": "4.27.1",
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": "4.27.0",
3
+ "version": "4.27.1",
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": "4.27.0",
3
+ "version": "4.27.1",
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": "4.27.0",
3
+ "version": "4.27.1",
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": "4.27.0",
3
+ "version": "4.27.1",
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": "4.27.0",
3
+ "version": "4.27.1",
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": "4.27.0",
3
+ "version": "4.27.1",
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": "4.27.0",
3
+ "version": "4.27.1",
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": "4.27.0",
3
+ "version": "4.27.1",
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": "4.27.0",
3
+ "version": "4.27.1",
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": "4.27.0",
3
+ "version": "4.27.1",
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": "4.27.0",
3
+ "version": "4.27.1",
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": "4.27.0",
3
+ "version": "4.27.1",
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": "4.27.0",
3
+ "version": "4.27.1",
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": "4.27.0",
3
+ "version": "4.27.1",
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": "4.27.0",
3
+ "version": "4.27.1",
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": "4.27.0",
3
+ "version": "4.27.1",
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": "4.27.0",
3
+ "version": "4.27.1",
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": "4.27.0",
3
+ "version": "4.27.1",
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": "4.27.0",
3
+ "version": "4.27.1",
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": "4.27.0",
3
+ "version": "4.27.1",
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": "4.27.0",
3
+ "version": "4.27.1",
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": "4.27.0",
3
+ "version": "4.27.1",
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": "4.27.0",
3
+ "version": "4.27.1",
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": "4.27.0",
3
+ "version": "4.27.1",
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": "4.27.0",
3
+ "version": "4.27.1",
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": "4.27.0",
3
+ "version": "4.27.1",
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": "4.27.0",
3
+ "version": "4.27.1",
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": "4.27.0",
3
+ "version": "4.27.1",
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": "4.27.0",
3
+ "version": "4.27.1",
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": "4.27.0",
3
+ "version": "4.27.1",
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": "4.27.0",
3
+ "version": "4.27.1",
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": "4.27.0",
3
+ "version": "4.27.1",
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": "4.27.0",
3
+ "version": "4.27.1",
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": "4.27.0",
3
+ "version": "4.27.1",
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": "4.27.0",
3
+ "version": "4.27.1",
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"
@@ -82,9 +82,43 @@ All GraphQL calls use:
82
82
  # or AWS has no path to the key at all — the ladder stopped at rung one, which
83
83
  # is also what left `/lisa:setup:linear` reading an OS keychain directly with
84
84
  # nowhere to migrate to. Mirrors `atlassian-access` and `notion-access`.
85
- read_linear_key() {
85
+ read_linear_key() { # $1=workspace slug; defaults to $WORKSPACE read from config
86
86
  [ -n "${LINEAR_API_KEY:-}" ] && { echo "$LINEAR_API_KEY"; return; }
87
87
 
88
+ # Per-workspace rung, matching `atlassian-access` and `notion-access` — and
89
+ # matching what `setup-linear` has always told the reader to export. This
90
+ # copy lacked it, so a key set the documented way validated during setup (the
91
+ # setup skill reads it) and was then ignored by every real Linear call, which
92
+ # silently fell through to whatever other credential it found. Nothing
93
+ # errored; the operation just ran as a different identity. The rung is also
94
+ # load-bearing headlessly: `analyze-claude-remote` reports Linear's env as
95
+ # `LINEAR_API_KEY` "or per-account `LINEAR_API_KEY_<ws-slug>`", and
96
+ # `generate-claude-remote-build-script` emits that suffixed name into cloud
97
+ # routine env templates — so a routine provisioned from those skills had no
98
+ # reachable key at all.
99
+ #
100
+ # An unset variable changes nothing: an empty workspace skips the rung
101
+ # entirely and the ladder below runs exactly as it did before.
102
+ local ws="${1:-${WORKSPACE:-}}"
103
+ if [ -n "$ws" ]; then
104
+ local slug; slug=$(echo "$ws" | tr '[:upper:]-' '[:lower:]_')
105
+ # Constrain the slug before it reaches `${!varname}`. Indirect expansion
106
+ # evaluates an array subscript, so a workspace carrying `[$(...)]` would
107
+ # RUN that command here — and `tr` above only folds case and dashes, it
108
+ # strips nothing. The slug's source is `.lisa.config.json`, which is
109
+ # repository-controlled input: exactly what this ladder already refuses to
110
+ # trust when it picks resolvers. A real workspace slug is lowercase
111
+ # alphanumerics and underscores, so anything else is not a slug we should
112
+ # be expanding — skip the rung and fall through rather than guessing.
113
+ case "$slug" in
114
+ ""|*[!a-z0-9_]*) ;;
115
+ *)
116
+ local varname="LINEAR_API_KEY_${slug}"
117
+ [ -n "${!varname:-}" ] && { echo "${!varname}"; return; }
118
+ ;;
119
+ esac
120
+ fi
121
+
88
122
  # The ladder is ordered across trusted machine-managed substrates and ends at
89
123
  # the installed package. Checkout-local paths are deliberately absent: a
90
124
  # familiar generated destination is still repository-controlled executable
@@ -132,9 +166,10 @@ linear_graphql() {
132
166
  local query="$1"
133
167
  local variables="${2:-{}}"
134
168
  local key
135
- key=$(read_linear_key) || {
136
- echo "Error: no Linear API key. Set LINEAR_API_KEY, or store it as" >&2
137
- echo "LINEAR_API_KEY in this project's secrets provider." >&2
169
+ key=$(read_linear_key "${WORKSPACE:-}") || {
170
+ echo "Error: no Linear API key. Set LINEAR_API_KEY (or the per-workspace" >&2
171
+ echo "LINEAR_API_KEY_<slug>), or store it as LINEAR_API_KEY in this" >&2
172
+ echo "project's secrets provider." >&2
138
173
  return 1
139
174
  }
140
175
  jq -n --arg query "$query" --argjson variables "$variables" \