@codyswann/lisa 3.68.0 → 3.69.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 +13 -7
- package/all/copy-overwrite/scripts/lisa-hooks/sonar-secrets.sh +20 -2
- package/dist/codex/scripts/format-on-edit.sh +28 -0
- package/dist/codex/scripts/lint-on-edit.sh +28 -0
- package/dist/codex/scripts/lisa-edit-gate.sh +152 -0
- package/dist/codex/scripts/rubocop-on-edit.sh +28 -0
- package/dist/codex/scripts/sg-scan-on-edit.sh +28 -0
- package/dist/core/lisa-owned-hash-ledger.d.ts.map +1 -1
- package/dist/core/lisa-owned-hash-ledger.js +2 -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 +33 -9
- package/dist/core/upstream-evidence-manifest.js.map +1 -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/debug-hook.sh +13 -3
- package/plugins/lisa/hooks/sonar-secrets.sh +20 -2
- 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/sonar-secrets.sh +20 -2
- package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cursor/hooks/sonar-secrets.sh +20 -2
- 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/hooks/lisa-edit-gate.sh +152 -0
- package/plugins/lisa-nestjs-agy/plugin.json +1 -1
- package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs-copilot/hooks/lisa-edit-gate.sh +152 -0
- package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs-cursor/hooks/lisa-edit-gate.sh +152 -0
- 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/hooks/lisa-edit-gate.sh +152 -0
- package/plugins/lisa-rails/hooks/rubocop-on-edit.sh +23 -0
- package/plugins/lisa-rails/hooks/sg-scan-on-edit.sh +23 -0
- package/plugins/lisa-rails-agy/plugin.json +1 -1
- package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails-copilot/hooks/lisa-edit-gate.sh +152 -0
- package/plugins/lisa-rails-copilot/hooks/rubocop-on-edit.sh +23 -0
- package/plugins/lisa-rails-copilot/hooks/sg-scan-on-edit.sh +23 -0
- package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails-cursor/hooks/lisa-edit-gate.sh +152 -0
- package/plugins/lisa-rails-cursor/hooks/rubocop-on-edit.sh +23 -0
- package/plugins/lisa-rails-cursor/hooks/sg-scan-on-edit.sh +23 -0
- package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript/hooks/format-on-edit.sh +23 -0
- package/plugins/lisa-typescript/hooks/lint-on-edit.sh +23 -0
- package/plugins/lisa-typescript/hooks/lisa-edit-gate.sh +152 -0
- package/plugins/lisa-typescript/hooks/sg-scan-on-edit.sh +23 -0
- package/plugins/lisa-typescript-agy/plugin.json +1 -1
- package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript-copilot/hooks/format-on-edit.sh +23 -0
- package/plugins/lisa-typescript-copilot/hooks/lint-on-edit.sh +23 -0
- package/plugins/lisa-typescript-copilot/hooks/lisa-edit-gate.sh +152 -0
- package/plugins/lisa-typescript-copilot/hooks/sg-scan-on-edit.sh +23 -0
- package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript-cursor/hooks/format-on-edit.sh +23 -0
- package/plugins/lisa-typescript-cursor/hooks/lint-on-edit.sh +23 -0
- package/plugins/lisa-typescript-cursor/hooks/lisa-edit-gate.sh +152 -0
- package/plugins/lisa-typescript-cursor/hooks/sg-scan-on-edit.sh +23 -0
- 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/debug-hook.sh +13 -3
- package/plugins/src/base/hooks/sonar-secrets.sh +20 -2
- package/plugins/src/nestjs/hooks/lisa-edit-gate.sh +152 -0
- package/plugins/src/rails/hooks/lisa-edit-gate.sh +152 -0
- package/plugins/src/rails/hooks/rubocop-on-edit.sh +23 -0
- package/plugins/src/rails/hooks/sg-scan-on-edit.sh +23 -0
- package/plugins/src/typescript/hooks/format-on-edit.sh +23 -0
- package/plugins/src/typescript/hooks/lint-on-edit.sh +23 -0
- package/plugins/src/typescript/hooks/lisa-edit-gate.sh +152 -0
- package/plugins/src/typescript/hooks/sg-scan-on-edit.sh +23 -0
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.69.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": {
|
|
@@ -4,14 +4,24 @@
|
|
|
4
4
|
# This script is a no-op when CLAUDE_DEBUG is not set or set to 0
|
|
5
5
|
##
|
|
6
6
|
|
|
7
|
+
# Read JSON input from stdin BEFORE the no-op check can exit.
|
|
8
|
+
#
|
|
9
|
+
# A hook that exits before consuming stdin closes the read end while its
|
|
10
|
+
# caller's write is still in flight, and the CALLER's write raises EPIPE. The
|
|
11
|
+
# hook exits 0 and looks healthy; the harness takes the failure. Measured on
|
|
12
|
+
# this path at 30 EPIPE in 30 invocations once the payload exceeds the pipe
|
|
13
|
+
# buffer, and ~0.5% at the real payload size (CodySwannGT/lisa#2949).
|
|
14
|
+
#
|
|
15
|
+
# This hook is the worst placement of that defect in the tree, because the
|
|
16
|
+
# early exit is the path it takes on every event of every session that has not
|
|
17
|
+
# set CLAUDE_DEBUG — which is all of them.
|
|
18
|
+
INPUT=$(cat)
|
|
19
|
+
|
|
7
20
|
# Exit immediately if debug mode is not enabled
|
|
8
21
|
if [[ "${CLAUDE_DEBUG:-0}" != "1" ]]; then
|
|
9
22
|
exit 0
|
|
10
23
|
fi
|
|
11
24
|
|
|
12
|
-
# Read JSON input from stdin
|
|
13
|
-
INPUT=$(cat)
|
|
14
|
-
|
|
15
25
|
# Parse hook event info using jq
|
|
16
26
|
HOOK_EVENT=$(echo "$INPUT" | jq -r '.hook_event_name // "unknown"')
|
|
17
27
|
TOOL_NAME=$(echo "$INPUT" | jq -r '.tool_name // "N/A"')
|
|
@@ -38,6 +38,26 @@
|
|
|
38
38
|
# Usage: sonar-secrets.sh <vendor-event-name> # hook payload on stdin
|
|
39
39
|
set -uo pipefail
|
|
40
40
|
|
|
41
|
+
# Read the payload FIRST, before anything here can stand aside.
|
|
42
|
+
#
|
|
43
|
+
# The caller pipes the hook envelope into our stdin. A path that exits before
|
|
44
|
+
# consuming it closes the read end while the caller's write is still in flight,
|
|
45
|
+
# and the caller's write raises EPIPE — a failure in the harness, produced by a
|
|
46
|
+
# hook that had nothing to say and exited 0 saying it. The evidence lands
|
|
47
|
+
# entirely on the writing side, which is why it read as a mystery for so long.
|
|
48
|
+
#
|
|
49
|
+
# It is a race, so it only fires when this process wins. Measured against the
|
|
50
|
+
# real payload at a 1-minute load average of 82: 3 EPIPE in 600 invocations of
|
|
51
|
+
# the no-event path (0.50%), and 30 in 30 once the payload exceeds the pipe
|
|
52
|
+
# buffer. Rare enough to look like a real failure, frequent enough to keep
|
|
53
|
+
# costing re-runs (CodySwannGT/lisa#2949).
|
|
54
|
+
#
|
|
55
|
+
# Reading first is the fix that also covers real callers, rather than only the
|
|
56
|
+
# tests: every exit below now happens after stdin has reached EOF. The same
|
|
57
|
+
# obligation is discharged as an explicit `cat >/dev/null` drain in
|
|
58
|
+
# install-pkgs.sh and setup-jira-cli.sh, which never use the payload at all.
|
|
59
|
+
payload="$(cat)"
|
|
60
|
+
|
|
41
61
|
event="${1:-}"
|
|
42
62
|
[[ -n "$event" ]] || exit 0
|
|
43
63
|
|
|
@@ -50,8 +70,6 @@ command -v sonar >/dev/null 2>&1 || exit 0
|
|
|
50
70
|
# is reviewable, not in whatever shell happened to launch the agent.
|
|
51
71
|
[[ "${LISA_SONAR_HOOK:-on}" == "off" ]] && exit 0
|
|
52
72
|
|
|
53
|
-
payload="$(cat)"
|
|
54
|
-
|
|
55
73
|
# The vendor signals its verdict as JSON on stdout and always exits 0, so the
|
|
56
74
|
# exit code carries no information and the reason text is the only channel.
|
|
57
75
|
inactive_marker='secret scanning is inactive'
|
|
@@ -38,6 +38,26 @@
|
|
|
38
38
|
# Usage: sonar-secrets.sh <vendor-event-name> # hook payload on stdin
|
|
39
39
|
set -uo pipefail
|
|
40
40
|
|
|
41
|
+
# Read the payload FIRST, before anything here can stand aside.
|
|
42
|
+
#
|
|
43
|
+
# The caller pipes the hook envelope into our stdin. A path that exits before
|
|
44
|
+
# consuming it closes the read end while the caller's write is still in flight,
|
|
45
|
+
# and the caller's write raises EPIPE — a failure in the harness, produced by a
|
|
46
|
+
# hook that had nothing to say and exited 0 saying it. The evidence lands
|
|
47
|
+
# entirely on the writing side, which is why it read as a mystery for so long.
|
|
48
|
+
#
|
|
49
|
+
# It is a race, so it only fires when this process wins. Measured against the
|
|
50
|
+
# real payload at a 1-minute load average of 82: 3 EPIPE in 600 invocations of
|
|
51
|
+
# the no-event path (0.50%), and 30 in 30 once the payload exceeds the pipe
|
|
52
|
+
# buffer. Rare enough to look like a real failure, frequent enough to keep
|
|
53
|
+
# costing re-runs (CodySwannGT/lisa#2949).
|
|
54
|
+
#
|
|
55
|
+
# Reading first is the fix that also covers real callers, rather than only the
|
|
56
|
+
# tests: every exit below now happens after stdin has reached EOF. The same
|
|
57
|
+
# obligation is discharged as an explicit `cat >/dev/null` drain in
|
|
58
|
+
# install-pkgs.sh and setup-jira-cli.sh, which never use the payload at all.
|
|
59
|
+
payload="$(cat)"
|
|
60
|
+
|
|
41
61
|
event="${1:-}"
|
|
42
62
|
[[ -n "$event" ]] || exit 0
|
|
43
63
|
|
|
@@ -50,8 +70,6 @@ command -v sonar >/dev/null 2>&1 || exit 0
|
|
|
50
70
|
# is reviewable, not in whatever shell happened to launch the agent.
|
|
51
71
|
[[ "${LISA_SONAR_HOOK:-on}" == "off" ]] && exit 0
|
|
52
72
|
|
|
53
|
-
payload="$(cat)"
|
|
54
|
-
|
|
55
73
|
# The vendor signals its verdict as JSON on stdout and always exits 0, so the
|
|
56
74
|
# exit code carries no information and the reason text is the only channel.
|
|
57
75
|
inactive_marker='secret scanning is inactive'
|
|
@@ -38,6 +38,26 @@
|
|
|
38
38
|
# Usage: sonar-secrets.sh <vendor-event-name> # hook payload on stdin
|
|
39
39
|
set -uo pipefail
|
|
40
40
|
|
|
41
|
+
# Read the payload FIRST, before anything here can stand aside.
|
|
42
|
+
#
|
|
43
|
+
# The caller pipes the hook envelope into our stdin. A path that exits before
|
|
44
|
+
# consuming it closes the read end while the caller's write is still in flight,
|
|
45
|
+
# and the caller's write raises EPIPE — a failure in the harness, produced by a
|
|
46
|
+
# hook that had nothing to say and exited 0 saying it. The evidence lands
|
|
47
|
+
# entirely on the writing side, which is why it read as a mystery for so long.
|
|
48
|
+
#
|
|
49
|
+
# It is a race, so it only fires when this process wins. Measured against the
|
|
50
|
+
# real payload at a 1-minute load average of 82: 3 EPIPE in 600 invocations of
|
|
51
|
+
# the no-event path (0.50%), and 30 in 30 once the payload exceeds the pipe
|
|
52
|
+
# buffer. Rare enough to look like a real failure, frequent enough to keep
|
|
53
|
+
# costing re-runs (CodySwannGT/lisa#2949).
|
|
54
|
+
#
|
|
55
|
+
# Reading first is the fix that also covers real callers, rather than only the
|
|
56
|
+
# tests: every exit below now happens after stdin has reached EOF. The same
|
|
57
|
+
# obligation is discharged as an explicit `cat >/dev/null` drain in
|
|
58
|
+
# install-pkgs.sh and setup-jira-cli.sh, which never use the payload at all.
|
|
59
|
+
payload="$(cat)"
|
|
60
|
+
|
|
41
61
|
event="${1:-}"
|
|
42
62
|
[[ -n "$event" ]] || exit 0
|
|
43
63
|
|
|
@@ -50,8 +70,6 @@ command -v sonar >/dev/null 2>&1 || exit 0
|
|
|
50
70
|
# is reviewable, not in whatever shell happened to launch the agent.
|
|
51
71
|
[[ "${LISA_SONAR_HOOK:-on}" == "off" ]] && exit 0
|
|
52
72
|
|
|
53
|
-
payload="$(cat)"
|
|
54
|
-
|
|
55
73
|
# The vendor signals its verdict as JSON on stdout and always exits 0, so the
|
|
56
74
|
# exit code carries no information and the reason text is the only channel.
|
|
57
75
|
inactive_marker='secret scanning is inactive'
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# This file is managed by Lisa.
|
|
3
|
+
# Do not edit directly — changes will be overwritten on the next `lisa` run.
|
|
4
|
+
# =============================================================================
|
|
5
|
+
# Edit-time gate façade
|
|
6
|
+
# =============================================================================
|
|
7
|
+
# Sourced by every on-edit hook. Answers one question before the hook runs
|
|
8
|
+
# anything: has the project declared what proves this property at this moment,
|
|
9
|
+
# and if so what should run instead of Lisa's written-in tool?
|
|
10
|
+
#
|
|
11
|
+
# WHY THIS EXISTS. Edit time is the highest-frequency enforcement surface Lisa
|
|
12
|
+
# owns and was the one surface with no configurability at all. The scripts
|
|
13
|
+
# resolved a RUNNER (`./node_modules/.bin/oxlint`, else `bunx`/`npx`) and
|
|
14
|
+
# hardcoded the TOOL, which is the inversion the gate registry exists to fix.
|
|
15
|
+
# Worse, they exit 2 — refusing the edit — when the written-in binary or config
|
|
16
|
+
# filename is absent, so a project that lints correctly with something else had
|
|
17
|
+
# every agent write refused until it installed Lisa's choice.
|
|
18
|
+
#
|
|
19
|
+
# THE CONTRACT, and the two halves that matter equally:
|
|
20
|
+
#
|
|
21
|
+
# declared -> the project's task runs and the hook's written-in tool is
|
|
22
|
+
# never consulted, so a missing binary cannot refuse the edit.
|
|
23
|
+
# undeclared -> NOTHING changes. The resolver is not even invoked when no
|
|
24
|
+
# registry is installed, and the hook falls through to exactly
|
|
25
|
+
# the command it ran before. That is the overwhelming majority
|
|
26
|
+
# of projects, and it is the case a façade silently breaks.
|
|
27
|
+
#
|
|
28
|
+
# ALL OR NOTHING FOR A MULTI-PROPERTY SCRIPT. `lisa_edit_gate_tasks` takes every
|
|
29
|
+
# property its caller proves and resolves only when EVERY one is declared. The
|
|
30
|
+
# Rails hook runs `rubocop -a` and then a second pass for unfixable errors — its
|
|
31
|
+
# own header calls it both formatter and linter — so standing down on `code-style`
|
|
32
|
+
# alone would silently stop proving `format-conformance`. Same shape as
|
|
33
|
+
# `lisa_gate_covers test-correctness coverage-adequacy` in the pre-push hook.
|
|
34
|
+
#
|
|
35
|
+
# HOW THE DECLARED TASK LEARNS WHICH FILE WAS EDITED. It does not take an
|
|
36
|
+
# argument: `<runner> <task>` with no arguments is the invocation every other
|
|
37
|
+
# Lisa façade uses, and adding a positional here would make the declaration
|
|
38
|
+
# mean something different at this moment than at every other one. The path is
|
|
39
|
+
# exported as LISA_EDITED_FILE instead, so a project that wants a per-file run
|
|
40
|
+
# can scope its own task and one that does not gets a whole-project run.
|
|
41
|
+
# =============================================================================
|
|
42
|
+
|
|
43
|
+
# Where the gate registry may be installed, in resolution order.
|
|
44
|
+
lisa_edit_gate_registry() {
|
|
45
|
+
for _lisa_candidate in \
|
|
46
|
+
"node_modules/@codyswann/lisa/all/copy-overwrite/scripts/lisa-gates.mjs" \
|
|
47
|
+
"scripts/lisa-gates.mjs" \
|
|
48
|
+
"all/copy-overwrite/scripts/lisa-gates.mjs"
|
|
49
|
+
do
|
|
50
|
+
if [ -f "$_lisa_candidate" ]; then
|
|
51
|
+
printf '%s' "$_lisa_candidate"
|
|
52
|
+
unset _lisa_candidate
|
|
53
|
+
return 0
|
|
54
|
+
fi
|
|
55
|
+
done
|
|
56
|
+
unset _lisa_candidate
|
|
57
|
+
return 1
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
# lisa_edit_gate_tasks <moment> <gate-id>...
|
|
61
|
+
#
|
|
62
|
+
# Prints one `<runner> <task>` line per DISTINCT declared task and returns 0,
|
|
63
|
+
# but only when every named gate resolves to a runnable task at <moment>.
|
|
64
|
+
# Returns 1 and prints nothing otherwise — including when no registry is
|
|
65
|
+
# installed, when node is unavailable, and when the resolver itself fails.
|
|
66
|
+
#
|
|
67
|
+
# Fail-safe in the direction that keeps proving things: any doubt returns 1 and
|
|
68
|
+
# the caller runs its built-in. A resolver that could not answer must never read
|
|
69
|
+
# as "the project has this covered".
|
|
70
|
+
lisa_edit_gate_tasks() {
|
|
71
|
+
_lisa_moment="$1"
|
|
72
|
+
shift
|
|
73
|
+
[ -n "$_lisa_moment" ] || { unset _lisa_moment; return 1; }
|
|
74
|
+
[ "$#" -gt 0 ] || { unset _lisa_moment; return 1; }
|
|
75
|
+
# NO SETTINGS FILE, NO SPAWN. Checked before anything else because this is
|
|
76
|
+
# the path essentially every project is on, and it runs on EVERY agent
|
|
77
|
+
# edit: asking the resolver would cost a node process per keystroke-sized
|
|
78
|
+
# write to answer "nothing is declared", which the absent file already
|
|
79
|
+
# answers. The equivalence control compares which COMMAND runs and would
|
|
80
|
+
# not have caught the cost.
|
|
81
|
+
[ -f .lisa.config.json ] || { unset _lisa_moment; return 1; }
|
|
82
|
+
command -v node >/dev/null 2>&1 || { unset _lisa_moment; return 1; }
|
|
83
|
+
|
|
84
|
+
_lisa_registry="$(lisa_edit_gate_registry)" || {
|
|
85
|
+
unset _lisa_moment _lisa_registry
|
|
86
|
+
return 1
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
# EXPORTED, not prefixed. An assignment prefix reaches only the FIRST
|
|
90
|
+
# command of a pipeline, and the filter that reads it is the second — so
|
|
91
|
+
# the prefixed form resolved an empty want-list and every script fell
|
|
92
|
+
# silently back to its built-in, which is a façade that looks wired and is
|
|
93
|
+
# not.
|
|
94
|
+
LISA_GATE_IDS="$*"
|
|
95
|
+
export LISA_GATE_IDS
|
|
96
|
+
_lisa_out="$(
|
|
97
|
+
node "$_lisa_registry" list \
|
|
98
|
+
--moment="$_lisa_moment" --json 2>/dev/null | node -e '
|
|
99
|
+
let raw = "";
|
|
100
|
+
process.stdin.on("data", chunk => { raw += chunk }).on("end", () => {
|
|
101
|
+
let resolved;
|
|
102
|
+
try { resolved = JSON.parse(raw || "[]"); } catch { process.exit(1); }
|
|
103
|
+
const wanted = (process.env.LISA_GATE_IDS || "").split(" ").filter(Boolean);
|
|
104
|
+
const commands = [];
|
|
105
|
+
for (const id of wanted) {
|
|
106
|
+
const hit = resolved.find(gate => gate.id === id);
|
|
107
|
+
// `run` mode with a task is the only shape that PROVES anything here. An
|
|
108
|
+
// awaited gate proves nothing at edit time and an intercepted one is
|
|
109
|
+
// Lisa running it elsewhere; both must leave the built-in in charge.
|
|
110
|
+
if (!hit || hit.mode !== "run" || !hit.command) process.exit(1);
|
|
111
|
+
if (!commands.includes(hit.command)) commands.push(hit.command);
|
|
112
|
+
}
|
|
113
|
+
if (commands.length === 0) process.exit(1);
|
|
114
|
+
process.stdout.write(commands.join("\n"));
|
|
115
|
+
});
|
|
116
|
+
'
|
|
117
|
+
)" || {
|
|
118
|
+
unset _lisa_moment _lisa_registry _lisa_out
|
|
119
|
+
return 1
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
[ -n "$_lisa_out" ] || {
|
|
123
|
+
unset _lisa_moment _lisa_registry _lisa_out
|
|
124
|
+
return 1
|
|
125
|
+
}
|
|
126
|
+
printf '%s' "$_lisa_out"
|
|
127
|
+
unset _lisa_moment _lisa_registry _lisa_out
|
|
128
|
+
return 0
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
# lisa_edit_gate_run <edited-file> <commands>
|
|
132
|
+
#
|
|
133
|
+
# Runs each resolved command with LISA_EDITED_FILE exported. Exits 2 on the
|
|
134
|
+
# first failure, which is the same refusal the built-in path uses, so a
|
|
135
|
+
# declared task that finds a real problem still stops the agent.
|
|
136
|
+
lisa_edit_gate_run() {
|
|
137
|
+
_lisa_file="$1"
|
|
138
|
+
shift
|
|
139
|
+
LISA_EDITED_FILE="$_lisa_file"
|
|
140
|
+
export LISA_EDITED_FILE
|
|
141
|
+
printf '%s\n' "$*" | while IFS= read -r _lisa_command; do
|
|
142
|
+
[ -n "$_lisa_command" ] || continue
|
|
143
|
+
echo "Running declared gate task: $_lisa_command"
|
|
144
|
+
# Deliberate word split: the resolver validated both halves as plain
|
|
145
|
+
# words, so no shell metacharacter reaches argv.
|
|
146
|
+
# shellcheck disable=SC2086
|
|
147
|
+
$_lisa_command || exit 2
|
|
148
|
+
done
|
|
149
|
+
_lisa_status=$?
|
|
150
|
+
unset _lisa_file _lisa_command
|
|
151
|
+
return $_lisa_status
|
|
152
|
+
}
|