@codyswann/lisa 2.225.5 → 2.226.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 (90) hide show
  1. package/README.md +7 -0
  2. package/all/create-only/scripts/remote-agent-aws-setup.sh +11 -0
  3. package/package.json +1 -1
  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-analyze-claude-remote/SKILL.md +21 -18
  7. package/plugins/lisa/.codex-plugin/skills/lisa-generate-claude-remote-build-script/SKILL.md +15 -27
  8. package/plugins/lisa/.codex-plugin/skills/lisa-setup-remote-aws/SKILL.md +53 -0
  9. package/plugins/lisa/.codex-plugin/skills/lisa-setup-remote-aws/agents/openai.yaml +4 -0
  10. package/plugins/lisa/commands/setup-remote-aws.md +6 -0
  11. package/plugins/lisa/scripts/install-remote-agent-aws.mjs +347 -0
  12. package/plugins/lisa/scripts/remote-agent-aws-setup.sh +204 -0
  13. package/plugins/lisa/skills/lisa-analyze-claude-remote/SKILL.md +21 -18
  14. package/plugins/lisa/skills/lisa-generate-claude-remote-build-script/SKILL.md +15 -27
  15. package/plugins/lisa/skills/lisa-setup-remote-aws/SKILL.md +53 -0
  16. package/plugins/lisa/skills/lisa-setup-remote-aws/agents/openai.yaml +4 -0
  17. package/plugins/lisa-agy/commands/lisa/setup-remote-aws.md +6 -0
  18. package/plugins/lisa-agy/plugin.json +1 -1
  19. package/plugins/lisa-agy/scripts/install-remote-agent-aws.mjs +347 -0
  20. package/plugins/lisa-agy/scripts/remote-agent-aws-setup.sh +204 -0
  21. package/plugins/lisa-agy/skills/lisa-analyze-claude-remote/SKILL.md +21 -18
  22. package/plugins/lisa-agy/skills/lisa-generate-claude-remote-build-script/SKILL.md +15 -27
  23. package/plugins/lisa-agy/skills/lisa-setup-remote-aws/SKILL.md +53 -0
  24. package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
  25. package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
  26. package/plugins/lisa-cdk-agy/plugin.json +1 -1
  27. package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
  28. package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
  29. package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
  30. package/plugins/lisa-copilot/commands/lisa/setup-remote-aws.md +6 -0
  31. package/plugins/lisa-copilot/scripts/install-remote-agent-aws.mjs +347 -0
  32. package/plugins/lisa-copilot/scripts/remote-agent-aws-setup.sh +204 -0
  33. package/plugins/lisa-copilot/skills/lisa-analyze-claude-remote/SKILL.md +21 -18
  34. package/plugins/lisa-copilot/skills/lisa-generate-claude-remote-build-script/SKILL.md +15 -27
  35. package/plugins/lisa-copilot/skills/lisa-setup-remote-aws/SKILL.md +53 -0
  36. package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
  37. package/plugins/lisa-cursor/commands/lisa/setup-remote-aws.md +6 -0
  38. package/plugins/lisa-cursor/scripts/install-remote-agent-aws.mjs +347 -0
  39. package/plugins/lisa-cursor/scripts/remote-agent-aws-setup.sh +204 -0
  40. package/plugins/lisa-cursor/skills/lisa-analyze-claude-remote/SKILL.md +21 -18
  41. package/plugins/lisa-cursor/skills/lisa-generate-claude-remote-build-script/SKILL.md +15 -27
  42. package/plugins/lisa-cursor/skills/lisa-setup-remote-aws/SKILL.md +53 -0
  43. package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
  44. package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
  45. package/plugins/lisa-expo-agy/plugin.json +1 -1
  46. package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
  47. package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
  48. package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
  49. package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
  50. package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
  51. package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
  52. package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
  53. package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
  54. package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
  55. package/plugins/lisa-nestjs-agy/plugin.json +1 -1
  56. package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
  57. package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
  58. package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
  59. package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
  60. package/plugins/lisa-openclaw-agy/plugin.json +1 -1
  61. package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
  62. package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
  63. package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
  64. package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
  65. package/plugins/lisa-phaser-agy/plugin.json +1 -1
  66. package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
  67. package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
  68. package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
  69. package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
  70. package/plugins/lisa-rails-agy/plugin.json +1 -1
  71. package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
  72. package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
  73. package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
  74. package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
  75. package/plugins/lisa-typescript-agy/plugin.json +1 -1
  76. package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
  77. package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
  78. package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
  79. package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
  80. package/plugins/lisa-wiki-agy/plugin.json +1 -1
  81. package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
  82. package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
  83. package/plugins/src/base/commands/setup-remote-aws.md +6 -0
  84. package/plugins/src/base/scripts/install-remote-agent-aws.mjs +347 -0
  85. package/plugins/src/base/scripts/remote-agent-aws-setup.sh +204 -0
  86. package/plugins/src/base/skills/lisa-analyze-claude-remote/SKILL.md +21 -18
  87. package/plugins/src/base/skills/lisa-generate-claude-remote-build-script/SKILL.md +15 -27
  88. package/plugins/src/base/skills/lisa-setup-remote-aws/SKILL.md +53 -0
  89. package/scripts/claude-remote-setup.sh +9 -2
  90. package/scripts/remote-agent-aws-setup.sh +17 -0
