@codyswann/lisa 2.328.5 → 2.330.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.
- package/all/copy-overwrite/scripts/lisa-enforcement-fallback.sh +4 -2
- package/all/copy-overwrite/scripts/lisa-hooks/block-instruction-file-edits.sh +142 -0
- package/all/merge/.claude/settings.json +9 -0
- package/dist/core/upstream-evidence-manifest.d.ts.map +1 -1
- package/dist/core/upstream-evidence-manifest.js +60 -6
- 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 +1 -41
- package/dist/opencode/hooks-installer.js.map +1 -1
- package/dist/opencode/plugin-catalog.d.ts +25 -0
- package/dist/opencode/plugin-catalog.d.ts.map +1 -0
- package/dist/opencode/plugin-catalog.js +47 -0
- package/dist/opencode/plugin-catalog.js.map +1 -0
- package/dist/opencode/plugin-templates/lisa-block-instruction-file-edits.ts +72 -0
- package/package.json +1 -1
- package/plugins/lisa/.claude-plugin/plugin.json +14 -1
- package/plugins/lisa/.codex-plugin/hooks.json +13 -0
- package/plugins/lisa/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa/.codex-plugin/skills/lisa-setup-workstation/SKILL.md +121 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-setup-workstation/agents/openai.yaml +4 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-setup-workstation/scripts/catalogue.mjs +262 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-setup-workstation/scripts/cli.mjs +315 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-setup-workstation/scripts/workstation.mjs +403 -0
- package/plugins/lisa/commands/setup/workstation.md +7 -0
- package/plugins/lisa/hooks/block-instruction-file-edits.agy.sh +58 -0
- package/plugins/lisa/hooks/block-instruction-file-edits.sh +142 -0
- package/plugins/lisa/skills/lisa-setup-workstation/SKILL.md +121 -0
- package/plugins/lisa/skills/lisa-setup-workstation/agents/openai.yaml +4 -0
- package/plugins/lisa/skills/lisa-setup-workstation/scripts/catalogue.mjs +262 -0
- package/plugins/lisa/skills/lisa-setup-workstation/scripts/cli.mjs +315 -0
- package/plugins/lisa/skills/lisa-setup-workstation/scripts/workstation.mjs +403 -0
- package/plugins/lisa-agy/commands/lisa/setup/workstation.md +7 -0
- package/plugins/lisa-agy/hooks/block-instruction-file-edits.agy.sh +58 -0
- package/plugins/lisa-agy/hooks/block-instruction-file-edits.sh +142 -0
- package/plugins/lisa-agy/hooks.json +13 -0
- package/plugins/lisa-agy/plugin.json +1 -1
- package/plugins/lisa-agy/skills/lisa-setup-workstation/SKILL.md +121 -0
- package/plugins/lisa-agy/skills/lisa-setup-workstation/scripts/catalogue.mjs +262 -0
- package/plugins/lisa-agy/skills/lisa-setup-workstation/scripts/cli.mjs +315 -0
- package/plugins/lisa-agy/skills/lisa-setup-workstation/scripts/workstation.mjs +403 -0
- 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 +14 -1
- package/plugins/lisa-copilot/commands/lisa/setup/workstation.md +7 -0
- package/plugins/lisa-copilot/hooks/block-instruction-file-edits.sh +142 -0
- package/plugins/lisa-copilot/skills/lisa-setup-workstation/SKILL.md +121 -0
- package/plugins/lisa-copilot/skills/lisa-setup-workstation/scripts/catalogue.mjs +262 -0
- package/plugins/lisa-copilot/skills/lisa-setup-workstation/scripts/cli.mjs +315 -0
- package/plugins/lisa-copilot/skills/lisa-setup-workstation/scripts/workstation.mjs +403 -0
- package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cursor/commands/lisa/setup/workstation.md +7 -0
- package/plugins/lisa-cursor/hooks/block-instruction-file-edits.sh +142 -0
- package/plugins/lisa-cursor/hooks/hooks.json +8 -0
- package/plugins/lisa-cursor/skills/lisa-setup-workstation/SKILL.md +121 -0
- package/plugins/lisa-cursor/skills/lisa-setup-workstation/scripts/catalogue.mjs +262 -0
- package/plugins/lisa-cursor/skills/lisa-setup-workstation/scripts/cli.mjs +315 -0
- package/plugins/lisa-cursor/skills/lisa-setup-workstation/scripts/workstation.mjs +403 -0
- 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/.claude-plugin/plugin.json +13 -0
- package/plugins/src/base/commands/setup/workstation.md +7 -0
- package/plugins/src/base/hooks/block-instruction-file-edits.agy.sh +58 -0
- package/plugins/src/base/hooks/block-instruction-file-edits.sh +142 -0
- package/plugins/src/base/skills/lisa-setup-workstation/SKILL.md +121 -0
- package/plugins/src/base/skills/lisa-setup-workstation/scripts/catalogue.mjs +262 -0
- package/plugins/src/base/skills/lisa-setup-workstation/scripts/cli.mjs +315 -0
- package/plugins/src/base/skills/lisa-setup-workstation/scripts/workstation.mjs +403 -0
- package/scripts/build-plugins.sh +2 -1
- package/scripts/generate-agy-plugin-artifacts.mjs +11 -0
- package/scripts/lib/per-agent-hook-filter.mjs +10 -0
- package/scripts/lisa-enforcement-fallback.sh +4 -2
|
@@ -106,6 +106,19 @@
|
|
|
106
106
|
{
|
|
107
107
|
"type": "command",
|
|
108
108
|
"command": "${CLAUDE_PLUGIN_ROOT}/hooks/block-shell-json-parsing.sh"
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
"type": "command",
|
|
112
|
+
"command": "${CLAUDE_PLUGIN_ROOT}/hooks/block-instruction-file-edits.sh"
|
|
113
|
+
}
|
|
114
|
+
]
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
"matcher": "Write|Edit|MultiEdit",
|
|
118
|
+
"hooks": [
|
|
119
|
+
{
|
|
120
|
+
"type": "command",
|
|
121
|
+
"command": "${CLAUDE_PLUGIN_ROOT}/hooks/block-instruction-file-edits.sh"
|
|
109
122
|
}
|
|
110
123
|
]
|
|
111
124
|
},
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "Prepare this machine — or a throwaway container — to run coding agents, before any repository exists. Detects which of Lisa's supported agents are already installed, asks which credential manager the machine uses (or none), and installs only what is missing, each by its vendor's own preferred method. Idempotent and headless; --print-dockerfile emits a spin-up/spin-down environment. Nothing is installed without --install."
|
|
3
|
+
allowed-tools: ["Skill"]
|
|
4
|
+
argument-hint: "[--install] [--agents=claude,codex] [--provider=bitwarden] [--json] [--print-dockerfile]"
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
Use the /lisa-setup-workstation skill to report and optionally install the coding agents, credential manager, and base tools this machine needs. $ARGUMENTS
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Antigravity (agy) PreToolUse adapter for Lisa's instruction-file guard.
|
|
3
|
+
#
|
|
4
|
+
# agy sends `{toolCall:{name:"run_command",args:{CommandLine:"..."}}}` and
|
|
5
|
+
# expects an allow/deny JSON object on stdout. The canonical hook consumes the
|
|
6
|
+
# Claude Bash-hook envelope and communicates denial with exit status 2. This
|
|
7
|
+
# adapter only translates protocols; all classification stays in the canonical
|
|
8
|
+
# block-instruction-file-edits.sh beside it (same delegation pattern as
|
|
9
|
+
# block-shell-json-parsing.agy.sh).
|
|
10
|
+
#
|
|
11
|
+
# Scope note: agy plugin hooks match `run_command`, so only the Bash arm of the
|
|
12
|
+
# guard reaches agy — an agy file-edit tool call writing AGENTS.md cannot be
|
|
13
|
+
# intercepted. That gap is accepted and recorded here rather than papered over.
|
|
14
|
+
#
|
|
15
|
+
# Fail-open on missing runtimes: a missed refusal here costs context bloat in a
|
|
16
|
+
# markdown file, not data loss.
|
|
17
|
+
set -uo pipefail
|
|
18
|
+
|
|
19
|
+
allow() {
|
|
20
|
+
printf '%s\n' '{"decision":"allow"}'
|
|
21
|
+
exit 0
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
deny() {
|
|
25
|
+
local reason="$1"
|
|
26
|
+
if command -v jq >/dev/null 2>&1; then
|
|
27
|
+
jq -cn --arg reason "$reason" '{decision:"deny",reason:$reason}'
|
|
28
|
+
else
|
|
29
|
+
printf '%s\n' '{"decision":"allow"}'
|
|
30
|
+
fi
|
|
31
|
+
exit 0
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
input="$(cat 2>/dev/null || true)"
|
|
35
|
+
[ -z "$input" ] && allow
|
|
36
|
+
command -v jq >/dev/null 2>&1 || allow
|
|
37
|
+
|
|
38
|
+
tool_name="$(printf '%s' "$input" | jq -r '.toolCall.name // empty' 2>/dev/null || true)"
|
|
39
|
+
[ "$tool_name" != "run_command" ] && allow
|
|
40
|
+
command_str="$(printf '%s' "$input" | jq -r '.toolCall.args.CommandLine // empty' 2>/dev/null || true)"
|
|
41
|
+
[ -z "$command_str" ] && allow
|
|
42
|
+
|
|
43
|
+
hook_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
44
|
+
canonical_hook="$hook_dir/block-instruction-file-edits.sh"
|
|
45
|
+
[ -r "$canonical_hook" ] || allow
|
|
46
|
+
|
|
47
|
+
canonical_input="$(jq -cn --arg command "$command_str" '{tool_name:"Bash",tool_input:{command:$command}}')"
|
|
48
|
+
canonical_output=""
|
|
49
|
+
canonical_status=0
|
|
50
|
+
if canonical_output="$(printf '%s' "$canonical_input" | /bin/bash "$canonical_hook" 2>&1)"; then
|
|
51
|
+
canonical_status=0
|
|
52
|
+
else
|
|
53
|
+
canonical_status=$?
|
|
54
|
+
fi
|
|
55
|
+
|
|
56
|
+
[ "$canonical_status" -eq 0 ] && allow
|
|
57
|
+
[ -n "$canonical_output" ] || canonical_output="Blocked: AGENTS.md / CLAUDE.md are human-authored session-instruction files."
|
|
58
|
+
deny "$canonical_output"
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# PreToolUse hook: refuse agent writes to the session-instruction files.
|
|
3
|
+
#
|
|
4
|
+
# `AGENTS.md`, `CLAUDE.md`, and `.github/copilot-instructions.md` are the files
|
|
5
|
+
# every agent loads at session start. They are human-authored and curated on
|
|
6
|
+
# purpose — Lisa's own contract says so in two places:
|
|
7
|
+
# - plugins/src/base/rules/reference/project-learnings.md
|
|
8
|
+
# - plugins/src/base/skills/lisa-debrief-apply/SKILL.md ("CLAUDE.md is
|
|
9
|
+
# human-authored ... apply never writes to any of the three")
|
|
10
|
+
#
|
|
11
|
+
# Nothing enforced it, so agents appended their own findings anyway: one fleet
|
|
12
|
+
# repo reached 949 lines of ticket-keyed trap dumps in `AGENTS.md` while its
|
|
13
|
+
# learnings ledger held 22. Every one of those lines is charged to the context
|
|
14
|
+
# of every later session in that project, forever. Prose did not hold; this is
|
|
15
|
+
# the executable control that does.
|
|
16
|
+
#
|
|
17
|
+
# Blocked signatures:
|
|
18
|
+
# 1. Write / Edit / MultiEdit / NotebookEdit whose target basename is one of
|
|
19
|
+
# the instruction files;
|
|
20
|
+
# 2. Bash output redirection (`>`, `>>`), `tee`, or `sed -i` aimed at one.
|
|
21
|
+
# Reads (`cat AGENTS.md`, `rg pattern AGENTS.md`) never fire.
|
|
22
|
+
#
|
|
23
|
+
# Exemptions (allowed):
|
|
24
|
+
# - `LISA_ALLOW_INSTRUCTION_FILE_WRITE` set — the operator's explicit override,
|
|
25
|
+
# named in the refusal so a human who really wants the edit can take it;
|
|
26
|
+
# - payloads carrying a `<!-- LISA_` marker: Lisa's own marker-bounded regions
|
|
27
|
+
# (the agy project-learnings bridge, cross-pollinate's rule section) replace
|
|
28
|
+
# in place on re-run and cannot grow the file;
|
|
29
|
+
# - paths under `node_modules/` or `dist/` — vendored copies, not the host's.
|
|
30
|
+
set -euo pipefail
|
|
31
|
+
|
|
32
|
+
input="$(cat)"
|
|
33
|
+
|
|
34
|
+
command -v jq >/dev/null 2>&1 || exit 0
|
|
35
|
+
|
|
36
|
+
# The operator's override. Checked before anything else so it is always the
|
|
37
|
+
# cheapest way out of a refusal a human deliberately wants to overrule.
|
|
38
|
+
if [ -n "${LISA_ALLOW_INSTRUCTION_FILE_WRITE:-}" ]; then
|
|
39
|
+
exit 0
|
|
40
|
+
fi
|
|
41
|
+
|
|
42
|
+
tool_name="$(printf '%s' "$input" | jq -r '.tool_name // empty')"
|
|
43
|
+
[ -n "$tool_name" ] || exit 0
|
|
44
|
+
|
|
45
|
+
# Lisa's own bounded bridges write marked regions. A payload carrying a marker
|
|
46
|
+
# is replacing a delimited block, not appending prose, so it cannot be the
|
|
47
|
+
# unbounded growth this guard exists to stop.
|
|
48
|
+
if printf '%s' "$input" | grep -q '<!-- LISA_'; then
|
|
49
|
+
exit 0
|
|
50
|
+
fi
|
|
51
|
+
|
|
52
|
+
# Basename match, case-insensitively: `agents.md` and `AGENTS.md` are the same
|
|
53
|
+
# file on the macOS checkouts this fleet runs on.
|
|
54
|
+
is_instruction_file() {
|
|
55
|
+
local candidate="$1"
|
|
56
|
+
case "$candidate" in
|
|
57
|
+
*/node_modules/* | node_modules/* | */dist/* | dist/*) return 1 ;;
|
|
58
|
+
esac
|
|
59
|
+
local base="${candidate##*/}"
|
|
60
|
+
case "$(printf '%s' "$base" | tr '[:upper:]' '[:lower:]')" in
|
|
61
|
+
agents.md | claude.md | copilot-instructions.md) return 0 ;;
|
|
62
|
+
esac
|
|
63
|
+
return 1
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
refuse() {
|
|
67
|
+
local target="$1"
|
|
68
|
+
cat >&2 <<EOF
|
|
69
|
+
BLOCKED: refusing to write \`$target\`.
|
|
70
|
+
|
|
71
|
+
WHY: this is a session-instruction file, not a place to record what you just
|
|
72
|
+
learned. Every line in it is loaded into the context of every agent, in every
|
|
73
|
+
future session, in this project, forever. It is human-curated on purpose. An
|
|
74
|
+
agent appending its own findings is how these files grow into hundreds of lines
|
|
75
|
+
of stale, ticket-specific trivia that every later session pays for and half of
|
|
76
|
+
which was only ever true once.
|
|
77
|
+
|
|
78
|
+
WHERE IT GOES INSTEAD — take the first one that fits:
|
|
79
|
+
|
|
80
|
+
1. Every agent genuinely needs this in every session, and only in this project.
|
|
81
|
+
That is a project rule — but do not write it yourself. Capture it with
|
|
82
|
+
\`/lisa:persist-learning\` so it lands in the learnings ledger with provenance
|
|
83
|
+
and a confidence score. The gardener (\`/lisa:learnings:audit\`) then proposes
|
|
84
|
+
promotion into \`.claude/rules/PROJECT_RULES.md\` as a human-gated ticket.
|
|
85
|
+
|
|
86
|
+
2. It changes how an existing skill should behave. Edit that skill's SKILL.md.
|
|
87
|
+
Knowledge belongs next to the procedure it modifies, not in a global preamble.
|
|
88
|
+
|
|
89
|
+
3. It is true beyond this project. Propose it upstream to Lisa via
|
|
90
|
+
\`/lisa:cross-pollinate\`, or open an issue on CodySwannGT/lisa. A local copy of
|
|
91
|
+
a general rule silently drifts the moment upstream changes.
|
|
92
|
+
|
|
93
|
+
4. None of the above — it is background someone may want to look up, not a
|
|
94
|
+
standing instruction. Write it into the project documentation (\`wiki/\` or
|
|
95
|
+
\`docs/\`).
|
|
96
|
+
|
|
97
|
+
If a human explicitly asked for this edit, they can re-run with
|
|
98
|
+
\`LISA_ALLOW_INSTRUCTION_FILE_WRITE=1\` set, which bypasses this guard.
|
|
99
|
+
EOF
|
|
100
|
+
exit 2
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
case "$tool_name" in
|
|
104
|
+
Write | Edit | MultiEdit | NotebookEdit | Update)
|
|
105
|
+
# `file_path` covers Write/Edit; `edits[].file_path` covers the MultiEdit
|
|
106
|
+
# shapes that carry a path per edit; `notebook_path` covers NotebookEdit.
|
|
107
|
+
paths="$(printf '%s' "$input" | jq -r '
|
|
108
|
+
[ .tool_input.file_path?,
|
|
109
|
+
.tool_input.path?,
|
|
110
|
+
.tool_input.notebook_path?,
|
|
111
|
+
(.tool_input.edits? // [] | .[].file_path?)
|
|
112
|
+
] | map(select(. != null and . != "")) | .[]' 2>/dev/null || true)"
|
|
113
|
+
while IFS= read -r candidate; do
|
|
114
|
+
[ -n "$candidate" ] || continue
|
|
115
|
+
if is_instruction_file "$candidate"; then
|
|
116
|
+
refuse "$candidate"
|
|
117
|
+
fi
|
|
118
|
+
done <<EOF
|
|
119
|
+
$paths
|
|
120
|
+
EOF
|
|
121
|
+
;;
|
|
122
|
+
Bash)
|
|
123
|
+
command_str="$(printf '%s' "$input" | jq -r '.tool_input.command // empty')"
|
|
124
|
+
[ -n "$command_str" ] || exit 0
|
|
125
|
+
# Fast path: no instruction filename mentioned means nothing to classify.
|
|
126
|
+
if ! printf '%s' "$command_str" |
|
|
127
|
+
grep -Eqi '(agents|claude|copilot-instructions)\.md'; then
|
|
128
|
+
exit 0
|
|
129
|
+
fi
|
|
130
|
+
# Write signatures only. A bare mention (`cat AGENTS.md`, `rg x AGENTS.md`)
|
|
131
|
+
# is a read and must stay allowed — the filename has to appear as the target
|
|
132
|
+
# of a redirection, a tee, or an in-place sed.
|
|
133
|
+
write_target='[^ |;&]*(agents|claude|copilot-instructions)\.md'
|
|
134
|
+
if printf '%s' "$command_str" |
|
|
135
|
+
grep -Eqi ">>?[[:space:]]*['\"]?$write_target|tee([[:space:]]+-[a-z]+)*[[:space:]]+['\"]?$write_target|sed[[:space:]]+[^|;&]*-i[^|;&]*$write_target"; then
|
|
136
|
+
refuse "$(printf '%s' "$command_str" |
|
|
137
|
+
grep -Eoi "$write_target" | head -1)"
|
|
138
|
+
fi
|
|
139
|
+
;;
|
|
140
|
+
esac
|
|
141
|
+
|
|
142
|
+
exit 0
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: lisa-setup-workstation
|
|
3
|
+
description: "Prepare a machine — a fresh laptop or a throwaway container — to run coding agents, before any repository exists. Detects which of Lisa's supported agents (Claude Code, Codex, Cursor, OpenCode, Antigravity, Copilot) are already installed, asks which credential manager the machine uses (Bitwarden, 1Password, Doppler, Vault, AWS, or none), and installs only what is missing, each by its vendor's own preferred method. Idempotent, headless by default, and emits a Dockerfile for a spin-up/spin-down environment. Run it on a new machine, in a container, or before cloning anything."
|
|
4
|
+
allowed-tools: ["Bash", "Read", "Skill"]
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Setup Workstation: $ARGUMENTS
|
|
8
|
+
|
|
9
|
+
Get a machine ready to run agents. No checkout required.
|
|
10
|
+
|
|
11
|
+
## Why this exists separately
|
|
12
|
+
|
|
13
|
+
`lisa-setup-local-env` reads `remoteEnv.tools` — a **project's** declared toolchain — so it
|
|
14
|
+
needs a checkout to exist before it can answer anything. That leaves a gap at the start:
|
|
15
|
+
a bare laptop or an empty container has no repository yet, and therefore nothing to ask.
|
|
16
|
+
|
|
17
|
+
This skill fills that gap and stops there. It describes a **machine**, never a project:
|
|
18
|
+
|
|
19
|
+
```text
|
|
20
|
+
workstation -> git clone -> lisa-detect-tooling -> lisa-setup-local-env
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Project-scoped tools stay in `remoteEnv.tools` where they belong. Nothing here reads a
|
|
24
|
+
repository, and nothing here duplicates that manifest.
|
|
25
|
+
|
|
26
|
+
## Usage
|
|
27
|
+
|
|
28
|
+
```sh
|
|
29
|
+
node scripts/cli.mjs # report what is present and missing
|
|
30
|
+
node scripts/cli.mjs --install # install what is missing
|
|
31
|
+
node scripts/cli.mjs --install --agents=claude,codex # only these agents
|
|
32
|
+
node scripts/cli.mjs --provider=bitwarden # credential manager (asked if a TTY)
|
|
33
|
+
node scripts/cli.mjs --json # machine-readable plan
|
|
34
|
+
node scripts/cli.mjs --print-dockerfile # an image that runs this same script
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Nothing is installed or written without `--install`. Exit status is non-zero when a required
|
|
38
|
+
tool is absent or an install fails, so this is usable as a gate.
|
|
39
|
+
|
|
40
|
+
## Ephemerality is not a mode
|
|
41
|
+
|
|
42
|
+
There is no Docker branch anywhere in this skill. A container gets a throwaway `$HOME` and
|
|
43
|
+
dies with it; a laptop keeps its own. Same code path either way — so the rarely-run one
|
|
44
|
+
cannot rot, which is the failure mode a second install path eventually always has.
|
|
45
|
+
|
|
46
|
+
`--print-dockerfile` emits an image rather than shipping a committed one, so the image cannot
|
|
47
|
+
drift from the catalogue it provisions. It deliberately bakes in **no repository**: clone
|
|
48
|
+
inside the container, then run `lisa-detect-tooling` and `lisa-setup-local-env` for that
|
|
49
|
+
project's own tools.
|
|
50
|
+
|
|
51
|
+
```sh
|
|
52
|
+
node scripts/cli.mjs --print-dockerfile --agents=claude,codex --provider=bitwarden > Dockerfile
|
|
53
|
+
docker build -t lisa-workstation . && docker run --rm -it lisa-workstation
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Already-installed wins, however it got there
|
|
57
|
+
|
|
58
|
+
A tool present by **any** means is reported and left alone. That dissolves the "Homebrew or
|
|
59
|
+
`~/.local/bin`" question entirely: we never contend for ownership of a tool someone else's
|
|
60
|
+
package manager installed, and never shadow it with an earlier PATH entry that would silently
|
|
61
|
+
win over their upgrades. The report names where each tool came from so the operator can see
|
|
62
|
+
why it was skipped.
|
|
63
|
+
|
|
64
|
+
Running twice installs nothing the second time, and says so rather than going quiet — silence
|
|
65
|
+
reads as "did nothing because it broke".
|
|
66
|
+
|
|
67
|
+
## Install method is a per-tool property, not a global policy
|
|
68
|
+
|
|
69
|
+
The obvious design — put every binary in `~/.local/bin` so one code path serves a laptop and a
|
|
70
|
+
container alike — is wrong, and the agents show why:
|
|
71
|
+
|
|
72
|
+
```text
|
|
73
|
+
claude -> ~/.local/share/claude/versions/2.1.221
|
|
74
|
+
cursor-agent -> ~/.local/share/cursor-agent/versions/2026.07.16-899851b
|
|
75
|
+
codex -> ~/.codex/packages/standalone/current/bin/codex
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Each vendor manages its own version directory, and `~/.local/bin` holds only a **symlink**.
|
|
79
|
+
Writing a raw binary there breaks the vendor's self-updater, which expects to swap a link
|
|
80
|
+
target and would instead find a real file in its way.
|
|
81
|
+
|
|
82
|
+
So each catalogue entry declares its own method, and the honest cost is recorded with it:
|
|
83
|
+
|
|
84
|
+
| kind | pinned | checksummed | notes |
|
|
85
|
+
| --- | --- | --- | --- |
|
|
86
|
+
| `release-zip` / `release-tar` | yes | yes | reuses the remote-env installer |
|
|
87
|
+
| `npm-global` | no | no | vendor registry |
|
|
88
|
+
| `vendor-script` | no | no | pipes a fetched script to a shell |
|
|
89
|
+
| `manual` | n/a | n/a | no headless installer published |
|
|
90
|
+
| `required` | n/a | n/a | expected from the OS or base image |
|
|
91
|
+
|
|
92
|
+
`vendor-script` is a real weakening of the guarantee `assertPinned` exists to provide, so the
|
|
93
|
+
report **names every unchecksummed install** rather than blending them in. Installing them
|
|
94
|
+
trusts the vendor's script at fetch time. The alternative — repackaging vendor binaries
|
|
95
|
+
ourselves — would break the self-updaters that own those directories.
|
|
96
|
+
|
|
97
|
+
## The credential manager is asked, never assumed
|
|
98
|
+
|
|
99
|
+
`lisa-secrets-access` already treats the provider as an axis: Bitwarden, 1Password, Doppler,
|
|
100
|
+
Vault, AWS, or plain environment variables. A bootstrap that installed `bws` unconditionally
|
|
101
|
+
would contradict that — pushing every workstation onto one vendor and quietly making the
|
|
102
|
+
others second-class.
|
|
103
|
+
|
|
104
|
+
So the provider is a question, `none` is a first-class answer, and only the selected one is
|
|
105
|
+
installed. Installing all of them would leave four unused credential CLIs on the machine,
|
|
106
|
+
each an extra thing to keep patched for no benefit.
|
|
107
|
+
|
|
108
|
+
Headless is the primary mode, so with no TTY and no `--provider`, the answer is `none` — not
|
|
109
|
+
a prompt that would hang a container build forever. A misspelled provider stops the run
|
|
110
|
+
instead of falling back, because quietly provisioning the wrong credential manager is worse
|
|
111
|
+
than stopping.
|
|
112
|
+
|
|
113
|
+
## What it will not do
|
|
114
|
+
|
|
115
|
+
- **Read a repository.** That is the next step, not this one.
|
|
116
|
+
- **Install a credential manager you did not pick.**
|
|
117
|
+
- **Reinstall over a tool that is already there**, whatever installed it.
|
|
118
|
+
- **Install anything without `--install`.**
|
|
119
|
+
- **Install `agy`.** Antigravity publishes no headless installer; it is reported as manual
|
|
120
|
+
with a pointer, because "not installed" and "cannot be installed by this tool" are
|
|
121
|
+
different answers to the operator.
|