@warlock.js/ai-workspace 5.30.0 → 5.31.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/CHANGELOG.md +46 -2
- package/README.md +34 -21
- package/cjs/index.cjs +3116 -309
- package/cjs/index.cjs.map +1 -1
- package/esm/backends/local.d.mts.map +1 -1
- package/esm/backends/local.mjs +165 -27
- package/esm/backends/local.mjs.map +1 -1
- package/esm/backends/mock.d.mts +11 -5
- package/esm/backends/mock.d.mts.map +1 -1
- package/esm/backends/mock.mjs +34 -0
- package/esm/backends/mock.mjs.map +1 -1
- package/esm/contracts/index.d.mts +3 -3
- package/esm/contracts/tool-io.type.d.mts +202 -4
- package/esm/contracts/tool-io.type.d.mts.map +1 -1
- package/esm/contracts/workspace-backend.contract.d.mts +61 -1
- package/esm/contracts/workspace-backend.contract.d.mts.map +1 -1
- package/esm/contracts/workspace-ops.contract.d.mts +32 -3
- package/esm/contracts/workspace-ops.contract.d.mts.map +1 -1
- package/esm/contracts/workspace-policy.type.d.mts +133 -12
- package/esm/contracts/workspace-policy.type.d.mts.map +1 -1
- package/esm/contracts/workspace.contract.d.mts +63 -9
- package/esm/contracts/workspace.contract.d.mts.map +1 -1
- package/esm/errors.d.mts +79 -5
- package/esm/errors.d.mts.map +1 -1
- package/esm/errors.mjs +22 -1
- package/esm/errors.mjs.map +1 -1
- package/esm/index.d.mts +13 -5
- package/esm/index.mjs +10 -2
- package/esm/ops.d.mts +21 -2
- package/esm/ops.d.mts.map +1 -1
- package/esm/ops.mjs +230 -64
- package/esm/ops.mjs.map +1 -1
- package/esm/policy/jail.mjs +73 -0
- package/esm/policy/jail.mjs.map +1 -0
- package/esm/policy/policy.d.mts +25 -15
- package/esm/policy/policy.d.mts.map +1 -1
- package/esm/policy/policy.mjs +61 -85
- package/esm/policy/policy.mjs.map +1 -1
- package/esm/policy/shell-check.d.mts +64 -0
- package/esm/policy/shell-check.d.mts.map +1 -0
- package/esm/policy/shell-check.mjs +627 -0
- package/esm/policy/shell-check.mjs.map +1 -0
- package/esm/policy/shell-parser.mjs +802 -0
- package/esm/policy/shell-parser.mjs.map +1 -0
- package/esm/policy/tokenize-command.d.mts +63 -0
- package/esm/policy/tokenize-command.d.mts.map +1 -0
- package/esm/policy/tokenize-command.mjs +75 -23
- package/esm/policy/tokenize-command.mjs.map +1 -1
- package/esm/shell-jobs.d.mts +45 -0
- package/esm/shell-jobs.d.mts.map +1 -0
- package/esm/shell-jobs.mjs +247 -0
- package/esm/shell-jobs.mjs.map +1 -0
- package/esm/todos.d.mts +22 -0
- package/esm/todos.d.mts.map +1 -0
- package/esm/todos.mjs +73 -0
- package/esm/todos.mjs.map +1 -0
- package/esm/tools/edit-file.d.mts.map +1 -1
- package/esm/tools/edit-file.mjs +2 -1
- package/esm/tools/edit-file.mjs.map +1 -1
- package/esm/tools/glob.d.mts.map +1 -1
- package/esm/tools/glob.mjs +2 -1
- package/esm/tools/glob.mjs.map +1 -1
- package/esm/tools/grep.d.mts.map +1 -1
- package/esm/tools/grep.mjs +2 -1
- package/esm/tools/grep.mjs.map +1 -1
- package/esm/tools/json-schemas.mjs +175 -0
- package/esm/tools/json-schemas.mjs.map +1 -0
- package/esm/tools/list-dir.d.mts +32 -0
- package/esm/tools/list-dir.d.mts.map +1 -0
- package/esm/tools/list-dir.mjs +50 -0
- package/esm/tools/list-dir.mjs.map +1 -0
- package/esm/tools/multi-edit.d.mts +38 -0
- package/esm/tools/multi-edit.d.mts.map +1 -0
- package/esm/tools/multi-edit.mjs +121 -0
- package/esm/tools/multi-edit.mjs.map +1 -0
- package/esm/tools/read-file.d.mts.map +1 -1
- package/esm/tools/read-file.mjs +2 -1
- package/esm/tools/read-file.mjs.map +1 -1
- package/esm/tools/run-shell.d.mts +34 -12
- package/esm/tools/run-shell.d.mts.map +1 -1
- package/esm/tools/run-shell.mjs +59 -53
- package/esm/tools/run-shell.mjs.map +1 -1
- package/esm/tools/run-tests.d.mts +8 -0
- package/esm/tools/run-tests.d.mts.map +1 -1
- package/esm/tools/run-tests.mjs +10 -3
- package/esm/tools/run-tests.mjs.map +1 -1
- package/esm/tools/schema.mjs +23 -18
- package/esm/tools/schema.mjs.map +1 -1
- package/esm/tools/shell-kill.d.mts +25 -0
- package/esm/tools/shell-kill.d.mts.map +1 -0
- package/esm/tools/shell-kill.mjs +36 -0
- package/esm/tools/shell-kill.mjs.map +1 -0
- package/esm/tools/shell-output.d.mts +30 -0
- package/esm/tools/shell-output.d.mts.map +1 -0
- package/esm/tools/shell-output.mjs +49 -0
- package/esm/tools/shell-output.mjs.map +1 -0
- package/esm/tools/todo.d.mts +43 -0
- package/esm/tools/todo.d.mts.map +1 -0
- package/esm/tools/todo.mjs +126 -0
- package/esm/tools/todo.mjs.map +1 -0
- package/esm/tools/write-file.d.mts.map +1 -1
- package/esm/tools/write-file.mjs +2 -1
- package/esm/tools/write-file.mjs.map +1 -1
- package/esm/workspace.d.mts +11 -8
- package/esm/workspace.d.mts.map +1 -1
- package/esm/workspace.mjs +108 -27
- package/esm/workspace.mjs.map +1 -1
- package/llms-full.txt +155 -34
- package/llms.txt +1 -1
- package/package.json +3 -3
- package/skills/README.md +2 -2
- package/skills/build-loop-agent/SKILL.md +7 -7
- package/skills/index.md +6 -1
- package/skills/use-a-workspace/SKILL.md +148 -27
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,49 @@ All notable changes to `@warlock.js/ai-workspace` are documented in this file.
|
|
|
4
4
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). `@warlock.js/*` packages are released in lockstep — every package shares the same version number, so a version below may list only the changes that affected this package.
|
|
6
6
|
|
|
7
|
+
## 5.31.0 - 2026-10-18
|
|
8
|
+
|
|
9
|
+
### Upgrading
|
|
10
|
+
|
|
11
|
+
- **BREAKING** `ws.tools.all()` now also returns `multi_edit`, `list_dir`, `todo_write` and `todo_read` (plus `shell_output` / `shell_kill` when `shell.background` is set), and `readonly().tools.all()` adds `list_dir` and the todo tools. To keep the previous set, use `ws.tools.pick("readFile", "editFile", "writeFile", "runShell", "runTests", "grep", "glob")`.
|
|
12
|
+
- **BREAKING** `WorkspaceOps` gains `multiEdit`, `listDir`, `startShell`, `shellOutput`, `shellKill`, `listShells` and `dispose`, and `Workspace` gains those plus `readTodos` / `writeTodos`. A custom implementation or test double must add them.
|
|
13
|
+
- **BREAKING** With deny rules in force (`shell.deny`, `shell.denyPatterns` or `shell.allowCommand`) and no allowlist entry for it, a command that runs another command out of the policy's sight — `sh`/`bash -c`, `eval`, `source`, `xargs`, `sudo`, `setsid`, … — is now refused (`reason: "nested-command"`). List the runner in `shell.allow` to permit it deliberately. `shell.deny` now also matches case-insensitively.
|
|
14
|
+
- Shell refusal messages changed: they now name the cause and say how to fix the command. Branch on the new `WorkspacePolicyError.reason` instead of parsing `message`.
|
|
15
|
+
- `makeRunShellTool(ops, { background: true })` returns `ToolContract<RunShellInput, RunShellResult | RunShellBackgroundResult>`; without `background` the type is unchanged.
|
|
16
|
+
|
|
17
|
+
### Added
|
|
18
|
+
|
|
19
|
+
- `list_dir` tool and `ws.listDir(path, { depth, ignore })`: a bounded, jailed directory listing (breadth-first, types and file sizes, `denyPaths` and `list.ignore` left out, symlinks reported but never followed), configured by the new `list` policy (`ignore`, `maxDepth`, `maxEntries`).
|
|
20
|
+
- `multi_edit` tool and `ws.multiEdit({ path, edits, expectHash })`: several exact-string replacements in one file, applied in order and written once — all of them or none. `WorkspaceEditError.editIndex` names the failing edit.
|
|
21
|
+
- Background commands: with `shell.background` (`{ maxJobs?, timeoutMs? }`), `run_shell({ background: true })` / `ws.startShell()` start a long-running command owned by the workspace; `shell_output` / `ws.shellOutput(id, { since, waitMs })` read its new output (newest `shell.maxOutputBytes` kept, with a `cursor`), and `shell_kill` / `ws.shellKill(id)` kill its whole process tree. `ws.listShells()` lists them; `WorkspaceJobError` reports an unknown id. Backends implement the new optional `start()`.
|
|
22
|
+
- `ws.dispose()` kills every background command (whole process trees) and makes the workspace refuse new commands; trees still running when the Node process exits are killed too.
|
|
23
|
+
- `todo_write` / `todo_read` tools and `ws.writeTodos()` / `ws.readTodos()`: an in-memory todo list per workspace, shared with its `readonly()` / `scope()` projections. `TodoStore` is exported.
|
|
24
|
+
- `shell.denyPatterns` (string prefix patterns or RegExps) and `shell.allowCommand(argv, ctx)` (return `true`, `false`, or a refusal explanation) check every command's arguments, so a policy can refuse `git push --force` or `rm -rf /` while allowing `git` and `rm`.
|
|
25
|
+
- `shell.interpreter: "sh"` — an explicit opt-in to shell syntax. The line runs through `/bin/sh -c` (`cmd.exe /d /v:off /s /c` on Windows) after it is parsed and every simple command in it — inside `;`, `&&`, `||`, pipes, subshells, `$( )`, backticks, here-documents and `NAME=value` prefixes — is checked against `deny` / `allow` / `denyPatterns` / `allowCommand`. Redirection targets and `cd` directories must stay in the jail (re-checked with symlinks resolved). Syntax the parser cannot read with confidence is refused. The default stays `"none"` (argv, no shell).
|
|
26
|
+
- `WorkspacePolicyError.reason` (`WorkspaceShellRefusalReason`: `"metacharacter"`, `"unbalanced-quotes"`, `"denied-executable"`, `"not-allowed"`, `"denied-pattern"`, `"nested-command"`, `"unsupported-syntax"`, `"path-escape"`) and `executable`, so hosts can phrase refusals themselves. In argv mode the message suggests the split, e.g. "`&&` is not supported in argv mode: run `git add -A`, then `git commit -m …`, as separate commands."
|
|
27
|
+
- `checkShellCommand(policy, command)` returns the shell policy's verdict (reason, message, every checked argv, paths) without running anything; `analyzeArgvCommand()` and `tokenizeCommand()` are exported.
|
|
28
|
+
- Backends may implement an optional `stat()`; the local and mock backends do. The mock backend's scripted commands accept `running: true` for background starts.
|
|
29
|
+
- The workspace skill and docs cover the new tools, shell mode, argument rules, background commands and `dispose()`.
|
|
30
|
+
|
|
31
|
+
### Changed
|
|
32
|
+
|
|
33
|
+
- The docs, the skill and the path resolver's comments explain how the workspace jail differs from `@warlock.js/fs`'s new confined reader (`fs.confined`) and why the workspace keeps its own resolver: it accepts absolute paths inside the jail, `allowPaths` roots, `denyPaths`, links that stay inside and paths that do not exist yet. Behaviour is unchanged.
|
|
34
|
+
|
|
35
|
+
### Fixed
|
|
36
|
+
|
|
37
|
+
- The package README no longer ends with a stray empty code block.
|
|
38
|
+
- The workspace tools now publish a JSON Schema of their input, so providers send the model the real parameter names instead of an empty object schema.
|
|
39
|
+
- `run_tests` quotes its `pattern` for the shell in use: single quotes under `interpreter: "sh"`, so `$` and backticks in a test-name filter stay literal.
|
|
40
|
+
|
|
41
|
+
### Security
|
|
42
|
+
|
|
43
|
+
- Wrapper commands no longer hide the command they run: `env`, `time`, `timeout`, `nice`, `nohup`, `command`, `builtin`, `exec` and `find -exec` are unwrapped and the inner command is checked too, and a wrapper used with options the policy cannot read is refused. Before, `env rm -rf /` passed a `deny: ["rm"]` policy.
|
|
44
|
+
- `shell.deny` matches executable names case-insensitively, so `RM` on a case-insensitive filesystem no longer slips past `deny: ["rm"]`.
|
|
45
|
+
- A model-supplied `timeoutMs` on `run_shell` can now only lower `shell.timeoutMs`, as documented, never raise it.
|
|
46
|
+
- Shell mode removes `ENV`, `BASH_ENV`, `SHELLOPTS`, `BASHOPTS`, `CDPATH`, `IFS`, `PS4`, `GLOBIGNORE` and `BASH_FUNC_*` from the spawned environment, refuses assignments to such variables, refuses `&` (use background commands), and refuses arithmetic-evaluating constructs (`$(( ))`, array subscripts in builtins) that bash can use to run hidden commands.
|
|
47
|
+
- The docs, README and skill now state plainly that shell mode is a policy layer checked before `/bin/sh` runs the line (defence in depth), not an OS sandbox, and recommend running untrusted agents in a container, microVM or as a separate unprivileged user.
|
|
48
|
+
- Killing a background command, a timeout and `dispose()` signal only the process tree the workspace spawned (its own process group on POSIX, `taskkill /T` on its PID on Windows).
|
|
49
|
+
|
|
7
50
|
## 5.30.0 - 2026-10-16
|
|
8
51
|
|
|
9
52
|
### Upgrading
|
|
@@ -15,13 +58,13 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
|
15
58
|
|
|
16
59
|
### Added
|
|
17
60
|
|
|
18
|
-
- `exec()` takes a per-call `signal` (`ExecOptions`); `run_shell` and `run_tests` pass the agent run's `ctx.signal`. An already-aborted signal never starts the command; an abort mid-run kills the process tree. Both resolve with the new `cancelled: true` on `RunShellResult`, never together with `timedOut
|
|
61
|
+
- `exec()` takes a per-call `signal` (`ExecOptions`); `run_shell` and `run_tests` pass the agent run's `ctx.signal`. An already-aborted signal never starts the command; an abort mid-run kills the process tree. Both resolve with the new `cancelled: true` on `RunShellResult`, never together with `timedOut`.
|
|
19
62
|
- `WorkspaceBackendExecOptions.signal` and `WorkspaceBackendExecResult.cancelled?` for custom backends; the local backend removes its abort listener when the command settles, and the mock backend honours a pre-aborted signal.
|
|
20
63
|
- Read results carry `startLine`, `endLine`, `truncated`, `bytesCapped` and `totalBytes`; `readFile()` and the `read_file` tool now return the same `ReadFileResult` for the same arguments.
|
|
21
64
|
|
|
22
65
|
### Fixed
|
|
23
66
|
|
|
24
|
-
- `read.maxBytes` is enforced: the returned `content` (line-number prefixes and newlines included) never exceeds it, so a single long line no longer bypasses the cap. Lines are cut on a character boundary, never mid-character; `hash` remains the SHA-256 of the whole file
|
|
67
|
+
- `read.maxBytes` is enforced: the returned `content` (line-number prefixes and newlines included) never exceeds it, so a single long line no longer bypasses the cap. Lines are cut on a character boundary, never mid-character; `hash` remains the SHA-256 of the whole file.
|
|
25
68
|
- Command output clipped at `shell.maxOutputBytes` is cut on a character boundary instead of ending in a replacement character.
|
|
26
69
|
- A read window past the end of the file returns empty `content` instead of one numbered empty line.
|
|
27
70
|
- Writes, edits and removes of the same path are serialized, so two concurrent `edit_file` calls (the agent now dispatches a trip's tool calls concurrently) can no longer both pass the stale-hash guard and silently drop one edit; the second re-reads and is rejected as stale, or applies on top when it pinned no hash.
|
|
@@ -99,6 +142,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
|
99
142
|
### Changed
|
|
100
143
|
|
|
101
144
|
- Lockstep release maintenance and dependency refresh.
|
|
145
|
+
|
|
102
146
|
## 5.20.0 - 2026-09-24
|
|
103
147
|
|
|
104
148
|
### Changed
|
package/README.md
CHANGED
|
@@ -16,11 +16,14 @@ OS sandbox. Every path is `realpath`-resolved and must sit under `cwd` (or an
|
|
|
16
16
|
`allowPaths` root); `denyPaths` globs are blocked even inside `cwd`; shell commands
|
|
17
17
|
are gated by an executable allow/deny list; and `process.env` is **never** inherited
|
|
18
18
|
wholesale (a command can't even find `node`/`npm` unless the policy opts in via
|
|
19
|
-
`shell.inheritEnv: ["PATH"]`). What it does **not** do is contain what an
|
|
19
|
+
`shell.inheritEnv: ["PATH"]`). What it does **not** do is contain what an _allowed_
|
|
20
20
|
process does once it runs — an allowed `node` can still open a socket or read a file
|
|
21
|
-
its own user can reach.
|
|
22
|
-
|
|
23
|
-
|
|
21
|
+
its own user can reach. Shell mode (`interpreter: "sh"`) is likewise a policy layer
|
|
22
|
+
checked before `/bin/sh` runs the line — defence in depth, not a sandbox. For an
|
|
23
|
+
untrusted agent, run the workspace inside a container or microVM with only the
|
|
24
|
+
project mounted, or at least as a separate unprivileged user, and keep this policy
|
|
25
|
+
as a second layer. Treat it as least-privilege guardrails for a **trusted** agent,
|
|
26
|
+
not a security boundary around a hostile one.
|
|
24
27
|
|
|
25
28
|
## Install
|
|
26
29
|
|
|
@@ -41,7 +44,7 @@ import "@warlock.js/ai-workspace";
|
|
|
41
44
|
### 1. Minimal build-loop agent
|
|
42
45
|
|
|
43
46
|
Hand the whole tool set to an agent and let it read, edit, and run tests until green.
|
|
44
|
-
The failure comes back as tool
|
|
47
|
+
The failure comes back as tool _data_, so the agent self-corrects.
|
|
45
48
|
|
|
46
49
|
```ts
|
|
47
50
|
import "@warlock.js/ai-workspace";
|
|
@@ -66,7 +69,7 @@ await dev.execute("`npm test` fails on the cart-total suite. Make it green.");
|
|
|
66
69
|
|
|
67
70
|
A reviewer needs to read and search — never to write or run a shell. Pick exactly
|
|
68
71
|
those three tools, or take a `readonly()` projection (which omits every mutating tool
|
|
69
|
-
|
|
72
|
+
_and_ rejects the mutating direct methods).
|
|
70
73
|
|
|
71
74
|
```ts
|
|
72
75
|
const ws = ai.workspace({ cwd: "/srv/acme-api" });
|
|
@@ -80,7 +83,7 @@ await reviewer.execute("Review src/cart for off-by-one bugs and summarize.");
|
|
|
80
83
|
|
|
81
84
|
// Equivalent, as a projection of the same jail:
|
|
82
85
|
const ro = ws.readonly();
|
|
83
|
-
ai.agent({ model, tools: ro.tools.all() }); // read_file
|
|
86
|
+
ai.agent({ model, tools: ro.tools.all() }); // read_file, list_dir, grep, glob, todo tools
|
|
84
87
|
```
|
|
85
88
|
|
|
86
89
|
### 3. Direct programmatic use (no LLM)
|
|
@@ -114,21 +117,32 @@ for (const file of await ws.glob("src/models/**/*.ts")) {
|
|
|
114
117
|
}
|
|
115
118
|
```
|
|
116
119
|
|
|
117
|
-
## What's in
|
|
120
|
+
## What's in the package
|
|
118
121
|
|
|
119
122
|
- **Policy jail** — `cwd` path jail (`realpath`-resolved), `allowPaths` / `denyPaths`,
|
|
120
|
-
a fail-closed shell allow/deny list (deny wins),
|
|
121
|
-
and opt-in `inheritEnv` (no
|
|
123
|
+
a fail-closed shell allow/deny list (deny wins), argument rules (`denyPatterns`,
|
|
124
|
+
`allowCommand`), per-command timeout + output cap, and opt-in `inheritEnv` (no
|
|
125
|
+
blanket `process.env`).
|
|
126
|
+
- **argv by default, shell on request** — commands run as one argv without a shell;
|
|
127
|
+
a refusal names the operator and suggests the split. `shell.interpreter: "sh"`
|
|
128
|
+
opts in to pipes, `&&` and redirections, with every command in the line checked
|
|
129
|
+
and redirections kept inside the jail.
|
|
122
130
|
- **Backends** — `local` (default; `@warlock.js/fs` + `node:child_process`) and
|
|
123
131
|
`mock` (in-memory, for hermetic disk-free tests).
|
|
124
|
-
- **
|
|
125
|
-
`
|
|
126
|
-
`
|
|
127
|
-
|
|
128
|
-
|
|
132
|
+
- **Tools** under `ws.tools.*` — `read_file`, `edit_file`, `multi_edit`, `write_file`,
|
|
133
|
+
`list_dir`, `run_shell` (optionally in the background), `shell_output`,
|
|
134
|
+
`shell_kill`, `run_tests`, `grep`, `glob`, `todo_write`, `todo_read` — each a
|
|
135
|
+
`ToolContract` (with a JSON Schema of its input) ready for `ai.agent({ tools })`.
|
|
136
|
+
- **Direct methods** — `readFile` / `writeFile` / `editFile` / `multiEdit` /
|
|
137
|
+
`listDir` / `exec` / `startShell` / `shellOutput` / `shellKill` / `listShells` /
|
|
138
|
+
`grep` / `glob` / `exists` / `mkdir` / `remove` / `readTodos` / `writeTodos`,
|
|
139
|
+
sharing the exact same policy seam, and `dispose()`.
|
|
140
|
+
- **Background commands** — with `shell.background`, dev servers and watchers run
|
|
141
|
+
as jobs owned by the workspace, read incrementally, and are killed (whole process
|
|
142
|
+
tree) by `shell_kill` or `dispose()`.
|
|
129
143
|
- **`tools.all()` / `tools.pick(...)`** — every tool, or a least-privilege subset.
|
|
130
|
-
- **`readonly()`** — a projection that vends only read
|
|
131
|
-
mutating direct method.
|
|
144
|
+
- **`readonly()`** — a projection that vends only the read-side tools (read, list,
|
|
145
|
+
grep, glob, todos) and rejects every mutating direct method.
|
|
132
146
|
- **`scope(subdir)`** — a sub-jailed view rooted at `subdir` (tighter blast radius).
|
|
133
147
|
- **Read-before-edit guard** — `read_file` returns a SHA-256 `hash`; `edit_file`
|
|
134
148
|
requires an exact, unique `oldString` (or `replaceAll`) and rejects on an
|
|
@@ -140,9 +154,9 @@ for (const file of await ws.glob("src/models/**/*.ts")) {
|
|
|
140
154
|
in `run_shell` / `run_tests`, stop the command (process tree killed, or never
|
|
141
155
|
started when already aborted) and report `cancelled: true`.
|
|
142
156
|
|
|
143
|
-
**
|
|
144
|
-
|
|
145
|
-
|
|
157
|
+
**Not in the package yet**: the transactional lifecycle (`snapshot` / `restore` /
|
|
158
|
+
`diff` / `changes`), `on()` hooks, `usage()`, and the worktree / container backends
|
|
159
|
+
plus provisioning helpers.
|
|
146
160
|
|
|
147
161
|
## Skills
|
|
148
162
|
|
|
@@ -152,4 +166,3 @@ Progressive-disclosure, per-task docs live under [`skills/`](./skills/README.md)
|
|
|
152
166
|
and operate it (policy, tools, direct methods, `readonly` / `scope`).
|
|
153
167
|
- [`build-loop-agent/`](./skills/build-loop-agent/SKILL.md) — wire `ws.tools.all()`
|
|
154
168
|
into a coding agent that reads → edits → runs tests until green.
|
|
155
|
-
```
|