codehere 0.2.2 → 0.3.0-rc.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +88 -68
- package/dist/domain/entities/task.js +4 -4
- package/dist/error-handler.js +4 -9
- package/dist/index.js +253 -1219
- package/dist/infrastructure/config/product-context.js +7 -6
- package/dist/infrastructure/hooks/claude-sdk-hook.js +335 -63
- package/dist/infrastructure/observability/execution-tracer.js +11 -6
- package/dist/infrastructure/orchestration/agent-adapter.js +13 -0
- package/dist/infrastructure/orchestration/agent-installer.js +101 -0
- package/dist/infrastructure/orchestration/agents-config-seed.js +56 -0
- package/dist/infrastructure/orchestration/aider-adapter.js +319 -0
- package/dist/infrastructure/orchestration/artifacts-extractor.js +175 -0
- package/dist/infrastructure/orchestration/claude-code-adapter.js +352 -0
- package/dist/infrastructure/orchestration/codex-adapter.js +310 -0
- package/dist/infrastructure/orchestration/generic-cli-adapter.js +553 -0
- package/dist/infrastructure/orchestration/opencode-adapter.js +275 -0
- package/dist/infrastructure/orchestration/progress-aggregator.js +189 -0
- package/dist/infrastructure/orchestration/readiness.js +106 -0
- package/dist/infrastructure/orchestration/session-manager.js +272 -0
- package/dist/infrastructure/orchestration/session-persistence.js +109 -0
- package/dist/infrastructure/protocols/mcp-helpers.js +10 -30
- package/dist/infrastructure/protocols/mcp-server-impl.js +359 -21
- package/dist/infrastructure/protocols/ui-page.js +1827 -0
- package/dist/infrastructure/security/file-collection.js +115 -0
- package/dist/infrastructure/security/trust-scorer.js +301 -56
- package/dist/infrastructure/storage/task-repository.js +8 -7
- package/dist/monitoring.js +9 -38
- package/dist/presentation/cli/commands/help-command.js +96 -139
- package/dist/presentation/cli/commands/hook-command.js +17 -24
- package/dist/presentation/cli/commands/report-command.js +59 -72
- package/dist/presentation/cli/commands/setup-command.js +4 -4
- package/dist/presentation/cli/commands/status-command.js +321 -30
- package/dist/presentation/cli/commands/trust-command.js +84 -72
- package/dist/presentation/cli/error-display.js +17 -21
- package/dist/ui.js +11 -11
- package/package.json +24 -62
- package/dist/agents/base.js +0 -101
- package/dist/agents/orchestrator-model-driven.js +0 -141
- package/dist/agents/react-orchestrator-agent.js +0 -54
- package/dist/application/agents/execution-agent.js +0 -508
- package/dist/application/agents/multi-agent-orchestrator.js +0 -295
- package/dist/application/agents/planning-agent.js +0 -433
- package/dist/application/agents/validation-agent.js +0 -317
- package/dist/application/architectures/plan-and-act.js +0 -242
- package/dist/application/index.js +0 -12
- package/dist/application/services/agent-factory.js +0 -28
- package/dist/application/services/dependency-container.js +0 -596
- package/dist/application/services/git-context-service.js +0 -83
- package/dist/application/services/intelligent-router.js +0 -934
- package/dist/application/services/tool-executor-service.js +0 -94
- package/dist/application/use-cases/ask-question-use-case.js +0 -338
- package/dist/application/use-cases/edit-file-use-case.js +0 -830
- package/dist/application/use-cases/explain-file-use-case.js +0 -50
- package/dist/application/use-cases/index-codebase-use-case.js +0 -113
- package/dist/application/use-cases/planning-use-case.js +0 -276
- package/dist/application/use-cases/react-orchestration-use-case.js +0 -329
- package/dist/application/use-cases/reflexion-use-case.js +0 -30
- package/dist/application/workflows/deterministic-workflows.js +0 -189
- package/dist/ast-parser.js +0 -331
- package/dist/cache.js +0 -180
- package/dist/chat.js +0 -251
- package/dist/code-analysis.js +0 -327
- package/dist/context.js +0 -187
- package/dist/docs.js +0 -146
- package/dist/domain/entities/code-chunk.js +0 -6
- package/dist/domain/entities/decision.js +0 -9
- package/dist/domain/entities/edit.js +0 -6
- package/dist/domain/entities/knowledge-node.js +0 -9
- package/dist/domain/entities/knowledge-preservation.js +0 -10
- package/dist/domain/entities/memory.js +0 -6
- package/dist/domain/entities/pattern.js +0 -7
- package/dist/domain/entities/plan.js +0 -6
- package/dist/domain/entities/query.js +0 -6
- package/dist/domain/errors/embeddings-not-found-error.js +0 -13
- package/dist/domain/index.js +0 -19
- package/dist/domain/interfaces/ai-service.interface.js +0 -6
- package/dist/domain/interfaces/code-analyzer.interface.js +0 -6
- package/dist/domain/interfaces/decision-repository.interface.js +0 -9
- package/dist/domain/interfaces/embedding-repository.interface.js +0 -6
- package/dist/domain/interfaces/knowledge-graph-repository.interface.js +0 -9
- package/dist/domain/interfaces/knowledge-preservation-repository.interface.js +0 -8
- package/dist/domain/interfaces/memory-repository.interface.js +0 -6
- package/dist/domain/interfaces/pattern-repository.interface.js +0 -6
- package/dist/domain/interfaces/verification-service.interface.js +0 -6
- package/dist/domain/services/cross-team-pattern-sharing.js +0 -125
- package/dist/domain/services/decision-recorder.js +0 -223
- package/dist/domain/services/decision-retriever.js +0 -133
- package/dist/domain/services/environment-mode-manager.js +0 -22
- package/dist/domain/services/graded-memory-activation.js +0 -69
- package/dist/domain/services/knowledge-access.js +0 -132
- package/dist/domain/services/knowledge-extractor.js +0 -247
- package/dist/domain/services/knowledge-graph-builder.js +0 -268
- package/dist/domain/services/knowledge-graph-query.js +0 -187
- package/dist/domain/services/memory-retriever.js +0 -82
- package/dist/domain/services/memory-synthesizer.js +0 -91
- package/dist/domain/services/pattern-extractor.js +0 -442
- package/dist/domain/services/query-intent-classifier.js +0 -140
- package/dist/domain/services/react-loop.js +0 -186
- package/dist/domain/services/risk-tier-detector.js +0 -78
- package/dist/edit.js +0 -319
- package/dist/embed.js +0 -459
- package/dist/formatter.js +0 -168
- package/dist/git.js +0 -198
- package/dist/infrastructure/ai/cohere-ai-service.js +0 -607
- package/dist/infrastructure/ai/command-r-evaluation.js +0 -206
- package/dist/infrastructure/ai/model-config.js +0 -84
- package/dist/infrastructure/ai/provider-ai-service.js +0 -477
- package/dist/infrastructure/cache/embedding-cache.js +0 -175
- package/dist/infrastructure/cache/query-result-cache.js +0 -66
- package/dist/infrastructure/cache/response-cache.js +0 -48
- package/dist/infrastructure/cache/security-scan-cache.js +0 -47
- package/dist/infrastructure/chunking/ast-code-chunker.js +0 -19
- package/dist/infrastructure/chunking/code-aware-chunker.js +0 -397
- package/dist/infrastructure/context/context-compressor.js +0 -329
- package/dist/infrastructure/context/conversation-context.js +0 -128
- package/dist/infrastructure/context/session-manager.js +0 -94
- package/dist/infrastructure/cost/cost-tracker.js +0 -360
- package/dist/infrastructure/file-scanner/file-scanner.js +0 -106
- package/dist/infrastructure/governance/nist-ai-rmf.js +0 -292
- package/dist/infrastructure/governance/policy-as-code.js +0 -258
- package/dist/infrastructure/governance/prompt-to-code-lineage.js +0 -239
- package/dist/infrastructure/index.js +0 -7
- package/dist/infrastructure/observability/prompt-versioner.js +0 -145
- package/dist/infrastructure/protocols/a2a-metadata.js +0 -137
- package/dist/infrastructure/protocols/a2a-protocol.js +0 -314
- package/dist/infrastructure/protocols/mcp-oauth.js +0 -184
- package/dist/infrastructure/protocols/mcp-server.js +0 -333
- package/dist/infrastructure/protocols/streaming-handler.js +0 -103
- package/dist/infrastructure/providers/cohere-provider.js +0 -444
- package/dist/infrastructure/providers/fake-provider.js +0 -130
- package/dist/infrastructure/providers/local-llm-provider.js +0 -330
- package/dist/infrastructure/providers/model-config.types.js +0 -9
- package/dist/infrastructure/providers/model-provider.interface.js +0 -8
- package/dist/infrastructure/providers/openai-provider.js +0 -413
- package/dist/infrastructure/providers/openrouter-provider.js +0 -399
- package/dist/infrastructure/providers/operation-provider-resolver.js +0 -105
- package/dist/infrastructure/providers/provider-config-loader.js +0 -120
- package/dist/infrastructure/providers/provider-registry.js +0 -88
- package/dist/infrastructure/safety/pattern-detector.js +0 -133
- package/dist/infrastructure/security/ai-sast-scanner.js +0 -290
- package/dist/infrastructure/security/docker-sandbox.js +0 -178
- package/dist/infrastructure/security/enhanced-security-gate.js +0 -151
- package/dist/infrastructure/security/license-scanner.js +0 -167
- package/dist/infrastructure/security/provider-bias-detector.js +0 -164
- package/dist/infrastructure/security/sandbox-executor.js +0 -64
- package/dist/infrastructure/serialization/toon-serializer.js +0 -119
- package/dist/infrastructure/storage/memory-stream-service.js +0 -204
- package/dist/infrastructure/storage/plan-repository.js +0 -96
- package/dist/infrastructure/storage/sqlite-decision-repository.js +0 -232
- package/dist/infrastructure/storage/sqlite-embedding-repository.js +0 -311
- package/dist/infrastructure/storage/sqlite-knowledge-graph-repository.js +0 -417
- package/dist/infrastructure/storage/sqlite-knowledge-preservation-repository.js +0 -230
- package/dist/infrastructure/storage/sqlite-memory-repository.js +0 -229
- package/dist/infrastructure/storage/sqlite-pattern-repository.js +0 -249
- package/dist/infrastructure/storage/task-helpers.js +0 -197
- package/dist/infrastructure/telemetry/telemetry-service.js +0 -167
- package/dist/infrastructure/ux/appropriate-friction.js +0 -213
- package/dist/infrastructure/ux/bi-directional-learning.js +0 -337
- package/dist/infrastructure/ux/capability-boundaries.js +0 -42
- package/dist/infrastructure/ux/comprehensive-formatter.js +0 -117
- package/dist/infrastructure/ux/contextual-feature-discovery.js +0 -10
- package/dist/infrastructure/ux/expectation-management.js +0 -327
- package/dist/infrastructure/ux/feature-discovery.js +0 -14
- package/dist/infrastructure/ux/feedback-prompt.js +0 -45
- package/dist/infrastructure/ux/hitl-validation.js +0 -297
- package/dist/infrastructure/ux/progress-indicator.js +0 -116
- package/dist/infrastructure/ux/progressive-disclosure.js +0 -236
- package/dist/infrastructure/ux/review-checkpoint.js +0 -72
- package/dist/infrastructure/ux/staged-feature-intro.js +0 -24
- package/dist/infrastructure/ux/stream-markdown.js +0 -151
- package/dist/infrastructure/ux/syntax-highlighter.js +0 -91
- package/dist/infrastructure/ux/team-standards.js +0 -196
- package/dist/infrastructure/ux/transparency.js +0 -71
- package/dist/infrastructure/validation/agent-feedback.js +0 -55
- package/dist/infrastructure/validation/agent-validation-helper.js +0 -137
- package/dist/infrastructure/validation/review-handler.js +0 -40
- package/dist/infrastructure/validation/summary-aggregator.js +0 -70
- package/dist/infrastructure/validation/summary-extractor.js +0 -45
- package/dist/infrastructure/validation/trace-summary.js +0 -52
- package/dist/infrastructure/verification/verification-service.js +0 -47
- package/dist/infrastructure/xai/attention-visualizer.js +0 -172
- package/dist/infrastructure/xai/cot-visualizer.js +0 -283
- package/dist/infrastructure/xai/decision-tree-log.js +0 -212
- package/dist/infrastructure/xai/interactive-xai.js +0 -260
- package/dist/infrastructure/xai/uncertainty-quantifier.js +0 -198
- package/dist/intelligent-retrieval.js +0 -272
- package/dist/intent/classifier.js +0 -2
- package/dist/intent/context.js +0 -2
- package/dist/iterative-refinement.js +0 -189
- package/dist/learning.js +0 -232
- package/dist/log.js +0 -31
- package/dist/migrate.js +0 -133
- package/dist/parallel-processor.js +0 -534
- package/dist/planner.js +0 -198
- package/dist/policy.js +0 -26
- package/dist/presentation/cli/commands/ask-command.js +0 -314
- package/dist/presentation/cli/commands/cost-command.js +0 -191
- package/dist/presentation/cli/commands/decision-command.js +0 -207
- package/dist/presentation/cli/commands/docs-command.js +0 -25
- package/dist/presentation/cli/commands/fix-command.js +0 -53
- package/dist/presentation/cli/commands/knowledge-command.js +0 -129
- package/dist/presentation/cli/commands/learning-dashboard-command.js +0 -183
- package/dist/presentation/cli/commands/models-command.js +0 -194
- package/dist/presentation/cli/commands/orchestrate-command.js +0 -207
- package/dist/presentation/cli/commands/plan-command.js +0 -246
- package/dist/presentation/cli/commands/product-command.js +0 -199
- package/dist/presentation/cli/commands/react-command.js +0 -181
- package/dist/presentation/cli/commands/review-command.js +0 -154
- package/dist/presentation/cli/commands/run-command.js +0 -188
- package/dist/presentation/cli/commands/smart-command.js +0 -395
- package/dist/presentation/cli/commands/suggest-command.js +0 -138
- package/dist/presentation/cli/commands/trace-command.js +0 -129
- package/dist/presentation/cli/commands/undo-command.js +0 -150
- package/dist/query-optimizer.js +0 -205
- package/dist/refactor.js +0 -116
- package/dist/review.js +0 -206
- package/dist/search.js +0 -163
- package/dist/session.js +0 -704
- package/dist/task-verification.js +0 -336
- package/dist/testgen.js +0 -166
- package/dist/token-optimizer.js +0 -277
- package/dist/tools/bash.js +0 -213
- package/dist/tools/edit.js +0 -235
- package/dist/tools/executor.js +0 -153
- package/dist/tools/git-tool.js +0 -292
- package/dist/tools/parser.js +0 -158
- package/dist/tools/registry.js +0 -302
- package/dist/tools/search.js +0 -100
- package/dist/types.js +0 -2
- package/dist/utils/check-api-status.js +0 -100
- package/dist/utils/diff-parser.js +0 -162
- package/dist/verification/backup.js +0 -259
- package/dist/verification/compiler.js +0 -264
- package/dist/verification/index.js +0 -273
- package/dist/verification/test-runner.js +0 -379
|
@@ -1,534 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Parallel Processing & Caching System
|
|
3
|
-
* Now supports sequential processing with rate limiting for API limits
|
|
4
|
-
*/
|
|
5
|
-
import { readFileSync, writeFileSync, existsSync, statSync, readdirSync, mkdirSync, rmSync, unlinkSync } from 'fs';
|
|
6
|
-
import { join } from 'path';
|
|
7
|
-
import { config } from 'dotenv';
|
|
8
|
-
import { encoding_for_model } from 'tiktoken';
|
|
9
|
-
// Load .env
|
|
10
|
-
config();
|
|
11
|
-
const enc = encoding_for_model('gpt-3.5-turbo');
|
|
12
|
-
const CHUNK_SIZE = 2000; // ~500 tokens. OpenAI handles 8191 tokens per embedding.
|
|
13
|
-
const OVERLAP = 50;
|
|
14
|
-
// Rate limiting state
|
|
15
|
-
let lastApiCallTime = 0;
|
|
16
|
-
let apiCallCount = 0;
|
|
17
|
-
let monthlyApiCallCount = 0;
|
|
18
|
-
const MIN_DELAY_MS = 200; // 200ms between calls (OpenAI allows 3000 RPM for embeddings)
|
|
19
|
-
const MONTHLY_LIMIT = 50000; // OpenAI production limit is much higher
|
|
20
|
-
/**
|
|
21
|
-
* Rate-limited API call wrapper
|
|
22
|
-
*/
|
|
23
|
-
async function rateLimitedCall(fn) {
|
|
24
|
-
// Check monthly limit
|
|
25
|
-
if (monthlyApiCallCount >= MONTHLY_LIMIT) {
|
|
26
|
-
throw new Error(`Monthly API limit reached (${MONTHLY_LIMIT} calls). Please upgrade to production key or wait for limit reset.`);
|
|
27
|
-
}
|
|
28
|
-
// Calculate delay needed
|
|
29
|
-
const now = Date.now();
|
|
30
|
-
const timeSinceLastCall = now - lastApiCallTime;
|
|
31
|
-
if (timeSinceLastCall < MIN_DELAY_MS) {
|
|
32
|
-
const delay = MIN_DELAY_MS - timeSinceLastCall;
|
|
33
|
-
await new Promise(resolve => setTimeout(resolve, delay));
|
|
34
|
-
}
|
|
35
|
-
// Make API call
|
|
36
|
-
lastApiCallTime = Date.now();
|
|
37
|
-
apiCallCount++;
|
|
38
|
-
monthlyApiCallCount++;
|
|
39
|
-
try {
|
|
40
|
-
return await fn();
|
|
41
|
-
}
|
|
42
|
-
catch (error) {
|
|
43
|
-
// If rate limit error, wait longer and retry once
|
|
44
|
-
if (error instanceof Error && error.message.includes('429')) {
|
|
45
|
-
console.log('Rate limit hit, waiting 5 seconds...');
|
|
46
|
-
await new Promise(resolve => setTimeout(resolve, 5000));
|
|
47
|
-
lastApiCallTime = Date.now();
|
|
48
|
-
monthlyApiCallCount++;
|
|
49
|
-
return await fn();
|
|
50
|
-
}
|
|
51
|
-
throw error;
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
/**
|
|
55
|
-
* Sequential indexing with caching and rate limiting
|
|
56
|
-
*/
|
|
57
|
-
export async function indexParallel(files, options = {}) {
|
|
58
|
-
// Auto-detect: use parallel for large repos (>100 files), sequential for small ones
|
|
59
|
-
const shouldUseParallel = options.sequential === false || (options.sequential === undefined && files.length > 100);
|
|
60
|
-
const maxConcurrency = options.maxConcurrency || (shouldUseParallel ? 10 : 1);
|
|
61
|
-
const useCache = options.useCache !== false;
|
|
62
|
-
const cacheDir = options.cacheDir || '.codehere-cache';
|
|
63
|
-
const startTime = Date.now();
|
|
64
|
-
let indexed = 0;
|
|
65
|
-
let cached = 0;
|
|
66
|
-
let totalChunks = 0;
|
|
67
|
-
let processed = 0;
|
|
68
|
-
let lastProgressUpdate = Date.now();
|
|
69
|
-
// Create cache directory
|
|
70
|
-
if (useCache && !existsSync(cacheDir)) {
|
|
71
|
-
mkdirSync(cacheDir, { recursive: true });
|
|
72
|
-
}
|
|
73
|
-
// Reset rate limiting state
|
|
74
|
-
lastApiCallTime = 0;
|
|
75
|
-
apiCallCount = 0;
|
|
76
|
-
// Progress update helper (throttled to every 2 seconds)
|
|
77
|
-
const updateProgress = (force = false) => {
|
|
78
|
-
const now = Date.now();
|
|
79
|
-
if (force || now - lastProgressUpdate > 2000) {
|
|
80
|
-
const percent = ((processed / files.length) * 100).toFixed(1);
|
|
81
|
-
const elapsed = ((now - startTime) / 1000).toFixed(0);
|
|
82
|
-
process.stdout.write(`\r[${processed}/${files.length}] ${percent}% | Cached: ${cached} | Indexed: ${indexed} | Time: ${elapsed}s`);
|
|
83
|
-
lastProgressUpdate = now;
|
|
84
|
-
}
|
|
85
|
-
};
|
|
86
|
-
if (shouldUseParallel && maxConcurrency > 1) {
|
|
87
|
-
// Parallel processing with smart batching
|
|
88
|
-
console.log(`Processing ${files.length} files in parallel (concurrency: ${maxConcurrency})...`);
|
|
89
|
-
// First pass: Check cache status for all files (fast, no API calls)
|
|
90
|
-
const fileStatuses = [];
|
|
91
|
-
if (useCache) {
|
|
92
|
-
// Check cache in parallel (no rate limiting needed)
|
|
93
|
-
const cacheChecks = await Promise.allSettled(files.map(async (file) => {
|
|
94
|
-
const cacheKey = getCacheKey(file);
|
|
95
|
-
const cachePath = join(cacheDir, cacheKey);
|
|
96
|
-
if (existsSync(cachePath)) {
|
|
97
|
-
try {
|
|
98
|
-
const cacheEntry = JSON.parse(readFileSync(cachePath, 'utf-8'));
|
|
99
|
-
const fileHash = getFileHash(file);
|
|
100
|
-
return { file, needsProcessing: cacheEntry.hash !== fileHash };
|
|
101
|
-
}
|
|
102
|
-
catch {
|
|
103
|
-
return { file, needsProcessing: true };
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
return { file, needsProcessing: true };
|
|
107
|
-
}));
|
|
108
|
-
for (const check of cacheChecks) {
|
|
109
|
-
if (check.status === 'fulfilled') {
|
|
110
|
-
fileStatuses.push(check.value);
|
|
111
|
-
}
|
|
112
|
-
else {
|
|
113
|
-
fileStatuses.push({ file: '', needsProcessing: true });
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
else {
|
|
118
|
-
fileStatuses.push(...files.map(file => ({ file, needsProcessing: true })));
|
|
119
|
-
}
|
|
120
|
-
// Separate cached vs needs-processing files
|
|
121
|
-
const cachedFiles = fileStatuses.filter(s => !s.needsProcessing).map(s => s.file);
|
|
122
|
-
const needsProcessingFiles = fileStatuses.filter(s => s.needsProcessing).map(s => s.file);
|
|
123
|
-
// Process cached files in parallel (no rate limiting)
|
|
124
|
-
if (cachedFiles.length > 0) {
|
|
125
|
-
const cachedResults = await Promise.allSettled(cachedFiles.map(file => processFileWithCache(file, useCache, cacheDir)));
|
|
126
|
-
for (const result of cachedResults) {
|
|
127
|
-
processed++;
|
|
128
|
-
if (result.status === 'fulfilled') {
|
|
129
|
-
cached++;
|
|
130
|
-
totalChunks += result.value.chunks;
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
updateProgress(true);
|
|
134
|
-
}
|
|
135
|
-
// Process files that need API calls with rate limiting and batching
|
|
136
|
-
if (needsProcessingFiles.length > 0) {
|
|
137
|
-
const batches = [];
|
|
138
|
-
for (let i = 0; i < needsProcessingFiles.length; i += maxConcurrency) {
|
|
139
|
-
batches.push(needsProcessingFiles.slice(i, i + maxConcurrency));
|
|
140
|
-
}
|
|
141
|
-
for (let batchIdx = 0; batchIdx < batches.length; batchIdx++) {
|
|
142
|
-
const batch = batches[batchIdx];
|
|
143
|
-
// Process batch in parallel (rate limiting handled inside processFileWithCache)
|
|
144
|
-
const results = await Promise.allSettled(batch.map(file => processFileWithCache(file, useCache, cacheDir)));
|
|
145
|
-
// Process results
|
|
146
|
-
for (let i = 0; i < results.length; i++) {
|
|
147
|
-
const result = results[i];
|
|
148
|
-
const file = batch[i];
|
|
149
|
-
processed++;
|
|
150
|
-
if (result.status === 'fulfilled') {
|
|
151
|
-
if (result.value.cached) {
|
|
152
|
-
cached++;
|
|
153
|
-
}
|
|
154
|
-
else {
|
|
155
|
-
indexed++;
|
|
156
|
-
}
|
|
157
|
-
totalChunks += result.value.chunks;
|
|
158
|
-
}
|
|
159
|
-
else {
|
|
160
|
-
const error = result.reason instanceof Error ? result.reason.message : String(result.reason);
|
|
161
|
-
console.error(`\n✗ Error: ${file}: ${error}`);
|
|
162
|
-
// If monthly limit hit, stop processing
|
|
163
|
-
if (error.includes('Monthly API limit')) {
|
|
164
|
-
console.error('\nStopping indexing due to monthly API limit.');
|
|
165
|
-
break;
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
// Update progress
|
|
170
|
-
updateProgress();
|
|
171
|
-
// Check if we should stop
|
|
172
|
-
if (processed >= files.length)
|
|
173
|
-
break;
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
else {
|
|
178
|
-
// Sequential processing (for small repos or when explicitly requested)
|
|
179
|
-
console.log(`Processing ${files.length} files sequentially...`);
|
|
180
|
-
for (let i = 0; i < files.length; i++) {
|
|
181
|
-
const file = files[i];
|
|
182
|
-
processed++;
|
|
183
|
-
try {
|
|
184
|
-
const result = await processFileWithCache(file, useCache, cacheDir);
|
|
185
|
-
if (result.cached) {
|
|
186
|
-
cached++;
|
|
187
|
-
}
|
|
188
|
-
else {
|
|
189
|
-
indexed++;
|
|
190
|
-
}
|
|
191
|
-
totalChunks += result.chunks;
|
|
192
|
-
}
|
|
193
|
-
catch (error) {
|
|
194
|
-
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
195
|
-
console.error(`\n[${i + 1}/${files.length}] ✗ Error: ${file}: ${errorMsg}`);
|
|
196
|
-
// If monthly limit hit, stop processing
|
|
197
|
-
if (errorMsg.includes('Monthly API limit')) {
|
|
198
|
-
console.error('Stopping indexing due to monthly API limit.');
|
|
199
|
-
break;
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
// Update progress every 10 files or every 2 seconds
|
|
203
|
-
if (i % 10 === 0 || i === files.length - 1) {
|
|
204
|
-
updateProgress(true);
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
// Final progress update
|
|
209
|
-
updateProgress(true);
|
|
210
|
-
console.log(''); // New line after progress
|
|
211
|
-
const timeMs = Date.now() - startTime;
|
|
212
|
-
console.log(`\nAPI Call Stats: ${apiCallCount} calls made, ${monthlyApiCallCount} total this month`);
|
|
213
|
-
return {
|
|
214
|
-
indexed,
|
|
215
|
-
cached,
|
|
216
|
-
totalChunks,
|
|
217
|
-
timeMs,
|
|
218
|
-
};
|
|
219
|
-
}
|
|
220
|
-
/**
|
|
221
|
-
* Process file with caching
|
|
222
|
-
*/
|
|
223
|
-
async function processFileWithCache(filepath, useCache, cacheDir) {
|
|
224
|
-
if (!useCache) {
|
|
225
|
-
// Process without cache
|
|
226
|
-
const content = readFileSync(filepath, 'utf-8');
|
|
227
|
-
const chunks = chunkTextLocal(content, CHUNK_SIZE, OVERLAP);
|
|
228
|
-
// Embed chunks (this is the slow part)
|
|
229
|
-
for (const chunk of chunks) {
|
|
230
|
-
await embedTextLocal(chunk);
|
|
231
|
-
}
|
|
232
|
-
return { cached: false, chunks: chunks.length };
|
|
233
|
-
}
|
|
234
|
-
// Check cache
|
|
235
|
-
const cacheKey = getCacheKey(filepath);
|
|
236
|
-
const cachePath = join(cacheDir, cacheKey);
|
|
237
|
-
if (existsSync(cachePath)) {
|
|
238
|
-
const cacheEntry = JSON.parse(readFileSync(cachePath, 'utf-8'));
|
|
239
|
-
// Check if file hasn't changed
|
|
240
|
-
const fileHash = getFileHash(filepath);
|
|
241
|
-
if (cacheEntry.hash === fileHash) {
|
|
242
|
-
// Cache hit - return cached chunks
|
|
243
|
-
return { cached: true, chunks: cacheEntry.chunks.length };
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
// Cache miss or stale - process file
|
|
247
|
-
try {
|
|
248
|
-
const content = readFileSync(filepath, 'utf-8');
|
|
249
|
-
if (!content || content.trim().length === 0) {
|
|
250
|
-
return { cached: false, chunks: 0 };
|
|
251
|
-
}
|
|
252
|
-
const chunks = chunkTextLocal(content, CHUNK_SIZE, OVERLAP);
|
|
253
|
-
if (chunks.length === 0) {
|
|
254
|
-
return { cached: false, chunks: 0 };
|
|
255
|
-
}
|
|
256
|
-
const fileHash = getFileHash(filepath);
|
|
257
|
-
// Embed chunks (rate limiting handled by embedTextLocal)
|
|
258
|
-
const embeddings = [];
|
|
259
|
-
for (let i = 0; i < chunks.length; i++) {
|
|
260
|
-
const chunk = chunks[i];
|
|
261
|
-
try {
|
|
262
|
-
const embedding = await embedTextLocal(chunk);
|
|
263
|
-
embeddings[i] = embedding;
|
|
264
|
-
}
|
|
265
|
-
catch (error) {
|
|
266
|
-
// If embedding fails, log and skip this chunk
|
|
267
|
-
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
268
|
-
console.error(`Failed to embed chunk ${i + 1}/${chunks.length} of ${filepath}: ${errorMsg}`);
|
|
269
|
-
// If monthly limit hit, stop processing this file
|
|
270
|
-
if (errorMsg.includes('Monthly API limit')) {
|
|
271
|
-
throw error; // Re-throw to stop processing
|
|
272
|
-
}
|
|
273
|
-
// Continue with other chunks for other errors
|
|
274
|
-
continue;
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
// Only save if we got at least one embedding
|
|
278
|
-
if (embeddings.length === 0) {
|
|
279
|
-
return { cached: false, chunks: 0 };
|
|
280
|
-
}
|
|
281
|
-
// Save to cache
|
|
282
|
-
const cacheEntry = {
|
|
283
|
-
filepath,
|
|
284
|
-
hash: fileHash,
|
|
285
|
-
chunks: chunks.slice(0, embeddings.length).map((content, i) => ({
|
|
286
|
-
content,
|
|
287
|
-
embedding: embeddings[i],
|
|
288
|
-
})),
|
|
289
|
-
timestamp: Date.now(),
|
|
290
|
-
};
|
|
291
|
-
writeFileSync(cachePath, JSON.stringify(cacheEntry, null, 2), 'utf-8');
|
|
292
|
-
return { cached: false, chunks: embeddings.length };
|
|
293
|
-
}
|
|
294
|
-
catch (error) {
|
|
295
|
-
// Re-throw monthly limit errors to stop processing
|
|
296
|
-
if (error instanceof Error && error.message.includes('Monthly API limit')) {
|
|
297
|
-
throw error;
|
|
298
|
-
}
|
|
299
|
-
// Log other errors but don't throw - allow other files to process
|
|
300
|
-
console.error(`Error processing ${filepath}: ${error instanceof Error ? error.message : String(error)}`);
|
|
301
|
-
return { cached: false, chunks: 0 };
|
|
302
|
-
}
|
|
303
|
-
}
|
|
304
|
-
/**
|
|
305
|
-
* Get cache key from filepath
|
|
306
|
-
*/
|
|
307
|
-
function getCacheKey(filepath) {
|
|
308
|
-
// Create a safe filename from filepath
|
|
309
|
-
return filepath.replace(/[^a-zA-Z0-9]/g, '_') + '.json';
|
|
310
|
-
}
|
|
311
|
-
/**
|
|
312
|
-
* Get file hash (simple: use mtime + size)
|
|
313
|
-
*/
|
|
314
|
-
function getFileHash(filepath) {
|
|
315
|
-
try {
|
|
316
|
-
const stat = statSync(filepath);
|
|
317
|
-
return `${stat.mtimeMs}-${stat.size}`;
|
|
318
|
-
}
|
|
319
|
-
catch {
|
|
320
|
-
return 'unknown';
|
|
321
|
-
}
|
|
322
|
-
}
|
|
323
|
-
/**
|
|
324
|
-
* Incremental update: only process changed files
|
|
325
|
-
*/
|
|
326
|
-
export async function updateIndexIncremental(baseDir, options = {}) {
|
|
327
|
-
const { cacheDir = '.codehere-cache', watchMode = false } = options;
|
|
328
|
-
// Find all code files
|
|
329
|
-
const files = findCodeFiles(baseDir);
|
|
330
|
-
let updated = 0;
|
|
331
|
-
let unchanged = 0;
|
|
332
|
-
let totalChunks = 0;
|
|
333
|
-
for (const filepath of files) {
|
|
334
|
-
const cacheKey = getCacheKey(filepath);
|
|
335
|
-
const cachePath = join(cacheDir, cacheKey);
|
|
336
|
-
if (existsSync(cachePath)) {
|
|
337
|
-
const cacheEntry = JSON.parse(readFileSync(cachePath, 'utf-8'));
|
|
338
|
-
const fileHash = getFileHash(filepath);
|
|
339
|
-
if (cacheEntry.hash === fileHash) {
|
|
340
|
-
// File unchanged
|
|
341
|
-
unchanged++;
|
|
342
|
-
totalChunks += cacheEntry.chunks.length;
|
|
343
|
-
continue;
|
|
344
|
-
}
|
|
345
|
-
}
|
|
346
|
-
// File changed or new - process it
|
|
347
|
-
const result = await processFileWithCache(filepath, true, cacheDir);
|
|
348
|
-
updated++;
|
|
349
|
-
totalChunks += result.chunks;
|
|
350
|
-
}
|
|
351
|
-
return {
|
|
352
|
-
updated,
|
|
353
|
-
unchanged,
|
|
354
|
-
totalChunks,
|
|
355
|
-
};
|
|
356
|
-
}
|
|
357
|
-
/**
|
|
358
|
-
* Find code files recursively
|
|
359
|
-
*/
|
|
360
|
-
function findCodeFiles(dir) {
|
|
361
|
-
const files = [];
|
|
362
|
-
try {
|
|
363
|
-
const entries = readdirSync(dir, { withFileTypes: true });
|
|
364
|
-
for (const entry of entries) {
|
|
365
|
-
const fullPath = join(dir, entry.name);
|
|
366
|
-
// Skip build artifacts, caches, and hidden dirs
|
|
367
|
-
if (entry.name.startsWith('.') || entry.name === 'node_modules' || entry.name === 'dist' || entry.name === 'build' || entry.name === '.codehere-cache' || entry.name === '.codehere') {
|
|
368
|
-
continue;
|
|
369
|
-
}
|
|
370
|
-
if (entry.isDirectory()) {
|
|
371
|
-
files.push(...findCodeFiles(fullPath));
|
|
372
|
-
}
|
|
373
|
-
else if (entry.isFile()) {
|
|
374
|
-
const ext = entry.name.split('.').pop()?.toLowerCase();
|
|
375
|
-
if (ext && ['ts', 'tsx', 'js', 'jsx'].includes(ext)) {
|
|
376
|
-
files.push(fullPath);
|
|
377
|
-
}
|
|
378
|
-
}
|
|
379
|
-
}
|
|
380
|
-
}
|
|
381
|
-
catch {
|
|
382
|
-
// Skip directories we can't read
|
|
383
|
-
}
|
|
384
|
-
return files;
|
|
385
|
-
}
|
|
386
|
-
/**
|
|
387
|
-
* Clear cache
|
|
388
|
-
*/
|
|
389
|
-
export function clearCache(cacheDir = '.codehere-cache') {
|
|
390
|
-
if (!existsSync(cacheDir)) {
|
|
391
|
-
return;
|
|
392
|
-
}
|
|
393
|
-
try {
|
|
394
|
-
rmSync(cacheDir, { recursive: true, force: true });
|
|
395
|
-
}
|
|
396
|
-
catch {
|
|
397
|
-
// Fallback: delete files individually
|
|
398
|
-
const files = readdirSync(cacheDir);
|
|
399
|
-
for (const file of files) {
|
|
400
|
-
const filePath = join(cacheDir, file);
|
|
401
|
-
try {
|
|
402
|
-
unlinkSync(filePath);
|
|
403
|
-
}
|
|
404
|
-
catch {
|
|
405
|
-
// Skip files we can't delete
|
|
406
|
-
}
|
|
407
|
-
}
|
|
408
|
-
}
|
|
409
|
-
}
|
|
410
|
-
/**
|
|
411
|
-
* Chunk text locally (duplicate from embed.ts for independence)
|
|
412
|
-
*/
|
|
413
|
-
function chunkTextLocal(text, targetTokens, overlap) {
|
|
414
|
-
const tokens = enc.encode(text);
|
|
415
|
-
const chunks = [];
|
|
416
|
-
if (tokens.length <= targetTokens) {
|
|
417
|
-
return [text];
|
|
418
|
-
}
|
|
419
|
-
let start = 0;
|
|
420
|
-
while (start < tokens.length) {
|
|
421
|
-
const end = Math.min(start + targetTokens, tokens.length);
|
|
422
|
-
const chunkTokens = tokens.slice(start, end);
|
|
423
|
-
const decoded = enc.decode(chunkTokens);
|
|
424
|
-
const chunkText = typeof decoded === 'string' ? decoded : new TextDecoder().decode(decoded);
|
|
425
|
-
chunks.push(chunkText);
|
|
426
|
-
if (end >= tokens.length)
|
|
427
|
-
break;
|
|
428
|
-
start = end - overlap;
|
|
429
|
-
}
|
|
430
|
-
return chunks;
|
|
431
|
-
}
|
|
432
|
-
/**
|
|
433
|
-
* Embed text locally (duplicate from embed.ts for independence)
|
|
434
|
-
* Now uses rate limiting
|
|
435
|
-
*/
|
|
436
|
-
async function embedTextLocal(text, retries = 3) {
|
|
437
|
-
// Check for FAKE_AI mode - return fake embeddings
|
|
438
|
-
if (process.env.CODEHERE_FAKE_AI === '1') {
|
|
439
|
-
// Return fake embeddings (1024 dimensions, small random values)
|
|
440
|
-
return Array(1024).fill(0).map(() => Math.random() * 0.001 - 0.0005);
|
|
441
|
-
}
|
|
442
|
-
// Use rate-limited wrapper with auto-detected provider
|
|
443
|
-
return await rateLimitedCall(async () => {
|
|
444
|
-
for (let attempt = 0; attempt < retries; attempt++) {
|
|
445
|
-
try {
|
|
446
|
-
if (process.env.OPENAI_API_KEY) {
|
|
447
|
-
const controller = new AbortController();
|
|
448
|
-
const timeout = setTimeout(() => controller.abort(), 10000);
|
|
449
|
-
const response = await fetch('https://api.openai.com/v1/embeddings', {
|
|
450
|
-
method: 'POST',
|
|
451
|
-
headers: {
|
|
452
|
-
'Authorization': `Bearer ${process.env.OPENAI_API_KEY}`,
|
|
453
|
-
'Content-Type': 'application/json',
|
|
454
|
-
},
|
|
455
|
-
body: JSON.stringify({ input: text, model: 'text-embedding-3-small' }),
|
|
456
|
-
signal: controller.signal,
|
|
457
|
-
});
|
|
458
|
-
clearTimeout(timeout);
|
|
459
|
-
if (!response.ok)
|
|
460
|
-
throw new Error(`OpenAI embedding error: ${response.status}`);
|
|
461
|
-
const data = await response.json();
|
|
462
|
-
return data.data?.[0]?.embedding || [];
|
|
463
|
-
}
|
|
464
|
-
else if (process.env.COHERE_API_KEY) {
|
|
465
|
-
const { CohereClient } = require('cohere-ai');
|
|
466
|
-
const cohere = new CohereClient({ token: process.env.COHERE_API_KEY });
|
|
467
|
-
const response = await cohere.embed({
|
|
468
|
-
texts: [text],
|
|
469
|
-
model: 'embed-english-v3.0',
|
|
470
|
-
inputType: 'search_document',
|
|
471
|
-
});
|
|
472
|
-
const embeddings = Array.isArray(response.embeddings)
|
|
473
|
-
? response.embeddings
|
|
474
|
-
: response.embeddings.embeddings || [];
|
|
475
|
-
return embeddings[0] || [];
|
|
476
|
-
}
|
|
477
|
-
else {
|
|
478
|
-
throw new Error('No embedding API key found. Set OPENAI_API_KEY or COHERE_API_KEY.');
|
|
479
|
-
}
|
|
480
|
-
}
|
|
481
|
-
catch (error) {
|
|
482
|
-
if (attempt === retries - 1)
|
|
483
|
-
throw error;
|
|
484
|
-
await new Promise(resolve => setTimeout(resolve, Math.pow(2, attempt) * 1000));
|
|
485
|
-
}
|
|
486
|
-
}
|
|
487
|
-
throw new Error('Failed to embed text after retries');
|
|
488
|
-
});
|
|
489
|
-
}
|
|
490
|
-
/**
|
|
491
|
-
* Get cache statistics
|
|
492
|
-
*/
|
|
493
|
-
export function getCacheStats(cacheDir = '.codehere-cache') {
|
|
494
|
-
if (!existsSync(cacheDir)) {
|
|
495
|
-
return {
|
|
496
|
-
totalFiles: 0,
|
|
497
|
-
totalChunks: 0,
|
|
498
|
-
totalSize: 0,
|
|
499
|
-
oldestEntry: 0,
|
|
500
|
-
newestEntry: 0,
|
|
501
|
-
};
|
|
502
|
-
}
|
|
503
|
-
const files = readdirSync(cacheDir).filter(f => f.endsWith('.json'));
|
|
504
|
-
let totalChunks = 0;
|
|
505
|
-
let totalSize = 0;
|
|
506
|
-
let oldestEntry = Date.now();
|
|
507
|
-
let newestEntry = 0;
|
|
508
|
-
for (const file of files) {
|
|
509
|
-
try {
|
|
510
|
-
const cachePath = join(cacheDir, file);
|
|
511
|
-
const stat = statSync(cachePath);
|
|
512
|
-
totalSize += stat.size;
|
|
513
|
-
const cacheEntry = JSON.parse(readFileSync(cachePath, 'utf-8'));
|
|
514
|
-
totalChunks += cacheEntry.chunks.length;
|
|
515
|
-
if (cacheEntry.timestamp < oldestEntry) {
|
|
516
|
-
oldestEntry = cacheEntry.timestamp;
|
|
517
|
-
}
|
|
518
|
-
if (cacheEntry.timestamp > newestEntry) {
|
|
519
|
-
newestEntry = cacheEntry.timestamp;
|
|
520
|
-
}
|
|
521
|
-
}
|
|
522
|
-
catch {
|
|
523
|
-
// Skip invalid cache entries
|
|
524
|
-
}
|
|
525
|
-
}
|
|
526
|
-
return {
|
|
527
|
-
totalFiles: files.length,
|
|
528
|
-
totalChunks,
|
|
529
|
-
totalSize,
|
|
530
|
-
oldestEntry,
|
|
531
|
-
newestEntry,
|
|
532
|
-
};
|
|
533
|
-
}
|
|
534
|
-
//# sourceMappingURL=parallel-processor.js.map
|