ソース情報
- リポジトリ
- CodySwannGT/lisa
- ソースの最終更新活動
- 2026年8月28日 12:31
- 検出された SKILL.md の言語
- 英語
- スター
- 3
- フォーク
- 3
インストール方法
デフォルトでは、最初にソースを確認する Prompt が選択されています。直接コマンドに切り替えるか、ローカルコピーをダウンロードすることもできます。
ソースファイルを確認
インストールを決める前に、SKILL.md と SkillsMP に表示されている付属ファイルをお読みください。
メニュー
デフォルトでは、最初にソースを確認する Prompt が選択されています。直接コマンドに切り替えるか、ローカルコピーをダウンロードすることもできます。
インストールを決める前に、SKILL.md と SkillsMP に表示されている付属ファイルをお読みください。
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
直接コマンドでは確認用 Prompt が省略されます。実行前にソースを確認してください。
npx skills add https://github.com/CodySwannGT/lisa --skill lisa-setup-remote-envコマンドは1行のまま表示されます。コピー前に横へスクロールして全体を確認してください。
ローカルで確認しますか?SkillsMP が現在取得できるファイルをダウンロードできます。
This skill should be used for any non-trivial request — features, bugs, stories, epics, spikes, or multi-step tasks. It accepts a ticket URL (Jira, Linear, GitHub), a file path containing a spec, or a plain-text prompt. It assembles an agent team, breaks the work into structured tasks, and manages the full lifecycle from research through implementation, code review, deploy, and empirical verification.
any non-trivial request —…
This skill should be used for any non-trivial request — features, bugs, stories, epics, spikes, or multi-step tasks. It accepts a ticket URL (Jira, Linear, GitHub), a file path containing a spec, or a plain-text prompt. It assembles an agent team, breaks the work into structured tasks, and manages the full lifecycle from research through implementation, code review, deploy, and empirical verification.
SKILL.md を表示中
| name | lisa-setup-remote-env |
| description | Provision and verify a remote… |
| allowed-tools | ["Bash","Read","Write","Edit","Skill"] |
Prepare a remote surface so a host project can execute there. Today that means Codex Cloud; the shape is deliberately surface-agnostic so Claude Code web, Cursor remote, and others slot in without redesign.
The remote environment's own configuration fields stay one line into the repository. Nothing else is pasted into a vendor UI.
Ask for that line; do not copy it from here.
npx -y @codyswann/lisa@latest remote-env --emit=claude-web
The field pins the exact Lisa it runs, and the emitter reads that version from the package it is running out of — so the line it prints is always pinned to the version that printed it.
A copy written into this file cannot have that property. It is a snapshot taken
when the package was built, and the release bumps the version after the
build, so a published tarball's copy names the previous release. That is not a
cosmetic lag: the pinned version predates whatever this file documents, so a
pasted copy can call flags that version does not implement. Measured on
2.338.1, whose embedded copy named 2.338.0 — a Lisa with no --print-tools,
no --tools, and no tools-from-notes.mjs. Unknown flags are ignored rather
than rejected, so it did not fail; it installed the full catalogue and re-ran the
whole setup, three times over, inside a five-minute budget.
That line is identical for every project AND every surface. Nothing in it names the repository or the package manager, so it can be pasted unchanged anywhere.
Locating the checkout is the only hard part, because the surfaces disagree about where the field runs:
| Surface | cwd when the field runs | Checkout |
|---|---|---|
| Codex Cloud | is the checkout | /workspace/<repo> |
| Claude Code web | $HOME, but not reliably | $HOME/<repo>, or another root |
So the checkout itself and one level down are tried relative to cwd first, then the same
pair under $HOME and /workspace — the roots the two surfaces actually use — because cwd is
not reliably either of them: a live Claude environment reported $HOME as /root with the
checkout at /home/user/lisa, which a cwd-only search cannot reach.
The cwd-relative candidates coming first is the invariant, and a test pins it. An earlier
version of this field used bash "$HOME"/*/scripts/... alone, which works on Claude Code web
and fails on Codex Cloud, where the checkout is not under $HOME at all: the glob matches
nothing and bash reports No such file or directory for a path still containing a literal *.
Every match is prepared, not the first. A Claude Code web environment can hold more than
one checkout, and stopping at the first glob hit would prepare whichever repository sorts
first and silently ignore the rest — arbitrary rather than merely limited. Each script
anchors itself on its own repository root, and each project's secrets land under its own
secrets.namespace, so preparing several is well defined rather than a collision.
The exit status is the first failure, and every checkout is still attempted: one broken repository must not hide the state of the others, and it must not report success either.
The explicit exit 1 on n=0 means a missing entrypoint says so rather than the field
silently succeeding — a for loop over a glob that matches nothing otherwise exits 0,
which is the quiet failure this whole section exists to avoid.
They are the same command. A container may be built fresh or resumed from cache; every step is idempotent and version-aware, so running it twice is correct, and running it on resume is what picks up a rotated value, an edited note, or a changed version pin.
The complete logic is repository-owned so it is reviewed, versioned, tested, and reusable. A large inline installer in a settings field is none of those things — and neither is a repository name and a package manager, which is what this field used to carry.
A clone does not contain the skills on the harnesses that matter here. OpenCode and Antigravity have them written into the checkout by lisa apply. Claude and Codex receive them as an installed plugin, which lives in the user's home directory — so a container that has just cloned the repository has never seen it.
node_modules/@codyswann/lisa is therefore the only copy present on a fresh container, and it is a good one: it is the version that project pins, which is the version its setup should run. The entrypoint searches the agent directories first and falls back to it.
So the install has to happen before the runner is resolved — and the entrypoint does it, rather than the settings field. Which package manager is read from the lockfile the project actually commits (bun.lock, pnpm-lock.yaml, yarn.lock, package-lock.json), never guessed: a guessed one fails on the container's first command with an error that blames the project rather than the guess. A successful install records that lockfile's digest inside node_modules; a cached container skips the next install only while the digest still matches, and reconciles once after a lockfile change. LISA_SKIP_INSTALL=1 opts out without recording a false successful reconciliation.
A project with no lockfile is not fatal on its own — a checkout may carry the skill directly — so the script says so and lets the resolver decide.
lisa-secrets-access. This skill never reimplements any part of that contract; the single chokepoint is what makes the one-store rule enforceable.setup.local.sh the project owns.Order matters. The toolchain comes first because the secrets step needs the provider CLI it installs. The hook comes last because it is the only part that may assume everything else is ready.
Not every surface runs all three here, and the reason is worth understanding before changing it.
A surface that re-runs this script when a container resumes — Codex Cloud — should materialize during setup. Re-running is exactly what picks up a rotated value, an edited note, or a changed version pin.
A surface that skips this script whenever a filesystem cache exists — Claude Code web — must not. Materializing here would write the values once and then never refresh them, so a credential rotated on Tuesday would still be serving Monday's value until the cache expired days later. Those surfaces materialize from a session-start hook instead, which runs every session including a resumed one:
bash "$CLAUDE_PROJECT_DIR/scripts/lisa-remote-env/session-start.sh" # project-scoped absolute hook
The installer also registers a user-scoped hook using the checkout's resolved absolute path. If invoking this asset by hand, either use that absolute path or change to the repository root first; a bare checkout-relative command is not a portable session-start configuration.
The selection comes from the surface's materializeAt capability in lisa-secrets-access, not from its name, so adding a surface does not mean editing a branch. The hook is committed to the repository, so it also fires on a developer's machine — it exits 0 immediately there rather than failing, because a correct local session must not look broken.
Run /lisa:detect-tooling first, every time. The manifest is the only thing that puts a binary on PATH, and nothing populates it — so a project ships npm scripts invoking maestro, wires an MCP server whose CLI it also shells out to, and configures Playwright thresholds, while remoteEnv.tools stays empty and each of those fails at the moment of use instead of at setup.
This repository has paid for that twice: gh was declared nowhere and a cloud session could not commit anything; tar was needed by an install method and asserted by nothing.
The detector proposes and a human decides. It writes nothing, so provisioning still only ever happens from a reviewed, pinned, checksummed entry.
remoteEnv.tools is not a remote-only manifest, and /lisa:setup:local-env is how a developer
applies it to their own machine — same pins, same checksums, same installers, differing only in
that nothing installs without --install-tools and that a newer tool already on PATH is left
alone rather than downgraded.
A downloaded tool declares a platforms map keyed <platform>-<arch>, each block carrying its
own install method, url, and sha256, because the artifact and the archive kind both vary
by platform. Before that existed, the only way to keep a Linux binary off a laptop was
surfaces: ["remote"], which achieved it by making the tool uninstallable there — so bws and
gh were required on developer machines and provisionable only in containers.
{
"remoteEnv": {
"tools": {
"require": [
{ "name": "python3" },
{ "name": "node", "minVersion": "20" },
{ "name": "curl" },
{ "name": "unzip" }
],
"install": [
{
"name": "gh",
"version": "2.83.0",
"platforms": {
"linux-x64": {
"install"
require — assert present, optionally at a minimum version. For everything the base image already provides.
install — provision it, pinned and checksummed. Only for what genuinely is not there.
Expected shape for most projects: a short require list, an empty or near-empty install list, and the provider CLI as the only thing always provisioned.
install methodsinstall | Required fields | Use it when |
|---|---|---|
release-zip | url and sha256 | The vendor publishes a Linux zip of the release. |
release-tar | url and sha256 — plus binary in practice | The vendor publishes no zip for Linux, only a .tar.gz. |
release-tree | url, sha256 and binary | The archive is a directory, not a single binary — an entry point that resolves its own siblings at run time. |
npm-global | package | The tool ships on the npm registry. |
Anything else is rejected by name at plan time, so a typo fails loudly rather than silently installing nothing.
release-tree exists because the single-file kinds fail silently on a tree. They extract the archive and copy one file onto PATH, which is right for a static binary and wrong for anything that locates its own resources. Maestro is the case that forced it — one 314 MB zip containing:
maestro/bin/maestro <- launcher
maestro/lib/*.jar <- 100+ MB of classpath
and a launcher that computes CLASSPATH=$APP_HOME/lib/* where APP_HOME is the parent of wherever the script itself sits. Declared as release-zip with binary: maestro/bin/maestro, the launcher lands in ~/.local/bin, APP_HOME resolves to ~/.local, and the classpath points at an empty directory. The install reports success and the tool dies at first use with Could not find or load main class — exactly the failure this manifest exists to turn into a loud setup error.
So release-tree extracts the whole archive to ~/.local/share/<name>/<version> and puts a wrapper on PATH that execs the entry point in place. Not a copy, and deliberately not a symlink either: a launcher that does not resolve symlinks before computing its own location would read the link's directory as its home and look for its resources beside the link — the same broken install, narrowed to a subset of tools. binary has no default here, because the archive root is a directory and guessing would install a directory as a binary.
Both archive kinds carry the same obligation and differ only in how they are unpacked: url and sha256 are both mandatory, the checksum is verified before the archive is unpacked, and a version bump must move the checksum in the same reviewed commit. Neither is a weaker path than the other — release-tar exists because some tools worth pinning simply do not publish a zip. gh is the case that forced it: it ships .deb, .rpm and .tar.gz and nothing else, so a zip-only installer could not pin the CLI that Lisa's own commit guardrails shell out to.
binary means something different for a tarball. It defaults to the tool's name and is resolved relative to the unpacked directory. Release tarballs almost always nest their contents under a versioned top-level directory, so for release-tar it must be the path within the archive — gh_2.83.0_linux_amd64/bin/gh, not gh. A bare name there resolves to nothing after a download that otherwise succeeded, and the install fails at the copy step rather than at the fetch, which reads as a broken release when it is a manifest mistake. Bump the version and this path moves with it, alongside the url and the sha256.
Because release-tar unpacks with tar rather than unzip, a project using it should assert tar in its require list the same way a zip-installing project asserts unzip — the base image providing it is an assumption, not a contract.
jq should break loudly at setup when that happens, not mysteriously mid-task weeks later. require is what converts an implicit assumption into an explicit check.Deliberately rejected — notes may not cause an install:
jq, ripgrep, browsers, a linter — so notes could only ever cover a subset, and a second mechanism would be needed anyway.Notes are an assertion target instead. At verify time, a tool that declares a credential which is not materialized is an error; a credential materialized with no declared consumer is a warning. The arrow points the safe way: the repository declares intent, and the provider is checked against it.
lisa-detect-tooling does read notes, and this section is why it may. Every objection above is an objection to a note causing something: authorising an install, being the sole source, deciding a version. None of that changes.
What the detector produces is a proposal with <pin>, <release url> and <sha256> left blank, printed for a human. It cannot install, cannot write config, and its output is inert until someone commits a pinned, checksummed entry that assertPinned then enforces. Provider write access therefore buys an attacker one line of text in a proposal a human reads — not arbitrary install access, which is the specific escalation this section exists to block.
The other objections stay true and stay unfixed: most tooling has no credential and most credentials imply no tool, so notes are one signal among four and the weakest of them. They are read after npm scripts and MCP servers, and a note alone should be the least persuasive reason to add anything.
lisa environment <surface> --tenant=<name> configures one surface for one
tenant. The only difference between them is whether Lisa can execute there:
| Surface | What happens | Materializes |
|---|---|---|
local | runs here: stores the bootstrap, materializes, installs AWS profiles | on request |
container | emits an image definition and a docker run line | at container start |
claude-web | emits text for the environment dialog | at setup and session start |
codex-cloud | emits text for the environment settings | at setup |
None of it needs a checkout, which is the point: the surfaces most in need of configuration are the ones with no repository attached.
--tenant is required on local because that path writes. Every namespace
is a directory under $XDG_CONFIG_HOME, so resolving the wrong one puts one
tenant's credentials where another tenant's sessions read — and on a machine
serving several, the two would share a store. The named tenant also outranks any
.lisa.config.json in the working directory: someone who typed --tenant=acme
means acme, whichever repository they happen to be standing in.
Re-running environment local is how a token is rotated. It is not an
installer, so it does not reinstall agents to replace one credential; it reports
that a bootstrap is already stored and leaves it alone unless --rotate says
otherwise.
workstation remains the separate question — what binaries does this machine
have — with no tenant and no credentials.
remote-env --emit=<surface> still works, and is the older spelling of the same
thing. It named the machinery rather than the task: from a laptop it reads as
"prepare the remote environment I am currently in", which is the opposite of
configuring a cloud environment.
Preference order, falling back:
For Codex Cloud today, start at tier 2 or 3. Verified on 2026-08-01: codex cloud exposes only exec | status | list | apply | diff. There is no environment-provisioning subcommand, so there is nothing to call. Do not claim tier 1 for this surface until the CLI grows one.
When emitting, produce exactly:
Environment name: <project> remote executor
Repository: <org>/<repo> (must be the default checkout)
Setup script: <printed by `remote-env --emit`; never copied
from prose, so its pin is always the version
that printed it>
Maintenance: <same line, printed by the same command>
(identical for every project — the script finds the
checkout and installs from the committed lockfile)
Environment vars: LISA_SECRETS_SURFACE=codex-cloud
BWS_ACCESS_TOKEN=<from the provider; an environment
variable, not a task secret — setup and cache-resume
maintenance both run before task secrets exist>
For Claude Code web there is no tier 1 or tier 2 to fall back from. A cloud environment is account-scoped configuration — network access level, environment variables, setup script — edited only in the environment selector at claude.ai/code, which has no settings page, no direct URL, and no API. /remote-env selects an environment; it cannot create or edit one.
Note what this surface's environment is not: it carries no repository. The repository arrives per session, so an environment is reusable across every project, and there is nothing to bind. Its durable handle is the routine that dispatch fires, which is why remoteEnv.surfaces["claude-web"] records routineId and fireUrl rather than a repository.
Generate the exact text to paste:
node scripts/setup-remote-env.mjs --emit=claude-web
It reads the bootstrap name from secrets.bootstrap.key and emits the environment fields, the .claude/settings.json hook block, and the base-image surprises worth knowing before they cost an afternoon:
gh is not pre-installed. If the project's flows shell out to it, add it to remoteEnv.tools.install, pinned and checksummed like anything else.proxy-injected. Tools that authenticate through the GitHub proxy work; a script that reads the variable directly gets the placeholder. The read-back asserts this rather than leaving it to be discovered against a live service.$HOME with the checkout one level down. That is why the field tries the checkout itself and then one level down relative to cwd, and only then the same pair under $HOME and /workspace — the script anchors itself on whichever matched. Nothing in the emitted line names this project, so it is the same line everywhere.An environment is not bound to a repository — the repository arrives per session, and one environment is technically reusable across all of them. Give each project its own anyway, because an environment's contents are project-shaped: its setup script is a repository-relative path and its bootstrap is scoped to that project's secrets. Pointing one project's session at another's environment runs a setup script that may not exist there, and a setup script that exits non-zero means the session never starts.
/remote-env writes remote.defaultEnvironmentId into user settings, which is one value for the whole machine — wrong as soon as a developer has two projects. Pin it per project instead:
node scripts/setup-remote-env.mjs --install --pin-env=<environment-id>
That writes .claude/settings.local.json, which outranks user settings and is gitignored — correct on both counts, since an environment belongs to one developer's account and is meaningless in someone else's checkout. Existing keys in that file are merged, not replaced; it commonly holds permission grants worth keeping.
Only the bootstrap belongs in the environment-variable box. Values there are stored as plain text and are readable by anyone who uses the environment — on an organization-shared environment, that is every member of the organization. Everything else is materialized by the session-start hook. There is no dedicated secrets store on this surface, and personal versus shared environments cannot be told apart programmatically, so this one is a rule the operator upholds rather than something the tooling can enforce.
Whatever tier provisioned it, the same read-back proves it. Trust comes from the verify, not the mechanism — which is what makes emit-tier as trustworthy as API-tier.
scripts/verify-remote-env.mjs [SECRETS_DIR]
Asserts, without printing a value: each declared tool at its pinned or minimum version, the detected surface, the secrets directory at mode 0700, both files at mode 0600, a clean checkout, and that every name in secrets.require resolves to a real credential rather than a proxy placeholder.
That last one exists because presence is a weaker claim than usability. A credential the surface keeps outside the sandbox and substitutes at egress is present and non-empty, so a presence check passes — and a script that reads the variable and puts it in a header sends the placeholder and fails against the service, with an error pointing anywhere but at the environment.
Never verify against vendor UI state. On 2026-08-01 a Codex environments table reported zero tasks for an environment that had demonstrably completed one, because the task records carried a null environment identifier and the --env filter was correspondingly unreliable. Reconcile through durable identifiers only.
Checked when setup runs, not at 3am:
@codyswann/lisa package available under node_modules.Fail with a message naming what is missing. Never provision-and-hope.
On Codex Cloud the bootstrap must be an environment variable, not a task secret: setup runs on a new container and maintenance runs on cache resume, both before task secrets exist. The tradeoff is that the variable remains visible during the task, so compensate by keeping the machine account narrowly scoped and instructing the task never to inspect or use it.
On Claude Code web the same placement applies for the same reason, with the exposure widened rather than narrowed: there is no secrets store, values are stored as plain text, and anyone who uses the environment can read them. On an organization-shared environment that is every member. Keep the bootstrap in a personal environment, scope the machine account to the minimum, and treat every other credential as something the session-start hook materializes rather than something a human pastes.
lisa-secrets-access — owns every part of the secrets contract this skill composes with.lisa-remote-dispatch — dispatches work to an environment this skill prepared.