@aurite-ai/kai 0.2.0-dev.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.
Files changed (31) hide show
  1. package/README.md +190 -0
  2. package/dist/kai-mcp.cjs +1652 -0
  3. package/dist/kai-mcp.cjs.map +7 -0
  4. package/dist/templates/copilot-configs/claude-code/.claude/CLAUDE.md +272 -0
  5. package/dist/templates/copilot-configs/claude-code/.claude/agents/architect.md +133 -0
  6. package/dist/templates/copilot-configs/claude-code/.claude/agents/implementer.md +88 -0
  7. package/dist/templates/copilot-configs/claude-code/.claude/settings.json +87 -0
  8. package/dist/templates/copilot-configs/claude-code/.claude/skills/documentation/SKILL.md +54 -0
  9. package/dist/templates/copilot-configs/claude-code/.claude/skills/verification/SKILL.md +125 -0
  10. package/dist/templates/frameworks/langgraph/README.md +118 -0
  11. package/dist/templates/frameworks/langgraph/main.py +97 -0
  12. package/dist/templates/frameworks/langgraph/pyproject.toml +29 -0
  13. package/dist/templates/frameworks/langgraph/src/agent/__init__.py +31 -0
  14. package/dist/templates/frameworks/langgraph/src/agent/graph.py +257 -0
  15. package/dist/templates/frameworks/langgraph/src/agent/state.py +65 -0
  16. package/dist/templates/frameworks/langgraph/src/agent/tools.py +30 -0
  17. package/dist/templates/frameworks/openai/README.md +131 -0
  18. package/dist/templates/frameworks/openai/main.py +80 -0
  19. package/dist/templates/frameworks/openai/pyproject.toml +28 -0
  20. package/dist/templates/frameworks/openai/src/agent/__init__.py +18 -0
  21. package/dist/templates/frameworks/openai/src/agent/agents.py +122 -0
  22. package/dist/templates/frameworks/openai/src/agent/tools.py +100 -0
  23. package/dist/templates/index.d.ts +85 -0
  24. package/dist/templates/index.d.ts.map +1 -0
  25. package/dist/templates/index.js +270 -0
  26. package/dist/templates/index.js.map +1 -0
  27. package/dist/templates/knowledge-base/langgraph-best-practices.mdc +277 -0
  28. package/dist/templates/knowledge-base/openai-agents-overview.mdc +602 -0
  29. package/dist/templates/project-env +7 -0
  30. package/dist/templates/project-gitignore +26 -0
  31. package/package.json +66 -0
