Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ Bump size is controlled by PR labels (`major` > `minor` > patch default). See [P
| `agents/` | Shared `createAgent`, Docker invoke, image ensure/build |
| `agents/base/` | Shared Arch base image constants (`agent-gwt/base:local`) |
| `agents/cursor/` | Cursor bindings only (Dockerfile path, image, auth, CLI run) |
| `agents/claude/` | Claude Code bindings only (Dockerfile path, image, credentials, CLI run) |
| `agents/claude/` | Claude Code bindings only (Dockerfile path, image, credentials, CLI run) |
| `docker/base/` | Shared Arch + yay Dockerfile (all agents `FROM` this tag) |
| `docker/<agent>/` | Per-agent Dockerfile (`FROM agent-gwt/base:local` + that product’s CLI) |
| `package-root` | Relative resolve to this package’s root (`src/` or `lib/` parent) — no directory scans |
Expand Down
64 changes: 46 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,33 @@ async function question_is_answered(this: Context) {
}
```

To copy a fixture tree instead of writing files one by one, use `copy_to_workspace`. Globs are resolved from the **current Vitest spec file’s directory** (not `process.cwd()`). A relative `from` is also resolved against that spec directory (absolute `from` is used as-is):

```ts
import { join } from "node:path";
import { copy_to_workspace } from "agent-gwt";

async function tests_are_in_workspace(this: Context) {
await copy_to_workspace(this.workspace, ["fixtures/**/*.spec.ts"]);
}

async function shared_fixtures_are_in_workspace(this: Context) {
await copy_to_workspace(this.workspace, ["**/*.spec.ts"], {
from: join(import.meta.dirname, "../shared-fixtures"),
});
}
```

Optional `base` strips a matching path prefix (`*` = one folder segment). `base: "fixtures/*"` turns `fixtures/suite-a/foo.spec.ts` into `foo.spec.ts` and `fixtures/suite-b/nested/bar.spec.ts` into `nested/bar.spec.ts`.

```ts
await copy_to_workspace(this.workspace, ["fixtures/**/*.spec.ts"], {
base: "fixtures/*",
});
```

An empty `globs` array, a glob that matches no files, a `base` that does not match every file, or two sources mapping to the same destination all throw, and nothing is copied.

### Inspecting the result

`this.agentResult` is the parsed JSON the CLI printed, for either agent. For Claude Code, `ClaudeAgentResult` types the useful fields:
Expand All @@ -194,11 +221,11 @@ A Claude run whose JSON reports `is_error: true` throws from `executing_the_agen

## Docker images

| Image | Role |
| --- | --- |
| `agent-gwt/base:local` | Shared Arch Linux base (`yay` + `aur` user). Used by all agents. |
| `agent-gwt/cursor-cli:local` | Cursor CLI on top of the base |
| `agent-gwt/claude-code:local` | Claude Code CLI (native binary) on top of the base |
| Image | Role |
| ----------------------------- | ---------------------------------------------------------------- |
| `agent-gwt/base:local` | Shared Arch Linux base (`yay` + `aur` user). Used by all agents. |
| `agent-gwt/cursor-cli:local` | Cursor CLI on top of the base |
| `agent-gwt/claude-code:local` | Claude Code CLI (native binary) on top of the base |

`buildAgentImage("cursor")` builds the base first, then the Cursor image; `buildAgentImage("claude")` does the same for Claude Code.

Expand Down Expand Up @@ -270,19 +297,20 @@ Pair workspace lifecycle separately: `withAspect(a_workspace, cleanup_workspace)

## Exports

| Export | Role |
| --- | --- |
| `AgentContext` | Extensible context type (`workspace`, `prompt`, `agent`, `image`, …) |
| `agent(opts)` | `withAspect` before — `{ name: "cursor" \| "claude", model?, image? }` |
| `buildAgentImage(name)` | Suite setup — builds base + agent image (use in vitest `globalSetup`) |
| `buildBaseImage()` | Builds `agent-gwt/base:local` only |
| `buildDockerImage(...)` | Builds an arbitrary Dockerfile (e.g. toolchain overlay) |
| `a_workspace` | Creates `/tmp/.agents-gwt/ws-*` (use in `withAspect` before, or in `given`) |
| `cleanup_workspace` | Remove the temp workspace (use in `withAspect` after) |
| `the_prompt(text)` | Curried `given` — sets `this.prompt` |
| `executing_the_agent` | `when` — runs `this.agent.run(...)` |
| `ClaudeAgentResult` | Type for Claude Code's JSON result (`is_error`, `result`, `num_turns`, `total_cost_usd`, …) |
| `ClaudeCredentials` / `resolveClaudeCredentials()` | Credential source for the Claude container (token, API key, or file) |
| Export | Role |
| -------------------------------------------------- | ------------------------------------------------------------------------------------------- |
| `AgentContext` | Extensible context type (`workspace`, `prompt`, `agent`, `image`, …) |
| `agent(opts)` | `withAspect` before — `{ name: "cursor" \| "claude", model?, image? }` |
| `buildAgentImage(name)` | Suite setup — builds base + agent image (use in vitest `globalSetup`) |
| `buildBaseImage()` | Builds `agent-gwt/base:local` only |
| `buildDockerImage(...)` | Builds an arbitrary Dockerfile (e.g. toolchain overlay) |
| `a_workspace` | Creates `/tmp/.agents-gwt/ws-*` (use in `withAspect` before, or in `given`) |
| `copy_to_workspace(workspace, globs, options?)` | Copy glob-matched files into `workspace` from the current spec directory (`from`, `base`) |
| `cleanup_workspace` | Remove the temp workspace (use in `withAspect` after) |
| `the_prompt(text)` | Curried `given` — sets `this.prompt` |
| `executing_the_agent` | `when` — runs `this.agent.run(...)` |
| `ClaudeAgentResult` | Type for Claude Code's JSON result (`is_error`, `result`, `num_turns`, `total_cost_usd`, …) |
| `ClaudeCredentials` / `resolveClaudeCredentials()` | Credential source for the Claude container (token, API key, or file) |

## Isolation notes

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@
"vite": "catalog:",
"vite-plus": "catalog:",
"vitest": "catalog:",
"vitest-gwt": "^4.1.3",
"vitest-gwt": "^4.1.4",
"wireit": "^0.14.13"
},
"peerDependencies": {
Expand Down
10 changes: 5 additions & 5 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion pnpm-workspace.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ catalog:
vitest: 4.1.11
"@vitest/coverage-v8": 4.1.11
minimumReleaseAgeExclude:
- vitest-gwt@4.1.3
- vitest-gwt@4.1.4
overrides:
vite@*: "catalog:"
vitest@*: "catalog:"
Expand Down
1 change: 1 addition & 0 deletions src/given/copy_to_workspace.fixtures/hello.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
hello
Loading