AI Decision System
Replayable, offline-verifiable records for AI-assisted go/no-go decisions: experiment promotions, typed model answers and human-approved actions.
Collect a project's container, image, environment variables, and commands into one wip.yml, forwarded to wslc.exe as safe argument arrays.
Developer toolsC# · PowerShellGitHub @slidictOnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
Wip is a native, standalone CLI for WSL Containers (WSLC), providing native-like interaction with containerized applications.
Taken from the repository README
Loading the full README. The saved excerpt is shown below.
Homepage: https://wslc-wip.slidict.com/ · Full documentation: wip Wiki
wip is an OSS CLI wrapper that brings a dip-like
workflow to Microsoft WSLC. It collects a project's container, image, environment variables, and
commands into a single wip.yml, and forwards them to wslc.exe / wslc as safe argument arrays
(no shell interpolation).
Status: early release. Expect to track WSLC's own interface as it evolves.
This README is just the fastest path to a running wip.yml. For everything else — every config
key, every command's flags, guides, and troubleshooting — see the
wip Wiki.Read the full README ↗Homepage: https://wslc-wip.slidict.com/ · Full documentation: wip Wiki
wip is an OSS CLI wrapper that brings a dip-like
workflow to Microsoft WSLC. It collects a project's container, image, environment variables, and
commands into a single wip.yml, and forwards them to wslc.exe / wslc as safe argument arrays
(no shell interpolation).
Status: early release. Expect to track WSLC's own interface as it evolves.
This README is just the fastest path to a running wip.yml. For everything else — every config
key, every command's flags, guides, and troubleshooting — see the
wip Wiki.
wslc.exe /
wslc as a safe argv array; wip never talks to Docker and never grows logic that competes with
wslc. See Architecture.
compose.yml yourself, or bridge to a third-party compose-for-wslc tool. See
Which mode should you use? below.wip init --ai — describe your stack in natural language and get a validated wip.yml
back, via any local OpenAI-compatible server (Ollama, LM Studio). See
AI-Assisted Initialization.wip up waits on a dependency's healthcheck: before starting
whatever depends on it, in every mode. See
Restart Policies.wip.exe runs on Windows and drives wslc
directly, but is meant to be typed from wherever you work. See
Running from a WSL2 Shell.wip config redacts credential-shaped values by default. See
Secret Masking.--debug shows where time is going — a step trace plus periodic host resource snapshots.
See Debug Output.v2 switched from Ruby to a C# Native AOT binary —
wip.exeis now a single self-contained executable with no runtime to install. The Ruby implementation still works and stays available at v1.1.4 / thewslc-wipgem, but has no further updates planned.
wip.yml runs in one of three modes, set with mode:. Pick the one that matches your project:
| Situation | Use |
|---|---|
No compose.yml — wip manages containers directly | mode: container (default) |
Have compose.yml, don't want to install a third-party tool | mode: compose-native |
Have compose.yml and already use/prefer a third-party compose-for-wslc tool | mode: compose |
wip init picks compose-native automatically when it finds a compose.yml/.yaml or
docker-compose.yml/.yaml next to it, and container otherwise. For the full breakdown,
trade-offs, and compose.yml key support, see
Choosing a Mode on the wiki.
Windows with WSL2 and Microsoft WSLC. There is no runtime to install: wip.exe is a
self-contained Native AOT binary.
WSLC is generally available starting with WSL 3.0.1. Update using the normal release channel, then check the installed version and tooling:
wsl --update
wsl --version
wip doctor
If Microsoft Store updates are unavailable, download the stable release directly
from GitHub with wsl --update --web-download, then run wip doctor again.
Install directly from this repository with Scoop:
scoop install https://raw.githubusercontent.com/slidict/wip/main/wip.json
Or with WinGet:
winget install Slidict.Wip
Or manually: download and extract wip-<version>-win-x64.zip from
Releases, then add the directory holding wip.exe
to your PATH.
Building from source requires MSVC build tools in addition to the .NET SDK — see Building from Source for the one-time setup and common linker errors.
This walks through mode: container (the default). Already have a compose.yml? See
Which mode should you use? first, or read the wiki's
Getting Started guide.
cd my-project
wip init # writes a starter wip.yml; edit the TODOs, then:
wip doctor
wip build
wip up -d
wip rails console
wip init --template NAME preloads the sync.exclude list in the generated wip.yml with the
directories that stack regenerates inside the container, or that are too large to mirror (and
names the preset in the comment above the list):
--template | sync.exclude patterns written |
|---|---|
rails | .git, log/, tmp/, storage/, public/assets/, public/packs/, .bundle/, vendor/bundle/, coverage/, node_modules/ |
ruby | .git, log/, tmp/, .bundle/, vendor/bundle/, coverage/ |
node | .git, node_modules/, dist/, build/, .next/, .cache/, coverage/ |
rust | .git, target/ |
csharp | .git, bin/, obj/, .vs/, packages/ |
wip init --template csharp
That is the whole of it — the preset touches no other key. mode: is still decided by whether a
compose file sits next to the target, every other key is still written as the same placeholder or
TODO, and omitting --template just starts sync.exclude at .git, tmp/, node_modules/
instead. An unknown name is rejected with the valid list, and --template cannot be combined with
--ai (which generates the whole file from a natural-language request instead).
The quick start above is intentionally minimal. For fuller wip.yml/compose.yml pairs modeled
on real stacks — with the sidecars, healthchecks, and day-to-day commands a project actually
ends up with — see examples/:
examples/rails — Rails + Postgres + Redis, mode: containerexamples/node — Node.js + MySQL + Redis, mode: compose-nativeEach has its own README with copy-and-adapt setup instructions.
Put a wip.yml in your project root. Running from a subdirectory walks up to find it, or pass
--config PATH to point at one explicitly.
version: 1
mode: container # default
container: app # required once dependencies: has entries
dependencies:
app:
image: slidict/slidict:development
workdir: /app
command: server
ports:
- "3000:3000"
volumes:
- ".:/app"
interaction:
rails:
type: exec
command: bin/rails
container: app
interactive: true
Every config key — dependencies, healthchecks, interaction:/commands:, env files,
.dockerignore handling, source sync, secret masking — is covered in full on the wiki, starting
at the Configuration Reference.
| Command | Description |
|---|---|
wip init | Write a starter wip.yml |
wip doctor | Diagnose WSL2, WSLC, config, and the --ai host |
wip build / wip up [-d] / wip down | Build the image / start the stack / stop and remove it |
wip exec / wip run | Run a command in the existing container / a new ephemeral one |
wip shell | Open the configured shell |
wip security scan <sandbox> --tool <zap|nuclei> --target <url> | Run one pinned scanner against a target on its isolated network |
wip logs [-f] | Follow logs |
wip NAME ARGS... | Run interaction.NAME, appending any extra arguments |
Every command's flags, per-mode behavior, and examples are on the CLI Command Reference.
Besides your app's stack, wip.yml can declare named sandboxes — long-lived containers with
their own image and their own storage, for running a tool somewhere isolated from the project's
own containers. wip up, wip down and wip exec never touch a sandbox, and wip sandbox never
touches the stack.
version: 1
resource_namespace: example-project
volumes:
- name: state
persistent: true # kept when the sandbox is destroyed; `false` means ephemeral
mount: /home/user/state
sandboxes:
- name: agent
image: example/tool:1
command: [sleep, infinity] # optional argv list; the image must provide this command
volumes: [state]
wip sandbox create agent # run it, mounting the declared volumes
wip sandbox exec agent --interactive -- sh # a session you drive; `--` keeps sh's own flags
wip sandbox stop agent # pause it — `create` resumes the same container
wip sandbox destroy agent # remove it; persistent volumes survive
The optional command list replaces the image CMD without shell splitting; omitting it
preserves the image CMD. The optional entrypoint executable replaces ENTRYPOINT; its
arguments belong in command. For Nuclei, use entrypoint: sleep with command: [infinity].
Flags and arguments containing spaces remain separate list elements, such
as [nginx, -g, 'daemon off;']; the first list element must not contain whitespace.
Changing a declared command or entrypoint requires destroying and recreating the sandbox.
create / list / status / exec / attach / stop / destroy are the whole set. exec
runs non-interactively with a 300-second deadline unless you pass --interactive, which has no
deadline and cannot be combined with --timeout. Anything that has to survive destroy belongs
in the image or on a persistent volume — and installing or signing into whatever CLI the image
ships is outside wip's scope.
For the operations and their guarantees, see wip sandbox; for the configuration and ownership model, Sandboxes; for declared volumes on their own, volume storage; for putting a sandbox behind a network it cannot reach past, network isolation; for running one built-in diagnostic tool behind that boundary, see Security scanning below and the security scan engine.
Sandbox networking is IPv4 only, because WSLC is. Measured against WSLC 3.0.3: a network
created with an IPv6 subnet and gateway is accepted and the subnet is recorded in its IPAM
configuration, but EnableIPv6 stays false, an IPv4 subnet is assigned alongside it, and a
container on that network gets no IPv6 address at all. The docker driver option
com.docker.network.enable_ipv6=true is stored among the network's options and ignored. So
an IPv6 declaration does not fail — it silently produces an IPv4-only network, which is the
part worth knowing. Anything that depends on IPv6, including diagnosing a target's IPv6
exposure, is out of reach until WSLC supports it.
wip security scan runs a built-in diagnostic tool inside a declared sandbox against a target
on the same isolated network.
--tool <name> from the list returned
by wip security tools. There is no plugin API, dynamic assembly loading, reflection, or
wip.yml key that registers an external scanner. Adding a tool requires a reviewed pull
request to wip itself — an intentional restriction so that every adapter's code,
dependencies, pinned image, permissions, network traffic, and report/coverage handling are
reviewed before the tool can run.internal: true, and the engine refuses to start or run a scan unless
it has verified that the network exists, belongs to wip, and is internal. There is no
unrestricted or fallback mode. Measured against WSLC
(#262,
network isolation), an internal network allows traffic
only between sandboxes on that same declared network while blocking external IPv4 (TCP and
ICMP), public DNS resolvers, DNS name resolution, the default bridge's host gateway, cloud
metadata endpoints (169.254.169.254), and sandboxes on other internal networks. IPv6 is out
of scope because WSLC is IPv4-only (see Sandboxes above).image: in wip.yml must use the exact name@sha256:<64 hex digits> reference; tags
(including version tags) are refused because they can be reassigned. Pinning guarantees that
the scanner binary and its bundled rules cannot change between runs unless the configuration
changes.--tool | Pinned image (image:) | Keepalive command: | What it runs | Omitted (reported as unperformed) |
|---|---|---|---|---|
zap | ghcr.io/zaproxy/zaproxy@sha256:5ccfff9a411743ab2615136a2edd7416bec9ce41572fa94e935df38c14adb2b6 | [sleep, infinity] | Bounded traditional spider (1 min, depth 2, 10 children, confined to the target HTTP(S) URL subtree; no form submission, robots.txt, or sitemap parsing; OAST callbacks removed) and passive scan (up to 1 min) | Active scanning, authenticated paths (#277), browser/Ajax crawling, form submission, out-of-band callbacks |
"Unperformed" is reported separately from "no findings." Every scan records which checks
and crawl scope were omitted (printed to stderr under Unperformed checks (raw): and written to
unperformed-checks.txt and result.json's coverage field when --output is given;
incomplete runs omit findings altogether and exit nonzero). A completed zap scan with zero
findings only reflects the unauthenticated passive scope that ran — a clean report is not proof
that active or authenticated checks passed, or that the target is safe.
wip security scan executes inside an already-running sandbox (wip sandbox create), and
sandbox create starts the container detached (run -d) and requires it to stay running.
Because scanner images typically exit immediately under their default CMD (zap's default
CMD is bash), you must declare a persistent foreground command: such as
command: [sleep, infinity] on the scanner sandbox
(#274) — omitting command: is the step first-time
readers most often get wrong. (command: overrides CMD, not ENTRYPOINT; if you change it
later, sandbox destroy and sandbox create the sandbox before scanning.)
version: 1
resource_namespace: example-project
networks:
- name: scan
internal: true
sandboxes:
- name: target
image: example/app:1
network: scan
- name: scanner
image: ghcr.io/zaproxy/zaproxy@sha256:5ccfff9a411743ab2615136a2edd7416bec9ce41572fa94e935df38c14adb2b6
network: scan
command: [sleep, infinity] # required so the detached scanner container stays running (#274)
wip security tools
wip sandbox create target
wip sandbox create scanner
wip security scan scanner --tool zap --target http://<target-ip>:8080/ --output scan-results
wip sandbox destroy scanner
wip sandbox destroy target
The engine stops the scanner container when the scan finishes (or times out); sandbox destroy
removes it. For the CLI workflow, see
wip security scan on the wiki; for
engine internals, adapter contracts, result schema, exit codes, and pinned rules staging, see
the security scan engine.
git clone https://github.com/slidict/wip.git
cd wip
dotnet build wip.slnx
dotnet test tests/Wip.Tests/Wip.Tests.csproj
Requires the .NET 10 SDK. The test suite doesn't need WSLC and runs on Linux and macOS as well as Windows. See CONTRIBUTING.md for the full setup, and Development on the wiki for the e2e suite, the golden-corpus tests, and architecture notes. Known gaps and deferred decisions are tracked in docs/csharp-migration-plan.md.
A Docker Desktop vs. Wip/WSLC benchmark workflow lives under
benchmark/, driven by the
wip-benchmark skill. Latest run (2026-09-16, single
Windows 11 PC): wip served ~40% higher throughput and about half the p50 latency versus Docker
Desktop, at roughly half the host CPU under load — see the
full report for the numbers and caveats.
Bug reports and pull requests are welcome on GitHub. See CONTRIBUTING.md for commit conventions, versioning policy, and the PR checklist.