@sylphx/flow 1.0.1 → 1.0.3
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/CHANGELOG.md +12 -0
- package/package.json +10 -9
- package/src/commands/codebase-command.ts +168 -0
- package/src/commands/flow-command.ts +1137 -0
- package/src/commands/flow-orchestrator.ts +296 -0
- package/src/commands/hook-command.ts +444 -0
- package/src/commands/init-command.ts +92 -0
- package/src/commands/init-core.ts +322 -0
- package/src/commands/knowledge-command.ts +161 -0
- package/src/commands/run-command.ts +120 -0
- package/src/components/benchmark-monitor.tsx +331 -0
- package/src/components/reindex-progress.tsx +261 -0
- package/src/composables/functional/index.ts +14 -0
- package/src/composables/functional/useEnvironment.ts +171 -0
- package/src/composables/functional/useFileSystem.ts +139 -0
- package/src/composables/index.ts +5 -0
- package/src/composables/useEnv.ts +13 -0
- package/src/composables/useRuntimeConfig.ts +27 -0
- package/src/composables/useTargetConfig.ts +45 -0
- package/src/config/ai-config.ts +376 -0
- package/src/config/constants.ts +35 -0
- package/src/config/index.ts +27 -0
- package/src/config/rules.ts +43 -0
- package/src/config/servers.ts +371 -0
- package/src/config/targets.ts +126 -0
- package/src/core/agent-loader.ts +141 -0
- package/src/core/agent-manager.ts +174 -0
- package/src/core/ai-sdk.ts +603 -0
- package/src/core/app-factory.ts +381 -0
- package/src/core/builtin-agents.ts +9 -0
- package/src/core/command-system.ts +550 -0
- package/src/core/config-system.ts +550 -0
- package/src/core/connection-pool.ts +390 -0
- package/src/core/di-container.ts +155 -0
- package/src/core/error-handling.ts +519 -0
- package/src/core/formatting/bytes.test.ts +115 -0
- package/src/core/formatting/bytes.ts +64 -0
- package/src/core/functional/async.ts +313 -0
- package/src/core/functional/either.ts +109 -0
- package/src/core/functional/error-handler.ts +135 -0
- package/src/core/functional/error-types.ts +311 -0
- package/src/core/functional/index.ts +19 -0
- package/src/core/functional/option.ts +142 -0
- package/src/core/functional/pipe.ts +189 -0
- package/src/core/functional/result.ts +204 -0
- package/src/core/functional/validation.ts +138 -0
- package/src/core/headless-display.ts +96 -0
- package/src/core/index.ts +6 -0
- package/src/core/installers/file-installer.ts +303 -0
- package/src/core/installers/mcp-installer.ts +213 -0
- package/src/core/interfaces/index.ts +22 -0
- package/src/core/interfaces/repository.interface.ts +91 -0
- package/src/core/interfaces/service.interface.ts +133 -0
- package/src/core/interfaces.ts +129 -0
- package/src/core/loop-controller.ts +200 -0
- package/src/core/result.ts +351 -0
- package/src/core/rule-loader.ts +147 -0
- package/src/core/rule-manager.ts +240 -0
- package/src/core/service-config.ts +252 -0
- package/src/core/session-service.ts +121 -0
- package/src/core/state-detector.ts +389 -0
- package/src/core/storage-factory.ts +115 -0
- package/src/core/stream-handler.ts +288 -0
- package/src/core/target-manager.ts +161 -0
- package/src/core/type-utils.ts +427 -0
- package/src/core/unified-storage.ts +456 -0
- package/src/core/upgrade-manager.ts +300 -0
- package/src/core/validation/limit.test.ts +155 -0
- package/src/core/validation/limit.ts +46 -0
- package/src/core/validation/query.test.ts +44 -0
- package/src/core/validation/query.ts +20 -0
- package/src/db/auto-migrate.ts +322 -0
- package/src/db/base-database-client.ts +144 -0
- package/src/db/cache-db.ts +218 -0
- package/src/db/cache-schema.ts +75 -0
- package/src/db/database.ts +70 -0
- package/src/db/index.ts +252 -0
- package/src/db/memory-db.ts +153 -0
- package/src/db/memory-schema.ts +29 -0
- package/src/db/schema.ts +289 -0
- package/src/db/session-repository.ts +733 -0
- package/src/domains/codebase/index.ts +5 -0
- package/src/domains/codebase/tools.ts +139 -0
- package/src/domains/index.ts +8 -0
- package/src/domains/knowledge/index.ts +10 -0
- package/src/domains/knowledge/resources.ts +537 -0
- package/src/domains/knowledge/tools.ts +174 -0
- package/src/domains/utilities/index.ts +6 -0
- package/src/domains/utilities/time/index.ts +5 -0
- package/src/domains/utilities/time/tools.ts +291 -0
- package/src/index.ts +211 -0
- package/src/services/agent-service.ts +273 -0
- package/src/services/claude-config-service.ts +252 -0
- package/src/services/config-service.ts +258 -0
- package/src/services/evaluation-service.ts +271 -0
- package/src/services/functional/evaluation-logic.ts +296 -0
- package/src/services/functional/file-processor.ts +273 -0
- package/src/services/functional/index.ts +12 -0
- package/src/services/index.ts +13 -0
- package/src/services/mcp-service.ts +432 -0
- package/src/services/memory.service.ts +476 -0
- package/src/services/search/base-indexer.ts +156 -0
- package/src/services/search/codebase-indexer-types.ts +38 -0
- package/src/services/search/codebase-indexer.ts +647 -0
- package/src/services/search/embeddings-provider.ts +455 -0
- package/src/services/search/embeddings.ts +316 -0
- package/src/services/search/functional-indexer.ts +323 -0
- package/src/services/search/index.ts +27 -0
- package/src/services/search/indexer.ts +380 -0
- package/src/services/search/knowledge-indexer.ts +422 -0
- package/src/services/search/semantic-search.ts +244 -0
- package/src/services/search/tfidf.ts +559 -0
- package/src/services/search/unified-search-service.ts +888 -0
- package/src/services/smart-config-service.ts +385 -0
- package/src/services/storage/cache-storage.ts +487 -0
- package/src/services/storage/drizzle-storage.ts +581 -0
- package/src/services/storage/index.ts +15 -0
- package/src/services/storage/lancedb-vector-storage.ts +494 -0
- package/src/services/storage/memory-storage.ts +268 -0
- package/src/services/storage/separated-storage.ts +467 -0
- package/src/services/storage/vector-storage.ts +13 -0
- package/src/shared/agents/index.ts +63 -0
- package/src/shared/files/index.ts +99 -0
- package/src/shared/index.ts +32 -0
- package/src/shared/logging/index.ts +24 -0
- package/src/shared/processing/index.ts +153 -0
- package/src/shared/types/index.ts +25 -0
- package/src/targets/claude-code.ts +574 -0
- package/src/targets/functional/claude-code-logic.ts +185 -0
- package/src/targets/functional/index.ts +6 -0
- package/src/targets/opencode.ts +529 -0
- package/src/types/agent.types.ts +32 -0
- package/src/types/api/batch.ts +108 -0
- package/src/types/api/errors.ts +118 -0
- package/src/types/api/index.ts +55 -0
- package/src/types/api/requests.ts +76 -0
- package/src/types/api/responses.ts +180 -0
- package/src/types/api/websockets.ts +85 -0
- package/src/types/api.types.ts +9 -0
- package/src/types/benchmark.ts +49 -0
- package/src/types/cli.types.ts +87 -0
- package/src/types/common.types.ts +35 -0
- package/src/types/database.types.ts +510 -0
- package/src/types/mcp-config.types.ts +448 -0
- package/src/types/mcp.types.ts +69 -0
- package/src/types/memory-types.ts +63 -0
- package/src/types/provider.types.ts +28 -0
- package/src/types/rule.types.ts +24 -0
- package/src/types/session.types.ts +214 -0
- package/src/types/target-config.types.ts +295 -0
- package/src/types/target.types.ts +140 -0
- package/src/types/todo.types.ts +25 -0
- package/src/types.ts +40 -0
- package/src/utils/advanced-tokenizer.ts +191 -0
- package/src/utils/agent-enhancer.ts +114 -0
- package/src/utils/ai-model-fetcher.ts +19 -0
- package/src/utils/async-file-operations.ts +516 -0
- package/src/utils/audio-player.ts +345 -0
- package/src/utils/cli-output.ts +266 -0
- package/src/utils/codebase-helpers.ts +211 -0
- package/src/utils/console-ui.ts +79 -0
- package/src/utils/database-errors.ts +140 -0
- package/src/utils/debug-logger.ts +49 -0
- package/src/utils/error-handler.ts +53 -0
- package/src/utils/file-operations.ts +310 -0
- package/src/utils/file-scanner.ts +259 -0
- package/src/utils/functional/array.ts +355 -0
- package/src/utils/functional/index.ts +15 -0
- package/src/utils/functional/object.ts +279 -0
- package/src/utils/functional/string.ts +281 -0
- package/src/utils/functional.ts +543 -0
- package/src/utils/help.ts +20 -0
- package/src/utils/immutable-cache.ts +106 -0
- package/src/utils/index.ts +78 -0
- package/src/utils/jsonc.ts +158 -0
- package/src/utils/logger.ts +396 -0
- package/src/utils/mcp-config.ts +249 -0
- package/src/utils/memory-tui.ts +414 -0
- package/src/utils/models-dev.ts +91 -0
- package/src/utils/notifications.ts +169 -0
- package/src/utils/object-utils.ts +51 -0
- package/src/utils/parallel-operations.ts +487 -0
- package/src/utils/paths.ts +143 -0
- package/src/utils/process-manager.ts +155 -0
- package/src/utils/prompts.ts +120 -0
- package/src/utils/search-tool-builder.ts +214 -0
- package/src/utils/secret-utils.ts +179 -0
- package/src/utils/security.ts +537 -0
- package/src/utils/session-manager.ts +168 -0
- package/src/utils/session-title.ts +87 -0
- package/src/utils/settings.ts +182 -0
- package/src/utils/simplified-errors.ts +410 -0
- package/src/utils/sync-utils.ts +159 -0
- package/src/utils/target-config.ts +570 -0
- package/src/utils/target-utils.ts +394 -0
- package/src/utils/template-engine.ts +94 -0
- package/src/utils/test-audio.ts +71 -0
- package/src/utils/todo-context.ts +46 -0
- package/src/utils/token-counter.ts +288 -0
- package/dist/index.d.ts +0 -10
- package/dist/index.js +0 -59554
- package/dist/lancedb.linux-x64-gnu-b7f0jgsz.node +0 -0
- package/dist/lancedb.linux-x64-musl-tgcv22rx.node +0 -0
- package/dist/shared/chunk-25dwp0dp.js +0 -89
- package/dist/shared/chunk-3pjb6063.js +0 -208
- package/dist/shared/chunk-4d6ydpw7.js +0 -2854
- package/dist/shared/chunk-4wjcadjk.js +0 -225
- package/dist/shared/chunk-5j4w74t6.js +0 -30
- package/dist/shared/chunk-5j8m3dh3.js +0 -58
- package/dist/shared/chunk-5thh3qem.js +0 -91
- package/dist/shared/chunk-6g9xy73m.js +0 -252
- package/dist/shared/chunk-7eq34c42.js +0 -23
- package/dist/shared/chunk-c2gwgx3r.js +0 -115
- package/dist/shared/chunk-cjd3mk4c.js +0 -1320
- package/dist/shared/chunk-g5cv6703.js +0 -368
- package/dist/shared/chunk-hpkhykhq.js +0 -574
- package/dist/shared/chunk-m2322pdk.js +0 -122
- package/dist/shared/chunk-nd5fdvaq.js +0 -26
- package/dist/shared/chunk-pgd3m6zf.js +0 -108
- package/dist/shared/chunk-qk8n91hw.js +0 -494
- package/dist/shared/chunk-rkkn8szp.js +0 -16855
- package/dist/shared/chunk-t16rfxh0.js +0 -61
- package/dist/shared/chunk-t4fbfa5v.js +0 -19
- package/dist/shared/chunk-t77h86w6.js +0 -276
- package/dist/shared/chunk-v0ez4aef.js +0 -71
- package/dist/shared/chunk-v29j2r3s.js +0 -32051
- package/dist/shared/chunk-vfbc6ew5.js +0 -765
- package/dist/shared/chunk-vmeqwm1c.js +0 -204
- package/dist/shared/chunk-x66eh37x.js +0 -137
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rule Manager
|
|
3
|
+
* Manages rule state and operations
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { Rule } from '../types/rule.types.js';
|
|
7
|
+
import { loadAllRules } from './rule-loader.js';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Rule manager state
|
|
11
|
+
*/
|
|
12
|
+
interface RuleManagerState {
|
|
13
|
+
rules: Map<string, Rule>;
|
|
14
|
+
cwd: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
let state: RuleManagerState | null = null;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Get the app store (lazy import to avoid circular dependencies)
|
|
21
|
+
*/
|
|
22
|
+
let getAppStore: (() => any) | null = null;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Set the app store getter (called during initialization)
|
|
26
|
+
*/
|
|
27
|
+
export function setRuleAppStoreGetter(getter: () => any): void {
|
|
28
|
+
getAppStore = getter;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Initialize rule manager
|
|
33
|
+
*/
|
|
34
|
+
export async function initializeRuleManager(cwd: string): Promise<void> {
|
|
35
|
+
const allRules = await loadAllRules(cwd);
|
|
36
|
+
|
|
37
|
+
const ruleMap = new Map<string, Rule>();
|
|
38
|
+
for (const rule of allRules) {
|
|
39
|
+
ruleMap.set(rule.id, rule);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
state = {
|
|
43
|
+
rules: ruleMap,
|
|
44
|
+
cwd,
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
// Initialize store with default enabled rules
|
|
48
|
+
if (getAppStore) {
|
|
49
|
+
const store = getAppStore();
|
|
50
|
+
if (store.getState) {
|
|
51
|
+
const currentEnabledRules = store.getState().enabledRuleIds || [];
|
|
52
|
+
|
|
53
|
+
// If no rules are enabled yet, enable all rules that have enabled: true in metadata
|
|
54
|
+
if (currentEnabledRules.length === 0) {
|
|
55
|
+
const defaultEnabledRules = allRules
|
|
56
|
+
.filter((rule) => rule.metadata.enabled !== false)
|
|
57
|
+
.map((rule) => rule.id);
|
|
58
|
+
|
|
59
|
+
if (defaultEnabledRules.length > 0) {
|
|
60
|
+
store.getState().setEnabledRuleIds(defaultEnabledRules);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Get all available rules
|
|
69
|
+
*/
|
|
70
|
+
export function getAllRules(): Rule[] {
|
|
71
|
+
if (!state) {
|
|
72
|
+
return [];
|
|
73
|
+
}
|
|
74
|
+
return Array.from(state.rules.values());
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Get rule by ID
|
|
79
|
+
*/
|
|
80
|
+
export function getRuleById(id: string): Rule | null {
|
|
81
|
+
if (!state) {
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
return state.rules.get(id) || null;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Get enabled rule IDs from store
|
|
89
|
+
*/
|
|
90
|
+
export function getEnabledRuleIds(): string[] {
|
|
91
|
+
if (getAppStore) {
|
|
92
|
+
const store = getAppStore();
|
|
93
|
+
if (store.getState) {
|
|
94
|
+
return store.getState().enabledRuleIds || [];
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return [];
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Get enabled rules
|
|
102
|
+
*/
|
|
103
|
+
export function getEnabledRules(): Rule[] {
|
|
104
|
+
if (!state) {
|
|
105
|
+
return [];
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const enabledIds = getEnabledRuleIds();
|
|
109
|
+
return enabledIds
|
|
110
|
+
.map((id) => state!.rules.get(id))
|
|
111
|
+
.filter((rule): rule is Rule => rule !== null);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Toggle a rule on/off
|
|
116
|
+
*/
|
|
117
|
+
export function toggleRule(ruleId: string): boolean {
|
|
118
|
+
if (!state || !state.rules.has(ruleId)) {
|
|
119
|
+
return false;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (getAppStore) {
|
|
123
|
+
const store = getAppStore();
|
|
124
|
+
if (store.getState) {
|
|
125
|
+
const currentEnabled = store.getState().enabledRuleIds || [];
|
|
126
|
+
|
|
127
|
+
if (currentEnabled.includes(ruleId)) {
|
|
128
|
+
// Disable: remove from list
|
|
129
|
+
store.getState().setEnabledRuleIds(currentEnabled.filter((id) => id !== ruleId));
|
|
130
|
+
} else {
|
|
131
|
+
// Enable: add to list
|
|
132
|
+
store.getState().setEnabledRuleIds([...currentEnabled, ruleId]);
|
|
133
|
+
}
|
|
134
|
+
return true;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return false;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Enable a rule
|
|
143
|
+
*/
|
|
144
|
+
export function enableRule(ruleId: string): boolean {
|
|
145
|
+
if (!state || !state.rules.has(ruleId)) {
|
|
146
|
+
return false;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
if (getAppStore) {
|
|
150
|
+
const store = getAppStore();
|
|
151
|
+
if (store.getState) {
|
|
152
|
+
const currentEnabled = store.getState().enabledRuleIds || [];
|
|
153
|
+
|
|
154
|
+
if (!currentEnabled.includes(ruleId)) {
|
|
155
|
+
store.getState().setEnabledRuleIds([...currentEnabled, ruleId]);
|
|
156
|
+
}
|
|
157
|
+
return true;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
return false;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Disable a rule
|
|
166
|
+
*/
|
|
167
|
+
export function disableRule(ruleId: string): boolean {
|
|
168
|
+
if (!state || !state.rules.has(ruleId)) {
|
|
169
|
+
return false;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
if (getAppStore) {
|
|
173
|
+
const store = getAppStore();
|
|
174
|
+
if (store.getState) {
|
|
175
|
+
const currentEnabled = store.getState().enabledRuleIds || [];
|
|
176
|
+
store.getState().setEnabledRuleIds(currentEnabled.filter((id) => id !== ruleId));
|
|
177
|
+
return true;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
return false;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Reload rules from disk
|
|
186
|
+
*/
|
|
187
|
+
export async function reloadRules(): Promise<void> {
|
|
188
|
+
if (!state) {
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
const cwd = state.cwd;
|
|
193
|
+
const currentEnabled = getEnabledRuleIds();
|
|
194
|
+
|
|
195
|
+
await initializeRuleManager(cwd);
|
|
196
|
+
|
|
197
|
+
// Keep only enabled rules that still exist
|
|
198
|
+
if (state && getAppStore) {
|
|
199
|
+
const store = getAppStore();
|
|
200
|
+
if (store.getState) {
|
|
201
|
+
const validEnabled = currentEnabled.filter((id) => state!.rules.has(id));
|
|
202
|
+
store.getState().setEnabledRuleIds(validEnabled);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Set enabled rules (replaces current enabled rules)
|
|
209
|
+
*/
|
|
210
|
+
export function setEnabledRules(ruleIds: string[]): boolean {
|
|
211
|
+
if (!state) {
|
|
212
|
+
return false;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// Validate all rule IDs exist
|
|
216
|
+
const validRuleIds = ruleIds.filter((id) => state!.rules.has(id));
|
|
217
|
+
|
|
218
|
+
if (getAppStore) {
|
|
219
|
+
const store = getAppStore();
|
|
220
|
+
if (store.getState) {
|
|
221
|
+
store.getState().setEnabledRuleIds(validRuleIds);
|
|
222
|
+
return true;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
return false;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Get combined content of all enabled rules
|
|
231
|
+
*/
|
|
232
|
+
export function getEnabledRulesContent(): string {
|
|
233
|
+
const enabledRules = getEnabledRules();
|
|
234
|
+
|
|
235
|
+
if (enabledRules.length === 0) {
|
|
236
|
+
return '';
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
return enabledRules.map((rule) => rule.content).join('\n\n');
|
|
240
|
+
}
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Service configuration and registration for DI container
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { targetManager } from '../core/target-manager.js';
|
|
6
|
+
import { MemoryDatabaseClient } from '../db/memory-db.js';
|
|
7
|
+
import { createMCPService } from '../services/mcp-service.js';
|
|
8
|
+
import { getDefaultEmbeddingProvider } from '../services/search/embeddings.js';
|
|
9
|
+
import { getSearchService } from '../services/search/unified-search-service.js';
|
|
10
|
+
import { createLogger as createRealLogger } from '../utils/logger.js';
|
|
11
|
+
// Import concrete implementations (will be updated as we refactor)
|
|
12
|
+
import { SeparatedMemoryStorage } from '../utils/separated-storage.js';
|
|
13
|
+
import { container, SERVICE_TOKENS } from './di-container.js';
|
|
14
|
+
import type {
|
|
15
|
+
IConfiguration,
|
|
16
|
+
IDatabaseConnection,
|
|
17
|
+
IEmbeddingProvider,
|
|
18
|
+
ILogger,
|
|
19
|
+
IMCPService,
|
|
20
|
+
ISearchService,
|
|
21
|
+
IStorage,
|
|
22
|
+
ITargetManager,
|
|
23
|
+
} from './interfaces.js';
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Configure and register all core services with the DI container
|
|
27
|
+
*/
|
|
28
|
+
export async function configureServices(): Promise<void> {
|
|
29
|
+
// Logger Service - Singleton
|
|
30
|
+
container.register<ILogger>(SERVICE_TOKENS.LOGGER, () => createLogger(), 'singleton');
|
|
31
|
+
|
|
32
|
+
// Configuration Service - Singleton
|
|
33
|
+
container.register<IConfiguration>(
|
|
34
|
+
SERVICE_TOKENS.CONFIG,
|
|
35
|
+
() => createConfiguration(),
|
|
36
|
+
'singleton'
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
// Database Connection - Singleton
|
|
40
|
+
container.register<IDatabaseConnection>(
|
|
41
|
+
SERVICE_TOKENS.DATABASE,
|
|
42
|
+
async () => {
|
|
43
|
+
const dbClient = new MemoryDatabaseClient();
|
|
44
|
+
await dbClient.initialize();
|
|
45
|
+
return dbClient;
|
|
46
|
+
},
|
|
47
|
+
'singleton'
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
// Memory Storage - Singleton
|
|
51
|
+
container.register<IStorage>(
|
|
52
|
+
SERVICE_TOKENS.MEMORY_STORAGE,
|
|
53
|
+
async () => {
|
|
54
|
+
const storage = new SeparatedMemoryStorage();
|
|
55
|
+
await storage.initialize();
|
|
56
|
+
return storage;
|
|
57
|
+
},
|
|
58
|
+
'singleton'
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
// Search Service - Singleton
|
|
62
|
+
container.register<ISearchService>(
|
|
63
|
+
SERVICE_TOKENS.SEARCH_SERVICE,
|
|
64
|
+
async () => {
|
|
65
|
+
const searchService = getSearchService();
|
|
66
|
+
await searchService.initialize();
|
|
67
|
+
return searchService;
|
|
68
|
+
},
|
|
69
|
+
'singleton'
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
// Target Manager - Singleton
|
|
73
|
+
container.register<ITargetManager>(
|
|
74
|
+
SERVICE_TOKENS.TARGET_MANAGER,
|
|
75
|
+
() => targetManager,
|
|
76
|
+
'singleton'
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
// Embedding Provider - Singleton (lazy initialization)
|
|
80
|
+
container.register<IEmbeddingProvider>(
|
|
81
|
+
SERVICE_TOKENS.EMBEDDING_PROVIDER,
|
|
82
|
+
async () => {
|
|
83
|
+
try {
|
|
84
|
+
return await getDefaultEmbeddingProvider();
|
|
85
|
+
} catch (_error) {
|
|
86
|
+
// Return a fallback provider that doesn't require external dependencies
|
|
87
|
+
return createFallbackEmbeddingProvider();
|
|
88
|
+
}
|
|
89
|
+
},
|
|
90
|
+
'singleton'
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
// MCP Service - Transient (since it depends on target)
|
|
94
|
+
container.register<IMCPService>(
|
|
95
|
+
SERVICE_TOKENS.MCP_SERVICE,
|
|
96
|
+
(targetId: string) => {
|
|
97
|
+
const targetOption = targetManager.getTarget(targetId);
|
|
98
|
+
if (targetOption._tag === 'None') {
|
|
99
|
+
throw new Error(`Target not found: ${targetId}`);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const target = targetOption.value;
|
|
103
|
+
return createMCPService({ target });
|
|
104
|
+
},
|
|
105
|
+
'transient'
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Create a logger instance that adapts our Logger to the ILogger interface
|
|
111
|
+
*/
|
|
112
|
+
function createLogger(): ILogger {
|
|
113
|
+
const logger = createRealLogger();
|
|
114
|
+
|
|
115
|
+
return {
|
|
116
|
+
info(message: string, ...args: any[]): void {
|
|
117
|
+
// Merge args into context for structured logging
|
|
118
|
+
const context = args.length > 0 ? { args } : undefined;
|
|
119
|
+
logger.info(message, context);
|
|
120
|
+
},
|
|
121
|
+
|
|
122
|
+
warn(message: string, ...args: any[]): void {
|
|
123
|
+
const context = args.length > 0 ? { args } : undefined;
|
|
124
|
+
logger.warn(message, context);
|
|
125
|
+
},
|
|
126
|
+
|
|
127
|
+
error(message: string, error?: Error | unknown, ...args: any[]): void {
|
|
128
|
+
const context = args.length > 0 ? { args } : undefined;
|
|
129
|
+
if (error instanceof Error) {
|
|
130
|
+
logger.error(message, error, context);
|
|
131
|
+
} else {
|
|
132
|
+
logger.error(message, undefined, { ...context, error });
|
|
133
|
+
}
|
|
134
|
+
},
|
|
135
|
+
|
|
136
|
+
debug(message: string, ...args: any[]): void {
|
|
137
|
+
const context = args.length > 0 ? { args } : undefined;
|
|
138
|
+
logger.debug(message, context);
|
|
139
|
+
},
|
|
140
|
+
|
|
141
|
+
success(message: string, ...args: any[]): void {
|
|
142
|
+
// Success is just info with different styling
|
|
143
|
+
const context = args.length > 0 ? { args, level: 'success' } : { level: 'success' };
|
|
144
|
+
logger.info(message, context);
|
|
145
|
+
},
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Create a configuration service
|
|
151
|
+
*/
|
|
152
|
+
function createConfiguration(): IConfiguration {
|
|
153
|
+
const config = new Map<string, any>();
|
|
154
|
+
|
|
155
|
+
// Load environment variables
|
|
156
|
+
const loadEnvConfig = () => {
|
|
157
|
+
config.set('env', process.env.NODE_ENV || 'development');
|
|
158
|
+
config.set('debug', process.env.DEBUG === 'true');
|
|
159
|
+
config.set('logLevel', process.env.LOG_LEVEL || 'info');
|
|
160
|
+
config.set('databasePath', process.env.DATABASE_PATH || '.sylphx-flow/memory.db');
|
|
161
|
+
config.set('embeddings.provider', process.env.EMBEDDINGS_PROVIDER || 'local');
|
|
162
|
+
config.set(
|
|
163
|
+
'embeddings.dimension',
|
|
164
|
+
Number.parseInt(process.env.EMBEDDINGS_DIMENSION || '384', 10)
|
|
165
|
+
);
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
loadEnvConfig();
|
|
169
|
+
|
|
170
|
+
return {
|
|
171
|
+
get<T = any>(key: string, defaultValue?: T): T {
|
|
172
|
+
return config.get(key) ?? defaultValue;
|
|
173
|
+
},
|
|
174
|
+
|
|
175
|
+
getRequired<T = any>(key: string): T {
|
|
176
|
+
const value = config.get(key);
|
|
177
|
+
if (value === undefined) {
|
|
178
|
+
throw new Error(`Required configuration key missing: ${key}`);
|
|
179
|
+
}
|
|
180
|
+
return value;
|
|
181
|
+
},
|
|
182
|
+
|
|
183
|
+
has(key: string): boolean {
|
|
184
|
+
return config.has(key);
|
|
185
|
+
},
|
|
186
|
+
|
|
187
|
+
set(key: string, value: any): void {
|
|
188
|
+
config.set(key, value);
|
|
189
|
+
},
|
|
190
|
+
|
|
191
|
+
async reload(): Promise<void> {
|
|
192
|
+
config.clear();
|
|
193
|
+
loadEnvConfig();
|
|
194
|
+
},
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Create a fallback embedding provider for when external providers are unavailable
|
|
200
|
+
*/
|
|
201
|
+
function createFallbackEmbeddingProvider(): IEmbeddingProvider {
|
|
202
|
+
return {
|
|
203
|
+
name: 'fallback-tfidf',
|
|
204
|
+
async embed(text: string): Promise<number[]> {
|
|
205
|
+
// Simple TF-IDF like fallback - just return a hash-based vector
|
|
206
|
+
const words = text.toLowerCase().split(/\s+/);
|
|
207
|
+
const vector = new Array(384).fill(0);
|
|
208
|
+
|
|
209
|
+
// Create a simple hash-based embedding
|
|
210
|
+
words.forEach((word, index) => {
|
|
211
|
+
const hash = simpleHash(word);
|
|
212
|
+
const index1 = hash % 384;
|
|
213
|
+
const index2 = (hash * 2) % 384;
|
|
214
|
+
vector[index1] = (index + 1) / words.length;
|
|
215
|
+
vector[index2] = (index + 1) / (words.length * 2);
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
return vector;
|
|
219
|
+
},
|
|
220
|
+
|
|
221
|
+
async isAvailable(): Promise<boolean> {
|
|
222
|
+
return true; // Always available as fallback
|
|
223
|
+
},
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Simple hash function for fallback embeddings
|
|
229
|
+
*/
|
|
230
|
+
function simpleHash(str: string): number {
|
|
231
|
+
let hash = 0;
|
|
232
|
+
for (let i = 0; i < str.length; i++) {
|
|
233
|
+
const char = str.charCodeAt(i);
|
|
234
|
+
hash = (hash << 5) - hash + char;
|
|
235
|
+
hash &= hash; // Convert to 32-bit integer
|
|
236
|
+
}
|
|
237
|
+
return Math.abs(hash);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Get a service from the container (convenience method)
|
|
242
|
+
*/
|
|
243
|
+
export async function getService<T>(token: string): Promise<T> {
|
|
244
|
+
return await container.resolve<T>(token);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* Check if a service is registered (convenience method)
|
|
249
|
+
*/
|
|
250
|
+
export function hasService(token: string): boolean {
|
|
251
|
+
return container.isRegistered(token);
|
|
252
|
+
}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Session Service
|
|
3
|
+
* Centralized session management for headless mode
|
|
4
|
+
* Uses database for persistence
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import chalk from 'chalk';
|
|
8
|
+
import { loadAIConfig, getConfiguredProviders } from '../config/ai-config.js';
|
|
9
|
+
import type { ProviderId, ProviderConfig } from '../config/ai-config.js';
|
|
10
|
+
import type { Session } from '../types/session.types.js';
|
|
11
|
+
import { getProvider } from '../providers/index.js';
|
|
12
|
+
import { fetchModels } from '../utils/ai-model-fetcher.js';
|
|
13
|
+
import { getSessionRepository } from '../db/database.js';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Get default model for a provider
|
|
17
|
+
* Priority: config default-model > first available model
|
|
18
|
+
*/
|
|
19
|
+
export async function getDefaultModel(providerId: ProviderId, providerConfig: ProviderConfig): Promise<string | null> {
|
|
20
|
+
// Try config first
|
|
21
|
+
const configModel = providerConfig['default-model'] as string | undefined;
|
|
22
|
+
if (configModel) {
|
|
23
|
+
return configModel;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Fetch first available model
|
|
27
|
+
try {
|
|
28
|
+
const models = await fetchModels(providerId, providerConfig);
|
|
29
|
+
return models[0]?.id || null;
|
|
30
|
+
} catch {
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Get or create session for headless mode
|
|
37
|
+
*/
|
|
38
|
+
export async function getOrCreateSession(continueSession: boolean): Promise<Session | null> {
|
|
39
|
+
const cwd = process.cwd();
|
|
40
|
+
const configResult = await loadAIConfig(cwd);
|
|
41
|
+
|
|
42
|
+
if (configResult._tag === 'Failure') {
|
|
43
|
+
console.error(chalk.red('✗ Failed to load AI config'));
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const config = configResult.value;
|
|
48
|
+
const configuredProviders = await getConfiguredProviders(cwd);
|
|
49
|
+
|
|
50
|
+
if (configuredProviders.length === 0) {
|
|
51
|
+
console.error(chalk.yellow('\n⚠️ No AI provider configured\n'));
|
|
52
|
+
console.error(chalk.dim('Run: sylphx code (to configure AI)\n'));
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const providerId = config.defaultProvider ?? configuredProviders[0];
|
|
57
|
+
if (!providerId) {
|
|
58
|
+
console.error(chalk.yellow('\n⚠️ No provider configured\n'));
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const providerConfig = config.providers?.[providerId];
|
|
63
|
+
if (!providerConfig) {
|
|
64
|
+
console.error(chalk.yellow('\n⚠️ Provider not configured\n'));
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Check if provider is properly configured
|
|
69
|
+
const provider = getProvider(providerId);
|
|
70
|
+
if (!provider.isConfigured(providerConfig)) {
|
|
71
|
+
console.error(chalk.yellow(`\n⚠️ ${provider.name} is not properly configured\n`));
|
|
72
|
+
console.error(chalk.dim('Run: sylphx code (to configure AI)\n'));
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Get session repository
|
|
77
|
+
const repository = await getSessionRepository();
|
|
78
|
+
|
|
79
|
+
// Try to continue last session
|
|
80
|
+
if (continueSession) {
|
|
81
|
+
const lastSession = await repository.getLastSession();
|
|
82
|
+
if (lastSession) {
|
|
83
|
+
console.error(chalk.dim(`Continuing session: ${lastSession.id}`));
|
|
84
|
+
console.error(chalk.dim(`Messages: ${lastSession.messages.length}\n`));
|
|
85
|
+
return lastSession;
|
|
86
|
+
}
|
|
87
|
+
console.error(chalk.yellow('No previous session found, creating new one\n'));
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Get default model (last used or first available)
|
|
91
|
+
const modelName = await getDefaultModel(providerId, providerConfig);
|
|
92
|
+
if (!modelName) {
|
|
93
|
+
console.error(chalk.yellow('\n⚠️ No models available for this provider\n'));
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Create new session in database
|
|
98
|
+
return await repository.createSession(providerId, modelName);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Show error message for models without tool support
|
|
103
|
+
*/
|
|
104
|
+
export function showModelToolSupportError(): void {
|
|
105
|
+
console.error(chalk.red('\n✗ No text response received from model\n'));
|
|
106
|
+
console.error(
|
|
107
|
+
chalk.yellow('The model may have called tools but did not generate a final text response.')
|
|
108
|
+
);
|
|
109
|
+
console.error(
|
|
110
|
+
chalk.yellow('This usually means:\n')
|
|
111
|
+
);
|
|
112
|
+
console.error(chalk.dim(' • The current model does not fully support multi-step tool calling'));
|
|
113
|
+
console.error(chalk.dim(' • Some models can call tools but cannot process results and respond\n'));
|
|
114
|
+
console.error(chalk.green('Recommended models with full tool support:'));
|
|
115
|
+
console.error(chalk.green(' • anthropic/claude-3.5-sonnet'));
|
|
116
|
+
console.error(chalk.green(' • anthropic/claude-3.5-haiku'));
|
|
117
|
+
console.error(chalk.green(' • openai/gpt-4o'));
|
|
118
|
+
console.error(chalk.green(' • google/gemini-2.0-flash-exp\n'));
|
|
119
|
+
console.error(chalk.dim('💡 Tip: Ask questions that don\'t require tools, or switch to a model above'));
|
|
120
|
+
console.error(chalk.dim('To configure: Run `sylphx code` (TUI mode) then type /provider\n'));
|
|
121
|
+
}
|