@codyswann/lisa 2.198.1 → 2.199.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. package/cdk/create-only/.github/workflows/deploy.yml +27 -3
  2. package/expo/create-only/.github/workflows/deploy.yml +27 -3
  3. package/nestjs/create-only/.github/workflows/deploy.yml +26 -3
  4. package/package.json +1 -1
  5. package/plugins/lisa/.claude-plugin/plugin.json +1 -1
  6. package/plugins/lisa/.codex-plugin/plugin.json +1 -1
  7. package/plugins/lisa/.codex-plugin/skills/lisa-setup-github-repo/SKILL.md +58 -3
  8. package/plugins/lisa/commands/lisa/setup/github-repo.md +1 -1
  9. package/plugins/lisa/rules/reference/config-resolution.md +6 -0
  10. package/plugins/lisa/skills/lisa-setup-github-repo/SKILL.md +59 -4
  11. package/plugins/lisa-agy/commands/lisa/setup/github-repo.md +1 -1
  12. package/plugins/lisa-agy/plugin.json +1 -1
  13. package/plugins/lisa-agy/skills/lisa-setup-github-repo/SKILL.md +59 -4
  14. package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
  15. package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
  16. package/plugins/lisa-cdk-agy/plugin.json +1 -1
  17. package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
  18. package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
  19. package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
  20. package/plugins/lisa-copilot/commands/lisa/setup/github-repo.md +1 -1
  21. package/plugins/lisa-copilot/rules/reference/config-resolution.md +6 -0
  22. package/plugins/lisa-copilot/skills/lisa-setup-github-repo/SKILL.md +59 -4
  23. package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
  24. package/plugins/lisa-cursor/commands/lisa/setup/github-repo.md +1 -1
  25. package/plugins/lisa-cursor/rules/config-resolution-reference.mdc +6 -0
  26. package/plugins/lisa-cursor/skills/lisa-setup-github-repo/SKILL.md +59 -4
  27. package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
  28. package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
  29. package/plugins/lisa-expo-agy/plugin.json +1 -1
  30. package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
  31. package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
  32. package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
  33. package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
  34. package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
  35. package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
  36. package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
  37. package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
  38. package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
  39. package/plugins/lisa-nestjs-agy/plugin.json +1 -1
  40. package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
  41. package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
  42. package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
  43. package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
  44. package/plugins/lisa-openclaw-agy/plugin.json +1 -1
  45. package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
  46. package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
  47. package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
  48. package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
  49. package/plugins/lisa-phaser-agy/plugin.json +1 -1
  50. package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
  51. package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
  52. package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
  53. package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
  54. package/plugins/lisa-rails-agy/plugin.json +1 -1
  55. package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
  56. package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
  57. package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
  58. package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
  59. package/plugins/lisa-typescript-agy/plugin.json +1 -1
  60. package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
  61. package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
  62. package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
  63. package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
  64. package/plugins/lisa-wiki-agy/plugin.json +1 -1
  65. package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
  66. package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
  67. package/plugins/src/base/commands/lisa/setup/github-repo.md +1 -1
  68. package/plugins/src/base/rules/reference/config-resolution.md +6 -0
  69. package/plugins/src/base/skills/lisa-setup-github-repo/SKILL.md +59 -4
  70. package/scripts/lisa-github-environments.sh +256 -0
  71. package/scripts/lisa-github-repo-setup.sh +11 -4
  72. package/typescript/copy-overwrite/.github/GITHUB_ACTIONS.md +30 -1
@@ -1,5 +1,4 @@
1
- # This file is managed by Lisa.
2
- # Do not edit directly — changes will be overwritten on the next `lisa` run.
1
+ # This file is created by Lisa (create-only — edit freely, Lisa won't overwrite it).
3
2
 
4
3
  name: 🚀 Release and Deploy
5
4
 
@@ -30,7 +29,11 @@ jobs:
30
29
  timeout-minutes: 5
31
30
  outputs:
32
31
  environment: ${{ steps.env.outputs.environment }}
32
+ approval_environment: ${{ steps.approval.outputs.approval_environment }}
33
+ require_approval: ${{ steps.approval.outputs.require_approval }}
33
34
  steps:
35
+ - name: 📥 Checkout
36
+ uses: actions/checkout@v6
34
37
  - name: 🔄 Set environment
35
38
  id: env
36
39
  run: |
@@ -39,6 +42,26 @@ jobs:
39
42
  else
40
43
  echo "environment=${{ github.ref_name }}" >> $GITHUB_OUTPUT
41
44
  fi
45
+ - name: 🚦 Resolve approval gate from .lisa.config.json
46
+ id: approval
47
+ run: |
48
+ BRANCH="${{ steps.env.outputs.environment }}"
49
+ CONFIG=".lisa.config.json"
50
+ APPROVAL_ENV="$BRANCH"
51
+ REQUIRE="false"
52
+ if [ -f "$CONFIG" ] && jq empty "$CONFIG" 2>/dev/null; then
53
+ # Friendly environment name for the branch: an explicit
54
+ # github.environments[].branch match wins, then the
55
+ # deploy.branches map, then the branch name itself.
56
+ NAME=$(jq -r --arg b "$BRANCH" '[.github.environments // {} | to_entries[] | select(.value.branch == $b) | .key] | first // empty' "$CONFIG")
57
+ if [ -z "$NAME" ]; then
58
+ NAME=$(jq -r --arg b "$BRANCH" '[.deploy.branches // {} | to_entries[] | select(.value == $b) | .key] | first // empty' "$CONFIG")
59
+ fi
60
+ APPROVAL_ENV="${NAME:-$BRANCH}"
61
+ REQUIRE=$(jq -r --arg e "$APPROVAL_ENV" '.github.environments[$e].require_approval // false' "$CONFIG")
62
+ fi
63
+ echo "approval_environment=$APPROVAL_ENV" >> $GITHUB_OUTPUT
64
+ echo "require_approval=$REQUIRE" >> $GITHUB_OUTPUT
42
65
 
