@codyswann/lisa 3.69.6 → 3.70.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.
- package/all/copy-overwrite/scripts/lisa-gates.mjs +254 -35
- package/all/copy-overwrite/scripts/lisa-hooks/block-no-verify.sh +224 -7
- package/all/create-only/.github/workflows/continuous-gates.yml +74 -0
- package/cdk/create-only/.github/workflows/deploy.yml +41 -1
- package/dist/codex/scripts/block-no-verify.sh +190 -3
- package/dist/configs/vitest/scratch-global-setup.d.ts +32 -0
- package/dist/configs/vitest/scratch-global-setup.d.ts.map +1 -1
- package/dist/configs/vitest/scratch-global-setup.js +48 -0
- package/dist/configs/vitest/scratch-global-setup.js.map +1 -1
- package/dist/core/lisa-owned-hash-ledger.d.ts.map +1 -1
- package/dist/core/lisa-owned-hash-ledger.js +3 -0
- package/dist/core/lisa-owned-hash-ledger.js.map +1 -1
- package/dist/core/upstream-evidence-manifest.d.ts.map +1 -1
- package/dist/core/upstream-evidence-manifest.js +18 -9
- package/dist/core/upstream-evidence-manifest.js.map +1 -1
- package/dist/opencode/hooks-installer.d.ts.map +1 -1
- package/dist/opencode/hooks-installer.js +14 -0
- package/dist/opencode/hooks-installer.js.map +1 -1
- package/expo/create-only/.github/workflows/deploy.yml +42 -1
- package/harper-fabric/create-only/.github/workflows/deploy.yml +37 -0
- package/nestjs/create-only/.github/workflows/deploy.yml +44 -1
- package/package.json +1 -1
- package/plugins/lisa/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa/hooks/block-no-verify.agy.sh +200 -6
- package/plugins/lisa/hooks/block-no-verify.sh +224 -7
- package/plugins/lisa-agy/hooks/block-no-verify.agy.sh +200 -6
- package/plugins/lisa-agy/plugin.json +1 -1
- package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk-agy/plugin.json +1 -1
- package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-copilot/hooks/block-no-verify.sh +224 -7
- package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cursor/hooks/block-no-verify.sh +224 -7
- package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-expo-agy/plugin.json +1 -1
- package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs-agy/plugin.json +1 -1
- package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw-agy/plugin.json +1 -1
- package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser-agy/plugin.json +1 -1
- package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-rails-agy/plugin.json +1 -1
- package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript-agy/plugin.json +1 -1
- package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki-agy/plugin.json +1 -1
- package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/src/base/hooks/block-no-verify.agy.sh +200 -6
- package/plugins/src/base/hooks/block-no-verify.sh +224 -7
- package/rails/create-only/.github/workflows/deploy.yml +41 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hooks-installer.d.ts","sourceRoot":"","sources":["../../src/opencode/hooks-installer.ts"],"names":[],"mappings":"AAuCA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAOrD,gFAAgF;AAChF,eAAO,MAAM,sBAAsB,WAAW,CAAC;AAE/C,sEAAsE;AACtE,eAAO,MAAM,wBAAwB,kBAAkB,CAAC;AAKxD,wEAAwE;AACxE,eAAO,MAAM,0BAA0B,eAAe,CAAC;AAEvD,2EAA2E;AAC3E,eAAO,MAAM,gCAAgC,oCAAoE,CAAC;
|
|
1
|
+
{"version":3,"file":"hooks-installer.d.ts","sourceRoot":"","sources":["../../src/opencode/hooks-installer.ts"],"names":[],"mappings":"AAuCA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAOrD,gFAAgF;AAChF,eAAO,MAAM,sBAAsB,WAAW,CAAC;AAE/C,sEAAsE;AACtE,eAAO,MAAM,wBAAwB,kBAAkB,CAAC;AAKxD,wEAAwE;AACxE,eAAO,MAAM,0BAA0B,eAAe,CAAC;AAEvD,2EAA2E;AAC3E,eAAO,MAAM,gCAAgC,oCAAoE,CAAC;AAmClH,gDAAgD;AAChD,MAAM,WAAW,0BAA0B;IACzC,uEAAuE;IACvE,QAAQ,CAAC,YAAY,EAAE,SAAS,MAAM,EAAE,CAAC;IACzC,mEAAmE;IACnE,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,6EAA6E;IAC7E,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAC;IAChC,yEAAyE;IACzE,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;CACrC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,YAAY,CAChC,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,EACf,aAAa,EAAE,SAAS,WAAW,EAAE,EACrC,oBAAoB,EAAE,SAAS,MAAM,EAAE,GACtC,OAAO,CAAC,0BAA0B,CAAC,CAyDrC;AAgGD;;;;;;;;;;;;GAYG;AACH,wBAAgB,sBAAsB,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM,CAyCpE;AAiGD;;;;;GAKG;AACH,wBAAsB,wBAAwB,CAC5C,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,SAAS,MAAM,EAAE,CAAC,CAa5B"}
|
|
@@ -52,9 +52,23 @@ export const OPENCODE_EAGER_RULES_INSTRUCTION = `${OPENCODE_CONFIG_DIR}/${OPENCO
|
|
|
52
52
|
* Each glob is matched against the parsed shell command; `*` matches any run of
|
|
53
53
|
* characters. Deny-only (no catch-all allow) so non-matching commands fall
|
|
54
54
|
* through to the host's / OpenCode's default posture.
|
|
55
|
+
*
|
|
56
|
+
* Documented parity gap. OpenCode gives Lisa a glob list, not a hook it can
|
|
57
|
+
* tokenize, so the short `-n` bypass the shell variants close by parsing a
|
|
58
|
+
* `git commit` argv is only PARTLY reachable here. The two globs below cover
|
|
59
|
+
* the spellings that put `-n` directly after the subcommand — `git commit -n`,
|
|
60
|
+
* and by prefix the bundled `-nm "msg"` / `-nam "msg"` — which is every form
|
|
61
|
+
* observed in practice. A cluster that reaches `-n` later on the line
|
|
62
|
+
* (`git commit -am wip -n`) is NOT matched, and cannot be without a glob broad
|
|
63
|
+
* enough to refuse `grep -n`. That is the same precision the `*--no-verify*`
|
|
64
|
+
* glob above already ships with: it likewise fires on the flag quoted inside a
|
|
65
|
+
* commit message. The tokenizing variants (Claude, agy, Codex) have no such
|
|
66
|
+
* gap; this list is a floor, not parity.
|
|
55
67
|
*/
|
|
56
68
|
const NO_VERIFY_DENY_PATTERNS = {
|
|
57
69
|
"*--no-verify*": "deny",
|
|
70
|
+
"*git commit -n*": "deny",
|
|
71
|
+
"*git commit -an*": "deny",
|
|
58
72
|
"*HUSKY=0*": "deny",
|
|
59
73
|
"*HUSKY_SKIP_HOOKS=*": "deny",
|
|
60
74
|
"*core.hooksPath*/dev/null*": "deny",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hooks-installer.js","sourceRoot":"","sources":["../../src/opencode/hooks-installer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,OAAO,KAAK,GAAG,MAAM,UAAU,CAAC;AAChC,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,OAAO,EAAE,EAAE,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC9E,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EACL,UAAU,EACV,MAAM,EACN,KAAK,IAAI,UAAU,GAEpB,MAAM,cAAc,CAAC;AAEtB,OAAO,EAAE,cAAc,EAA2B,MAAM,qBAAqB,CAAC;AAC9E,OAAO,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAC/D,OAAO,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AACpD,OAAO,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAC9D,OAAO,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAEhE,gFAAgF;AAChF,MAAM,CAAC,MAAM,sBAAsB,GAAG,QAAQ,CAAC;AAE/C,sEAAsE;AACtE,MAAM,CAAC,MAAM,wBAAwB,GAAG,eAAe,CAAC;AAExD,iFAAiF;AACjF,MAAM,kBAAkB,GAAG,OAAO,CAAC;AAEnC,wEAAwE;AACxE,MAAM,CAAC,MAAM,0BAA0B,GAAG,YAAY,CAAC;AAEvD,2EAA2E;AAC3E,MAAM,CAAC,MAAM,gCAAgC,GAAG,GAAG,mBAAmB,IAAI,0BAA0B,aAAa,CAAC;AAElH
|
|
1
|
+
{"version":3,"file":"hooks-installer.js","sourceRoot":"","sources":["../../src/opencode/hooks-installer.ts"],"names":[],"mappings":"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"}
|
|
@@ -86,11 +86,36 @@ jobs:
|
|
|
86
86
|
echo "approval_environment=$APPROVAL_ENV" >> $GITHUB_OUTPUT
|
|
87
87
|
echo "require_approval=$REQUIRE" >> $GITHUB_OUTPUT
|
|
88
88
|
|
|
89
|
+
# ── Pre-deploy gates ──────────────────────────────────────────────────────
|
|
90
|
+
# Resolves `pre-deploy:<environment>` from the `gates` block of
|
|
91
|
+
# .lisa.config.json and runs whatever is declared there, BEFORE the release.
|
|
92
|
+
#
|
|
93
|
+
# This is what makes a declaration like
|
|
94
|
+
#
|
|
95
|
+
# "runtime-web-vulnerability": { "pre-deploy:production": "required" }
|
|
96
|
+
#
|
|
97
|
+
# mean something. A required gate that fails — or that resolves to no prover at
|
|
98
|
+
# all, which is UNPROVABLE rather than passing — stops the workflow here, so
|
|
99
|
+
# the release is never cut. A gate job running BESIDE the deploy would go red
|
|
100
|
+
# after the tag and the artefacts had already shipped, which is a report.
|
|
101
|
+
#
|
|
102
|
+
# A project that declares nothing at this moment gets a green job that ran no
|
|
103
|
+
# gates and says so, and the deploy behaves exactly as it did before.
|
|
104
|
+
pre_deploy_gates:
|
|
105
|
+
name: 🚦 Pre-Deploy Gates
|
|
106
|
+
uses: CodySwannGT/lisa/.github/workflows/gates.yml@main
|
|
107
|
+
needs: [determine_environment]
|
|
108
|
+
with:
|
|
109
|
+
moment: pre-deploy:${{ needs.determine_environment.outputs.environment }}
|
|
110
|
+
node_version: '22.21.1'
|
|
111
|
+
package_manager: 'bun'
|
|
112
|
+
secrets: inherit
|
|
113
|
+
|
|
89
114
|
release:
|
|
90
115
|
name: 📦 Release
|
|
91
116
|
# Reference to the quality checks workflow
|
|
92
117
|
uses: CodySwannGT/lisa/.github/workflows/release.yml@main
|
|
93
|
-
needs: [determine_environment]
|
|
118
|
+
needs: [determine_environment, pre_deploy_gates]
|
|
94
119
|
permissions:
|
|
95
120
|
contents: write
|
|
96
121
|
pull-requests: read
|
|
@@ -262,3 +287,19 @@ jobs:
|
|
|
262
287
|
|
|
263
288
|
- name: 📢 Notify on success
|
|
264
289
|
run: echo "Successfully deployed version ${{ steps.get_version.outputs.version }} to ${{ needs.determine_environment.outputs.environment }}"
|
|
290
|
+
|
|
291
|
+
# ── Post-deploy gates ─────────────────────────────────────────────────────
|
|
292
|
+
# Proves a property of what is now live — an accessibility audit or a DAST
|
|
293
|
+
# baseline against the published web build. A red here does not un-ship the
|
|
294
|
+
# release; it names the gate that does not hold so an operator can act on it,
|
|
295
|
+
# which is the difference between a post-deploy gate and a post-deploy log.
|
|
296
|
+
post_deploy_gates:
|
|
297
|
+
name: 🚦 Post-Deploy Gates
|
|
298
|
+
uses: CodySwannGT/lisa/.github/workflows/gates.yml@main
|
|
299
|
+
needs: [determine_environment, deploy]
|
|
300
|
+
if: ${{ always() && !cancelled() && needs.deploy.result == 'success' }}
|
|
301
|
+
with:
|
|
302
|
+
moment: post-deploy:${{ needs.determine_environment.outputs.environment }}
|
|
303
|
+
node_version: '22.21.1'
|
|
304
|
+
package_manager: 'bun'
|
|
305
|
+
secrets: inherit
|
|
@@ -17,8 +17,30 @@ concurrency:
|
|
|
17
17
|
cancel-in-progress: true
|
|
18
18
|
|
|
19
19
|
jobs:
|
|
20
|
+
# ── Pre-deploy gates ──────────────────────────────────────────────────────
|
|
21
|
+
# Resolves `pre-deploy:<branch>` from the `gates` block of .lisa.config.json
|
|
22
|
+
# and runs whatever is declared there, BEFORE the deploy job.
|
|
23
|
+
#
|
|
24
|
+
# This is what makes a declaration like
|
|
25
|
+
#
|
|
26
|
+
# "runtime-web-vulnerability": { "pre-deploy:main": "required" }
|
|
27
|
+
#
|
|
28
|
+
# mean something. A required gate that fails — or that resolves to no prover at
|
|
29
|
+
# all, which is UNPROVABLE rather than passing — stops the workflow here, so
|
|
30
|
+
# nothing is deployed. A gate job running BESIDE the deploy would go red after
|
|
31
|
+
# the thing had already shipped, which is a report.
|
|
32
|
+
pre_deploy_gates:
|
|
33
|
+
name: 🚦 Pre-Deploy Gates
|
|
34
|
+
uses: CodySwannGT/lisa/.github/workflows/gates.yml@main
|
|
35
|
+
with:
|
|
36
|
+
moment: pre-deploy:${{ github.ref_name }}
|
|
37
|
+
node_version: '22.21.1'
|
|
38
|
+
package_manager: 'bun'
|
|
39
|
+
secrets: inherit
|
|
40
|
+
|
|
20
41
|
deploy:
|
|
21
42
|
name: Build, deploy, and verify
|
|
43
|
+
needs: [pre_deploy_gates]
|
|
22
44
|
runs-on: ubuntu-latest
|
|
23
45
|
timeout-minutes: 30
|
|
24
46
|
env:
|
|
@@ -83,3 +105,18 @@ jobs:
|
|
|
83
105
|
else
|
|
84
106
|
echo "No verify script defined; add one to smoke-test the deployed Harper endpoint."
|
|
85
107
|
fi
|
|
108
|
+
|
|
109
|
+
# ── Post-deploy gates ─────────────────────────────────────────────────────
|
|
110
|
+
# Proves a property of what is now live. A red here does not un-ship the
|
|
111
|
+
# deploy; it names the gate that does not hold so an operator can act on it,
|
|
112
|
+
# which is the difference between a post-deploy gate and a post-deploy log.
|
|
113
|
+
post_deploy_gates:
|
|
114
|
+
name: 🚦 Post-Deploy Gates
|
|
115
|
+
uses: CodySwannGT/lisa/.github/workflows/gates.yml@main
|
|
116
|
+
needs: [deploy]
|
|
117
|
+
if: ${{ always() && !cancelled() && needs.deploy.result == 'success' }}
|
|
118
|
+
with:
|
|
119
|
+
moment: post-deploy:${{ github.ref_name }}
|
|
120
|
+
node_version: '22.21.1'
|
|
121
|
+
package_manager: 'bun'
|
|
122
|
+
secrets: inherit
|
|
@@ -81,11 +81,37 @@ jobs:
|
|
|
81
81
|
echo "approval_environment=$APPROVAL_ENV" >> $GITHUB_OUTPUT
|
|
82
82
|
echo "require_approval=$REQUIRE" >> $GITHUB_OUTPUT
|
|
83
83
|
|
|
84
|
+
# ── Pre-deploy gates ──────────────────────────────────────────────────────
|
|
85
|
+
# Resolves `pre-deploy:<environment>` from the `gates` block of
|
|
86
|
+
# .lisa.config.json and runs whatever is declared there, BEFORE the release.
|
|
87
|
+
#
|
|
88
|
+
# This is what makes a declaration like
|
|
89
|
+
#
|
|
90
|
+
# "runtime-web-vulnerability": { "pre-deploy:production": "required" }
|
|
91
|
+
# "load-capacity": { "pre-deploy:production": "required" }
|
|
92
|
+
#
|
|
93
|
+
# mean something. A required gate that fails — or that resolves to no prover at
|
|
94
|
+
# all, which is UNPROVABLE rather than passing — stops the workflow here, so
|
|
95
|
+
# the release is never cut. A gate job running BESIDE the deploy would go red
|
|
96
|
+
# after the tag and the artefacts had already shipped, which is a report.
|
|
97
|
+
#
|
|
98
|
+
# A project that declares nothing at this moment gets a green job that ran no
|
|
99
|
+
# gates and says so, and the deploy behaves exactly as it did before.
|
|
100
|
+
pre_deploy_gates:
|
|
101
|
+
name: 🚦 Pre-Deploy Gates
|
|
102
|
+
uses: CodySwannGT/lisa/.github/workflows/gates.yml@main
|
|
103
|
+
needs: [determine_environment]
|
|
104
|
+
with:
|
|
105
|
+
moment: pre-deploy:${{ needs.determine_environment.outputs.environment }}
|
|
106
|
+
node_version: '22.21.1'
|
|
107
|
+
package_manager: 'bun'
|
|
108
|
+
secrets: inherit
|
|
109
|
+
|
|
84
110
|
# Step 1: Create a release
|
|
85
111
|
release:
|
|
86
112
|
name: 📦 Release
|
|
87
113
|
uses: CodySwannGT/lisa/.github/workflows/release.yml@main
|
|
88
|
-
needs: [determine_environment]
|
|
114
|
+
needs: [determine_environment, pre_deploy_gates]
|
|
89
115
|
permissions:
|
|
90
116
|
contents: write
|
|
91
117
|
pull-requests: read
|
|
@@ -391,6 +417,23 @@ jobs:
|
|
|
391
417
|
- name: 📢 Notify on success
|
|
392
418
|
run: echo "Successfully deployed version ${{ needs.release.outputs.version }} to ${{ needs.determine_environment.outputs.environment }}"
|
|
393
419
|
|
|
420
|
+
# ── Post-deploy gates ─────────────────────────────────────────────────────
|
|
421
|
+
# Proves a property of what is now live — a DAST baseline against the running
|
|
422
|
+
# service, a load profile against the deployed stack. A red here does not
|
|
423
|
+
# un-ship the release; it names the gate that does not hold so an operator can
|
|
424
|
+
# act on it, which is the difference between a post-deploy gate and a
|
|
425
|
+
# post-deploy log.
|
|
426
|
+
post_deploy_gates:
|
|
427
|
+
name: 🚦 Post-Deploy Gates
|
|
428
|
+
uses: CodySwannGT/lisa/.github/workflows/gates.yml@main
|
|
429
|
+
needs: [determine_environment, deploy]
|
|
430
|
+
if: ${{ always() && !cancelled() && needs.deploy.result == 'success' }}
|
|
431
|
+
with:
|
|
432
|
+
moment: post-deploy:${{ needs.determine_environment.outputs.environment }}
|
|
433
|
+
node_version: '22.21.1'
|
|
434
|
+
package_manager: 'bun'
|
|
435
|
+
secrets: inherit
|
|
436
|
+
|
|
394
437
|
# Step 3: Load testing for staging deployments
|
|
395
438
|
load_testing:
|
|
396
439
|
name: 🔍 Load Testing
|
package/package.json
CHANGED
|
@@ -143,7 +143,7 @@
|
|
|
143
143
|
"zod-validation-error": "^4.0.0"
|
|
144
144
|
},
|
|
145
145
|
"name": "@codyswann/lisa",
|
|
146
|
-
"version": "3.
|
|
146
|
+
"version": "3.70.1",
|
|
147
147
|
"description": "Claude Code governance framework that applies guardrails, guidance, and automated enforcement to projects",
|
|
148
148
|
"main": "dist/index.js",
|
|
149
149
|
"exports": {
|
|
@@ -1,11 +1,20 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
2
|
# Antigravity (agy) PreToolUse hook: blocks shell commands that bypass Lisa's
|
|
3
3
|
# git quality gates (exact parity with the Claude block-no-verify.sh): the
|
|
4
|
-
# `--no-verify` long flag
|
|
5
|
-
#
|
|
6
|
-
#
|
|
7
|
-
#
|
|
8
|
-
#
|
|
4
|
+
# `--no-verify` long flag and any abbreviation git accepts for it, its short
|
|
5
|
+
# form `-n` as a real argv token of a `git commit` (bare or bundled, as in
|
|
6
|
+
# `-nm "msg"`), `HUSKY=0`/`HUSKY_SKIP_HOOKS=` (disables husky hooks), and
|
|
7
|
+
# `core.hooksPath` pointed anywhere but an allowlisted in-repo hooks dir
|
|
8
|
+
# (disables all git hooks). Shell-token matching avoids false positives from
|
|
9
|
+
# issue bodies, heredocs, and commit-message prose while still catching quoted
|
|
10
|
+
# real argv values such as `git -c "core.hooksPath=/dev/null"`.
|
|
11
|
+
#
|
|
12
|
+
# This file used to be cited BY the Claude variant as the reason the short `-n`
|
|
13
|
+
# was excluded ("grep cannot distinguish a real -n option from prose"). Neither
|
|
14
|
+
# variant greps: both tokenize, which is exactly what tells an option from a
|
|
15
|
+
# quoted message. The cross-reference outlived the matcher it described, and a
|
|
16
|
+
# stale rationale is worse than none — it reads as a considered decision and
|
|
17
|
+
# stops the next reader from re-examining it.
|
|
9
18
|
#
|
|
10
19
|
# agy protocol (distinct from the Claude block-no-verify.sh exit-code protocol):
|
|
11
20
|
# - stdin = JSON: { "toolCall": { "name": "run_command",
|
|
@@ -25,7 +34,7 @@ allow() {
|
|
|
25
34
|
}
|
|
26
35
|
|
|
27
36
|
deny() {
|
|
28
|
-
printf '%s\n' '{"decision":"deny","reason":"This command bypasses Lisa pre-commit/pre-push quality gates (--no-verify, HUSKY=0, or core.hooksPath disabling). Fix the underlying issue (security audit, lint, typecheck, tests, formatting) instead. If a fix is genuinely impossible, ask the user to make the risk-acceptance decision and add a specific documented ignore; never bypass the hook."}'
|
|
37
|
+
printf '%s\n' '{"decision":"deny","reason":"This command bypasses Lisa pre-commit/pre-push quality gates (--no-verify or its short form -n, HUSKY=0, or core.hooksPath disabling). `git commit -n` and a cluster such as `-nm \"msg\"` skip pre-commit exactly as completely as the long flag. Fix the underlying issue (security audit, lint, typecheck, tests, formatting) instead. If a fix is genuinely impossible, ask the user to make the risk-acceptance decision and add a specific documented ignore; never bypass the hook."}'
|
|
29
38
|
exit 0
|
|
30
39
|
}
|
|
31
40
|
|
|
@@ -123,6 +132,191 @@ def is_permitted_hooks_path(value):
|
|
|
123
132
|
return cleaned.rstrip("/") in PERMITTED_HOOKS_PATHS
|
|
124
133
|
|
|
125
134
|
|
|
135
|
+
# `git commit -n` is the short spelling of --no-verify and skips pre-commit and
|
|
136
|
+
# commit-msg identically; so does a cluster such as `-nm "msg"`. Matching it
|
|
137
|
+
# needs SCOPE, which is why it was long excluded: `-n` means --dry-run to `git
|
|
138
|
+
# push`, --no-stat to `git merge`, and a line count to head/tail/sort/grep. Only
|
|
139
|
+
# `git commit` reads it as a hook bypass, so only that invocation's argv is
|
|
140
|
+
# scanned. Parity with the Claude block-no-verify.sh, which carries the full
|
|
141
|
+
# rationale for why a tokenizer can make this distinction and a grep could not.
|
|
142
|
+
#
|
|
143
|
+
# A SECOND tokenizer pass, deliberately not shared with the checks above:
|
|
144
|
+
# `shlex.split` glues `|` and `;` to their neighbours, so `git commit -m x &&
|
|
145
|
+
# grep -n foo` would read as one long git invocation and refuse the grep.
|
|
146
|
+
# `punctuation_chars=True` emits the operators as their own tokens, giving the
|
|
147
|
+
# scan a boundary to stop at.
|
|
148
|
+
COMMAND_SEPARATORS = {
|
|
149
|
+
";", "|", "||", "&", "&&", "(", ")", "<", ">", ">>", "<<", "&|",
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
# git's own options taking a SEPARATE value token, skipped when looking for the
|
|
153
|
+
# subcommand so `git -c core.hooksPath=x commit` still reaches `commit`.
|
|
154
|
+
GIT_GLOBAL_SEPARATE_VALUE = {
|
|
155
|
+
"-c", "-C", "--config-env", "--git-dir", "--work-tree",
|
|
156
|
+
"--namespace", "--exec-path", "--super-prefix",
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
# `git commit` long options whose value can be a separate token, listed so the
|
|
160
|
+
# VALUE is never read as a flag (`git commit --author "A -n B" -m x` is fine).
|
|
161
|
+
# Only genuinely value-taking options belong here: a boolean added by mistake
|
|
162
|
+
# would swallow the next token and miss `git commit --amend -n`.
|
|
163
|
+
COMMIT_LONG_SEPARATE_VALUE = {
|
|
164
|
+
"--message", "--file", "--author", "--date", "--template", "--cleanup",
|
|
165
|
+
"--reuse-message", "--reedit-message", "--fixup", "--squash",
|
|
166
|
+
"--pathspec-from-file", "--trailer",
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
# Short options REQUIRING a value. In a cluster the value is whatever follows in
|
|
170
|
+
# the same token, or the next token when nothing does — which is why `-mn` is
|
|
171
|
+
# the message "n" and not a bypass, while `-nm msg` is one.
|
|
172
|
+
COMMIT_SHORT_REQUIRED_VALUE = set("mFcCt")
|
|
173
|
+
|
|
174
|
+
# Short options taking an OPTIONAL value, which git reads only when attached:
|
|
175
|
+
# `-uno` is --untracked-files=no, so `-un` is a mode and not a bypass.
|
|
176
|
+
COMMIT_SHORT_OPTIONAL_VALUE = set("uS")
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
def line_boundaries_as_separators(text):
|
|
180
|
+
"""Turn newlines into command separators, keeping continuations intact.
|
|
181
|
+
|
|
182
|
+
A newline ends a command exactly as `;` does, but shlex treats it as plain
|
|
183
|
+
whitespace — so `git commit -m x` on one line and `grep -n foo` on the next
|
|
184
|
+
read as ONE invocation and the grep gets refused. That is the same
|
|
185
|
+
false-positive class the short-form match exists to avoid, arriving through
|
|
186
|
+
the boundary rather than through the token.
|
|
187
|
+
|
|
188
|
+
Backslash-newline is joined first, because there the newline is NOT a
|
|
189
|
+
boundary: `git commit \\` + newline + `-nm x` is one command, and turning
|
|
190
|
+
that newline into a separator would hide a real bypass.
|
|
191
|
+
|
|
192
|
+
Args:
|
|
193
|
+
text: The command line, heredoc payloads already stripped.
|
|
194
|
+
|
|
195
|
+
Returns:
|
|
196
|
+
The same command with line breaks spelled as separators.
|
|
197
|
+
"""
|
|
198
|
+
joined = text.replace("\r\n", "\n").replace("\\\n", " ")
|
|
199
|
+
return joined.replace("\n", " ; ")
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
def shell_tokens(text):
|
|
203
|
+
"""Tokenize a command with shell operators kept as their own tokens.
|
|
204
|
+
|
|
205
|
+
Args:
|
|
206
|
+
text: The command line, heredoc payloads already stripped.
|
|
207
|
+
|
|
208
|
+
Returns:
|
|
209
|
+
The token list, with `;`, `|`, `&&`, `(` and friends standing alone.
|
|
210
|
+
"""
|
|
211
|
+
lexer = shlex.shlex(
|
|
212
|
+
line_boundaries_as_separators(text), posix=True, punctuation_chars=True
|
|
213
|
+
)
|
|
214
|
+
lexer.whitespace_split = True
|
|
215
|
+
# shlex treats `#` as a comment introducer and would truncate the line;
|
|
216
|
+
# `shlex.split` disables it, and so must this.
|
|
217
|
+
lexer.commenters = ""
|
|
218
|
+
return list(lexer)
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
def cluster_skips_verification(cluster):
|
|
222
|
+
"""Read a short-option cluster the way git's own parser reads it.
|
|
223
|
+
|
|
224
|
+
Args:
|
|
225
|
+
cluster: A single token beginning with one `-`, e.g. `-nm` or `-mn`.
|
|
226
|
+
|
|
227
|
+
Returns:
|
|
228
|
+
A pair (bypasses, consumes_next_token).
|
|
229
|
+
"""
|
|
230
|
+
body = cluster[1:]
|
|
231
|
+
for offset, letter in enumerate(body):
|
|
232
|
+
if letter == "n":
|
|
233
|
+
return (True, False)
|
|
234
|
+
if letter in COMMIT_SHORT_REQUIRED_VALUE:
|
|
235
|
+
return (False, offset == len(body) - 1)
|
|
236
|
+
if letter in COMMIT_SHORT_OPTIONAL_VALUE:
|
|
237
|
+
return (False, False)
|
|
238
|
+
return (False, False)
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
def commit_bypasses_verification(argv):
|
|
242
|
+
"""Whether a `git commit` invocation's argv carries a real short `-n`.
|
|
243
|
+
|
|
244
|
+
Args:
|
|
245
|
+
argv: Tokens following the `commit` subcommand, to the end of the line.
|
|
246
|
+
|
|
247
|
+
Returns:
|
|
248
|
+
True if git would read one of them as --no-verify.
|
|
249
|
+
"""
|
|
250
|
+
index = 0
|
|
251
|
+
while index < len(argv):
|
|
252
|
+
token = argv[index]
|
|
253
|
+
index += 1
|
|
254
|
+
# `--` ends the options; a file legitimately named `-n` is not a bypass.
|
|
255
|
+
if token in COMMAND_SEPARATORS or token == "--":
|
|
256
|
+
return False
|
|
257
|
+
if token.startswith("--"):
|
|
258
|
+
if token in COMMIT_LONG_SEPARATE_VALUE:
|
|
259
|
+
index += 1
|
|
260
|
+
continue
|
|
261
|
+
# A bare `-` is git's stdin placeholder, not an option cluster.
|
|
262
|
+
if not token.startswith("-") or token == "-":
|
|
263
|
+
continue
|
|
264
|
+
bypasses, consumes_next = cluster_skips_verification(token)
|
|
265
|
+
if bypasses:
|
|
266
|
+
return True
|
|
267
|
+
if consumes_next:
|
|
268
|
+
index += 1
|
|
269
|
+
return False
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
def subcommand_after_git(tokens, start):
|
|
273
|
+
"""Find the subcommand a `git` token introduces, and its argv.
|
|
274
|
+
|
|
275
|
+
Args:
|
|
276
|
+
tokens: The full operator-aware token list.
|
|
277
|
+
start: Index just past the `git` token.
|
|
278
|
+
|
|
279
|
+
Returns:
|
|
280
|
+
A pair (subcommand, argv), or None when the invocation names none.
|
|
281
|
+
"""
|
|
282
|
+
index = start
|
|
283
|
+
while index < len(tokens):
|
|
284
|
+
token = tokens[index]
|
|
285
|
+
if token in COMMAND_SEPARATORS:
|
|
286
|
+
return None
|
|
287
|
+
if not token.startswith("-"):
|
|
288
|
+
return (token, tokens[index + 1:])
|
|
289
|
+
index += 2 if token in GIT_GLOBAL_SEPARATE_VALUE else 1
|
|
290
|
+
return None
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
def git_commit_skips_verification(text):
|
|
294
|
+
"""Whether the command runs `git commit` with the short `-n` bypass.
|
|
295
|
+
|
|
296
|
+
Args:
|
|
297
|
+
text: The command line, heredoc payloads already stripped.
|
|
298
|
+
|
|
299
|
+
Returns:
|
|
300
|
+
True if any `git commit` invocation on the line skips verification.
|
|
301
|
+
"""
|
|
302
|
+
try:
|
|
303
|
+
scoped_tokens = shell_tokens(text)
|
|
304
|
+
except ValueError:
|
|
305
|
+
return False
|
|
306
|
+
for index, token in enumerate(scoped_tokens):
|
|
307
|
+
# `/usr/bin/git` and a bare `git` are the same program; an env prefix
|
|
308
|
+
# (`HUSKY=1 git commit -n`) simply sits in an earlier token.
|
|
309
|
+
if token != "git" and not token.endswith("/git"):
|
|
310
|
+
continue
|
|
311
|
+
found = subcommand_after_git(scoped_tokens, index + 1)
|
|
312
|
+
if found and found[0] == "commit" and commit_bypasses_verification(found[1]):
|
|
313
|
+
return True
|
|
314
|
+
return False
|
|
315
|
+
|
|
316
|
+
|
|
317
|
+
if git_commit_skips_verification(strip_heredocs(command)):
|
|
318
|
+
sys.exit(1)
|
|
319
|
+
|
|
126
320
|
for i, token in enumerate(normalized_tokens):
|
|
127
321
|
if disables_verification(token):
|
|
128
322
|
sys.exit(1)
|