@globant/coda-linux-arm64 1.3.0 → 1.5.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/assets/agents/coda-help.md +25 -12
- package/assets/agents/council.md +11 -0
- package/assets/agents/reviewer.md +14 -0
- package/assets/agents/side.md +15 -0
- package/assets/docs/add-mcp-server-skill.md +137 -0
- package/assets/docs/agents.md +1 -1
- package/assets/docs/cli-reference.md +40 -4
- package/assets/docs/cli-vs-batch.md +2 -0
- package/assets/docs/config-json.md +71 -57
- package/assets/docs/config-reference.md +99 -48
- package/assets/docs/configuration.md +33 -18
- package/assets/docs/connect-provider.md +34 -31
- package/assets/docs/extensions.md +25 -28
- package/assets/docs/faq.md +47 -15
- package/assets/docs/glossary.md +8 -8
- package/assets/docs/guide-automate.md +6 -2
- package/assets/docs/guide-changes.md +4 -4
- package/assets/docs/guide-collaborate.md +8 -8
- package/assets/docs/guide-extend.md +32 -5
- package/assets/docs/hooks.md +33 -69
- package/assets/docs/how-it-works.md +3 -3
- package/assets/docs/index.md +8 -7
- package/assets/docs/installation.md +9 -9
- package/assets/docs/logging.md +1 -1
- package/assets/docs/overview.md +9 -8
- package/assets/docs/permissions.md +35 -16
- package/assets/docs/quickstart.md +9 -5
- package/assets/docs/sessions.md +6 -2
- package/assets/docs/shortcuts.md +53 -3
- package/assets/docs/tools-reference.md +21 -10
- package/assets/docs/workflows.md +2 -1
- package/assets/output-styles/attention-kind.md +50 -0
- package/assets/output-styles/concise.md +46 -0
- package/assets/output-styles/default.md +4 -0
- package/assets/output-styles/explanatory.md +84 -0
- package/assets/output-styles/learning.md +103 -0
- package/assets/output-styles/plain.md +39 -0
- package/assets/output-styles/proactive.md +52 -0
- package/assets/output-styles/rundown.md +23 -0
- package/assets/output-styles/spartan.md +26 -0
- package/assets/output-styles/visual.md +96 -0
- package/assets/skills/configure-mcp/SKILL.md +281 -0
- package/assets/skills/create-hook/SKILL.md +5 -5
- package/assets/skills/init-rules/SKILL.md +78 -26
- package/coda +2 -2
- package/lib/opentui/parser.worker.js +15 -14
- package/package.json +1 -1
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: configure-mcp
|
|
3
|
+
description: Configure g-coda's MCP client to connect to an MCP server from a Git URL, remote HTTP endpoint, or npm/pip package, at project or global scope. Use when the user asks to add, register, configure, install, or set up an MCP server or MCP client. e.g. "add the playwright MCP server", "configure an MCP server from this URL", "install this MCP tool globally".
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Configure MCP
|
|
7
|
+
|
|
8
|
+
Configure g-coda's MCP client to connect to an MCP server by registering the server entry in the correct config file at the user-chosen scope. The input can be a Git URL, remote HTTP endpoint, or npm/pip package. Reject inputs from which no valid transport (`command` or `url`) can be derived — write nothing in that case.
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## Golden Rule
|
|
13
|
+
|
|
14
|
+
**Never overwrite existing server entries.** Always merge your new entry into the existing `mcpServers` object. If a server with the same name already exists, confirm with the user before replacing it.
|
|
15
|
+
|
|
16
|
+
**Never hardcode secrets.** Tokens, API keys, and credentials must use `${VAR}` or `${VAR:-default}` interpolation — never inline values. A default is literal text up to the first `}`; it cannot contain another `${…}` reference (no `${A:-${B}}`) or a line break.
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## Step 1: Determine Scope
|
|
21
|
+
|
|
22
|
+
Ask the user which scope they want if not specified:
|
|
23
|
+
|
|
24
|
+
| Scope | Write to | When to use |
|
|
25
|
+
|---|---|---|
|
|
26
|
+
| **Project** | `<cwd>/.coda/mcp.json` | Only this repo; shared via version control |
|
|
27
|
+
| **Global** | `~/.coda/mcp.json` | All projects on this machine; personal |
|
|
28
|
+
|
|
29
|
+
- `<cwd>` = `process.cwd()` where g-coda is running, not the nearest `.git` root.
|
|
30
|
+
- If unsure, **default to project scope**.
|
|
31
|
+
- A project entry always wins over a global entry with the same server name.
|
|
32
|
+
|
|
33
|
+
**Target file:** always write to `mcp.json` (top-level `mcpServers` key).
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## Step 2: Detect Input Type and Validate
|
|
38
|
+
|
|
39
|
+
### 2a. Remote HTTP endpoint
|
|
40
|
+
|
|
41
|
+
Input is `https://…` or `http://…` pointing to a live MCP endpoint (Streamable HTTP transport).
|
|
42
|
+
|
|
43
|
+
Example entry — replace the URL, token variable name, and header values with the real ones:
|
|
44
|
+
|
|
45
|
+
```jsonc
|
|
46
|
+
{
|
|
47
|
+
"url": "https://api.example.com/mcp",
|
|
48
|
+
"authorizationToken": "Bearer ${API_TOKEN}",
|
|
49
|
+
"headers": { "X-Custom": "${CUSTOM_HEADER}" },
|
|
50
|
+
"timeout": 120
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
> ⚠️ Make sure no tokens or secrets are exposed — always use `${VAR}` interpolation for credentials.
|
|
55
|
+
|
|
56
|
+
Transport: `url` → HTTP. Do **not** set `command`.
|
|
57
|
+
|
|
58
|
+
### 2b. npm/pip package or CLI tool
|
|
59
|
+
|
|
60
|
+
Input is a package name, `npx`/`uvx` invocation, or local binary path (stdio transport).
|
|
61
|
+
|
|
62
|
+
Example entry — replace the package name and env var names with the real ones:
|
|
63
|
+
|
|
64
|
+
```jsonc
|
|
65
|
+
{
|
|
66
|
+
"command": "npx",
|
|
67
|
+
"args": ["-y", "@scope/mcp-package@latest"],
|
|
68
|
+
"env": { "API_KEY": "${MY_API_KEY}" }
|
|
69
|
+
}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
> ⚠️ Make sure no tokens or secrets are exposed — always use `${VAR}` interpolation for credentials.
|
|
73
|
+
|
|
74
|
+
Common patterns: `npx -y <pkg>`, `uvx <pkg>`, `node /path/to/server.js`, `/path/to/binary`.
|
|
75
|
+
|
|
76
|
+
Transport: `command` → stdio. Do **not** set `url`.
|
|
77
|
+
|
|
78
|
+
### 2c. Git URL
|
|
79
|
+
|
|
80
|
+
Most MCP server repositories support **multiple installation methods** (e.g. `npx`, `uvx`, Docker, a remote HTTP endpoint, or a local build). Do not assume — read the repo's documentation first, then ask the user which method they prefer.
|
|
81
|
+
|
|
82
|
+
Steps:
|
|
83
|
+
|
|
84
|
+
1. **Clone temporarily** to read the docs:
|
|
85
|
+
- Project scope: `<cwd>/.coda/tmp/<name>/`
|
|
86
|
+
- Global scope: `~/.coda/tmp/<name>/`
|
|
87
|
+
- Command: `git clone --depth 1 <url> <tmp-dir>`
|
|
88
|
+
2. **Read the README and any MCP-specific docs** to identify all available installation methods.
|
|
89
|
+
3. **Ask the user** which method they prefer — present the options you found. Common options:
|
|
90
|
+
- `npx -y <pkg>` or `uvx <pkg>` (no local build needed — preferred when available)
|
|
91
|
+
- Remote HTTP endpoint (if the project exposes one)
|
|
92
|
+
- Local build (`npm install && npm run build`, entry via `node dist/index.js`)
|
|
93
|
+
4. **Derive the entry** using the chosen method (see sections 2a and 2b for the corresponding entry shape).
|
|
94
|
+
5. **Write the config entry** to `mcp.json`.
|
|
95
|
+
6. **Delete the temporary clone** (`<tmp-dir>`) — it is no longer needed once the config entry is written.
|
|
96
|
+
|
|
97
|
+
> If the chosen method does not require the cloned directory at runtime (e.g. `npx`/`uvx`/HTTP), the tmp directory is always safe to remove. If the user explicitly chooses a local-build method and the runtime depends on the clone, move the directory to its final location (`<cwd>/.coda/mcp-servers/<name>/` or `~/.coda/mcp-servers/<name>/`) instead of deleting it, and tell the user.
|
|
98
|
+
|
|
99
|
+
### 2d. Rejection — no derivable transport
|
|
100
|
+
|
|
101
|
+
If you **cannot** determine either a `command` or a `url` from the input, **do not write anything**. Respond:
|
|
102
|
+
|
|
103
|
+
> "I couldn't determine a valid MCP server transport from what you provided. An MCP server needs either a runnable command (for stdio transport) or an HTTP URL (for Streamable HTTP transport). Please share the server's npm package name, a Git URL, a binary path, or its HTTP endpoint."
|
|
104
|
+
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
## Step 3: Build the Server Entry
|
|
108
|
+
|
|
109
|
+
A valid entry has **exactly one** of `command` (stdio) or `url` (http) — never both, never neither.
|
|
110
|
+
|
|
111
|
+
| Field | Transport | Description |
|
|
112
|
+
|---|---|---|
|
|
113
|
+
| `command` | stdio | Executable to run as a subprocess |
|
|
114
|
+
| `args` | stdio | Arguments for the subprocess |
|
|
115
|
+
| `cwd` | stdio | Working directory for the subprocess |
|
|
116
|
+
| `url` | http | MCP server HTTP endpoint |
|
|
117
|
+
| `authorizationToken` | http | Shorthand `Authorization` header; supports `${VAR}` |
|
|
118
|
+
| `oauth` | http | `true` for an OAuth 2.1 server; the user signs in with `coda mcp login <name>` |
|
|
119
|
+
| `oauthClientId` | http | Pre-registered OAuth client id, for servers without dynamic client registration (Glob.AI: `mcp`); requires `oauth: true` |
|
|
120
|
+
| `headers` | http | Extra request headers; string values support `${VAR}` |
|
|
121
|
+
| `timeout` | both | Timeout in seconds for each request to the server; unset means 60 s for MCP requests and 30 s for OAuth sign-in requests |
|
|
122
|
+
| `env` | both | Environment variables; supports `${VAR}` and `${VAR:-default}` |
|
|
123
|
+
| `disabled` | both | `true` to skip at load time (default: `false`) |
|
|
124
|
+
|
|
125
|
+
Secrets in `env`, `headers`, and `authorizationToken` are resolved from `~/.coda/.secrets`, project `.env`, and `process.env` at load time. Never put raw credentials in the config.
|
|
126
|
+
|
|
127
|
+
Choose a descriptive, kebab-case server name (e.g., `playwright`, `github`, `my-internal-api`).
|
|
128
|
+
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
## Step 4: Non-Destructive Merge into Config
|
|
132
|
+
|
|
133
|
+
### Writing to `mcp.json` (preferred)
|
|
134
|
+
|
|
135
|
+
```jsonc
|
|
136
|
+
// <cwd>/.coda/mcp.json (project) or ~/.coda/mcp.json (global)
|
|
137
|
+
{
|
|
138
|
+
"mcpServers": {
|
|
139
|
+
"<server-name>": { /* entry fields */ }
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
Merge steps:
|
|
145
|
+
1. Read the existing file, or start with `{ "mcpServers": {} }` if absent.
|
|
146
|
+
2. Check whether `mcpServers["<server-name>"]` exists — confirm with the user before replacing.
|
|
147
|
+
3. Add or replace only the target key; preserve all other entries.
|
|
148
|
+
4. Write the file back.
|
|
149
|
+
|
|
150
|
+
---
|
|
151
|
+
|
|
152
|
+
## Step 5: Verify and Notify
|
|
153
|
+
|
|
154
|
+
After writing:
|
|
155
|
+
|
|
156
|
+
1. Show the user exactly what was written (entry + file path).
|
|
157
|
+
2. Confirm the file is valid JSON (no trailing commas, balanced braces).
|
|
158
|
+
3. Remind the user how to activate the change:
|
|
159
|
+
|
|
160
|
+
> **MCP changes are not picked up by `/skills refresh`.** To activate:
|
|
161
|
+
> - Run `/mcp` → select **Reload MCP servers**, or
|
|
162
|
+
> - Restart g-coda.
|
|
163
|
+
|
|
164
|
+
4. For cloned Git repos, delete the temporary directory (`<cwd>/.coda/tmp/<name>/` or `~/.coda/tmp/<name>/`) once the config entry has been written — unless the user chose a local-build method that requires the directory at runtime, in which case it was already moved to its final location as part of step 2c.
|
|
165
|
+
|
|
166
|
+
---
|
|
167
|
+
|
|
168
|
+
## Examples
|
|
169
|
+
|
|
170
|
+
### Example 1 — npm package, project scope
|
|
171
|
+
|
|
172
|
+
**User:** "Add the Playwright MCP server to this project."
|
|
173
|
+
|
|
174
|
+
Written to `<cwd>/.coda/mcp.json`:
|
|
175
|
+
```json
|
|
176
|
+
{
|
|
177
|
+
"mcpServers": {
|
|
178
|
+
"playwright": {
|
|
179
|
+
"command": "npx",
|
|
180
|
+
"args": ["-y", "@playwright/mcp@latest"]
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
**Response:** Added `playwright` to `.coda/mcp.json`. Run `/mcp` → Reload MCP servers to activate it.
|
|
187
|
+
|
|
188
|
+
---
|
|
189
|
+
|
|
190
|
+
### Example 2 — HTTP endpoint, global scope, with auth
|
|
191
|
+
|
|
192
|
+
**User:** "Set up the GitHub MCP server globally. My token is in `GITHUB_TOKEN`."
|
|
193
|
+
|
|
194
|
+
Written to `~/.coda/mcp.json`:
|
|
195
|
+
```json
|
|
196
|
+
{
|
|
197
|
+
"mcpServers": {
|
|
198
|
+
"github": {
|
|
199
|
+
"url": "https://api.githubcopilot.com/mcp/",
|
|
200
|
+
"authorizationToken": "Bearer ${GITHUB_TOKEN}",
|
|
201
|
+
"timeout": 120
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
**Response:** Added `github` to `~/.coda/mcp.json`. Make sure `GITHUB_TOKEN` is set in your environment or `~/.coda/.secrets`. Run `/mcp` → Reload MCP servers to activate it.
|
|
208
|
+
|
|
209
|
+
---
|
|
210
|
+
|
|
211
|
+
### Example 3 — Git URL, project scope
|
|
212
|
+
|
|
213
|
+
**User:** "Install from https://github.com/example/my-mcp-server into this project."
|
|
214
|
+
|
|
215
|
+
Steps:
|
|
216
|
+
1. Clone to `<cwd>/.coda/tmp/my-mcp-server/` with `git clone --depth 1 <url> <tmp-dir>`.
|
|
217
|
+
2. Read `README.md` — it documents two options: `npx -y @example/my-mcp-server` or a local build.
|
|
218
|
+
3. Ask the user: *"The repository supports `npx` (no local build needed) or a local build. Which do you prefer?"*
|
|
219
|
+
4. User chooses `npx`. Derive the entry (section 2b).
|
|
220
|
+
5. Write config, then delete `<cwd>/.coda/tmp/my-mcp-server/`.
|
|
221
|
+
|
|
222
|
+
Written to `<cwd>/.coda/mcp.json`:
|
|
223
|
+
```json
|
|
224
|
+
{
|
|
225
|
+
"mcpServers": {
|
|
226
|
+
"my-mcp-server": {
|
|
227
|
+
"command": "npx",
|
|
228
|
+
"args": ["-y", "@example/my-mcp-server@latest"]
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
**Response:** Added `my-mcp-server` to `.coda/mcp.json` and removed the temporary clone. Run `/mcp` → Reload MCP servers to activate it.
|
|
235
|
+
|
|
236
|
+
---
|
|
237
|
+
|
|
238
|
+
### Example 4 — Rejection
|
|
239
|
+
|
|
240
|
+
**User:** "Add `my-special-tool` as an MCP server."
|
|
241
|
+
|
|
242
|
+
No URL or command is derivable from the name alone.
|
|
243
|
+
|
|
244
|
+
**Response:** I couldn't determine a valid MCP server transport for `my-special-tool`. Please share the npm package name, a Git URL, a binary path, or the HTTP endpoint.
|
|
245
|
+
|
|
246
|
+
---
|
|
247
|
+
|
|
248
|
+
## Error Handling
|
|
249
|
+
|
|
250
|
+
| Situation | Action |
|
|
251
|
+
|---|---|
|
|
252
|
+
| File exists but is invalid JSON | Report the parse error and path; do not overwrite. Ask user to fix manually. |
|
|
253
|
+
| Server name already exists | Show the existing entry; ask user to confirm replacement. |
|
|
254
|
+
| No transport derivable | Reject with a clear explanation (Step 2d). Do not write anything. |
|
|
255
|
+
| Git clone fails | Report the error and path; do not write a config entry. Clean up any partial tmp directory. |
|
|
256
|
+
| No installation method found in repo docs | Tell the user no clear method was found; ask them to provide the command or URL directly. |
|
|
257
|
+
| Secret value unclear | Ask which env var name to use; never guess a value. |
|
|
258
|
+
|
|
259
|
+
---
|
|
260
|
+
|
|
261
|
+
## Instructions for the Agent
|
|
262
|
+
|
|
263
|
+
1. **Clarify scope first** — ask project vs global if not stated. Default to project.
|
|
264
|
+
2. **Derive the entry** — HTTP URL → `url` field; package/git/binary → `command` field. For Git URLs, clone to a tmp dir, read the docs, ask the user which method to use, then delete the tmp dir. If no transport is derivable, reject immediately (do not write).
|
|
265
|
+
3. **Choose the config file** — always use `mcp.json` (top-level `mcpServers` key).
|
|
266
|
+
4. **Merge non-destructively** — read the existing file first; preserve all other entries.
|
|
267
|
+
5. **No inline secrets** — always use `${VAR}` notation; tell the user which env var to set.
|
|
268
|
+
6. **Validate JSON** — ensure the written file is syntactically valid.
|
|
269
|
+
7. **Remind about reload** — MCP changes need `/mcp` → Reload MCP servers or a restart. `/skills refresh` does NOT reload MCP.
|
|
270
|
+
8. **Show the result** — always display the final entry and the full path of the file written.
|
|
271
|
+
|
|
272
|
+
---
|
|
273
|
+
|
|
274
|
+
## Deliverables
|
|
275
|
+
|
|
276
|
+
When done, provide:
|
|
277
|
+
|
|
278
|
+
1. The exact file path written (e.g., `<cwd>/.coda/mcp.json` or `~/.coda/mcp.json`).
|
|
279
|
+
2. The server entry that was added (JSON block).
|
|
280
|
+
3. Any env vars the user needs to set (if secrets are involved).
|
|
281
|
+
4. The activation command: `/mcp` → Reload MCP servers (or restart g-coda).
|
|
@@ -65,7 +65,7 @@ Spawns a shell command via `sh -c`. g-coda writes a JSON payload to the command'
|
|
|
65
65
|
| `command` | string | required | Shell command to run |
|
|
66
66
|
| `args` | string[] | — | Extra arguments appended to the command |
|
|
67
67
|
| `timeout` | number | 600 | Seconds before the hook is killed |
|
|
68
|
-
| `shell` | `"bash"` \| `"powershell"` |
|
|
68
|
+
| `shell` | `"sh"` \| `"bash"` \| `"powershell"` \| `"auto"` | `"auto"` (POSIX-first: `sh` on macOS/Linux, Git Bash on Windows, PowerShell as a last resort) | Shell to use |
|
|
69
69
|
| `statusMessage` | string | — | Spinner text shown while the hook runs |
|
|
70
70
|
| `if` | string | — | Conditional expression (tool events only) |
|
|
71
71
|
|
|
@@ -154,15 +154,15 @@ Every hook receives a JSON object on stdin. All events include these base fields
|
|
|
154
154
|
```json
|
|
155
155
|
{
|
|
156
156
|
"session_id": "abc123",
|
|
157
|
-
"transcript_path": "
|
|
157
|
+
"transcript_path": "",
|
|
158
158
|
"cwd": "/path/to/project",
|
|
159
159
|
"hook_event_name": "PreToolUse",
|
|
160
|
-
"
|
|
161
|
-
"agent_id": "...",
|
|
162
|
-
"agent_type": "main"
|
|
160
|
+
"cli_version": "0.4.2"
|
|
163
161
|
}
|
|
164
162
|
```
|
|
165
163
|
|
|
164
|
+
`transcript_path` is always an empty string (Coda persists conversations to a database, not a transcript file; the field exists for wire-compatibility). `cli_version` is the running Coda version — also available to command hooks as the `CODA_VERSION` env var, which is cheaper than parsing stdin in hot-path hooks. `permission_mode`, `agent_id`, and `agent_type` are reserved fields that are currently never populated — do not depend on them.
|
|
165
|
+
|
|
166
166
|
Additional fields by event:
|
|
167
167
|
|
|
168
168
|
**Tool events** (`PreToolUse`, `PostToolUse`, `PostToolUseFailure`):
|
|
@@ -17,6 +17,8 @@ This skill assumes the session is running in **auto mode** (unattended/hands-off
|
|
|
17
17
|
|
|
18
18
|
If the user is not in auto mode, still proceed — the rules are valid in any mode — but note that in `default`/`read-only` mode many commands already require approval, so `deny`/`ask` rules matter most.
|
|
19
19
|
|
|
20
|
+
Keeping auto after the rules land: on a fresh install the silent `auto` default applies to the current session only, and only while no `permissions` block exists. Saving a rule never changes the starting mode, so once the first rule is written, later sessions start in `default`. If the user wants to keep starting in auto, ask them, and if they agree propose it explicitly: `propose_policy` with `{ "op": "set-mode", "mode": "auto", "scope": "global" }`. It is a loosen, so the user sees and approves exactly that change. Project scope can only lower the mode, so raising it must be global. Headless, a loosen is auto-refused: report the `set-mode` suggestion instead. If the user already chose a mode, don't propose one.
|
|
21
|
+
|
|
20
22
|
## Interactive vs. headless
|
|
21
23
|
|
|
22
24
|
- **Interactive:** follow every step, using `ask_user` to confirm choices.
|
|
@@ -24,7 +26,25 @@ If the user is not in auto mode, still proceed — the rules are valid in any mo
|
|
|
24
26
|
|
|
25
27
|
---
|
|
26
28
|
|
|
27
|
-
## Step 1 —
|
|
29
|
+
## Step 1 — Detect the OS and shell dialect
|
|
30
|
+
|
|
31
|
+
CODA's command rules are **dialect-scoped**: a `Bash(...)` rule only matches when the session's resolved shell routes to the POSIX extractor, and a `PowerShell(...)` rule only matches when it routes to the PowerShell extractor. A bash rule is **inert on Windows PowerShell**, and a PowerShell rule is inert on macOS/Linux. So you MUST author rules for the dialect the user actually runs, or the rules silently match nothing.
|
|
32
|
+
|
|
33
|
+
Detect the platform first, then pick the dialect:
|
|
34
|
+
|
|
35
|
+
- Run a quick probe to learn the OS/shell. Prefer a portable check, e.g. `uname -s` (prints `Darwin`/`Linux` on macOS/Linux; fails or is absent on native Windows), or inspect the environment. On Windows, `$PSVersionTable` / `echo $env:OS` confirm PowerShell.
|
|
36
|
+
- Map the result to a dialect:
|
|
37
|
+
- **macOS (`Darwin`) or Linux → `bash` dialect.** Author `Bash(...)` rules. (This also covers WSL and any POSIX shell — bash/zsh/sh/dash/ksh/ash.)
|
|
38
|
+
- **Windows PowerShell / pwsh → `pwsh` dialect.** Author `PowerShell(...)` rules (the tags `PowerShell(...)`, `powershell(...)`, and `pwsh(...)` are all equivalent).
|
|
39
|
+
- If the probe is ambiguous, ask the user with `ask_user` which shell they run (`bash`/`zsh` vs `PowerShell`) before proposing any command rules.
|
|
40
|
+
|
|
41
|
+
State the detected dialect back to the user, e.g. *"You're on macOS, so I'll propose `Bash(...)` rules"* or *"You're on Windows PowerShell, so I'll propose `PowerShell(...)` rules"*, and use **that dialect for every command rule** in the steps below. Everywhere this skill shows `Bash(...)`, substitute `PowerShell(...)` when the user is on Windows.
|
|
42
|
+
|
|
43
|
+
> Note: file rules (`Read`/`Edit`/`Write`) and MCP rules (`mcp__server`) are **not** dialect-scoped — they apply on every OS unchanged. Only command rules switch between `Bash(...)` and `PowerShell(...)`.
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## Step 2 — Explore the project
|
|
28
48
|
|
|
29
49
|
Understand what commands this project legitimately runs so your rules fit reality. Do NOT skip this.
|
|
30
50
|
|
|
@@ -38,7 +58,9 @@ Search in the ~/.coda/coda.db database for bash commands to get a sense of the c
|
|
|
38
58
|
|
|
39
59
|
---
|
|
40
60
|
|
|
41
|
-
## Step
|
|
61
|
+
## Step 3 — Choose how permissive the rules should be
|
|
62
|
+
|
|
63
|
+
> Use the dialect you detected in Step 1 for every command rule below. The examples show the `Bash(...)` (macOS/Linux) form; on Windows author the `PowerShell(...)` equivalents from the palette further down.
|
|
42
64
|
|
|
43
65
|
Ask the user which posture they want. Use `ask_user`:
|
|
44
66
|
|
|
@@ -47,32 +69,34 @@ Ask the user which posture they want. Use `ask_user`:
|
|
|
47
69
|
> - **Restrictive** — allow only a short, explicit allowlist; `ask` before anything not on it; deny a broad set of destructive/network commands.
|
|
48
70
|
> - **Balanced** — allow the confirmed dev commands, `ask` for git-writes and network/publish, deny destructive ones.
|
|
49
71
|
|
|
50
|
-
Then, using what you found in Step
|
|
72
|
+
Then, using what you found in Step 2, propose the concrete rule set and confirm the risky ones with `ask_user` before writing. Typical shapes (adapt to the actual project):
|
|
51
73
|
|
|
52
|
-
- **Allow** (permissive
|
|
74
|
+
- **Allow** (permissive): `Bash(pnpm:*)`, `Bash(git:*)`, `Bash(bunx)`, `Bash(npx)`, `Bash(git:*)`, `Bash(python3:*)`.
|
|
53
75
|
- **Ask** (guardrails on state-changing / outbound actions): `Bash(git push:*)`, `Bash(git commit:*)`, `Bash(npm publish:*)`, `Bash(curl:*)`.
|
|
54
76
|
- **Deny** (destructive / dangerous): `Bash(rm:*)`, `Bash(git reset --hard:*)`, `Bash(chmod:*)` — deny whatever the user does not want the agent doing unattended.
|
|
55
77
|
|
|
56
78
|
Rule syntax reminders (this is exactly what `propose_policy` writes):
|
|
57
|
-
-
|
|
58
|
-
-
|
|
79
|
+
- **Command rules are dialect-scoped (Step 1):** `Bash(...)` matches POSIX shells (macOS/Linux); `PowerShell(...)` (also written `pwsh(...)` / `powershell(...)`) matches Windows PowerShell. Author the tag for the detected OS — the other tag is inert.
|
|
80
|
+
- `Bash(npm:*)` = any command starting with `npm`; `Bash(git push origin main)` = exact match; `Bash(*)` = any bash command (**too permissive — don't recommend it**, see below); bare `Bash` = the whole tool. On Windows the same shapes are `PowerShell(npm:*)`, `PowerShell(Remove-Item -Recurse ...)`, etc.
|
|
81
|
+
- PowerShell command names are **case-insensitive** and cmdlet aliases are normalized — `PowerShell(Invoke-Expression:*)` also matches `iex`, and `-Force` matches `-force`. Bash names stay case-sensitive.
|
|
82
|
+
- File tools take globs (dialect-independent): `Read(//etc/passwd)` (double slash = absolute), `Edit(src/**)`, `Write(package.json)`.
|
|
59
83
|
- MCP: `mcp__filesystem` (whole server) or `mcp__filesystem__read_file` (one tool).
|
|
60
84
|
- Effects: `allow` (no prompt), `ask` (always prompt, even in auto), `deny` (always block).
|
|
61
85
|
|
|
62
|
-
Precedence is **deny-first**: `deny` > `ask` > `allow` > mode. Nothing overrides a `deny`, and nothing overrides the catastrophic floor (`rm -rf /`, `sudo`, private-key/credential reads, writes to secret files like `.env` / `.secrets`, etc. — never relaxable, so don't bother proposing rules for those). Note that editing `config.json`, `mcp.json`, or `agents.md` is *not* on that floor: it is allowed as an approval-gated authoring write (see Step
|
|
86
|
+
Precedence is **deny-first**: `deny` > `ask` > `allow` > mode. Nothing overrides a `deny`, and nothing overrides the catastrophic floor (`rm -rf /`, `sudo`, private-key/credential reads, writes to secret files like `.env` / `.secrets`, etc. — never relaxable, so don't bother proposing rules for those). Note that editing `config.json`, `mcp.json`, or `agents.md` is *not* on that floor: it is allowed as an approval-gated authoring write (see Step 6).
|
|
63
87
|
|
|
64
88
|
### Command-scoped allow rules
|
|
65
89
|
|
|
66
|
-
IMPORTANT: Do not recommend `Bash(*)` as
|
|
67
|
-
Bash(*) doesn't allow every bash command, it isn't the most permissive rule possible.
|
|
68
|
-
Each command is broken down and evaluated independently.
|
|
90
|
+
IMPORTANT: Do not recommend `Bash(*)` (or `PowerShell(*)`). A token-less wildcard is deliberately **rejected as a floor-relaxer** by the engine: install-class commands (package managers, package runners, interpreter installs) keep asking even with `Bash(*)` present, so it does not actually give the blanket auto-approval users expect — it just weakens their allowlist without buying the convenience. Each sub-command of a compound command (`a && b`) is also evaluated independently, so a broad allow can never rescue a sub-command a floor or deny rule blocks.
|
|
69
91
|
|
|
70
|
-
Recommendation: **allow one command family at a time** with `Bash(<tool>:*)
|
|
92
|
+
Recommendation: **allow one command family at a time** with `Bash(<tool>:*)` (or `PowerShell(<tool>:*)` on Windows). Each such rule auto-allows only that executable and its arguments — and, because it names a binary, it *can* relax that command's install-class floor — while everything else still falls back to `ask`/`deny`/mode.
|
|
71
93
|
|
|
72
94
|
- If a user needs to run **all `pnpm` commands**, the ideal rule is `Bash(pnpm:*)` — not `Bash(*)`.
|
|
73
95
|
- Same idea per tool: `Bash(bun:*)` for all `bun` commands, `Bash(cargo:*)` for all `cargo` commands, and so on.
|
|
74
96
|
|
|
75
|
-
Build the allow list from the tools the project actually uses (from Step
|
|
97
|
+
Build the allow list from the tools the project actually uses (from Step 2). **Pick the palette for the detected OS** (Step 1) — use the `Bash(...)` palette on macOS/Linux, the `PowerShell(...)` palette on Windows. Do not mix dialects.
|
|
98
|
+
|
|
99
|
+
**macOS / Linux (`bash` dialect):**
|
|
76
100
|
|
|
77
101
|
```jsonc
|
|
78
102
|
// ❌ do NOT recommend this:
|
|
@@ -107,14 +131,46 @@ Build the allow list from the tools the project actually uses (from Step 1). A p
|
|
|
107
131
|
"Bash(sqlite3:*)", "Bash(ffmpeg:*)", "Bash(ffprobe:*)", "Bash(open:*)"
|
|
108
132
|
```
|
|
109
133
|
|
|
134
|
+
**Windows PowerShell (`pwsh` dialect):** same idea, one command family per rule, but authored with the `PowerShell(...)` tag. Cross-platform tools (git, node, package managers) keep the same executable names; native cmdlets replace the POSIX coreutils. Names are case-insensitive.
|
|
135
|
+
|
|
136
|
+
```jsonc
|
|
137
|
+
// ❌ do NOT recommend this:
|
|
138
|
+
"PowerShell(*)",
|
|
139
|
+
|
|
140
|
+
// ✅ Recommended: scope to one command family each.
|
|
141
|
+
// Package managers / runtimes (same executables as POSIX):
|
|
142
|
+
"PowerShell(bun:*)", "PowerShell(bunx:*)", "PowerShell(pnpm:*)",
|
|
143
|
+
"PowerShell(npm:*)", "PowerShell(npx:*)", "PowerShell(node:*)",
|
|
144
|
+
"PowerShell(python:*)", "PowerShell(py:*)", "PowerShell(uv:*)", "PowerShell(uvx:*)",
|
|
145
|
+
// Lint / format / build / type-check / test:
|
|
146
|
+
"PowerShell(biome:*)", "PowerShell(tsc:*)", "PowerShell(vitest:*)",
|
|
147
|
+
"PowerShell(jest:*)", "PowerShell(eslint:*)", "PowerShell(prettier:*)",
|
|
148
|
+
// Build / task runners:
|
|
149
|
+
"PowerShell(dotnet:*)", "PowerShell(cargo:*)", "PowerShell(go:*)", "PowerShell(msbuild:*)",
|
|
150
|
+
// Version control:
|
|
151
|
+
"PowerShell(git:*)", "PowerShell(gh:*)",
|
|
152
|
+
// Read-only file / text inspection (cmdlets + common aliases):
|
|
153
|
+
"PowerShell(Get-Content:*)", "PowerShell(Get-ChildItem:*)", "PowerShell(Select-String:*)",
|
|
154
|
+
"PowerShell(Get-Item:*)", "PowerShell(Test-Path:*)", "PowerShell(Measure-Object:*)",
|
|
155
|
+
"PowerShell(Write-Output:*)", "PowerShell(Write-Host:*)", "PowerShell(Sort-Object:*)",
|
|
156
|
+
"PowerShell(Where-Object:*)", "PowerShell(ForEach-Object:*)",
|
|
157
|
+
// Environment / navigation:
|
|
158
|
+
"PowerShell(Set-Location:*)", "PowerShell(Get-Location:*)",
|
|
159
|
+
"PowerShell(Set-Item:*)", "PowerShell(Get-Command:*)",
|
|
160
|
+
// Filesystem-mutating — riskier, usually `ask`/`deny`, not blanket `allow`:
|
|
161
|
+
"PowerShell(New-Item:*)", "PowerShell(Copy-Item:*)", "PowerShell(Move-Item:*)",
|
|
162
|
+
"PowerShell(Remove-Item:*)", "PowerShell(Rename-Item:*)"
|
|
163
|
+
```
|
|
164
|
+
|
|
110
165
|
Caveats to relay when proposing these:
|
|
111
166
|
- These are all **fairly permissive** examples; if the user wants this you should add one rule for each command the user has in their history of commands executed.
|
|
112
|
-
- The filesystem-mutating (`rm
|
|
113
|
-
- You can scope tighter than a whole tool: `Bash(pnpm test:*)`
|
|
167
|
+
- The filesystem-mutating families (`rm`/`mv`/`cp`/`chmod` on POSIX; `Remove-Item`/`Move-Item`/`Copy-Item` on PowerShell) and network families (`curl`, `wget`, `Invoke-WebRequest`, `Invoke-RestMethod`) change state or reach outbound — prefer `ask` (or `deny` for the destructive ones) over a blanket `allow`, and confirm each with `ask_user` first.
|
|
168
|
+
- You can scope tighter than a whole tool: `Bash(pnpm test:*)` / `PowerShell(git log:*)` allow only that sub-command. Prefer the narrowest rule that still lets the project's real commands run.
|
|
169
|
+
- **A command rule keyed on a bare name can be bypassed by an absolute path**: a `deny Bash(rm:*)` does *not* block `/bin/rm`, and a `deny Bash(curl:*)` does not block `/usr/bin/curl` (same for `PowerShell(...)`). What guarantees protection against `rm -rf /` and friends is the **catastrophic floor**, not user deny rules. Treat `deny`/`ask` command rules as convenience guardrails, not airtight blocks.
|
|
114
170
|
|
|
115
171
|
---
|
|
116
172
|
|
|
117
|
-
## Step
|
|
173
|
+
## Step 4 — Choose the target scope: project or global
|
|
118
174
|
|
|
119
175
|
Ask the user with `ask_user`:
|
|
120
176
|
|
|
@@ -128,11 +184,11 @@ Map their choice to the `scope` argument (`"project"` or `"global"`) you pass to
|
|
|
128
184
|
|
|
129
185
|
---
|
|
130
186
|
|
|
131
|
-
## Step
|
|
187
|
+
## Step 5 — Apply the rules with `propose_policy`
|
|
132
188
|
|
|
133
189
|
For each rule the user confirmed, call `propose_policy` once:
|
|
134
190
|
|
|
135
|
-
- Add a rule: `op: "add-rule"`, `rule: "<rule string>"`, `effect: "allow" | "ask" | "deny"`, `scope: "<from Step
|
|
191
|
+
- Add a rule: `op: "add-rule"`, `rule: "<rule string>"`, `effect: "allow" | "ask" | "deny"`, `scope: "<from Step 4>"`.
|
|
136
192
|
- Change the mode (if needed): `op: "set-mode"`, `mode: "read-only" | "default" | "auto"`, `scope`.
|
|
137
193
|
- Remove a stale rule: `op: "remove-rule"` with the same `rule` + `effect`.
|
|
138
194
|
|
|
@@ -145,13 +201,13 @@ Do them in a sensible order (deny rules first, then ask, then allow, then any mo
|
|
|
145
201
|
|
|
146
202
|
---
|
|
147
203
|
|
|
148
|
-
## Step
|
|
204
|
+
## Step 6 — Optionally enable the LLM safety-check layer
|
|
149
205
|
|
|
150
206
|
Ask the user with `ask_user`:
|
|
151
207
|
|
|
152
208
|
> "Do you want an **LLM safety check** on commands the agent would otherwise run? When enabled, a classifier model reviews commands and can flag/deny risky ones as an extra layer on top of your rules."
|
|
153
209
|
|
|
154
|
-
This layer is configured under `permissions.llmSafetyCheck` in `config.json`. `propose_policy` does not cover this block, so **edit `config.json` directly** — the same config file they chose in Step
|
|
210
|
+
This layer is configured under `permissions.llmSafetyCheck` in `config.json`. `propose_policy` does not cover this block, so **edit `config.json` directly** — the same config file they chose in Step 4 (`<repo>/.coda/config.json` for project, `~/.coda/config.json` for global) — merging the block into the existing `permissions` object. Read the file first (create it if absent), then apply the change with `edit`/`write`.
|
|
155
211
|
|
|
156
212
|
This is a control-plane authoring write, so CODA will show a **mandatory approval prompt** describing the effect before anything is saved. Tell the user to expect and approve it; if they decline, the block is not written. If you cannot get approval (e.g. headless), fall back to giving them the JSON snippet to paste in by hand.
|
|
157
213
|
|
|
@@ -165,10 +221,7 @@ The block to add:
|
|
|
165
221
|
// Optional: an explicit model list REPLACES the built-in default.
|
|
166
222
|
// Omit this key to use the shipped default classifier models.
|
|
167
223
|
// If you want to use another model, just modify the list.
|
|
168
|
-
"models": ["openai/gpt-5.4", "openai/gpt-5.4", "anthropic/claude-sonnet-4-6", "anthropic/claude-haiku-4-5"]
|
|
169
|
-
// Optional: after this many auto-approvals in an interactive session,
|
|
170
|
-
// a one-time nudge suggests adding an explicit allow rule. Default: 10.
|
|
171
|
-
"nudgeThreshold": 10
|
|
224
|
+
"models": ["openai/gpt-5.4", "openai/gpt-5.4", "anthropic/claude-sonnet-4-6", "anthropic/claude-haiku-4-5"]
|
|
172
225
|
}
|
|
173
226
|
}
|
|
174
227
|
}
|
|
@@ -177,12 +230,11 @@ The block to add:
|
|
|
177
230
|
Notes to give the user:
|
|
178
231
|
- It is **default-off**; an absent block leaves it inert. Set `enabled: true` to turn it on.
|
|
179
232
|
- If `enabled: true` but `models` is an empty list `[]`, the check is inert (a startup warning fires). Omit `models` to use the built-in default, or list at least one model.
|
|
180
|
-
-
|
|
181
|
-
- If they picked **project** scope in Step 3, this belongs in the project config; if **global**, in `~/.coda/config.json`. Since it rides the global→project cascade, a project block overrides the global one.
|
|
233
|
+
- If they picked **project** scope in Step 4, this belongs in the project config; if **global**, in `~/.coda/config.json`. Since it rides the global→project cascade, a project block overrides the global one.
|
|
182
234
|
|
|
183
235
|
---
|
|
184
236
|
|
|
185
|
-
## Step
|
|
237
|
+
## Step 7 — Wrap up: tell the user to restart
|
|
186
238
|
|
|
187
239
|
After applying rules and (optionally) writing the safety-check block, finish with a short summary of what was set, then clearly instruct the user:
|
|
188
240
|
|
package/coda
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
[diffend] Oversized file quarantined before diffing.
|
|
2
2
|
name: package/coda
|
|
3
|
-
size:
|
|
4
|
-
sha256:
|
|
3
|
+
size: 108710184 bytes
|
|
4
|
+
sha256: cb9742b992c992bee91b9447b54238b8a6858709f10d0467ceb53eaa1afdd3e9
|
|
@@ -18,22 +18,23 @@ var __toESM = (mod, isNodeMode, target) => {
|
|
|
18
18
|
return cached;
|
|
19
19
|
}
|
|
20
20
|
target = mod != null ? __create(__getProtoOf(mod)) : {};
|
|
21
|
-
const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
21
|
+
const to = isNodeMode || !mod || !mod.__esModule || !__hasOwnProp.call(mod, "default") ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
|
|
22
|
+
if (mod && typeof mod === "object" || typeof mod === "function") {
|
|
23
|
+
for (let key of __getOwnPropNames(mod))
|
|
24
|
+
if (!__hasOwnProp.call(to, key))
|
|
25
|
+
__defProp(to, key, {
|
|
26
|
+
get: __accessProp.bind(mod, key),
|
|
27
|
+
enumerable: true
|
|
28
|
+
});
|
|
29
|
+
}
|
|
28
30
|
if (canCache)
|
|
29
31
|
cache.set(mod, to);
|
|
30
32
|
return to;
|
|
31
33
|
};
|
|
32
34
|
var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
|
|
33
|
-
var __require = import.meta.require;
|
|
34
35
|
|
|
35
36
|
// ../../node_modules/.pnpm/web-tree-sitter@0.25.10/node_modules/web-tree-sitter/tree-sitter.wasm
|
|
36
|
-
var require_tree_sitter = __commonJS((exports, module2)
|
|
37
|
+
var require_tree_sitter = __commonJS(function(exports, module2) {
|
|
37
38
|
module2.exports = Bun.fileURLToPath(new URL("./tree-sitter-3jzf13jk.wasm", import.meta.url));
|
|
38
39
|
});
|
|
39
40
|
|
|
@@ -1576,7 +1577,7 @@ var Module2 = (() => {
|
|
|
1576
1577
|
throw new Error(response.status + " : " + response.url);
|
|
1577
1578
|
}, "readAsync");
|
|
1578
1579
|
}
|
|
1579
|
-
}
|
|
1580
|
+
}
|
|
1580
1581
|
var out = Module["print"] || console.log.bind(console);
|
|
1581
1582
|
var err = Module["printErr"] || console.error.bind(console);
|
|
1582
1583
|
Object.assign(Module, moduleOverrides);
|
|
@@ -3362,8 +3363,8 @@ class DownloadUtils {
|
|
|
3362
3363
|
return "";
|
|
3363
3364
|
}
|
|
3364
3365
|
}
|
|
3365
|
-
function isBunfsPath(
|
|
3366
|
-
return
|
|
3366
|
+
function isBunfsPath(path2) {
|
|
3367
|
+
return path2.includes("$bunfs") || /^B:[\\/]~BUN/i.test(path2);
|
|
3367
3368
|
}
|
|
3368
3369
|
function getBunfsRootPath() {
|
|
3369
3370
|
return process.platform === "win32" ? "B:\\~BUN\\root" : "/$bunfs/root";
|
|
@@ -3463,8 +3464,8 @@ var stripANSI = bun?.stripANSI ?? stripAnsi;
|
|
|
3463
3464
|
var writeFile2 = bun?.write ?? writeFilePortable;
|
|
3464
3465
|
async function resolveBundledFilePath(loadBundledFile, fallbackPath, metaUrl) {
|
|
3465
3466
|
if (!bun) {
|
|
3466
|
-
const
|
|
3467
|
-
return fileURLToPath(
|
|
3467
|
+
const path2 = typeof fallbackPath === "function" ? fallbackPath() : fallbackPath;
|
|
3468
|
+
return fileURLToPath(path2 instanceof URL ? path2 : new URL(path2, metaUrl));
|
|
3468
3469
|
}
|
|
3469
3470
|
const loadedPath = (await loadBundledFile()).default;
|
|
3470
3471
|
if (loadedPath.startsWith("file:")) {
|