43
66
  release:
44
67
  name: 📦 Release
@@ -49,7 +72,8 @@ jobs:
49
72
  environment: ${{ needs.determine_environment.outputs.environment }}
50
73
  release_strategy: 'standard-version'
51
74
  skip_jobs: 'test:e2e,test:integration'
52
- require_approval: false
75
+ require_approval: ${{ needs.determine_environment.outputs.require_approval == 'true' }}
76
+ approval_environment: ${{ needs.determine_environment.outputs.approval_environment }}
53
77
  require_signatures: false
54
78
  generate_sbom: true
55
79
  override_blackout: true
@@ -1,5 +1,4 @@
1
- # This file is managed by Lisa.
2
- # Do not edit directly — changes will be overwritten on the next `lisa` run.
1
+ # This file is created by Lisa (create-only — edit freely, Lisa won't overwrite it).
3
2
 
4
3
  # This is an example deployment workflow that demonstrates the recommended pattern:
5
4
  # 1. Call the release.yml workflow to create a release
@@ -50,7 +49,11 @@ jobs:
50
49
  timeout-minutes: 5
51
50
  outputs:
52
51
  environment: ${{ steps.env.outputs.environment }}
52
+ approval_environment: ${{ steps.approval.outputs.approval_environment }}
53
+ require_approval: ${{ steps.approval.outputs.require_approval }}
53
54
  steps:
55
+ - name: 📥 Checkout
56
+ uses: actions/checkout@v6
54
57
  - name: 🔄 Set environment
55
58
  id: env
56
59
  run: |
@@ -59,6 +62,26 @@ jobs:
59
62
  else
60
63
  echo "environment=${{ github.ref_name }}" >> $GITHUB_OUTPUT
61
64
  fi
65
+ - name: 🚦 Resolve approval gate from .lisa.config.json
66
+ id: approval
67
+ run: |
68
+ BRANCH="${{ steps.env.outputs.environment }}"
69
+ CONFIG=".lisa.config.json"
70
+ APPROVAL_ENV="$BRANCH"
71
+ REQUIRE="false"
72
+ if [ -f "$CONFIG" ] && jq empty "$CONFIG" 2>/dev/null; then
73
+ # Friendly environment name for the branch: an explicit
74
+ # github.environments[].branch match wins, then the
75
+ # deploy.branches map, then the branch name itself.
76
+ NAME=$(jq -r --arg b "$BRANCH" '[.github.environments // {} | to_entries[] | select(.value.branch == $b) | .key] | first // empty' "$CONFIG")
77
+ if [ -z "$NAME" ]; then
78
+ NAME=$(jq -r --arg b "$BRANCH" '[.deploy.branches // {} | to_entries[] | select(.value == $b) | .key] | first // empty' "$CONFIG")
79
+ fi
80
+ APPROVAL_ENV="${NAME:-$BRANCH}"
81
+ REQUIRE=$(jq -r --arg e "$APPROVAL_ENV" '.github.environments[$e].require_approval // false' "$CONFIG")
82
+ fi
83
+ echo "approval_environment=$APPROVAL_ENV" >> $GITHUB_OUTPUT
84
+ echo "require_approval=$REQUIRE" >> $GITHUB_OUTPUT
62
85
 
63
86
  release:
64
87
  name: 📦 Release
@@ -69,7 +92,8 @@ jobs:
69
92
  environment: ${{ needs.determine_environment.outputs.environment }}
70
93
  release_strategy: 'standard-version'
71
94
  skip_jobs: 'test:e2e,test:unit,test:integration'
72
- require_approval: false
95
+ require_approval: ${{ needs.determine_environment.outputs.require_approval == 'true' }}
96
+ approval_environment: ${{ needs.determine_environment.outputs.approval_environment }}
73
97
  require_signatures: false
74
98
  generate_sbom: true
75
99
  node_version: '22.21.1'
@@ -42,7 +42,11 @@ jobs:
42
42
  timeout-minutes: 5
43
43
  outputs:
44
44
  environment: ${{ steps.env.outputs.environment }}
45
+ approval_environment: ${{ steps.approval.outputs.approval_environment }}
46
+ require_approval: ${{ steps.approval.outputs.require_approval }}
45
47
  steps:
48
+ - name: 📥 Checkout
49
+ uses: actions/checkout@v6
46
50
  - name: 🔄 Set environment
47
51
  id: env
48
52
  run: |
@@ -51,6 +55,26 @@ jobs:
51
55
  else
52
56
  echo "environment=${{ github.ref_name }}" >> $GITHUB_OUTPUT
53
57
  fi