@@ -0,0 +1,270 @@
1
+ /**
2
+ * Template access utilities for file-based templates.
3
+ *
4
+ * Templates are stored as files in the templates/ directory and are
5
+ * read from the filesystem at runtime. This makes templates easier
6
+ * to maintain and edit compared to embedding them as strings.
7
+ */
8
+ import * as fs from 'node:fs/promises';
9
+ import * as path from 'node:path';
10
+ import { fileURLToPath } from 'node:url';
11
+ // =============================================================================
12
+ // Template Directory Resolution
13
+ // =============================================================================
14
+ // Cache the templates directory path
15
+ let _templatesDir = null;
16
+ /**
17
+ * Get the absolute path to the templates directory.
18
+ *
19
+ * Works for both:
20
+ * - Development: templates/ relative to src/templates/
21
+ * - Bundled CJS: templates/ relative to the bundle location
22
+ * - npm installed: templates/ in package directory
23
+ * - Docker: via KAI_TEMPLATES_DIR environment variable
24
+ */
25
+ function getTemplatesDir() {
26
+ // Return cached value if available
27
+ if (_templatesDir) {
28
+ return _templatesDir;
29
+ }
30
+ // 1. Check KAI_TEMPLATES_DIR env var first (Docker, explicit config)
31
+ const envPath = process.env.KAI_TEMPLATES_DIR;
32
+ if (envPath) {
33
+ _templatesDir = envPath;
34
+ return _templatesDir;
35
+ }
36
+ // 2. Try to detect from module location
37
+ // In CJS bundles, import.meta.url is empty/undefined
38
+ // In ESM development, we use import.meta.url
39
+ // Check if we're in a CJS bundle (import.meta.url will be undefined or empty)
40
+ // biome-ignore lint/suspicious/noExplicitAny: CJS/ESM compatibility check
41
+ const metaUrl = import.meta?.url;
42
+ if (metaUrl) {
43
+ // ESM mode (development or tsc compiled)
44
+ const __filename = fileURLToPath(metaUrl);
45
+ const __dirname = path.dirname(__filename);
46
+ // Development: templates/ is at apps/mcp/templates/ (up from src/templates/)
47
+ if (__dirname.includes('src')) {
48
+ _templatesDir = path.resolve(__dirname, '..', '..', 'templates');
49
+ return _templatesDir;
50
+ }
51
+ // TSC compiled: dist/templates/index.js -> templates are in dist/templates/
52
+ // __dirname = dist/templates, templates are at dist/templates/copilot-configs/, etc.
53
+ if (__dirname.includes('dist')) {
54
+ _templatesDir = __dirname;
55
+ return _templatesDir;
56
+ }
57
+ // ESM bundled fallback
58
+ _templatesDir = path.resolve(__dirname, 'templates');
59
+ return _templatesDir;
60
+ }
61
+ // CJS mode (bundled)
62
+ // In bundled CJS with esbuild, we need to find templates/ relative to the bundle
63
+ // The bundle is at dist/kai-mcp.cjs, templates are at dist/templates/
64
+ // Try __dirname first (available in CJS context, more reliable than process.argv)
65
+ // In bundled CJS, __dirname points to the directory containing the bundle
66
+ // biome-ignore lint/suspicious/noExplicitAny: CJS global check
67
+ const cjsDirname = typeof __dirname !== 'undefined' ? __dirname : undefined;
68
+ if (cjsDirname) {
69
+ _templatesDir = path.resolve(cjsDirname, 'templates');
70
+ return _templatesDir;
71
+ }
72
+ // Fallback: Use process.argv[1] which is the path to the script being executed
73
+ // This works for `node dist/kai-mcp.cjs` and npx scenarios
74
+ // but may fail when loaded as a library or in some test runners
75
+ const scriptPath = process.argv[1];
76
+ if (scriptPath) {
77
+ const scriptDir = path.dirname(scriptPath);
78
+ _templatesDir = path.resolve(scriptDir, 'templates');
79
+ return _templatesDir;
80
+ }
81
+ // Last resort: use cwd
82
+ _templatesDir = path.resolve(process.cwd(), 'templates');
83
+ return _templatesDir;
84
+ }
85
+ // =============================================================================
86
+ // Template Metadata
87
+ // =============================================================================
88
+ /**
89
+ * Available framework templates.
90
+ */
91
+ export const FRAMEWORK_TEMPLATES = [
92
+ {
93
+ id: 'langgraph',
94
+ name: 'LangGraph',
95
+ description: 'Python agent framework using LangGraph for stateful workflows',
96
+ path: 'frameworks/langgraph',
97
+ },
98
+ {
99
+ id: 'openai',
100
+ name: 'OpenAI Agent SDK',
101
+ description: 'Python agent framework using OpenAI Agents SDK for tool calling and multi-agent handoffs',
102
+ path: 'frameworks/openai',
103
+ },
104
+ ];
105
+ /**
106
+ * Available copilot configuration templates.
107
+ */
108
+ export const COPILOT_CONFIG_TEMPLATES = [
109
+ {
110
+ id: 'claude-code',
111
+ name: 'Claude Code',
112
+ description: 'Configuration for Claude Code (formerly Cline)',
113
+ path: 'copilot-configs/claude-code',
114
+ },
115
+ ];
116
+ /**
117
+ * Get a framework template by ID.
118
+ */
119
+ export function getFrameworkTemplate(id) {
120
+ return FRAMEWORK_TEMPLATES.find((t) => t.id === id);
121
+ }
122
+ /**
123
+ * Get a copilot config template by ID.
124
+ */
125
+ export function getCopilotConfigTemplate(id) {
126
+ return COPILOT_CONFIG_TEMPLATES.find((t) => t.id === id);
127
+ }
128
+ /**
129
+ * List all available frameworks.
130
+ */
131
+ export function listFrameworks() {
132
+ return [...FRAMEWORK_TEMPLATES];
133
+ }
134
+ /**
135
+ * List all available copilot configs.
136
+ */
137
+ export function listCopilotConfigs() {
138
+ return [...COPILOT_CONFIG_TEMPLATES];
139
+ }
140
+ // =============================================================================
141
+ // File Reading Utilities
142
+ // =============================================================================
143
+ /**
144
+ * Recursively read all files from a directory.
145
+ * Returns TemplateFile[] with relative paths.
146
+ */
147
+ async function readDirectoryRecursive(dirPath, basePath = '') {
148
+ const files = [];
149
+ try {
150
+ const entries = await fs.readdir(dirPath, { withFileTypes: true });
151
+ for (const entry of entries) {
152
+ const fullPath = path.join(dirPath, entry.name);
153
+ const relativePath = basePath ? path.join(basePath, entry.name) : entry.name;
154
+ if (entry.isDirectory()) {
155
+ // Recursively read subdirectory
156
+ const subFiles = await readDirectoryRecursive(fullPath, relativePath);
157
+ files.push(...subFiles);
158
+ }
159
+ else if (entry.isFile()) {
160
+ // Read file content
161
+ const content = await fs.readFile(fullPath, 'utf-8');
162
+ files.push({
163
+ path: relativePath,
164
+ content,
165
+ });
166
+ }
167
+ }
168
+ }
169
+ catch (error) {
170
+ // Log template read failures for debugging
171
+ // ENOENT (directory doesn't exist) is expected in some scenarios, log as debug
172
+ const isNotFound = error.code === 'ENOENT';
173
+ if (isNotFound) {
174
+ console.warn(`[kai] Template directory not found: ${dirPath}`);
175
+ }
176
+ else {
177
+ console.error(`[kai] Failed to read template directory: ${dirPath}`, error);
178
+ }
179
+ // Return empty array - caller should handle gracefully
180
+ }
181
+ return files;
182
+ }
183
+ /**
184
+ * Read a single template file.
185
+ */
186
+ async function readTemplateFile(relativePath) {
187
+ const templatesDir = getTemplatesDir();
188
+ const fullPath = path.join(templatesDir, relativePath);
189
+ try {
190
+ return await fs.readFile(fullPath, 'utf-8');
191
+ }
192
+ catch {
193
+ return null;
194
+ }
195
+ }
196
+ // =============================================================================
197
+ // Public API: Get Template Files
198
+ // =============================================================================
199
+ /**
200
+ * Get project-level template files (.env template, .gitignore).
201
+ */
202
+ export async function getProjectFiles() {
203
+ const files = [];
204
+ const envContent = await readTemplateFile('project-env');
205
+ if (envContent) {
206
+ files.push({ path: '.env', content: envContent });
207
+ }
208
+ const gitignoreContent = await readTemplateFile('project-gitignore');
209
+ if (gitignoreContent) {
210
+ files.push({ path: '.gitignore', content: gitignoreContent });
211
+ }
212
+ return files;
213
+ }
214
+ /**
215
+ * Get LangGraph framework files.
216
+ */
217
+ export async function getLangGraphFiles() {
218
+ const templatesDir = getTemplatesDir();
219
+ const frameworkDir = path.join(templatesDir, 'frameworks', 'langgraph');
220
+ return readDirectoryRecursive(frameworkDir);
221
+ }
222
+ /**
223
+ * Get OpenAI framework files.
224
+ */
225
+ export async function getOpenAIFiles() {
226
+ const templatesDir = getTemplatesDir();
227
+ const frameworkDir = path.join(templatesDir, 'frameworks', 'openai');
228
+ return readDirectoryRecursive(frameworkDir);
229
+ }
230
+ /**
231
+ * Get Claude Code configuration files.
232
+ */
233
+ export async function getClaudeCodeFiles() {
234
+ const templatesDir = getTemplatesDir();
235
+ const configDir = path.join(templatesDir, 'copilot-configs', 'claude-code');
236
+ return readDirectoryRecursive(configDir);
237
+ }
238
+ /**
239
+ * Get knowledge base seed files.
240
+ */
241
+ export async function getKnowledgeBaseFiles() {
242
+ const templatesDir = getTemplatesDir();
243
+ const kbDir = path.join(templatesDir, 'knowledge-base');
244
+ return readDirectoryRecursive(kbDir);
245
+ }
246
+ /**
247
+ * Get template files for a framework.
248
+ */
249
+ export async function getFrameworkFiles(frameworkId) {
250
+ switch (frameworkId) {
251
+ case 'langgraph':
252
+ return getLangGraphFiles();
253
+ case 'openai':
254
+ return getOpenAIFiles();
255
+ default:
256
+ throw new Error(`Unknown framework: ${frameworkId}`);
257
+ }
258
+ }
259
+ /**
260
+ * Get config files for a copilot.
261
+ */
262
+ export async function getCopilotConfigFiles(copilotId) {
263
+ switch (copilotId) {
264
+ case 'claude-code':
265
+ return getClaudeCodeFiles();
266
+ default:
267
+ throw new Error(`Unknown copilot: ${copilotId}`);
268
+ }
269
+ }
270
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/templates/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,277 @@
1
+ ---
2
+ type: knowledge
3
+ title: LangGraph Best Practices
4
+ summary: >
5
+ Comprehensive reference for building LangGraph agent workflows including core
6
+ concepts, graph construction patterns, state management, advanced patterns
7
+ like Send API and Command, and production-ready code examples.
8
+ created_at: "2026-02-10T00:00:00Z"
9
+ updated_at: "2026-02-10T00:00:00Z"
10
+
11
+ source:
12
+ file: langgraph-best-practices.mdc
13
+ project: null
14
+ path: null
15
+
16
+ classification:
17
+ category: reference
18
+ confidence: 1.0
19
+ reasoning: >
20
+ Seed content providing technical reference documentation for the LangGraph
21
+ framework. Covers API patterns, state management, and code examples.
22
+ topics:
23
+ - LangGraph
24
+ - Agent Workflows
25
+ - State Management
26
+ - Python AI Agents
27
+ - Structured Output
28
+
29
+ status: active
30
+ ---
31
+
32
+ # LangGraph Best Practices
33
+
34
+ ## Core Concepts
35
+
36
+ LangGraph models agent workflows as graphs with three key components:
37
+
38
+ 1. **State**: A shared data structure representing the current snapshot of your application
39
+ - Use TypedDict or Pydantic BaseModel for schema definition
40
+ - Define reducers to specify how updates are applied to each state key
41
+ - Default reducer overwrites values; use Annotated types for custom reducers (e.g., operator.add for lists)
42
+ - For message-based workflows, use MessagesState or the add_messages reducer
43
+
44
+ 2. **Nodes**: Functions that encode logic and perform computation
45
+ - Accept state as input, perform work, return state updates
46
+ - Can be synchronous or asynchronous Python functions
47
+ - Can accept optional config (RunnableConfig) and runtime (Runtime) parameters
48
+ - Nodes do the work - they contain LLM calls or regular code
49
+ - Add nodes with builder.add_node("node_name", node_function)
50
+
51
+ 3. **Edges**: Functions that determine which node executes next
52
+ - Edges tell what to do next based on current state
53
+ - Normal edges: Direct transitions with add_edge("node_a", "node_b")
54
+ - Conditional edges: Dynamic routing with add_conditional_edges("node_a", routing_function)
55
+ - Use START constant for entry points, END for terminal nodes
56
+ - Multiple outgoing edges execute destination nodes in parallel
57
+
58
+ ## Graph Construction Pattern
59
+
60
+ 1. Define State schema (TypedDict or Pydantic model)
61
+ 2. Create StateGraph: builder = StateGraph(State)
62
+ 3. Add nodes: builder.add_node("name", function)
63
+ 4. Add edges: builder.add_edge(START, "first_node") and builder.add_edge("node_a", "node_b")
64
+ 5. Add conditional edges if needed: builder.add_conditional_edges("node", routing_func)
65
+ 6. Compile: graph = builder.compile()
66
+
67
+ ## State Management
68
+
69
+ - **Multiple Schemas**: Use InputState, OutputState, OverallState, and PrivateState for different node communication patterns
70
+ - **Reducers**: Annotate state keys with reducer functions to control update behavior
71
+ - Default: Overwrite existing value
72
+ - operator.add: Append to lists
73
+ - add_messages: Smart message list management with ID tracking
74
+ - **MessagesState**: Prebuilt state for chat applications with messages key using add_messages reducer
75
+
76
+ ## Advanced Patterns
77
+
78
+ - **Send API**: For map-reduce patterns, return Send objects from conditional edges to dynamically create parallel branches
79
+ - **Command**: Combine state updates and routing in a single node by returning Command(update={...}, goto="next_node")
80
+ - Use Command when you need both state updates AND routing decisions
81
+ - Requires type annotation: Command[Literal["node_name"]]
82
+ - Command adds dynamic edges but doesn't override static edges
83
+ - **Subgraphs**: Use Command.PARENT to navigate from subgraph nodes to parent graph nodes
84
+
85
+ ## Execution Model
86
+
87
+ - Graph uses message passing with discrete "super-steps"
88
+ - Nodes in parallel are part of same super-step
89
+ - Sequential nodes belong to separate super-steps
90
+ - Execution terminates when all nodes are inactive and no messages in transit
91
+
92
+ ## Important Requirements
93
+
94
+ - **MUST compile graph** before use: graph = builder.compile()
95
+ - Use START constant for entry points, END for terminal nodes
96
+ - Import required: from langgraph.graph import StateGraph, START, END
97
+ - For messages: from langgraph.graph.message import add_messages
98
+ - For commands: from langgraph.types import Command, Send
99
+
100
+ ## LLM Usage
101
+
102
+ When calling llms, such as through ChatAnthropic, always specify structured output and call the models with .with_structured_output. Also specify method="json_schema".
103
+
104
+ Here is an example code snippet:
105
+ ```
106
+ from langchain_anthropic import ChatAnthropic
107
+ from pydantic import BaseModel, Field
108
+
109
+ model = ChatAnthropic(model="claude-sonnet-4-5-20250929")
110
+
111
+ class Movie(BaseModel):
112
+ """A movie with details."""
113
+ title: str = Field(..., description="The title of the movie")
114
+ year: int = Field(..., description="The year the movie was released")
115
+ director: str = Field(..., description="The director of the movie")
116
+ rating: float = Field(..., description="The movie's rating out of 10")
117
+
118
+ model_with_structure = model.with_structured_output(Movie, method="json_schema")
119
+ response = model_with_structure.invoke("Provide details about the movie Inception")
120
+ ```
121
+ In this code snippet, response will contain an object like Movie(title='Inception', year=2010, director='Christopher Nolan', rating=8.8)
122
+
123
+ Generate production-ready, well-structured LangGraph workflows following these patterns.
124
+
125
+ Here is an example workflow for reference:
126
+
127
+ # Step 1: Define tools and model
128
+
129
+ from langchain.tools import tool
130
+ from langchain.chat_models import init_chat_model
131
+
132
+
133
+ model = init_chat_model(
134
+ "claude-sonnet-4-5-20250929",
135
+ temperature=0
136
+ )
137
+
138
+
139
+ # Define tools
140
+ @tool
141
+ def multiply(a: int, b: int) -> int:
142
+ """Multiply a and b.
143
+
144
+ Args:
145
+ a: First int
146
+ b: Second int
147
+ """
148
+ return a * b
149
+
150
+
151
+ @tool
152
+ def add(a: int, b: int) -> int:
153
+ """Adds a and b.
154
+
155
+ Args:
156
+ a: First int
157
+ b: Second int
158
+ """
159
+ return a + b
160
+
161
+
162
+ @tool
163
+ def divide(a: int, b: int) -> float:
164
+ """Divide a and b.
165
+
166
+ Args:
167
+ a: First int
168
+ b: Second int
169
+ """
170
+ return a / b
171
+
172
+
173
+ # Augment the LLM with tools
174
+ tools = [add, multiply, divide]
175
+ tools_by_name = {tool.name: tool for tool in tools}
176
+ model_with_tools = model.bind_tools(tools)
177
+
178
+ # Step 2: Define state
179
+
180
+ from langchain.messages import AnyMessage
181
+ from typing_extensions import TypedDict, Annotated
182
+ import operator
183
+
184
+
185
+ class MessagesState(TypedDict):
186
+ messages: Annotated[list[AnyMessage], operator.add]
187
+ llm_calls: int
188
+
189
+ # Step 3: Define model node
190
+ from langchain.messages import SystemMessage
191
+
192
+
193
+ def llm_call(state: dict):
194
+ """LLM decides whether to call a tool or not"""
195
+
196
+ return {
197
+ "messages": [
198
+ model_with_tools.invoke(
199
+ [
200
+ SystemMessage(
201
+ content="You are a helpful assistant tasked with performing arithmetic on a set of inputs."
202
+ )
203
+ ]
204
+ + state["messages"]
205
+ )
206
+ ],
207
+ "llm_calls": state.get('llm_calls', 0) + 1
208
+ }
209
+
210
+
211
+ # Step 4: Define tool node
212
+
213
+ from langchain.messages import ToolMessage
214
+
215
+
216
+ def tool_node(state: dict):
217
+ """Performs the tool call"""
218
+
219
+ result = []
220
+ for tool_call in state["messages"][-1].tool_calls:
221
+ tool = tools_by_name[tool_call["name"]]
222
+ observation = tool.invoke(tool_call["args"])
223
+ result.append(ToolMessage(content=observation, tool_call_id=tool_call["id"]))
224
+ return {"messages": result}
225
+
226
+ # Step 5: Define logic to determine whether to end
227
+
228
+ from typing import Literal
229
+ from langgraph.graph import StateGraph, START, END
230
+
231
+
232
+ # Conditional edge function to route to the tool node or end based upon whether the LLM made a tool call
233
+ def should_continue(state: MessagesState) -> Literal["tool_node", END]:
234
+ """Decide if we should continue the loop or stop based upon whether the LLM made a tool call"""
235
+
236
+ messages = state["messages"]
237
+ last_message = messages[-1]
238
+
239
+ # If the LLM makes a tool call, then perform an action
240
+ if last_message.tool_calls:
241
+ return "tool_node"
242
+
243
+ # Otherwise, we stop (reply to the user)
244
+ return END
245
+
246
+ # Step 6: Build agent
247
+
248
+ # Build workflow
249
+ agent_builder = StateGraph(MessagesState)
250
+
251
+ # Add nodes
252
+ agent_builder.add_node("llm_call", llm_call)
253
+ agent_builder.add_node("tool_node", tool_node)
254
+
255
+ # Add edges to connect nodes
256
+ agent_builder.add_edge(START, "llm_call")
257
+ agent_builder.add_conditional_edges(
258
+ "llm_call",
259
+ should_continue,
260
+ ["tool_node", END]
261
+ )
262
+ agent_builder.add_edge("tool_node", "llm_call")
263
+
264
+ # Compile the agent
265
+ agent = agent_builder.compile()
266
+
267
+
268
+ from IPython.display import Image, display
269
+ # Show the agent
270
+ display(Image(agent.get_graph(xray=True).draw_mermaid_png()))
271
+
272
+ # Invoke
273
+ from langchain.messages import HumanMessage
274
+ messages = [HumanMessage(content="Add 3 and 4.")]
275
+ messages = agent.invoke({"messages": messages})
276
+ for m in messages["messages"]:
277
+ m.pretty_print()