@codyswann/lisa 2.226.0 → 2.226.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 (61) hide show
  1. package/package.json +1 -1
  2. package/plugins/lisa/.claude-plugin/plugin.json +1 -1
  3. package/plugins/lisa/.codex-plugin/plugin.json +1 -1
  4. package/plugins/lisa/scripts/install-remote-agent-aws.mjs +109 -10
  5. package/plugins/lisa/scripts/remote-agent-aws-setup.sh +79 -9
  6. package/plugins/lisa-agy/plugin.json +1 -1
  7. package/plugins/lisa-agy/scripts/install-remote-agent-aws.mjs +109 -10
  8. package/plugins/lisa-agy/scripts/remote-agent-aws-setup.sh +79 -9
  9. package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
  10. package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
  11. package/plugins/lisa-cdk-agy/plugin.json +1 -1
  12. package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
  13. package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
  14. package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
  15. package/plugins/lisa-copilot/scripts/install-remote-agent-aws.mjs +109 -10
  16. package/plugins/lisa-copilot/scripts/remote-agent-aws-setup.sh +79 -9
  17. package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
  18. package/plugins/lisa-cursor/scripts/install-remote-agent-aws.mjs +109 -10
  19. package/plugins/lisa-cursor/scripts/remote-agent-aws-setup.sh +79 -9
  20. package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
  21. package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
  22. package/plugins/lisa-expo-agy/plugin.json +1 -1
  23. package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
  24. package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
  25. package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
  26. package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
  27. package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
  28. package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
  29. package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
  30. package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
  31. package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
  32. package/plugins/lisa-nestjs-agy/plugin.json +1 -1
  33. package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
  34. package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
  35. package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
  36. package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
  37. package/plugins/lisa-openclaw-agy/plugin.json +1 -1
  38. package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
  39. package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
  40. package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
  41. package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
  42. package/plugins/lisa-phaser-agy/plugin.json +1 -1
  43. package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
  44. package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
  45. package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
  46. package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
  47. package/plugins/lisa-rails-agy/plugin.json +1 -1
  48. package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
  49. package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
  50. package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
  51. package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
  52. package/plugins/lisa-typescript-agy/plugin.json +1 -1
  53. package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
  54. package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
  55. package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
  56. package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
  57. package/plugins/lisa-wiki-agy/plugin.json +1 -1
  58. package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
  59. package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
  60. package/plugins/src/base/scripts/install-remote-agent-aws.mjs +109 -10
  61. package/plugins/src/base/scripts/remote-agent-aws-setup.sh +79 -9
package/package.json CHANGED
@@ -102,7 +102,7 @@
102
102
  "form-data": ">=4.0.6"
103
103
  },
104
104
  "name": "@codyswann/lisa",
105
- "version": "2.226.0",
105
+ "version": "2.226.2",
106
106
  "description": "Claude Code governance framework that applies guardrails, guidance, and automated enforcement to projects",
107
107
  "main": "dist/index.js",
108
108
  "exports": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa",
3
- "version": "2.226.0",
3
+ "version": "2.226.2",
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": "2.226.0",
3
+ "version": "2.226.2",
4
4
  "description": "Universal governance: agents, skills, commands, hooks, and rules for all projects.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -98,9 +98,17 @@ function installCopilotAdapter(project) {
98
98
  );
99
99
  mkdirSync(path.dirname(workflowPath), { recursive: true });
100
100
  const marker = "Lisa remote AWS bootstrap";
101
+ const secretEnvironment =
102
+ " LISA_AWS_BOOTSTRAP_JSON: ${{ secrets.LISA_AWS_BOOTSTRAP_JSON }}";
103
+ const checkoutStep = [
104
+ " - uses: actions/checkout@v4",
105
+ " with:",
106
+ " persist-credentials: false",
107
+ ].join("\n");
101
108
  const step = [
102
109
  ` - name: ${marker}`,
103
110
  " env:",
111
+ secretEnvironment,
104
112
  " LISA_REMOTE_AGENT: copilot",
105
113
  ` run: ${INSTALL_COMMAND}`,
106
114
  ].join("\n");