58
+ - name: 🚦 Resolve approval gate from .lisa.config.json
59
+ id: approval
60
+ run: |
61
+ BRANCH="${{ steps.env.outputs.environment }}"
62
+ CONFIG=".lisa.config.json"
63
+ APPROVAL_ENV="$BRANCH"
64
+ REQUIRE="false"
65
+ if [ -f "$CONFIG" ] && jq empty "$CONFIG" 2>/dev/null; then
66
+ # Friendly environment name for the branch: an explicit
67
+ # github.environments[].branch match wins, then the
68
+ # deploy.branches map, then the branch name itself.
69
+ NAME=$(jq -r --arg b "$BRANCH" '[.github.environments // {} | to_entries[] | select(.value.branch == $b) | .key] | first // empty' "$CONFIG")
70
+ if [ -z "$NAME" ]; then
71
+ NAME=$(jq -r --arg b "$BRANCH" '[.deploy.branches // {} | to_entries[] | select(.value == $b) | .key] | first // empty' "$CONFIG")
72
+ fi
73
+ APPROVAL_ENV="${NAME:-$BRANCH}"
74
+ REQUIRE=$(jq -r --arg e "$APPROVAL_ENV" '.github.environments[$e].require_approval // false' "$CONFIG")
75
+ fi
76
+ echo "approval_environment=$APPROVAL_ENV" >> $GITHUB_OUTPUT
77
+ echo "require_approval=$REQUIRE" >> $GITHUB_OUTPUT
54
78
 
55
79
  # Step 1: Create a release
56
80
  release:
@@ -61,11 +85,10 @@ jobs:
61
85
  environment: ${{ needs.determine_environment.outputs.environment }}
62
86
  release_strategy: 'standard-version'
63
87
  skip_jobs: 'test:e2e,test:unit,test:integration,test'
64
- # require_approval: ${{ needs.determine_environment.outputs.environment == 'main' }}
65
- # require_signatures: ${{ needs.determine_environment.outputs.environment == 'main' }}
66
88
  generate_sbom: true
67
89
  override_blackout: true
68
- require_approval: false
90
+ require_approval: ${{ needs.determine_environment.outputs.require_approval == 'true' }}
91
+ approval_environment: ${{ needs.determine_environment.outputs.approval_environment }}
69
92
  require_signatures: false
70
93
  node_version: '22.21.1'
71
94
  package_manager: 'bun'
package/package.json CHANGED
@@ -94,7 +94,7 @@
94
94
  "ws": ">=8.20.1"
95
95
  },
96
96
  "name": "@codyswann/lisa",
97
- "version": "2.198.1",
97
+ "version": "2.199.0",
98
98
  "description": "Claude Code governance framework that applies guardrails, guidance, and automated enforcement to projects",
99
99
  "main": "dist/index.js",
100
100
  "exports": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa",
3
- "version": "2.198.1",
3
+ "version": "2.199.0",
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.198.1",
3
+ "version": "2.199.0",
4
4
  "description": "Universal governance: agents, skills, commands, hooks, and rules for all projects.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: lisa-setup-github-repo
3
3
  description: "Apply Lisa's GitHub repository…"
4
- allowed-tools: ["Bash", "Read", "AskUserQuestion"]
4
+ allowed-tools: ["Bash", "Read", "Edit", "AskUserQuestion"]
5
5
  ---
6
6
 
7
7
  # Setup GitHub Repository Governance
@@ -29,6 +29,28 @@ Apply the fleet-standard GitHub repository configuration to this project's repo.
29
29
  - `prevent delete`, `protect tags` (`v*`), plus stack overlays (`cdk validation`, staging-only `playwright`)
30
30
  - Repos without `.github/workflows/` get only app-based required checks — an Actions check that can never report would block every PR forever
31
31
  3. **Deploy key** (`scripts/setup-deploy-key.sh --yes`) — write-access deploy key + `DEPLOY_KEY` secret, skipped when already configured. The `base` ruleset's `DeployKey: always` bypass is what lets CI version-bump pushes through protected branches.
32
+ 4. **Deployment environments** (`scripts/lisa-github-environments.sh`) — entirely optional; only runs when `.lisa.config.json` declares environments:
33
+ ```json
34
+ {
35
+ "github": {
36
+ "environments": {
37
+ "production": {
38
+ "branch": "main",
39
+ "require_approval": true,
40
+ "reviewers": ["some-user", "some-org/some-team"],
41
+ "prevent_self_review": false,
42
+ "wait_timer": 0
43
+ },
44
+ "staging": { "branch": "staging" }
45
+ }
46
+ }
47
+ }
48
+ ```
49
+ - Environment names are friendly names (`production`), mapped to branches. Branch resolution order: explicit `branch` → `deploy.branches[<name>]` → the name itself.
50
+ - `require_approval: true` provisions **required reviewers** (usernames or `org/team-slug`, max 6) — GitHub pauses any workflow job bound to the environment until a listed reviewer approves it in the Actions UI. Reviewers are mandatory when `require_approval` is true; the script refuses a gate nobody can approve.
51
+ - Every declared environment also gets a **deployment branch policy** pinned to its branch, so only that branch can deploy to it.
52
+ - Provisioning matters: GitHub silently auto-creates an environment **without** protection rules the first time a workflow references it, so an unprovisioned approval gate gates on nothing.
53
+ - The stack `deploy.yml` templates read this same config at runtime and pass `require_approval`/`approval_environment` to `release.yml`, whose `release_approval` job is where the run pauses. Requires a public repo or a paid plan for private repos (the script skips gracefully otherwise).
32
54
 
