agent-dir 0.5.0 → 1.0.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/README.md CHANGED
@@ -4,477 +4,96 @@
4
4
 
5
5
  </div>
6
6
 
7
- > **`agent-dir` turns a trusted local project directory into a controlled, authenticated MCP endpoint for AI agents.**
7
+ # agent-dir
8
8
 
9
- `agent-dir` is a cross-platform TypeScript CLI that exposes a local project to an AI agent through an authenticated MCP server. It provides controlled file access, targeted file patches, and explicitly allowlisted development commands, with optional tunneling through [Wormhole](https://wormhole.bar/) or [Cloudflare Tunnel](https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/).
9
+ **Expose a trusted local project to AI agents through a controlled, authenticated MCP endpoint.**
10
10
 
11
- > **Security:** this is a remote-control development tool. Only expose directories you trust, keep the authentication token private, and allow only commands you actually need.
11
+ Agent Dir is a cross-platform TypeScript CLI for bounded project-file access, targeted edits, and explicitly allowlisted development commands. Optional tunnels let an agent connect remotely while the local server retains authentication and filesystem boundaries.
12
+
13
+ > **Security:** this is a remote-control development tool. Expose only directories you trust, keep authentication and tunnel tokens private, and allow only commands you need.
12
14
 
13
15
  ## Requirements
14
16
 
15
- - Node.js 24 LTS or newer
17
+ - Node.js 24 or newer
16
18
  - npm
17
- - Wormhole CLI or `cloudflared` CLI if remote access is needed
18
-
19
- Node.js 24 is the supported LTS baseline. The project currently tracks TypeScript 7 and Biome 2.5.
19
+ - Wormhole CLI or `cloudflared` if remote access is required
20
20
 
21
21
  ## Quick start
22
22
 
23
+ Install globally:
24
+
23
25
  ```bash
24
26
  npm install -g agent-dir
25
27
  agent-dir .
26
28
  ```
27
29
 
28
- Or run it without a global install:
30
+ Or use it without a global installation:
29
31
 
30
32
  ```bash
31
33
  npx agent-dir .
32
34
  ```
33
35
 
34
- For remote MCP access with Wormhole:
35
-
36
- ```bash
37
- agent-dir . --tunnel wormhole --subdomain my-project-x7k4m2
38
- ```
39
-
40
- For a stable hostname on your own Cloudflare-managed domain, configure a Cloudflare Tunnel route to the Agent Dir port. Cloudflare Tunnel is the default for profiles that have a `tunnels.cloudflared` entry. Use `-c` to select it explicitly or `-w` to override it with Wormhole:
41
-
42
- ```bash
43
- agent-dir scriptr # profile default (Cloudflare when configured)
44
- agent-dir scriptr -c # explicitly use Cloudflare Tunnel
45
- agent-dir scriptr -w # explicitly use Wormhole
46
- ```
47
-
48
- For a one-off run, the equivalent long option remains available:
36
+ The server starts with a generated Bearer token. Configure an MCP client to connect to the displayed `/mcp` endpoint and send:
49
37
 
50
- ```bash
51
- agent-dir . --tunnel cloudflared --hostname mcp.example.com
38
+ ```http
39
+ Authorization: Bearer <token>
52
40
  ```
53
41
 
54
- Agent Dir starts `cloudflared tunnel run`, monitors its connection, and restarts the process if it disconnects. The `--hostname` value is the public URL displayed by Agent Dir; Cloudflare DNS routing and the named tunnel's ingress configuration must already exist. See [Cloudflare Tunnel setup](https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/get-started/).
55
-
56
- ### Wormhole domains
57
-
58
- When using a custom Wormhole subdomain, choose a distinctive name such as `my-project-x7k4m2`. Avoid generic names such as `project`, `test`, `dev`, or `scriptr` to reduce collisions and make the public endpoint less predictable.
59
-
60
- The Wormhole URL is only the transport endpoint. `agent-dir` authentication is enforced by the local HTTP server; Wormhole does not provide the Bearer-token authentication described below.
61
-
62
- > **Wormhole subdomain limit:** Wormhole limits how many subdomains a user can have registered/active at once. If you see an error such as `Subdomain limit reached (max 3 per user)`, the tunnel is being rejected by Wormhole, not by `agent-dir`. Release an existing Wormhole subdomain or use `--random` to let Wormhole choose a temporary random URL.
63
- >
64
- > A subdomain can remain registered while another `wormhole` process is still running, so stop unused Wormhole tunnels before creating another one. If a configured subdomain is unavailable, `agent-dir` will report the Wormhole registration failure instead of silently starting without a tunnel.
65
-
66
- ## First-run setup
67
-
68
- On the first interactive run, when no `agent-dir` config exists, the CLI walks you through creating a saved profile. After the profile name, directory, and port, it asks whether to configure Cloudflare Tunnel first; if enabled, it collects the public hostname and Cloudflare connector token. The Cloudflare connector token is entered without being echoed to the terminal. It then asks whether to configure Wormhole as well and optionally collects a custom subdomain. You can enable either provider, both, or neither. Cloudflare is the default when both are configured. The generated Agent Dir MCP bearer token is saved separately from the Cloudflare connector token.
42
+ Use the Authorization header rather than putting tokens in URLs. Authentication cannot be disabled.
69
43
 
70
- Telemetry is optional and stays local. During setup, `agent-dir` explains the available levels: **none**, **anonymous**, **basic**, **detailed**, and **diagnostic**. Telemetry is disabled by default if you choose the default option. The same choices can later be changed with `agent-dir telemetry enable|disable`.
44
+ ## Remote access
71
45
 
72
- You can also run the setup wizard explicitly at any time:
73
-
74
- ```bash
75
- agent-dir setup
76
- ```
77
-
78
- This is useful for creating another profile or replacing an existing profile. The wizard saves the profile and exits without starting a tunnel.
79
-
80
- You can skip setup completely when you only want a temporary random Wormhole URL:
46
+ For a temporary Wormhole endpoint:
81
47
 
82
48
  ```bash
83
49
  agent-dir . --random --tunnel wormhole
84
50
  ```
85
51
 
86
- If you answer **No** at the setup prompt, `agent-dir` automatically starts that temporary random tunnel and does not create a config file.
87
-
88
- Non-interactive invocations and invocations with explicit launch options do not start the setup wizard.
89
-
90
- ## Profiles
91
-
92
- Profiles save settings you use repeatedly: project directory, tunnel, Wormhole subdomain or Cloudflare hostname, port, npm scripts, allowed non-npm commands, blocked command prefixes, and whether dedicated Git MCP tools are enabled. Dedicated Git tools are enabled automatically when `git` is in the allowed command list, or explicitly with `--git`; use `--no-git` to disable them for a run.
93
-
94
- ```bash
95
- agent-dir config add my-project \
96
- --directory ~/src/my-project \
97
- --tunnel wormhole \
98
- --subdomain my-project-x7k4m2 \
99
- --npm typecheck,lint,format,test,build \
100
- --command grep,find,rg,git \
101
- --blacklist "git commit,git push --force"
102
- ```
103
-
104
- Profiles can store both tunnel configurations at once. The Cloudflare connector token is separate from the Agent Dir MCP bearer token (`--token`); store it under `tunnels.cloudflared.token` or pass `--cloudflared-token` when creating the profile. Config files are stored at `~/.config/agent-dir/config.json` with owner-only permissions.
105
-
106
- ```json
107
- {
108
- "directory": "~/src/my-project",
109
- "port": 3002,
110
- "tunnel": "cloudflared",
111
- "defaultTunnel": "cloudflared",
112
- "tunnels": {
113
- "wormhole": { "subdomain": "my-project-x7k4m2" },
114
- "cloudflared": {
115
- "hostname": "mcp.example.com",
116
- "token": "CLOUDFLARE_TUNNEL_TOKEN"
117
- }
118
- }
119
- }
120
- ```
121
-
122
- When creating a profile from the CLI, pass `--hostname` and optionally `--cloudflared-token`. **Avoid passing secrets as command-line arguments where possible**: they may be exposed in shell history or process listings. Prefer the interactive setup prompt for the connector token. To configure both providers, include both `--subdomain` and `--hostname`, and set the default using `--tunnel cloudflared` (or `-c`). `agent-dir config show` redacts configured tunnel tokens.
123
-
124
- For Cloudflare, save the hostname instead of a Wormhole subdomain:
125
-
126
- ```bash
127
- agent-dir config add scriptr-mcp \
128
- --directory ~/src/my-project \
129
- --tunnel cloudflared \
130
- --hostname mcp.example.com \
131
- --npm typecheck,lint,test
132
- ```
133
-
134
- Then start the saved profile:
135
-
136
- ```bash
137
- agent-dir scriptr-mcp
138
- ```
139
-
140
- Manage profiles with:
141
-
142
- ```bash
143
- agent-dir config list
144
- agent-dir config show my-project
145
- agent-dir config delete my-project
146
- agent-dir config delete --all
147
- ```
148
-
149
- Deletion asks for confirmation by default. Use `--yes` for automation:
150
-
151
- ```bash
152
- agent-dir config delete my-project --yes
153
- agent-dir config delete --all --yes
154
- ```
155
-
156
- The older `config remove <name>` command remains an alias for `config delete <name>`.
157
-
158
- Retrieve or rotate a profile's authentication token with:
159
-
160
- ```bash
161
- agent-dir config token my-project
162
- agent-dir config token my-project --rotate
163
- ```
164
-
165
- The token command prints the secret intentionally; avoid sharing or committing its output.
166
-
167
- Profiles are stored per-user at `~/.config/agent-dir/config.json`, not in the project repository. The config directory and file are written with user-only permissions. Command-line options can override saved values for one run.
168
-
169
- ## Telemetry
170
-
171
- Telemetry is **disabled by default** and is local-only. When enabled, events are stored under `~/.config/agent-dir/telemetry/<profile>/session-<id>.jsonl` with user-only permissions. Each profile and process session gets a separate append-only JSONL file; restarting Agent Dir starts a new session file, and historical events are not truncated. Tool events also include rough byte-based token-size estimates, hashed request/input/output fingerprints, repeated-output signals, cumulative output size, and hashed per-file size metadata. These estimates do not reveal Claude's actual token billing or hidden context. File contents, source code, and raw paths are not stored in the added file metrics. No telemetry is sent to a remote service. The previous shared `telemetry.jsonl` file is migrated into the new layout on the next telemetry read/reset and preserved as a legacy archive.
172
-
173
- Enable a privacy level with:
52
+ For a configured Wormhole subdomain:
174
53
 
175
54
  ```bash
176
- agent-dir telemetry enable anonymous
177
- agent-dir telemetry enable basic
178
- agent-dir telemetry enable detailed
179
- agent-dir telemetry enable diagnostic
180
- ```
181
-
182
- Levels add bounded operational context:
183
-
184
- - **anonymous** — MCP methods, tool/command families, success, duration, sizes, result counts, truncation/pagination, and safe error categories.
185
- - **basic** — anonymous data plus bounded Agent Dir/Node/platform and MCP client version information.
186
- - **detailed** — basic data plus coarse project classification such as language, package manager, Git/CodeGraph availability, and project size.
187
-
188
- Standard telemetry levels never record file contents, command arguments, authentication tokens, environment variables, or project paths. The opt-in diagnostic level additionally records local troubleshooting metadata such as project paths, hostname, username, process identifiers, Node version, memory usage, and process uptime; it remains local and is never uploaded by Agent Dir. Session telemetry separates wall-clock session lifetime from active MCP request time and the gaps between requests; those gaps may include agent reasoning, network delay, or other idle time and are not presented as agent thinking time. Persistence failures do not affect MCP requests.
189
-
190
- Manage telemetry with:
191
-
192
- ```bash
193
- agent-dir telemetry status
194
- agent-dir telemetry schema
195
- agent-dir telemetry show [profile] [session-id] [--follow]
196
- agent-dir telemetry summary [profile]
197
- agent-dir telemetry disable
198
- agent-dir telemetry reset <profile>
199
- agent-dir telemetry reset --all
200
- ```
201
-
202
- Use `agent-dir telemetry show scriptr` or `agent-dir telemetry summary scriptr` to focus on one profile. `agent-dir telemetry show scriptr <session-id>` filters to a single session. `agent-dir telemetry reset scriptr` removes only that profile’s telemetry; `agent-dir telemetry reset --all` clears all telemetry and resets the configured level. For the event model, metric definitions, privacy levels, and guidance for interpreting raw and aggregated telemetry, see [Telemetry interpretation](docs/telemetry.md).
203
-
204
- ## MCP transport and compatibility
205
-
206
- The HTTP MCP endpoint is `POST /mcp`. The implementation supports two protocol compatibility paths:
207
-
208
- ### Modern stateless MCP
209
-
210
- The native protocol path uses MCP `2026-07-28`. Requests carry the protocol version and client capabilities in `params._meta`. The server does not require an initialize handshake or `Mcp-Session-Id` for this path.
211
-
212
- Standard Streamable HTTP requests do **not** need the implementation-specific `MCP-Protocol-Version`, `Mcp-Method`, or `Mcp-Name` headers. If a client sends those optional headers, `agent-dir` validates them against the JSON-RPC request and metadata instead of requiring them.
213
-
214
- The modern implementation includes:
215
-
216
- - `server/discover`
217
- - cursor pagination for list-style methods
218
- - `subscriptions/listen` for tool, prompt, and resource change events
219
- - resource subscriptions and filesystem change notifications
220
- - cache metadata
221
- - structured tool results with `resultType`
222
- - `outputSchema` for tools
223
- - the stable `io.modelcontextprotocol/skills` extension
224
-
225
- ### Legacy MCP compatibility
226
-
227
- Clients using the MCP `2025-11-25` initialize-based lifecycle are also supported. A legacy client can:
228
-
229
- 1. send `initialize` with `params.protocolVersion: "2025-11-25"`, `capabilities`, and `clientInfo`;
230
- 2. negotiate `2025-11-25`;
231
- 3. send subsequent requests with `MCP-Protocol-Version: 2025-11-25` without the modern `params._meta` object.
232
-
233
- This compatibility path is intentionally narrow. It does not weaken the modern protocol validation, and conflicting protocol headers are rejected.
234
-
235
- The compatibility layer exists for clients such as MCP integrations that still perform the standard `initialize` handshake instead of using the native stateless lifecycle.
236
-
237
- ### Authentication and protocol troubleshooting
238
-
239
- Authentication is independent of protocol negotiation. A valid Bearer token is still required before MCP handling:
240
-
241
- ```http
242
- Authorization: Bearer <token>
243
- ```
244
-
245
- For clients that cannot send an Authorization header, the server also accepts:
246
-
247
- ```text
248
- https://your-subdomain.wormhole.bar/mcp?token=<token>
55
+ agent-dir . --tunnel wormhole --subdomain my-project-x7k4m2
249
56
  ```
250
57
 
251
- If a client reports that the server needs sign-in, inspect the server's request log before changing credentials. A `400` from MCP can be a protocol compatibility error rather than an authentication failure. Request logs now include the MCP error message and, for JSON-RPC failures, safe diagnostic context such as the method, request id, parameter names, metadata presence, protocol version, and protocol header. Parameter values are not logged.
252
-
253
- The server log distinguishes protocol/header validation from MCP handler errors. This makes transient client interoperability failures diagnosable without exposing request payloads or secrets.
254
-
255
- ## MCP tools
256
-
257
- | Tool | Purpose |
258
- |---|---|
259
- | `list_files` | Recursive project discovery |
260
- | `list_dirs` | Direct directory discovery for one or more directories |
261
- | `read_range` | Read a bounded line range from one UTF-8 file |
262
- | `read_files` | Read one or more UTF-8 files in one call |
263
- | `write_files` | Create or replace one or more files in one call |
264
- | `patch_files` | Apply targeted text replacements to one or more files |
265
- | `delete_files` | Delete one or more files in one call |
266
- | `search_files` / `find_files` | Search file contents or find paths by glob |
267
- | `search_code` | Search source-like files |
268
- | `find_symbol` / `find_definition` | Locate likely symbol definitions |
269
- | `find_references` | Locate symbol references |
270
- | `find_imports` / `find_exports` | Inspect source dependencies and exports |
271
- | `git_status` / `git_diff` / `git_log` | Git inspection |
272
- | `git_stage` / `git_unstage` | Stage or unstage paths |
273
- | `git_commit` | Commit staged changes |
274
- | `git_restore` | Restore paths, discarding unstaged changes |
275
- | `git_push` | Push the current branch to a remote |
276
- | `project_overview` | Project structure, languages, package managers, and Git state |
277
- | `package_info` / `file_info` | Project and filesystem metadata |
278
- | `diagnostics` | Project-independent diagnostics; does not run tests, lint, or typecheck |
279
- | `run_npm_batch` | Run one or more explicitly allowlisted npm scripts sequentially |
280
- | `run_command_batch` | Run one or more explicitly allowlisted executables sequentially without a shell |
281
- | `codegraph_explore` | Optional CodeGraph structural code intelligence when the project is indexed |
282
-
283
- Batch operations execute sequentially and stop on the first failed command. Tool calls return modern `structuredContent` alongside a serialized text representation, and tools that return structured data advertise an `outputSchema`. List-style protocol methods use opaque cursors when more than 50 entries are available.
284
-
285
- Modern `subscriptions/listen` replaces the legacy GET/SSE notification model. Clients can subscribe to tool, prompt, resource-list, and resource-specific change events; filesystem mutations publish resource-change events to active subscribers.
286
-
287
- ### Optional CodeGraph integration
288
-
289
- Agent Dir can optionally bridge the CodeGraph MCP server into the same Agent Dir MCP endpoint. CodeGraph remains an independent tool and dependency; Agent Dir only exposes its `codegraph_explore` capability when the current project has a readable `.codegraph/codegraph.db` index and the `codegraph` executable is available on `PATH`. Agent Dir does not start CodeGraph until `codegraph_explore` is actually called.
290
-
291
- Install CodeGraph separately if you want this capability:
58
+ For Cloudflare Tunnel, configure its DNS and ingress route first, then launch with a hostname:
292
59
 
293
60
  ```bash
294
- npm install -g @colbymchenry/codegraph
295
- codegraph init
296
- ```
297
-
298
- CodeGraph exposes `codegraph_explore` as its primary/default MCP tool. Agent Dir forwards its request and structured result without reimplementing graph analysis, and launches CodeGraph with the Agent Dir project root fixed as `--path`; callers cannot select another project through the forwarded tool. Agent Dir intentionally exposes only `codegraph_explore`, even if a CodeGraph installation enables additional MCP tools.
299
-
300
- The integration distinguishes these states in `server/discover`:
301
-
302
- - `not_installed` — CodeGraph is unavailable on `PATH`.
303
- - `not_indexed` — CodeGraph is installed, but this project has no readable `.codegraph/codegraph.db`.
304
- - `available` — CodeGraph is installed and this project is indexed.
305
- - `startup_failed` / `runtime_failed` — CodeGraph could not initialize or later terminated; Agent Dir remains available and reports the failure locally.
306
-
307
- The CodeGraph child process is reused for subsequent calls and is terminated with Agent Dir shutdown. Agent Dir passes a deliberately limited environment to the child and does not forward arbitrary `projectPath` values, credentials, or unrelated filesystem paths.
308
-
309
- CodeGraph is optional: without it, the normal Agent Dir tool surface and behavior are unchanged.
310
-
311
- ### Command permissions
312
-
313
- npm scripts must be explicitly enabled:
314
-
315
- ```text
316
- --npm typecheck,lint,format,test,build
317
- ```
318
-
319
- Normal executables use a separate allowlist:
320
-
321
- ```text
322
- --command grep,find,rg,git
323
- ```
324
-
325
- Commands are launched with `shell: false`; the MCP client supplies the executable and arguments separately. Dedicated Git MCP tools are separately capability-gated; they are exposed only when Git is enabled for the active profile. If Git is disabled, direct calls to Git tools are rejected even if a client attempts to invoke them by name. An executable that is not in the allowlist is rejected. A configured `blacklistedCommands` entry overrides the allowlist and blocks matching command prefixes, so `git` can be allowed while `git commit` is blocked and `git status` remains available. Entries are whitespace-separated command/argument prefixes, for example `git commit` or `git push --force`.
326
-
327
- Avoid allowing `sh`, `bash`, `zsh`, `cmd`, `node`, or `python` unless you intentionally want to grant a much broader execution capability.
328
-
329
- ## Agent Skills
330
-
331
- ### Agent-aware instructions and capabilities
332
-
333
- Agent Dir automatically describes how an AI agent should use the server. MCP initialization and `server/discover` return dynamically generated instructions, and `resources/list` exposes two virtual resources:
334
-
335
- ```text
336
- agent-dir://instructions
337
- agent-dir://capabilities
338
- ```
339
-
340
- The instructions emphasize efficient tool selection: targeted search before reading, bounded ranges before whole-file reads, dedicated tools before generic commands, batched related operations, narrow validation before full checks, and scoped Git inspection before full diffs. The execution policy is generated from the active profile, so changing `allowedScripts`, `commands`, or `blacklistedCommands` automatically changes what the agent is told it can execute.
341
-
342
- The capabilities resource is machine-readable and includes the running Agent Dir version, registered tool names, execution policy, and preferred/avoid tool-selection patterns. This keeps the MCP server itself as the source of truth; client-specific instruction files do not need to be maintained when Agent Dir changes.
343
-
344
- The server implements the stable MCP Skills extension (`io.modelcontextprotocol/skills`) over the standard Resources primitive. It discovers project-local `SKILL.md` files under:
345
-
346
- ```text
347
- skills/
348
- .agents/skills/
349
- .claude/skills/
350
- .github/skills/
351
- ```
352
-
353
- Skills are exposed through `skills/list`, `skills/get`, `resources/list`, and `resources/read`. Skill entries contain parsed frontmatter plus SHA-256 digests and byte sizes for every served file. Skill manifests enforce the 512-resource and 16 MiB limits. Binary supporting files are returned as MCP blobs. `resources/directory/read` lists direct children of a skill resource directory. The extension advertises `directoryRead: true`.
354
-
355
- This repository now includes a maintainer-facing compatibility skill at `skills/agent-dir-maintainer/SKILL.md`. It documents the modern and legacy MCP paths, troubleshooting signals, and release/test expectations for agents working on this project.
356
-
357
- ## Authentication
358
-
359
- By default, `agent-dir` generates a random Bearer token when the server starts. The token protects both the MCP endpoint and the HTTP file API.
360
-
361
- Clients should authenticate with:
362
-
363
- ```http
364
- Authorization: Bearer <token>
365
- ```
366
-
367
- The CLI also supports explicitly configured profile tokens and the `--token` option. There is no `--no-auth` option; authentication cannot be disabled through the CLI. Keep the token secret and do not commit it to a repository or publish it alongside a tunnel URL.
368
-
369
- For clients that cannot send an `Authorization` header, the HTTP server also accepts the token as a query parameter:
370
-
371
- ```text
372
- https://your-subdomain.wormhole.bar/mcp?token=<token>
373
- ```
374
-
375
- Query-string tokens may be exposed in URL logs and should be treated as less secure than the Authorization header.
376
-
377
- ## REST API
378
-
379
- The MCP server is the primary interface, but the HTTP server also exposes:
380
-
381
- ```text
382
- GET /__tree
383
- GET /path/to/file
384
- PUT /path/to/file
385
- DELETE /path/to/file
386
- POST /mcp
387
- ```
388
-
389
- All HTTP endpoints are authenticated by default.
390
-
391
- ## Releases
392
-
393
- Releases are automated with [semantic-release](https://github.com/semantic-release/semantic-release) from the `main` branch. Developers should **not** manually edit the version in `package.json` for normal releases.
394
-
395
- Use [Conventional Commits](https://www.conventionalcommits.org/) for commits that should communicate release impact:
396
-
397
- | Commit | Release |
398
- | --- | --- |
399
- | `fix: handle tunnel reconnect` | Patch (`0.3.0` → `0.3.1`) |
400
- | `feat: add project snapshots` | Minor (`0.3.0` → `0.4.0`) |
401
- | `feat!: change authentication protocol` | Major (`0.3.0` → `1.0.0`) |
402
- | `feat: change API` with a `BREAKING CHANGE:` footer | Major |
403
- | `docs:`, `test:`, `chore:`, `refactor:`, `ci:`, etc. | No release by default |
404
-
405
- After a pull request is reviewed and merged into `main`, the release workflow runs the normal validation (`npm run check`, `npm test`, and `npm pack --dry-run`). If validation succeeds, semantic-release determines the next SemVer version from the Conventional Commit history, updates `package.json` and `package-lock.json`, creates the release commit, creates the `vX.Y.Z` Git tag, creates the GitHub Release, and publishes the package to npm.
406
-
407
- The release commit is marked `[skip ci]`, so it does not start another release cycle. A push to `main` that contains no release-worthy Conventional Commit produces no release.
408
-
409
- For an intentional breaking release, use the `!` marker on the commit type/scope or add a `BREAKING CHANGE:` footer, for example:
410
-
411
- ```text
412
- feat!: change authentication protocol
413
- ```
414
-
415
- or:
416
-
417
- ```text
418
- feat: change authentication protocol
419
-
420
- BREAKING CHANGE: clients must use the new authentication protocol
61
+ agent-dir . --tunnel cloudflared --hostname mcp.example.com
421
62
  ```
422
63
 
423
- Do not use arbitrary keywords such as `bump` to control releases. The existing `v0.3.0` release is the baseline for this automation.
64
+ See [Configuration and tunnel setup](docs/CONFIGURATION.md) for the setup wizard, saved profiles, authentication tokens, tunnel configuration, and command permissions.
424
65
 
425
- ## Contributing
66
+ ## What it provides
426
67
 
427
- Contributions are welcome. See [CONTRIBUTING.md](.github/CONTRIBUTING.md) for development setup, testing requirements, MCP compatibility guidance, and pull-request expectations.
68
+ - A compact MCP toolset for project context, file discovery, source search, bounded reads, targeted changes, and validation.
69
+ - Authenticated, stateless MCP over `POST /mcp`.
70
+ - Explicit npm-script and executable allowlists; commands run without a shell.
71
+ - Optional CodeGraph structural code intelligence when CodeGraph is installed and the project is indexed.
72
+ - Optional local-only telemetry, disabled by default.
428
73
 
429
- Use the GitHub issue templates for bug reports, feature requests, and usage questions. Security vulnerabilities should be reported privately through the process in [SECURITY.md](SECURITY.md), not through a public issue.
74
+ See [MCP interface and security model](docs/MCP.md) for the supported protocol, tools, authentication, command policy, and optional CodeGraph integration.
430
75
 
431
- See [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md) for community participation guidelines.
76
+ ## Documentation
432
77
 
433
- ## Development
78
+ - [Configuration and tunnel setup](docs/CONFIGURATION.md) — setup wizard, profiles, Cloudflare Tunnel, Wormhole, tokens, and command allowlists.
79
+ - [MCP interface and security model](docs/MCP.md) — supported endpoint, protocol behavior, tools, authentication, command execution, and CodeGraph.
80
+ - [Telemetry interpretation](docs/telemetry.md) — privacy levels, event model, metric definitions, and interpretation guidance.
81
+ - [Release procedure](docs/RELEASING.md) — validation, branch/PR flow, semantic-release, and npm publishing.
82
+ - [Contributing guide](https://github.com/SarfDime/agent-dir/blob/main/.github/CONTRIBUTING.md) — development setup, checks, and pull-request expectations.
83
+ - [Security policy](SECURITY.md) — supported versions and private vulnerability reporting.
84
+ - [Maintainer skill](https://github.com/SarfDime/agent-dir/blob/main/skills/agent-dir-maintainer/SKILL.md) — repository-specific guidance for coding agents.
434
85
 
435
- The application source and tests are fully TypeScript. JavaScript is generated into `dist/` for execution and publishing. The published package contains only the built CLI/runtime, README, and license; development sources and tests are excluded from the npm tarball.
436
-
437
- ```bash
438
- npm run build
439
- npm run typecheck
440
- npm test
441
- npm run format
442
- npm run lint
443
- npm run check
444
- ```
445
-
446
- `npm run check` runs Biome plus TypeScript. Before publishing:
86
+ ## Development checks
447
87
 
448
88
  ```bash
89
+ npm ci
449
90
  npm run check
450
91
  npm test
451
92
  npm pack --dry-run
452
93
  ```
453
94
 
454
- Review the `npm pack --dry-run` file list before publishing to confirm no local configuration, credentials, source-only files, or development artifacts are included.
455
-
456
- ## Toolchain
457
-
458
- - **Node.js:** 24 LTS baseline
459
- - **TypeScript:** 7.x
460
- - **Biome:** 2.5.x
461
- - **Module system:** native Node ESM with `NodeNext`
462
- - **Formatting/linting:** Biome with strict recommended rules and import organization
463
-
464
- The TypeScript configuration uses strict checking, exact optional properties, unchecked indexed access, isolated modules, explicit Node typings, and consistent module resolution.
465
-
466
- ## Security model
467
-
468
- File access is rooted at the shared directory and resolves existing paths through their real filesystem targets, preventing symlinks from escaping the exposed root. HTTP request bodies are limited to 10 MiB. npm scripts and external executables use explicit allowlists, and external commands are not passed through a shell.
469
-
470
- Because file writes, deletion, command execution, and Git write operations can modify a project or remote repository, expose only directories and capabilities you intend an AI agent to control. Git write tools are intentionally explicit: staging, unstaging, committing, restoring, and pushing are separate operations. `git_restore` discards unstaged changes, and `git_push` can modify a remote repository. If you do not want Git mutation, do not use the Git write tools and do not allow `git` through the generic command allowlist.
471
-
472
- The diagnostics tool is deliberately project-independent. It checks conditions such as invalid JSON, broken symlinks, and unresolved merge-conflict markers instead of assuming a particular test runner, linter, compiler, or package ecosystem. Never share directories containing credentials, SSH keys, private certificates, production secrets, or unrelated personal data.
473
-
474
- ## Credits
475
-
476
- Remote tunneling is provided by [Wormhole](https://wormhole.bar/) or [Cloudflare Tunnel](https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/). `agent-dir` invokes the installed CLI and does not provide either tunneling service itself. Cloudflare support expects an already-configured named tunnel.
95
+ Review the package file list from `npm pack --dry-run` before publishing. Releases are automated from `main`; do not manually bump the package version or publish from a local machine. See the [release procedure](docs/RELEASING.md).
477
96
 
478
97
  ## License
479
98
 
480
- MIT
99
+ [MIT](LICENSE)
package/SECURITY.md ADDED
@@ -0,0 +1,39 @@
1
+ # Security Policy
2
+
3
+ ## Supported versions
4
+
5
+ Security fixes are generally made against the latest published version.
6
+
7
+ If you are running an unreleased checkout, include the commit or tag when reporting a vulnerability.
8
+
9
+ ## Reporting a vulnerability
10
+
11
+ **Please do not report security vulnerabilities through public GitHub issues.**
12
+
13
+ Use GitHub's private security advisory flow:
14
+
15
+ https://github.com/SarfDime/agent-dir/security/advisories/new
16
+
17
+ Include:
18
+
19
+ - a clear description of the vulnerability;
20
+ - affected version or commit;
21
+ - reproduction steps or a proof of concept;
22
+ - security impact;
23
+ - any suggested mitigation.
24
+
25
+ Please remove secrets, credentials, personal data, and unrelated sensitive information from the report.
26
+
27
+ ## Security model
28
+
29
+ agent-dir is designed to expose a trusted local project to an authenticated AI agent. Important security boundaries include:
30
+
31
+ - file access is rooted at the configured project directory;
32
+ - existing paths are resolved through their real filesystem targets to prevent symlink escapes;
33
+ - MCP and HTTP endpoints require authentication;
34
+ - npm scripts and generic executables are explicitly allowlisted;
35
+ - external commands are launched without a shell;
36
+ - Git commands are available only when the `git` executable is allowlisted, with configured blacklist prefixes taking precedence;
37
+ - request diagnostics avoid logging arbitrary request parameter values.
38
+
39
+ When contributing, preserve these boundaries. A compatibility fix must not become an authentication bypass, path traversal, command-execution expansion, or sensitive-data logging change.
@@ -0,0 +1,38 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="1200" height="260" viewBox="0 0 1200 260" role="img" aria-labelledby="title desc">
2
+ <title id="title">AGENT-DIR</title>
3
+ <desc id="desc">Expose a local project to AI agents through authenticated MCP.</desc>
4
+ <defs>
5
+ <linearGradient id="bg" x1="0%" y1="0%" x2="100%" y2="100%">
6
+ <stop offset="0%" stop-color="#111827"/>
7
+ <stop offset="48%" stop-color="#312e81"/>
8
+ <stop offset="100%" stop-color="#0f766e"/>
9
+ </linearGradient>
10
+ <radialGradient id="glow" cx="50%" cy="45%" r="65%">
11
+ <stop offset="0%" stop-color="#a5b4fc" stop-opacity=".28"/>
12
+ <stop offset="100%" stop-color="#a5b4fc" stop-opacity="0"/>
13
+ </radialGradient>
14
+ </defs>
15
+ <rect width="1200" height="260" rx="28" fill="url(#bg)"/>
16
+ <rect width="1200" height="260" rx="28" fill="url(#glow)"/>
17
+ <g fill="none" stroke="#fff" stroke-opacity=".08">
18
+ <path d="M0 210C240 120 420 310 690 175S1040 65 1200 120"/>
19
+ <path d="M0 235C250 145 430 335 700 200S1040 90 1200 145"/>
20
+ </g>
21
+ <text x="600" y="112" text-anchor="middle" fill="#fff" font-family="Inter,Segoe UI,Arial,sans-serif" font-size="58" font-weight="800" letter-spacing="3">AGENT-DIR</text>
22
+ <text x="600" y="158" text-anchor="middle" fill="#e0e7ff" font-family="Inter,Segoe UI,Arial,sans-serif" font-size="23">Expose a local project to AI agents through authenticated MCP.</text>
23
+ <text x="600" y="198" text-anchor="middle" fill="#c7d2fe" font-family="Inter,Segoe UI,Arial,sans-serif" font-size="15">Local files · Code intelligence · Git inspection · Allowlisted commands · Optional Wormhole tunneling</text>
24
+ <g transform="translate(600 222)">
25
+ <g transform="translate(-212 0)">
26
+ <rect width="132" height="28" rx="7" fill="#111827" fill-opacity=".72" stroke="#ffffff" stroke-opacity=".16"/>
27
+ <text x="66" y="19" text-anchor="middle" fill="#fff" font-family="Inter,Segoe UI,Arial,sans-serif" font-size="13" font-weight="700">Node.js 24+</text>
28
+ </g>
29
+ <g transform="translate(-62 0)">
30
+ <rect width="124" height="28" rx="7" fill="#6b4fbb" fill-opacity=".92"/>
31
+ <text x="62" y="19" text-anchor="middle" fill="#fff" font-family="Inter,Segoe UI,Arial,sans-serif" font-size="13" font-weight="700">MCP 2026-07-28</text>
32
+ </g>
33
+ <g transform="translate(78 0)">
34
+ <rect width="94" height="28" rx="7" fill="#d4b000" fill-opacity=".94"/>
35
+ <text x="47" y="19" text-anchor="middle" fill="#111827" font-family="Inter,Segoe UI,Arial,sans-serif" font-size="13" font-weight="700">License MIT</text>
36
+ </g>
37
+ </g>
38
+ </svg>
@@ -51,7 +51,6 @@ const { values, positionals } = parseArgs({
51
51
  command: { type: "string" },
52
52
  blacklist: { type: "string" },
53
53
  token: { type: "string" },
54
- git: { type: "boolean" },
55
54
  rotate: { type: "boolean" },
56
55
  "no-tunnel": { type: "boolean" },
57
56
  random: { type: "boolean" },
@@ -344,8 +343,6 @@ if (command === "config") {
344
343
  const blacklist = parseList(values.blacklist);
345
344
  if (blacklist !== undefined)
346
345
  profile.blacklistedCommands = blacklist;
347
- if (values.git !== undefined)
348
- profile.git = values.git;
349
346
  if (profile.tunnel === "wormhole" && !profile.subdomain && !values.random) {
350
347
  fail("Wormhole profiles require --subdomain <name>, or use --random.");
351
348
  }
@@ -411,8 +408,6 @@ if (commands !== undefined)
411
408
  overrides.commands = commands;
412
409
  if (blacklist !== undefined)
413
410
  overrides.blacklistedCommands = blacklist;
414
- if (values.git !== undefined)
415
- overrides.git = values.git;
416
411
  const commandConfig = mergeProfile(profile ?? { directory, port, tunnel }, overrides);
417
412
  const telemetryConfigId = profile
418
413
  ? command
@@ -427,7 +422,6 @@ const telemetryConfigId = profile
427
422
  npm: commandConfig.npm?.allowedScripts ?? [],
428
423
  commands: commandConfig.commands ?? [],
429
424
  blacklistedCommands: commandConfig.blacklistedCommands ?? [],
430
- git: commandConfig.git ?? commandConfig.commands?.includes("git") ?? false,
431
425
  }))
432
426
  .digest("hex")
433
427
  .slice(0, 12);
@@ -451,7 +445,6 @@ if (allowedCommands)
451
445
  if (commandConfig.blacklistedCommands?.length) {
452
446
  bannerOptions.blacklistedCommands = commandConfig.blacklistedCommands;
453
447
  }
454
- bannerOptions.gitEnabled = commandConfig.git ?? commandConfig.commands?.includes("git") ?? false;
455
448
  printBanner(bannerOptions);
456
449
  const telemetrySessionId = randomUUID();
457
450
  const server = await startServer({
package/dist/src/auth.js CHANGED
@@ -14,7 +14,9 @@ function secureCompare(left, right) {
14
14
  return timingSafeEqual(leftHash, rightHash);
15
15
  }
16
16
  export function unauthorized() {
17
- return new Response(JSON.stringify({ error: "Unauthorized" }), {
17
+ return new Response(JSON.stringify({
18
+ error: "Unauthorized: provide a valid Authorization: Bearer <token> header.",
19
+ }), {
18
20
  status: 401,
19
21
  headers: {
20
22
  "content-type": "application/json; charset=utf-8",