@sylphx/flow 1.1.1 → 1.2.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/CHANGELOG.md +14 -0
- package/package.json +1 -1
- package/src/commands/hook-command.ts +10 -230
- package/src/composables/index.ts +0 -1
- package/src/config/servers.ts +35 -78
- package/src/core/interfaces.ts +0 -33
- package/src/domains/index.ts +0 -2
- package/src/index.ts +0 -4
- package/src/services/mcp-service.ts +0 -16
- package/src/targets/claude-code.ts +3 -9
- package/src/targets/functional/claude-code-logic.ts +4 -22
- package/src/targets/opencode.ts +0 -6
- package/src/types/mcp.types.ts +29 -38
- package/src/types/target.types.ts +0 -2
- package/src/types.ts +0 -1
- package/src/commands/codebase-command.ts +0 -168
- package/src/commands/knowledge-command.ts +0 -161
- package/src/composables/useTargetConfig.ts +0 -45
- package/src/core/formatting/bytes.test.ts +0 -115
- package/src/core/validation/limit.test.ts +0 -155
- package/src/core/validation/query.test.ts +0 -44
- package/src/domains/codebase/index.ts +0 -5
- package/src/domains/codebase/tools.ts +0 -139
- package/src/domains/knowledge/index.ts +0 -10
- package/src/domains/knowledge/resources.ts +0 -537
- package/src/domains/knowledge/tools.ts +0 -174
- package/src/services/search/base-indexer.ts +0 -156
- package/src/services/search/codebase-indexer-types.ts +0 -38
- package/src/services/search/codebase-indexer.ts +0 -647
- package/src/services/search/embeddings-provider.ts +0 -455
- package/src/services/search/embeddings.ts +0 -316
- package/src/services/search/functional-indexer.ts +0 -323
- package/src/services/search/index.ts +0 -27
- package/src/services/search/indexer.ts +0 -380
- package/src/services/search/knowledge-indexer.ts +0 -422
- package/src/services/search/semantic-search.ts +0 -244
- package/src/services/search/tfidf.ts +0 -559
- package/src/services/search/unified-search-service.ts +0 -888
- package/src/services/storage/cache-storage.ts +0 -487
- package/src/services/storage/drizzle-storage.ts +0 -581
- package/src/services/storage/index.ts +0 -15
- package/src/services/storage/lancedb-vector-storage.ts +0 -494
- package/src/services/storage/memory-storage.ts +0 -268
- package/src/services/storage/separated-storage.ts +0 -467
- package/src/services/storage/vector-storage.ts +0 -13
package/src/types/mcp.types.ts
CHANGED
|
@@ -1,30 +1,26 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* MCP (Model Context Protocol)
|
|
3
|
-
*
|
|
2
|
+
* MCP (Model Context Protocol) Configuration Types
|
|
3
|
+
* Defines configuration formats for different MCP server implementations
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import type { Resolvable } from './common.types.js';
|
|
7
|
-
|
|
8
6
|
/**
|
|
9
|
-
* MCP server configuration
|
|
10
|
-
* Communicates via standard input/output
|
|
7
|
+
* Base MCP server configuration with stdio transport
|
|
11
8
|
*/
|
|
12
|
-
export
|
|
9
|
+
export type MCPServerConfig = {
|
|
13
10
|
type: 'stdio';
|
|
14
|
-
command:
|
|
15
|
-
args?:
|
|
11
|
+
command: string;
|
|
12
|
+
args?: string[];
|
|
16
13
|
env?: Record<string, string>;
|
|
17
|
-
}
|
|
14
|
+
};
|
|
18
15
|
|
|
19
16
|
/**
|
|
20
|
-
* MCP server configuration
|
|
21
|
-
* Communicates via HTTP requests
|
|
17
|
+
* MCP server configuration with HTTP transport
|
|
22
18
|
*/
|
|
23
|
-
export
|
|
19
|
+
export type MCPServerConfigHTTP = {
|
|
24
20
|
type: 'http';
|
|
25
|
-
url:
|
|
21
|
+
url: string;
|
|
26
22
|
headers?: Record<string, string>;
|
|
27
|
-
}
|
|
23
|
+
};
|
|
28
24
|
|
|
29
25
|
/**
|
|
30
26
|
* Union of all possible MCP server configurations
|
|
@@ -32,38 +28,33 @@ export interface MCPServerConfigHTTP {
|
|
|
32
28
|
export type MCPServerConfigUnion = MCPServerConfig | MCPServerConfigHTTP;
|
|
33
29
|
|
|
34
30
|
/**
|
|
35
|
-
*
|
|
36
|
-
* Used to disable specific server features per target
|
|
31
|
+
* OpenCode-specific configuration format
|
|
37
32
|
*/
|
|
38
|
-
export type
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
33
|
+
export type OpenCodeConfig = {
|
|
34
|
+
type: 'local' | 'remote';
|
|
35
|
+
command?: string[];
|
|
36
|
+
url?: string;
|
|
37
|
+
headers?: Record<string, string>;
|
|
38
|
+
environment?: Record<string, string>;
|
|
42
39
|
};
|
|
43
40
|
|
|
44
41
|
/**
|
|
45
|
-
*
|
|
42
|
+
* Type guard for stdio config
|
|
46
43
|
*/
|
|
47
|
-
export
|
|
48
|
-
|
|
49
|
-
mcp?: Record<string, MCPServerConfigUnion>;
|
|
44
|
+
export function isStdioConfig(config: MCPServerConfigUnion): config is MCPServerConfig {
|
|
45
|
+
return config.type === 'stdio';
|
|
50
46
|
}
|
|
51
47
|
|
|
52
48
|
/**
|
|
53
|
-
* Type guard
|
|
54
|
-
*/
|
|
55
|
-
export const isStdioConfig = (config: MCPServerConfigUnion): config is MCPServerConfig =>
|
|
56
|
-
config.type === 'stdio';
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* Type guard: Check if config is HTTP-based
|
|
49
|
+
* Type guard for HTTP config
|
|
60
50
|
*/
|
|
61
|
-
export
|
|
62
|
-
config.type === 'http';
|
|
51
|
+
export function isHttpConfig(config: MCPServerConfigUnion): config is MCPServerConfigHTTP {
|
|
52
|
+
return config.type === 'http';
|
|
53
|
+
}
|
|
63
54
|
|
|
64
55
|
/**
|
|
65
|
-
* Type guard
|
|
66
|
-
* Alias for isStdioConfig for backward compatibility
|
|
56
|
+
* Type guard for CLI command config (stdio)
|
|
67
57
|
*/
|
|
68
|
-
export
|
|
69
|
-
isStdioConfig(config);
|
|
58
|
+
export function isCLICommandConfig(config: MCPServerConfigUnion): config is MCPServerConfig {
|
|
59
|
+
return isStdioConfig(config);
|
|
60
|
+
}
|
|
@@ -69,8 +69,6 @@ export interface Target {
|
|
|
69
69
|
// Configuration
|
|
70
70
|
/** Target-specific configuration */
|
|
71
71
|
readonly config: TargetConfig;
|
|
72
|
-
/** MCP server configuration for this target */
|
|
73
|
-
readonly mcpServerConfig?: MCPServerConfigFlags;
|
|
74
72
|
|
|
75
73
|
// Required transformation methods
|
|
76
74
|
/** Transform agent content for the target */
|
package/src/types.ts
CHANGED
|
@@ -1,168 +0,0 @@
|
|
|
1
|
-
import chalk from 'chalk';
|
|
2
|
-
import { Command } from 'commander';
|
|
3
|
-
import ora from 'ora';
|
|
4
|
-
import { ReindexMonitor } from '../components/reindex-progress.js';
|
|
5
|
-
import { CodebaseIndexer } from '../services/search/codebase-indexer.js';
|
|
6
|
-
import { getDefaultEmbeddingProvider } from '../services/search/embeddings.js';
|
|
7
|
-
import { getSearchService } from '../services/search/unified-search-service.js';
|
|
8
|
-
import { CLIError } from '../utils/error-handler.js';
|
|
9
|
-
|
|
10
|
-
export const codebaseSearchCommand = new Command('search')
|
|
11
|
-
.description('Search codebase files and source code')
|
|
12
|
-
.argument('<query>', 'Search query - use natural language, function names, or technical terms')
|
|
13
|
-
.option('-l, --limit <number>', 'Maximum number of results to return', '10')
|
|
14
|
-
.option('--include-content', 'Include file content snippets in results', true)
|
|
15
|
-
.option('--extensions <exts...>', 'Filter by file extensions (e.g., .ts .tsx .js)')
|
|
16
|
-
.option('--path <pattern>', 'Filter by path pattern (e.g., src/components)')
|
|
17
|
-
.option('--exclude <patterns...>', 'Exclude paths containing these patterns')
|
|
18
|
-
.action(async (query, options) => {
|
|
19
|
-
try {
|
|
20
|
-
console.log('');
|
|
21
|
-
console.log(chalk.cyan.bold('▸ Search Codebase'));
|
|
22
|
-
console.log(chalk.gray(` Query: "${query}"`));
|
|
23
|
-
|
|
24
|
-
const spinner = ora('Searching...').start();
|
|
25
|
-
const searchService = getSearchService();
|
|
26
|
-
await searchService.initialize();
|
|
27
|
-
|
|
28
|
-
const result = await searchService.searchCodebase(query, {
|
|
29
|
-
limit: Number.parseInt(String(options.limit), 10) || 10,
|
|
30
|
-
include_content: options.includeContent !== false,
|
|
31
|
-
file_extensions: options.extensions,
|
|
32
|
-
path_filter: options.path,
|
|
33
|
-
exclude_paths: options.exclude,
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
spinner.stop();
|
|
37
|
-
|
|
38
|
-
const output = searchService.formatResultsForCLI(result.results, query, result.totalIndexed);
|
|
39
|
-
console.log(output);
|
|
40
|
-
console.log('');
|
|
41
|
-
} catch (error) {
|
|
42
|
-
console.error(chalk.red(`\n✗ Error: ${(error as Error).message}\n`));
|
|
43
|
-
process.exit(1);
|
|
44
|
-
}
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
export const codebaseReindexCommand = new Command('reindex')
|
|
48
|
-
.description('Reindex all codebase files')
|
|
49
|
-
.action(async () => {
|
|
50
|
-
try {
|
|
51
|
-
const indexer = new CodebaseIndexer();
|
|
52
|
-
|
|
53
|
-
// Check if API key exists - only use embeddings if key is present
|
|
54
|
-
const hasApiKey = !!process.env.OPENAI_API_KEY;
|
|
55
|
-
const embeddingProvider = hasApiKey ? await getDefaultEmbeddingProvider() : undefined;
|
|
56
|
-
const mode: 'tfidf-only' | 'semantic' = hasApiKey ? 'semantic' : 'tfidf-only';
|
|
57
|
-
|
|
58
|
-
// Create monitor for progress display
|
|
59
|
-
const monitor = new ReindexMonitor();
|
|
60
|
-
|
|
61
|
-
// Start the UI
|
|
62
|
-
monitor.start(0); // Will update total when we know file count
|
|
63
|
-
|
|
64
|
-
let totalFiles = 0;
|
|
65
|
-
let phase: 'tokenizing' | 'calculating' | 'completed' = 'tokenizing';
|
|
66
|
-
|
|
67
|
-
// Set initial mode
|
|
68
|
-
monitor.updateProgress({ mode });
|
|
69
|
-
|
|
70
|
-
const result = await indexer.indexCodebase({
|
|
71
|
-
force: true, // Reindex should always force rebuild
|
|
72
|
-
embeddingProvider,
|
|
73
|
-
onProgress: (progress) => {
|
|
74
|
-
// Track total files on first progress update
|
|
75
|
-
if (totalFiles === 0 && progress.total > 0) {
|
|
76
|
-
totalFiles = progress.total;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
// Update Ink UI only (stderr output was interfering with Ink rendering)
|
|
80
|
-
monitor.updateProgress({
|
|
81
|
-
current: progress.current,
|
|
82
|
-
total: progress.total,
|
|
83
|
-
fileName: progress.fileName,
|
|
84
|
-
status: progress.status,
|
|
85
|
-
phase,
|
|
86
|
-
});
|
|
87
|
-
},
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
// Handle cache hit - simulate progress to show 100%
|
|
91
|
-
if (result.stats.cacheHit) {
|
|
92
|
-
monitor.updateProgress({
|
|
93
|
-
current: result.stats.totalFiles,
|
|
94
|
-
total: result.stats.totalFiles,
|
|
95
|
-
fileName: '',
|
|
96
|
-
status: 'completed',
|
|
97
|
-
phase: 'tokenizing',
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
// Small delay
|
|
101
|
-
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
102
|
-
} else {
|
|
103
|
-
// Update to calculating phase
|
|
104
|
-
phase = 'calculating';
|
|
105
|
-
monitor.updateProgress({ phase: 'calculating' });
|
|
106
|
-
|
|
107
|
-
// Small delay to show calculating phase
|
|
108
|
-
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
// Show completion with stats
|
|
112
|
-
monitor.updateProgress({
|
|
113
|
-
phase: 'completed',
|
|
114
|
-
stats: {
|
|
115
|
-
documentsProcessed: result.stats.totalFiles,
|
|
116
|
-
uniqueTerms: result.tfidfIndex.idf.size,
|
|
117
|
-
},
|
|
118
|
-
});
|
|
119
|
-
|
|
120
|
-
// Give time to see the completion message
|
|
121
|
-
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
122
|
-
|
|
123
|
-
monitor.stop();
|
|
124
|
-
} catch (error) {
|
|
125
|
-
throw new CLIError(`Codebase reindex failed: ${(error as Error).message}`);
|
|
126
|
-
}
|
|
127
|
-
});
|
|
128
|
-
|
|
129
|
-
export const codebaseStatusCommand = new Command('status')
|
|
130
|
-
.description('Get codebase search system status')
|
|
131
|
-
.action(async () => {
|
|
132
|
-
try {
|
|
133
|
-
console.log('');
|
|
134
|
-
console.log(chalk.cyan.bold('▸ Codebase Status'));
|
|
135
|
-
|
|
136
|
-
const searchService = getSearchService();
|
|
137
|
-
await searchService.initialize();
|
|
138
|
-
const status = await searchService.getStatus();
|
|
139
|
-
|
|
140
|
-
if (status.codebase.indexed) {
|
|
141
|
-
console.log(chalk.green('\n✓ Indexed and ready'));
|
|
142
|
-
console.log(chalk.gray(` Files: ${status.codebase.fileCount}`));
|
|
143
|
-
if (status.codebase.indexedAt) {
|
|
144
|
-
console.log(
|
|
145
|
-
chalk.gray(` Last indexed: ${new Date(status.codebase.indexedAt).toLocaleString()}`)
|
|
146
|
-
);
|
|
147
|
-
}
|
|
148
|
-
} else {
|
|
149
|
-
console.log(chalk.yellow('\n⚠ Not indexed'));
|
|
150
|
-
console.log(chalk.gray(' Run: sylphx-flow codebase reindex'));
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
console.log(chalk.cyan('\n▸ Available Commands'));
|
|
154
|
-
console.log(chalk.gray(' • codebase search <query>'));
|
|
155
|
-
console.log(chalk.gray(' • codebase reindex'));
|
|
156
|
-
console.log(chalk.gray(' • codebase status'));
|
|
157
|
-
console.log('');
|
|
158
|
-
} catch (error) {
|
|
159
|
-
console.error(chalk.red(`\n✗ Error: ${(error as Error).message}\n`));
|
|
160
|
-
process.exit(1);
|
|
161
|
-
}
|
|
162
|
-
});
|
|
163
|
-
|
|
164
|
-
export const codebaseCommand = new Command('codebase')
|
|
165
|
-
.description('Manage codebase indexing and search')
|
|
166
|
-
.addCommand(codebaseSearchCommand)
|
|
167
|
-
.addCommand(codebaseReindexCommand)
|
|
168
|
-
.addCommand(codebaseStatusCommand);
|
|
@@ -1,161 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Knowledge CLI commands
|
|
3
|
-
* Knowledge base search and management functionality
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { Command } from 'commander';
|
|
7
|
-
import { getKnowledgeContent } from '../domains/knowledge/resources.js';
|
|
8
|
-
import { getSearchService } from '../services/search/unified-search-service.js';
|
|
9
|
-
import { CLIError } from '../utils/error-handler.js';
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Knowledge search command
|
|
13
|
-
*/
|
|
14
|
-
export const knowledgeSearchCommand = new Command('search')
|
|
15
|
-
.description('Search knowledge base, documentation, and guides')
|
|
16
|
-
.argument('<query>', 'Search query - use natural language, technology names, or topic keywords')
|
|
17
|
-
.option('-l, --limit <number>', 'Maximum number of results to return', '10')
|
|
18
|
-
.option('--include-content', 'Include full content in results', true)
|
|
19
|
-
.action(async (query, options) => {
|
|
20
|
-
try {
|
|
21
|
-
console.log(`📚 Searching knowledge base for: "${query}"`);
|
|
22
|
-
|
|
23
|
-
const searchService = getSearchService();
|
|
24
|
-
await searchService.initialize();
|
|
25
|
-
|
|
26
|
-
const result = await searchService.searchKnowledge(query, {
|
|
27
|
-
limit: Number.parseInt(options.limit, 10) || 10,
|
|
28
|
-
include_content: options.includeContent !== false,
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
const output = searchService.formatResultsForCLI(result.results, query, result.totalIndexed);
|
|
32
|
-
console.log(output);
|
|
33
|
-
} catch (error) {
|
|
34
|
-
throw new CLIError(`Knowledge search failed: ${(error as Error).message}`);
|
|
35
|
-
}
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* Knowledge get command
|
|
40
|
-
*/
|
|
41
|
-
export const knowledgeGetCommand = new Command('get')
|
|
42
|
-
.description('Get specific knowledge document by URI')
|
|
43
|
-
.argument('<uri>', 'Knowledge URI to access (e.g., "knowledge://stacks/react-app")')
|
|
44
|
-
.action(async (uri) => {
|
|
45
|
-
try {
|
|
46
|
-
const content = await getKnowledgeContent(uri);
|
|
47
|
-
console.log(content);
|
|
48
|
-
} catch (error) {
|
|
49
|
-
const errorMessage = `Knowledge get failed: ${(error as Error).message}`;
|
|
50
|
-
|
|
51
|
-
// Show available URIs
|
|
52
|
-
const searchService = getSearchService();
|
|
53
|
-
const availableURIs = await searchService.getAvailableKnowledgeURIs();
|
|
54
|
-
if (availableURIs.length > 0) {
|
|
55
|
-
console.log('\n**Available knowledge URIs:**');
|
|
56
|
-
for (const uri of availableURIs) {
|
|
57
|
-
console.log(`• ${uri}`);
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
throw new CLIError(errorMessage);
|
|
61
|
-
}
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* Knowledge list command
|
|
66
|
-
*/
|
|
67
|
-
export const knowledgeListCommand = new Command('list')
|
|
68
|
-
.description('List all available knowledge resources')
|
|
69
|
-
.option('--category <type>', 'Filter by category (stacks, guides, universal, data)')
|
|
70
|
-
.action(async (options) => {
|
|
71
|
-
try {
|
|
72
|
-
const searchService = getSearchService();
|
|
73
|
-
await searchService.initialize();
|
|
74
|
-
const availableURIs = await searchService.getAvailableKnowledgeURIs();
|
|
75
|
-
|
|
76
|
-
if (availableURIs.length === 0) {
|
|
77
|
-
console.log('📭 No knowledge documents available');
|
|
78
|
-
return;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
let filteredURIs = availableURIs;
|
|
82
|
-
if (options.category) {
|
|
83
|
-
filteredURIs = availableURIs.filter((uri) => uri.includes(`/${options.category}/`));
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
console.log(`📚 Available Knowledge Resources (${filteredURIs.length} documents):\n`);
|
|
87
|
-
|
|
88
|
-
// Group by category
|
|
89
|
-
const grouped = filteredURIs.reduce(
|
|
90
|
-
(acc, uri) => {
|
|
91
|
-
const category = uri.split('/')[2] || 'unknown';
|
|
92
|
-
if (!acc[category]) {
|
|
93
|
-
acc[category] = [];
|
|
94
|
-
}
|
|
95
|
-
acc[category].push(uri);
|
|
96
|
-
return acc;
|
|
97
|
-
},
|
|
98
|
-
{} as Record<string, string[]>
|
|
99
|
-
);
|
|
100
|
-
|
|
101
|
-
for (const [category, uris] of Object.entries(grouped)) {
|
|
102
|
-
console.log(`### ${category.charAt(0).toUpperCase() + category.slice(1)}`);
|
|
103
|
-
for (const uri of uris) {
|
|
104
|
-
const name = uri.split('/').pop() || 'Unknown';
|
|
105
|
-
console.log(`• ${name} - ${uri}`);
|
|
106
|
-
}
|
|
107
|
-
console.log('');
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
console.log('**Usage:**');
|
|
111
|
-
console.log('• sylphx knowledge search <query> - Search knowledge base');
|
|
112
|
-
console.log('• sylphx knowledge get <uri> - Get specific document');
|
|
113
|
-
} catch (error) {
|
|
114
|
-
throw new CLIError(`Knowledge status failed: ${(error as Error).message}`);
|
|
115
|
-
}
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
/**
|
|
119
|
-
* Knowledge status command
|
|
120
|
-
*/
|
|
121
|
-
export const knowledgeStatusCommand = new Command('status')
|
|
122
|
-
.description('Get knowledge base system status')
|
|
123
|
-
.action(async () => {
|
|
124
|
-
try {
|
|
125
|
-
console.log('\n### 📚 Knowledge Base Status\n');
|
|
126
|
-
|
|
127
|
-
const searchService = getSearchService();
|
|
128
|
-
await searchService.initialize();
|
|
129
|
-
const status = await searchService.getStatus();
|
|
130
|
-
|
|
131
|
-
if (status.knowledge.indexed) {
|
|
132
|
-
console.log('**Status:** ✓ Ready');
|
|
133
|
-
console.log(`**Documents:** ${status.knowledge.documentCount} files`);
|
|
134
|
-
} else if (status.knowledge.isIndexing) {
|
|
135
|
-
console.log(`**Status:** 🔄 Building index (${status.knowledge.progress || 0}%)`);
|
|
136
|
-
console.log('**Note:** Please wait a moment and try again');
|
|
137
|
-
} else {
|
|
138
|
-
console.log('**Status:** ⚠️ Not initialized');
|
|
139
|
-
console.log('**Note:** Will auto-index on first search');
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
console.log('\n**Available Commands:**');
|
|
143
|
-
console.log('• sylphx knowledge search <query> - Search knowledge base');
|
|
144
|
-
console.log('• sylphx knowledge get <uri> - Get specific document');
|
|
145
|
-
} catch (error) {
|
|
146
|
-
throw new CLIError(`Knowledge list failed: ${(error as Error).message}`);
|
|
147
|
-
}
|
|
148
|
-
});
|
|
149
|
-
|
|
150
|
-
/**
|
|
151
|
-
* Main knowledge command
|
|
152
|
-
*/
|
|
153
|
-
export const knowledgeCommand = new Command('knowledge').description(
|
|
154
|
-
'Knowledge base search and management commands'
|
|
155
|
-
);
|
|
156
|
-
|
|
157
|
-
// Add subcommands
|
|
158
|
-
knowledgeCommand.addCommand(knowledgeSearchCommand);
|
|
159
|
-
knowledgeCommand.addCommand(knowledgeGetCommand);
|
|
160
|
-
knowledgeCommand.addCommand(knowledgeListCommand);
|
|
161
|
-
knowledgeCommand.addCommand(knowledgeStatusCommand);
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
import { getTarget, getDefaultTargetUnsafe } from '../config/targets.js';
|
|
2
|
-
import { projectSettings } from '../utils/settings.js';
|
|
3
|
-
import type { MCPServerConfigFlags } from '../types.js';
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Get the current target's MCP server configuration
|
|
7
|
-
* Follows the same resolution pattern as targetManager.resolveTarget()
|
|
8
|
-
* Returns undefined if no target is set or target has no mcpServerConfig
|
|
9
|
-
*/
|
|
10
|
-
export async function useTargetConfig(): Promise<MCPServerConfigFlags | undefined> {
|
|
11
|
-
try {
|
|
12
|
-
// Try to get target from project settings first
|
|
13
|
-
const settings = await projectSettings.load();
|
|
14
|
-
const targetId = settings?.target;
|
|
15
|
-
|
|
16
|
-
if (targetId) {
|
|
17
|
-
const targetOption = getTarget(targetId);
|
|
18
|
-
if (targetOption._tag === 'Some') {
|
|
19
|
-
return targetOption.value.mcpServerConfig;
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
// Fall back to default target
|
|
24
|
-
const defaultTarget = getDefaultTargetUnsafe();
|
|
25
|
-
return defaultTarget.mcpServerConfig;
|
|
26
|
-
} catch {
|
|
27
|
-
// If no target can be resolved, return undefined
|
|
28
|
-
return undefined;
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* Get a specific target's MCP server configuration by ID
|
|
34
|
-
*/
|
|
35
|
-
export function useTargetConfigById(targetId: string): MCPServerConfigFlags | undefined {
|
|
36
|
-
try {
|
|
37
|
-
const targetOption = getTarget(targetId);
|
|
38
|
-
if (targetOption._tag === 'None') {
|
|
39
|
-
return undefined;
|
|
40
|
-
}
|
|
41
|
-
return targetOption.value.mcpServerConfig;
|
|
42
|
-
} catch {
|
|
43
|
-
return undefined;
|
|
44
|
-
}
|
|
45
|
-
}
|
|
@@ -1,115 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tests for Byte Formatting Utilities
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import { describe, expect, it } from 'bun:test';
|
|
6
|
-
import { formatBytes, formatFileSize } from './bytes.js';
|
|
7
|
-
|
|
8
|
-
describe('formatBytes', () => {
|
|
9
|
-
describe('default options (decimals: 2, longUnits)', () => {
|
|
10
|
-
it('formats 0 bytes', () => {
|
|
11
|
-
expect(formatBytes(0)).toBe('0 Bytes');
|
|
12
|
-
});
|
|
13
|
-
|
|
14
|
-
it('formats bytes', () => {
|
|
15
|
-
expect(formatBytes(500)).toBe('500 Bytes');
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
it('formats kilobytes', () => {
|
|
19
|
-
expect(formatBytes(1024)).toBe('1 KB');
|
|
20
|
-
expect(formatBytes(1536)).toBe('1.5 KB');
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
it('formats megabytes', () => {
|
|
24
|
-
expect(formatBytes(1048576)).toBe('1 MB');
|
|
25
|
-
expect(formatBytes(1572864)).toBe('1.5 MB');
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
it('formats gigabytes', () => {
|
|
29
|
-
expect(formatBytes(1073741824)).toBe('1 GB');
|
|
30
|
-
expect(formatBytes(1610612736)).toBe('1.5 GB');
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
it('formats terabytes', () => {
|
|
34
|
-
expect(formatBytes(1099511627776)).toBe('1 TB');
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
it('rounds to 2 decimal places', () => {
|
|
38
|
-
expect(formatBytes(1587)).toBe('1.55 KB');
|
|
39
|
-
expect(formatBytes(1638400)).toBe('1.56 MB');
|
|
40
|
-
});
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
describe('with decimals option', () => {
|
|
44
|
-
it('formats with 0 decimals', () => {
|
|
45
|
-
expect(formatBytes(1536, { decimals: 0 })).toBe('2 KB');
|
|
46
|
-
expect(formatBytes(1024, { decimals: 0 })).toBe('1 KB');
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
it('formats with 1 decimal', () => {
|
|
50
|
-
expect(formatBytes(1536, { decimals: 1 })).toBe('1.5 KB');
|
|
51
|
-
expect(formatBytes(1587, { decimals: 1 })).toBe('1.5 KB');
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
it('formats with 3 decimals', () => {
|
|
55
|
-
expect(formatBytes(1587, { decimals: 3 })).toBe('1.55 KB'); // toFixed trims to 1.550, then trimmed
|
|
56
|
-
});
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
describe('with shortUnits option', () => {
|
|
60
|
-
it('uses short unit for 0 bytes', () => {
|
|
61
|
-
expect(formatBytes(0, { shortUnits: true })).toBe('0 B');
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
it('uses short unit for bytes', () => {
|
|
65
|
-
expect(formatBytes(500, { shortUnits: true })).toBe('500 B');
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
it('uses short units for kilobytes', () => {
|
|
69
|
-
expect(formatBytes(1024, { shortUnits: true })).toBe('1 KB');
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
it('uses short units for megabytes', () => {
|
|
73
|
-
expect(formatBytes(1048576, { shortUnits: true })).toBe('1 MB');
|
|
74
|
-
});
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
describe('combined options', () => {
|
|
78
|
-
it('uses short units with 1 decimal', () => {
|
|
79
|
-
expect(formatBytes(1536, { decimals: 1, shortUnits: true })).toBe('1.5 KB');
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
it('uses short units with 0 decimals', () => {
|
|
83
|
-
expect(formatBytes(1536, { decimals: 0, shortUnits: true })).toBe('2 KB');
|
|
84
|
-
});
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
describe('edge cases', () => {
|
|
88
|
-
it('handles 1 byte', () => {
|
|
89
|
-
expect(formatBytes(1)).toBe('1 Bytes');
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
it('handles very large numbers', () => {
|
|
93
|
-
const result = formatBytes(1099511627776 * 1024); // 1 PB
|
|
94
|
-
expect(result).toContain('TB'); // Will show in TB since we only go up to TB
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
it('handles fractional KB', () => {
|
|
98
|
-
expect(formatBytes(1500)).toBe('1.46 KB');
|
|
99
|
-
});
|
|
100
|
-
});
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
describe('formatFileSize', () => {
|
|
104
|
-
it('formats with 1 decimal and short units', () => {
|
|
105
|
-
expect(formatFileSize(0)).toBe('0 B');
|
|
106
|
-
expect(formatFileSize(1024)).toBe('1 KB'); // toFixed(1) gives "1.0", trimmed to "1"
|
|
107
|
-
expect(formatFileSize(1536)).toBe('1.5 KB');
|
|
108
|
-
expect(formatFileSize(1048576)).toBe('1 MB'); // toFixed(1) gives "1.0", trimmed to "1"
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
it('is an alias for formatBytes with specific options', () => {
|
|
112
|
-
const bytes = 1572864;
|
|
113
|
-
expect(formatFileSize(bytes)).toBe(formatBytes(bytes, { decimals: 1, shortUnits: true }));
|
|
114
|
-
});
|
|
115
|
-
});
|