33
55
  ## Workflow
34
56
 
@@ -56,7 +78,31 @@ bash "$LISA_SCRIPTS/lisa-github-repo-setup.sh" .
56
78
 
57
79
  Requires `gh` authenticated with **admin** permission on the repo. A 403 on rulesets means the plan doesn't support them (private repo on a free personal plan) — settings and deploy key still apply; the script skips rulesets gracefully.
58
80
 
59
- ### Step 4 — Verify
81
+ ### Step 4 — Wire the approval gate into an existing deploy.yml (guided edit)
82
+
83
+ Only when `.lisa.config.json` has a `github.environments` entry with `require_approval: true` AND `.github/workflows/deploy.yml` exists but does not reference `approval_environment`:
84
+
85
+ ```bash
86
+ [ -f .github/workflows/deploy.yml ] \
87
+ && jq -e '[.github.environments // {} | .[] | select(.require_approval == true)] | length > 0' .lisa.config.json > /dev/null 2>&1 \
88
+ && ! grep -q 'approval_environment' .github/workflows/deploy.yml \
89
+ && echo "deploy.yml needs approval wiring"
90
+ ```
91
+
92
+ A `grep` hit is only a first pass — before skipping the edit, read the `release` job's `with:` block and confirm it actually passes both `require_approval` and `approval_environment` from the `determine_environment` outputs (a commented-out or unrelated occurrence doesn't count as wired).
93
+
94
+ `deploy.yml` is create-only — the project owns it, so Lisa never patches it automatically. Show the user the two changes from the current stack template (`<stack>/create-only/.github/workflows/deploy.yml` in the Lisa repo) and offer to apply them via Edit:
95
+
96
+ 1. In `determine_environment`, add a checkout step plus the `🚦 Resolve approval gate from .lisa.config.json` step, and expose the `approval_environment` / `require_approval` outputs.
97
+ 2. In the `release` job's `with:` block, replace `require_approval: false` with:
98
+ ```yaml
99
+ require_approval: ${{ needs.determine_environment.outputs.require_approval == 'true' }}
100
+ approval_environment: ${{ needs.determine_environment.outputs.approval_environment }}
101
+ ```
102
+
103
+ Skip this step (and say why) if the project's deploy.yml doesn't call Lisa's `release.yml` (rails uses `release-rails.yml` and harper-fabric has no release call — approval gating for those stacks is not wired yet).
104
+
105
+ ### Step 5 — Verify
60
106
 
61
107
  ```bash
62
108
  gh api "repos/$(gh repo view --json nameWithOwner -q .nameWithOwner)" \
@@ -64,4 +110,13 @@ gh api "repos/$(gh repo view --json nameWithOwner -q .nameWithOwner)" \
64
110
  gh api "repos/$(gh repo view --json nameWithOwner -q .nameWithOwner)/rulesets" --jq '[.[].name]'
65
111
  ```
66
112
 
67
- Report the applied settings and ruleset names. If any step failed, surface the error — do not mark the setup complete.
113
+ When environments were configured, also confirm each one carries its protection rules and branch policy:
114
+
115
+ ```bash
116
+ gh api "repos/$(gh repo view --json nameWithOwner -q .nameWithOwner)/environments" \
117
+ --jq '.environments[] | {name, protection_rules, deployment_branch_policy}'
118
+ ```
119
+
120
+ For every environment declared in `github.environments`, treat a missing `deployment_branch_policy` — or, when `require_approval` is true, an empty `protection_rules` — as a failure: the environment exists but is unprotected, so an approval gate bound to it would block nothing.
121
+
122
+ Report the applied settings, ruleset names, and environments. If any step failed, surface the error — do not mark the setup complete.
@@ -1,5 +1,5 @@
1
1
  ---
2
- description: "Apply Lisa's GitHub repository governance baseline: merge-only settings with auto-merge and delete-branch-on-merge, branch + tag rulesets (CodeRabbit/GitGuardian/Quality Checks gates, prevent delete, protect tags), and a CI deploy key + DEPLOY_KEY secret. Idempotent; per-repo overrides via .lisa.config.json github.settings."
2
+ description: "Apply Lisa's GitHub repository governance baseline: merge-only settings with auto-merge and delete-branch-on-merge, branch + tag rulesets (CodeRabbit/GitGuardian/Quality Checks gates, prevent delete, protect tags), a CI deploy key + DEPLOY_KEY secret, and optional deployment environments with human-approval gates from .lisa.config.json github.environments. Idempotent; per-repo overrides via .lisa.config.json github.settings."
3
3
  allowed-tools: ["Skill"]
4
4
  argument-hint: ""
5
5
  ---
@@ -274,6 +274,12 @@ Each vendor section is **conditionally required**: required only when that vendo
274
274
  | `github.projects.v2.owner.slug` | GitHub Project coordination is enabled | Owner login for the shared ProjectV2. In v1 it MUST match the tracked repository namespace (`github.org`); cross-namespace coordination is rejected. |
275
275
  | `github.projects.v2.number` | GitHub Project coordination is enabled | Human-facing ProjectV2 number from the GitHub UI / URL. Later utilities resolve the opaque node id from this owner + number pair. |
