@bastani/atomic 0.9.25-alpha.2 → 0.9.25-alpha.3
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 +27 -0
- package/dist/builtin/intercom/package.json +2 -2
- package/dist/builtin/mcp/CHANGELOG.md +13 -0
- package/dist/builtin/mcp/index.bundle.mjs +489 -208
- package/dist/builtin/mcp/package.json +2 -2
- package/dist/builtin/subagents/package.json +3 -3
- package/dist/builtin/web-access/package.json +2 -2
- package/dist/builtin/workflows/CHANGELOG.md +12 -0
- package/dist/builtin/workflows/builtin/{chunk-e1254zvd.js → chunk-kfsnh3w7.js} +40 -12
- package/dist/builtin/workflows/builtin/{chunk-cpw5gtzg.js → chunk-rqmasd3d.js} +1 -1
- package/dist/builtin/workflows/builtin/{chunk-qy6g0baf.js → chunk-wz783pvd.js} +1 -1
- package/dist/builtin/workflows/builtin/goal.js +2 -2
- package/dist/builtin/workflows/builtin/index.js +3 -3
- package/dist/builtin/workflows/builtin/ralph.js +2 -2
- package/dist/builtin/workflows/package.json +2 -2
- package/dist/builtin/workflows/src/extension/index.bundle.mjs +472 -88
- package/dist/builtin/workflows/src/index.js +65 -28
- package/dist/core/agent-session-accessors.d.ts.map +1 -1
- package/dist/core/agent-session-accessors.js +7 -0
- package/dist/core/agent-session-accessors.js.map +1 -1
- package/dist/core/agent-session-extension-bindings.d.ts.map +1 -1
- package/dist/core/agent-session-extension-bindings.js +16 -2
- package/dist/core/agent-session-extension-bindings.js.map +1 -1
- package/dist/core/agent-session-methods.d.ts +7 -1
- package/dist/core/agent-session-methods.d.ts.map +1 -1
- package/dist/core/agent-session-methods.js.map +1 -1
- package/dist/core/agent-session-state.d.ts.map +1 -1
- package/dist/core/agent-session-state.js +3 -2
- package/dist/core/agent-session-state.js.map +1 -1
- package/dist/core/agent-session-types.d.ts +1 -0
- package/dist/core/agent-session-types.d.ts.map +1 -1
- package/dist/core/agent-session-types.js.map +1 -1
- package/dist/core/agent-session.d.ts +3 -0
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +6 -0
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/extensions/api-types.d.ts +3 -0
- package/dist/core/extensions/api-types.d.ts.map +1 -1
- package/dist/core/extensions/api-types.js.map +1 -1
- package/dist/core/extensions/index.d.ts +2 -0
- package/dist/core/extensions/index.d.ts.map +1 -1
- package/dist/core/extensions/index.js +1 -0
- package/dist/core/extensions/index.js.map +1 -1
- package/dist/core/extensions/loader-api.d.ts.map +1 -1
- package/dist/core/extensions/loader-api.js +11 -0
- package/dist/core/extensions/loader-api.js.map +1 -1
- package/dist/core/extensions/loader-runtime.d.ts.map +1 -1
- package/dist/core/extensions/loader-runtime.js +3 -0
- package/dist/core/extensions/loader-runtime.js.map +1 -1
- package/dist/core/extensions/mcp-server-registry.d.ts.map +1 -1
- package/dist/core/extensions/mcp-server-registry.js +4 -0
- package/dist/core/extensions/mcp-server-registry.js.map +1 -1
- package/dist/core/extensions/runner.d.ts +3 -0
- package/dist/core/extensions/runner.d.ts.map +1 -1
- package/dist/core/extensions/runner.js +4 -0
- package/dist/core/extensions/runner.js.map +1 -1
- package/dist/core/extensions/runtime-types.d.ts +3 -0
- package/dist/core/extensions/runtime-types.d.ts.map +1 -1
- package/dist/core/extensions/runtime-types.js.map +1 -1
- package/dist/core/extensions/tool-types.d.ts +1 -0
- package/dist/core/extensions/tool-types.d.ts.map +1 -1
- package/dist/core/extensions/tool-types.js.map +1 -1
- package/dist/core/extensions/workflow-run-control-hub.d.ts +23 -0
- package/dist/core/extensions/workflow-run-control-hub.d.ts.map +1 -0
- package/dist/core/extensions/workflow-run-control-hub.js +57 -0
- package/dist/core/extensions/workflow-run-control-hub.js.map +1 -0
- package/dist/core/extensions/workflow-run-control.d.ts +163 -0
- package/dist/core/extensions/workflow-run-control.d.ts.map +1 -0
- package/dist/core/extensions/workflow-run-control.js +73 -0
- package/dist/core/extensions/workflow-run-control.js.map +1 -0
- package/dist/core/mcp-servers.d.ts +4 -1
- package/dist/core/mcp-servers.d.ts.map +1 -1
- package/dist/core/mcp-servers.js.map +1 -1
- package/dist/core/remote-catalog-provider.d.ts.map +1 -1
- package/dist/core/remote-catalog-provider.js +4 -9
- package/dist/core/remote-catalog-provider.js.map +1 -1
- package/dist/core/sdk.js +1 -0
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/tool-loadout.d.ts +1 -0
- package/dist/core/tool-loadout.d.ts.map +1 -1
- package/dist/core/tool-loadout.js +3 -0
- package/dist/core/tool-loadout.js.map +1 -1
- package/dist/core/tools/bash.d.ts.map +1 -1
- package/dist/core/tools/bash.js +11 -30
- package/dist/core/tools/bash.js.map +1 -1
- package/dist/extensions/codemode/execute.d.ts.map +1 -1
- package/dist/extensions/codemode/execute.js +26 -0
- package/dist/extensions/codemode/execute.js.map +1 -1
- package/dist/extensions/codemode/renderer.d.ts.map +1 -1
- package/dist/extensions/codemode/renderer.js +38 -23
- package/dist/extensions/codemode/renderer.js.map +1 -1
- package/dist/extensions/codemode/tool.d.ts.map +1 -1
- package/dist/extensions/codemode/tool.js +22 -10
- package/dist/extensions/codemode/tool.js.map +1 -1
- package/dist/extensions/tool-search/tool.d.ts +1 -1
- package/dist/extensions/tool-search/tool.d.ts.map +1 -1
- package/dist/extensions/tool-search/tool.js +3 -13
- package/dist/extensions/tool-search/tool.js.map +1 -1
- package/dist/index-extensions.d.ts +2 -1
- package/dist/index-extensions.d.ts.map +1 -1
- package/dist/index-extensions.js +1 -1
- package/dist/index-extensions.js.map +1 -1
- package/dist/modes/interactive/components/visual-truncate.d.ts +27 -3
- package/dist/modes/interactive/components/visual-truncate.d.ts.map +1 -1
- package/dist/modes/interactive/components/visual-truncate.js +39 -6
- package/dist/modes/interactive/components/visual-truncate.js.map +1 -1
- package/docs/extensions/events.md +2 -0
- package/docs/mcp-servers.md +47 -0
- package/docs/providers.md +8 -0
- package/docs/sdk/reference.md +16 -0
- package/docs/sdk.md +67 -1
- package/docs/settings.md +2 -0
- package/docs/tools.md +6 -0
- package/docs/workflows/operations.md +3 -1
- package/examples/extensions/built-in-tool-renderer.ts +16 -40
- package/examples/extensions/minimal-mode.ts +28 -108
- package/npm-shrinkwrap.json +69 -69
- package/package.json +8 -8
package/docs/mcp-servers.md
CHANGED
|
@@ -7,6 +7,12 @@ description: "Configure MCP servers, discover tools, and authenticate connection
|
|
|
7
7
|
|
|
8
8
|
Atomic includes MCP support in both npm and binary installations. No separate extension install is needed. Use `/mcp` to inspect servers or `/mcp setup` to configure them.
|
|
9
9
|
|
|
10
|
+
## Quick setup
|
|
11
|
+
|
|
12
|
+
Run `atomic`, then use `/mcp setup` to create configuration or preview imports from another client. For manual setup, add a local server's `command` and `args`, or a remote server's `url`, to one of the configuration files below. Restart Atomic after editing a file manually.
|
|
13
|
+
|
|
14
|
+
Use `/mcp` to inspect connections and `/mcp tools` to list tools. Servers connect lazily by default; discovery or a tool call can establish the connection. Use `/mcp reconnect my-server` to reconnect a configured server. For a server that requires OAuth, run `/mcp-auth my-server` before unattended work.
|
|
15
|
+
|
|
10
16
|
## Configure a server
|
|
11
17
|
|
|
12
18
|
Put shared project configuration in `.mcp.json` at your project root:
|
|
@@ -55,6 +61,17 @@ The Atomic agent directory can be relocated with `ATOMIC_CODING_AGENT_DIR`. Use
|
|
|
55
61
|
|
|
56
62
|
Servers connect lazily by default. Adding a server does not require an immediate connection at startup.
|
|
57
63
|
|
|
64
|
+
### Configuration rules
|
|
65
|
+
|
|
66
|
+
- `command` is one executable and `args` contains its arguments, not one shell command string. Use `cwd` for the server's working directory and `env` for its environment.
|
|
67
|
+
- Remote servers use `url` and optional `headers`. Keep credentials out of shared files; use environment variable references or `bearerTokenEnv` for bearer authentication.
|
|
68
|
+
- Put personal servers and credentials in user-level configuration. Use project configuration only for servers the project requires, in trusted projects.
|
|
69
|
+
- Use `timeoutMs` for the per-request inactivity timeout. Progress notifications reset it.
|
|
70
|
+
|
|
71
|
+
### Import configuration from another client
|
|
72
|
+
|
|
73
|
+
Clients that use a top-level `mcpServers` object can share the same server entries. Copy compatible entries into `.mcp.json`, or use `/mcp setup` to preview detected imports before saving. Check executable paths, working directories, and required environment variables on the current machine. Restart Atomic after manual changes, then use `/mcp` to diagnose connection errors.
|
|
74
|
+
|
|
58
75
|
## Servers from packages and extensions
|
|
59
76
|
|
|
60
77
|
Installed [packages](/packages/authoring#mcp-servers) and extensions can contribute MCP servers. Contributed servers sit below all four configuration files: a server with the same name in any of them replaces the contributed entry completely. To turn a contributed server off, give its name an entry with only `disabled`:
|
|
@@ -86,6 +103,8 @@ mcp({ tool: "my_server_search", args: '{"query":"example"}' })
|
|
|
86
103
|
|
|
87
104
|
Use the tool names returned by discovery. `args` is a JSON string, not an object. Search may connect configured servers when their metadata has not yet been cached.
|
|
88
105
|
|
|
106
|
+
Tool names replace punctuation with `_` and receive a leading `_` if they start with a digit. Names longer than 64 characters and tools whose names collide after this conversion receive a deterministic hash suffix. Both gateway discovery and direct tools use these names; call the returned name rather than constructing one yourself. Server names that differ only in `-` and `_` are rejected within one configuration file. A higher-precedence file replaces a contributed or lower-precedence server with the same normalized name.
|
|
107
|
+
|
|
89
108
|
To expose a server's tools directly in the agent's tool list, add `"directTools": true` to that server's configuration. To expose only selected tools, set `directTools` to an array of the original MCP tool names. The default is gateway-only access.
|
|
90
109
|
|
|
91
110
|
In a headless SDK session, cached direct tools are available at startup, but discovery does not connect uncached lazy servers. Call the `mcp` gateway when you need them. Set the server's `lifecycle` to `"eager"` or `"keep-alive"` if it must connect during startup.
|
|
@@ -94,8 +113,36 @@ In a headless SDK session, cached direct tools are available at startup, but dis
|
|
|
94
113
|
|
|
95
114
|
For an OAuth server, run `/mcp-auth my-server` in an interactive session. You can also select the server in `/mcp` and press Enter or `Ctrl+A`. Run `/mcp logout my-server` to remove stored OAuth credentials and disconnect.
|
|
96
115
|
|
|
116
|
+
While sign-in waits for browser approval, Atomic displays the authorization URL as a terminal hyperlink with a Cmd/Ctrl+click hint. You can use it if the browser did not open automatically.
|
|
117
|
+
|
|
97
118
|
Automatic OAuth is opt-in through `settings.autoAuth`. Browser-based authorization requires an interactive session; authenticate before running unattended work.
|
|
98
119
|
|
|
120
|
+
Atomic registers OAuth clients as `atomic`. If a server requires a known client name, configure it with `oauth.clientName`:
|
|
121
|
+
|
|
122
|
+
```json
|
|
123
|
+
{ "mcpServers": { "figma": { "url": "https://mcp.figma.com/mcp", "oauth": { "clientName": "Claude Code" } } } }
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
The name is sent only during dynamic client registration. Run `/mcp logout figma` before signing in again to register under a changed name.
|
|
127
|
+
|
|
128
|
+
### Authenticate with a provider login
|
|
129
|
+
|
|
130
|
+
An HTTP server can use the token of a provider you have signed in to instead of MCP OAuth:
|
|
131
|
+
|
|
132
|
+
```json
|
|
133
|
+
{ "mcpServers": { "radius": { "url": "https://radius.example/mcp", "auth": { "provider": "radius" } } } }
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
Atomic reads the provider's current token for each request, so refreshes apply. MCP does not copy or store the token. If the server rejects it or the provider has no token, the connection fails with a message to run `/login <provider>`.
|
|
137
|
+
|
|
138
|
+
After signing in with `/login <provider>`, retry the MCP gateway call. Atomic reconnects with the current token, including for servers with cached tools; you do not need to run `/mcp reconnect`.
|
|
139
|
+
|
|
140
|
+
Because the token goes to the server's `url`, `auth.provider` has limits:
|
|
141
|
+
|
|
142
|
+
- It is accepted in the global `mcp.json` files and in servers registered by extensions. It is ignored, with a warning, in project `.mcp.json` and `.atomic/mcp.json`, in project-relative imports such as `.vscode/mcp.json`, and in package manifest servers.
|
|
143
|
+
- The URL must use `https`, or `http` on `localhost`, `127.0.0.1`, or `[::1]`.
|
|
144
|
+
- Atomic sends the token only to the server's origin. It follows `307` and `308` redirects within that origin and refuses every other redirect.
|
|
145
|
+
|
|
99
146
|
## Troubleshooting
|
|
100
147
|
|
|
101
148
|
- Run `/mcp` to check server status and `/mcp tools` to list available tools.
|
package/docs/providers.md
CHANGED
|
@@ -124,12 +124,20 @@ Anthropic fast mode delivers up to 2.5x higher output tokens per second at twice
|
|
|
124
124
|
|
|
125
125
|
Anthropic subscription auth is active for Claude Pro/Max accounts. Third-party harness usage draws from [extra usage](https://claude.ai/settings/usage) and is billed per token, not against Claude plan limits.
|
|
126
126
|
|
|
127
|
+
Run `/login anthropic` and choose subscription authentication. **Browser login (default)** uses a local callback and still accepts a pasted redirect URL. Choose **Copy code login (headless)** when your browser runs on another machine: complete sign-in in that browser, then paste the `code#state` value Anthropic displays into Atomic. This method does not need a reachable local callback.
|
|
128
|
+
|
|
127
129
|
For gateway-issued Anthropic bearer credentials, set `ANTHROPIC_AUTH_TOKEN` without `ANTHROPIC_API_KEY` or `ANTHROPIC_OAUTH_TOKEN`. A populated bearer token counts as configured Anthropic authentication, so `/model`, saved/default selection, cycling, RPC catalogs, and isolated model pickers keep Anthropic models available. Atomic sends it as `Authorization: Bearer …` for normal turns, branch summaries, and Verbatim Compaction without replacing caller-supplied custom headers.
|
|
128
130
|
|
|
129
131
|
Claude Opus 5 is available from the bundled/dynamic Anthropic and Amazon Bedrock catalogs. With bearer-only Anthropic auth, select the exact `anthropic/claude-opus-5-*` entry through `/model`; Bedrock uses its catalog-advertised inference profile. `xhigh` appears only when the chosen entry advertises it. Bedrock requests retain adaptive thinking, prompt caching, and AWS validation/error details from the provider runtime.
|
|
130
132
|
|
|
131
133
|
`ANTHROPIC_AUTH_TOKEN` is specifically for Anthropic-compatible gateways that require a bearer header. It does not synthesize an API key or `x-api-key`, and callers may still add independent custom headers/base URLs through `models.json` or an extension. Empty environment variables do not count as configured. If token and API-key sources are both configured, normal credential resolution rules apply; avoid setting both accidentally.
|
|
132
134
|
|
|
135
|
+
### Anthropic workload identity federation
|
|
136
|
+
|
|
137
|
+
With no Anthropic key, bearer token, or login configured, Atomic uses workload identity federation when `ANTHROPIC_FEDERATION_RULE_ID`, `ANTHROPIC_ORGANIZATION_ID`, and `ANTHROPIC_IDENTITY_TOKEN_FILE` are all set. The Anthropic SDK exchanges the identity token for a short-lived access token and refreshes it, re-reading the token file each time, so keep that file fresh in long sessions. `ANTHROPIC_SERVICE_ACCOUNT_ID` and `ANTHROPIC_WORKSPACE_ID` are sent when set.
|
|
138
|
+
|
|
139
|
+
Keys, `ANTHROPIC_AUTH_TOKEN`, and custom authorization headers take precedence. Federation applies only to the `anthropic` provider, not to other Anthropic-compatible providers.
|
|
140
|
+
|
|
133
141
|
### GitHub Copilot
|
|
134
142
|
|
|
135
143
|
- Press Enter for github.com, or enter your GitHub Enterprise Server domain
|
package/docs/sdk/reference.md
CHANGED
|
@@ -849,6 +849,22 @@ type EditDiffResult
|
|
|
849
849
|
SessionManager
|
|
850
850
|
SettingsManager
|
|
851
851
|
|
|
852
|
+
// Workflow run control (session.workflows)
|
|
853
|
+
type SessionWorkflows
|
|
854
|
+
type WorkflowRunSummary
|
|
855
|
+
type WorkflowRunDetail
|
|
856
|
+
type WorkflowRunControlOutcome
|
|
857
|
+
type WorkflowRunControlFailedRun
|
|
858
|
+
WorkflowRunControlError
|
|
859
|
+
WorkflowRunNotFoundError
|
|
860
|
+
WorkflowRunOwnershipError
|
|
861
|
+
WorkflowRunNotResumableError
|
|
862
|
+
WorkflowStageNotFoundError
|
|
863
|
+
WorkflowStageAmbiguousError
|
|
864
|
+
WorkflowStageResumeUnsupportedError
|
|
865
|
+
WorkflowRunDatabaseError
|
|
866
|
+
WorkflowRunControlUnavailableError
|
|
867
|
+
|
|
852
868
|
// Tool factories
|
|
853
869
|
createCodingTools
|
|
854
870
|
createReadOnlyTools
|
package/docs/sdk.md
CHANGED
|
@@ -210,7 +210,7 @@ To continue a saved run in another session:
|
|
|
210
210
|
|
|
211
211
|
1. Keep its definition and durable storage available.
|
|
212
212
|
2. Bind the new host.
|
|
213
|
-
3. Use `/workflow resume <run-id
|
|
213
|
+
3. Use `session.workflows.resume(runId)` ([Workflow run control](#workflow-run-control)), `/workflow resume <run-id>`, or the workflow tool's `resume` action.
|
|
214
214
|
|
|
215
215
|
Rebinding alone does not reopen a saved run. See [workflow operations](/workflows/operations) for inspection, graceful quit, and resume.
|
|
216
216
|
|
|
@@ -226,6 +226,69 @@ Rebinding host callbacks preserves pending MCP OAuth ownership. Authorization st
|
|
|
226
226
|
|
|
227
227
|
Web provider configuration caches are session-local. Configuration discovery and environment keys are unchanged; create a new session to pick up changed cached settings. GitHub extraction returns the owned clone path for use with file tools, so callers need not predict its directory.
|
|
228
228
|
|
|
229
|
+
### Workflow run control
|
|
230
|
+
|
|
231
|
+
`session.workflows` manages the workflow runs that this session owns, without slash commands or the model-facing `workflow` tool. It uses the same run-control service as the tool, so ownership and durability rules are identical: a run owned by another session, or executing in another live Atomic process, stays read-only here.
|
|
232
|
+
|
|
233
|
+
```typescript
|
|
234
|
+
interface SessionWorkflows {
|
|
235
|
+
listRuns(filter?: { status?: WorkflowRunFilterStatus }): Promise<readonly WorkflowRunSummary[]>;
|
|
236
|
+
getRun(runId: string): Promise<WorkflowRunDetail>;
|
|
237
|
+
getStages(runId: string, filter?: { status?: WorkflowStageStatus }): Promise<readonly WorkflowRunStageSummary[]>;
|
|
238
|
+
pause(runId: string, options?: { stageId?: string }): Promise<WorkflowRunControlOutcome>;
|
|
239
|
+
pause(target: { all: true }): Promise<WorkflowRunControlOutcome>;
|
|
240
|
+
quit(target: string | { all: true }): Promise<WorkflowRunControlOutcome>;
|
|
241
|
+
resume(runId: string, options?: { stageId?: string; message?: string }): Promise<WorkflowRunControlOutcome>;
|
|
242
|
+
}
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
`listRuns()` returns the same per-run data as `workflow status`: id, workflow name, status, active stages, and unanswered prompts (`awaitingInput`). Run IDs accept the full UUID or a unique 8-character prefix. `getRun()`, `getStages()`, `pause()` and `quit()` resolve a prefix against the runs this session holds and reject it at once when none matches, so pass the full UUID for a run recorded by an earlier process. `pause`, `quit` and `resume` resolve with the tool's acknowledged outcome: `action`, `runId` (`--all` for a batch), a `status` of `ok`, `running`, `paused`, `partial`, `noop` or `cancelled`, and a human-readable `message`. A `noop` means the request was understood and found nothing to change, such as pausing a run that already ended. A `partial` outcome means part of the request took effect. For `pause({ all: true })` and `quit({ all: true })`, `partial` means this call stopped at least one run and others are still active: `failedRuns` lists each run that is still active with its `runId`, a `reason` (for example `pause_failed` or `no_active_stages`) and a `message`, so check `outcome.status === "partial"` before treating a batch stop as complete. If active runs could not be stopped and this call stopped none, the call rejects with `WorkflowRunControlError` (`WORKFLOW_RUN_CONTROL_FAILED`) whose `failedRuns` carries the same list, even when other runs were already paused. Runs that are already paused or already ended are not failures, and a batch with no active run left to stop resolves as a `noop`. Call `getRun()` before retrying.
|
|
246
|
+
|
|
247
|
+
Pausing a run doesn't park a stage that is waiting on a prompt. Such a run reports `paused` and counts as already paused, because nothing in it advances on its own. If your host then answers that prompt, the stage can continue its turn before you call `resume`. To keep a paused run fully stopped, hold prompt answers until you resume it.
|
|
248
|
+
|
|
249
|
+
Requests that cannot be carried out reject with a subclass of `WorkflowRunControlError`. Branch on `code` or use `instanceof`:
|
|
250
|
+
|
|
251
|
+
| Class | `code` | Meaning |
|
|
252
|
+
| --- | --- | --- |
|
|
253
|
+
| `WorkflowRunNotFoundError` | `WORKFLOW_RUN_NOT_FOUND` | The run ID is unknown or malformed, or a prefix is ambiguous or matches no run in this session. |
|
|
254
|
+
| `WorkflowRunOwnershipError` | `WORKFLOW_RUN_OWNED_ELSEWHERE` | The run belongs to another session, or is running in another live Atomic process. Control it from its owner. |
|
|
255
|
+
| `WorkflowRunNotResumableError` | `WORKFLOW_RUN_NOT_RESUMABLE` | The run completed, was killed, or has no durable progress to resume. |
|
|
256
|
+
| `WorkflowStageNotFoundError` | `WORKFLOW_STAGE_NOT_FOUND` | `stageId` matches no stage of the run, or the stage has no control to pause. |
|
|
257
|
+
| `WorkflowStageAmbiguousError` | `WORKFLOW_STAGE_AMBIGUOUS` | `stageId` matches more than one stage; pass a more specific identifier. |
|
|
258
|
+
| `WorkflowStageResumeUnsupportedError` | `WORKFLOW_STAGE_RESUME_UNSUPPORTED` | A durable run resumes as a whole; call `resume()` without `stageId`. |
|
|
259
|
+
| `WorkflowRunDatabaseError` | `WORKFLOW_RUN_DATABASE` | Workflow durability is unavailable, including when the database cannot be inspected to check a run's owner during `pause()` or `quit()`. Restore the database, then inspect the run before retrying. |
|
|
260
|
+
| `WorkflowRunControlError` | `WORKFLOW_RUN_CONTROL_FAILED` | Another failure; `message` explains it. |
|
|
261
|
+
| `WorkflowRunControlUnavailableError` | `WORKFLOW_RUN_CONTROL_UNAVAILABLE` | The workflows package is disabled, or the session was disposed. |
|
|
262
|
+
|
|
263
|
+
Each error carries the `runId` when one applies. A refused batch also carries `failedRuns`.
|
|
264
|
+
|
|
265
|
+
A host with a stop button can pause its session's runs and resume the same runs later:
|
|
266
|
+
|
|
267
|
+
```typescript
|
|
268
|
+
import { createAgentSession, WorkflowRunControlError } from "@bastani/atomic";
|
|
269
|
+
|
|
270
|
+
const { session } = await createAgentSession();
|
|
271
|
+
|
|
272
|
+
async function onStopClicked() {
|
|
273
|
+
const outcome = await session.workflows.pause({ all: true });
|
|
274
|
+
showStatus(outcome.message);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
async function onResumeClicked() {
|
|
278
|
+
for (const run of await session.workflows.listRuns({ status: "paused" })) {
|
|
279
|
+
try {
|
|
280
|
+
const outcome = await session.workflows.resume(run.runId);
|
|
281
|
+
showStatus(`${run.name}: ${outcome.message}`);
|
|
282
|
+
} catch (error) {
|
|
283
|
+
if (!(error instanceof WorkflowRunControlError)) throw error;
|
|
284
|
+
showStatus(`${run.name}: ${error.code}: ${error.message}`);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
`pause` holds the live run and keeps it resumable in this process; `quit` retires the executor at a durability boundary so a later process can resume it. After `quit`, or when resuming a run from a previous process, `resume` follows the durable resume path: keep the workflow definition and durable storage available. See [pausing, quitting, and resuming](/workflows/operations#pausing-quitting-and-resuming) for what each action guarantees. To watch runs change instead of polling `listRuns()`, use the observation contract in [Workflow activity and lifecycle hooks](/extensions/events#workflow-activity-and-lifecycle-hooks).
|
|
291
|
+
|
|
229
292
|
### Workflow and subagent children
|
|
230
293
|
|
|
231
294
|
Children inherit the invoking session's model/auth runtime, settings, agent directory, human-input callbacks, and diagnostic sink. Choosing a child model or fallback does not switch to global credentials.
|
|
@@ -275,6 +338,9 @@ interface AgentSession {
|
|
|
275
338
|
sessionFile: string | undefined;
|
|
276
339
|
sessionId: string;
|
|
277
340
|
|
|
341
|
+
// Typed control of the workflow runs this session owns
|
|
342
|
+
readonly workflows: SessionWorkflows;
|
|
343
|
+
|
|
278
344
|
// Model and thinking control
|
|
279
345
|
setModel(model: Model): Promise<void>;
|
|
280
346
|
setThinkingLevel(level: ThinkingLevel): void;
|
package/docs/settings.md
CHANGED
|
@@ -476,6 +476,8 @@ A list containing only modifiers starts from Atomic's standard defaults. Modifie
|
|
|
476
476
|
|
|
477
477
|
Plain names establish a replacement list before its modifiers apply. A project list of only modifiers layers over the global selection. A project list with plain names, or an empty list, replaces the global selection. For example, global `["read", "bash"]` plus project `["-bash", "+ls"]` resolves to `["read", "ls"]`.
|
|
478
478
|
|
|
479
|
+
`/reload` enables tools newly added to `defaultTools`. It does not disable removed tools or re-enable unchanged tools you turned off during the session. Explicit `--tools`, `--no-tools`, and `--no-builtin-tools` choices override the setting on reload too; excluded tools remain excluded.
|
|
480
|
+
|
|
479
481
|
`codemode` and `tool_search` are built-in extension tools registered inactive. Add `"+codemode"` or `"+tool_search"` to `defaultTools` to activate them alongside ordinary defaults. See [codemode](/tools#codemode) and [tool search](/tools#tool_search) for usage and safety boundaries.
|
|
480
482
|
|
|
481
483
|
### Sessions
|
package/docs/tools.md
CHANGED
|
@@ -41,16 +41,22 @@ Use `text(value)`, `image(dataUrlOrImageContent)`, `console.log(...)`, or a top-
|
|
|
41
41
|
return await tools.read({ path: "README.md" });
|
|
42
42
|
```
|
|
43
43
|
|
|
44
|
+
`image()` accepts base64 PNG, JPEG, GIF, or WebP data URLs and MCP image content. It detects the MIME type from the data, ignores an incorrect supplied type, and rejects malformed base64 or unsupported formats before returning an image. Remote HTTP image URLs are not supported; fetch the image through a permitted tool first.
|
|
45
|
+
|
|
44
46
|
There is no default deadline. Text output defaults to 10,000 estimated tokens; longer output keeps its start and end and, when saving succeeds, names a temporary file containing the full text. Failed scripts retain partial output and report the error. Unawaited calls are cancelled when a script ends; await work you need to finish.
|
|
45
47
|
|
|
46
48
|
Tools with `outputSchema` return their `structuredContent` to scripts, including structured error results. Other tools return text and throw on errors. Completed `bash` calls provide structured output as described in the [SDK reference](/sdk/reference#bash-tool-behavior).
|
|
47
49
|
|
|
48
50
|
`ALL_TOOLS`, `searchTools(query, { limit, namespace })`, and `describeTool(name)` discover the currently permitted tools. `store(key, value)` and `load(key)` preserve JSON values across successful calls on the current session branch, including after resume. Failed scripts discard store writes, not tool side effects. `models.getModelsOfType`, `getAvailableOfType`, and `getModelOfType` expose public catalog metadata; `models.classify(model, context)` calls a registered classifier with host-resolved credentials, at most four concurrently. Reported classifier usage contributes to the script's session cost.
|
|
49
51
|
|
|
52
|
+
Use `await describeNamespace(name)` to retrieve a callable tool namespace's `{ name, description?, instructions?, tools }`, or `undefined` when it is unavailable. Namespace instructions are returned on request rather than included in inline listings. `searchTools()` also searches those instructions; pass `{ namespace: name }` to restrict the search.
|
|
53
|
+
|
|
50
54
|
Set [codemode settings](/settings#tools) to control inline declaration size and whether ordinary direct tool declarations remain visible. Atomic's existing [MCP host](/mcp-servers) remains the only MCP connection/configuration owner: codemode calls the permitted gateway or direct tools, not a second MCP host. Use MCP discovery for server tools not yet registered in the session.
|
|
51
55
|
|
|
52
56
|
## `tool_search`
|
|
53
57
|
|
|
58
|
+
Deferred tools and their namespaces are omitted from codemode's inline catalog. Discover them with `searchTools()`, `describeNamespace()`, or `ALL_TOOLS`; `tool_search` can load matching registered tools for direct calls.
|
|
59
|
+
|
|
54
60
|
`tool_search` is inactive by default. Enable it with `"defaultTools": ["+tool_search"]` or include it in `--tools`. It ranks currently registered `codemode` and `deferred` tools by their metadata and activates matching tools for the next model call. It does not bypass session tool restrictions or replace MCP server discovery.
|
|
55
61
|
|
|
56
62
|
## `code_search`
|
|
@@ -372,6 +372,8 @@ This reopens only the conversation. The workflow DAG and terminal stage snapshot
|
|
|
372
372
|
|
|
373
373
|
Workflow stages and their subagent transcripts are excluded from ordinary `/resume`, `atomic -r`, `--continue`, and global history. Use workflow inspection and resume commands for stages; terminal subagents remain transcript artifacts, not resumable children. An explicit `--session` file path can still open a stage transcript. Older unmarked workflow sessions may remain in ordinary history.
|
|
374
374
|
|
|
375
|
+
Embedding hosts can run the same pause, quit, and resume actions programmatically with `session.workflows`; see [Workflow run control](/sdk#workflow-run-control).
|
|
376
|
+
|
|
375
377
|
## Workflow activity for extensions
|
|
376
378
|
|
|
377
379
|
Extensions can subscribe with `ctx.observeWorkflowActivity(observer)` and use the typed `workflow_lifecycle`, `workflow_activity_changed`, `workflow_stage_completed`, and `workflow_heartbeat` hooks. See [Workflow activity and lifecycle hooks](/extensions/events#workflow-activity-and-lifecycle-hooks) for the public types and subscription example.
|
|
@@ -470,7 +472,7 @@ If a resume continuation reports `cleanup skipped: database admission unavailabl
|
|
|
470
472
|
|
|
471
473
|
While root registration is pending, status shows `pending` with phase `starting`, not healthy execution. Status listing and exact-run detail include `phase`, `phaseAgeMs`, `dependencyError`, and `lastProgressAt`. Last progress records admission or a tracked node starting/settling, not polling or a heartbeat. Database failures report `blocked_dependency`; a paused run still reports `paused`. Starting and dependency-blocked runs do not emit "still running" heartbeats.
|
|
472
474
|
|
|
473
|
-
For an admitted live executor with no active stage or tool, pause installs a local barrier immediately and acknowledges within 500ms of starting database settlement. The acknowledgement and status distinguish `observed` from `durable` control persistence. If confirmation is still pending at acknowledgement, settlement continues in the background for up to 10 seconds total. Status becomes `durable` on success; only a settlement failure or deadline reports `blocked_dependency`. `observed` is not proof of a saved pause and does not guarantee cross-process resume. During registration, pause acknowledges immediately while database settlement continues in the background. After registration, resume allows up to 10 seconds for database confirmation before releasing the barrier. A failed resume leaves the executor paused; restore database availability and retry the same run. These limits do not cover stage cancellation or arbitrary external I/O.
|
|
475
|
+
For an admitted live executor with no active stage or tool, pause installs a local barrier immediately and acknowledges within 500ms of starting database settlement. The acknowledgement and status distinguish `observed` from `durable` control persistence. If confirmation is still pending at acknowledgement, settlement continues in the background for up to 10 seconds total. Status becomes `durable` on success; only a settlement failure or deadline reports `blocked_dependency`. `observed` is not proof of a saved pause and does not guarantee cross-process resume. During registration, pause acknowledges immediately while database settlement continues in the background. After registration, resume allows up to 10 seconds for database confirmation before releasing the barrier, and a resume requested while the pause's confirmation is still pending waits for it within that same 10 seconds instead of cancelling it. A failed resume leaves the executor paused; restore database availability and retry the same run. These limits do not cover stage cancellation or arbitrary external I/O.
|
|
474
476
|
|
|
475
477
|
If registration times out or reports an unavailable database while the run is paused, restore the existing database without resetting data, then resume that same run in the same Atomic process. Resume retries registration with the original identity and releases the existing executor once; repeated concurrent resume requests share the retry and do not start another executor. If the run was not paused, admission failure ends the attempt. A non-retryable admission rejection, such as a permission error, is reported on resume instead of retrying the live executor. Retain the full run ID and inspect it before retrying. Completed checkpoints remain reusable, but an external side effect whose checkpoint was never confirmed can still repeat after a crash; make such operations idempotent.
|
|
476
478
|
|
|
@@ -12,8 +12,8 @@
|
|
|
12
12
|
*
|
|
13
13
|
* How it works:
|
|
14
14
|
* - registerTool() with the same name as a built-in replaces it entirely
|
|
15
|
-
* - We create
|
|
16
|
-
* and
|
|
15
|
+
* - We create the original definitions via createReadToolDefinition(), etc.
|
|
16
|
+
* and override only their renderers
|
|
17
17
|
* - renderCall() controls what's shown when the tool is invoked
|
|
18
18
|
* - renderResult() controls what's shown after execution completes
|
|
19
19
|
* - renderShell: "self" lets a tool render its own outer shell instead of
|
|
@@ -26,23 +26,21 @@
|
|
|
26
26
|
*/
|
|
27
27
|
|
|
28
28
|
import type { BashToolDetails, EditToolDetails, ExtensionAPI, ReadToolDetails } from "@bastani/atomic";
|
|
29
|
-
import {
|
|
29
|
+
import {
|
|
30
|
+
createBashToolDefinition,
|
|
31
|
+
createEditToolDefinition,
|
|
32
|
+
createReadToolDefinition,
|
|
33
|
+
createWriteToolDefinition,
|
|
34
|
+
} from "@bastani/atomic";
|
|
30
35
|
import { Text } from "@earendil-works/pi-tui";
|
|
31
36
|
|
|
32
37
|
export default function (pi: ExtensionAPI) {
|
|
33
38
|
const cwd = process.cwd();
|
|
34
39
|
|
|
35
40
|
// --- Read tool: show path and line count ---
|
|
36
|
-
const originalRead =
|
|
41
|
+
const originalRead = createReadToolDefinition(cwd);
|
|
37
42
|
pi.registerTool({
|
|
38
|
-
|
|
39
|
-
label: "read",
|
|
40
|
-
description: originalRead.description,
|
|
41
|
-
parameters: originalRead.parameters,
|
|
42
|
-
|
|
43
|
-
async execute(toolCallId, params, signal, onUpdate) {
|
|
44
|
-
return originalRead.execute(toolCallId, params, signal, onUpdate);
|
|
45
|
-
},
|
|
43
|
+
...originalRead,
|
|
46
44
|
|
|
47
45
|
renderCall(args, theme, _context) {
|
|
48
46
|
let text = theme.fg("toolTitle", theme.bold("read "));
|
|
@@ -92,16 +90,9 @@ export default function (pi: ExtensionAPI) {
|
|
|
92
90
|
});
|
|
93
91
|
|
|
94
92
|
// --- Bash tool: show command and exit code ---
|
|
95
|
-
const originalBash =
|
|
93
|
+
const originalBash = createBashToolDefinition(cwd);
|
|
96
94
|
pi.registerTool({
|
|
97
|
-
|
|
98
|
-
label: "bash",
|
|
99
|
-
description: originalBash.description,
|
|
100
|
-
parameters: originalBash.parameters,
|
|
101
|
-
|
|
102
|
-
async execute(toolCallId, params, signal, onUpdate) {
|
|
103
|
-
return originalBash.execute(toolCallId, params, signal, onUpdate);
|
|
104
|
-
},
|
|
95
|
+
...originalBash,
|
|
105
96
|
|
|
106
97
|
renderCall(args, theme, _context) {
|
|
107
98
|
let text = theme.fg("toolTitle", theme.bold("$ "));
|
|
@@ -151,17 +142,9 @@ export default function (pi: ExtensionAPI) {
|
|
|
151
142
|
});
|
|
152
143
|
|
|
153
144
|
// --- Edit tool: show path and diff stats ---
|
|
154
|
-
const originalEdit =
|
|
145
|
+
const originalEdit = createEditToolDefinition(cwd);
|
|
155
146
|
pi.registerTool({
|
|
156
|
-
|
|
157
|
-
label: "edit",
|
|
158
|
-
description: originalEdit.description,
|
|
159
|
-
parameters: originalEdit.parameters,
|
|
160
|
-
renderShell: "self",
|
|
161
|
-
|
|
162
|
-
async execute(toolCallId, params, signal, onUpdate) {
|
|
163
|
-
return originalEdit.execute(toolCallId, params, signal, onUpdate);
|
|
164
|
-
},
|
|
147
|
+
...originalEdit,
|
|
165
148
|
|
|
166
149
|
renderCall(args, theme, _context) {
|
|
167
150
|
let text = theme.fg("toolTitle", theme.bold("edit "));
|
|
@@ -216,16 +199,9 @@ export default function (pi: ExtensionAPI) {
|
|
|
216
199
|
});
|
|
217
200
|
|
|
218
201
|
// --- Write tool: show path and size ---
|
|
219
|
-
const originalWrite =
|
|
202
|
+
const originalWrite = createWriteToolDefinition(cwd);
|
|
220
203
|
pi.registerTool({
|
|
221
|
-
|
|
222
|
-
label: "write",
|
|
223
|
-
description: originalWrite.description,
|
|
224
|
-
parameters: originalWrite.parameters,
|
|
225
|
-
|
|
226
|
-
async execute(toolCallId, params, signal, onUpdate) {
|
|
227
|
-
return originalWrite.execute(toolCallId, params, signal, onUpdate);
|
|
228
|
-
},
|
|
204
|
+
...originalWrite,
|
|
229
205
|
|
|
230
206
|
renderCall(args, theme, _context) {
|
|
231
207
|
let text = theme.fg("toolTitle", theme.bold("write "));
|
|
@@ -18,13 +18,13 @@
|
|
|
18
18
|
|
|
19
19
|
import type { ExtensionAPI } from "@bastani/atomic";
|
|
20
20
|
import {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
21
|
+
createBashToolDefinition,
|
|
22
|
+
createEditToolDefinition,
|
|
23
|
+
createFindToolDefinition,
|
|
24
|
+
createLsToolDefinition,
|
|
25
|
+
createReadToolDefinition,
|
|
26
|
+
createSearchToolDefinition,
|
|
27
|
+
createWriteToolDefinition,
|
|
28
28
|
} from "@bastani/atomic";
|
|
29
29
|
import { Text } from "@earendil-works/pi-tui";
|
|
30
30
|
import { homedir } from "os";
|
|
@@ -40,45 +40,26 @@ function shortenPath(path: string): string {
|
|
|
40
40
|
return path;
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
-
// Cache for built-in tools by cwd
|
|
44
|
-
const toolCache = new Map<string, ReturnType<typeof createBuiltInTools>>();
|
|
45
|
-
|
|
46
43
|
function createBuiltInTools(cwd: string) {
|
|
47
44
|
return {
|
|
48
|
-
read:
|
|
49
|
-
bash:
|
|
50
|
-
edit:
|
|
51
|
-
write:
|
|
52
|
-
find:
|
|
53
|
-
|
|
54
|
-
ls:
|
|
45
|
+
read: createReadToolDefinition(cwd),
|
|
46
|
+
bash: createBashToolDefinition(cwd),
|
|
47
|
+
edit: createEditToolDefinition(cwd),
|
|
48
|
+
write: createWriteToolDefinition(cwd),
|
|
49
|
+
find: createFindToolDefinition(cwd),
|
|
50
|
+
search: createSearchToolDefinition(cwd),
|
|
51
|
+
ls: createLsToolDefinition(cwd),
|
|
55
52
|
};
|
|
56
53
|
}
|
|
57
54
|
|
|
58
|
-
function getBuiltInTools(cwd: string) {
|
|
59
|
-
let tools = toolCache.get(cwd);
|
|
60
|
-
if (!tools) {
|
|
61
|
-
tools = createBuiltInTools(cwd);
|
|
62
|
-
toolCache.set(cwd, tools);
|
|
63
|
-
}
|
|
64
|
-
return tools;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
55
|
export default function (pi: ExtensionAPI) {
|
|
56
|
+
const tools = createBuiltInTools(process.cwd());
|
|
57
|
+
|
|
68
58
|
// =========================================================================
|
|
69
59
|
// Read Tool
|
|
70
60
|
// =========================================================================
|
|
71
61
|
pi.registerTool({
|
|
72
|
-
|
|
73
|
-
label: "read",
|
|
74
|
-
description:
|
|
75
|
-
"Read the contents of a file. Supports text files and images (jpg, png, gif, webp). Images are sent as attachments. For text files, output is truncated to 2000 lines or 50KB (whichever is hit first). Use offset/limit for large files.",
|
|
76
|
-
parameters: getBuiltInTools(process.cwd()).read.parameters,
|
|
77
|
-
|
|
78
|
-
async execute(toolCallId, params, signal, onUpdate, ctx) {
|
|
79
|
-
const tools = getBuiltInTools(ctx.cwd);
|
|
80
|
-
return tools.read.execute(toolCallId, params, signal, onUpdate);
|
|
81
|
-
},
|
|
62
|
+
...tools.read,
|
|
82
63
|
|
|
83
64
|
renderCall(args, theme, _context) {
|
|
84
65
|
const path = shortenPath(args.path || "");
|
|
@@ -116,16 +97,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
116
97
|
// Bash Tool
|
|
117
98
|
// =========================================================================
|
|
118
99
|
pi.registerTool({
|
|
119
|
-
|
|
120
|
-
label: "bash",
|
|
121
|
-
description:
|
|
122
|
-
"Execute a bash command in the current working directory. Returns stdout and stderr. Output is truncated to last 2000 lines or 50KB (whichever is hit first).",
|
|
123
|
-
parameters: getBuiltInTools(process.cwd()).bash.parameters,
|
|
124
|
-
|
|
125
|
-
async execute(toolCallId, params, signal, onUpdate, ctx) {
|
|
126
|
-
const tools = getBuiltInTools(ctx.cwd);
|
|
127
|
-
return tools.bash.execute(toolCallId, params, signal, onUpdate);
|
|
128
|
-
},
|
|
100
|
+
...tools.bash,
|
|
129
101
|
|
|
130
102
|
renderCall(args, theme, _context) {
|
|
131
103
|
const command = args.command || "...";
|
|
@@ -165,16 +137,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
165
137
|
// Write Tool
|
|
166
138
|
// =========================================================================
|
|
167
139
|
pi.registerTool({
|
|
168
|
-
|
|
169
|
-
label: "write",
|
|
170
|
-
description:
|
|
171
|
-
"Write content to a file. Creates the file if it doesn't exist, overwrites if it does. Automatically creates parent directories.",
|
|
172
|
-
parameters: getBuiltInTools(process.cwd()).write.parameters,
|
|
173
|
-
|
|
174
|
-
async execute(toolCallId, params, signal, onUpdate, ctx) {
|
|
175
|
-
const tools = getBuiltInTools(ctx.cwd);
|
|
176
|
-
return tools.write.execute(toolCallId, params, signal, onUpdate);
|
|
177
|
-
},
|
|
140
|
+
...tools.write,
|
|
178
141
|
|
|
179
142
|
renderCall(args, theme, _context) {
|
|
180
143
|
const path = shortenPath(args.path || "");
|
|
@@ -207,16 +170,8 @@ export default function (pi: ExtensionAPI) {
|
|
|
207
170
|
// Edit Tool
|
|
208
171
|
// =========================================================================
|
|
209
172
|
pi.registerTool({
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
description:
|
|
213
|
-
"Edit a file by replacing exact text. The oldText must match exactly (including whitespace). Use this for precise, surgical edits.",
|
|
214
|
-
parameters: getBuiltInTools(process.cwd()).edit.parameters,
|
|
215
|
-
|
|
216
|
-
async execute(toolCallId, params, signal, onUpdate, ctx) {
|
|
217
|
-
const tools = getBuiltInTools(ctx.cwd);
|
|
218
|
-
return tools.edit.execute(toolCallId, params, signal, onUpdate);
|
|
219
|
-
},
|
|
173
|
+
...tools.edit,
|
|
174
|
+
renderShell: "default",
|
|
220
175
|
|
|
221
176
|
renderCall(args, theme, _context) {
|
|
222
177
|
const path = shortenPath(args.path || "");
|
|
@@ -252,16 +207,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
252
207
|
// Find Tool
|
|
253
208
|
// =========================================================================
|
|
254
209
|
pi.registerTool({
|
|
255
|
-
|
|
256
|
-
label: "find",
|
|
257
|
-
description:
|
|
258
|
-
"Find files by name pattern (glob). Searches recursively from the specified path. Output limited to 200 results.",
|
|
259
|
-
parameters: getBuiltInTools(process.cwd()).find.parameters,
|
|
260
|
-
|
|
261
|
-
async execute(toolCallId, params, signal, onUpdate, ctx) {
|
|
262
|
-
const tools = getBuiltInTools(ctx.cwd);
|
|
263
|
-
return tools.find.execute(toolCallId, params, signal, onUpdate);
|
|
264
|
-
},
|
|
210
|
+
...tools.find,
|
|
265
211
|
|
|
266
212
|
renderCall(args, theme, _context) {
|
|
267
213
|
const pattern = args.pattern || "";
|
|
@@ -307,34 +253,17 @@ export default function (pi: ExtensionAPI) {
|
|
|
307
253
|
});
|
|
308
254
|
|
|
309
255
|
// =========================================================================
|
|
310
|
-
//
|
|
256
|
+
// Search Tool
|
|
311
257
|
// =========================================================================
|
|
312
258
|
pi.registerTool({
|
|
313
|
-
|
|
314
|
-
label: "grep",
|
|
315
|
-
description:
|
|
316
|
-
"Search file contents by regex pattern. Uses ripgrep for fast searching. Output limited to 200 matches.",
|
|
317
|
-
parameters: getBuiltInTools(process.cwd()).grep.parameters,
|
|
318
|
-
|
|
319
|
-
async execute(toolCallId, params, signal, onUpdate, ctx) {
|
|
320
|
-
const tools = getBuiltInTools(ctx.cwd);
|
|
321
|
-
return tools.grep.execute(toolCallId, params, signal, onUpdate);
|
|
322
|
-
},
|
|
259
|
+
...tools.search,
|
|
323
260
|
|
|
324
261
|
renderCall(args, theme, _context) {
|
|
325
262
|
const pattern = args.pattern || "";
|
|
326
|
-
const
|
|
327
|
-
const glob = args.glob;
|
|
328
|
-
const limit = args.limit;
|
|
263
|
+
const paths = typeof args.paths === "string" ? args.paths : args.paths?.join(", ") || ".";
|
|
329
264
|
|
|
330
|
-
let text = `${theme.fg("toolTitle", theme.bold("
|
|
331
|
-
text += theme.fg("toolOutput", ` in ${
|
|
332
|
-
if (glob) {
|
|
333
|
-
text += theme.fg("toolOutput", ` (${glob})`);
|
|
334
|
-
}
|
|
335
|
-
if (limit !== undefined) {
|
|
336
|
-
text += theme.fg("toolOutput", ` limit ${limit}`);
|
|
337
|
-
}
|
|
265
|
+
let text = `${theme.fg("toolTitle", theme.bold("search"))} ${theme.fg("accent", `/${pattern}/`)}`;
|
|
266
|
+
text += theme.fg("toolOutput", ` in ${shortenPath(paths)}`);
|
|
338
267
|
|
|
339
268
|
return new Text(text, 0, 0);
|
|
340
269
|
},
|
|
@@ -372,16 +301,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
372
301
|
// Ls Tool
|
|
373
302
|
// =========================================================================
|
|
374
303
|
pi.registerTool({
|
|
375
|
-
|
|
376
|
-
label: "ls",
|
|
377
|
-
description:
|
|
378
|
-
"List directory contents with file sizes. Shows files and directories with their sizes. Output limited to 500 entries.",
|
|
379
|
-
parameters: getBuiltInTools(process.cwd()).ls.parameters,
|
|
380
|
-
|
|
381
|
-
async execute(toolCallId, params, signal, onUpdate, ctx) {
|
|
382
|
-
const tools = getBuiltInTools(ctx.cwd);
|
|
383
|
-
return tools.ls.execute(toolCallId, params, signal, onUpdate);
|
|
384
|
-
},
|
|
304
|
+
...tools.ls,
|
|
385
305
|
|
|
386
306
|
renderCall(args, theme, _context) {
|
|
387
307
|
const path = shortenPath(args.path || ".");
|