@globant/coda-windows-x64 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/assets/agents/coda-help.md +166 -0
- package/assets/agents/create-workflow.md +264 -0
- package/assets/agents/explore.md +26 -0
- package/assets/docs/agents.md +162 -0
- package/assets/docs/cli-reference.md +131 -0
- package/assets/docs/cli-vs-batch.md +58 -0
- package/assets/docs/config-json.md +314 -0
- package/assets/docs/config-reference.md +329 -0
- package/assets/docs/configuration.md +105 -0
- package/assets/docs/connect-provider.md +77 -0
- package/assets/docs/extensions.md +260 -0
- package/assets/docs/faq.md +152 -0
- package/assets/docs/glossary.md +41 -0
- package/assets/docs/guide-automate.md +135 -0
- package/assets/docs/guide-changes.md +101 -0
- package/assets/docs/guide-collaborate.md +119 -0
- package/assets/docs/guide-extend.md +120 -0
- package/assets/docs/guide-understand.md +95 -0
- package/assets/docs/hooks.md +704 -0
- package/assets/docs/how-it-works.md +73 -0
- package/assets/docs/index.md +62 -0
- package/assets/docs/installation.md +71 -0
- package/assets/docs/logging.md +123 -0
- package/assets/docs/overview.md +91 -0
- package/assets/docs/permissions.md +93 -0
- package/assets/docs/quickstart.md +104 -0
- package/assets/docs/sessions.md +139 -0
- package/assets/docs/shortcuts.md +61 -0
- package/assets/docs/tools-reference.md +81 -0
- package/assets/docs/workflows.md +146 -0
- package/assets/skills/create-extension/SKILL.md +293 -0
- package/assets/skills/create-hook/SKILL.md +442 -0
- package/assets/skills/create-skill/SKILL.md +180 -0
- package/assets/skills/plan/SKILL.md +25 -0
- package/coda.exe +0 -0
- package/lib/keytar/build/Release/keytar.node +0 -0
- package/lib/keytar/lib/keytar.js +43 -0
- package/lib/opentui/assets/javascript/highlights.scm +205 -0
- package/lib/opentui/assets/javascript/tree-sitter-javascript.wasm +0 -0
- package/lib/opentui/assets/markdown/highlights.scm +150 -0
- package/lib/opentui/assets/markdown/injections.scm +27 -0
- package/lib/opentui/assets/markdown/tree-sitter-markdown.wasm +0 -0
- package/lib/opentui/assets/markdown_inline/highlights.scm +115 -0
- package/lib/opentui/assets/markdown_inline/tree-sitter-markdown_inline.wasm +0 -0
- package/lib/opentui/assets/typescript/highlights.scm +604 -0
- package/lib/opentui/assets/typescript/tree-sitter-typescript.wasm +0 -0
- package/lib/opentui/assets/zig/highlights.scm +284 -0
- package/lib/opentui/assets/zig/tree-sitter-zig.wasm +0 -0
- package/lib/opentui/parser.worker.js +4244 -0
- package/lib/opentui/tree-sitter-3jzf13jk.wasm +0 -0
- package/lib/ripgrep/COPYING +3 -0
- package/lib/ripgrep/LICENSE-MIT +21 -0
- package/lib/ripgrep/UNLICENSE +24 -0
- package/lib/ripgrep/rg.exe +0 -0
- package/package.json +20 -0
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: create-extension
|
|
3
|
+
description: Create g-coda extensions that register tools, slash commands, lifecycle hooks, and providers. Use when the user wants to create, write, or scaffold a new extension, or asks about the extension API, tool registration, or command registration.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Creating g-coda Extensions
|
|
7
|
+
|
|
8
|
+
## Quick start
|
|
9
|
+
|
|
10
|
+
An extension is a TypeScript file that exports `activate` or a `default` function receiving `ExtensionAPI`. Place it in `.coda/extensions/` (project) or `~/.coda/extensions/` (global).
|
|
11
|
+
|
|
12
|
+
```typescript
|
|
13
|
+
import { z, type ExtensionAPI, type AgentTool, type ToolContext } from "@globant/coda-core";
|
|
14
|
+
|
|
15
|
+
export default function activate(api: ExtensionAPI): void {
|
|
16
|
+
// Register tools, commands, hooks, shortcuts, providers
|
|
17
|
+
}
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
`@globant/coda-core` re-exports `z` (Zod) and `resolveGitWorkspaceRoot` so extensions only need one import source.
|
|
21
|
+
|
|
22
|
+
## Extension locations
|
|
23
|
+
|
|
24
|
+
| Priority | Source | Scope |
|
|
25
|
+
|----------|--------|-------|
|
|
26
|
+
| 1 | `.coda/extensions/*.ts` | Project-level (highest) |
|
|
27
|
+
| 2 | `~/.coda/extensions/*.ts` | User-global |
|
|
28
|
+
| 3 | `config.json` → `extensions[]` | Configured paths |
|
|
29
|
+
| 4 | `-e ./path.ts` | CLI flag |
|
|
30
|
+
|
|
31
|
+
Subdirectories with `package.json` containing `coda.extensions` or `index.ts` are also discovered.
|
|
32
|
+
|
|
33
|
+
## Registering tools
|
|
34
|
+
|
|
35
|
+
Tools are functions the LLM can call. Use Zod for parameter schemas.
|
|
36
|
+
|
|
37
|
+
```typescript
|
|
38
|
+
const myTool: AgentTool<MyParams, string> = {
|
|
39
|
+
name: "my_tool",
|
|
40
|
+
description: "Clear description of what the tool does and when to use it",
|
|
41
|
+
parameters: z.object({
|
|
42
|
+
query: z.string().describe("What to search for"),
|
|
43
|
+
limit: z.number().int().positive().optional().describe("Max results"),
|
|
44
|
+
}),
|
|
45
|
+
label: "My Tool",
|
|
46
|
+
execute: async (
|
|
47
|
+
toolCallId: string,
|
|
48
|
+
params: MyParams,
|
|
49
|
+
context: ToolContext,
|
|
50
|
+
): Promise<string> => {
|
|
51
|
+
const cwd = context.cwd ?? process.cwd();
|
|
52
|
+
// Implementation here
|
|
53
|
+
return "result string returned to the LLM";
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
api.registerTool(myTool);
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### AgentTool interface
|
|
61
|
+
|
|
62
|
+
```typescript
|
|
63
|
+
interface AgentTool<TParams = unknown, TResult = unknown> {
|
|
64
|
+
readonly name: string;
|
|
65
|
+
readonly description: string;
|
|
66
|
+
readonly parameters: z.ZodTypeAny;
|
|
67
|
+
readonly label: string;
|
|
68
|
+
execute(toolCallId: string, params: TParams, context: ToolContext): Promise<TResult>;
|
|
69
|
+
}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### ToolContext (available inside execute)
|
|
73
|
+
|
|
74
|
+
| Field | Type | Purpose |
|
|
75
|
+
|-------|------|---------|
|
|
76
|
+
| `cwd` | `string?` | Project directory |
|
|
77
|
+
| `signal` | `AbortSignal?` | Cancellation signal |
|
|
78
|
+
| `hitl` | `IHitlManager` | Human-in-the-loop prompts |
|
|
79
|
+
| `operations` | `Operations` | `readFile`, `writeFile`, `listFiles` |
|
|
80
|
+
| `sessionId` | `string` | Current session ID |
|
|
81
|
+
| `mcp` | `IMCPService?` | MCP server access |
|
|
82
|
+
| `onUpdate` | `(update) => void` | Stream progress updates |
|
|
83
|
+
| `model` | `ToolContextModelInfo?` | Current model info |
|
|
84
|
+
|
|
85
|
+
### Tool description tips
|
|
86
|
+
|
|
87
|
+
- Start with the category in CAPS (e.g. `"SEMANTIC CODE SEARCH (colgrep)."`)
|
|
88
|
+
- Explain when the LLM should choose this tool over alternatives
|
|
89
|
+
- Include parameter examples in the description
|
|
90
|
+
- For complex tools, use a `parametersForLlm` schema (small surface: `command` + `args` as JSON string) and validate with a full schema after merging
|
|
91
|
+
|
|
92
|
+
## Registering slash commands
|
|
93
|
+
|
|
94
|
+
Commands are invoked by the user via `/name args`.
|
|
95
|
+
|
|
96
|
+
```typescript
|
|
97
|
+
api.registerCommand("deploy", {
|
|
98
|
+
description: "Deploy the application to staging or production",
|
|
99
|
+
getArgumentCompletions: (prefix) => {
|
|
100
|
+
const options = [
|
|
101
|
+
{ value: "staging", label: "staging — deploy to staging" },
|
|
102
|
+
{ value: "production", label: "production — deploy to production" },
|
|
103
|
+
];
|
|
104
|
+
const p = prefix.trim().toLowerCase();
|
|
105
|
+
if (!p) return options;
|
|
106
|
+
return options.filter((o) => o.value.startsWith(p));
|
|
107
|
+
},
|
|
108
|
+
handler: async (args, ctx) => {
|
|
109
|
+
if (!args.trim()) {
|
|
110
|
+
ctx.addMessage?.("system", "Usage: /deploy [staging|production]");
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
// ctx.sendMessage sends a user message that triggers an agent response
|
|
114
|
+
await ctx.sendMessage?.(`Deploy to ${args.trim()}`);
|
|
115
|
+
},
|
|
116
|
+
});
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### ExtensionCommandContext (available in handler)
|
|
120
|
+
|
|
121
|
+
| Field | Purpose |
|
|
122
|
+
|-------|---------|
|
|
123
|
+
| `cwd` | Current working directory |
|
|
124
|
+
| `addMessage(role, content)` | Add system/error/user message to chat |
|
|
125
|
+
| `sendMessage(text)` | Send as user message (triggers agent response) |
|
|
126
|
+
| `clearMessages()` | Clear conversation |
|
|
127
|
+
| `setModel(id)` | Switch model |
|
|
128
|
+
| `exit()` | Close the CLI |
|
|
129
|
+
| `session` | AgentSession (read-only) |
|
|
130
|
+
| `ui` | `select`, `confirm`, `input`, `notify`, `setStatus` |
|
|
131
|
+
| `showModelPicker()` | Open model picker overlay |
|
|
132
|
+
| `showSettings()` | Open settings overlay |
|
|
133
|
+
| `showMcpManager()` | Open MCP manager overlay |
|
|
134
|
+
| `mcp`, `sessionId` | MCP service and session refs |
|
|
135
|
+
| `waitForIdle()` | Wait until agent finishes current turn |
|
|
136
|
+
|
|
137
|
+
### Reserved command names
|
|
138
|
+
|
|
139
|
+
Extensions cannot override these built-in commands:
|
|
140
|
+
|
|
141
|
+
```
|
|
142
|
+
model, models, clear, compact, exit, quit, help, mcp, settings
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
## Subscribing to lifecycle hooks
|
|
146
|
+
|
|
147
|
+
```typescript
|
|
148
|
+
api.on("session_start", async ({ sessionId }) => {
|
|
149
|
+
console.error("Session started:", sessionId);
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
api.on("context", async (context) => {
|
|
153
|
+
// Mutate in place before each LLM call
|
|
154
|
+
context.systemPrompt += "\n\n## My custom instructions\n...";
|
|
155
|
+
});
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### Available hooks
|
|
159
|
+
|
|
160
|
+
| Hook | Payload | Description |
|
|
161
|
+
|------|---------|-------------|
|
|
162
|
+
| `session_start` | `{ sessionId }` | After session creation |
|
|
163
|
+
| `session_end` | `{ sessionId }` | Session ends |
|
|
164
|
+
| `session_shutdown` | `{}` | Process exit; flush buffers |
|
|
165
|
+
| `before_agent_start` | `{ text, sessionId }` | Before agent loop |
|
|
166
|
+
| `agent_start` / `agent_end` | `{ turnId }` | Agent loop boundaries |
|
|
167
|
+
| `turn_start` / `turn_end` | `{ turnId }` | Per-LLM-turn boundaries |
|
|
168
|
+
| `context` | `{ systemPrompt, messages, tools }` | Mutate before LLM call |
|
|
169
|
+
| `input` | `InputEvent` | Transform user input before send |
|
|
170
|
+
| `tool_call` | `ToolCallEvent` | Intercept/deny tool calls |
|
|
171
|
+
| `tool_result` | `ToolResultEvent` | Modify tool results |
|
|
172
|
+
| `model_select` | — | When `setModel()` is called |
|
|
173
|
+
| `session_before_compact` | — | Before compaction; return `{ cancel: true }` to cancel |
|
|
174
|
+
|
|
175
|
+
## Tool interception
|
|
176
|
+
|
|
177
|
+
Block dangerous tool calls or modify results:
|
|
178
|
+
|
|
179
|
+
```typescript
|
|
180
|
+
api.on("tool_call", async ({ toolName, input }) => {
|
|
181
|
+
if (toolName === "bash" && input.command?.includes("rm -rf /")) {
|
|
182
|
+
return { deny: true, reason: "Blocked destructive command" };
|
|
183
|
+
}
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
api.on("tool_result", async (event) => {
|
|
187
|
+
if (event.toolName === "read") {
|
|
188
|
+
event.content = redactSecrets(event.content);
|
|
189
|
+
}
|
|
190
|
+
});
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
## Input transformation
|
|
194
|
+
|
|
195
|
+
Transform or intercept user messages before they reach the agent:
|
|
196
|
+
|
|
197
|
+
```typescript
|
|
198
|
+
api.on("input", async ({ text, source }) => {
|
|
199
|
+
if (text.startsWith("!")) {
|
|
200
|
+
return { action: "transform", text: `Execute shell command: ${text.slice(1)}` };
|
|
201
|
+
}
|
|
202
|
+
return { action: "continue" };
|
|
203
|
+
});
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
Return values: `{ action: "continue" }` (pass through), `{ action: "transform", text }` (replace), `{ action: "handled" }` (swallow).
|
|
207
|
+
|
|
208
|
+
## Keyboard shortcuts (CLI only)
|
|
209
|
+
|
|
210
|
+
```typescript
|
|
211
|
+
api.registerShortcut?.("ctrl+shift+d", {
|
|
212
|
+
description: "Quick deploy",
|
|
213
|
+
handler: (ctx) => {
|
|
214
|
+
ctx.addMessage?.("system", "Deploying...");
|
|
215
|
+
},
|
|
216
|
+
});
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
Reserved shortcuts that cannot be overridden:
|
|
220
|
+
|
|
221
|
+
```
|
|
222
|
+
escape, ctrl+c, ctrl+l, ctrl+o, ctrl+u, ctrl+d, ctrl+j, ctrl+s, ctrl+shift+c
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
## Provider registration
|
|
226
|
+
|
|
227
|
+
Register a custom model provider:
|
|
228
|
+
|
|
229
|
+
```typescript
|
|
230
|
+
api.registerProvider?.("my-llm", {
|
|
231
|
+
baseUrl: "https://api.example.com",
|
|
232
|
+
apiKey: process.env.MY_API_KEY,
|
|
233
|
+
models: [{ id: "my-model", name: "My Model", contextWindow: 128000 }],
|
|
234
|
+
});
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
Providers are merged into config before session creation.
|
|
238
|
+
|
|
239
|
+
## Spawning subprocesses (CLI tool pattern)
|
|
240
|
+
|
|
241
|
+
For tools that wrap external binaries, use `child_process.spawn`:
|
|
242
|
+
|
|
243
|
+
```typescript
|
|
244
|
+
import { spawn } from "node:child_process";
|
|
245
|
+
|
|
246
|
+
function runBinary(
|
|
247
|
+
args: readonly string[],
|
|
248
|
+
cwd: string,
|
|
249
|
+
timeoutMs: number,
|
|
250
|
+
): Promise<{ stdout: string; stderr: string; exitCode: number }> {
|
|
251
|
+
return new Promise((resolve, reject) => {
|
|
252
|
+
const child = spawn("my-binary", args, {
|
|
253
|
+
cwd,
|
|
254
|
+
env: process.env,
|
|
255
|
+
shell: false,
|
|
256
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
257
|
+
});
|
|
258
|
+
let stdout = "";
|
|
259
|
+
let stderr = "";
|
|
260
|
+
child.stdout?.setEncoding("utf8");
|
|
261
|
+
child.stderr?.setEncoding("utf8");
|
|
262
|
+
child.stdout?.on("data", (chunk: string) => { stdout += chunk; });
|
|
263
|
+
child.stderr?.on("data", (chunk: string) => { stderr += chunk; });
|
|
264
|
+
const timer = setTimeout(() => child.kill("SIGTERM"), timeoutMs);
|
|
265
|
+
child.on("error", (err) => { clearTimeout(timer); reject(err); });
|
|
266
|
+
child.on("close", (code) => {
|
|
267
|
+
clearTimeout(timer);
|
|
268
|
+
resolve({ stdout, stderr, exitCode: code ?? -1 });
|
|
269
|
+
});
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
Wire the spawn helper into `execute` and pass `context.signal` for cancellation support.
|
|
275
|
+
|
|
276
|
+
## Checklist
|
|
277
|
+
|
|
278
|
+
Before delivering an extension, verify:
|
|
279
|
+
|
|
280
|
+
- [ ] Exports `activate` or `default` function receiving `ExtensionAPI`
|
|
281
|
+
- [ ] Imports types and `z` from `@globant/coda-core` (not directly from `zod`)
|
|
282
|
+
- [ ] Tool `description` clearly explains when the LLM should use it
|
|
283
|
+
- [ ] Tool `parameters` use Zod with `.describe()` on each field
|
|
284
|
+
- [ ] Tool `execute` returns a string (the LLM reads the return value)
|
|
285
|
+
- [ ] Command names don't collide with reserved built-ins
|
|
286
|
+
- [ ] Shortcut IDs don't collide with reserved shortcuts
|
|
287
|
+
- [ ] Error handling returns user-friendly messages (not raw stack traces)
|
|
288
|
+
- [ ] Subprocess tools respect `context.signal` for cancellation
|
|
289
|
+
- [ ] File placed in `.coda/extensions/` (project) or `~/.coda/extensions/` (global)
|
|
290
|
+
|
|
291
|
+
## Reference
|
|
292
|
+
|
|
293
|
+
For the full architecture (hook flow diagrams, desktop integration, runner internals), see the Coda extensions guide shipped with the CLI under `docs/` in the g-coda repository (`docs/core/extensions/extensions.md`).
|