276
276
  | `github.projects.v2.required` | no | Coordination strictness flag. Default `false` keeps Project membership best-effort; `true` makes Project membership failures block the write. Setup/doctor/runtime validation reads Project ownership + access and branches on this flag: best-effort failures warn, required-mode failures stop the write. |
277
+ | `github.environments` | no | Optional map of friendly environment name → deployment-environment declaration, provisioned by `/lisa:setup:github-repo` (`scripts/lisa-github-environments.sh`). Absent → no environments are touched. Each declared environment gets a deployment branch policy pinned to its branch. |
278
+ | `github.environments.<name>.branch` | no | Branch that deploys to this environment. Resolution order: this field → `deploy.branches[<name>]` → the environment name itself. |
279
+ | `github.environments.<name>.require_approval` | no | Default `false`. `true` provisions required reviewers on the environment and makes the stack `deploy.yml` templates pass `require_approval`/`approval_environment` to `release.yml`, pausing the run at the `release_approval` job until a reviewer approves. |
280
+ | `github.environments.<name>.reviewers` | `require_approval = true` | Array of GitHub usernames or `org/team-slug` entries (max 6) allowed to approve deployments. Mandatory with `require_approval` — an approval gate nobody can approve is refused at provision time. |
281
+ | `github.environments.<name>.prevent_self_review` | no | Default `false`. `true` stops the person who triggered the deployment from approving it themselves. |
282
+ | `github.environments.<name>.wait_timer` | no | Default `0`. Minutes to delay the deployment after approval. |
277
283
 
278
284
  When `tracker = "github"` AND `source = "github"` (self-host), both reads and writes hit the same GitHub repo. Label namespaces are kept separate so the two flows don't collide — see "Self-host edge case" below.
279
285
 
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: lisa-setup-github-repo
3
- description: "Apply Lisa's GitHub repository governance baseline to the current project's repo: repository settings (merge-only, auto-merge, delete-branch-on-merge, update-branch suggestions, wiki off, secret scanning where available), branch + tag rulesets from Lisa templates (base PR gate with CodeRabbit/GitGuardian/Quality Checks, prevent delete, protect tags, stack overlays), and a write-access deploy key + DEPLOY_KEY secret so release workflows can push version bumps through the rulesets' DeployKey bypass. Idempotent — re-running updates settings and rulesets in place and skips an already-configured deploy key."
4
- allowed-tools: ["Bash", "Read", "AskUserQuestion"]
3
+ description: "Apply Lisa's GitHub repository governance baseline to the current project's repo: repository settings (merge-only, auto-merge, delete-branch-on-merge, update-branch suggestions, wiki off, secret scanning where available), branch + tag rulesets from Lisa templates (base PR gate with CodeRabbit/GitGuardian/Quality Checks, prevent delete, protect tags, stack overlays), a write-access deploy key + DEPLOY_KEY secret so release workflows can push version bumps through the rulesets' DeployKey bypass, and optional deployment environments with human-approval gates (required reviewers) from the github.environments block in .lisa.config.json. Idempotent — re-running updates settings, rulesets, and environments in place and skips an already-configured deploy key."
4
+ allowed-tools: ["Bash", "Read", "Edit", "AskUserQuestion"]
5
5
  ---
6
6
 
7
7
  # Setup GitHub Repository Governance
@@ -29,6 +29,28 @@ Apply the fleet-standard GitHub repository configuration to this project's repo.
29
29
  - `prevent delete`, `protect tags` (`v*`), plus stack overlays (`cdk validation`, staging-only `playwright`)
30
30
  - Repos without `.github/workflows/` get only app-based required checks — an Actions check that can never report would block every PR forever
31
31
  3. **Deploy key** (`scripts/setup-deploy-key.sh --yes`) — write-access deploy key + `DEPLOY_KEY` secret, skipped when already configured. The `base` ruleset's `DeployKey: always` bypass is what lets CI version-bump pushes through protected branches.
32
+ 4. **Deployment environments** (`scripts/lisa-github-environments.sh`) — entirely optional; only runs when `.lisa.config.json` declares environments:
33
+ ```json
34
+ {
35
+ "github": {
36
+ "environments": {
37
+ "production": {
38
+ "branch": "main",
39
+ "require_approval": true,
40
+ "reviewers": ["some-user", "some-org/some-team"],
41
+ "prevent_self_review": false,
42
+ "wait_timer": 0
43
+ },
44
+ "staging": { "branch": "staging" }
45
+ }
46
+ }
47
+ }
48
+ ```
49
+ - Environment names are friendly names (`production`), mapped to branches. Branch resolution order: explicit `branch` → `deploy.branches[<name>]` → the name itself.
50
+ - `require_approval: true` provisions **required reviewers** (usernames or `org/team-slug`, max 6) — GitHub pauses any workflow job bound to the environment until a listed reviewer approves it in the Actions UI. Reviewers are mandatory when `require_approval` is true; the script refuses a gate nobody can approve.
51
+ - Every declared environment also gets a **deployment branch policy** pinned to its branch, so only that branch can deploy to it.
52
+ - Provisioning matters: GitHub silently auto-creates an environment **without** protection rules the first time a workflow references it, so an unprovisioned approval gate gates on nothing.
53
+ - The stack `deploy.yml` templates read this same config at runtime and pass `require_approval`/`approval_environment` to `release.yml`, whose `release_approval` job is where the run pauses. Requires a public repo or a paid plan for private repos (the script skips gracefully otherwise).
32
54
 
