@germanescobar/anita 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 +353 -0
- package/dist/agent/agents.d.ts +16 -0
- package/dist/agent/agents.js +115 -0
- package/dist/agent/context-budget.d.ts +7 -0
- package/dist/agent/context-budget.js +17 -0
- package/dist/agent/context-builder.d.ts +34 -0
- package/dist/agent/context-builder.js +175 -0
- package/dist/agent/executor.d.ts +13 -0
- package/dist/agent/executor.js +65 -0
- package/dist/agent/loop.d.ts +54 -0
- package/dist/agent/loop.js +548 -0
- package/dist/agent/policies.d.ts +25 -0
- package/dist/agent/policies.js +177 -0
- package/dist/agent/session.d.ts +12 -0
- package/dist/agent/session.js +42 -0
- package/dist/attachments.d.ts +3 -0
- package/dist/attachments.js +73 -0
- package/dist/cli/index.d.ts +5 -0
- package/dist/cli/index.js +327 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +4 -0
- package/dist/models/anthropic.d.ts +15 -0
- package/dist/models/anthropic.js +195 -0
- package/dist/models/openai-responses.d.ts +62 -0
- package/dist/models/openai-responses.js +377 -0
- package/dist/models/openai.d.ts +32 -0
- package/dist/models/openai.js +330 -0
- package/dist/models/provider.d.ts +33 -0
- package/dist/models/provider.js +1 -0
- package/dist/models/resolve.d.ts +48 -0
- package/dist/models/resolve.js +211 -0
- package/dist/security/sensitive-content.d.ts +6 -0
- package/dist/security/sensitive-content.js +59 -0
- package/dist/skills/skills.d.ts +62 -0
- package/dist/skills/skills.js +371 -0
- package/dist/storage/event-store.d.ts +7 -0
- package/dist/storage/event-store.js +36 -0
- package/dist/storage/session-store.d.ts +11 -0
- package/dist/storage/session-store.js +64 -0
- package/dist/tools/delete-file.d.ts +2 -0
- package/dist/tools/delete-file.js +25 -0
- package/dist/tools/edit-file.d.ts +2 -0
- package/dist/tools/edit-file.js +50 -0
- package/dist/tools/read-file.d.ts +2 -0
- package/dist/tools/read-file.js +122 -0
- package/dist/tools/registry.d.ts +9 -0
- package/dist/tools/registry.js +122 -0
- package/dist/tools/run-command.d.ts +2 -0
- package/dist/tools/run-command.js +103 -0
- package/dist/tools/write-file.d.ts +2 -0
- package/dist/tools/write-file.js +29 -0
- package/dist/types/agent.d.ts +44 -0
- package/dist/types/agent.js +1 -0
- package/dist/types/conversation.d.ts +43 -0
- package/dist/types/conversation.js +201 -0
- package/dist/types/events.d.ts +8 -0
- package/dist/types/events.js +1 -0
- package/dist/types/messages.d.ts +39 -0
- package/dist/types/messages.js +1 -0
- package/dist/types/output.d.ts +19 -0
- package/dist/types/output.js +1 -0
- package/dist/types/stream.d.ts +55 -0
- package/dist/types/stream.js +1 -0
- package/dist/types/tools.d.ts +28 -0
- package/dist/types/tools.js +1 -0
- package/package.json +45 -0
package/README.md
ADDED
|
@@ -0,0 +1,353 @@
|
|
|
1
|
+
# Coding Agent
|
|
2
|
+
|
|
3
|
+
An intelligent AI-powered coding agent that helps you write, edit, and run code. It provides conversational interactions with AI models while having direct access to your file system and shell commands.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Conversational AI** - Chat with AI models using natural language
|
|
8
|
+
- **File System Tools** - Read, write, and edit files seamlessly
|
|
9
|
+
- **Command Execution** - Run shell commands safely with approval prompts
|
|
10
|
+
- **Session Management** - Resume previous conversations and track history
|
|
11
|
+
- **Multi-Provider Support** - Switch between local Ollama, Ollama Cloud, and OpenRouter models
|
|
12
|
+
- **Event Logging** - Track all interactions and tool executions
|
|
13
|
+
- **Reasoning Capture** - Preserve provider-exposed reasoning traces when available
|
|
14
|
+
- **Type-Safe** - Built with TypeScript for reliability and type safety
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
Install from npm:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
npm install -g @germanescobar/anita
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
That installs the `anita` command globally, so users can run:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
anita chat "Create a simple React component that displays a greeting"
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Local Development Install
|
|
31
|
+
|
|
32
|
+
To install from a local checkout:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
# Clone the repository
|
|
36
|
+
git clone <repository-url>
|
|
37
|
+
cd coding-agent
|
|
38
|
+
|
|
39
|
+
# Install dependencies
|
|
40
|
+
npm install
|
|
41
|
+
|
|
42
|
+
# Build the project
|
|
43
|
+
npm run build
|
|
44
|
+
|
|
45
|
+
# Install the anita command globally from this checkout
|
|
46
|
+
npm link
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
After that, the `anita` command will be available in your shell.
|
|
50
|
+
|
|
51
|
+
`npm link` is typically a one-time setup step for a local checkout. After changing files in `src/`, run:
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
npm run build
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
You only need to run `npm link` again if the package's global command setup changes, such as the package name or the `bin` mapping in `package.json`.
|
|
58
|
+
|
|
59
|
+
If you don't want to install it globally, you can also run it directly from the repository with:
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
npm run start -- <command>
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Usage
|
|
66
|
+
|
|
67
|
+
### Basic Chat
|
|
68
|
+
|
|
69
|
+
Start a new conversation:
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
anita chat "Create a simple React component that displays a greeting"
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### Resume a Session
|
|
76
|
+
|
|
77
|
+
Continue from a previous conversation:
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
anita chat "Can you explain this code?" --resume <session-id>
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### List Past Sessions
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
anita sessions
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### View Event Logs
|
|
90
|
+
|
|
91
|
+
View detailed events for a specific session:
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
anita events <session-id>
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### List Supported Models
|
|
98
|
+
|
|
99
|
+
Show the built-in model choices grouped by provider:
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
anita models
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Each listed value is a `provider/model` string that can be passed to `--model`. Local Ollama models use the `ollama/` provider, while Ollama Cloud models use `ollama-cloud/`.
|
|
106
|
+
Models that support attachments are annotated with `[images]`, `[files]`, or both.
|
|
107
|
+
|
|
108
|
+
### Custom Model
|
|
109
|
+
|
|
110
|
+
Use a different AI model:
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
anita chat "Your message here" --model ollama/glm-4.7-flash:latest
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### Additional System Prompt
|
|
117
|
+
|
|
118
|
+
Add run-specific system instructions:
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
anita chat "Review this module" --system-prompt "Be concise and prioritize correctness issues."
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
`--system-prompt` is additive. It does not replace Anita's built-in instructions, safety rules, user instructions, or tool permission policy.
|
|
125
|
+
|
|
126
|
+
Attach an image or PDF to models that support the relevant attachment type:
|
|
127
|
+
|
|
128
|
+
```bash
|
|
129
|
+
anita chat "Explain this screenshot" --model openrouter/moonshotai/kimi-k2.6 --attach ./screen.png
|
|
130
|
+
anita chat "Summarize this brief" --model ollama-cloud/minimax-m3 --attach ./brief.pdf
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
`--attach` accepts local paths or HTTP(S) URLs and can be repeated. Supported local attachment types are `png`, `jpg`, `jpeg`, `webp`, `gif`, and `pdf`.
|
|
134
|
+
|
|
135
|
+
### AGENTS.md Instructions
|
|
136
|
+
|
|
137
|
+
Anita automatically includes AGENTS.md guidance in the system prompt when it builds context for a chat session.
|
|
138
|
+
|
|
139
|
+
Discovery order:
|
|
140
|
+
|
|
141
|
+
1. Global defaults from `~/.anita/AGENTS.md`
|
|
142
|
+
2. Repository instructions from `<repo-root>/AGENTS.md`
|
|
143
|
+
|
|
144
|
+
When both files exist, repository instructions are placed after global instructions and override them when they conflict.
|
|
145
|
+
|
|
146
|
+
Create a global instructions file:
|
|
147
|
+
|
|
148
|
+
```bash
|
|
149
|
+
anita agents init --global
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
Create a repository instructions file at the Git repository root, or in the current directory when outside Git:
|
|
153
|
+
|
|
154
|
+
```bash
|
|
155
|
+
anita agents init
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
Use `--force` with either command to overwrite an existing file.
|
|
159
|
+
|
|
160
|
+
### Context Compaction
|
|
161
|
+
|
|
162
|
+
Anita tracks an approximate context budget for each session. When the model context reaches a high-water mark for the selected model's context window, Anita folds older history into a rolling summary while keeping a recent token-budgeted tail, including recent tool calls and tool results, available verbatim.
|
|
163
|
+
|
|
164
|
+
By default, compaction starts around 80% of the usable model context window after reserving response tokens, preserves roughly 24,000 recent tokens, and skips compaction when the older prefix is too small to reduce context meaningfully.
|
|
165
|
+
|
|
166
|
+
### Ollama Cloud
|
|
167
|
+
|
|
168
|
+
Use a supported Ollama Cloud model with the `ollama-cloud/` provider. This is separate from the local `ollama/` provider, which still targets a local Ollama daemon.
|
|
169
|
+
|
|
170
|
+
```bash
|
|
171
|
+
export OLLAMA_API_KEY=<your-ollama-api-key>
|
|
172
|
+
anita chat "Your message here" --model ollama-cloud/glm-5.2
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
Supported Ollama Cloud models:
|
|
176
|
+
|
|
177
|
+
- `ollama-cloud/glm-5.2`
|
|
178
|
+
- `ollama-cloud/minimax-m3` `[images, files]`
|
|
179
|
+
- `ollama-cloud/deepseek-v4-pro`
|
|
180
|
+
- `ollama-cloud/kimi-k2.7-code` `[images]`
|
|
181
|
+
|
|
182
|
+
Ollama Cloud requests use an explicit `max_tokens` value of 8192 so the provider leaves context room for the prompt.
|
|
183
|
+
|
|
184
|
+
### JSON Event Streaming
|
|
185
|
+
|
|
186
|
+
Use `--stream-json` to emit one JSON event per line for machine-readable integrations:
|
|
187
|
+
|
|
188
|
+
```bash
|
|
189
|
+
anita chat "Add a hello world script" --stream-json
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
Example stream:
|
|
193
|
+
|
|
194
|
+
```json
|
|
195
|
+
{"type":"run.started","sessionId":"9e6f8a7d-7ff1-4c2c-b3d8-9c3ed5a1d4b7","model":"ollama/glm-4.7-flash:latest","workingDirectory":"/path/to/project","timestamp":"2026-04-09T15:00:00.000Z"}
|
|
196
|
+
{"type":"assistant.reasoning","text":"I should inspect the project structure before changing files."}
|
|
197
|
+
{"type":"assistant.text","text":"I added a hello world script."}
|
|
198
|
+
{"type":"tool.call","id":"toolu_123","name":"write_file","input":{"path":"hello.js","content":"console.log(\"hello world\");\n"}}
|
|
199
|
+
{"type":"tool.result","id":"toolu_123","name":"write_file","content":"File written successfully.","isError":false}
|
|
200
|
+
{"type":"run.completed","sessionId":"9e6f8a7d-7ff1-4c2c-b3d8-9c3ed5a1d4b7","status":"completed","stopReason":"end_turn","timestamp":"2026-04-09T15:00:01.000Z"}
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
Without `--stream-json`, the CLI uses the normal human-readable terminal output.
|
|
204
|
+
|
|
205
|
+
When using an OpenAI-compatible backend that exposes reasoning traces, Anita will also store them in the `assistant_response` event payload as `reasoning` and emit an `assistant.reasoning` stream event.
|
|
206
|
+
|
|
207
|
+
## Publishing
|
|
208
|
+
|
|
209
|
+
To publish the package to npm under the `@germanescobar` scope:
|
|
210
|
+
|
|
211
|
+
```bash
|
|
212
|
+
# Log in to npm
|
|
213
|
+
npm login
|
|
214
|
+
|
|
215
|
+
# Build and publish the public scoped package
|
|
216
|
+
npm publish --access public
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
After publishing, users can install it with:
|
|
220
|
+
|
|
221
|
+
```bash
|
|
222
|
+
npm install -g @germanescobar/anita
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
## Architecture
|
|
226
|
+
|
|
227
|
+
The Coding Agent is built with a modular architecture:
|
|
228
|
+
|
|
229
|
+
### Core Components
|
|
230
|
+
|
|
231
|
+
- **Agent Loop** - Manages the conversation cycle, including message handling and tool execution
|
|
232
|
+
- **Context Builder** - Separates stable agent instructions from dynamic environment context
|
|
233
|
+
- **Executor** - Executes AI-generated tool calls with safety policies
|
|
234
|
+
- **Provider** - Abstracts OpenAI-compatible AI model interactions
|
|
235
|
+
- **Tool Registry** - Manages available tools and their schemas
|
|
236
|
+
|
|
237
|
+
### Storage Layer
|
|
238
|
+
|
|
239
|
+
- **Event Store** - Persist all interactions and events
|
|
240
|
+
- **Session Store** - Save and retrieve conversation sessions
|
|
241
|
+
|
|
242
|
+
### Tools
|
|
243
|
+
|
|
244
|
+
- **read-file** - Read file contents
|
|
245
|
+
- **write-file** - Create or overwrite files
|
|
246
|
+
- **edit-file** - Edit existing files with precise replacements
|
|
247
|
+
- **run-command** - Execute shell commands (with approval)
|
|
248
|
+
|
|
249
|
+
## Project Structure
|
|
250
|
+
|
|
251
|
+
```
|
|
252
|
+
coding-agent/
|
|
253
|
+
├── src/
|
|
254
|
+
│ ├── agent/
|
|
255
|
+
│ │ ├── context-builder.ts # Builds static prompts and dynamic context
|
|
256
|
+
│ │ ├── executor.ts # Executes tool calls safely
|
|
257
|
+
│ │ ├── loop.ts # Main conversation loop
|
|
258
|
+
│ │ ├── policies.ts # Safety and approval policies
|
|
259
|
+
│ │ └── session.ts # Session management
|
|
260
|
+
│ ├── cli/
|
|
261
|
+
│ │ └── index.ts # Command-line interface
|
|
262
|
+
│ ├── models/
|
|
263
|
+
│ │ ├── anthropic.ts # Anthropic Claude provider implementation
|
|
264
|
+
│ │ ├── openai.ts # OpenAI-compatible provider implementation
|
|
265
|
+
│ │ ├── provider.ts # AI provider abstraction
|
|
266
|
+
│ │ └── resolve.ts # Provider factory
|
|
267
|
+
│ ├── storage/
|
|
268
|
+
│ │ ├── event-store.ts # Persistent event store
|
|
269
|
+
│ │ └── session-store.ts # Persistent session store
|
|
270
|
+
│ ├── tools/
|
|
271
|
+
│ │ ├── edit-file.ts # Edit file tool
|
|
272
|
+
│ │ ├── read-file.ts # Read file tool
|
|
273
|
+
│ │ ├── registry.ts # Tool registry and schema generation
|
|
274
|
+
│ │ └── run-command.ts # Command execution tool
|
|
275
|
+
│ ├── types/
|
|
276
|
+
│ │ ├── agent.ts # Agent type definitions
|
|
277
|
+
│ │ ├── events.ts # Event types
|
|
278
|
+
│ │ ├── messages.ts # Message type definitions
|
|
279
|
+
│ │ └── tools.ts # Tool type definitions
|
|
280
|
+
│ ├── CLI.ts
|
|
281
|
+
│ └── index.ts
|
|
282
|
+
├── package.json
|
|
283
|
+
├── tsconfig.json
|
|
284
|
+
└── README.md
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
## How It Works
|
|
288
|
+
|
|
289
|
+
1. **User sends a message** via the CLI
|
|
290
|
+
2. **Context is built** based on the current working directory and conversation history
|
|
291
|
+
3. **AI model processes** the message and determines if tools are needed
|
|
292
|
+
4. **Tool execution** happens if needed (safely with approval prompts)
|
|
293
|
+
5. **Results are passed** back to the AI for further processing
|
|
294
|
+
6. **Final response** is displayed to the user
|
|
295
|
+
7. **Session is saved** with all messages and events
|
|
296
|
+
|
|
297
|
+
## API Compatibility
|
|
298
|
+
|
|
299
|
+
Available providers (provider/model format):
|
|
300
|
+
|
|
301
|
+
- `ollama/<local-model>`
|
|
302
|
+
- `ollama-cloud/glm-5.2`
|
|
303
|
+
- `ollama-cloud/minimax-m3`
|
|
304
|
+
- `ollama-cloud/deepseek-v4-pro`
|
|
305
|
+
- `ollama-cloud/kimi-k2.7-code`
|
|
306
|
+
- `openrouter/z-ai/glm-5.1`
|
|
307
|
+
- `openrouter/minimax/minimax-m3`
|
|
308
|
+
- `openrouter/deepseek/deepseek-v4-pro`
|
|
309
|
+
- `openrouter/moonshotai/kimi-k2.6`
|
|
310
|
+
|
|
311
|
+
`ollama/<local-model>` targets `http://localhost:11434/v1`. `ollama-cloud/<model>` targets Ollama Cloud and uses `OLLAMA_API_KEY`. `openrouter/<model>` targets OpenRouter and uses `OPENROUTER_API_KEY`.
|
|
312
|
+
|
|
313
|
+
## Configuration
|
|
314
|
+
|
|
315
|
+
Session data is stored in the `.anita/` directory in your working directory:
|
|
316
|
+
|
|
317
|
+
```
|
|
318
|
+
.anita/
|
|
319
|
+
├── events/ # Individual event logs
|
|
320
|
+
│ └── <session-id>/
|
|
321
|
+
└── sessions/ # Session metadata
|
|
322
|
+
└── <session-id>
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
For backward compatibility, Anita falls back to the legacy `.coding-agent/` directory when it contains existing sessions and `.anita/` does not.
|
|
326
|
+
|
|
327
|
+
## Development
|
|
328
|
+
|
|
329
|
+
```bash
|
|
330
|
+
# Run the CLI in development mode
|
|
331
|
+
npm run dev -- chat "Create a simple React component that displays a greeting"
|
|
332
|
+
|
|
333
|
+
# Build for production
|
|
334
|
+
npm run build
|
|
335
|
+
|
|
336
|
+
# Run the built CLI without installing it globally
|
|
337
|
+
npm run start -- chat "Create a simple React component that displays a greeting"
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
## Safety Considerations
|
|
341
|
+
|
|
342
|
+
- All shell commands require explicit approval before execution
|
|
343
|
+
- Tool results are previewed before being shown to the AI
|
|
344
|
+
- File modifications can be reviewed via event logs
|
|
345
|
+
- Maximum iteration limit prevents infinite loops (default: 50)
|
|
346
|
+
|
|
347
|
+
## License
|
|
348
|
+
|
|
349
|
+
MIT
|
|
350
|
+
|
|
351
|
+
## Contributing
|
|
352
|
+
|
|
353
|
+
Contributions are welcome! Feel free to submit issues and pull requests.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export type AgentInstructionsScope = "global" | "repository";
|
|
2
|
+
export interface AgentInstructionsFile {
|
|
3
|
+
scope: AgentInstructionsScope;
|
|
4
|
+
path: string;
|
|
5
|
+
content: string;
|
|
6
|
+
}
|
|
7
|
+
export interface LoadAgentInstructionsOptions {
|
|
8
|
+
cwd: string;
|
|
9
|
+
homeDir?: string;
|
|
10
|
+
repositoryRoot?: string;
|
|
11
|
+
}
|
|
12
|
+
export declare function getGlobalAgentsPath(homeDir?: string): string;
|
|
13
|
+
export declare function getProjectAgentsPath(cwd: string): string;
|
|
14
|
+
export declare function getRepositoryAgentsPath(cwd: string): string;
|
|
15
|
+
export declare function loadAgentInstructions(options: LoadAgentInstructionsOptions): AgentInstructionsFile[];
|
|
16
|
+
export declare function formatAgentInstructionsForPrompt(files: AgentInstructionsFile[]): string;
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { execFileSync } from "node:child_process";
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import os from "node:os";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
const AGENTS_FILE_NAME = "AGENTS.md";
|
|
6
|
+
const CONFIG_DIR_NAME = ".anita";
|
|
7
|
+
const LEGACY_CONFIG_DIR_NAME = ".ada";
|
|
8
|
+
/* Canonical location for the global AGENTS.md. Used when writing the file. */
|
|
9
|
+
export function getGlobalAgentsPath(homeDir = os.homedir()) {
|
|
10
|
+
return path.join(homeDir, CONFIG_DIR_NAME, AGENTS_FILE_NAME);
|
|
11
|
+
}
|
|
12
|
+
/*
|
|
13
|
+
* Resolve the global AGENTS.md for reading, preferring the canonical `.anita`
|
|
14
|
+
* directory and falling back to the legacy `.ada` directory so existing
|
|
15
|
+
* configurations keep working after the rename.
|
|
16
|
+
*/
|
|
17
|
+
function resolveReadableGlobalAgentsPath(homeDir) {
|
|
18
|
+
const primary = path.join(homeDir, CONFIG_DIR_NAME, AGENTS_FILE_NAME);
|
|
19
|
+
if (fs.existsSync(primary))
|
|
20
|
+
return primary;
|
|
21
|
+
const legacy = path.join(homeDir, LEGACY_CONFIG_DIR_NAME, AGENTS_FILE_NAME);
|
|
22
|
+
if (fs.existsSync(legacy))
|
|
23
|
+
return legacy;
|
|
24
|
+
return primary;
|
|
25
|
+
}
|
|
26
|
+
export function getProjectAgentsPath(cwd) {
|
|
27
|
+
return path.join(cwd, AGENTS_FILE_NAME);
|
|
28
|
+
}
|
|
29
|
+
export function getRepositoryAgentsPath(cwd) {
|
|
30
|
+
return getProjectAgentsPath(findRepositoryRoot(cwd) ?? cwd);
|
|
31
|
+
}
|
|
32
|
+
export function loadAgentInstructions(options) {
|
|
33
|
+
const repositoryRoot = options.repositoryRoot ?? findRepositoryRoot(options.cwd) ?? options.cwd;
|
|
34
|
+
const candidates = [
|
|
35
|
+
{
|
|
36
|
+
scope: "global",
|
|
37
|
+
path: resolveReadableGlobalAgentsPath(options.homeDir ?? os.homedir()),
|
|
38
|
+
},
|
|
39
|
+
{ scope: "repository", path: getProjectAgentsPath(repositoryRoot) },
|
|
40
|
+
];
|
|
41
|
+
const loaded = [];
|
|
42
|
+
const seenPaths = new Set();
|
|
43
|
+
for (const candidate of candidates) {
|
|
44
|
+
const file = readInstructionsFile(candidate.scope, candidate.path);
|
|
45
|
+
if (!file)
|
|
46
|
+
continue;
|
|
47
|
+
let realPath = file.path;
|
|
48
|
+
try {
|
|
49
|
+
realPath = fs.realpathSync(file.path);
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
realPath = file.path;
|
|
53
|
+
}
|
|
54
|
+
if (seenPaths.has(realPath))
|
|
55
|
+
continue;
|
|
56
|
+
seenPaths.add(realPath);
|
|
57
|
+
loaded.push(file);
|
|
58
|
+
}
|
|
59
|
+
return loaded;
|
|
60
|
+
}
|
|
61
|
+
export function formatAgentInstructionsForPrompt(files) {
|
|
62
|
+
if (files.length === 0)
|
|
63
|
+
return "";
|
|
64
|
+
const lines = [
|
|
65
|
+
"",
|
|
66
|
+
"Additional instructions from AGENTS.md files:",
|
|
67
|
+
"AGENTS.md instructions are subordinate to Anita's built-in safety rules, user instructions, and tool permission policy.",
|
|
68
|
+
"Instructions from later AGENTS.md files override earlier AGENTS.md files only when they conflict with each other.",
|
|
69
|
+
];
|
|
70
|
+
for (const file of files) {
|
|
71
|
+
const label = file.scope === "global" ? "Global" : "Repository";
|
|
72
|
+
lines.push("");
|
|
73
|
+
lines.push(`${label} AGENTS.md (${file.path}):`);
|
|
74
|
+
lines.push(file.content.trimEnd());
|
|
75
|
+
}
|
|
76
|
+
return lines.join("\n");
|
|
77
|
+
}
|
|
78
|
+
function findRepositoryRoot(cwd) {
|
|
79
|
+
try {
|
|
80
|
+
const output = execFileSync("git", ["rev-parse", "--show-toplevel"], {
|
|
81
|
+
cwd,
|
|
82
|
+
encoding: "utf-8",
|
|
83
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
84
|
+
timeout: 5000,
|
|
85
|
+
});
|
|
86
|
+
const root = output.trim();
|
|
87
|
+
return root.length > 0 ? root : null;
|
|
88
|
+
}
|
|
89
|
+
catch {
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
function readInstructionsFile(scope, filePath) {
|
|
94
|
+
try {
|
|
95
|
+
const stat = fs.statSync(filePath);
|
|
96
|
+
if (!stat.isFile())
|
|
97
|
+
return null;
|
|
98
|
+
return {
|
|
99
|
+
scope,
|
|
100
|
+
path: filePath,
|
|
101
|
+
content: fs.readFileSync(filePath, "utf-8"),
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
catch (err) {
|
|
105
|
+
if (isMissingPathError(err))
|
|
106
|
+
return null;
|
|
107
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
108
|
+
throw new Error(`Failed to read ${filePath}: ${message}`);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
function isMissingPathError(err) {
|
|
112
|
+
if (!(err instanceof Error))
|
|
113
|
+
return false;
|
|
114
|
+
return "code" in err && (err.code === "ENOENT" || err.code === "ENOTDIR");
|
|
115
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export interface ContextBudgetOptions {
|
|
2
|
+
thresholdTokens: number;
|
|
3
|
+
preserveRecentMessages: number;
|
|
4
|
+
summaryMaxCharacters?: number;
|
|
5
|
+
}
|
|
6
|
+
export declare const DEFAULT_CONTEXT_BUDGET: ContextBudgetOptions;
|
|
7
|
+
export declare function getDefaultContextBudget(modelString: string): ContextBudgetOptions;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export const DEFAULT_CONTEXT_BUDGET = {
|
|
2
|
+
thresholdTokens: 120_000,
|
|
3
|
+
preserveRecentMessages: 8,
|
|
4
|
+
summaryMaxCharacters: 6_000,
|
|
5
|
+
};
|
|
6
|
+
const KIMI_CLOUD_CONTEXT_BUDGET = {
|
|
7
|
+
...DEFAULT_CONTEXT_BUDGET,
|
|
8
|
+
thresholdTokens: 48_000,
|
|
9
|
+
};
|
|
10
|
+
export function getDefaultContextBudget(modelString) {
|
|
11
|
+
const normalizedModel = modelString.toLowerCase();
|
|
12
|
+
if (normalizedModel.startsWith("ollama-cloud/") &&
|
|
13
|
+
normalizedModel.includes("kimi")) {
|
|
14
|
+
return KIMI_CLOUD_CONTEXT_BUDGET;
|
|
15
|
+
}
|
|
16
|
+
return DEFAULT_CONTEXT_BUDGET;
|
|
17
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { ApprovalMode, PolicyContext } from "./policies.js";
|
|
2
|
+
import type { ConversationItem } from "../types/conversation.js";
|
|
3
|
+
import type { Message } from "../types/messages.js";
|
|
4
|
+
import type { Skill } from "../skills/skills.js";
|
|
5
|
+
export type NetworkAccess = "available" | "unavailable" | "unknown";
|
|
6
|
+
export interface RuntimeContextOptions {
|
|
7
|
+
shell?: string;
|
|
8
|
+
approvalMode?: ApprovalMode;
|
|
9
|
+
networkAccess?: NetworkAccess;
|
|
10
|
+
writeScope?: string;
|
|
11
|
+
policyContext?: PolicyContext;
|
|
12
|
+
skills?: Skill[];
|
|
13
|
+
agentsHomeDir?: string;
|
|
14
|
+
agentsRepositoryRoot?: string;
|
|
15
|
+
systemPrompt?: string;
|
|
16
|
+
}
|
|
17
|
+
export declare class ContextBuilder {
|
|
18
|
+
private workingDirectory;
|
|
19
|
+
private runtimeContext;
|
|
20
|
+
constructor(workingDirectory: string, runtimeContext?: RuntimeContextOptions);
|
|
21
|
+
buildSystemPrompt(): string;
|
|
22
|
+
private buildUserSystemPromptSection;
|
|
23
|
+
private buildEnvironmentFooter;
|
|
24
|
+
buildDynamicContext(): Promise<string>;
|
|
25
|
+
buildMessagesWithDynamicContext(messages: Message[]): Promise<Message[]>;
|
|
26
|
+
buildItemsWithDynamicContext(items: ConversationItem[]): Promise<ConversationItem[]>;
|
|
27
|
+
private buildEnvironmentContext;
|
|
28
|
+
private buildPolicyContext;
|
|
29
|
+
private describeApprovalMode;
|
|
30
|
+
private describeNetworkAccess;
|
|
31
|
+
private describeDecision;
|
|
32
|
+
private getGitContext;
|
|
33
|
+
private runQuiet;
|
|
34
|
+
}
|