package/README.md CHANGED
@@ -84,6 +84,13 @@ Skill bodies are not copied into the project and unrelated Lisa stacks are not
84
84
  loaded. Project settings use `[features].hooks`; the deprecated `codex_hooks`
85
85
  key is removed during reconciliation.
86
86
 
87
+ Remote coding environments use one vendor-neutral AWS bootstrap rather than
88
+ repository-specific or agent-specific IAM users. Run `/lisa:setup-remote-aws`
89
+ to install the common setup script and native Cursor/Copilot adapters; Claude,
90
+ Codex, Cursor, Copilot, and user-managed Antigravity/OpenCode hosts all consume
91
+ the same `LISA_AWS_BOOTSTRAP_JSON` bundle. See
92
+ [Remote coding-agent AWS access](docs/remote-agent-aws.md).
93
+
87
94
  The supported stacks, setup flags, and exact invocation evolve as the project grows, so ask for the current set rather than copying a list that may have moved on:
88
95
 
89
96
  > **Prompt for your coding agent**
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env bash
2
+ # Installed by Lisa. The authoritative implementation is shipped by the
3
+ # lisa-setup-remote-aws skill and the @codyswann/lisa package.
4
+
5
+ set -euo pipefail
6
+
7
+ LOCAL_LISA_SCRIPT="$(npm root 2>/dev/null)/@codyswann/lisa/plugins/lisa/scripts/remote-agent-aws-setup.sh"
8
+ [ -x "$LOCAL_LISA_SCRIPT" ] && exec "$LOCAL_LISA_SCRIPT" "$@"
9
+
10
+ echo "remote-agent-aws-setup: install @codyswann/lisa before running this setup script" >&2
11
+ exit 1
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.225.5",
105
+ "version": "2.226.1",
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.225.5",
3
+ "version": "2.226.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": "2.225.5",
3
+ "version": "2.226.1",
4
4
  "description": "Universal governance: agents, skills, commands, hooks, and rules for all projects.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -150,24 +150,27 @@ Group the findings as:
150
150
  Terraform providers).
151
151
  - `aws sso login`, `aws:signin:*`, `sso_start_url`, `sso_account_id`, `sso_role_name`, or
152
152
  `sso_session` in docs or config.