33
55
  ## Workflow
34
56
 
@@ -56,7 +78,31 @@ bash "$LISA_SCRIPTS/lisa-github-repo-setup.sh" .
56
78
 
57
79
  Requires `gh` authenticated with **admin** permission on the repo. A 403 on rulesets means the plan doesn't support them (private repo on a free personal plan) — settings and deploy key still apply; the script skips rulesets gracefully.
58
80
 
59
- ### Step 4 — Verify
81
+ ### Step 4 — Wire the approval gate into an existing deploy.yml (guided edit)
82
+
83
+ Only when `.lisa.config.json` has a `github.environments` entry with `require_approval: true` AND `.github/workflows/deploy.yml` exists but does not reference `approval_environment`:
84
+
85
+ ```bash
86
+ [ -f .github/workflows/deploy.yml ] \
87
+ && jq -e '[.github.environments // {} | .[] | select(.require_approval == true)] | length > 0' .lisa.config.json > /dev/null 2>&1 \
88
+ && ! grep -q 'approval_environment' .github/workflows/deploy.yml \
89
+ && echo "deploy.yml needs approval wiring"
90
+ ```
91
+
92
+ A `grep` hit is only a first pass — before skipping the edit, read the `release` job's `with:` block and confirm it actually passes both `require_approval` and `approval_environment` from the `determine_environment` outputs (a commented-out or unrelated occurrence doesn't count as wired).
93
+
94
+ `deploy.yml` is create-only — the project owns it, so Lisa never patches it automatically. Show the user the two changes from the current stack template (`<stack>/create-only/.github/workflows/deploy.yml` in the Lisa repo) and offer to apply them via Edit:
95
+
96
+ 1. In `determine_environment`, add a checkout step plus the `🚦 Resolve approval gate from .lisa.config.json` step, and expose the `approval_environment` / `require_approval` outputs.
97
+ 2. In the `release` job's `with:` block, replace `require_approval: false` with:
98
+ ```yaml
99
+ require_approval: ${{ needs.determine_environment.outputs.require_approval == 'true' }}
100
+ approval_environment: ${{ needs.determine_environment.outputs.approval_environment }}
101
+ ```
102
+
103
+ Skip this step (and say why) if the project's deploy.yml doesn't call Lisa's `release.yml` (rails uses `release-rails.yml` and harper-fabric has no release call — approval gating for those stacks is not wired yet).
104
+
105
+ ### Step 5 — Verify
60
106
 
61
107
  ```bash
62
108
  gh api "repos/$(gh repo view --json nameWithOwner -q .nameWithOwner)" \
@@ -64,4 +110,13 @@ gh api "repos/$(gh repo view --json nameWithOwner -q .nameWithOwner)" \
64
110
  gh api "repos/$(gh repo view --json nameWithOwner -q .nameWithOwner)/rulesets" --jq '[.[].name]'
65
111
  ```
66
112
 
67
- Report the applied settings and ruleset names. If any step failed, surface the error — do not mark the setup complete.
113
+ When environments were configured, also confirm each one carries its protection rules and branch policy:
114
+
115
+ ```bash
116
+ gh api "repos/$(gh repo view --json nameWithOwner -q .nameWithOwner)/environments" \
117
+ --jq '.environments[] | {name, protection_rules, deployment_branch_policy}'
118
+ ```
119
+
120
+ For every environment declared in `github.environments`, treat a missing `deployment_branch_policy` — or, when `require_approval` is true, an empty `protection_rules` — as a failure: the environment exists but is unprotected, so an approval gate bound to it would block nothing.
121
+
122
+ Report the applied settings, ruleset names, and environments. If any step failed, surface the error — do not mark the setup complete.
@@ -1,5 +1,5 @@
1
1
  ---
2
- description: "Apply Lisa's GitHub repository governance baseline: merge-only settings with auto-merge and delete-branch-on-merge, branch + tag rulesets (CodeRabbit/GitGuardian/Quality Checks gates, prevent delete, protect tags), and a CI deploy key + DEPLOY_KEY secret. Idempotent; per-repo overrides via .lisa.config.json github.settings."
2
+ description: "Apply Lisa's GitHub repository governance baseline: merge-only settings with auto-merge and delete-branch-on-merge, branch + tag rulesets (CodeRabbit/GitGuardian/Quality Checks gates, prevent delete, protect tags), a CI deploy key + DEPLOY_KEY secret, and optional deployment environments with human-approval gates from .lisa.config.json github.environments. Idempotent; per-repo overrides via .lisa.config.json github.settings."
3
3
  allowed-tools: ["Skill"]
4
4
  argument-hint: ""
5
5
  ---
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa",
3
- "version": "2.198.1",
3
+ "version": "2.199.0",
4
4
  "description": "Universal governance — agents, skills, commands, hooks, and rules for all projects",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: lisa-setup-github-repo
