k0ntext 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +623 -0
- package/bin/k0ntext.js +12 -0
- package/dist/agents/cleanup-agent.d.ts +39 -0
- package/dist/agents/cleanup-agent.d.ts.map +1 -0
- package/dist/agents/cleanup-agent.js +56 -0
- package/dist/agents/cleanup-agent.js.map +1 -0
- package/dist/agents/performance-agent.d.ts +37 -0
- package/dist/agents/performance-agent.d.ts.map +1 -0
- package/dist/agents/performance-agent.js +91 -0
- package/dist/agents/performance-agent.js.map +1 -0
- package/dist/analyzer/index.d.ts +5 -0
- package/dist/analyzer/index.d.ts.map +1 -0
- package/dist/analyzer/index.js +5 -0
- package/dist/analyzer/index.js.map +1 -0
- package/dist/analyzer/intelligent-analyzer.d.ts +111 -0
- package/dist/analyzer/intelligent-analyzer.d.ts.map +1 -0
- package/dist/analyzer/intelligent-analyzer.js +537 -0
- package/dist/analyzer/intelligent-analyzer.js.map +1 -0
- package/dist/cli/commands/cleanup.d.ts +3 -0
- package/dist/cli/commands/cleanup.d.ts.map +1 -0
- package/dist/cli/commands/cleanup.js +24 -0
- package/dist/cli/commands/cleanup.js.map +1 -0
- package/dist/cli/commands/export.d.ts +9 -0
- package/dist/cli/commands/export.d.ts.map +1 -0
- package/dist/cli/commands/export.js +72 -0
- package/dist/cli/commands/export.js.map +1 -0
- package/dist/cli/commands/import.d.ts +9 -0
- package/dist/cli/commands/import.d.ts.map +1 -0
- package/dist/cli/commands/import.js +62 -0
- package/dist/cli/commands/import.js.map +1 -0
- package/dist/cli/commands/performance.d.ts +9 -0
- package/dist/cli/commands/performance.d.ts.map +1 -0
- package/dist/cli/commands/performance.js +36 -0
- package/dist/cli/commands/performance.js.map +1 -0
- package/dist/cli/commands/validate.d.ts +9 -0
- package/dist/cli/commands/validate.d.ts.map +1 -0
- package/dist/cli/commands/validate.js +82 -0
- package/dist/cli/commands/validate.js.map +1 -0
- package/dist/cli/commands/watch.d.ts +9 -0
- package/dist/cli/commands/watch.d.ts.map +1 -0
- package/dist/cli/commands/watch.js +72 -0
- package/dist/cli/commands/watch.js.map +1 -0
- package/dist/cli/generate.d.ts +3 -0
- package/dist/cli/generate.d.ts.map +1 -0
- package/dist/cli/generate.js +194 -0
- package/dist/cli/generate.js.map +1 -0
- package/dist/cli/index.d.ts +9 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +448 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/sync.d.ts +26 -0
- package/dist/cli/sync.d.ts.map +1 -0
- package/dist/cli/sync.js +163 -0
- package/dist/cli/sync.js.map +1 -0
- package/dist/config/cleanup-config.d.ts +26 -0
- package/dist/config/cleanup-config.d.ts.map +1 -0
- package/dist/config/cleanup-config.js +21 -0
- package/dist/config/cleanup-config.js.map +1 -0
- package/dist/db/client.d.ts +284 -0
- package/dist/db/client.d.ts.map +1 -0
- package/dist/db/client.js +688 -0
- package/dist/db/client.js.map +1 -0
- package/dist/db/index.d.ts +6 -0
- package/dist/db/index.d.ts.map +1 -0
- package/dist/db/index.js +6 -0
- package/dist/db/index.js.map +1 -0
- package/dist/db/schema.d.ts +41 -0
- package/dist/db/schema.d.ts.map +1 -0
- package/dist/db/schema.js +226 -0
- package/dist/db/schema.js.map +1 -0
- package/dist/embeddings/index.d.ts +5 -0
- package/dist/embeddings/index.d.ts.map +1 -0
- package/dist/embeddings/index.js +5 -0
- package/dist/embeddings/index.js.map +1 -0
- package/dist/embeddings/openrouter.d.ts +133 -0
- package/dist/embeddings/openrouter.d.ts.map +1 -0
- package/dist/embeddings/openrouter.js +455 -0
- package/dist/embeddings/openrouter.js.map +1 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +18 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp.d.ts +29 -0
- package/dist/mcp.d.ts.map +1 -0
- package/dist/mcp.js +257 -0
- package/dist/mcp.js.map +1 -0
- package/docs/ARCHIVE/MIGRATE_TO_NEW_REPO.md +222 -0
- package/docs/ARCHIVE/MIGRATE_TO_UNIFIED.md +220 -0
- package/docs/CLEANUP.md +76 -0
- package/docs/MCP_QUICKSTART.md +219 -0
- package/docs/QUICKSTART.md +119 -0
- package/docs/TROUBLESHOOTING.md +611 -0
- package/package.json +100 -0
- package/skills/context-optimize/SKILL.md +86 -0
- package/skills/implement/SKILL.md +150 -0
- package/skills/plan/SKILL.md +143 -0
- package/skills/research/SKILL.md +103 -0
- package/skills/validate/SKILL.md +62 -0
- package/skills/verify-docs/SKILL.md +77 -0
- package/src/agents/cleanup-agent.ts +96 -0
- package/src/agents/performance-agent.ts +117 -0
- package/src/analyzer/index.ts +10 -0
- package/src/analyzer/intelligent-analyzer.ts +640 -0
- package/src/cli/commands/cleanup.ts +26 -0
- package/src/cli/commands/export.ts +82 -0
- package/src/cli/commands/import.ts +73 -0
- package/src/cli/commands/performance.ts +40 -0
- package/src/cli/commands/validate.ts +98 -0
- package/src/cli/commands/watch.ts +83 -0
- package/src/cli/generate.ts +219 -0
- package/src/cli/index.ts +510 -0
- package/src/cli/sync.ts +194 -0
- package/src/config/cleanup-config.ts +42 -0
- package/src/db/client.ts +949 -0
- package/src/db/index.ts +19 -0
- package/src/db/schema.ts +241 -0
- package/src/embeddings/index.ts +11 -0
- package/src/embeddings/openrouter.ts +592 -0
- package/src/index.ts +57 -0
- package/src/mcp.ts +354 -0
- package/templates/AI_CONTEXT.md.template +245 -0
- package/templates/base/README.md +260 -0
- package/templates/base/RPI_WORKFLOW_PLAN.md +325 -0
- package/templates/base/agents/api-developer.md +76 -0
- package/templates/base/agents/context-engineer.md +525 -0
- package/templates/base/agents/core-architect.md +76 -0
- package/templates/base/agents/database-ops.md +76 -0
- package/templates/base/agents/deployment-ops.md +76 -0
- package/templates/base/agents/integration-hub.md +76 -0
- package/templates/base/analytics/README.md +114 -0
- package/templates/base/automation/config.json +58 -0
- package/templates/base/automation/generators/code-mapper.js +308 -0
- package/templates/base/automation/generators/index-builder.js +321 -0
- package/templates/base/automation/hooks/post-commit.sh +83 -0
- package/templates/base/automation/hooks/pre-commit.sh +103 -0
- package/templates/base/ci-templates/README.md +108 -0
- package/templates/base/ci-templates/github-actions/context-check.yml +144 -0
- package/templates/base/ci-templates/github-actions/validate-docs.yml +105 -0
- package/templates/base/commands/analytics.md +238 -0
- package/templates/base/commands/auto-sync.md +172 -0
- package/templates/base/commands/collab.md +194 -0
- package/templates/base/commands/context-optimize.md +226 -0
- package/templates/base/commands/help.md +485 -0
- package/templates/base/commands/rpi-implement.md +164 -0
- package/templates/base/commands/rpi-plan.md +147 -0
- package/templates/base/commands/rpi-research.md +145 -0
- package/templates/base/commands/session-resume.md +144 -0
- package/templates/base/commands/session-save.md +112 -0
- package/templates/base/commands/validate-all.md +77 -0
- package/templates/base/commands/verify-docs-current.md +86 -0
- package/templates/base/config/base.json +57 -0
- package/templates/base/config/environments/development.json +13 -0
- package/templates/base/config/environments/production.json +17 -0
- package/templates/base/config/environments/staging.json +13 -0
- package/templates/base/config/local.json.example +21 -0
- package/templates/base/context/.meta/generated-at.json +18 -0
- package/templates/base/context/ARCHITECTURE_SNAPSHOT.md +156 -0
- package/templates/base/context/CODE_TO_WORKFLOW_MAP.md +94 -0
- package/templates/base/context/FILE_OWNERSHIP.md +57 -0
- package/templates/base/context/INTEGRATION_POINTS.md +92 -0
- package/templates/base/context/KNOWN_GOTCHAS.md +195 -0
- package/templates/base/context/TESTING_MAP.md +95 -0
- package/templates/base/context/WORKFLOW_INDEX.md +129 -0
- package/templates/base/context/workflows/WORKFLOW_TEMPLATE.md +294 -0
- package/templates/base/indexes/agents/CAPABILITY_MATRIX.md +255 -0
- package/templates/base/indexes/agents/CATEGORY_INDEX.md +44 -0
- package/templates/base/indexes/code/CATEGORY_INDEX.md +38 -0
- package/templates/base/indexes/routing/CATEGORY_INDEX.md +39 -0
- package/templates/base/indexes/search/CATEGORY_INDEX.md +39 -0
- package/templates/base/indexes/workflows/CATEGORY_INDEX.md +38 -0
- package/templates/base/knowledge/README.md +98 -0
- package/templates/base/knowledge/sessions/README.md +88 -0
- package/templates/base/knowledge/sessions/TEMPLATE.md +150 -0
- package/templates/base/knowledge/shared/decisions/0001-adopt-context-engineering.md +144 -0
- package/templates/base/knowledge/shared/decisions/README.md +49 -0
- package/templates/base/knowledge/shared/decisions/TEMPLATE.md +123 -0
- package/templates/base/knowledge/shared/patterns/README.md +62 -0
- package/templates/base/knowledge/shared/patterns/TEMPLATE.md +120 -0
- package/templates/base/plans/PLAN_TEMPLATE.md +316 -0
- package/templates/base/plans/active/.gitkeep +0 -0
- package/templates/base/plans/completed/.gitkeep +0 -0
- package/templates/base/research/RESEARCH_TEMPLATE.md +245 -0
- package/templates/base/research/active/.gitkeep +0 -0
- package/templates/base/research/completed/.gitkeep +0 -0
- package/templates/base/schemas/agent.schema.json +141 -0
- package/templates/base/schemas/anchors.schema.json +54 -0
- package/templates/base/schemas/automation.schema.json +93 -0
- package/templates/base/schemas/command.schema.json +134 -0
- package/templates/base/schemas/hashes.schema.json +40 -0
- package/templates/base/schemas/manifest.schema.json +117 -0
- package/templates/base/schemas/plan.schema.json +136 -0
- package/templates/base/schemas/research.schema.json +115 -0
- package/templates/base/schemas/roles.schema.json +34 -0
- package/templates/base/schemas/session.schema.json +77 -0
- package/templates/base/schemas/settings.schema.json +244 -0
- package/templates/base/schemas/staleness.schema.json +53 -0
- package/templates/base/schemas/team-config.schema.json +42 -0
- package/templates/base/schemas/workflow.schema.json +126 -0
- package/templates/base/session/checkpoints/.gitkeep +2 -0
- package/templates/base/session/current/state.json +20 -0
- package/templates/base/session/history/.gitkeep +2 -0
- package/templates/base/settings.json +3 -0
- package/templates/base/standards/COMPATIBILITY.md +219 -0
- package/templates/base/standards/EXTENSION_GUIDELINES.md +280 -0
- package/templates/base/standards/QUALITY_CHECKLIST.md +211 -0
- package/templates/base/standards/README.md +66 -0
- package/templates/base/sync/anchors.json +6 -0
- package/templates/base/sync/hashes.json +6 -0
- package/templates/base/sync/staleness.json +10 -0
- package/templates/base/team/README.md +168 -0
- package/templates/base/team/config.json +79 -0
- package/templates/base/team/roles.json +145 -0
- package/templates/base/tools/bin/claude-context.js +151 -0
- package/templates/base/tools/lib/anchor-resolver.js +276 -0
- package/templates/base/tools/lib/config-loader.js +363 -0
- package/templates/base/tools/lib/detector.js +350 -0
- package/templates/base/tools/lib/diagnose.js +206 -0
- package/templates/base/tools/lib/drift-detector.js +373 -0
- package/templates/base/tools/lib/errors.js +199 -0
- package/templates/base/tools/lib/index.js +36 -0
- package/templates/base/tools/lib/init.js +192 -0
- package/templates/base/tools/lib/logger.js +230 -0
- package/templates/base/tools/lib/placeholder.js +201 -0
- package/templates/base/tools/lib/session-manager.js +354 -0
- package/templates/base/tools/lib/validate.js +521 -0
- package/templates/base/tools/package.json +49 -0
- package/templates/handlebars/aider-config.hbs +146 -0
- package/templates/handlebars/antigravity.hbs +377 -0
- package/templates/handlebars/claude.hbs +183 -0
- package/templates/handlebars/cline.hbs +62 -0
- package/templates/handlebars/continue-config.hbs +116 -0
- package/templates/handlebars/copilot.hbs +130 -0
- package/templates/handlebars/partials/gotcha-list.hbs +11 -0
- package/templates/handlebars/partials/header.hbs +3 -0
- package/templates/handlebars/partials/workflow-summary.hbs +16 -0
- package/templates/handlebars/windsurf-rules.hbs +69 -0
- package/templates/hooks/post-commit.hbs +28 -0
- package/templates/hooks/pre-commit.hbs +46 -0
|
@@ -0,0 +1,592 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenRouter Client
|
|
3
|
+
*
|
|
4
|
+
* Client for OpenRouter API supporting both embeddings and chat completions.
|
|
5
|
+
* Used for intelligent initialization and context understanding.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { createHash } from 'crypto';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* OpenRouter API endpoints
|
|
12
|
+
*/
|
|
13
|
+
const OPENROUTER_EMBEDDINGS_URL = 'https://openrouter.ai/api/v1/embeddings';
|
|
14
|
+
const OPENROUTER_CHAT_URL = 'https://openrouter.ai/api/v1/chat/completions';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Default models
|
|
18
|
+
*/
|
|
19
|
+
const DEFAULT_EMBEDDING_MODEL = 'openai/text-embedding-3-small';
|
|
20
|
+
const DEFAULT_CHAT_MODEL = 'anthropic/claude-3-haiku';
|
|
21
|
+
const EMBEDDING_DIMENSION = 1536;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Embedding response from OpenRouter
|
|
25
|
+
*/
|
|
26
|
+
interface EmbeddingResponse {
|
|
27
|
+
data: Array<{
|
|
28
|
+
embedding: number[];
|
|
29
|
+
index: number;
|
|
30
|
+
}>;
|
|
31
|
+
model: string;
|
|
32
|
+
usage: {
|
|
33
|
+
prompt_tokens: number;
|
|
34
|
+
total_tokens: number;
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Chat completion response from OpenRouter
|
|
40
|
+
*/
|
|
41
|
+
interface ChatResponse {
|
|
42
|
+
id: string;
|
|
43
|
+
choices: Array<{
|
|
44
|
+
message: {
|
|
45
|
+
role: string;
|
|
46
|
+
content: string;
|
|
47
|
+
};
|
|
48
|
+
finish_reason: string;
|
|
49
|
+
}>;
|
|
50
|
+
model: string;
|
|
51
|
+
usage: {
|
|
52
|
+
prompt_tokens: number;
|
|
53
|
+
completion_tokens: number;
|
|
54
|
+
total_tokens: number;
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Message for chat completion
|
|
60
|
+
*/
|
|
61
|
+
export interface ChatMessage {
|
|
62
|
+
role: 'system' | 'user' | 'assistant';
|
|
63
|
+
content: string;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Configuration for OpenRouter client
|
|
68
|
+
*/
|
|
69
|
+
export interface OpenRouterConfig {
|
|
70
|
+
apiKey: string;
|
|
71
|
+
embeddingModel?: string;
|
|
72
|
+
chatModel?: string;
|
|
73
|
+
siteUrl?: string;
|
|
74
|
+
siteName?: string;
|
|
75
|
+
minRequestInterval?: number; // ms between requests for rate limiting
|
|
76
|
+
requestTimeout?: number; // ms timeout for requests
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* OpenRouter-specific error types
|
|
81
|
+
*/
|
|
82
|
+
export class OpenRouterError extends Error {
|
|
83
|
+
constructor(message: string, public code?: string) {
|
|
84
|
+
super(message);
|
|
85
|
+
this.name = 'OpenRouterError';
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export class OpenRouterRateLimitError extends OpenRouterError {
|
|
90
|
+
constructor(message: string, public retryAfter?: number) {
|
|
91
|
+
super(message, 'RATE_LIMIT');
|
|
92
|
+
this.name = 'OpenRouterRateLimitError';
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export class OpenRouterAuthError extends OpenRouterError {
|
|
97
|
+
constructor(message: string) {
|
|
98
|
+
super(message, 'AUTH_ERROR');
|
|
99
|
+
this.name = 'OpenRouterAuthError';
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export class OpenRouterTimeoutError extends OpenRouterError {
|
|
104
|
+
constructor(message: string) {
|
|
105
|
+
super(message, 'TIMEOUT');
|
|
106
|
+
this.name = 'OpenRouterTimeoutError';
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* OpenRouter client for embeddings and chat
|
|
112
|
+
*/
|
|
113
|
+
export class OpenRouterClient {
|
|
114
|
+
private apiKey: string;
|
|
115
|
+
private embeddingModel: string;
|
|
116
|
+
private chatModel: string;
|
|
117
|
+
private siteUrl: string;
|
|
118
|
+
private siteName: string;
|
|
119
|
+
private embeddingCache: Map<string, number[]> = new Map();
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Retry configuration
|
|
123
|
+
*/
|
|
124
|
+
private maxRetries = 3;
|
|
125
|
+
private baseRetryDelay = 1000; // ms
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Rate limiting configuration
|
|
129
|
+
*/
|
|
130
|
+
private minRequestInterval: number;
|
|
131
|
+
private lastRequestTime = 0;
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Timeout configuration
|
|
135
|
+
*/
|
|
136
|
+
private requestTimeout: number;
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Sleep for specified milliseconds
|
|
140
|
+
*/
|
|
141
|
+
private sleep(ms: number): Promise<void> {
|
|
142
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Create a timeout promise that rejects after specified milliseconds
|
|
147
|
+
*/
|
|
148
|
+
private createTimeoutPromise(ms: number): Promise<never> {
|
|
149
|
+
return new Promise((_, reject) => {
|
|
150
|
+
setTimeout(() => {
|
|
151
|
+
reject(new OpenRouterTimeoutError(`Request timed out after ${ms}ms`));
|
|
152
|
+
}, ms);
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Wrap a fetch promise with timeout
|
|
158
|
+
*/
|
|
159
|
+
private async fetchWithTimeout(url: string, options: RequestInit, timeout: number): Promise<Response> {
|
|
160
|
+
return Promise.race([
|
|
161
|
+
fetch(url, options),
|
|
162
|
+
this.createTimeoutPromise(timeout)
|
|
163
|
+
]);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Apply rate limiting before API requests
|
|
168
|
+
*/
|
|
169
|
+
private async applyRateLimit(): Promise<void> {
|
|
170
|
+
const now = Date.now();
|
|
171
|
+
const timeSinceLastRequest = now - this.lastRequestTime;
|
|
172
|
+
|
|
173
|
+
if (timeSinceLastRequest < this.minRequestInterval) {
|
|
174
|
+
const waitTime = this.minRequestInterval - timeSinceLastRequest;
|
|
175
|
+
await this.sleep(waitTime);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
this.lastRequestTime = Date.now();
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
constructor(config: OpenRouterConfig) {
|
|
182
|
+
if (!config.apiKey) {
|
|
183
|
+
throw new Error('OPENROUTER_API_KEY is required');
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
this.apiKey = config.apiKey;
|
|
187
|
+
this.embeddingModel = config.embeddingModel || DEFAULT_EMBEDDING_MODEL;
|
|
188
|
+
this.chatModel = config.chatModel || DEFAULT_CHAT_MODEL;
|
|
189
|
+
this.siteUrl = config.siteUrl || 'https://github.com/SireJeff/claude-context-engineering-template';
|
|
190
|
+
this.siteName = config.siteName || 'AI Context';
|
|
191
|
+
this.minRequestInterval = config.minRequestInterval || 100; // ms between requests
|
|
192
|
+
this.requestTimeout = config.requestTimeout || 30000; // 30 seconds default
|
|
193
|
+
this.lastRequestTime = 0;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Generate embedding for a single text
|
|
198
|
+
*/
|
|
199
|
+
async embed(text: string): Promise<number[]> {
|
|
200
|
+
// Check cache
|
|
201
|
+
const cacheKey = this.hashText(text);
|
|
202
|
+
if (this.embeddingCache.has(cacheKey)) {
|
|
203
|
+
return this.embeddingCache.get(cacheKey)!;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
let lastError: Error | null = null;
|
|
207
|
+
for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
|
|
208
|
+
try {
|
|
209
|
+
// Apply rate limiting before each attempt
|
|
210
|
+
await this.applyRateLimit();
|
|
211
|
+
|
|
212
|
+
const response = await this.fetchWithTimeout(OPENROUTER_EMBEDDINGS_URL, {
|
|
213
|
+
method: 'POST',
|
|
214
|
+
headers: {
|
|
215
|
+
'Authorization': `Bearer ${this.apiKey}`,
|
|
216
|
+
'Content-Type': 'application/json',
|
|
217
|
+
'HTTP-Referer': this.siteUrl,
|
|
218
|
+
'X-Title': this.siteName
|
|
219
|
+
},
|
|
220
|
+
body: JSON.stringify({
|
|
221
|
+
model: this.embeddingModel,
|
|
222
|
+
input: text
|
|
223
|
+
})
|
|
224
|
+
}, this.requestTimeout);
|
|
225
|
+
|
|
226
|
+
if (!response.ok) {
|
|
227
|
+
const error = await response.text();
|
|
228
|
+
// Don't retry auth errors
|
|
229
|
+
if (response.status === 401 || response.status === 403) {
|
|
230
|
+
throw new OpenRouterAuthError(`Authentication failed: ${error}`);
|
|
231
|
+
}
|
|
232
|
+
// Rate limit errors - check for retry-after header
|
|
233
|
+
if (response.status === 429) {
|
|
234
|
+
const retryAfter = response.headers.get('Retry-After');
|
|
235
|
+
throw new OpenRouterRateLimitError(
|
|
236
|
+
`Rate limit exceeded: ${error}`,
|
|
237
|
+
retryAfter ? parseInt(retryAfter, 10) : undefined
|
|
238
|
+
);
|
|
239
|
+
}
|
|
240
|
+
throw new OpenRouterError(`OpenRouter API error (${response.status}): ${error}`);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
const data = await response.json() as EmbeddingResponse;
|
|
244
|
+
|
|
245
|
+
if (
|
|
246
|
+
!data ||
|
|
247
|
+
!Array.isArray(data.data) ||
|
|
248
|
+
data.data.length === 0 ||
|
|
249
|
+
!data.data[0] ||
|
|
250
|
+
!Array.isArray(data.data[0].embedding)
|
|
251
|
+
) {
|
|
252
|
+
throw new OpenRouterError('OpenRouter API error: no embedding data returned in response', 'NO_DATA');
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
const embedding = data.data[0].embedding;
|
|
256
|
+
|
|
257
|
+
// Cache the result
|
|
258
|
+
this.embeddingCache.set(cacheKey, embedding);
|
|
259
|
+
|
|
260
|
+
return embedding;
|
|
261
|
+
} catch (error) {
|
|
262
|
+
// Don't retry auth errors - they'll be thrown as OpenRouterAuthError
|
|
263
|
+
if (error instanceof OpenRouterAuthError) {
|
|
264
|
+
throw error;
|
|
265
|
+
}
|
|
266
|
+
// Don't retry rate limit errors with explicit retry-after
|
|
267
|
+
if (error instanceof OpenRouterRateLimitError && error.retryAfter) {
|
|
268
|
+
throw error;
|
|
269
|
+
}
|
|
270
|
+
lastError = error instanceof Error ? error : new Error(String(error));
|
|
271
|
+
|
|
272
|
+
// Don't retry if it's the last attempt or certain error types
|
|
273
|
+
if (attempt === this.maxRetries) {
|
|
274
|
+
break;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// Exponential backoff: wait longer with each attempt
|
|
278
|
+
const delay = this.baseRetryDelay * Math.pow(2, attempt);
|
|
279
|
+
await this.sleep(delay);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
throw lastError || new OpenRouterError('OpenRouter API error: max retries exceeded', 'MAX_RETRIES');
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* Generate embeddings for multiple texts (batch)
|
|
288
|
+
*/
|
|
289
|
+
async embedBatch(texts: string[]): Promise<number[][]> {
|
|
290
|
+
// Check cache for all texts
|
|
291
|
+
const uncachedTexts: string[] = [];
|
|
292
|
+
const uncachedIndices: number[] = [];
|
|
293
|
+
const results: (number[] | null)[] = texts.map((text, index) => {
|
|
294
|
+
const cacheKey = this.hashText(text);
|
|
295
|
+
if (this.embeddingCache.has(cacheKey)) {
|
|
296
|
+
return this.embeddingCache.get(cacheKey)!;
|
|
297
|
+
}
|
|
298
|
+
uncachedTexts.push(text);
|
|
299
|
+
uncachedIndices.push(index);
|
|
300
|
+
return null;
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
// If all cached, return immediately
|
|
304
|
+
if (uncachedTexts.length === 0) {
|
|
305
|
+
return results as number[][];
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// Batch request to OpenRouter with retry
|
|
309
|
+
let lastError: Error | null = null;
|
|
310
|
+
let data: EmbeddingResponse | null = null;
|
|
311
|
+
|
|
312
|
+
for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
|
|
313
|
+
try {
|
|
314
|
+
// Apply rate limiting before each attempt
|
|
315
|
+
await this.applyRateLimit();
|
|
316
|
+
|
|
317
|
+
const response = await this.fetchWithTimeout(OPENROUTER_EMBEDDINGS_URL, {
|
|
318
|
+
method: 'POST',
|
|
319
|
+
headers: {
|
|
320
|
+
'Authorization': `Bearer ${this.apiKey}`,
|
|
321
|
+
'Content-Type': 'application/json',
|
|
322
|
+
'HTTP-Referer': this.siteUrl,
|
|
323
|
+
'X-Title': this.siteName
|
|
324
|
+
},
|
|
325
|
+
body: JSON.stringify({
|
|
326
|
+
model: this.embeddingModel,
|
|
327
|
+
input: uncachedTexts
|
|
328
|
+
})
|
|
329
|
+
}, this.requestTimeout);
|
|
330
|
+
|
|
331
|
+
if (!response.ok) {
|
|
332
|
+
const error = await response.text();
|
|
333
|
+
// Don't retry auth errors
|
|
334
|
+
if (response.status === 401 || response.status === 403) {
|
|
335
|
+
throw new OpenRouterAuthError(`Authentication failed: ${error}`);
|
|
336
|
+
}
|
|
337
|
+
// Rate limit errors
|
|
338
|
+
if (response.status === 429) {
|
|
339
|
+
const retryAfter = response.headers.get('Retry-After');
|
|
340
|
+
throw new OpenRouterRateLimitError(
|
|
341
|
+
`Rate limit exceeded: ${error}`,
|
|
342
|
+
retryAfter ? parseInt(retryAfter, 10) : undefined
|
|
343
|
+
);
|
|
344
|
+
}
|
|
345
|
+
throw new OpenRouterError(`OpenRouter API error (${response.status}): ${error}`);
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
data = await response.json() as EmbeddingResponse;
|
|
349
|
+
break; // Success, exit retry loop
|
|
350
|
+
} catch (error) {
|
|
351
|
+
// Don't retry auth errors - they'll be thrown as OpenRouterAuthError
|
|
352
|
+
if (error instanceof OpenRouterAuthError) {
|
|
353
|
+
throw error;
|
|
354
|
+
}
|
|
355
|
+
// Don't retry rate limit errors with explicit retry-after
|
|
356
|
+
if (error instanceof OpenRouterRateLimitError && error.retryAfter) {
|
|
357
|
+
throw error;
|
|
358
|
+
}
|
|
359
|
+
lastError = error instanceof Error ? error : new Error(String(error));
|
|
360
|
+
|
|
361
|
+
if (attempt === this.maxRetries) {
|
|
362
|
+
break;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
const delay = this.baseRetryDelay * Math.pow(2, attempt);
|
|
366
|
+
await this.sleep(delay);
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
if (!data) {
|
|
371
|
+
throw lastError || new OpenRouterError('OpenRouter API error: max retries exceeded', 'MAX_RETRIES');
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
// Fill in results and cache
|
|
375
|
+
for (let i = 0; i < data.data.length; i++) {
|
|
376
|
+
const embedding = data.data[i].embedding;
|
|
377
|
+
const originalIndex = uncachedIndices[i];
|
|
378
|
+
const text = uncachedTexts[i];
|
|
379
|
+
|
|
380
|
+
results[originalIndex] = embedding;
|
|
381
|
+
this.embeddingCache.set(this.hashText(text), embedding);
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
return results as number[][];
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
/**
|
|
388
|
+
* Chat completion - for intelligent context understanding
|
|
389
|
+
*/
|
|
390
|
+
async chat(messages: ChatMessage[], options?: {
|
|
391
|
+
temperature?: number;
|
|
392
|
+
maxTokens?: number;
|
|
393
|
+
model?: string;
|
|
394
|
+
}): Promise<string> {
|
|
395
|
+
let lastError: Error | null = null;
|
|
396
|
+
let data: ChatResponse | null = null;
|
|
397
|
+
|
|
398
|
+
for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
|
|
399
|
+
try {
|
|
400
|
+
// Apply rate limiting before each attempt
|
|
401
|
+
await this.applyRateLimit();
|
|
402
|
+
|
|
403
|
+
const response = await this.fetchWithTimeout(OPENROUTER_CHAT_URL, {
|
|
404
|
+
method: 'POST',
|
|
405
|
+
headers: {
|
|
406
|
+
'Authorization': `Bearer ${this.apiKey}`,
|
|
407
|
+
'Content-Type': 'application/json',
|
|
408
|
+
'HTTP-Referer': this.siteUrl,
|
|
409
|
+
'X-Title': this.siteName
|
|
410
|
+
},
|
|
411
|
+
body: JSON.stringify({
|
|
412
|
+
model: options?.model || this.chatModel,
|
|
413
|
+
messages,
|
|
414
|
+
temperature: options?.temperature ?? 0.3,
|
|
415
|
+
max_tokens: options?.maxTokens ?? 4096
|
|
416
|
+
})
|
|
417
|
+
}, this.requestTimeout);
|
|
418
|
+
|
|
419
|
+
if (!response.ok) {
|
|
420
|
+
const error = await response.text();
|
|
421
|
+
// Don't retry auth errors
|
|
422
|
+
if (response.status === 401 || response.status === 403) {
|
|
423
|
+
throw new OpenRouterAuthError(`Authentication failed: ${error}`);
|
|
424
|
+
}
|
|
425
|
+
// Rate limit errors
|
|
426
|
+
if (response.status === 429) {
|
|
427
|
+
const retryAfter = response.headers.get('Retry-After');
|
|
428
|
+
throw new OpenRouterRateLimitError(
|
|
429
|
+
`Rate limit exceeded: ${error}`,
|
|
430
|
+
retryAfter ? parseInt(retryAfter, 10) : undefined
|
|
431
|
+
);
|
|
432
|
+
}
|
|
433
|
+
throw new OpenRouterError(`OpenRouter Chat API error (${response.status}): ${error}`);
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
data = await response.json() as ChatResponse;
|
|
437
|
+
break; // Success, exit retry loop
|
|
438
|
+
} catch (error) {
|
|
439
|
+
// Don't retry auth errors - they'll be thrown as OpenRouterAuthError
|
|
440
|
+
if (error instanceof OpenRouterAuthError) {
|
|
441
|
+
throw error;
|
|
442
|
+
}
|
|
443
|
+
// Don't retry rate limit errors with explicit retry-after
|
|
444
|
+
if (error instanceof OpenRouterRateLimitError && error.retryAfter) {
|
|
445
|
+
throw error;
|
|
446
|
+
}
|
|
447
|
+
lastError = error instanceof Error ? error : new Error(String(error));
|
|
448
|
+
|
|
449
|
+
if (attempt === this.maxRetries) {
|
|
450
|
+
break;
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
const delay = this.baseRetryDelay * Math.pow(2, attempt);
|
|
454
|
+
await this.sleep(delay);
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
if (!data) {
|
|
459
|
+
throw lastError || new OpenRouterError('OpenRouter Chat API error: max retries exceeded', 'MAX_RETRIES');
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
const firstChoice = data?.choices && Array.isArray(data.choices) ? data.choices[0] : undefined;
|
|
463
|
+
const content = firstChoice?.message?.content;
|
|
464
|
+
|
|
465
|
+
if (typeof content !== 'string' || content.trim().length === 0) {
|
|
466
|
+
throw new Error('OpenRouter Chat API returned an unexpected response structure: missing or empty message content.');
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
return content;
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
/**
|
|
473
|
+
* Analyze codebase content intelligently
|
|
474
|
+
*/
|
|
475
|
+
async analyzeContent(content: string, analysisType: 'summarize' | 'extract_workflows' | 'extract_architecture' | 'suggest_context'): Promise<string> {
|
|
476
|
+
const systemPrompts: Record<string, string> = {
|
|
477
|
+
summarize: `You are an expert code analyzer. Provide a concise summary of the given code or documentation.
|
|
478
|
+
Focus on: purpose, key functionality, dependencies, and important patterns.
|
|
479
|
+
Keep the summary under 500 words.`,
|
|
480
|
+
|
|
481
|
+
extract_workflows: `You are an expert at analyzing codebases to identify workflows.
|
|
482
|
+
Extract the main workflows from the given code. For each workflow, identify:
|
|
483
|
+
1. Entry points (API routes, CLI commands, event handlers)
|
|
484
|
+
2. Key processing steps
|
|
485
|
+
3. Dependencies and integrations
|
|
486
|
+
4. Data flow
|
|
487
|
+
Output as structured markdown with file:line references where possible.`,
|
|
488
|
+
|
|
489
|
+
extract_architecture: `You are an expert software architect.
|
|
490
|
+
Analyze the given code to extract the architecture patterns:
|
|
491
|
+
1. Project structure and organization
|
|
492
|
+
2. Design patterns used
|
|
493
|
+
3. Key components and their responsibilities
|
|
494
|
+
4. Integration points
|
|
495
|
+
5. Technology stack
|
|
496
|
+
Output as structured markdown.`,
|
|
497
|
+
|
|
498
|
+
suggest_context: `You are an AI context engineering expert.
|
|
499
|
+
Analyze the given codebase content and suggest:
|
|
500
|
+
1. Key context files that should be created
|
|
501
|
+
2. Important workflows to document
|
|
502
|
+
3. Agent configurations needed
|
|
503
|
+
4. Commands that would be useful
|
|
504
|
+
5. Knowledge base entries to capture
|
|
505
|
+
Be specific and actionable in your suggestions.`
|
|
506
|
+
};
|
|
507
|
+
|
|
508
|
+
return this.chat([
|
|
509
|
+
{ role: 'system', content: systemPrompts[analysisType] },
|
|
510
|
+
{ role: 'user', content }
|
|
511
|
+
]);
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
/**
|
|
515
|
+
* Calculate cosine similarity between two embeddings
|
|
516
|
+
*/
|
|
517
|
+
static cosineSimilarity(a: number[], b: number[]): number {
|
|
518
|
+
if (a.length !== b.length) {
|
|
519
|
+
throw new Error('Embeddings must have the same dimension');
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
let dotProduct = 0;
|
|
523
|
+
let normA = 0;
|
|
524
|
+
let normB = 0;
|
|
525
|
+
|
|
526
|
+
for (let i = 0; i < a.length; i++) {
|
|
527
|
+
dotProduct += a[i] * b[i];
|
|
528
|
+
normA += a[i] * a[i];
|
|
529
|
+
normB += b[i] * b[i];
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
const magnitude = Math.sqrt(normA) * Math.sqrt(normB);
|
|
533
|
+
if (magnitude === 0) return 0;
|
|
534
|
+
|
|
535
|
+
return dotProduct / magnitude;
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
/**
|
|
539
|
+
* Get embedding dimension
|
|
540
|
+
*/
|
|
541
|
+
getDimension(): number {
|
|
542
|
+
return EMBEDDING_DIMENSION;
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
/**
|
|
546
|
+
* Clear embedding cache
|
|
547
|
+
*/
|
|
548
|
+
clearCache(): void {
|
|
549
|
+
this.embeddingCache.clear();
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
/**
|
|
553
|
+
* Get cache size
|
|
554
|
+
*/
|
|
555
|
+
getCacheSize(): number {
|
|
556
|
+
return this.embeddingCache.size;
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
/**
|
|
560
|
+
* Hash text for caching
|
|
561
|
+
*/
|
|
562
|
+
private hashText(text: string): string {
|
|
563
|
+
return createHash('md5').update(text).digest('hex');
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
/**
|
|
568
|
+
* Create OpenRouter client from environment
|
|
569
|
+
*/
|
|
570
|
+
export function createOpenRouterClient(): OpenRouterClient {
|
|
571
|
+
const apiKey = process.env.OPENROUTER_API_KEY;
|
|
572
|
+
|
|
573
|
+
if (!apiKey) {
|
|
574
|
+
throw new Error(
|
|
575
|
+
'OPENROUTER_API_KEY environment variable is required.\n' +
|
|
576
|
+
'Get your API key at: https://openrouter.ai/keys'
|
|
577
|
+
);
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
return new OpenRouterClient({
|
|
581
|
+
apiKey,
|
|
582
|
+
embeddingModel: process.env.OPENROUTER_EMBEDDING_MODEL,
|
|
583
|
+
chatModel: process.env.OPENROUTER_CHAT_MODEL
|
|
584
|
+
});
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
/**
|
|
588
|
+
* Check if OpenRouter API key is available
|
|
589
|
+
*/
|
|
590
|
+
export function hasOpenRouterKey(): boolean {
|
|
591
|
+
return !!process.env.OPENROUTER_API_KEY;
|
|
592
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AI Context
|
|
3
|
+
*
|
|
4
|
+
* Unified AI Context Engineering package.
|
|
5
|
+
* Provides intelligent context for all AI coding assistants.
|
|
6
|
+
*
|
|
7
|
+
* @packageDocumentation
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
// Database
|
|
11
|
+
export {
|
|
12
|
+
DatabaseClient,
|
|
13
|
+
type ContextItem,
|
|
14
|
+
type GraphEdge,
|
|
15
|
+
type GitCommit,
|
|
16
|
+
type SyncState,
|
|
17
|
+
type AIToolConfig,
|
|
18
|
+
type SearchResult
|
|
19
|
+
} from './db/client.js';
|
|
20
|
+
|
|
21
|
+
export {
|
|
22
|
+
SCHEMA_VERSION,
|
|
23
|
+
RELATION_TYPES,
|
|
24
|
+
CONTEXT_TYPES,
|
|
25
|
+
SYNC_STATUSES,
|
|
26
|
+
AI_TOOLS,
|
|
27
|
+
AI_TOOL_FOLDERS,
|
|
28
|
+
type RelationType,
|
|
29
|
+
type ContextType,
|
|
30
|
+
type SyncStatus,
|
|
31
|
+
type AITool
|
|
32
|
+
} from './db/schema.js';
|
|
33
|
+
|
|
34
|
+
// OpenRouter / Embeddings
|
|
35
|
+
export {
|
|
36
|
+
OpenRouterClient,
|
|
37
|
+
createOpenRouterClient,
|
|
38
|
+
hasOpenRouterKey,
|
|
39
|
+
type OpenRouterConfig,
|
|
40
|
+
type ChatMessage
|
|
41
|
+
} from './embeddings/openrouter.js';
|
|
42
|
+
|
|
43
|
+
// Analyzer
|
|
44
|
+
export {
|
|
45
|
+
IntelligentAnalyzer,
|
|
46
|
+
createIntelligentAnalyzer,
|
|
47
|
+
type DiscoveredFile,
|
|
48
|
+
type AnalysisResult
|
|
49
|
+
} from './analyzer/intelligent-analyzer.js';
|
|
50
|
+
|
|
51
|
+
// MCP Server
|
|
52
|
+
export {
|
|
53
|
+
createServer,
|
|
54
|
+
startServer,
|
|
55
|
+
main as startMcpServer,
|
|
56
|
+
type ServerConfig
|
|
57
|
+
} from './mcp.js';
|