153
- - `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `AWS_SESSION_TOKEN`, `AWS_DEFAULT_REGION`,
154
- `AWS_PROFILE`, role ARN, external ID, account, or CloudWatch/STS references.
153
+ - `LISA_AWS_BOOTSTRAP_JSON`, legacy `AWS_ACCESS_KEY_ID` /
154
+ `AWS_SECRET_ACCESS_KEY`, `AWS_PROFILE`, role ARN, external ID, account, or
155
+ CloudWatch/STS references.
155
156
 
156
157
  If AWS is present, emit an AWS credential finding and inventory entry. If the repo's current
157
158
  local path is `aws sso login` or an `aws:signin:*` script, classify that local path as a `GAP`
158
159
  for headless remote routines: it is an interactive browser/device-authorization flow and cannot
159
160
  complete in the cloud. Route to the AWS row in the Credential reference instead of suggesting
160
- SSO auth. The finding must name the headless substrate: a dedicated IAM principal (user or role)
161
- with only permission to `sts:AssumeRole`, environment credentials in the routine UI, and
162
- `~/.aws/config` profiles using `role_arn`, `credential_source = Environment`, optional
163
- `external_id`, and `region`.
161
+ SSO auth. The finding must name Lisa's headless substrate: one dedicated IAM
162
+ user with only `sts:AssumeRole`; one complete Secrets Manager SecretString
163
+ set as `LISA_AWS_BOOTSTRAP_JSON`; and `/lisa:setup-remote-aws`, which writes a
164
+ named source profile plus automatically refreshed role profiles. Explicitly
165
+ reject standard `AWS_ACCESS_KEY_ID` variables because they can bypass the
166
+ intended assume-role profile.
164
167
 
165
168
  When the repository contains concrete non-secret AWS metadata (role ARNs, account aliases,
166
169
  profile names, regions, or ExternalId values), include it in an `awsProfiles` inventory array so
167
170
  `/lisa:generate-claude-remote-build-script` can write matching `~/.aws/config` profiles. Never
168
- invent account IDs, ExternalIds, role names, or regions. If AWS is detected but profile metadata
169
- is absent, emit the required AWS secret names plus an action to add project-specific profile
170
- metadata to the generated artifact or environment notes.
171
+ invent account IDs, ExternalIds, role names, or regions. If AWS is detected
172
+ but profile metadata is absent, require a cdkstarter bootstrap bundle rather
173
+ than asking the repository to reconstruct account metadata by hand.
171
174
 
172
175
  5. **MCP servers** — read every committed `.mcp.json`. For each server report transport and auth.
173
176
  Project-scoped HTTP/SSE servers with no interactive auth are `OK`. Flag stdio servers as
@@ -276,14 +279,14 @@ unsuffixed `…_TOKEN`/`…_KEY` is the simplest to set in a single-account rout
276
279
  - Access: the personal API key inherits the user's workspace permissions — the user must be able to read/create/update Issues in the destination team.
277
280
 
278
281
  ### AWS — host-project operations, logs, deploys, CDK/Serverless/SST, or AWS SDK usage
279
- - Headless substrate: a dedicated IAM principal (user or role) with long-lived bootstrap credentials
280
- stored in the routine environment, used only to call `sts:AssumeRole` into per-account operational
281
- roles. The routine writes `~/.aws/config` profiles with `role_arn`, `credential_source =
282
- Environment`, optional `external_id`, and `region`; agents use `aws --profile <profile> ...`.
283
- - Env: `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`; optional `AWS_SESSION_TOKEN` for temporary
284
- bootstrap credentials; `AWS_DEFAULT_REGION` when no profile region is provided. Role ARNs,
285
- ExternalIds, profile names, and regions are non-secret project metadata and belong in generated
286
- artifacts or project docs, not in the skill's global guidance.
282
+ - Headless substrate: cdkstarter's vendor-neutral remote-agent kit. Its single
283
+ Secrets Manager SecretString contains the assume-only access key, ExternalId,
284
+ and per-account role metadata. `/lisa:setup-remote-aws` writes a private named
285
+ source profile and renewable role profiles; agents use
286
+ `aws --profile <profile> ...`.
287
+ - Env: `LISA_AWS_BOOTSTRAP_JSON` (secret, required) and
288
+ `LISA_REMOTE_AGENT` (plain platform label). Do not set `AWS_ACCESS_KEY_ID` or
289
+ `AWS_SECRET_ACCESS_KEY` in the remote environment.
287
290
  - Allowlist: `*.amazonaws.com`, or narrower service hosts such as `sts.amazonaws.com`,
288
291
  `logs.<region>.amazonaws.com`, `cloudwatch.<region>.amazonaws.com`,
289
292
  `xray.<region>.amazonaws.com`, `ssm.<region>.amazonaws.com`, and service-specific endpoints the
@@ -366,7 +369,7 @@ so the generator can render acquisition comments into its template:
366
369
  {
367
370
  "name": "dev",
368
371
  "roleArn": "arn:aws:iam::<account-id>:role/<role-name>",
369
- "credentialSource": "Environment",
372
+ "credentialSource": "lisa-remote-agent-bootstrap",
370
373
  "externalId": "<project-external-id>",
371
374
  "region": "us-east-1"
372
375
  }
@@ -71,9 +71,9 @@ tracker/source, plus the host project's own package manager and tooling — not
71
71
  include `JAM_PAT`, `SONAR_TOKEN`, or similar documented substrate env vars
72
72
  only as optional secrets, with their acquire/scope comments when the analysis
73
73
  supplied them. Never invent values or promote dormant substrates to required.
74
- When AWS entries are present, list `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, optional
75
- `AWS_SESSION_TOKEN`, and `AWS_DEFAULT_REGION` exactly as the analysis reported. State that these
76
- bootstrap credentials are for STS assume-role only; do not emit or recommend `aws sso login`.
74
+ When AWS entries are present, list only `LISA_AWS_BOOTSTRAP_JSON` as the
75
+ required secret and `LISA_REMOTE_AGENT=claude` as plain configuration. Never
76
+ emit standard `AWS_ACCESS_KEY_ID` variables and never recommend `aws sso login`.
77
77
 