@@ -118,7 +126,7 @@ function installCopilotAdapter(project) {
118
126
  " permissions:",
119
127
  " contents: read",
120
128
  " steps:",
121
- " - uses: actions/checkout@v4",
129
+ checkoutStep,
122
130
  step,
123
131
  "",
124
132
  ].join("\n")
@@ -127,7 +135,6 @@ function installCopilotAdapter(project) {
127
135
  }
128
136
 
129
137
  const current = readFileSync(workflowPath, "utf8");
130
- if (current.includes(marker)) return path.relative(project, workflowPath);
131
138
  const lines = current.split("\n");
132
139
  const jobIndex = lines.findIndex(line =>
133
140
  /^\s{2}copilot-setup-steps:\s*$/.test(line)
@@ -146,10 +153,95 @@ function installCopilotAdapter(project) {
146
153
  "Existing copilot-setup-steps.yml must contain jobs.copilot-setup-steps.steps before Lisa can merge the AWS bootstrap step"
147
154
  );
148
155
  }
149
- const additions = current.includes("actions/checkout@")
150
- ? [step]
151
- : [" - uses: actions/checkout@v4", step];
152
- lines.splice(stepsIndex + 1, 0, ...additions);
156
+
157
+ let changed = false;
158
+ const checkoutIndex = lines.findIndex(
159
+ (line, index) =>
160
+ index > stepsIndex &&
161
+ (jobEndIndex < 0 || index < jobEndIndex) &&
162
+ /^\s{6}-\s+uses:\s+actions\/checkout@/.test(line)
163
+ );
164
+ if (checkoutIndex >= 0) {
165
+ const nextStepIndex = lines.findIndex(
166
+ (line, index) => index > checkoutIndex && /^\s{6}-\s/.test(line)
167
+ );
168
+ const checkoutEnd = nextStepIndex < 0 ? lines.length : nextStepIndex;
169
+ const withIndex = lines.findIndex(
170
+ (line, index) =>
171
+ index > checkoutIndex &&
172
+ index < checkoutEnd &&
173
+ /^\s{8}with:\s*$/.test(line)
174
+ );
175
+ const persistenceIndex = lines.findIndex(
176
+ (line, index) =>
177
+ index > checkoutIndex &&
178
+ index < checkoutEnd &&
179
+ /^\s{10}persist-credentials:\s*/.test(line)
180
+ );
181
+ if (persistenceIndex >= 0) {
182
+ if (lines[persistenceIndex] !== " persist-credentials: false") {
183
+ lines[persistenceIndex] = " persist-credentials: false";
184
+ changed = true;
185
+ }
186
+ } else if (withIndex >= 0) {
187
+ lines.splice(withIndex + 1, 0, " persist-credentials: false");
188
+ changed = true;
189
+ } else {
190
+ lines.splice(
191
+ checkoutIndex + 1,
192
+ 0,
193
+ " with:",
194
+ " persist-credentials: false"
195
+ );
196
+ changed = true;
197
+ }
198
+ }
199
+
200
+ const markerIndex = lines.findIndex(line => line.includes(marker));
201
+ if (markerIndex >= 0) {
202
+ const nextStepIndex = lines.findIndex(
203
+ (line, index) => index > markerIndex && /^\s{6}-\s/.test(line)
204
+ );
205
+ const managedStepEnd = nextStepIndex < 0 ? lines.length : nextStepIndex;
206
+ const managedStep = lines.slice(markerIndex, managedStepEnd);
207
+ if (!managedStep.includes(secretEnvironment)) {
208
+ const environmentIndex = lines.findIndex(
209
+ (line, index) =>
210
+ index > markerIndex &&
211
+ index < managedStepEnd &&
212
+ /^\s{8}env:\s*$/.test(line)
213
+ );
214
+ if (environmentIndex < 0) {
215
+ throw new Error(
216
+ "Existing Lisa remote AWS bootstrap step must contain an env block"
217
+ );
218
+ }
219
+ lines.splice(environmentIndex + 1, 0, secretEnvironment);
220
+ changed = true;
221
+ }
222
+ if (checkoutIndex < 0) {
223
+ lines.splice(stepsIndex + 1, 0, ...checkoutStep.split("\n"));
224
+ changed = true;
225
+ }
226
+ if (changed) writeFileSync(workflowPath, lines.join("\n"));
227
+ return path.relative(project, workflowPath);
228
+ }
229
+ const additions = checkoutIndex >= 0 ? [step] : [checkoutStep, step];
230
+ const nextCheckoutStepIndex = lines.findIndex(
231
+ (line, index) => index > checkoutIndex && /^\s{6}-\s/.test(line)
232
+ );
233
+ const updatedJobEndIndex = lines.findIndex(
234
+ (line, index) => index > jobIndex && /^\s{2}\S[^:]*:\s*$/.test(line)
235
+ );
236
+ const additionIndex =
237
+ checkoutIndex < 0
238
+ ? stepsIndex + 1
239
+ : nextCheckoutStepIndex >= 0
240
+ ? nextCheckoutStepIndex
241
+ : updatedJobEndIndex >= 0
242
+ ? updatedJobEndIndex
243
+ : lines.length;
244
+ lines.splice(additionIndex, 0, ...additions);
153
245
  writeFileSync(workflowPath, lines.join("\n"));
