plugins/src/harper-fabric/skills/harper-build-and-deploy/SKILL.md
This skill should be used when building, running locally, or deploying a Harper (HarperDB/Fabric) component — running harper dev/run, producing the generated resources.js and web/** from TypeScript via the project build, packaging the harper-app component, deploying to Harper Fabric, or handling deploy-time secrets. Use it for any change that affects the deployable surface or the dev loop. Pairs with harper-component-model, harper-config-yaml, and harper-resources.
npx skillsauth add codyswanngt/lisa harper-build-and-deployInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
The Harper lifecycle is: develop locally → build TypeScript into the deployable component → run/iterate → deploy to Fabric. This skill covers the CLI, the project's build step, and the deploy surface so a change is provably finished, not just compiling.
The CLI binary is harper (v5; older installs use harperdb). Key commands:
| Command | What it does |
| --- | --- |
| harper dev <path/to/app> | Dev mode: watches files, single-threaded, auto-restarts worker threads on change, with console logging. The fast iteration loop. Does not restart the main thread. |
| harper run <path/to/app> | Run an app from any directory. Use when you need the main thread to (re)start; manage start/stop yourself. |
| harper start / stop / restart | Background (daemon) lifecycle. |
| harper status | Harper and clustering status. |
| harper get_components | List installed components. |
In this project, dev typically runs against the component dir, e.g.
harper dev harper-app (use the project's documented run command if it wraps this).
Harper loads JavaScript (resources.js) and serves static files (web/**). In this
project those are generated, not authored.
What the build actually does:
src/ into deployable JavaScript.harper-app/resources.js, the aggregate resource module loaded by the
jsResource extension.harper-app/resource-*.js when the project
build splits resources for Harper's runtime loader.harper-app/web/** for the static
extension to serve.harper-app/lib/** and rewrites imports
such as ../lib/foo.js to ./lib/foo.js. Fabric packages the component root
as a flat deploy unit, and Node resolves real paths at runtime, so imports must
point at files that exist inside the packaged harper-app/ root.?v= query parameters to browser-module imports when the
project build owns web asset versioning.Generated Harper deploy artifacts usually include:
harper-app/*.js — every compiled module the build emits to the harper-app
root (resources.js, resource-*.js, and any other output such as a route
negotiation module). The single-star does not cross a directory separator,
so it does not match hand-written shims one level down.harper-app/web/**harper-app/lib/**The guard surfaces (generated-artifact-globs.txt for the PreToolUse block hook,
.gitignore, .prettierignore, the ESLint/oxlint/knip ignores, and
tsconfig.eslint.json) all key off harper-app/*.js so a newly-named compiled
module is protected automatically. Name compiled resource modules
resource-*.ts so their JS output is unambiguously generated.
If you keep a hand-written .js at the harper-app root (e.g. an SEO shell),
the root-level rule would otherwise treat it as generated: re-include it with a
!harper-app/<file>.js line below the managed gitignore block, and add it to
.lisa/harper-generated-artifact-allowlist.txt so the block hook lets you edit
it. Hand-written shims nested under harper-app/<route>/index.js need no
exemption — the root-level rule never matches them.
Every lint, format, dead-code, search, or generated-artifact guard must ignore generated paths unless it is explicitly validating the build output itself. When a new generated directory appears, add it to every relevant ignore surface in the same change; partial ignores fail later gates in non-obvious ways.
src/ is source. bun run build produces the deployable
Harper assets from it.harper-app/. Deploying
stale artifacts ships code that does not match src/.A deployable Harper component must keep these at the component root Fabric packages:
config.yaml — active extensions ([[harper-config-yaml]])schema.graphql — data model ([[harper-schema-graphql]])resources.js — generated custom logicweb/** — generated static assetsIf a change touches this surface, it is not done until the local build and the relevant deployed or smoke path agree.
Fabric is Harper's distributed deploy network. Lisa ships a create-only GitHub
Actions workflow at .github/workflows/deploy.yml for Harper/Fabric projects. Use
that workflow as the canonical deployment path for repositories that have adopted
the template: it builds the project, runs harper deploy_component against the
configured Fabric target, and then runs the project's smoke verification script.
Required GitHub secrets:
CLI_TARGET or HARPER_FABRIC_TARGET — Fabric target URL.CLI_TARGET_USERNAME — deploy username.CLI_TARGET_PASSWORD — deploy password.Optional GitHub variables:
HARPER_PROJECT — Fabric project name; defaults to the repository name.HARPER_PACKAGE — package path; defaults to harper-app.For local debugging or one-off deploys, the equivalent CLI command packages the component and sends it to a target instance:
harper deploy_component \
project=<app-name> \
package=<path-or-git-url> \
target=https://<instance>:9925 \
username=<user> \
password=<pass> \
restart=true \
replicated=true
package deploys the current directory.restart=true restarts threads after deploy so new code loads.replicated=true asks Harper/Fabric to apply the component deploy across the
cluster rather than only the node receiving the deploy request. Include it when
targeting Fabric or any clustered deployment.CLI_TARGET_USERNAME / CLI_TARGET_PASSWORD instead of
inline flags — prefer that so secrets never land in shell history or tracked files.Fabric is a distributed runtime: a project can run on one or more Harper nodes, often grouped by region or environment. Your application code is packaged as a component, and the data layer is replicated through Harper's database and clustering model.
Keep these semantics separate:
replicated=true, the deployed component package should reach the cluster nodes
that serve the application. Without it, you may update only the target node and
leave other nodes running older code.restart=true reloads code after
the package lands; it is not a substitute for checking every node or region that
receives traffic.After a replicated deploy, verify from the topology the app actually uses:
replicated=true for Fabric/cluster
targets.harper status or the project's Fabric status command to see the expected
nodes/regions.Keep runtime secrets out of tracked files. Use environment variables, the
loadEnv extension, or an OS keychain helper. Document where secrets live (which
env var, which store) without recording their values.
bun run build — produces fresh resources.js / web/**.bun run typecheck.harper deploy_component plus the project smoke
command against the local or deployed Harper endpoint.bash scripts/zap-baseline.sh with ZAP_TARGET_URL set to the deployed app.If a verification command cannot run, report the exact command and the blocker — do not claim completion. When you hit a Harper/Fabric limitation or workaround, record the symptom, root cause, fix, and the tempting-but-broken alternatives in the project's Fabric runbook.
development
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.
tools
Provision and verify a remote execution environment for a host project — Codex Cloud today, other remote surfaces as they are added. Generates a repository-owned setup script that installs the declared toolchain, materializes secrets through lisa-secrets-access, and runs the project's own hook. Provisions by API where one exists, by driving the vendor console where one does not, and by emitting exact config otherwise — then proves the result with the same read-back regardless of which tier did the work. Use before dispatching any work with executionEnv.
tools
Bring a developer's machine in line with the toolchain the project declares. Reports every tool in remoteEnv.tools that is missing, outdated, or unpinned for this platform, and installs the missing ones into ~/.local/bin from the same pinned, checksummed entries the remote surfaces use — but only when asked. Same manifest, same pins, same installers as lisa-setup-remote-env; what differs is consent and that the pin is a floor rather than an equality. Run it on a fresh checkout, after a manifest change, or when a tool fails at the moment of use.
tools
Route one unit of work to a remote execution surface. Reads the executionEnv parameter (local by default, codex-cloud or claude-web today), verifies the environment is provisioned and bound to this repository, submits a thin skill invocation, records the task identifier to .lisa/remote-dispatch.json, and exits without polling. Routing only — the remote runs the identical skill from the identical repository. Composable and inline: other skills invoke it via the Skill tool rather than users calling it directly.