3
- description: "Apply Lisa's GitHub repository governance baseline to the current project's repo: repository settings (merge-only, auto-merge, delete-branch-on-merge, update-branch suggestions, wiki off, secret scanning where available), branch + tag rulesets from Lisa templates (base PR gate with CodeRabbit/GitGuardian/Quality Checks, prevent delete, protect tags, stack overlays), and a write-access deploy key + DEPLOY_KEY secret so release workflows can push version bumps through the rulesets' DeployKey bypass. Idempotent — re-running updates settings and rulesets in place and skips an already-configured deploy key."
4
- allowed-tools: ["Bash", "Read", "AskUserQuestion"]
3
+ description: "Apply Lisa's GitHub repository governance baseline to the current project's repo: repository settings (merge-only, auto-merge, delete-branch-on-merge, update-branch suggestions, wiki off, secret scanning where available), branch + tag rulesets from Lisa templates (base PR gate with CodeRabbit/GitGuardian/Quality Checks, prevent delete, protect tags, stack overlays), a write-access deploy key + DEPLOY_KEY secret so release workflows can push version bumps through the rulesets' DeployKey bypass, and optional deployment environments with human-approval gates (required reviewers) from the github.environments block in .lisa.config.json. Idempotent — re-running updates settings, rulesets, and environments in place and skips an already-configured deploy key."
4
+ allowed-tools: ["Bash", "Read", "Edit", "AskUserQuestion"]
5
5
  ---
6
6
 
7
7
  # Setup GitHub Repository Governance
@@ -29,6 +29,28 @@ Apply the fleet-standard GitHub repository configuration to this project's repo.
29
29
  - `prevent delete`, `protect tags` (`v*`), plus stack overlays (`cdk validation`, staging-only `playwright`)
30
30
  - Repos without `.github/workflows/` get only app-based required checks — an Actions check that can never report would block every PR forever
31
31
  3. **Deploy key** (`scripts/setup-deploy-key.sh --yes`) — write-access deploy key + `DEPLOY_KEY` secret, skipped when already configured. The `base` ruleset's `DeployKey: always` bypass is what lets CI version-bump pushes through protected branches.
32
+ 4. **Deployment environments** (`scripts/lisa-github-environments.sh`) — entirely optional; only runs when `.lisa.config.json` declares environments:
33
+ ```json
34
+ {
35
+ "github": {
36
+ "environments": {
37
+ "production": {
38
+ "branch": "main",
39
+ "require_approval": true,
40
+ "reviewers": ["some-user", "some-org/some-team"],
41
+ "prevent_self_review": false,
42
+ "wait_timer": 0
43
+ },
44
+ "staging": { "branch": "staging" }
45
+ }
46
+ }
47
+ }
48
+ ```
49
+ - Environment names are friendly names (`production`), mapped to branches. Branch resolution order: explicit `branch` → `deploy.branches[<name>]` → the name itself.
50
+ - `require_approval: true` provisions **required reviewers** (usernames or `org/team-slug`, max 6) — GitHub pauses any workflow job bound to the environment until a listed reviewer approves it in the Actions UI. Reviewers are mandatory when `require_approval` is true; the script refuses a gate nobody can approve.
51
+ - Every declared environment also gets a **deployment branch policy** pinned to its branch, so only that branch can deploy to it.
52
+ - Provisioning matters: GitHub silently auto-creates an environment **without** protection rules the first time a workflow references it, so an unprovisioned approval gate gates on nothing.
53
+ - The stack `deploy.yml` templates read this same config at runtime and pass `require_approval`/`approval_environment` to `release.yml`, whose `release_approval` job is where the run pauses. Requires a public repo or a paid plan for private repos (the script skips gracefully otherwise).
32
54
 
33
55
  ## Workflow
34
56
 
@@ -56,7 +78,31 @@ bash "$LISA_SCRIPTS/lisa-github-repo-setup.sh" .
56
78
 
57
79
  Requires `gh` authenticated with **admin** permission on the repo. A 403 on rulesets means the plan doesn't support them (private repo on a free personal plan) — settings and deploy key still apply; the script skips rulesets gracefully.
58
80
 
59
- ### Step 4 — Verify
81
+ ### Step 4 — Wire the approval gate into an existing deploy.yml (guided edit)
82
+
83
+ Only when `.lisa.config.json` has a `github.environments` entry with `require_approval: true` AND `.github/workflows/deploy.yml` exists but does not reference `approval_environment`:
84
+
85
+ ```bash
86
+ [ -f .github/workflows/deploy.yml ] \
87
+ && jq -e '[.github.environments // {} | .[] | select(.require_approval == true)] | length > 0' .lisa.config.json > /dev/null 2>&1 \
88
+ && ! grep -q 'approval_environment' .github/workflows/deploy.yml \
89
+ && echo "deploy.yml needs approval wiring"
90
+ ```
91
+
92
+ A `grep` hit is only a first pass — before skipping the edit, read the `release` job's `with:` block and confirm it actually passes both `require_approval` and `approval_environment` from the `determine_environment` outputs (a commented-out or unrelated occurrence doesn't count as wired).
93
+
94
+ `deploy.yml` is create-only — the project owns it, so Lisa never patches it automatically. Show the user the two changes from the current stack template (`<stack>/create-only/.github/workflows/deploy.yml` in the Lisa repo) and offer to apply them via Edit:
95
+
96
+ 1. In `determine_environment`, add a checkout step plus the `🚦 Resolve approval gate from .lisa.config.json` step, and expose the `approval_environment` / `require_approval` outputs.
97
+ 2. In the `release` job's `with:` block, replace `require_approval: false` with:
98
+ ```yaml
99
+ require_approval: ${{ needs.determine_environment.outputs.require_approval == 'true' }}
100
+ approval_environment: ${{ needs.determine_environment.outputs.approval_environment }}
101
+ ```
102
+
103
+ Skip this step (and say why) if the project's deploy.yml doesn't call Lisa's `release.yml` (rails uses `release-rails.yml` and harper-fabric has no release call — approval gating for those stacks is not wired yet).
104
+
105
+ ### Step 5 — Verify
60
106
 