78
78
  3a. **Emit substrate setup snippets.** When the inventory marks an MCP
79
79
  `headlessUsable: true` through a documented substrate, render the matching
@@ -91,18 +91,11 @@ tracker/source, plus the host project's own package manager and tooling — not
91
91
  transport supports static-token auth. Do not print a Jam `.mcp.json` header snippet because
92
92
  Jam's preferred headless substrate is its PAT-authenticated CLI.
93
93
 
94
- 3b. **Emit AWS assume-role profile setup.** When the inventory includes `awsProfiles`, write an
95
- idempotent `~/.aws/config` block gated on `[ -n "${AWS_ACCESS_KEY_ID:-}" ]`. The generated block
96
- must:
97
- - `mkdir -p "$HOME/.aws"` and create or append profile stanzas without writing secrets.
98
- - Emit one `[profile <name>]` stanza per inventory profile with `role_arn`,
99
- `credential_source = Environment`, `region` when present, and `external_id` when present.
100
- - Keep regions and ExternalIds as non-secret project metadata from the inventory; never invent
101
- account IDs, role names, profile names, regions, or ExternalIds.
102
- - Warn and skip profile writing when AWS profile metadata is absent, while still listing the
103
- required `AWS_*` env var names in the secret template.
104
- - Include a comment that agents should use `aws --profile <name> ...` and must not run
105
- `aws sso login` in headless routines.
94
+ 3b. **Install the shared AWS bootstrap.** When AWS is present, invoke
95
+ `/lisa:setup-remote-aws --platform=claude`. Reuse the resulting
96
+ `scripts/remote-agent-aws-setup.sh`; do not generate a second credential or
97
+ profile implementation. Add `bash scripts/remote-agent-aws-setup.sh` to the
98
+ generated cloud setup after required package installation.
106
99
 
107
100
  4. **Emit the allowlist + gaps notice.** List any custom domains the setup or runtime reaches
108
101
  (from `networkAccess.allowlistDomains`, falling back to legacy `allowlistDomains`) that the user
@@ -139,9 +132,9 @@ shape, not a fixed payload):
139
132
  # # Note: GH_TOKEN is for gh CLI only. Raw git uses Claude's connected-GitHub proxy/identity;
140
133
  # # sibling repos reached only by raw git do not need to be in this token scope.
141
134
  # - GH_TOKEN=<token> # REQUIRED, github is the active tracker+source
142
- # - AWS_ACCESS_KEY_ID=<access-key-id> # REQUIRED when AWS inventory is active
143
- # - AWS_SECRET_ACCESS_KEY=<secret-access-key> # REQUIRED when AWS inventory is active
144
- # - AWS_SESSION_TOKEN=<session-token> # OPTIONAL for temporary bootstrap creds
135
+ # - LISA_AWS_BOOTSTRAP_JSON=<complete SecretString> # REQUIRED for AWS
136
+ # PLAIN:
137
+ # - LISA_REMOTE_AGENT=claude
145
138
  # NETWORK: set the environment to Custom and allowlist these non-default domains if not on Full:
146
139
  # - <networkAccess.allowlistDomains, if any>