154
246
  return path.relative(project, workflowPath);
155
247
  }
@@ -161,13 +253,20 @@ function writeGuide(project, platform, secretName) {
161
253
  guidePath,
162
254
  `# Remote coding-agent AWS access
163
255
 
256
+ ## Context
257
+
164
258
  This repository uses Lisa's vendor-neutral AWS bootstrap. Configure the complete
165
259
  Secrets Manager SecretString as the secret \`LISA_AWS_BOOTSTRAP_JSON\`; do not
166
260
  split it into \`AWS_ACCESS_KEY_ID\` and \`AWS_SECRET_ACCESS_KEY\` environment
167
261
  variables. The setup script writes a named source profile whose only permission
168
262
  is \`sts:AssumeRole\`, then creates automatically refreshed role profiles.
169
263
 
170
- ## Runtime configuration
264
+ ## Goal
265
+
266
+ Give supported remote coding agents renewable, least-privilege AWS CLI access
267
+ without borrowing a developer identity or granting direct production repair.
268
+
269
+ ## Changes
171
270
 
172
271
  | Runtime | Setup |
173
272
  |---|---|
@@ -180,7 +279,7 @@ is \`sts:AssumeRole\`, then creates automatically refreshed role profiles.
180
279
 
181
280
  Generated for: \`${platform}\`.
182
281
 
183
- ## Profiles
282
+ ## Implementation
184
283
 
185
284
  The bootstrap bundle defines the available profile names. \`dev\` is the
186
285
  default when present. Select another account explicitly, for example:
@@ -193,8 +292,6 @@ aws --profile production cloudformation describe-stacks
193
292
  Production and shared profiles are observer-only. Production repair remains a
194
293
  human-driven local-workstation operation.
195
294
 
196
- ## Administrator rollout
197
-
198
295
  After the infrastructure pipeline deploys the remote-agent IAM stacks, retrieve
199
296
  the complete bundle from the shared account. Do not extract or distribute its
200
297
  individual access-key fields:
@@ -210,6 +307,8 @@ Store that exact output as the masked secret \`LISA_AWS_BOOTSTRAP_JSON\` on each
210
307
  remote-agent platform. Run the setup command and require its live
211
308
  \`sts:GetCallerIdentity\` check to pass before considering the platform ready.
212
309
 
310
+ ## Notes
311
+
213
312
  Rotate by replacing the bootstrap IAM access key through infrastructure and
214
313
  then updating this one secret on every configured platform. Disabling or
215
314
  deleting the bootstrap user immediately prevents new role sessions.
@@ -14,6 +14,7 @@ set -euo pipefail
14
14
  umask 077
15
15
 
16
16
  BOOTSTRAP_PROFILE="lisa-remote-agent-bootstrap"
17
+ AWS_CLI_VERSION="2.36.2"
17
18
 
18
19
  fail() {
19
20
  echo "remote-agent-aws-setup: $*" >&2
@@ -35,17 +36,17 @@ as_root() {
35
36
  }
36
37
 
37
38
  install_base_tools() {
38
- need curl && need unzip && need jq && return 0
39
+ need curl && need unzip && need jq && need gpg && return 0
39
40
 
40
41
  if need apt-get; then
41
42
  as_root apt-get update -y
42
- as_root apt-get install -y curl unzip jq
43
+ as_root apt-get install -y curl unzip jq gnupg
43
44
  elif need dnf; then
44
- as_root dnf install -y curl unzip jq
45
+ as_root dnf install -y curl unzip jq gnupg2
45
46
  elif need yum; then
46
- as_root yum install -y curl unzip jq
47
+ as_root yum install -y curl unzip jq gnupg2
47
48
  else
48
- fail "install curl, unzip, and jq in the remote image before running this script"
49
+ fail "install curl, unzip, jq, and gpg in the remote image before running this script"
49
50
  fi
50
51
  }
51
52
 
@@ -62,9 +63,49 @@ install_aws_cli() {
62
63
 
63
64
  temporary_directory="$(mktemp -d)"
64
65
  trap 'rm -rf "${temporary_directory:-}"' EXIT
66
+ local installer_url
67
+ installer_url="https://awscli.amazonaws.com/awscli-exe-linux-${aws_architecture}-${AWS_CLI_VERSION}.zip"
65
68
  curl -fsSL \
66
- "https://awscli.amazonaws.com/awscli-exe-linux-${aws_architecture}.zip" \
69
+ "$installer_url" \
67
70
  -o "$temporary_directory/awscliv2.zip"
71
+ curl -fsSL "$installer_url.sig" -o "$temporary_directory/awscliv2.sig"
72
+ cat >"$temporary_directory/aws-cli-public-key.asc" <<'AWS_CLI_PUBLIC_KEY'
73
+ -----BEGIN PGP PUBLIC KEY BLOCK-----
74
+
75
+ mQINBF2Cr7UBEADJZHcgusOJl7ENSyumXh85z0TRV0xJorM2B/JL0kHOyigQluUG
76
+ ZMLhENaG0bYatdrKP+3H91lvK050pXwnO/R7fB/FSTouki4ciIx5OuLlnJZIxSzx
77
+ PqGl0mkxImLNbGWoi6Lto0LYxqHN2iQtzlwTVmq9733zd3XfcXrZ3+LblHAgEt5G
78
+ TfNxEKJ8soPLyWmwDH6HWCnjZ/aIQRBTIQ05uVeEoYxSh6wOai7ss/KveoSNBbYz
79
+ gbdzoqI2Y8cgH2nbfgp3DSasaLZEdCSsIsK1u05CinE7k2qZ7KgKAUIcT/cR/grk
80
+ C6VwsnDU0OUCideXcQ8WeHutqvgZH1JgKDbznoIzeQHJD238GEu+eKhRHcz8/jeG
81
+ 94zkcgJOz3KbZGYMiTh277Fvj9zzvZsbMBCedV1BTg3TqgvdX4bdkhf5cH+7NtWO
82
+ lrFj6UwAsGukBTAOxC0l/dnSmZhJ7Z1KmEWilro/gOrjtOxqRQutlIqG22TaqoPG
83
+ fYVN+en3Zwbt97kcgZDwqbuykNt64oZWc4XKCa3mprEGC3IbJTBFqglXmZ7l9ywG
84
+ EEUJYOlb2XrSuPWml39beWdKM8kzr1OjnlOm6+lpTRCBfo0wa9F8YZRhHPAkwKkX
85
+ XDeOGpWRj4ohOx0d2GWkyV5xyN14p2tQOCdOODmz80yUTgRpPVQUtOEhXQARAQAB
86
+ tCFBV1MgQ0xJIFRlYW0gPGF3cy1jbGlAYW1hem9uLmNvbT6JAlQEEwEIAD4CGwMF
87
+ CwkIBwIGFQoJCAsCBBYCAwECHgECF4AWIQT7Xbd/1cEYuAURraimMQrMRnJHXAUC
88
+ akV0ygUJDqP4lQAKCRCmMQrMRnJHXFHjD/9eyZLYcKuQOlLvtqSDtUBiEZf6ZZjM
89
+ i3ygYH8rJNtuToUH+HvSpe819urJCquXhDrlK6N+aqW0hCLtNABJG/vsafIgvIYJ
90
+ hSGgpgtNnQyMV1jViRWqPjbouw8OkYKBThUfT1i2Y+wn58ifs6ODBCmTexWtXspA
91
+ Si+Gt49xDOW0APmbOPnI+a4HJW6tVEo6MWS0WjzpiBayR3d1A4pt4YrPfSdDgpLo
92
+ h2SLQqlRqvvVZJaWBjhkErNFpfsBA06sDcPEOb0G8LBUbR4WOcdvhe5LubJbZuxC
93
+ AG9kNPCVeQP1ixwjgjXKysaxeQ6rv0VzIQgRp6tLVLWhy6AKDNvLjFSsmXZ1Wl08
94
+ Y/RlOHXlzLuQMRE6sR1wOdRxc9TsrNWTGiBK65cvSWOy03JeBkQQ8pesqltiyxI9
95
+ U21kkgiXtTSKNGfKK8pO27D81YANhRqPK7iTp6kuFiY2WtOg90KTMNlIT+Ff85Y2
96
+ b1rHj6Z0SrCkJujhWk3IBPic/wJgz01LEc/OAdUPlby90RJZcIBhSlWhT7mXnXIO
97
+ c0HWlNQrns2s3CTyYwZSiSlYe9ApeLwhjDo8NhbFuCAy61l6O5UsR4AfZxx/rGKv
98
+ 2wFb1/RN/P4gNe6vmxZAPjR0AQcwD3tc2McimOLr/22kmPz8IH3I0X7WoSFr0Biz
99
+ E91G7bb0hOb/cA==
100
+ =knv7
101
+ -----END PGP PUBLIC KEY BLOCK-----
102
+ AWS_CLI_PUBLIC_KEY
103
+ mkdir -m 700 "$temporary_directory/gnupg"
104
+ gpg --batch --homedir "$temporary_directory/gnupg" \
105
+ --import "$temporary_directory/aws-cli-public-key.asc" >/dev/null 2>&1
106
+ gpg --batch --homedir "$temporary_directory/gnupg" \
107
+ --verify "$temporary_directory/awscliv2.sig" \
108
+ "$temporary_directory/awscliv2.zip"
68
109
  unzip -q "$temporary_directory/awscliv2.zip" -d "$temporary_directory"
69
110
  as_root "$temporary_directory/aws/install" \
70
111
  --install-dir /usr/local/aws-cli \
@@ -73,6 +114,27 @@ install_aws_cli() {
73
114
  trap - EXIT
74
115
  }
75
116
 
117
+ remove_profile_setting() {
118
+ local credentials_file profile setting temporary_file
119
+ credentials_file="$1"
120
+ profile="$2"
121
+ setting="$3"
122
+ [ -f "$credentials_file" ] || return 0
123
+
124
+ temporary_file="$(mktemp "${credentials_file}.XXXXXX")"
125
+ awk -v profile="$profile" -v setting="$setting" '
126
+ /^\[[^]]+\][[:space:]]*$/ {
127
+ section = $0
128
+ sub(/^\[/, "", section)
129
+ sub(/\][[:space:]]*$/, "", section)
130
+ in_profile = section == profile
131
+ }
132
+ !(in_profile && $0 ~ "^[[:space:]]*" setting "[[:space:]]*=") { print }
133
+ ' "$credentials_file" >"$temporary_file"
134
+ chmod 600 "$temporary_file"
135
+ mv "$temporary_file" "$credentials_file"
136
+ }
137
+
76
138
  sanitize_session_name() {
77
139
  local candidate
78
140
  candidate="$(printf '%s' "${LISA_REMOTE_AGENT:-remote-agent}" \
@@ -113,22 +175,30 @@ profiles_json="$(printf '%s' "$bootstrap_json" | jq -c '
113
175
  end
114
176
  ')"
115
177
 
116
- printf '%s' "$profiles_json" | jq -e '
178
+ printf '%s' "$profiles_json" | jq -e --arg bootstrap_profile "$BOOTSTRAP_PROFILE" '
117
179
  type == "object" and length > 0 and
118
180
  all(to_entries[];
181
+ (.key != $bootstrap_profile) and
119
182
  (.key | test("^[A-Za-z0-9_-]+$")) and
120
183
  (.value.roleArn | type == "string" and startswith("arn:aws:iam::")) and
121
184
  (.value.region | type == "string" and length > 0)
122
185
  )
123
186
  ' >/dev/null || fail "bootstrap profiles must map safe names to roleArn and region"
124
187
 
125
- mkdir -p "$HOME/.aws"
188
+ credentials_file="${AWS_SHARED_CREDENTIALS_FILE:-$HOME/.aws/credentials}"
189
+ config_file="${AWS_CONFIG_FILE:-$HOME/.aws/config}"
190
+ mkdir -p "$HOME/.aws" "$(dirname "$credentials_file")" "$(dirname "$config_file")"
126
191
  chmod 700 "$HOME/.aws"
127
192
 
128
193
  aws configure set aws_access_key_id "$access_key_id" --profile "$BOOTSTRAP_PROFILE"
129
194
  aws configure set aws_secret_access_key "$secret_access_key" --profile "$BOOTSTRAP_PROFILE"
130
195
  if [ -n "$session_token" ]; then
131
196
  aws configure set aws_session_token "$session_token" --profile "$BOOTSTRAP_PROFILE"
197
+ else
198
+ remove_profile_setting \
199
+ "$credentials_file" \
200
+ "$BOOTSTRAP_PROFILE" \
201
+ "aws_session_token"
132
202
  fi
133
203
 
134
204
  session_name="$(sanitize_session_name)"
@@ -153,7 +223,7 @@ aws configure set external_id "$external_id" --profile default
153
223
  aws configure set role_session_name "$session_name" --profile default
154
224
  aws configure set region "$default_region" --profile default
155
225
 
156
- chmod 600 "$HOME/.aws/credentials" "$HOME/.aws/config"
226
+ chmod 600 "$credentials_file" "$config_file"
157
227
 
158
228
  if [ "${LISA_AWS_SKIP_VERIFY:-0}" != "1" ]; then
159
229
  AWS_PAGER="" aws sts get-caller-identity --profile "$default_profile" >/dev/null
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa",
3
- "version": "2.226.0",
3
+ "version": "2.226.2",
4
4
  "description": "Universal governance — agents, skills, commands, hooks, and rules for all projects",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -98,9 +98,17 @@ function installCopilotAdapter(project) {
98
98
  );
99
99
  mkdirSync(path.dirname(workflowPath), { recursive: true });
100
100
  const marker = "Lisa remote AWS bootstrap";
101
+ const secretEnvironment =
102
+ " LISA_AWS_BOOTSTRAP_JSON: ${{ secrets.LISA_AWS_BOOTSTRAP_JSON }}";
103
+ const checkoutStep = [
104
+ " - uses: actions/checkout@v4",
105
+ " with:",
106
+ " persist-credentials: false",
107
+ ].join("\n");
101
108
  const step = [
102
109
  ` - name: ${marker}`,
103
110
  " env:",
111
+ secretEnvironment,
104
112
  " LISA_REMOTE_AGENT: copilot",
105
113
  ` run: ${INSTALL_COMMAND}`,
106
114
  ].join("\n");
@@ -118,7 +126,7 @@ function installCopilotAdapter(project) {
118
126
  " permissions:",
119
127
  " contents: read",
120
128
  " steps:",
121
- " - uses: actions/checkout@v4",
129
+ checkoutStep,
122
130
  step,
123
131
  "",
124
132
  ].join("\n")
@@ -127,7 +135,6 @@ function installCopilotAdapter(project) {
127
135
  }
128
136
 
129
137
  const current = readFileSync(workflowPath, "utf8");
130
- if (current.includes(marker)) return path.relative(project, workflowPath);
131
138
  const lines = current.split("\n");
132
139
  const jobIndex = lines.findIndex(line =>
133
140
  /^\s{2}copilot-setup-steps:\s*$/.test(line)
@@ -146,10 +153,95 @@ function installCopilotAdapter(project) {
146
153
  "Existing copilot-setup-steps.yml must contain jobs.copilot-setup-steps.steps before Lisa can merge the AWS bootstrap step"
147
154
  );
148
155
  }
149
- const additions = current.includes("actions/checkout@")
150
- ? [step]
151
- : [" - uses: actions/checkout@v4", step];
152
- lines.splice(stepsIndex + 1, 0, ...additions);
156
+
157
+ let changed = false;
158
+ const checkoutIndex = lines.findIndex(
159
+ (line, index) =>
160
+ index > stepsIndex &&
161
+ (jobEndIndex < 0 || index < jobEndIndex) &&
162
+ /^\s{6}-\s+uses:\s+actions\/checkout@/.test(line)
163
+ );
164
+ if (checkoutIndex >= 0) {
165
+ const nextStepIndex = lines.findIndex(
166
+ (line, index) => index > checkoutIndex && /^\s{6}-\s/.test(line)
167
+ );
168
+ const checkoutEnd = nextStepIndex < 0 ? lines.length : nextStepIndex;
169
+ const withIndex = lines.findIndex(
170
+ (line, index) =>
171
+ index > checkoutIndex &&
172
+ index < checkoutEnd &&
173
+ /^\s{8}with:\s*$/.test(line)
174
+ );
175
+ const persistenceIndex = lines.findIndex(
176
+ (line, index) =>
177
+ index > checkoutIndex &&
178
+ index < checkoutEnd &&
179
+ /^\s{10}persist-credentials:\s*/.test(line)
180
+ );
181
+ if (persistenceIndex >= 0) {
182
+ if (lines[persistenceIndex] !== " persist-credentials: false") {
183
+ lines[persistenceIndex] = " persist-credentials: false";
184
+ changed = true;
185
+ }
186
+ } else if (withIndex >= 0) {
187
+ lines.splice(withIndex + 1, 0, " persist-credentials: false");
188
+ changed = true;
189
+ } else {
190
+ lines.splice(
191
+ checkoutIndex + 1,
192
+ 0,
193
+ " with:",
194
+ " persist-credentials: false"
195
+ );
196
+ changed = true;
197
+ }
198
+ }
199
+
200
+ const markerIndex = lines.findIndex(line => line.includes(marker));
201
+ if (markerIndex >= 0) {
202
+ const nextStepIndex = lines.findIndex(
203
+ (line, index) => index > markerIndex && /^\s{6}-\s/.test(line)
204
+ );
205
+ const managedStepEnd = nextStepIndex < 0 ? lines.length : nextStepIndex;
206
+ const managedStep = lines.slice(markerIndex, managedStepEnd);
207
+ if (!managedStep.includes(secretEnvironment)) {
208
+ const environmentIndex = lines.findIndex(
209
+ (line, index) =>
210
+ index > markerIndex &&
211
+ index < managedStepEnd &&
212
+ /^\s{8}env:\s*$/.test(line)
213
+ );
214
+ if (environmentIndex < 0) {
215
+ throw new Error(
216
+ "Existing Lisa remote AWS bootstrap step must contain an env block"
217
+ );
218
+ }
219
+ lines.splice(environmentIndex + 1, 0, secretEnvironment);
220
+ changed = true;
221
+ }
222
+ if (checkoutIndex < 0) {
223
+ lines.splice(stepsIndex + 1, 0, ...checkoutStep.split("\n"));
224
+ changed = true;
225
+ }
226
+ if (changed) writeFileSync(workflowPath, lines.join("\n"));
227
+ return path.relative(project, workflowPath);
228
+ }
229
+ const additions = checkoutIndex >= 0 ? [step] : [checkoutStep, step];
230
+ const nextCheckoutStepIndex = lines.findIndex(
231
+ (line, index) => index > checkoutIndex && /^\s{6}-\s/.test(line)
232
+ );
233
+ const updatedJobEndIndex = lines.findIndex(
234
+ (line, index) => index > jobIndex && /^\s{2}\S[^:]*:\s*$/.test(line)
235
+ );
236
+ const additionIndex =
237
+ checkoutIndex < 0
238
+ ? stepsIndex + 1
239
+ : nextCheckoutStepIndex >= 0
240
+ ? nextCheckoutStepIndex
241
+ : updatedJobEndIndex >= 0
242
+ ? updatedJobEndIndex
243
+ : lines.length;
244
+ lines.splice(additionIndex, 0, ...additions);
153
245
  writeFileSync(workflowPath, lines.join("\n"));
154
246
  return path.relative(project, workflowPath);
155
247
  }
@@ -161,13 +253,20 @@ function writeGuide(project, platform, secretName) {
161
253
  guidePath,
162
254
  `# Remote coding-agent AWS access
163
255
 
256
+ ## Context
257
+
164
258
  This repository uses Lisa's vendor-neutral AWS bootstrap. Configure the complete
165
259
  Secrets Manager SecretString as the secret \`LISA_AWS_BOOTSTRAP_JSON\`; do not
166
260
  split it into \`AWS_ACCESS_KEY_ID\` and \`AWS_SECRET_ACCESS_KEY\` environment
167
261
  variables. The setup script writes a named source profile whose only permission
168
262
  is \`sts:AssumeRole\`, then creates automatically refreshed role profiles.
169
263
 
170
- ## Runtime configuration
264
+ ## Goal
265
+
266
+ Give supported remote coding agents renewable, least-privilege AWS CLI access
267
+ without borrowing a developer identity or granting direct production repair.
268
+
269
+ ## Changes
171
270
 
172
271
  | Runtime | Setup |
173
272
  |---|---|
@@ -180,7 +279,7 @@ is \`sts:AssumeRole\`, then creates automatically refreshed role profiles.
180
279
 
181
280
  Generated for: \`${platform}\`.
182
281
 
183
- ## Profiles
282
+ ## Implementation
184
283
 
185
284
  The bootstrap bundle defines the available profile names. \`dev\` is the
186
285
  default when present. Select another account explicitly, for example:
@@ -193,8 +292,6 @@ aws --profile production cloudformation describe-stacks
193
292
  Production and shared profiles are observer-only. Production repair remains a
194
293
  human-driven local-workstation operation.
195
294
 
196
- ## Administrator rollout
197
-
198
295
  After the infrastructure pipeline deploys the remote-agent IAM stacks, retrieve
199
296
  the complete bundle from the shared account. Do not extract or distribute its
200
297
  individual access-key fields:
@@ -210,6 +307,8 @@ Store that exact output as the masked secret \`LISA_AWS_BOOTSTRAP_JSON\` on each
210
307
  remote-agent platform. Run the setup command and require its live
211
308
  \`sts:GetCallerIdentity\` check to pass before considering the platform ready.
212
309
 
310
+ ## Notes
311
+
213
312
  Rotate by replacing the bootstrap IAM access key through infrastructure and
214
313
  then updating this one secret on every configured platform. Disabling or
215
314
  deleting the bootstrap user immediately prevents new role sessions.