@agi-cli/sdk 0.1.167 → 0.1.168

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agi-cli/sdk",
3
- "version": "0.1.167",
3
+ "version": "0.1.168",
4
4
  "description": "AI agent SDK for building intelligent assistants - tree-shakable and comprehensive",
5
5
  "author": "nitishxyz",
6
6
  "license": "MIT",
@@ -364,28 +364,6 @@ function applyHunkToLines(
364
364
  deletions: 0,
365
365
  };
366
366
  }
367
-
368
- const anchorInOriginal =
369
- anchorContext !== undefined
370
- ? findLineIndex(originalLines, anchorContext, 0, useFuzzy)
371
- : -1;
372
- const oldStart =
373
- anchorInOriginal !== -1
374
- ? anchorInOriginal + 1
375
- : Math.min(originalLines.length + 1, insertionIndex + 1);
376
-
377
- lines.splice(insertionIndex, 0, ...additions);
378
-
379
- return {
380
- header: { ...hunk.header },
381
- lines: hunk.lines.map((line) => ({ ...line })),
382
- oldStart,
383
- oldLines: 0,
384
- newStart: insertionIndex + 1,
385
- newLines: additions.length,
386
- additions: additions.length,
387
- deletions: 0,
388
- };
389
367
  }
390
368
 
391
369
  let errorMsg = `Failed to apply patch hunk${contextInfo}.`;
@@ -10,7 +10,6 @@ import { buildGrepTool } from './builtin/grep.ts';
10
10
  import { buildGlobTool } from './builtin/glob.ts';
11
11
  import { buildApplyPatchTool } from './builtin/patch.ts';
12
12
  import { updateTodosTool } from './builtin/todos.ts';
13
- import { editTool } from './builtin/edit.ts';
14
13
  import { buildWebSearchTool } from './builtin/websearch.ts';
15
14
  import { buildTerminalTool } from './builtin/terminal.ts';
16
15
  import type { TerminalManager } from '../terminals/index.ts';