147
140
  set -uo pipefail
@@ -184,13 +177,8 @@ need gh || (sudo apt-get update -y && sudo apt-get install -y gh)
184
177
  need jq || sudo apt-get install -y jq
185
178
  require gh; require jq
186
179
 
187
- # --- AWS assume-role profiles, when inventory includes awsProfiles ---
188
- if [ -n "${AWS_ACCESS_KEY_ID:-}" ]; then
189
- mkdir -p "$HOME/.aws"
190
- # Generated stanzas use credential_source=Environment and contain no secrets.
191
- # Agents should run: aws --profile <profile> ...
192
- # Do not run aws sso login in headless routines; SSO requires an interactive browser/device flow.
193
- fi
180
+ # --- AWS assume-role profiles, when AWS inventory is active ---
181
+ bash scripts/remote-agent-aws-setup.sh
194
182
 
195
183
  # --- optional, only with --include-optional ---
196
184
  # (docker / ruby / chromium / etc., guarded)
@@ -213,8 +201,8 @@ to stop are: the analysis could not run, or the `--out` path is not writable.
213
201
  raw git/cross-repo clone guidance must not expand the token scope to sibling repositories.
214
202
  - Never emit an install for a tool the analysis did not surface, and never install `OPTIONAL` tools
215
203
  unless `--include-optional` is set.
216
- - When AWS is in the inventory, generate environment-backed assume-role profile stanzas from
217
- `awsProfiles`; never emit `aws sso login` as a remote setup step.
204
+ - When AWS is in the inventory, reuse `/lisa:setup-remote-aws` and its shared
205
+ setup script; never emit a second profile implementation or `aws sso login`.
218
206
  - Prefer `networkAccess.allowlistDomains` over legacy top-level `allowlistDomains`; never emit
219
207
  domains already covered by the routine environment's default Trusted list.
220
208
  - Keep the script idempotent and detect-before-install so it is safe to re-run and cache.
