Add comprehensive tests for ast-grep and tree-sitter relationship extraction

- Introduced test suite for AstGrepPythonProcessor covering pattern definitions, parsing, and relationship extraction.
- Added comparison tests between tree-sitter and ast-grep for consistency in relationship extraction.
- Implemented tests for ast-grep binding module to verify functionality and availability.
- Ensured tests cover various scenarios including inheritance, function calls, and imports.
This commit is contained in:
catlog22
2026-02-15 21:14:14 +08:00
parent 126a357aa2
commit 48a6a1f2aa
56 changed files with 10622 additions and 374 deletions

View File

@@ -0,0 +1,154 @@
// ========================================
// Remote Notification Configuration Manager
// ========================================
// Manages persistent storage of remote notification settings
// Storage: ~/.ccw/config/remote-notification.json
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';
import { join } from 'path';
import { getCCWHome, ensureStorageDir } from './storage-paths.js';
import type {
RemoteNotificationConfig,
DEFAULT_REMOTE_NOTIFICATION_CONFIG,
} from '../types/remote-notification.js';
import { DeepPartial, deepMerge } from '../types/util.js';
/**
* Configuration file path
*/
function getConfigFilePath(): string {
return join(getCCWHome(), 'config', 'remote-notification.json');
}
/**
* Ensure configuration directory exists
*/
function ensureConfigDir(): void {
const configDir = join(getCCWHome(), 'config');
ensureStorageDir(configDir);
}
/**
* Default configuration factory
*/
export function getDefaultConfig(): RemoteNotificationConfig {
return {
enabled: false,
platforms: {},
events: [
{ event: 'ask-user-question', platforms: ['discord', 'telegram'], enabled: true },
{ event: 'session-start', platforms: [], enabled: false },
{ event: 'session-end', platforms: [], enabled: false },
{ event: 'task-completed', platforms: [], enabled: false },
{ event: 'task-failed', platforms: ['discord', 'telegram'], enabled: true },
],
timeout: 10000,
};
}
/**
* Load remote notification configuration
* Returns default config if file doesn't exist
*/
export function loadConfig(): RemoteNotificationConfig {
const configPath = getConfigFilePath();
if (!existsSync(configPath)) {
return getDefaultConfig();
}
try {
const data = readFileSync(configPath, 'utf-8');
const parsed = JSON.parse(data);
// Merge with defaults to ensure all fields exist
return deepMerge(getDefaultConfig(), parsed);
} catch (error) {
console.error('[RemoteNotificationConfig] Failed to load config:', error);
return getDefaultConfig();
}
}
/**
* Save remote notification configuration
*/
export function saveConfig(config: RemoteNotificationConfig): void {
ensureConfigDir();
const configPath = getConfigFilePath();
try {
writeFileSync(configPath, JSON.stringify(config, null, 2), 'utf-8');
} catch (error) {
console.error('[RemoteNotificationConfig] Failed to save config:', error);
throw error;
}
}
/**
* Update configuration with partial changes
*/
export function updateConfig(
updates: DeepPartial<RemoteNotificationConfig>
): RemoteNotificationConfig {
const current = loadConfig();
const updated = deepMerge(current, updates);
saveConfig(updated);
return updated;
}
/**
* Reset configuration to defaults
*/
export function resetConfig(): RemoteNotificationConfig {
const defaultConfig = getDefaultConfig();
saveConfig(defaultConfig);
return defaultConfig;
}
/**
* Check if any platform is configured and enabled
*/
export function hasEnabledPlatform(config: RemoteNotificationConfig): boolean {
if (!config.enabled) return false;
const { discord, telegram, webhook } = config.platforms;
return (
(discord?.enabled && !!discord.webhookUrl) ||
(telegram?.enabled && !!telegram.botToken && !!telegram.chatId) ||
(webhook?.enabled && !!webhook.url)
);
}
/**
* Get enabled platforms for a specific event
*/
export function getEnabledPlatformsForEvent(
config: RemoteNotificationConfig,
eventType: string
): string[] {
if (!config.enabled) return [];
const eventConfig = config.events.find((e) => e.event === eventType);
if (!eventConfig || !eventConfig.enabled) return [];
return eventConfig.platforms.filter((platform) => {
const platformConfig = config.platforms[platform as keyof typeof config.platforms];
if (!platformConfig) return false;
switch (platform) {
case 'discord':
return (platformConfig as { enabled: boolean; webhookUrl?: string }).enabled &&
!!(platformConfig as { webhookUrl?: string }).webhookUrl;
case 'telegram':
return (platformConfig as { enabled: boolean; botToken?: string; chatId?: string }).enabled &&
!!(platformConfig as { botToken?: string }).botToken &&
!!(platformConfig as { chatId?: string }).chatId;
case 'webhook':
return (platformConfig as { enabled: boolean; url?: string }).enabled &&
!!(platformConfig as { url?: string }).url;
default:
return false;
}
});
}

View File

@@ -388,6 +388,15 @@ export interface ProjectPaths {
/** Skills directory */
skills: string;
};
/** Unified vector index paths (HNSW-backed) */
unifiedVectors: {
/** Root: <projectRoot>/unified-vectors/ */
root: string;
/** SQLite database for vector metadata */
vectorsDb: string;
/** HNSW index file */
hnswIndex: string;
};
}
/**
@@ -454,6 +463,11 @@ export function getProjectPaths(projectPath: string): ProjectPaths {
memoryMd: join(projectDir, 'core-memory', 'v2', 'MEMORY.md'),
skills: join(projectDir, 'core-memory', 'v2', 'skills'),
},
unifiedVectors: {
root: join(projectDir, 'unified-vectors'),
vectorsDb: join(projectDir, 'unified-vectors', 'vectors.db'),
hnswIndex: join(projectDir, 'unified-vectors', 'vectors.hnsw'),
},
};
}
@@ -483,6 +497,11 @@ export function getProjectPathsById(projectId: string): ProjectPaths {
memoryMd: join(projectDir, 'core-memory', 'v2', 'MEMORY.md'),
skills: join(projectDir, 'core-memory', 'v2', 'skills'),
},
unifiedVectors: {
root: join(projectDir, 'unified-vectors'),
vectorsDb: join(projectDir, 'unified-vectors', 'vectors.db'),
hnswIndex: join(projectDir, 'unified-vectors', 'vectors.hnsw'),
},
};
}