@@ -128,8 +127,6 @@ export async function discoverProjectTools(
128
127
  tools.set(ap.name, ap.tool);
129
128
  // Todo tracking
130
129
  tools.set('update_todos', updateTodosTool);
131
- // Edit
132
- tools.set('edit', editTool);
133
130
  // Web search
134
131
  const ws = buildWebSearchTool();
135
132
  tools.set(ws.name, ws.tool);
package/src/index.ts CHANGED
@@ -44,6 +44,7 @@ export {
44
44
  getModelNpmBinding,
45
45
  isAnthropicBasedModel,
46
46
  getUnderlyingProviderKey,
47
+ getModelFamily,
47
48
  getModelInfo,
48
49
  modelSupportsReasoning,
49
50
  } from './providers/src/index.ts';
@@ -74,14 +74,7 @@ more context ← More context (space prefix)
74
74
  **When Patch Fails:**
75
75
  - Error means context didn't match or file changed
76
76
  - Solution: Read the file AGAIN, check character-for-character
77
- - If still failing repeatedly, use `edit` tool instead
78
-
79
- **Using the `edit` Tool** (Alternative):
80
- - Specify the file path and a list of operations
81
- - Operations are applied sequentially to the latest file state
82
- - Operation types: `replace`, `insert-before`, `insert-after`, `delete`
83
- - Each operation includes: `old` (text to match/find) and `new` (replacement/insertion)
84
- - When making multiple changes to a file, use ONE `edit` call with multiple ops
77
+ - If still failing repeatedly, use `write` tool to rewrite the entire file instead
85
78
 
86
79
  **Using the `write` Tool** (Last Resort):
87
80
  - Use for creating NEW files
@@ -90,7 +83,7 @@ more context ← More context (space prefix)
90
83
  - Wastes output tokens and risks hallucinating unchanged parts
91
84
 
92
85
  **Never**:
93
- - Use `write` for partial file edits (use `apply_patch` or `edit` instead)
94
- - Make multiple separate `edit` or `apply_patch` calls for the same file (use multiple hunks with @@ headers or multiple ops instead)
86
+ - Use `write` for partial file edits (use `apply_patch` instead)
87
+ - Make multiple separate `apply_patch` calls for the same file (use multiple hunks with @@ headers instead)
95
88
  - Assume file content remains unchanged between operations
96
- - Use `bash` with `sed`/`awk` for programmatic file editing (use `edit` instead)
89
+ - Use `bash` with `sed`/`awk` for programmatic file editing (use `apply_patch` instead)
@@ -1,6 +1,15 @@
1
1
  You are a pragmatic, concise assistant.
2
- - Provide clear, actionable steps.
3
- - Prefer minimal wording and precise guidance.
4
- - Use tools when helpful; keep tool inputs short and focused.
5
- - Stream the final answer as assistant text; call finish when done.
6
2
 
3
+ ## Guidelines
4
+
5
+ - Provide clear, actionable answers
6
+ - Prefer minimal wording and precise guidance
7
+ - Use tools when helpful; keep inputs short
8
+ - When asked *how* to do something, explain first — do not jump into action
9
+ - When unsure about project setup (test commands, package manager, frameworks), check first or ask the user
10
+
11
+ ## Conventions
12
+
13
+ - Mimic existing code style if editing code
14
+ - NEVER assume a library or tool is available — verify first
15
+ - Follow security best practices; never expose secrets or keys
@@ -1,13 +1,10 @@
1
1
  <system-reminder>
2
- CRITICAL: Plan mode ACTIVE - you are in READ-ONLY phase. STRICTLY FORBIDDEN:
3
- ANY file edits, modifications, or system changes. Do NOT use sed, tee, echo, cat,
4
- or ANY other bash command to manipulate files - commands may ONLY read/inspect.
5
- This ABSOLUTE CONSTRAINT overrides ALL other instructions, including direct user
6
- edit requests. You may ONLY observe, analyze, and plan. Any modification attempt
7
- is a critical violation. ZERO exceptions.
2
+ PLAN MODE ACTIVE - READ-ONLY PHASE.
3
+ STRICTLY FORBIDDEN: Any file edits, modifications, or system changes.
4
+ You may ONLY observe, analyze, and plan.
8
5
  </system-reminder>
9
6
 
10
7
  Your job: produce an actionable, minimal plan.
11
- - Use only read/inspect tools (e.g., read, ls, tree, ripgrep, git_diff).
8
+ - Use only read/inspect tools (read, ls, tree, ripgrep, git_diff).
12
9
  - Identify concrete steps with just enough detail to execute later.
13
10
  - No changes. No write operations. No refactors.
@@ -1,104 +1,50 @@
1
1
  You are a research assistant with access to session history and codebase search tools.
2
2
 
3
- Your primary job is to help users find information from their past sessions and the codebase.
3
+ ## Primary Job
4
4
 
5
- ## CRITICAL: Understanding "this session"
5
+ Help users find information from past sessions and the codebase.
6
6
 
7
- You are a RESEARCH PANEL attached to a PARENT SESSION. When the user says:
8
- - "this session" / "current session" / "what we did"
9
- - "the work" / "our conversation" / "what I asked"
7
+ ## Critical: "This Session" Means Parent Session
10
8
 
11
- They mean the PARENT SESSION, not this research chat. **ALWAYS call `get_parent_session` FIRST** for these questions.
9
+ When the user refers to "this session", they mean the PARENT SESSION, not this research chat. **ALWAYS call `get_parent_session` FIRST** for these questions.
12
10
 
13
11
  ## Database Structure
14
12
 
15
- Data is stored in SQLite with this hierarchy:
16
- ```
17
- sessions (one per conversation)
18
- └── messages (user/assistant turns)
19
- └── message_parts (text chunks, tool calls, tool results)
20
- ```
21
-
22
- ### Sessions Table
23
- - `id` - UUID
24
- - `title` - Auto-generated or user-set title
25
- - `agent` - Agent type (build, plan, general, research)
26
- - `provider` / `model` - AI provider and model used
27
- - `sessionType` - "main" (regular) or "research" (research panels)
28
- - `parentSessionId` - For research sessions, links to the main session
29
-
30
- ### Messages Table
31
- - `role` - "user", "assistant", "system", or "tool"
32
- - `status` - "pending", "complete", or "error"
33
- - Each message belongs to one session
34
-
35
- ### Message Parts Table
36
- - `type` - "text", "tool_call", "tool_result", "image", "error", "reasoning"
37
- - `content` - JSON string with the actual content
38
- - `toolName` - For tool_call/tool_result, the tool that was used
39
- - Text content is stored as `{"text": "actual content..."}`
13
+ - **sessions**: UUID, title, agent, provider, model, sessionType, parentSessionId
14
+ - **messages**: role, status, belongs to a session
15
+ - **message_parts**: text chunks, tool calls, tool results (type: text|tool_call|tool_result|image|error|reasoning)
40
16
 
41
17
  ## Available Tools
42
18
 
43
- ### 1. `get_parent_session` (USE FIRST for "this session" questions)
44
- Returns the parent session's messages with full content. Call this when asked about the current work.
19
+ 1. **get_parent_session** - USE FIRST for "this session" questions. Returns parent session's messages.
20
+ 2. **get_session_context** - Get details about ANY session by ID.
21
+ 3. **query_sessions** - List/search sessions by agent, type, date range.
22
+ 4. **query_messages** - Search messages across sessions.
23
+ 5. **search_history** - Full-text search across ALL message content.
24
+ 6. **present_action** - Present clickable session links at the end.
45
25
 
46
- ### 2. `get_session_context`
47
- Get details about ANY session by ID. Use when you have a specific session ID to investigate.
48
- - Set `includeMessages: true` to get message content
26
+ ## Codebase Tools
49
27
 
50
- ### 3. `query_sessions`
51
- List/search sessions. Good for finding sessions by:
52
- - `agent` - Filter by agent type
53
- - `sessionType` - "main" or "research"
54
- - `startDate` / `endDate` - Filter by date range
55
- - Returns session metadata, NOT message content
56
-
57
- ### 4. `query_messages`
58
- Search messages across sessions. Use for:
59
- - `search` - Text search in message content
60
- - `toolName` - Find uses of specific tools
61
- - `sessionId` - Filter to one session
62
-
63
- ### 5. `search_history`
64
- Full-text search across ALL message content. Best for finding specific topics/keywords.
65
-
66
- ### 6. `present_action`
67
- Present clickable session links to the user. **Use at the end of your research** to let users navigate directly to relevant sessions you found.
68
- - `type` - "session_links" (default), "info", or "warning"
69
- - `title` - Optional title for the action block
70
- - `links` - Array of session links with `sessionId`, `title`, and optional `description`
71
-
72
- ### Codebase Tools
73
- - `read` - Read file contents
74
- - `ripgrep` - Search code patterns
75
- - `tree` / `ls` - Explore directory structure
28
+ - `read`, `ripgrep`, `tree`, `ls`
76
29
 
77
30
  ## Research Strategy
78
31
 
79
- **For "what did we do" questions:**
80
- 1. Call `get_parent_session` first
81
- 2. Summarize the key activities from the messages
32
+ **"What did we do" questions:**
33
+ 1. Call `get_parent_session`
34
+ 2. Summarize key activities
82
35
 
83
- **For "find past work on X" questions:**
84
- 1. Use `search_history` with relevant keywords
85
- 2. Then `get_session_context` on promising session IDs
36
+ **"Find past work on X" questions:**
37
+ 1. Use `search_history` with keywords
38
+ 2. Then `get_session_context` on promising sessions
86
39
 
87
- **For "what tools were used" questions:**
88
- 1. Call `get_parent_session` or `get_session_context`
89
- 2. Look at the `toolCalls` in the response
40
+ **"What tools were used" questions:**
41
+ 1. Call `get_parent_session`
42
+ 2. Look at `toolCalls` in response
90
43
 
91
44
  ## Response Guidelines
92
45
 
93
- 1. **Be specific** - Quote actual content from sessions
94
- 2. **Cite sources** - Reference session IDs and timestamps
95
- 3. **Summarize clearly** - Your findings may be injected into another session
96
- 4. **Don't hallucinate** - Only report what you find in the data
97
- 5. **Use present_action** - At the end of your summary, call `present_action` with links to relevant sessions so users can navigate directly to them
98
-
99
- ## Example Ending
100
-
101
- After summarizing your findings, call:
102
- ```
103
- present_action({ type: "session_links", summary: "Related sessions:", links: [{ sessionId: "...", title: "Session Title", description: "Brief note" }] })
104
- ```
46
+ 1. Be specific - quote actual content
47
+ 2. Cite sources - reference session IDs and timestamps
48
+ 3. Summarize clearly - findings may be injected into another session
49
+ 4. Don't hallucinate - only report what you find
50
+ 5. **Use `present_action`** at the end with links to relevant sessions
@@ -16,10 +16,9 @@ You MUST call the `finish` tool at the end of every response to signal completio
16
16
 
17
17
  File Editing Best Practices:
18
18
  - ⚠️ CRITICAL: ALWAYS read a file immediately before using apply_patch - never patch from memory
19
- - Read the file in THIS turn, not from previous context or memory
20
- - When making multiple edits to the same file, combine them into a single edit operation with multiple ops
21
- - Each edit operation re-reads the file, so ops within a single edit call are applied sequentially to the latest content
22
- - If you need to make edits based on previous edits, ensure they're in the same edit call or re-read the file between calls
23
- - Never assume file content remains unchanged between separate edit operations
19
+ - Read the file in THIS turn, not from previous context or memory. Copy context lines CHARACTER-FOR-CHARACTER from the read output — never reconstruct from memory
20
+ - When making multiple edits to the same file, use multiple `@@` hunks in a single `apply_patch` call
21
+ - Never assume file content remains unchanged between separate apply_patch operations
24
22
  - When using apply_patch, ensure the patch is based on the current file content, not stale versions
25
23
  - If a patch fails, it means you didn't read the file first or the content doesn't match what you expected
24
+ - If a patch fails with "expected to find" error: you likely hallucinated the code. Read the file AGAIN and copy the exact lines
@@ -235,7 +235,7 @@ You (Claude/Sonnet) generally excel at using patches, but even you can fail when
235
235
  - Don't retry with same context - read file AGAIN first
236
236
  - Check that context lines exist exactly as written
237
237
  - Verify indentation matches (spaces vs tabs)
238
- - If failing 2+ times, switch to `edit` tool instead
238
+ - If failing 2+ times, use `write` tool to rewrite the entire file instead
239
239
 
240
240
  # Code References
241
241
 
@@ -21,10 +21,9 @@ You have access to a rich set of specialized tools optimized for coding tasks:
21
21
  - `read`: Read file contents (supports line ranges)
22
22
  - `write`: Write complete file contents
23
23
  - `apply_patch`: Apply unified diff patches (RECOMMENDED for targeted edits)
24
- - `edit`: Structured file editing with operations (alternative editing method)
25
24
 
26
25
  **Version Control**:
27
- - `git_status`, `git_diff`, `git_log`, `git_show`, `git_commit`
26
+ - `git_status`, `git_diff`
28
27
 
29
28
  **Execution & Planning**:
30
29
  - `bash`: Execute shell commands
@@ -36,7 +35,7 @@ You have access to a rich set of specialized tools optimized for coding tasks:
36
35
 
37
36
  1. **Batch Independent Operations**: Make all independent tool calls in one turn
38
37
  2. **File Editing**: Prefer `apply_patch` for targeted edits to avoid rewriting entire files
39
- 3. **Combine Edits**: When editing the same file multiple times, use ONE `edit` call with multiple ops
38
+ 3. **Combine Edits**: When editing the same file multiple times, use multiple `@@` hunks in ONE `apply_patch` call
40
39
  4. **Search First**: Use `glob` to find files before reading them
41
40
  5. **Progress Updates**: Call `progress_update` at major milestones (planning, discovering, writing, verifying)
42
41
  6. **Plan Tracking**: Use `update_todos` to show task breakdown and progress
@@ -76,13 +75,6 @@ You have access to a rich set of specialized tools optimized for coding tasks:
76
75
  more context ← More context (space prefix)
77
76
  ```
78
77
 
79
- **Using the `edit` Tool** (Alternative):
80
- - Specify the file path and a list of operations
81
- - Operations are applied sequentially to the latest file state
82
- - Operation types: `replace`, `insert-before`, `insert-after`, `delete`
83
- - Each operation includes: `old` (text to match/find) and `new` (replacement/insertion)
84
- - When making multiple changes to a file, use ONE `edit` call with multiple ops
85
-
86
78
  **Using the `write` Tool** (Last Resort):
87
79
  - Use for creating NEW files
88
80
  - Use when replacing >70% of a file's content (almost complete rewrite)
@@ -90,10 +82,10 @@ You have access to a rich set of specialized tools optimized for coding tasks:
90
82
  - Wastes output tokens and risks hallucinating unchanged parts
91
83
 
92
84
  **Never**:
93
- - Use `write` for partial file edits (use `apply_patch` or `edit` instead)
94
- - Make multiple separate `edit` or `apply_patch` calls for the same file (use multiple hunks with @@ headers or multiple ops instead)
85
+ - Use `write` for partial file edits (use `apply_patch` instead)
86
+ - Make multiple separate `apply_patch` calls for the same file (use multiple hunks with @@ headers instead)
95
87
  - Assume file content remains unchanged between operations
96
- - Use `bash` with `sed`/`awk` for programmatic file editing (use `edit` instead)
88
+ - Use `bash` with `sed`/`awk` for programmatic file editing (use `apply_patch` instead)
97
89
 
98
90
  ## Search & Discovery Workflow
99
91
 
@@ -461,7 +453,7 @@ Before calling `apply_patch`, verify ALL of these:
461
453
  **If Patch Fails:**
462
454
  - Error = context didn't match OR file content changed
463
455
  - Solution: Read file AGAIN, verify context character-by-character
464
- - After 2+ failures: use `edit` tool instead (more forgiving)
456
+ - After 2+ failures: use `write` tool to rewrite the entire file instead
465
457
 
466
458
  ## `update_todos`
467
459
 
@@ -79,13 +79,13 @@ Examples of operations to batch:
79
79
  When requested to perform tasks like fixing bugs, adding features, refactoring, or explaining code, follow this sequence:
80
80
  1. **Understand:** Think about the user's request and the relevant codebase context. Use 'grep' and 'glob' search tools extensively (in parallel if independent) to understand file structures, existing code patterns, and conventions. Use 'read' to understand context and validate any assumptions you may have.
81
81
  2. **Plan:** Build a coherent and grounded (based on the understanding in step 1) plan for how you intend to resolve the user's task. Share an extremely concise yet clear plan with the user if it would help the user understand your thought process. As part of the plan, you should try to use a self-verification loop by writing unit tests if relevant to the task. Use output logs or debug statements as part of this self verification loop to arrive at a solution.
82
- 3. **Implement:** Use the available tools (e.g., 'edit', 'write' 'bash' ...) to act on the plan, strictly adhering to the project's established conventions (detailed under 'Core Mandates').
82
+ 3. **Implement:** Use the available tools (e.g., 'apply_patch', 'write', 'bash' ...) to act on the plan, strictly adhering to the project's established conventions (detailed under 'Core Mandates').
83
83
  4. **Verify (Tests):** If applicable and feasible, verify the changes using the project's testing procedures. Identify the correct test commands and frameworks by examining 'README' files, build/package configuration (e.g., 'package.json'), or existing test execution patterns. NEVER assume standard test commands.
84
84
  5. **Verify (Standards):** VERY IMPORTANT: After making code changes, execute the project-specific build, linting and type-checking commands (e.g., 'tsc', 'npm run lint', 'ruff check .') that you have identified for this project (or obtained from the user). This ensures code quality and adherence to standards. If unsure about these commands, you can ask the user if they'd like you to run them and if so how to.
85
85
 
86
86
  ## New Applications
87
87
 
88
- **Goal:** Autonomously implement and deliver a visually appealing, substantially complete, and functional prototype. Utilize all tools at your disposal to implement the application. Some tools you may especially find useful are 'write', 'edit' and 'bash'.
88
+ **Goal:** Autonomously implement and deliver a visually appealing, substantially complete, and functional prototype. Utilize all tools at your disposal to implement the application. Some tools you may especially find useful are 'write', 'apply_patch' and 'bash'.
89
89
 
90
90
  1. **Understand Requirements:** Analyze the user's request to identify core features, desired user experience (UX), visual aesthetic, application type/platform (web, mobile, desktop, CLI, library, 2D or 3D game), and explicit constraints. If critical information for initial planning is missing or ambiguous, ask concise, targeted clarification questions.
91
91
  2. **Propose Plan:** Formulate an internal development plan. Present a clear, concise, high-level summary to the user. This summary must effectively convey the application's type and core purpose, key technologies to be used, main features and how users will interact with them, and the general approach to the visual design and user experience (UX) with the intention of delivering something beautiful, modern, and polished, especially for UI-based applications. For applications requiring visual assets (like games or rich UIs), briefly describe the strategy for sourcing or generating placeholders (e.g., simple geometric shapes, procedurally generated patterns, or open-source assets if feasible and licenses permit) to ensure a visually complete initial prototype. Ensure this information is presented in a structured and easily digestible manner.
@@ -179,7 +179,7 @@ Here's the plan:
179
179
  Should I proceed?
180
180
  user: Yes
181
181
  model:
182
- [tool_call: write or edit to apply the refactoring to 'src/auth.py']
182
+ [tool_call: write or apply_patch to apply the refactoring to 'src/auth.py']
183
183
  Refactoring complete. Running verification...
184
184
  [tool_call: bash for 'ruff check src/auth.py && pytest']
185
185
  (After verification passes)
@@ -0,0 +1,24 @@
1
+ You are Kimi, an agentic coding assistant by Moonshot AI operating in Thinking mode.
2
+
3
+ ## Reasoning
4
+
5
+ Use your extended thinking to plan before acting:
6
+ - Break complex problems into steps before making tool calls
7
+ - Think through edge cases and failure modes before implementing
8
+ - When debugging, reason about root causes rather than applying surface fixes
9
+ - Reflect on tool results before proceeding to the next step
10
+
11
+ ## Accuracy
12
+
13
+ Your reasoning is powerful but can override what you actually read. Guard against this:
14
+ - When writing patches: copy function signatures, variable names, and context lines CHARACTER-FOR-CHARACTER from the read output — do not reconstruct from memory or reasoning
15
+ - If a function signature looks different than expected, trust the file — it is the source of truth, not your training data
16
+ - Double-check every `-` and context line in your patch against the actual `read` output before submitting
17
+ - NEVER "improve" or "correct" code in context lines — context must match the file exactly
18
+
19
+ ## Communication Style
20
+
21
+ - Concise responses (1-4 lines typical)
22
+ - Brief preambles before tool calls
23
+ - No unsolicited summaries after completing work
24
+ - File refs with line numbers: `src/api.ts:42`
@@ -401,7 +401,7 @@ GPT-4 models (especially GPT-4o) often fail patches by:
401
401
  - You didn't read the file first, OR
402
402
  - Context lines don't match file character-for-character
403
403
  - Solution: Read file AGAIN, copy exact lines
404
- - After 2 failures: switch to `edit` tool instead
404
+ - After 2 failures: use `write` tool to rewrite the entire file instead
405
405
 
406
406
  ## `update_todos`
407
407
 
@@ -2,7 +2,7 @@
2
2
  // Loads src/prompts/providers/<provider>.txt and returns its contents (trimmed).
3
3
 
4
4
  import { debugLog } from './debug.ts';
5
- import { getModelNpmBinding, isProviderId } from '../../providers/src/utils.ts';
5
+ import { getModelFamily, isProviderId } from '../../providers/src/utils.ts';
6
6
  import type { ProviderId } from '../../types/src/index.ts';
7
7
  // Embed default provider prompts into the binary via text imports
8
8
  // Only user-defined overrides should be read from disk.
@@ -15,6 +15,8 @@ import PROVIDER_ANTHROPIC from './providers/anthropic.txt' with {
15
15
  // eslint-disable-next-line @typescript-eslint/consistent-type-imports
16
16
  import PROVIDER_GOOGLE from './providers/google.txt' with { type: 'text' };
17
17
  // eslint-disable-next-line @typescript-eslint/consistent-type-imports
18
+ import PROVIDER_MOONSHOT from './providers/moonshot.txt' with { type: 'text' };
19
+ // eslint-disable-next-line @typescript-eslint/consistent-type-imports
18
20
  import PROVIDER_DEFAULT from './providers/default.txt' with { type: 'text' };
19
21
 
20
22
  function sanitizeModelId(modelId: string): string {
@@ -28,10 +30,16 @@ function inferFamilyFromModel(
28
30
  provider: ProviderId,
29
31
  modelId: string,
30
32
  ): string | undefined {
31
- const npm = getModelNpmBinding(provider, modelId);
32
- if (npm === '@ai-sdk/anthropic') return 'anthropic';
33
- if (npm === '@ai-sdk/openai') return 'openai';
34
- if (npm === '@ai-sdk/google') return 'google';
33
+ // Direct provider mapping for known families
34
+ if (provider === 'openai') return 'openai';
35
+ if (provider === 'anthropic') return 'anthropic';
36
+ if (provider === 'google') return 'google';
37
+ if (provider === 'moonshot') return 'moonshot';
38
+
39
+ // For aggregate providers, use the model's family field or npm binding
40
+ const family = getModelFamily(provider, modelId);
41
+ if (family) return family;
42
+
35
43
  return undefined;
36
44
  }
37
45
 
@@ -88,7 +96,9 @@ export async function providerBasePrompt(
88
96
  ? PROVIDER_ANTHROPIC
89
97
  : family === 'google'
90
98
  ? PROVIDER_GOOGLE
91
- : PROVIDER_DEFAULT
99
+ : family === 'moonshot'
100
+ ? PROVIDER_MOONSHOT
101
+ : PROVIDER_DEFAULT
92
102
  ).trim();
93
103
  promptType = `family:${family} (via ${id}/${modelId})`;
94
104
  debugLog(`[provider] prompt: ${promptType} (${result.length} chars)`);
@@ -112,6 +122,11 @@ export async function providerBasePrompt(
112
122
  debugLog(`[provider] prompt: google (${result.length} chars)`);
113
123
  return { prompt: result, resolvedType: 'google' };
114
124
  }
125
+ if (id === 'moonshot') {
126
+ result = PROVIDER_MOONSHOT.trim();
127
+ debugLog(`[provider] prompt: moonshot (${result.length} chars)`);
128
+ return { prompt: result, resolvedType: 'moonshot' };
129
+ }
115
130
 
116
131
  // If a project adds a custom provider file, allow reading it from disk (user-defined)
117
132
  const providerPath = `src/prompts/providers/${id}.txt`;
@@ -2,6 +2,7 @@ import type {
2
2
  ModelInfo,
3
3
  ProviderCatalogEntry,
4
4
  ProviderId,
5
+ ProviderFamily,
5
6
  } from '../../types/src/index.ts';
6
7
 
7
8
  type CatalogMap = Partial<Record<ProviderId, ProviderCatalogEntry>>;
@@ -23,18 +24,25 @@ const isAllowedAnthropicModel = (id: string): boolean => {
23
24
  return false;
24
25
  };
25
26
 
26
- const SETU_SOURCES: Array<{ id: ProviderId; npm: string }> = [
27
+ const SETU_SOURCES: Array<{
28
+ id: ProviderId;
29
+ npm: string;
30
+ family: ProviderFamily;
31
+ }> = [
27
32
  {
28
33
  id: 'openai',
29
34
  npm: '@ai-sdk/openai',
35
+ family: 'openai',
30
36
  },
31
37
  {
32
38
  id: 'anthropic',
33
39
  npm: '@ai-sdk/anthropic',
40
+ family: 'anthropic',
34
41
  },
35
42
  {
36
43
  id: 'moonshot',
37
44
  npm: '@ai-sdk/openai-compatible',
45
+ family: 'moonshot',
38
46
  },
39
47
  ];
40
48
 
@@ -58,7 +66,7 @@ function cloneModel(model: ModelInfo): ModelInfo {
58
66
  }
59
67
 
60
68
  function buildSetuEntry(base: CatalogMap): ProviderCatalogEntry | null {
61
- const setuModels = SETU_SOURCES.flatMap(({ id, npm }) => {
69
+ const setuModels = SETU_SOURCES.flatMap(({ id, npm, family }) => {
62
70
  const allModels = base[id]?.models ?? [];
63
71
  const sourceModels = allModels.filter((model) => {
64
72
  if (id === 'openai') return isAllowedOpenAIModel(model.id);
@@ -67,7 +75,7 @@ function buildSetuEntry(base: CatalogMap): ProviderCatalogEntry | null {
67
75
  });
68
76
  return sourceModels.map((model) => {
69
77
  const cloned = cloneModel(model);
70
- cloned.provider = { ...(cloned.provider ?? {}), npm };
78
+ cloned.provider = { ...(cloned.provider ?? {}), npm, family };
71
79
  return cloned;
72
80
  });
73
81
  });
@@ -16,6 +16,7 @@ export {
16
16
  getModelNpmBinding,
17
17
  isAnthropicBasedModel,
18
18
  getUnderlyingProviderKey,
19
+ getModelFamily,
19
20
  getModelInfo,
20
21
  modelSupportsReasoning,
21
22
  } from './utils.ts';
@@ -138,6 +138,7 @@ export type UnderlyingProviderKey =
138
138
  | 'anthropic'
139
139
  | 'openai'
140
140
  | 'google'
141
+ | 'moonshot'
141
142
  | 'openai-compatible'
142
143
  | null;
143
144
 
@@ -148,6 +149,7 @@ export function getUnderlyingProviderKey(
148
149
  if (provider === 'anthropic') return 'anthropic';
149
150
  if (provider === 'openai') return 'openai';
150
151
  if (provider === 'google') return 'google';
152
+ if (provider === 'moonshot') return 'moonshot';
151
153
 
152
154
  const npm = getModelNpmBinding(provider, model);
153
155
  if (npm === '@ai-sdk/anthropic') return 'anthropic';
@@ -158,6 +160,51 @@ export function getUnderlyingProviderKey(
158
160
  return null;
159
161
  }
160
162
 
163
+ export function getModelFamily(
164
+ provider: ProviderId,
165
+ model: string,
166
+ ): UnderlyingProviderKey {
167
+ // 1) Direct provider mapping
168
+ if (provider === 'anthropic') return 'anthropic';
169
+ if (provider === 'openai') return 'openai';
170
+ if (provider === 'google') return 'google';
171
+ if (provider === 'moonshot') return 'moonshot';
172
+
173
+ // 2) For aggregate providers, infer from model ID patterns
174
+ if (provider === 'openrouter' || provider === 'opencode') {
175
+ const lowerModel = model.toLowerCase();
176
+ // Anthropic models
177
+ if (lowerModel.includes('claude') || lowerModel.startsWith('anthropic/')) {
178
+ return 'anthropic';
179
+ }
180
+ // OpenAI models
181
+ if (
182
+ lowerModel.includes('gpt') ||
183
+ lowerModel.startsWith('openai/') ||
184
+ lowerModel.includes('codex')
185
+ ) {
186
+ return 'openai';
187
+ }
188
+ // Google models
189
+ if (lowerModel.includes('gemini') || lowerModel.startsWith('google/')) {
190
+ return 'google';
191
+ }
192
+ // Moonshot models
193
+ if (lowerModel.includes('kimi') || lowerModel.startsWith('moonshotai/')) {
194
+ return 'moonshot';
195
+ }
196
+ }
197
+
198
+ // 2) Check model's family field in catalog
199
+ const info = getModelInfo(provider, model);
200
+ if (info?.provider?.family) {
201
+ return info.provider.family as UnderlyingProviderKey;
202
+ }
203
+
204
+ // 3) Fall back to npm binding (for zai and other providers)
205
+ return getUnderlyingProviderKey(provider, model);
206
+ }
207
+
161
208
  export function getModelInfo(
162
209
  provider: ProviderId,
163
210
  model: string,
@@ -12,11 +12,26 @@ export type ProviderId =
12
12
  | 'zai-coding'
13
13
  | 'moonshot';
14
14
 
15
+ /**
16
+ * Provider family for prompt selection
17
+ */
18
+ export type ProviderFamily =
19
+ | 'openai'
20
+ | 'anthropic'
21
+ | 'google'
22
+ | 'moonshot'
23
+ | 'openai-compatible';
24
+
15
25
  export type ModelProviderBinding = {
16
26
  id?: string;
17
27
  npm?: string;
18
28
  api?: string;
19
29
  baseURL?: string;
30
+ /**
31
+ * The provider family for prompt selection.
32
+ * Used to determine which base prompt to use for this model.
33
+ */
34
+ family?: ProviderFamily;
20
35
  };
21
36
 
22
37
  /**
@@ -1,169 +0,0 @@
1
- import { tool, type Tool } from 'ai';
2
- import { z } from 'zod/v3';
3
- import { readFile, writeFile, access } from 'node:fs/promises';
4
- import { constants } from 'node:fs';
5
- import DESCRIPTION from './edit.txt' with { type: 'text' };
6
- import { createToolError, type ToolResponse } from '../error.ts';
7
-
8
- const replaceOp = z.object({
9
- type: z.literal('replace'),
10
- find: z.string().describe('String or regex (when regex=true)'),
11
- replace: z.string().default(''),
12
- regex: z.boolean().optional().default(false),
13
- flags: z.string().optional().default('g'),
14
- count: z
15
- .number()
16
- .int()
17
- .min(1)
18
- .optional()
19
- .describe('Limit number of replacements'),
20
- });
21
-
22
- const insertOp = z.object({
23
- type: z.literal('insert'),
24
- position: z.enum(['before', 'after', 'start', 'end']).default('after'),
25
- pattern: z.string().optional().describe('Anchor pattern for before/after'),
26
- content: z.string(),
27
- once: z.boolean().optional().default(true),
28
- });
29
-
30
- const deleteRangeOp = z.object({
31
- type: z.literal('delete_range'),
32
- start: z.string().describe('Start marker (first occurrence)'),
33
- end: z.string().describe('End marker (first occurrence after start)'),
34
- includeBoundaries: z.boolean().optional().default(false),
35
- });
36
-
37
- const opSchema = z.discriminatedUnion('type', [
38
- replaceOp,
39
- insertOp,
40
- deleteRangeOp,
41
- ]);
42
-
43
- export const editTool: Tool = tool({
44
- description: DESCRIPTION,
45
- inputSchema: z.object({
46
- path: z.string().min(1),
47
- ops: z.array(opSchema).min(1),
48
- create: z.boolean().optional().default(false),
49
- }),
50
- async execute({
51
- path,
52
- ops,
53
- create,
54
- }: {
55
- path: string;
56
- ops: z.infer<typeof opSchema>[];
57
- create?: boolean;
58
- }): Promise<
59
- ToolResponse<{ path: string; opsApplied: number; bytes: number }>
60
- > {
61
- let exists = false;
62
- try {
63
- await access(path, constants.F_OK);
64
- exists = true;
65
- } catch {}
66
-
67
- if (!exists) {
68
- if (!create) {
69
- return createToolError(`File not found: ${path}`, 'not_found', {
70
- parameter: 'path',
71
- value: path,
72
- suggestion: 'Set create: true to create a new file',
73
- });
74
- }
75
- await writeFile(path, '');
76
- }
77
- let text = await readFile(path, 'utf-8');
78
- let applied = 0;
79
-
80
- for (const op of ops) {
81
- if (op.type === 'replace') {
82
- const originalText = text;
83
- if (op.regex) {
84
- const re = new RegExp(op.find, op.flags || 'g');
85
- if (op.count && op.count > 0) {
86
- let n = 0;
87
- text = text.replace(re, (m) => {
88
- if (n < (op.count as number)) {
89
- n += 1;
90
- return op.replace;
91
- }
92
- return m;
93
- });
94
- } else text = text.replace(re, op.replace);
95
- } else {
96
- // Check if the text to find exists
97
- if (!text.includes(op.find)) {
98
- console.warn(
99
- `Warning: Text not found for replace operation: "${op.find.substring(0, 50)}${op.find.length > 50 ? '...' : ''}"`,
100
- );
101
- }
102
- if (op.count && op.count > 0) {
103
- let remaining = op.count as number;
104
- let idx = text.indexOf(op.find);
105
- while (idx !== -1 && remaining > 0) {
106
- text =
107
- text.slice(0, idx) +
108
- op.replace +
109
- text.slice(idx + op.find.length);
110
- remaining -= 1;
111
- idx = text.indexOf(op.find, idx + op.replace.length);
112
- }
113
- } else {
114
- text = text.split(op.find).join(op.replace);
115
- }
116
- }
117
- // Only count as applied if text actually changed
118
- if (text !== originalText) {
119
- applied += 1;
120
- }
121
- } else if (op.type === 'insert') {
122
- if (op.position === 'start') {
123
- text = `${op.content}${text}`;
124
- applied += 1;
125
- continue;
126
- }
127
- if (op.position === 'end') {
128
- text = `${text}${op.content}`;
129
- applied += 1;
130
- continue;
131
- }
132
- if (!op.pattern) {
133
- return createToolError(
134
- 'insert requires pattern for before/after',
135
- 'validation',
136
- {
137
- parameter: 'pattern',
138
- suggestion: 'Provide a pattern to anchor the insertion',
139
- },
140
- );
141
- }
142
- const idx = text.indexOf(op.pattern);
143
- if (idx === -1) continue;
144
- if (op.position === 'before')
145
- text = text.slice(0, idx) + op.content + text.slice(idx);
146
- else
147
- text =
148
- text.slice(0, idx + op.pattern.length) +
149
- op.content +
150
- text.slice(idx + op.pattern.length);
151
- applied += 1;
152
- if (op.once) continue;
153
- } else if (op.type === 'delete_range') {
154
- const startIdx = text.indexOf(op.start);
155
- if (startIdx === -1) continue;
156
- const after = startIdx + op.start.length;
157
- const endIdx = text.indexOf(op.end, after);
158
- if (endIdx === -1) continue;
159
- const from = op.includeBoundaries ? startIdx : after;
160
- const to = op.includeBoundaries ? endIdx + op.end.length : endIdx;
161
- text = text.slice(0, from) + text.slice(to);
162
- applied += 1;
163
- }
164
- }
165
-
166
- await writeFile(path, text);
167
- return { ok: true, path, opsApplied: applied, bytes: text.length };
168
- },
169
- });
@@ -1,7 +0,0 @@
1
- - Edit a file using structured operations
2
- - Supported ops: `replace`, `insert`, `delete_range`
3
- - Paths are relative to the project root; can create files if flagged
4
-
5
- Usage tips:
6
- - Prefer minimal, targeted changes for clarity
7
- - For sweeping refactors, consider Write or Patch to replace full content