@@ -0,0 +1,53 @@
1
+ ---
2
+ name: lisa-setup-remote-aws
3
+ description: "Install and validate Lisa's…"
4
+ allowed-tools: ["Bash", "Read", "Write", "Glob", "Grep"]
5
+ ---
6
+
7
+ # Set up remote-agent AWS access: $ARGUMENTS
8
+
9
+ Install the vendor-neutral AWS bootstrap into the current repository.
10
+
11
+ ## Arguments
12
+
13
+ - `--platform=all|claude|codex|cursor|copilot|agy|opencode` (default `all`)
14
+ - `--project=<path>` (default current working directory)
15
+ - `--secret-name=<name>` (default `remote-agent-credentials`)
16
+
17
+ ## Procedure
18
+
19
+ 1. Resolve the plugin root from `PLUGIN_ROOT`, `CLAUDE_PLUGIN_ROOT`,
20
+ `CURSOR_PLUGIN_ROOT`, or the installed `@codyswann/lisa/plugins/lisa` package.
21
+ 2. Run:
22
+
23
+ ```bash
24
+ node "$PLUGIN_ROOT/scripts/install-remote-agent-aws.mjs" $ARGUMENTS
25
+ ```
26
+
27
+ 3. Inspect every path returned by the installer. It must install
28
+ `scripts/remote-agent-aws-setup.sh`; for Cursor it merges
29
+ `.cursor/environment.json`; for Copilot it creates or merges
30
+ `.github/workflows/copilot-setup-steps.yml`; and it always writes
31
+ `docs/remote-agent-aws.md`.
32
+ 4. Run `bash -n scripts/remote-agent-aws-setup.sh`.
33
+ 5. Never write or request the actual bootstrap SecretString in repository
34
+ files. Tell the operator to retrieve the `remote-agent-credentials` secret
35
+ from the shared AWS account and paste its complete SecretString into the
36
+ platform secret named `LISA_AWS_BOOTSTRAP_JSON`. The default cdkstarter
37
+ secret name is `remote-agent-credentials`; downstream infrastructure may
38
+ configure a different name. Pass that name with `--secret-name` so the
39
+ generated runbook contains the exact retrieval command.
40
+ 6. Report the external step that remains: configure that one secret in the
41
+ remote environment and launch a smoke session.
42
+
43
+ ## Contract
44
+
45
+ A future coding agent is supported without an AWS change when its remote
46
+ environment provides a Linux shell, a setup/start hook, one opaque secret,
47
+ a writable home directory, and HTTPS access to AWS STS plus the allowed service
48
+ endpoints. Set `LISA_REMOTE_AGENT` to a stable lowercase platform label; it is
49
+ used only as `role_session_name`.
50
+
51
+ Do not create per-repository or per-agent IAM users. Do not expose the bootstrap
52
+ key through standard `AWS_ACCESS_KEY_ID` variables. Do not add production repair
53
+ permissions; production repair is a human-driven local-workstation workflow.
@@ -0,0 +1,4 @@
1
+ display_name: "Setup Remote AWS"
2
+ short_description: "Install and validate Lisa's…"
3
+ default_prompt:
4
+ - "Use $lisa-setup-remote-aws: Install and validate Lisa's…."
@@ -0,0 +1,6 @@
1
+ ---
2
+ description: "Install Lisa's vendor-neutral AWS CLI bootstrap and platform adapters for remote coding environments."
3
+ argument-hint: "[--platform=all|claude|codex|cursor|copilot|agy|opencode] [--project=<path>] [--secret-name=<name>]"
4
+ ---
5
+
6
+ Use the /lisa-setup-remote-aws skill to install and validate remote coding-agent AWS access. $ARGUMENTS
@@ -0,0 +1,347 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Install Lisa's vendor-neutral remote AWS bootstrap into a host project.
4
+ *
5
+ * This installer writes only non-secret repository artifacts. The bootstrap
6
+ * SecretString is configured directly in each remote-agent platform.
7
+ * @module install-remote-agent-aws
8
+ */
9
+ import {
10
+ chmodSync,
11
+ existsSync,
12
+ mkdirSync,
13
+ readFileSync,
14
+ writeFileSync,
15
+ } from "node:fs";
16
+ import path from "node:path";
17
+ import { fileURLToPath } from "node:url";
18
+
19
+ const SCRIPT_NAME = "remote-agent-aws-setup.sh";
20
+ const INSTALL_COMMAND = `bash scripts/${SCRIPT_NAME}`;
21
+ const VALID_PLATFORMS = new Set([
22
+ "all",
23
+ "claude",
24
+ "codex",
25
+ "cursor",
26
+ "copilot",
27
+ "agy",
28
+ "opencode",
29
+ ]);
30
+
31
+ function parseArguments(arguments_) {
32
+ let project = process.cwd();
33
+ let platform = "all";
34
+ let secretName = "remote-agent-credentials";
35
+ for (const argument of arguments_) {
36
+ if (argument.startsWith("--project=")) {
37
+ project = path.resolve(argument.slice("--project=".length));
38
+ } else if (argument.startsWith("--platform=")) {
39
+ platform = argument.slice("--platform=".length).toLowerCase();
40
+ } else if (argument.startsWith("--secret-name=")) {
41
+ secretName = argument.slice("--secret-name=".length);
42
+ } else {
43
+ throw new Error(`Unknown argument: ${argument}`);
44
+ }
45
+ }
46
+ if (!VALID_PLATFORMS.has(platform)) {
47
+ throw new Error(
48
+ `Unsupported platform '${platform}'. Expected one of: ${[...VALID_PLATFORMS].join(", ")}`
49
+ );
50
+ }
51
+ if (!/^[A-Za-z0-9/_+=.@-]+$/.test(secretName)) {
52
+ throw new Error("--secret-name must be a valid Secrets Manager name");
53
+ }
54
+ return { project, platform, secretName };
55
+ }
56
+
57
+ function writeSetupScript(project) {
58
+ const source = path.join(
59
+ path.dirname(fileURLToPath(import.meta.url)),
60
+ SCRIPT_NAME
61
+ );
62
+ const destination = path.join(project, "scripts", SCRIPT_NAME);
63
+ mkdirSync(path.dirname(destination), { recursive: true });
64
+ writeFileSync(destination, readFileSync(source));
65
+ chmodSync(destination, 0o700);
66
+ return path.relative(project, destination);
67
+ }
68
+
69
+ function installCursorAdapter(project) {
70
+ const environmentPath = path.join(project, ".cursor", "environment.json");
71
+ mkdirSync(path.dirname(environmentPath), { recursive: true });
72
+
73
+ const environment = existsSync(environmentPath)
74
+ ? JSON.parse(readFileSync(environmentPath, "utf8"))
75
+ : {};
76
+ const existingInstall = environment.install;
77
+ if (existingInstall !== undefined && typeof existingInstall !== "string") {
78
+ throw new Error(
79
+ `.cursor/environment.json install must be a string before Lisa can merge ${INSTALL_COMMAND}`
80
+ );
81
+ }
82
+ const lisaInstall = `LISA_REMOTE_AGENT=cursor ${INSTALL_COMMAND}`;
83
+ if (!existingInstall?.includes(SCRIPT_NAME)) {
84
+ environment.install = existingInstall
85
+ ? `${lisaInstall} && ${existingInstall}`
86
+ : lisaInstall;
87
+ }
88
+ writeFileSync(environmentPath, `${JSON.stringify(environment, null, 2)}\n`);
89
+ return path.relative(project, environmentPath);
90
+ }
91
+
92
+ function installCopilotAdapter(project) {
93
+ const workflowPath = path.join(
94
+ project,
95
+ ".github",
96
+ "workflows",
97
+ "copilot-setup-steps.yml"
98
+ );
99
+ mkdirSync(path.dirname(workflowPath), { recursive: true });
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");
108
+ const step = [
109
+ ` - name: ${marker}`,
110
+ " env:",
111
+ secretEnvironment,
112
+ " LISA_REMOTE_AGENT: copilot",
113
+ ` run: ${INSTALL_COMMAND}`,
114
+ ].join("\n");
115
+
116
+ if (!existsSync(workflowPath)) {
117
+ writeFileSync(
118
+ workflowPath,
119
+ [
120
+ 'name: "Copilot Setup Steps"',
121
+ "on:",
122
+ " workflow_dispatch:",
123
+ "jobs:",
124
+ " copilot-setup-steps:",
125
+ " runs-on: ubuntu-latest",
126
+ " permissions:",
127
+ " contents: read",
128
+ " steps:",
129
+ checkoutStep,
130
+ step,
131
+ "",
132
+ ].join("\n")
133
+ );
134
+ return path.relative(project, workflowPath);
135
+ }
136
+
137
+ const current = readFileSync(workflowPath, "utf8");
138
+ const lines = current.split("\n");
139
+ const jobIndex = lines.findIndex(line =>
140
+ /^\s{2}copilot-setup-steps:\s*$/.test(line)
141
+ );
142
+ const jobEndIndex = lines.findIndex(
143
+ (line, index) => index > jobIndex && /^\s{2}\S[^:]*:\s*$/.test(line)
144
+ );
145
+ const stepsIndex = lines.findIndex(
146
+ (line, index) =>
147
+ index > jobIndex &&
148
+ (jobEndIndex < 0 || index < jobEndIndex) &&
149
+ /^\s{4}steps:\s*$/.test(line)
150
+ );
151
+ if (jobIndex < 0 || stepsIndex < 0) {
152
+ throw new Error(
153
+ "Existing copilot-setup-steps.yml must contain jobs.copilot-setup-steps.steps before Lisa can merge the AWS bootstrap step"
154
+ );
155
+ }
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);
245
+ writeFileSync(workflowPath, lines.join("\n"));
246
+ return path.relative(project, workflowPath);
247
+ }
248
+
249
+ function writeGuide(project, platform, secretName) {
250
+ const guidePath = path.join(project, "docs", "remote-agent-aws.md");
251
+ mkdirSync(path.dirname(guidePath), { recursive: true });
252
+ writeFileSync(
253
+ guidePath,
254
+ `# Remote coding-agent AWS access
255
+
256
+ ## Context
257
+
258
+ This repository uses Lisa's vendor-neutral AWS bootstrap. Configure the complete
259
+ Secrets Manager SecretString as the secret \`LISA_AWS_BOOTSTRAP_JSON\`; do not
260
+ split it into \`AWS_ACCESS_KEY_ID\` and \`AWS_SECRET_ACCESS_KEY\` environment
261
+ variables. The setup script writes a named source profile whose only permission
262
+ is \`sts:AssumeRole\`, then creates automatically refreshed role profiles.
263
+
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
270
+
271
+ | Runtime | Setup |
272
+ |---|---|
273
+ | Claude | Add \`LISA_AWS_BOOTSTRAP_JSON\` to the cloud environment, set \`LISA_REMOTE_AGENT=claude\`, and run \`${INSTALL_COMMAND}\` in the setup script. |
274
+ | Codex | Add \`LISA_AWS_BOOTSTRAP_JSON\` as a setup secret, set \`LISA_REMOTE_AGENT=codex\`, and run \`${INSTALL_COMMAND}\` in the setup script. Permit required AWS endpoints during the agent phase. |
275
+ | Cursor | Add the secret to the selected cloud environment. \`.cursor/environment.json\` runs the setup script with \`LISA_REMOTE_AGENT=cursor\`. |
276
+ | Copilot | Add the value as an organization-level **Agents** secret. \`copilot-setup-steps.yml\` runs the setup script with \`LISA_REMOTE_AGENT=copilot\`. |
277
+ | Antigravity | Direct AWS CLI is supported only on a user-managed remote host. Google's managed preview currently documents proxy-injected HTTP credentials, not arbitrary AWS credential files. |
278
+ | OpenCode | Run the setup script on the VPS/container that hosts \`opencode serve\`, with \`LISA_REMOTE_AGENT=opencode\`. OpenCode does not supply the host. |
279
+
280
+ Generated for: \`${platform}\`.
281
+
282
+ ## Implementation
283
+
284
+ The bootstrap bundle defines the available profile names. \`dev\` is the
285
+ default when present. Select another account explicitly, for example:
286
+
287
+ \`\`\`bash
288
+ aws --profile staging sts get-caller-identity
289
+ aws --profile production cloudformation describe-stacks
290
+ \`\`\`
291
+
292
+ Production and shared profiles are observer-only. Production repair remains a
293
+ human-driven local-workstation operation.
294
+
295
+ After the infrastructure pipeline deploys the remote-agent IAM stacks, retrieve
296
+ the complete bundle from the shared account. Do not extract or distribute its
297
+ individual access-key fields:
298
+
299
+ \`\`\`bash
300
+ aws --profile shared secretsmanager get-secret-value \\
301
+ --secret-id ${secretName} \\
302
+ --query SecretString \\
303
+ --output text
304
+ \`\`\`
305
+
306
+ Store that exact output as the masked secret \`LISA_AWS_BOOTSTRAP_JSON\` on each
307
+ remote-agent platform. Run the setup command and require its live
308
+ \`sts:GetCallerIdentity\` check to pass before considering the platform ready.
309
+
310
+ ## Notes
311
+
312
+ Rotate by replacing the bootstrap IAM access key through infrastructure and
313
+ then updating this one secret on every configured platform. Disabling or
314
+ deleting the bootstrap user immediately prevents new role sessions.
315
+ `
316
+ );
317
+ return path.relative(project, guidePath);
318
+ }
319
+
320
+ export function installRemoteAgentAws(arguments_ = process.argv.slice(2)) {
321
+ const { project, platform, secretName } = parseArguments(arguments_);
322
+ if (!existsSync(project))
323
+ throw new Error(`Project does not exist: ${project}`);
324
+
325
+ const written = [writeSetupScript(project)];
326
+ if (platform === "all" || platform === "cursor") {
327
+ written.push(installCursorAdapter(project));
328
+ }
329
+ if (platform === "all" || platform === "copilot") {
330
+ written.push(installCopilotAdapter(project));
331
+ }
332
+ written.push(writeGuide(project, platform, secretName));
333
+ return { project, platform, secretName, written };
334
+ }
335
+
336
+ if (process.argv[1] === fileURLToPath(import.meta.url)) {
337
+ try {
338
+ process.stdout.write(
339
+ `${JSON.stringify(installRemoteAgentAws(), null, 2)}\n`
340
+ );
341
+ } catch (error) {
342
+ process.stderr.write(
343
+ `install-remote-agent-aws: ${error instanceof Error ? error.message : String(error)}\n`
344
+ );
345
+ process.exitCode = 1;
346
+ }
347
+ }