61
107
  ```bash
62
108
  gh api "repos/$(gh repo view --json nameWithOwner -q .nameWithOwner)" \
@@ -64,4 +110,13 @@ gh api "repos/$(gh repo view --json nameWithOwner -q .nameWithOwner)" \
64
110
  gh api "repos/$(gh repo view --json nameWithOwner -q .nameWithOwner)/rulesets" --jq '[.[].name]'
65
111
  ```
66
112
 
67
- Report the applied settings and ruleset names. If any step failed, surface the error — do not mark the setup complete.
113
+ When environments were configured, also confirm each one carries its protection rules and branch policy:
114
+
115
+ ```bash
116
+ gh api "repos/$(gh repo view --json nameWithOwner -q .nameWithOwner)/environments" \
117
+ --jq '.environments[] | {name, protection_rules, deployment_branch_policy}'
118
+ ```
119
+
120
+ For every environment declared in `github.environments`, treat a missing `deployment_branch_policy` — or, when `require_approval` is true, an empty `protection_rules` — as a failure: the environment exists but is unprotected, so an approval gate bound to it would block nothing.
121
+
122
+ Report the applied settings, ruleset names, and environments. If any step failed, surface the error — do not mark the setup complete.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-cdk",
3
- "version": "2.198.1",
3
+ "version": "2.199.0",
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": "2.198.1",
3
+ "version": "2.199.0",
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": "2.198.1",
3
+ "version": "2.199.0",
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": "2.198.1",
3
+ "version": "2.199.0",
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": "2.198.1",
3
+ "version": "2.199.0",
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": "2.198.1",
3
+ "version": "2.199.0",
4
4
  "description": "Universal governance — agents, skills, commands, hooks, and rules for all projects",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,5 +1,5 @@
1
1
  ---
2
- description: "Apply Lisa's GitHub repository governance baseline: merge-only settings with auto-merge and delete-branch-on-merge, branch + tag rulesets (CodeRabbit/GitGuardian/Quality Checks gates, prevent delete, protect tags), and a CI deploy key + DEPLOY_KEY secret. Idempotent; per-repo overrides via .lisa.config.json github.settings."
2
+ description: "Apply Lisa's GitHub repository governance baseline: merge-only settings with auto-merge and delete-branch-on-merge, branch + tag rulesets (CodeRabbit/GitGuardian/Quality Checks gates, prevent delete, protect tags), a CI deploy key + DEPLOY_KEY secret, and optional deployment environments with human-approval gates from .lisa.config.json github.environments. Idempotent; per-repo overrides via .lisa.config.json github.settings."
3
3
  allowed-tools: ["Skill"]
4
4
  argument-hint: ""
5
5
  ---
@@ -274,6 +274,12 @@ Each vendor section is **conditionally required**: required only when that vendo
274
274
  | `github.projects.v2.owner.slug` | GitHub Project coordination is enabled | Owner login for the shared ProjectV2. In v1 it MUST match the tracked repository namespace (`github.org`); cross-namespace coordination is rejected. |
275
275
  | `github.projects.v2.number` | GitHub Project coordination is enabled | Human-facing ProjectV2 number from the GitHub UI / URL. Later utilities resolve the opaque node id from this owner + number pair. |
276
276
  | `github.projects.v2.required` | no | Coordination strictness flag. Default `false` keeps Project membership best-effort; `true` makes Project membership failures block the write. Setup/doctor/runtime validation reads Project ownership + access and branches on this flag: best-effort failures warn, required-mode failures stop the write. |
277
+ | `github.environments` | no | Optional map of friendly environment name → deployment-environment declaration, provisioned by `/lisa:setup:github-repo` (`scripts/lisa-github-environments.sh`). Absent → no environments are touched. Each declared environment gets a deployment branch policy pinned to its branch. |
278
+ | `github.environments.<name>.branch` | no | Branch that deploys to this environment. Resolution order: this field → `deploy.branches[<name>]` → the environment name itself. |
279
+ | `github.environments.<name>.require_approval` | no | Default `false`. `true` provisions required reviewers on the environment and makes the stack `deploy.yml` templates pass `require_approval`/`approval_environment` to `release.yml`, pausing the run at the `release_approval` job until a reviewer approves. |
280
+ | `github.environments.<name>.reviewers` | `require_approval = true` | Array of GitHub usernames or `org/team-slug` entries (max 6) allowed to approve deployments. Mandatory with `require_approval` — an approval gate nobody can approve is refused at provision time. |
281
+ | `github.environments.<name>.prevent_self_review` | no | Default `false`. `true` stops the person who triggered the deployment from approving it themselves. |
282
+ | `github.environments.<name>.wait_timer` | no | Default `0`. Minutes to delay the deployment after approval. |
277
283
 
278
284
  When `tracker = "github"` AND `source = "github"` (self-host), both reads and writes hit the same GitHub repo. Label namespaces are kept separate so the two flows don't collide — see "Self-host edge case" below.
279
285