gitclaw 0.3.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 +440 -0
- package/dist/agents.d.ts +8 -0
- package/dist/agents.js +82 -0
- package/dist/audit.d.ts +27 -0
- package/dist/audit.js +55 -0
- package/dist/compliance.d.ts +30 -0
- package/dist/compliance.js +108 -0
- package/dist/config.d.ts +11 -0
- package/dist/config.js +43 -0
- package/dist/examples.d.ts +6 -0
- package/dist/examples.js +40 -0
- package/dist/exports.d.ts +13 -0
- package/dist/exports.js +6 -0
- package/dist/hooks.d.ts +24 -0
- package/dist/hooks.js +108 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +542 -0
- package/dist/knowledge.d.ts +17 -0
- package/dist/knowledge.js +55 -0
- package/dist/loader.d.ts +64 -0
- package/dist/loader.js +222 -0
- package/dist/sandbox.d.ts +28 -0
- package/dist/sandbox.js +54 -0
- package/dist/sdk-hooks.d.ts +8 -0
- package/dist/sdk-hooks.js +31 -0
- package/dist/sdk-types.d.ts +127 -0
- package/dist/sdk-types.js +1 -0
- package/dist/sdk.d.ts +6 -0
- package/dist/sdk.js +444 -0
- package/dist/session.d.ts +15 -0
- package/dist/session.js +127 -0
- package/dist/skills.d.ts +18 -0
- package/dist/skills.js +104 -0
- package/dist/tool-loader.d.ts +3 -0
- package/dist/tool-loader.js +138 -0
- package/dist/tools/cli.d.ts +3 -0
- package/dist/tools/cli.js +86 -0
- package/dist/tools/index.d.ts +13 -0
- package/dist/tools/index.js +29 -0
- package/dist/tools/memory.d.ts +3 -0
- package/dist/tools/memory.js +128 -0
- package/dist/tools/read.d.ts +3 -0
- package/dist/tools/read.js +46 -0
- package/dist/tools/sandbox-cli.d.ts +4 -0
- package/dist/tools/sandbox-cli.js +48 -0
- package/dist/tools/sandbox-memory.d.ts +4 -0
- package/dist/tools/sandbox-memory.js +117 -0
- package/dist/tools/sandbox-read.d.ts +4 -0
- package/dist/tools/sandbox-read.js +25 -0
- package/dist/tools/sandbox-write.d.ts +4 -0
- package/dist/tools/sandbox-write.js +26 -0
- package/dist/tools/shared.d.ts +38 -0
- package/dist/tools/shared.js +69 -0
- package/dist/tools/write.d.ts +3 -0
- package/dist/tools/write.js +28 -0
- package/dist/workflows.d.ts +8 -0
- package/dist/workflows.js +81 -0
- package/package.json +57 -0
package/README.md
ADDED
|
@@ -0,0 +1,440 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="https://img.shields.io/npm/v/gitclaw?style=flat-square&color=blue" alt="npm version" />
|
|
3
|
+
<img src="https://img.shields.io/badge/node-%3E%3D20-brightgreen?style=flat-square" alt="node version" />
|
|
4
|
+
<img src="https://img.shields.io/github/license/open-gitagent/gitclaw?style=flat-square" alt="license" />
|
|
5
|
+
<img src="https://img.shields.io/badge/TypeScript-5.7-blue?style=flat-square&logo=typescript&logoColor=white" alt="typescript" />
|
|
6
|
+
</p>
|
|
7
|
+
|
|
8
|
+
<h1 align="center">Gitclaw</h1>
|
|
9
|
+
|
|
10
|
+
<p align="center">
|
|
11
|
+
<strong>A universal git-native AI agent framework.</strong><br/>
|
|
12
|
+
Your agent lives inside a git repo — identity, rules, memory, tools, and skills are all version-controlled files.
|
|
13
|
+
</p>
|
|
14
|
+
|
|
15
|
+
<p align="center">
|
|
16
|
+
<a href="#quick-start">Quick Start</a> •
|
|
17
|
+
<a href="#sdk">SDK</a> •
|
|
18
|
+
<a href="#architecture">Architecture</a> •
|
|
19
|
+
<a href="#tools">Tools</a> •
|
|
20
|
+
<a href="#hooks">Hooks</a> •
|
|
21
|
+
<a href="#skills">Skills</a>
|
|
22
|
+
</p>
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## Why Gitclaw?
|
|
27
|
+
|
|
28
|
+
Most agent frameworks treat configuration as code scattered across your application. Gitclaw flips this — **your agent IS a git repository**:
|
|
29
|
+
|
|
30
|
+
- **`agent.yaml`** — model, tools, runtime config
|
|
31
|
+
- **`SOUL.md`** — personality and identity
|
|
32
|
+
- **`RULES.md`** — behavioral constraints
|
|
33
|
+
- **`memory/`** — git-committed memory with full history
|
|
34
|
+
- **`tools/`** — declarative YAML tool definitions
|
|
35
|
+
- **`skills/`** — composable skill modules
|
|
36
|
+
- **`hooks/`** — lifecycle hooks (script or programmatic)
|
|
37
|
+
|
|
38
|
+
Fork an agent. Branch a personality. `git log` your agent's memory. Diff its rules. This is **agents as repos**.
|
|
39
|
+
|
|
40
|
+
## Quick Start
|
|
41
|
+
|
|
42
|
+
### CLI
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
npm install -g gitclaw
|
|
46
|
+
|
|
47
|
+
# Set your API key
|
|
48
|
+
export OPENAI_API_KEY="sk-..."
|
|
49
|
+
# or: export ANTHROPIC_API_KEY="sk-ant-..."
|
|
50
|
+
|
|
51
|
+
# Point gitclaw at any directory — it handles everything
|
|
52
|
+
gitclaw --dir ~/my-project
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
That's it. Gitclaw auto-scaffolds everything on first run:
|
|
56
|
+
- `git init` if not already a repo
|
|
57
|
+
- Creates `agent.yaml`, `SOUL.md`, `memory/MEMORY.md`
|
|
58
|
+
- Commits the scaffold
|
|
59
|
+
- Drops you into the REPL
|
|
60
|
+
|
|
61
|
+
```
|
|
62
|
+
? Repository path (. for current dir): ~/my-project
|
|
63
|
+
Initializing git repository...
|
|
64
|
+
Created agent.yaml (model: openai:gpt-4o-mini)
|
|
65
|
+
my-project v0.1.0
|
|
66
|
+
Model: openai:gpt-4o-mini
|
|
67
|
+
Tools: cli, read, write, memory
|
|
68
|
+
→ List all files and explain the project
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Or run without `--dir` and it will ask you interactively:
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
gitclaw
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Single-shot mode:
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
gitclaw --dir ~/my-project -p "Create a hello world script"
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### SDK
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
npm install gitclaw
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
```typescript
|
|
90
|
+
import { query, tool } from "gitclaw";
|
|
91
|
+
|
|
92
|
+
// Simple query
|
|
93
|
+
for await (const msg of query({
|
|
94
|
+
prompt: "List all TypeScript files and summarize them",
|
|
95
|
+
dir: "./my-agent",
|
|
96
|
+
model: "openai:gpt-4o-mini",
|
|
97
|
+
})) {
|
|
98
|
+
if (msg.type === "delta") process.stdout.write(msg.content);
|
|
99
|
+
if (msg.type === "assistant") console.log("\n\nDone.");
|
|
100
|
+
}
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## SDK
|
|
104
|
+
|
|
105
|
+
The SDK provides a programmatic interface to Gitclaw agents. It mirrors the [Claude Agent SDK](https://github.com/anthropics/claude-code-sdk) pattern but runs **in-process** — no subprocesses, no IPC.
|
|
106
|
+
|
|
107
|
+
### `query(options): Query`
|
|
108
|
+
|
|
109
|
+
Returns an `AsyncGenerator<GCMessage>` that streams agent events.
|
|
110
|
+
|
|
111
|
+
```typescript
|
|
112
|
+
import { query } from "gitclaw";
|
|
113
|
+
|
|
114
|
+
for await (const msg of query({
|
|
115
|
+
prompt: "Refactor the auth module",
|
|
116
|
+
dir: "/path/to/agent",
|
|
117
|
+
model: "anthropic:claude-sonnet-4-5-20250929",
|
|
118
|
+
})) {
|
|
119
|
+
switch (msg.type) {
|
|
120
|
+
case "delta": // streaming text chunk
|
|
121
|
+
process.stdout.write(msg.content);
|
|
122
|
+
break;
|
|
123
|
+
case "assistant": // complete response
|
|
124
|
+
console.log(`\nTokens: ${msg.usage?.totalTokens}`);
|
|
125
|
+
break;
|
|
126
|
+
case "tool_use": // tool invocation
|
|
127
|
+
console.log(`Tool: ${msg.toolName}(${JSON.stringify(msg.args)})`);
|
|
128
|
+
break;
|
|
129
|
+
case "tool_result": // tool output
|
|
130
|
+
console.log(`Result: ${msg.content}`);
|
|
131
|
+
break;
|
|
132
|
+
case "system": // lifecycle events & errors
|
|
133
|
+
console.log(`[${msg.subtype}] ${msg.content}`);
|
|
134
|
+
break;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### `tool(name, description, schema, handler): GCToolDefinition`
|
|
140
|
+
|
|
141
|
+
Define custom tools the agent can call:
|
|
142
|
+
|
|
143
|
+
```typescript
|
|
144
|
+
import { query, tool } from "gitclaw";
|
|
145
|
+
|
|
146
|
+
const search = tool(
|
|
147
|
+
"search_docs",
|
|
148
|
+
"Search the documentation",
|
|
149
|
+
{
|
|
150
|
+
properties: {
|
|
151
|
+
query: { type: "string", description: "Search query" },
|
|
152
|
+
limit: { type: "number", description: "Max results" },
|
|
153
|
+
},
|
|
154
|
+
required: ["query"],
|
|
155
|
+
},
|
|
156
|
+
async (args) => {
|
|
157
|
+
const results = await mySearchEngine(args.query, args.limit ?? 10);
|
|
158
|
+
return { text: JSON.stringify(results), details: { count: results.length } };
|
|
159
|
+
},
|
|
160
|
+
);
|
|
161
|
+
|
|
162
|
+
for await (const msg of query({
|
|
163
|
+
prompt: "Find docs about authentication",
|
|
164
|
+
tools: [search],
|
|
165
|
+
})) {
|
|
166
|
+
// agent can now call search_docs
|
|
167
|
+
}
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
### Hooks
|
|
171
|
+
|
|
172
|
+
Programmatic lifecycle hooks for gating, logging, and control:
|
|
173
|
+
|
|
174
|
+
```typescript
|
|
175
|
+
for await (const msg of query({
|
|
176
|
+
prompt: "Deploy the service",
|
|
177
|
+
hooks: {
|
|
178
|
+
preToolUse: async (ctx) => {
|
|
179
|
+
// Block dangerous operations
|
|
180
|
+
if (ctx.toolName === "cli" && ctx.args.command?.includes("rm -rf"))
|
|
181
|
+
return { action: "block", reason: "Destructive command blocked" };
|
|
182
|
+
|
|
183
|
+
// Modify arguments
|
|
184
|
+
if (ctx.toolName === "write" && !ctx.args.path.startsWith("/safe/"))
|
|
185
|
+
return { action: "modify", args: { ...ctx.args, path: `/safe/${ctx.args.path}` } };
|
|
186
|
+
|
|
187
|
+
return { action: "allow" };
|
|
188
|
+
},
|
|
189
|
+
onError: async (ctx) => {
|
|
190
|
+
console.error(`Agent error: ${ctx.error}`);
|
|
191
|
+
},
|
|
192
|
+
},
|
|
193
|
+
})) {
|
|
194
|
+
// ...
|
|
195
|
+
}
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
### QueryOptions Reference
|
|
199
|
+
|
|
200
|
+
| Option | Type | Description |
|
|
201
|
+
|---|---|---|
|
|
202
|
+
| `prompt` | `string \| AsyncIterable` | User prompt or multi-turn stream |
|
|
203
|
+
| `dir` | `string` | Agent directory (default: `cwd`) |
|
|
204
|
+
| `model` | `string` | `"provider:model-id"` |
|
|
205
|
+
| `env` | `string` | Environment config (`config/<env>.yaml`) |
|
|
206
|
+
| `systemPrompt` | `string` | Override discovered system prompt |
|
|
207
|
+
| `systemPromptSuffix` | `string` | Append to discovered system prompt |
|
|
208
|
+
| `tools` | `GCToolDefinition[]` | Additional tools |
|
|
209
|
+
| `replaceBuiltinTools` | `boolean` | Skip cli/read/write/memory |
|
|
210
|
+
| `allowedTools` | `string[]` | Tool name allowlist |
|
|
211
|
+
| `disallowedTools` | `string[]` | Tool name denylist |
|
|
212
|
+
| `hooks` | `GCHooks` | Programmatic lifecycle hooks |
|
|
213
|
+
| `maxTurns` | `number` | Max agent turns |
|
|
214
|
+
| `abortController` | `AbortController` | Cancellation signal |
|
|
215
|
+
| `constraints` | `object` | `temperature`, `maxTokens`, `topP`, `topK` |
|
|
216
|
+
|
|
217
|
+
### Message Types
|
|
218
|
+
|
|
219
|
+
| Type | Description | Key Fields |
|
|
220
|
+
|---|---|---|
|
|
221
|
+
| `delta` | Streaming text/thinking chunk | `deltaType`, `content` |
|
|
222
|
+
| `assistant` | Complete LLM response | `content`, `model`, `usage`, `stopReason` |
|
|
223
|
+
| `tool_use` | Tool invocation | `toolName`, `args`, `toolCallId` |
|
|
224
|
+
| `tool_result` | Tool output | `content`, `isError`, `toolCallId` |
|
|
225
|
+
| `system` | Lifecycle events | `subtype`, `content`, `metadata` |
|
|
226
|
+
| `user` | User message (multi-turn) | `content` |
|
|
227
|
+
|
|
228
|
+
## Architecture
|
|
229
|
+
|
|
230
|
+
```
|
|
231
|
+
my-agent/
|
|
232
|
+
├── agent.yaml # Model, tools, runtime config
|
|
233
|
+
├── SOUL.md # Agent identity & personality
|
|
234
|
+
├── RULES.md # Behavioral rules & constraints
|
|
235
|
+
├── DUTIES.md # Role-specific responsibilities
|
|
236
|
+
├── memory/
|
|
237
|
+
│ └── MEMORY.md # Git-committed agent memory
|
|
238
|
+
├── tools/
|
|
239
|
+
│ └── *.yaml # Declarative tool definitions
|
|
240
|
+
├── skills/
|
|
241
|
+
│ └── <name>/
|
|
242
|
+
│ ├── SKILL.md # Skill instructions (YAML frontmatter)
|
|
243
|
+
│ └── scripts/ # Skill scripts
|
|
244
|
+
├── workflows/
|
|
245
|
+
│ └── *.yaml|*.md # Multi-step workflow definitions
|
|
246
|
+
├── agents/
|
|
247
|
+
│ └── <name>/ # Sub-agent definitions
|
|
248
|
+
├── hooks/
|
|
249
|
+
│ └── hooks.yaml # Lifecycle hook scripts
|
|
250
|
+
├── knowledge/
|
|
251
|
+
│ └── index.yaml # Knowledge base entries
|
|
252
|
+
├── config/
|
|
253
|
+
│ ├── default.yaml # Default environment config
|
|
254
|
+
│ └── <env>.yaml # Environment overrides
|
|
255
|
+
├── examples/
|
|
256
|
+
│ └── *.md # Few-shot examples
|
|
257
|
+
└── compliance/
|
|
258
|
+
└── *.yaml # Compliance & audit config
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
### Agent Manifest (`agent.yaml`)
|
|
262
|
+
|
|
263
|
+
```yaml
|
|
264
|
+
spec_version: "0.1.0"
|
|
265
|
+
name: my-agent
|
|
266
|
+
version: 1.0.0
|
|
267
|
+
description: An agent that does things
|
|
268
|
+
|
|
269
|
+
model:
|
|
270
|
+
preferred: "anthropic:claude-sonnet-4-5-20250929"
|
|
271
|
+
fallback: ["openai:gpt-4o"]
|
|
272
|
+
constraints:
|
|
273
|
+
temperature: 0.7
|
|
274
|
+
max_tokens: 4096
|
|
275
|
+
|
|
276
|
+
tools: [cli, read, write, memory]
|
|
277
|
+
|
|
278
|
+
runtime:
|
|
279
|
+
max_turns: 50
|
|
280
|
+
timeout: 120
|
|
281
|
+
|
|
282
|
+
# Optional
|
|
283
|
+
extends: "https://github.com/org/base-agent.git"
|
|
284
|
+
skills: [code-review, deploy]
|
|
285
|
+
delegation:
|
|
286
|
+
mode: auto
|
|
287
|
+
compliance:
|
|
288
|
+
risk_level: medium
|
|
289
|
+
human_in_the_loop: true
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
## Tools
|
|
293
|
+
|
|
294
|
+
### Built-in Tools
|
|
295
|
+
|
|
296
|
+
| Tool | Description |
|
|
297
|
+
|---|---|
|
|
298
|
+
| `cli` | Execute shell commands |
|
|
299
|
+
| `read` | Read files with pagination |
|
|
300
|
+
| `write` | Write/create files |
|
|
301
|
+
| `memory` | Load/save git-committed memory |
|
|
302
|
+
|
|
303
|
+
### Declarative Tools
|
|
304
|
+
|
|
305
|
+
Define tools as YAML in `tools/`:
|
|
306
|
+
|
|
307
|
+
```yaml
|
|
308
|
+
# tools/search.yaml
|
|
309
|
+
name: search
|
|
310
|
+
description: Search the codebase
|
|
311
|
+
input_schema:
|
|
312
|
+
properties:
|
|
313
|
+
query:
|
|
314
|
+
type: string
|
|
315
|
+
description: Search query
|
|
316
|
+
path:
|
|
317
|
+
type: string
|
|
318
|
+
description: Directory to search
|
|
319
|
+
required: [query]
|
|
320
|
+
implementation:
|
|
321
|
+
script: search.sh
|
|
322
|
+
runtime: sh
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
The script receives args as JSON on stdin and returns output on stdout.
|
|
326
|
+
|
|
327
|
+
## Hooks
|
|
328
|
+
|
|
329
|
+
Script-based hooks in `hooks/hooks.yaml`:
|
|
330
|
+
|
|
331
|
+
```yaml
|
|
332
|
+
hooks:
|
|
333
|
+
on_session_start:
|
|
334
|
+
- script: validate-env.sh
|
|
335
|
+
description: Check environment is ready
|
|
336
|
+
pre_tool_use:
|
|
337
|
+
- script: audit-tools.sh
|
|
338
|
+
description: Log and gate tool usage
|
|
339
|
+
post_response:
|
|
340
|
+
- script: notify.sh
|
|
341
|
+
on_error:
|
|
342
|
+
- script: alert.sh
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
Hook scripts receive context as JSON on stdin and return:
|
|
346
|
+
|
|
347
|
+
```json
|
|
348
|
+
{ "action": "allow" }
|
|
349
|
+
{ "action": "block", "reason": "Not permitted" }
|
|
350
|
+
{ "action": "modify", "args": { "modified": "args" } }
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
## Skills
|
|
354
|
+
|
|
355
|
+
Skills are composable instruction modules in `skills/<name>/`:
|
|
356
|
+
|
|
357
|
+
```
|
|
358
|
+
skills/
|
|
359
|
+
code-review/
|
|
360
|
+
SKILL.md
|
|
361
|
+
scripts/
|
|
362
|
+
lint.sh
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
```markdown
|
|
366
|
+
---
|
|
367
|
+
name: code-review
|
|
368
|
+
description: Review code for quality and security
|
|
369
|
+
---
|
|
370
|
+
|
|
371
|
+
# Code Review
|
|
372
|
+
|
|
373
|
+
When reviewing code:
|
|
374
|
+
1. Check for security vulnerabilities
|
|
375
|
+
2. Verify error handling
|
|
376
|
+
3. Run the lint script for style checks
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
Invoke via CLI: `/skill:code-review Review the auth module`
|
|
380
|
+
|
|
381
|
+
## Multi-Model Support
|
|
382
|
+
|
|
383
|
+
Gitclaw works with any LLM provider supported by [pi-ai](https://github.com/nicepkg/pi-ai):
|
|
384
|
+
|
|
385
|
+
```yaml
|
|
386
|
+
# agent.yaml
|
|
387
|
+
model:
|
|
388
|
+
preferred: "anthropic:claude-sonnet-4-5-20250929"
|
|
389
|
+
fallback:
|
|
390
|
+
- "openai:gpt-4o"
|
|
391
|
+
- "google:gemini-2.0-flash"
|
|
392
|
+
```
|
|
393
|
+
|
|
394
|
+
Supported providers: `anthropic`, `openai`, `google`, `xai`, `groq`, `mistral`, and more.
|
|
395
|
+
|
|
396
|
+
## Inheritance & Composition
|
|
397
|
+
|
|
398
|
+
Agents can extend base agents:
|
|
399
|
+
|
|
400
|
+
```yaml
|
|
401
|
+
# agent.yaml
|
|
402
|
+
extends: "https://github.com/org/base-agent.git"
|
|
403
|
+
|
|
404
|
+
# Dependencies
|
|
405
|
+
dependencies:
|
|
406
|
+
- name: shared-tools
|
|
407
|
+
source: "https://github.com/org/shared-tools.git"
|
|
408
|
+
version: main
|
|
409
|
+
mount: tools
|
|
410
|
+
|
|
411
|
+
# Sub-agents
|
|
412
|
+
delegation:
|
|
413
|
+
mode: auto
|
|
414
|
+
```
|
|
415
|
+
|
|
416
|
+
## Compliance & Audit
|
|
417
|
+
|
|
418
|
+
Built-in compliance validation and audit logging:
|
|
419
|
+
|
|
420
|
+
```yaml
|
|
421
|
+
# agent.yaml
|
|
422
|
+
compliance:
|
|
423
|
+
risk_level: high
|
|
424
|
+
human_in_the_loop: true
|
|
425
|
+
data_classification: confidential
|
|
426
|
+
regulatory_frameworks: [SOC2, GDPR]
|
|
427
|
+
recordkeeping:
|
|
428
|
+
audit_logging: true
|
|
429
|
+
retention_days: 90
|
|
430
|
+
```
|
|
431
|
+
|
|
432
|
+
Audit logs are written to `.gitagent/audit.jsonl` with full tool invocation traces.
|
|
433
|
+
|
|
434
|
+
## Contributing
|
|
435
|
+
|
|
436
|
+
Contributions are welcome! Please open an issue or submit a pull request.
|
|
437
|
+
|
|
438
|
+
## License
|
|
439
|
+
|
|
440
|
+
MIT
|
package/dist/agents.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export interface SubAgentMetadata {
|
|
2
|
+
name: string;
|
|
3
|
+
description: string;
|
|
4
|
+
type: "directory" | "file";
|
|
5
|
+
path: string;
|
|
6
|
+
}
|
|
7
|
+
export declare function discoverSubAgents(agentDir: string): Promise<SubAgentMetadata[]>;
|
|
8
|
+
export declare function formatSubAgentsForPrompt(agents: SubAgentMetadata[]): string;
|
package/dist/agents.js
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { readFile, readdir, stat } from "fs/promises";
|
|
2
|
+
import { join } from "path";
|
|
3
|
+
import yaml from "js-yaml";
|
|
4
|
+
function parseFrontmatter(content) {
|
|
5
|
+
const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/);
|
|
6
|
+
if (!match) {
|
|
7
|
+
return { frontmatter: {}, body: content };
|
|
8
|
+
}
|
|
9
|
+
const frontmatter = yaml.load(match[1]);
|
|
10
|
+
return { frontmatter, body: match[2] };
|
|
11
|
+
}
|
|
12
|
+
export async function discoverSubAgents(agentDir) {
|
|
13
|
+
const agentsDir = join(agentDir, "agents");
|
|
14
|
+
try {
|
|
15
|
+
const s = await stat(agentsDir);
|
|
16
|
+
if (!s.isDirectory())
|
|
17
|
+
return [];
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
return [];
|
|
21
|
+
}
|
|
22
|
+
const entries = await readdir(agentsDir, { withFileTypes: true });
|
|
23
|
+
const agents = [];
|
|
24
|
+
for (const entry of entries) {
|
|
25
|
+
const entryPath = join(agentsDir, entry.name);
|
|
26
|
+
if (entry.isDirectory()) {
|
|
27
|
+
// Directory form: agents/<name>/agent.yaml
|
|
28
|
+
const agentYamlPath = join(entryPath, "agent.yaml");
|
|
29
|
+
try {
|
|
30
|
+
const raw = await readFile(agentYamlPath, "utf-8");
|
|
31
|
+
const data = yaml.load(raw);
|
|
32
|
+
if (data?.name && data?.description) {
|
|
33
|
+
agents.push({
|
|
34
|
+
name: data.name,
|
|
35
|
+
description: data.description,
|
|
36
|
+
type: "directory",
|
|
37
|
+
path: `agents/${entry.name}`,
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
// Skip directories without valid agent.yaml
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
else if (entry.name.endsWith(".md") && entry.isFile()) {
|
|
46
|
+
// File form: agents/<name>.md
|
|
47
|
+
try {
|
|
48
|
+
const raw = await readFile(entryPath, "utf-8");
|
|
49
|
+
const { frontmatter } = parseFrontmatter(raw);
|
|
50
|
+
const name = frontmatter.name || entry.name.replace(/\.md$/, "");
|
|
51
|
+
const description = frontmatter.description || "";
|
|
52
|
+
if (description) {
|
|
53
|
+
agents.push({
|
|
54
|
+
name,
|
|
55
|
+
description,
|
|
56
|
+
type: "file",
|
|
57
|
+
path: `agents/${entry.name}`,
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
// Skip unreadable files
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return agents.sort((a, b) => a.name.localeCompare(b.name));
|
|
67
|
+
}
|
|
68
|
+
export function formatSubAgentsForPrompt(agents) {
|
|
69
|
+
if (agents.length === 0)
|
|
70
|
+
return "";
|
|
71
|
+
const entries = agents
|
|
72
|
+
.map((a) => `<agent>\n<name>${a.name}</name>\n<description>${a.description}</description>\n<type>${a.type}</type>\n<path>${a.path}</path>\n</agent>`)
|
|
73
|
+
.join("\n");
|
|
74
|
+
return `# Sub-Agents
|
|
75
|
+
|
|
76
|
+
<available_agents>
|
|
77
|
+
${entries}
|
|
78
|
+
</available_agents>
|
|
79
|
+
|
|
80
|
+
To delegate to a sub-agent, use the \`cli\` tool to run: \`gitclaw --dir ${"{agent_path}"} -p "task description"\`
|
|
81
|
+
For file-based agents, use the \`read\` tool to load their instructions.`;
|
|
82
|
+
}
|
package/dist/audit.d.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export interface AuditEntry {
|
|
2
|
+
timestamp: string;
|
|
3
|
+
session_id: string;
|
|
4
|
+
event: string;
|
|
5
|
+
tool?: string;
|
|
6
|
+
args?: Record<string, any>;
|
|
7
|
+
result?: string;
|
|
8
|
+
error?: string;
|
|
9
|
+
[key: string]: any;
|
|
10
|
+
}
|
|
11
|
+
export declare class AuditLogger {
|
|
12
|
+
private logPath;
|
|
13
|
+
private sessionId;
|
|
14
|
+
private enabled;
|
|
15
|
+
constructor(gitagentDir: string, sessionId: string, enabled: boolean);
|
|
16
|
+
log(event: string, data?: Partial<AuditEntry>): Promise<void>;
|
|
17
|
+
logToolUse(tool: string, args: Record<string, any>): Promise<void>;
|
|
18
|
+
logToolResult(tool: string, result: string): Promise<void>;
|
|
19
|
+
logResponse(): Promise<void>;
|
|
20
|
+
logError(error: string): Promise<void>;
|
|
21
|
+
logSessionStart(): Promise<void>;
|
|
22
|
+
logSessionEnd(): Promise<void>;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Check if audit logging is enabled via compliance config.
|
|
26
|
+
*/
|
|
27
|
+
export declare function isAuditEnabled(compliance?: Record<string, any>): boolean;
|
package/dist/audit.js
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { appendFile, mkdir } from "fs/promises";
|
|
2
|
+
import { join, dirname } from "path";
|
|
3
|
+
export class AuditLogger {
|
|
4
|
+
logPath;
|
|
5
|
+
sessionId;
|
|
6
|
+
enabled;
|
|
7
|
+
constructor(gitagentDir, sessionId, enabled) {
|
|
8
|
+
this.logPath = join(gitagentDir, "audit.jsonl");
|
|
9
|
+
this.sessionId = sessionId;
|
|
10
|
+
this.enabled = enabled;
|
|
11
|
+
}
|
|
12
|
+
async log(event, data = {}) {
|
|
13
|
+
if (!this.enabled)
|
|
14
|
+
return;
|
|
15
|
+
const entry = {
|
|
16
|
+
timestamp: new Date().toISOString(),
|
|
17
|
+
session_id: this.sessionId,
|
|
18
|
+
event,
|
|
19
|
+
...data,
|
|
20
|
+
};
|
|
21
|
+
try {
|
|
22
|
+
await mkdir(dirname(this.logPath), { recursive: true });
|
|
23
|
+
await appendFile(this.logPath, JSON.stringify(entry) + "\n", "utf-8");
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
// Audit logging failures are non-fatal
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
async logToolUse(tool, args) {
|
|
30
|
+
await this.log("tool_use", { tool, args });
|
|
31
|
+
}
|
|
32
|
+
async logToolResult(tool, result) {
|
|
33
|
+
await this.log("tool_result", { tool, result: result.slice(0, 1000) });
|
|
34
|
+
}
|
|
35
|
+
async logResponse() {
|
|
36
|
+
await this.log("response");
|
|
37
|
+
}
|
|
38
|
+
async logError(error) {
|
|
39
|
+
await this.log("error", { error });
|
|
40
|
+
}
|
|
41
|
+
async logSessionStart() {
|
|
42
|
+
await this.log("session_start");
|
|
43
|
+
}
|
|
44
|
+
async logSessionEnd() {
|
|
45
|
+
await this.log("session_end");
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Check if audit logging is enabled via compliance config.
|
|
50
|
+
*/
|
|
51
|
+
export function isAuditEnabled(compliance) {
|
|
52
|
+
if (!compliance)
|
|
53
|
+
return false;
|
|
54
|
+
return compliance.recordkeeping?.audit_logging === true;
|
|
55
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { AgentManifest } from "./loader.js";
|
|
2
|
+
export interface ComplianceConfig {
|
|
3
|
+
risk_level?: "low" | "medium" | "high" | "critical";
|
|
4
|
+
human_in_the_loop?: boolean;
|
|
5
|
+
data_classification?: string;
|
|
6
|
+
regulatory_frameworks?: string[];
|
|
7
|
+
recordkeeping?: {
|
|
8
|
+
audit_logging?: boolean;
|
|
9
|
+
retention_days?: number;
|
|
10
|
+
};
|
|
11
|
+
review?: {
|
|
12
|
+
required_approvers?: number;
|
|
13
|
+
auto_review?: boolean;
|
|
14
|
+
};
|
|
15
|
+
[key: string]: any;
|
|
16
|
+
}
|
|
17
|
+
export interface ComplianceWarning {
|
|
18
|
+
rule: string;
|
|
19
|
+
message: string;
|
|
20
|
+
severity: "error" | "warning";
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Validate compliance section of agent manifest against spec rules.
|
|
24
|
+
*/
|
|
25
|
+
export declare function validateCompliance(manifest: AgentManifest): ComplianceWarning[];
|
|
26
|
+
/**
|
|
27
|
+
* Load compliance directory files and format summary for system prompt.
|
|
28
|
+
*/
|
|
29
|
+
export declare function loadComplianceContext(agentDir: string): Promise<string>;
|
|
30
|
+
export declare function formatComplianceWarnings(warnings: ComplianceWarning[]): string;
|