chore: archive unused test scripts and temporary documents

- Moved 6 empty test files (*.test.ts) to archive/
- Moved 5 Python test scripts (*.py) to archive/
- Moved 5 outdated/temporary documents to archive/
- Cleaned up root directory for better organization
This commit is contained in:
catlog22
2026-01-24 21:26:03 +08:00
parent 862365ffaf
commit 874b70726d
16 changed files with 0 additions and 0 deletions

62
archive/1.18.0 Normal file
View File

@@ -0,0 +1,62 @@
Collecting onnxruntime-directml
Using cached onnxruntime_directml-1.23.0-cp310-cp310-win_amd64.whl (25.1 MB)
Collecting protobuf
Using cached protobuf-6.33.3-cp310-abi3-win_amd64.whl (436 kB)
Collecting packaging
Using cached packaging-25.0-py3-none-any.whl (66 kB)
Collecting coloredlogs
Using cached coloredlogs-15.0.1-py2.py3-none-any.whl (46 kB)
Collecting flatbuffers
Using cached flatbuffers-25.12.19-py2.py3-none-any.whl (26 kB)
Collecting numpy>=1.21.6
Using cached numpy-2.2.6-cp310-cp310-win_amd64.whl (12.9 MB)
Collecting sympy
Using cached sympy-1.14.0-py3-none-any.whl (6.3 MB)
Collecting humanfriendly>=9.1
Using cached humanfriendly-10.0-py2.py3-none-any.whl (86 kB)
Collecting mpmath<1.4,>=1.1.0
Using cached mpmath-1.3.0-py3-none-any.whl (536 kB)
Collecting pyreadline3
Using cached pyreadline3-3.5.4-py3-none-any.whl (83 kB)
Installing collected packages: mpmath, flatbuffers, sympy, pyreadline3, protobuf, packaging, numpy, humanfriendly, coloredlogs, onnxruntime-directml
Attempting uninstall: mpmath
Found existing installation: mpmath 1.3.0
Uninstalling mpmath-1.3.0:
Successfully uninstalled mpmath-1.3.0
Attempting uninstall: flatbuffers
Found existing installation: flatbuffers 25.12.19
Uninstalling flatbuffers-25.12.19:
Successfully uninstalled flatbuffers-25.12.19
Attempting uninstall: sympy
Found existing installation: sympy 1.14.0
Uninstalling sympy-1.14.0:
Successfully uninstalled sympy-1.14.0
Attempting uninstall: pyreadline3
Found existing installation: pyreadline3 3.5.4
Uninstalling pyreadline3-3.5.4:
Successfully uninstalled pyreadline3-3.5.4
Attempting uninstall: protobuf
Found existing installation: protobuf 6.33.3
Uninstalling protobuf-6.33.3:
Successfully uninstalled protobuf-6.33.3
Attempting uninstall: packaging
Found existing installation: packaging 25.0
Uninstalling packaging-25.0:
Successfully uninstalled packaging-25.0
Attempting uninstall: numpy
Found existing installation: numpy 2.2.6
Uninstalling numpy-2.2.6:
Successfully uninstalled numpy-2.2.6
Attempting uninstall: humanfriendly
Found existing installation: humanfriendly 10.0
Uninstalling humanfriendly-10.0:
Successfully uninstalled humanfriendly-10.0
Attempting uninstall: coloredlogs
Found existing installation: coloredlogs 15.0.1
Uninstalling coloredlogs-15.0.1:
Successfully uninstalled coloredlogs-15.0.1
Attempting uninstall: onnxruntime-directml
Found existing installation: onnxruntime-directml 1.23.0
Uninstalling onnxruntime-directml-1.23.0:
Successfully uninstalled onnxruntime-directml-1.23.0
Successfully installed coloredlogs-15.0.1 flatbuffers-25.12.19 humanfriendly-10.0 mpmath-1.3.0 numpy-2.2.6 onnxruntime-directml-1.23.0 packaging-25.0 protobuf-6.33.3 pyreadline3-3.5.4 sympy-1.14.0

View File

@@ -0,0 +1,167 @@
# CLI History Store 数据库迁移优化 - 完成报告
## 📋 任务概况
优化 CLI History Store 的数据库迁移逻辑,解决每次 CLI 执行都输出重复迁移日志的问题。
## ✅ 实现清单
### 1. 完善 turns 表结构 - COMPLETED
**文件**: `ccw/src/tools/cli-history-store.ts:149-169`
`initSchema()` 的 CREATE TABLE 语句中添加了 5 个缺失的列:
-`cached INTEGER DEFAULT 0` (行 162)
-`stdout_full TEXT` (行 163)
-`stderr_full TEXT` (行 164)
-`parsed_output TEXT` (行 165)
-`final_output TEXT` (行 166)
**验证**:
```bash
sed -n '162,166p' ccw/src/tools/cli-history-store.ts
# 输出: 所有 5 列定义已确认
```
### 2. 重构迁移逻辑 - COMPLETED
**文件**: `ccw/src/tools/cli-history-store.ts:331-361`
将逐个迁移(每列一条日志)改为批量迁移(单条汇总日志):
```typescript
// 改进前: 5 条独立的 console.log 调用
if (!hasCached) {
console.log('[CLI History] Migrating database: adding cached column...');
// ...
}
if (!hasStdoutFull) {
console.log('[CLI History] Migrating database: adding stdout_full column...');
// ...
}
// ... 重复 3 次
// 改进后: 1 条汇总日志
const missingTurnsColumns: string[] = [];
for (const [col, def] of Object.entries(turnsColumnDefs)) {
if (!turnsColumns.has(col)) {
missingTurnsColumns.push(col);
}
}
if (missingTurnsColumns.length > 0) {
console.log(`[CLI History] Migrating turns table: adding ${missingTurnsColumns.length} columns...`);
// ...
}
```
**关键改进**:
- 使用 Set 高效查询列名
- 集中定义列配置 (`turnsColumnDefs`)
- 条件输出:仅在有迁移时显示一条汇总日志
**验证**:
```bash
sed -n '353,361p' ccw/src/tools/cli-history-store.ts
# 输出: 批量迁移逻辑已确认
```
### 3. memory-store.ts 评估 - COMPLETED
**文件**: `ccw/src/core/memory-store.ts`
**结论**: **无需修复**
原因:
- 表结构完整,所有列在 `initDatabase()` 中已定义
- 迁移逻辑清晰,仅处理 2 个后续添加的列
- 无类似的批量列缺失问题
## 📊 效果对比
| 指标 | 修复前 | 修复后 | 改进 |
|------|--------|--------|------|
| **新安装日志数** | 5 条 | 0 条 | -100% |
| **旧库升级日志数** | 每次 5 条 | 首次 1 条 | -80% |
| **后续启动日志** | 每次 5 条 | 静默 | -100% |
| **表结构完整性** | 运行时创建 | 创建时完整 | ✓ |
## 🧪 测试验证
### 测试脚本执行
```bash
node test-cli-history-migration.js
```
### 测试结果
```
✓ Test 1: New database creation - 所有列已在创建时定义
✓ Test 2: Subsequent initialization - 后续初始化静默
✓ Test 3: Column verification - 所有 16 列已验证
✓ All required columns present: id, conversation_id, turn_number,
timestamp, prompt, duration_ms, status, exit_code, stdout, stderr,
truncated, cached, stdout_full, stderr_full, parsed_output, final_output
```
## 📁 文件变更
### 修改的文件
```
ccw/src/tools/cli-history-store.ts
├── 149-169: 添加 5 列到 CREATE TABLE turns
└── 331-361: 重构迁移逻辑为批量处理
```
### 无需修改的文件
```
ccw/src/core/memory-store.ts (表结构完整)
```
## 🔍 根本原因分析
**原问题根源**:
1. `turns` 表在 `initSchema()` 中缺少 5 个列定义
2. 新数据库创建时表结构不完整
3. 每次实例化都执行 `migrateSchema()` 检查
4. CLI 每次作为新进程运行,单例缓存失效
5. 逐个迁移导致 5 条重复日志
**修复策略**:
1. ✅ 在 initSchema() 中添加完整列定义
2. ✅ 实现批量迁移逻辑
3. ✅ 条件输出:仅在必要时显示汇总日志
## 🎯 后续行动
### 即时验证
```bash
# 1. 编译验证
npm run build
# 2. 集成测试
npm test -- --grep "cli-history"
# 3. 手动测试
rm -rf ~/.ccw/test-project
ccw cli -p "test query" --tool gemini --mode analysis
# 预期: 无迁移日志输出
```
### 长期监控
- 监控 CLI 执行日志输出,确认无重复迁移日志
- 定期审查新增列的使用情况
- 保持迁移逻辑与表结构定义同步
## 📚 相关文档
- `MIGRATION_FIX_SUMMARY.md` - 详细实现总结
- `ccw/src/tools/cli-history-store.ts` - 源代码实现
## ✨ 总结
**所有计划项目已完成**
- 新数据库创建时表结构完整
- 旧数据库升级时日志输出优化
- 批量迁移策略有效降低日志噪声
- 向后兼容性保持完好
- 代码质量和可维护性得到提升
**预期影响**: CLI 执行时将不再输出重复的数据库迁移日志,提升用户体验。

View File

@@ -0,0 +1,310 @@
# CodexLens MCP Integration - Implementation Summary
> Implementation Date: 2026-01-19
> Project: CCW Dashboard + CodexLens MCP
## 实现概览
成功完成 codex-lens LSP 功能分析及其 MCP 端点设计,并在 CCW view 中实现工具选择功能。
---
## ✅ 已完成任务
### 1. **Gemini 分析 - CodexLens LSP 功能** ✓
**输出文件**`D:\Claude_dms3\codex-lens\docs\MCP_ENDPOINT_DESIGN.md`
**分析结果**
- **4 个核心 MCP 工具**已规划:
1. `code.symbol.search` - 符号搜索(对应 `workspace/symbol``textDocument/completion`
2. `code.symbol.findDefinition` - 查找定义(对应 `textDocument/definition`
3. `code.symbol.findReferences` - 查找引用(对应 `textDocument/references`
4. `code.symbol.getHoverInfo` - 悬停信息(对应 `textDocument/hover`
**技术细节**
- 后端实现基于 `GlobalSymbolIndex``ChainSearchEngine`
- 完整的 MCP schema 定义(参数、返回值、使用场景)
- 命名规范:`code.symbol.<operation>`
---
### 2. **实现 Multi-Select 字段类型** ✓
**修改文件**`ccw/src/templates/dashboard-js/components/mcp-manager.js`
**核心实现**
#### 字段渲染line 1506-1529
```javascript
${field.type === 'multi-select' ? `
<div id="wizard-field-${field.key}" class="space-y-2 p-2 bg-muted/30 border border-border rounded-lg max-h-48 overflow-y-auto">
${(field.options || []).map(opt => {
const isChecked = (field.default || []).includes(opt.value);
return `
<label class="flex items-center gap-2 p-2 rounded-md hover:bg-accent cursor-pointer transition-colors">
<input type="checkbox"
class="wizard-multi-select-${field.key} rounded border-border text-primary focus:ring-primary"
value="${escapeHtml(opt.value)}"
${isChecked ? 'checked' : ''}>
<span class="text-sm text-foreground">${escapeHtml(opt.label)}</span>
${opt.desc ? `<span class="text-xs text-muted-foreground ml-auto">${escapeHtml(opt.desc)}</span>` : ''}
</label>
`;
}).join('')}
</div>
` : `
<input type="${field.type || 'text'}" ...>
`}
```
#### 值收集line 1637-1661
```javascript
if (field.type === 'multi-select') {
const checkboxes = document.querySelectorAll(`.wizard-multi-select-${field.key}:checked`);
const selectedValues = Array.from(checkboxes).map(cb => cb.value);
if (field.required && selectedValues.length === 0) {
showRefreshToast(`${t(field.labelKey)} - ${t('mcp.wizard.selectAtLeastOne')}`, 'error');
hasError = true;
break;
}
values[field.key] = selectedValues;
}
```
**特性**
- ✅ 复选框列表 UI
- ✅ 默认值预选
- ✅ 必填验证
- ✅ 悬停高亮效果
- ✅ 滚动容器max-height: 48
---
### 3. **添加 CodexLens 到推荐 MCP 列表** ✓
**修改位置**`ccw/src/templates/dashboard-js/components/mcp-manager.js` (line 1430-1457)
**MCP 定义**
```javascript
{
id: 'codex-lens-tools',
nameKey: 'mcp.codexLens.name',
descKey: 'mcp.codexLens.desc',
icon: 'code-2',
category: 'code-intelligence',
fields: [
{
key: 'tools',
labelKey: 'mcp.codexLens.field.tools',
type: 'multi-select', // 使用新的 multi-select 类型
options: [
{ value: 'symbol.search', label: 'Symbol Search', desc: 'Workspace symbol search' },
{ value: 'symbol.findDefinition', label: 'Find Definition', desc: 'Go to definition' },
{ value: 'symbol.findReferences', label: 'Find References', desc: 'Find all references' },
{ value: 'symbol.getHoverInfo', label: 'Hover Information', desc: 'Rich symbol info' }
],
default: ['symbol.search', 'symbol.findDefinition', 'symbol.findReferences'],
required: true,
descKey: 'mcp.codexLens.field.tools.desc'
}
],
buildConfig: (values) => {
const tools = values.tools || [];
const env = { CODEXLENS_ENABLED_TOOLS: tools.join(',') };
return buildCrossPlatformMcpConfig('npx', ['-y', 'codex-lens-mcp'], { env });
}
}
```
**配置生成**
```javascript
// 示例输出
{
command: "cmd", // Windows 自动包装
args: ["/c", "npx", "-y", "codex-lens-mcp"],
env: {
CODEXLENS_ENABLED_TOOLS: "symbol.search,symbol.findDefinition,symbol.findReferences"
}
}
```
---
### 4. **添加 i18n 翻译支持** ✓
**修改文件**`ccw/src/templates/dashboard-js/i18n.js`
#### 英文翻译line 959-963
```javascript
'mcp.codexLens.name': 'CodexLens Tools',
'mcp.codexLens.desc': 'Code intelligence tools for symbol search, navigation, and reference finding',
'mcp.codexLens.field.tools': 'Enabled Tools',
'mcp.codexLens.field.tools.desc': 'Select the code intelligence tools to enable for this MCP server',
'mcp.wizard.selectAtLeastOne': 'Please select at least one option',
```
#### 中文翻译line 3286-3290
```javascript
'mcp.codexLens.name': 'CodexLens 工具',
'mcp.codexLens.desc': '代码智能工具,提供符号搜索、代码导航和引用查找功能',
'mcp.codexLens.field.tools': '启用的工具',
'mcp.codexLens.field.tools.desc': '选择要启用的代码智能工具',
'mcp.wizard.selectAtLeastOne': '请至少选择一个选项',
```
---
## 📋 使用流程
### 用户操作步骤
1. **打开 CCW Dashboard**
```bash
ccw view
```
2. **导航到 MCP Manager**
- 点击侧边栏 "MCP Servers" → "Manage"
3. **安装 CodexLens MCP**
- 滚动到 "Recommended MCP" 部分
- 找到 "CodexLens Tools" 卡片
- 点击 "Install" 按钮
4. **配置工具**
- 在弹出的安装向导中,看到 **4 个工具选项**(带复选框)
- 默认已选中 3 个:
- ☑ Symbol Search
- ☑ Find Definition
- ☑ Find References
- ☐ Hover Information
- 可以勾选/取消勾选任意工具
5. **选择安装目标**
- **Project**:项目级(`.mcp.json`
- **Global**:全局级(`~/.claude.json`)默认选中
- **Codex**Codex 全局(`~/.codex/config.toml`
6. **确认安装**
- 点击 "Install" 按钮
- 等待安装完成提示
### 生成的配置示例
**Claude 配置(`.claude.json` 或 `.mcp.json`**
```json
{
"mcpServers": {
"codex-lens-tools": {
"command": "cmd",
"args": ["/c", "npx", "-y", "codex-lens-mcp"],
"env": {
"CODEXLENS_ENABLED_TOOLS": "symbol.search,symbol.findDefinition,symbol.findReferences"
}
}
}
}
```
**Codex 配置(`~/.codex/config.toml`**
```toml
[mcp_servers.codex-lens-tools]
command = "npx"
args = ["-y", "codex-lens-mcp"]
env = { CODEXLENS_ENABLED_TOOLS = "symbol.search,symbol.findDefinition,symbol.findReferences" }
enabled = true
```
---
## 🎯 技术亮点
### 1. **跨平台兼容性**
- 使用 `buildCrossPlatformMcpConfig` 自动处理 Windows `cmd /c` 包装
- 支持 Claude、Codex 两种配置格式
### 2. **动态工具控制**
- 通过环境变量 `CODEXLENS_ENABLED_TOOLS` 控制启用的工具
- 前端 UI 实时更新配置
### 3. **可扩展字段类型**
- 新增 `multi-select` 字段类型
- 保持向后兼容(`text`, `password` 等)
### 4. **国际化支持**
- 完整的中英文翻译
- UI 文本动态切换
---
## 📁 修改文件清单
| 文件路径 | 修改内容 | 行数 |
|---------|---------|------|
| `codex-lens/docs/MCP_ENDPOINT_DESIGN.md` | **新增** - MCP 端点设计文档 | 262 |
| `ccw/src/templates/dashboard-js/components/mcp-manager.js` | 字段渲染 multi-select 支持 | 1506-1529 |
| `ccw/src/templates/dashboard-js/components/mcp-manager.js` | 值收集 multi-select 处理 | 1637-1661 |
| `ccw/src/templates/dashboard-js/components/mcp-manager.js` | 添加 codex-lens-tools MCP 定义 | 1430-1457 |
| `ccw/src/templates/dashboard-js/i18n.js` | 添加英文翻译5 个键) | 959-963 |
| `ccw/src/templates/dashboard-js/i18n.js` | 添加中文翻译5 个键) | 3286-3290 |
---
## 🚀 后续工作
### 待实现功能CodexLens MCP Server
1. **实现 MCP Server**
- 创建 `codex-lens-mcp` npm 包
- 实现 4 个 MCP 工具的 handler
- 环境变量解析:`CODEXLENS_ENABLED_TOOLS`
2. **测试**
- 单元测试MCP 工具实现
- 集成测试:与 CCW Dashboard 集成
- E2E 测试:完整安装流程
3. **文档**
- 用户手册:如何使用 CodexLens MCP
- API 文档MCP 工具详细说明
- 故障排除指南
### 可选增强
- **实时索引进度**:显示代码索引状态
- **更多工具**`code.symbol.getDocumentSymbols`
- **过滤器**:按文件类型/语言过滤符号
- **性能监控**:工具调用延迟统计
---
## 📊 性能指标
| 指标 | 值 |
|------|-----|
| Gemini 分析时间 | 67.6s |
| 新增代码行数 | ~150 lines |
| 支持的工具数 | 4 tools |
| i18n 翻译键 | 10 keys (5 en + 5 zh) |
| 修改文件数 | 3 files |
---
## 🎉 总结
成功完成 codex-lens LSP 到 MCP 的完整设计和 CCW Dashboard 集成:
1.**完整的 MCP 端点设计**4 个工具,详细 schema
2.**可复用的 multi-select 字段类型**
3.**CodexLens MCP 集成到推荐列表**
4.**完整的中英文国际化支持**
用户现在可以在 CCW Dashboard 中一键安装 CodexLens MCP 服务,并通过可视化界面选择启用的代码智能工具。
---
Generated: 2026-01-19
Status: ✅ Ready for Testing

View File

@@ -0,0 +1,122 @@
# Package Name Fix Summary
## 问题描述
用户在使用 `ccw view` 界面安装 CodexLens 时遇到错误:
```
Error: Failed to install codexlens: Using Python 3.12.3 environment at: .codexlens/venv
× No solution found when resolving dependencies:
╰─▶ Because there are no versions of codexlens[semantic] and you require codexlens[semantic], we can conclude that your requirements are unsatisfiable.
```
## 根本原因
1. **包名不一致**pyproject.toml 中定义的包名是 `codex-lens`(带连字符),但代码中尝试安装 `codexlens`(没有连字符)
2. **包未发布到 PyPI**`codex-lens` 是本地开发包,没有发布到 PyPI只能通过本地路径安装
3. **本地路径查找逻辑问题**`findLocalPackagePath()` 函数在非开发环境(从 node_modules 运行)时会提前返回 null导致找不到本地路径
## 修复内容
### 1. 核心文件修复 (ccw/src/tools/codex-lens.ts)
#### 1.1 修改 `findLocalPackagePath()` 函数
- **移除** `isDevEnvironment()` 早期返回逻辑
- **添加** 更多本地路径搜索位置(包括父目录)
- **总是** 尝试查找本地路径,即使从 node_modules 运行
#### 1.2 修改 `bootstrapWithUv()` 函数
- **移除** PyPI 安装的 fallback 逻辑
- **改为** 找不到本地路径时直接返回错误,提供清晰的修复指导
#### 1.3 修改 `installSemanticWithUv()` 函数
- **移除** PyPI 安装的 fallback 逻辑
- **改为** 找不到本地路径时直接返回错误
#### 1.4 修改 `bootstrapVenv()` 函数pip fallback
- **移除** PyPI 安装的 fallback 逻辑
- **改为** 找不到本地路径时抛出错误
#### 1.5 修复包名引用
- 将所有 `codexlens` 更改为 `codex-lens`3 处)
### 2. 文档和脚本修复
修复以下文件中的包名引用(`codexlens``codex-lens`
-`ccw/scripts/memory_embedder.py`
-`ccw/scripts/README-memory-embedder.md`
-`ccw/scripts/QUICK-REFERENCE.md`
-`ccw/scripts/IMPLEMENTATION-SUMMARY.md`
## 修复后的行为
### 安装流程
1. **查找本地路径**
- 检查 `process.cwd()/codex-lens`
- 检查 `__dirname/../../../codex-lens`(项目根目录)
- 检查 `homedir()/codex-lens`
- 检查 `parent(cwd)/codex-lens`(新增)
2. **本地安装**(找到路径):
```bash
uv pip install -e /path/to/codex-lens[semantic]
```
3. **失败并提示**(找不到路径):
```
Cannot find codex-lens directory for local installation.
codex-lens is a local development package (not published to PyPI) and must be installed from local files.
To fix this:
1. Ensure the 'codex-lens' directory exists in your project root
2. Verify pyproject.toml exists in codex-lens directory
3. Run ccw from the correct working directory
4. Or manually install: cd codex-lens && pip install -e .[semantic]
```
## 验证步骤
1. 确认 `codex-lens` 目录存在于项目根目录
2. 确认 `codex-lens/pyproject.toml` 存在
3. 从项目根目录运行 ccw
4. 尝试安装 CodexLens semantic 依赖
## 正确的手动安装方式
```bash
# 从项目根目录
cd D:\Claude_dms3\codex-lens
pip install -e .[semantic]
# 或者使用绝对路径
pip install -e D:\Claude_dms3\codex-lens[semantic]
# GPU 加速CUDA
pip install -e .[semantic-gpu]
# GPU 加速DirectMLWindows
pip install -e .[semantic-directml]
```
## 注意事项
- **不要** 使用 `pip install codex-lens[semantic]`(会失败,包未发布到 PyPI
- **必须** 使用 `-e` 参数进行 editable 安装
- **必须** 从正确的工作目录运行(包含 codex-lens 目录的目录)
## 影响范围
- ✅ ccw view 界面安装
- ✅ 命令行 UV 安装
- ✅ 命令行 pip fallback 安装
- ✅ 文档和脚本中的安装说明
## 测试建议
1. 从全局安装的 ccw 运行npm install -g
2. 从本地开发目录运行npm link
3. 从不同的工作目录运行
4. 测试所有三种 GPU 模式cpu, cuda, directml

330
archive/benchmark_search.py Normal file
View File

@@ -0,0 +1,330 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
Multi-dimensional search benchmark: Compare search methods across multiple queries.
Dimensions:
1. Speed (time_ms)
2. Result Quality (relevance score distribution)
3. Ranking Stability (position changes vs baseline)
4. Coverage (unique files found)
"""
import subprocess
import sys
import os
import re
import json
import time
import io
# Fix Windows console encoding
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8', errors='replace')
sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding='utf-8', errors='replace')
from dataclasses import dataclass, field
from typing import List, Dict, Any, Optional
from pathlib import Path
os.chdir(r"D:\dongdiankaifa9\hydro_generator_module")
# Test queries covering different search intents
TEST_QUERIES = [
("热网络计算", "Chinese: thermal network calculation"),
("ThermalResistance", "Code identifier"),
("boundary condition handling", "Natural language"),
("stator slot cooling", "Domain-specific"),
("def build", "Code pattern"),
]
# Search methods to compare
SEARCH_METHODS = [
("hybrid", None, "Hybrid (FTS+Vector RRF)"),
("vector", None, "Pure Vector"),
("cascade", "binary", "Cascade Binary"),
("cascade", "hybrid", "Cascade Hybrid (Cross-Encoder)"),
]
ansi_escape = re.compile(r'\x1b\[[0-9;]*m')
@dataclass
class SearchResult:
method: str
strategy: Optional[str]
query: str
time_ms: float
count: int
top_files: List[str]
top_scores: List[float]
success: bool
error: Optional[str] = None
def run_search(query: str, method: str, strategy: Optional[str] = None, limit: int = 10) -> SearchResult:
"""Run a search and return structured result."""
cmd = [sys.executable, "-m", "codexlens", "search", query,
"--method", method, "--limit", str(limit), "--json"]
if strategy and method == "cascade":
cmd.extend(["--cascade-strategy", strategy])
start = time.perf_counter()
result = subprocess.run(cmd, capture_output=True, text=True, encoding="utf-8")
elapsed = (time.perf_counter() - start) * 1000
# Strip ANSI codes
output = ansi_escape.sub('', result.stdout + result.stderr)
# Parse JSON
start_idx = output.find('{')
if start_idx < 0:
return SearchResult(
method=method, strategy=strategy, query=query,
time_ms=elapsed, count=0, top_files=[], top_scores=[],
success=False, error="No JSON found"
)
# Parse nested JSON properly
in_string = False
escaped = False
depth = 0
end_idx = start_idx
for i, c in enumerate(output[start_idx:]):
if escaped:
escaped = False
continue
if c == '\\':
escaped = True
continue
if c == '"' and not escaped:
in_string = not in_string
continue
if not in_string:
if c == '{':
depth += 1
elif c == '}':
depth -= 1
if depth == 0:
end_idx = start_idx + i + 1
break
try:
data = json.loads(output[start_idx:end_idx])
if not data.get("success"):
return SearchResult(
method=method, strategy=strategy, query=query,
time_ms=elapsed, count=0, top_files=[], top_scores=[],
success=False, error=data.get("error", "Unknown error")
)
results = data.get("result", {}).get("results", [])[:limit]
stats = data.get("result", {}).get("stats", {})
top_files = [os.path.basename(r.get("path", "")) for r in results]
top_scores = [r.get("score", 0) for r in results]
return SearchResult(
method=method, strategy=strategy, query=query,
time_ms=stats.get("time_ms", elapsed),
count=len(results),
top_files=top_files,
top_scores=top_scores,
success=True
)
except Exception as e:
return SearchResult(
method=method, strategy=strategy, query=query,
time_ms=elapsed, count=0, top_files=[], top_scores=[],
success=False, error=str(e)
)
def calculate_ranking_similarity(baseline: List[str], candidate: List[str]) -> float:
"""Calculate ranking similarity using normalized DCG."""
if not baseline or not candidate:
return 0.0
# Simple overlap-based similarity with position weighting
score = 0.0
for i, file in enumerate(candidate[:10]):
if file in baseline:
baseline_pos = baseline.index(file)
# Weight by position similarity
pos_diff = abs(i - baseline_pos)
score += 1.0 / (1 + pos_diff * 0.2)
return score / min(len(baseline), 10)
def print_divider(char="=", width=80):
print(char * width)
def main():
print_divider()
print("🔬 CodexLens 搜索方法多维度对比测试")
print_divider()
print(f"测试目录: {os.getcwd()}")
print(f"测试查询数: {len(TEST_QUERIES)}")
print(f"对比方法数: {len(SEARCH_METHODS)}")
print_divider()
all_results: Dict[str, Dict[str, SearchResult]] = {}
# Run all tests
for query, query_desc in TEST_QUERIES:
print(f"\n📝 查询: \"{query}\" ({query_desc})")
print("-" * 60)
all_results[query] = {}
for method, strategy, method_name in SEARCH_METHODS:
method_key = f"{method}_{strategy}" if strategy else method
print(f"{method_name}...", end=" ", flush=True)
result = run_search(query, method, strategy)
all_results[query][method_key] = result
if result.success:
print(f"{result.time_ms:.0f}ms, {result.count} results")
else:
print(f"{result.error}")
# === Analysis ===
print("\n")
print_divider()
print("📊 综合分析报告")
print_divider()
# 1. Speed Comparison
print("\n### 1⃣ 速度对比 (平均耗时 ms)")
print("-" * 60)
method_times: Dict[str, List[float]] = {f"{m}_{s}" if s else m: [] for m, s, _ in SEARCH_METHODS}
for query in all_results:
for method_key, result in all_results[query].items():
if result.success:
method_times[method_key].append(result.time_ms)
speed_ranking = []
for method, strategy, method_name in SEARCH_METHODS:
method_key = f"{method}_{strategy}" if strategy else method
times = method_times[method_key]
if times:
avg_time = sum(times) / len(times)
min_time = min(times)
max_time = max(times)
speed_ranking.append((method_name, avg_time, min_time, max_time))
speed_ranking.sort(key=lambda x: x[1])
print(f"{'方法':<35} {'平均':>10} {'最快':>10} {'最慢':>10}")
print("-" * 65)
for method_name, avg, min_t, max_t in speed_ranking:
print(f"{method_name:<35} {avg:>10.0f} {min_t:>10.0f} {max_t:>10.0f}")
# Speed winner
if speed_ranking:
fastest = speed_ranking[0]
slowest = speed_ranking[-1]
speedup = slowest[1] / fastest[1] if fastest[1] > 0 else 0
print(f"\n🏆 最快: {fastest[0]} (比最慢快 {speedup:.1f}x)")
# 2. Score Distribution
print("\n### 2⃣ 相关性得分分布 (Top-10 平均分)")
print("-" * 60)
method_scores: Dict[str, List[float]] = {f"{m}_{s}" if s else m: [] for m, s, _ in SEARCH_METHODS}
for query in all_results:
for method_key, result in all_results[query].items():
if result.success and result.top_scores:
avg_score = sum(result.top_scores) / len(result.top_scores)
method_scores[method_key].append(avg_score)
print(f"{'方法':<35} {'平均分':>12} {'分布范围':>20}")
print("-" * 67)
for method, strategy, method_name in SEARCH_METHODS:
method_key = f"{method}_{strategy}" if strategy else method
scores = method_scores[method_key]
if scores:
avg_score = sum(scores) / len(scores)
min_score = min(scores)
max_score = max(scores)
print(f"{method_name:<35} {avg_score:>12.4f} {min_score:.4f} - {max_score:.4f}")
# 3. Ranking Stability (vs Hybrid as baseline)
print("\n### 3⃣ 排名稳定性 (与 Hybrid 基线对比)")
print("-" * 60)
print(f"{'方法':<35} {'相似度':>12} {'说明':>20}")
print("-" * 67)
for method, strategy, method_name in SEARCH_METHODS:
method_key = f"{method}_{strategy}" if strategy else method
if method_key == "hybrid":
print(f"{method_name:<35} {'1.0000':>12} {'(基线)':>20}")
continue
similarities = []
for query in all_results:
baseline = all_results[query].get("hybrid")
candidate = all_results[query].get(method_key)
if baseline and candidate and baseline.success and candidate.success:
sim = calculate_ranking_similarity(baseline.top_files, candidate.top_files)
similarities.append(sim)
if similarities:
avg_sim = sum(similarities) / len(similarities)
diff_level = "高度一致" if avg_sim > 0.7 else "中度差异" if avg_sim > 0.4 else "显著差异"
print(f"{method_name:<35} {avg_sim:>12.4f} {diff_level:>20}")
# 4. Detailed Query Comparison
print("\n### 4⃣ 各查询详细对比")
print("-" * 60)
for query, query_desc in TEST_QUERIES:
print(f"\n📌 \"{query}\" ({query_desc})")
print()
# Show top-3 results for each method
for method, strategy, method_name in SEARCH_METHODS:
method_key = f"{method}_{strategy}" if strategy else method
result = all_results[query].get(method_key)
if result and result.success:
print(f" [{method_name}] {result.time_ms:.0f}ms")
for i, (file, score) in enumerate(zip(result.top_files[:3], result.top_scores[:3]), 1):
print(f" {i}. {file:<40} {score:.4f}")
else:
print(f" [{method_name}] 失败: {result.error if result else 'N/A'}")
print()
# 5. Summary
print_divider()
print("📋 总结")
print_divider()
print("""
┌─────────────────────────────────────────────────────────────────────┐
│ 方法特点总结 │
├─────────────────────────────────────────────────────────────────────┤
│ Hybrid (FTS+Vector) │ 基线方法,综合质量好,速度中等 │
│ Pure Vector │ 语义理解强,适合自然语言查询 │
│ Cascade Binary │ 速度最快,适合大代码库快速检索 │
│ Cascade Hybrid │ Cross-Encoder 精排,质量最高但速度较慢 │
└─────────────────────────────────────────────────────────────────────┘
推荐使用场景:
• 日常搜索: hybrid (默认)
• 大代码库快速检索: cascade --cascade-strategy binary
• 追求最高质量: cascade --cascade-strategy hybrid
• 自然语言查询: vector
""")
print_divider()
if __name__ == "__main__":
main()

62
archive/codex_prompt.md Normal file
View File

@@ -0,0 +1,62 @@
# Custom Prompts
<DocsTip>
Custom prompts are deprecated. Use [skills](https://developers.openai.com/codex/skills) for reusable
instructions that Codex can invoke explicitly or implicitly.
</DocsTip>
Custom prompts (deprecated) let you turn Markdown files into reusable prompts that you can invoke as slash commands in both the Codex CLI and the Codex IDE extension.
Custom prompts require explicit invocation and live in your local Codex home directory (for example, `~/.codex`), so they're not shared through your repository. If you want to share a prompt (or want Codex to implicitly invoke it), [use skills](https://developers.openai.com/codex/skills).
1. Create the prompts directory:
```bash
mkdir -p ~/.codex/prompts
```
2. Create `~/.codex/prompts/draftpr.md` with reusable guidance:
```markdown
---
description: Prep a branch, commit, and open a draft PR
argument-hint: [FILES=<paths>] [PR_TITLE="<title>"]
---
Create a branch named `dev/<feature_name>` for this work.
If files are specified, stage them first: $FILES.
Commit the staged changes with a clear message.
Open a draft PR on the same branch. Use $PR_TITLE when supplied; otherwise write a concise summary yourself.
```
3. Restart Codex so it loads the new prompt (restart your CLI session, and reload the IDE extension if you are using it).
Expected: Typing `/prompts:draftpr` in the slash command menu shows your custom command with the description from the front matter and hints that files and a PR title are optional.
## Add metadata and arguments
Codex reads prompt metadata and resolves placeholders the next time the session starts.
- **Description:** Shown under the command name in the popup. Set it in YAML front matter as `description:`.
- **Argument hint:** Document expected parameters with `argument-hint: KEY=<value>`.
- **Positional placeholders:** `$1` through `$9` expand from space-separated arguments you provide after the command. `$ARGUMENTS` includes them all.
- **Named placeholders:** Use uppercase names like `$FILE` or `$TICKET_ID` and supply values as `KEY=value`. Quote values with spaces (for example, `FOCUS="loading state"`).
- **Literal dollar signs:** Write `$$` to emit a single `$` in the expanded prompt.
After editing prompt files, restart Codex or open a new chat so the updates load. Codex ignores non-Markdown files in the prompts directory.
## Invoke and manage custom commands
1. In Codex (CLI or IDE extension), type `/` to open the slash command menu.
2. Enter `prompts:` or the prompt name, for example `/prompts:draftpr`.
3. Supply required arguments:
```text
/prompts:draftpr FILES="src/pages/index.astro src/lib/api.ts" PR_TITLE="Add hero animation"
```
4. Press Enter to send the expanded instructions (skip either argument when you don't need it).
Expected: Codex expands the content of `draftpr.md`, replacing placeholders with the arguments you supplied, then sends the result as a message.
Manage prompts by editing or deleting files under `~/.codex/prompts/`. Codex scans only the top-level Markdown files in that folder, so place each custom prompt directly under `~/.codex/prompts/` rather than in subdirectories.

View File

@@ -0,0 +1,77 @@
#!/usr/bin/env python
"""Compare search results with and without reranker."""
import json
import subprocess
import sys
import os
os.chdir(r"D:\dongdiankaifa9\hydro_generator_module")
query = "热网络计算"
def run_search(method: str) -> dict:
"""Run search and return parsed JSON result."""
cmd = [sys.executable, "-m", "codexlens", "search", query, "--method", method, "--limit", "10", "--json"]
result = subprocess.run(cmd, capture_output=True, text=True, encoding="utf-8")
# Find JSON in output (skip debug lines)
for line in result.stdout.split("\n"):
if line.strip().startswith("{"):
try:
return json.loads(line)
except:
pass
# Try to find JSON object in stderr
output = result.stdout + result.stderr
start = output.find('{"success"')
if start >= 0:
# Find matching closing brace
depth = 0
for i, c in enumerate(output[start:]):
if c == '{':
depth += 1
elif c == '}':
depth -= 1
if depth == 0:
try:
return json.loads(output[start:start+i+1])
except:
pass
break
return {"success": False, "error": "Failed to parse JSON"}
print("=" * 60)
print("搜索对比: 有无 Reranker 效果")
print("查询:", query)
print("=" * 60)
# Run hybrid search (no reranker)
print("\n[1] Hybrid 搜索 (无 Reranker)")
print("-" * 40)
hybrid_result = run_search("hybrid")
if hybrid_result.get("success"):
results = hybrid_result.get("result", {}).get("results", [])[:10]
for i, r in enumerate(results, 1):
path = r.get("path", "").split("\\")[-1]
score = r.get("score", 0)
print(f"{i:2}. {path[:45]:<45} score={score:.4f}")
else:
print("搜索失败:", hybrid_result.get("error"))
# Run cascade search (with reranker)
print("\n[2] Cascade 搜索 (使用 Reranker)")
print("-" * 40)
cascade_result = run_search("cascade")
if cascade_result.get("success"):
results = cascade_result.get("result", {}).get("results", [])[:10]
for i, r in enumerate(results, 1):
path = r.get("path", "").split("\\")[-1]
score = r.get("score", 0)
print(f"{i:2}. {path[:45]:<45} score={score:.4f}")
else:
print("搜索失败:", cascade_result.get("error"))
print("\n" + "=" * 60)
print("对比说明:")
print("- Hybrid: FTS + Vector 融合,无二次重排序")
print("- Cascade: Vector 粗筛 + Reranker API 精排")
print("=" * 60)

119
archive/compare_search.py Normal file
View File

@@ -0,0 +1,119 @@
#!/usr/bin/env python
"""Compare search results: Hybrid vs Cascade with Reranker."""
import subprocess
import sys
import os
import re
import json
os.chdir(r"D:\dongdiankaifa9\hydro_generator_module")
query = "热网络计算"
ansi_escape = re.compile(r'\x1b\[[0-9;]*m')
def run_search(method: str) -> dict:
"""Run search and return parsed result dict."""
cmd = [sys.executable, "-m", "codexlens", "search", query,
"--method", method, "--limit", "10", "--json"]
result = subprocess.run(cmd, capture_output=True, text=True, encoding="utf-8")
# Strip ANSI codes
output = ansi_escape.sub('', result.stdout + result.stderr)
# Find and parse JSON (properly handle nested structures)
start = output.find('{')
if start < 0:
return {"success": False, "error": "No JSON found"}
# Count braces properly, handling strings
in_string = False
escaped = False
depth = 0
end_idx = start
for i, c in enumerate(output[start:]):
if escaped:
escaped = False
continue
if c == '\\':
escaped = True
continue
if c == '"' and not escaped:
in_string = not in_string
continue
if not in_string:
if c == '{':
depth += 1
elif c == '}':
depth -= 1
if depth == 0:
end_idx = start + i + 1
break
try:
return json.loads(output[start:end_idx])
except Exception as e:
return {"success": False, "error": str(e)}
print("=" * 75)
print(f"搜索对比: Hybrid vs Cascade")
print(f"查询: {query}")
print("=" * 75)
# Hybrid search (no cross-encoder reranking)
print("\n[1] Hybrid 搜索 (无 Cross-Encoder Reranker):")
print("-" * 75)
hybrid_result = run_search("hybrid")
hybrid_files = []
if hybrid_result.get("success"):
results = hybrid_result.get("result", {}).get("results", [])[:10]
for i, r in enumerate(results, 1):
name = os.path.basename(r.get("path", ""))
score = r.get("score", 0)
hybrid_files.append(name)
print(f"{i:2}. {name:<45} score={score:.4f}")
else:
print("搜索失败:", hybrid_result.get("error"))
# Cascade search (with cross-encoder reranking when strategy=hybrid)
print("\n[2] Cascade 搜索 (使用 Cross-Encoder Reranker):")
print("-" * 75)
cascade_result = run_search("cascade")
cascade_files = []
if cascade_result.get("success"):
results = cascade_result.get("result", {}).get("results", [])[:10]
for i, r in enumerate(results, 1):
name = os.path.basename(r.get("path", ""))
score = r.get("score", 0)
cascade_files.append(name)
print(f"{i:2}. {name:<45} score={score:.4f}")
else:
print("搜索失败:", cascade_result.get("error"))
# Compare ranking changes
print("\n[3] 排名变化分析:")
print("-" * 75)
changes = []
for i, name in enumerate(cascade_files):
if name in hybrid_files:
old_pos = hybrid_files.index(name) + 1
new_pos = i + 1
if old_pos != new_pos:
direction = "" if new_pos < old_pos else ""
changes.append(f" {name}: #{old_pos} → #{new_pos} {direction}")
else:
changes.append(f" {name}: NEW (不在 Hybrid 前10)")
if changes:
print("Reranker 排序变化:")
for c in changes:
print(c)
else:
print("排序相同 (无变化)")
print("\n" + "=" * 75)
print("配置说明:")
print("- Hybrid: FTS + Vector 融合 (无二次精排)")
print("- Cascade: 粗筛 + Cross-Encoder Reranker 精排")
print("- Reranker: Qwen/Qwen3-Reranker-8B via SiliconFlow API")
print("=" * 75)

View File

@@ -0,0 +1,2 @@
import './ccw/tests/integration/cli-executor/gemini-workflow.test.ts';

View File

@@ -0,0 +1,2 @@
import './ccw/tests/integration/cli-executor/multi-tool-workflow.test.ts';

View File

@@ -0,0 +1,2 @@
import './ccw/tests/integration/cli-executor/orchestration.test.ts';

2
archive/refactor_temp.py Normal file
View File

@@ -0,0 +1,2 @@
# Refactor script for GraphAnalyzer
print("Starting refactor...")

View File

@@ -0,0 +1,146 @@
"""Test script to verify embeddings improvements in init and status commands."""
import json
import subprocess
import sys
from pathlib import Path
def run_command(cmd):
"""Run a command and capture output."""
result = subprocess.run(
cmd,
shell=True,
capture_output=True,
text=True,
)
return result.stdout, result.stderr, result.returncode
def test_init_embeddings():
"""Test that init command reports embeddings statistics."""
print("Testing init command with embeddings reporting...")
# Create a temporary test directory
test_dir = Path("test_temp_project")
test_dir.mkdir(exist_ok=True)
# Create a simple Python file
test_file = test_dir / "test.py"
test_file.write_text("""
def hello_world():
print("Hello, World!")
def add_numbers(a, b):
return a + b
""")
# Run init with JSON output
cmd = f"codexlens init {test_dir} --json --no-embeddings"
stdout, stderr, returncode = run_command(cmd)
if returncode != 0:
print(f"❌ Init command failed: {stderr}")
return False
try:
result = json.loads(stdout)
# Check for embeddings field
if "embeddings" not in result.get("result", {}):
print("❌ Missing 'embeddings' field in result")
return False
embeddings = result["result"]["embeddings"]
# Check required fields
required_fields = ["generated", "error"]
for field in required_fields:
if field not in embeddings:
print(f"❌ Missing required field '{field}' in embeddings")
return False
# Since we used --no-embeddings, it should show as not generated
if embeddings["generated"]:
print("❌ Expected embeddings to not be generated with --no-embeddings")
return False
print("✅ Init command embeddings reporting works correctly")
return True
except json.JSONDecodeError as e:
print(f"❌ Failed to parse JSON output: {e}")
print(f"Output: {stdout}")
return False
finally:
# Cleanup
import shutil
if test_dir.exists():
shutil.rmtree(test_dir)
def test_status_embeddings():
"""Test that status command reports embeddings coverage."""
print("\nTesting status command with embeddings coverage...")
# Run status with JSON output
cmd = "codexlens status --json"
stdout, stderr, returncode = run_command(cmd)
if returncode != 0:
print(f"❌ Status command failed: {stderr}")
return False
try:
result = json.loads(stdout)
# Check for features field
if "features" not in result.get("result", {}):
print("❌ Missing 'features' field in result")
return False
features = result["result"]["features"]
# Check that vector_search field exists (may be true or false)
if "vector_search" not in features:
print("❌ Missing 'vector_search' field in features")
return False
# Check if embeddings info is present (optional, depends on whether embeddings exist)
embeddings = result["result"].get("embeddings")
if embeddings:
print(f" Embeddings coverage: {embeddings.get('coverage_percent', 0):.1f}%")
print(f" Files with embeddings: {embeddings.get('files_with_embeddings', 0)}/{embeddings.get('total_files', 0)}")
print(f" Total chunks: {embeddings.get('total_chunks', 0)}")
else:
print(" No embeddings found (this is OK if none were generated)")
print("✅ Status command embeddings reporting works correctly")
return True
except json.JSONDecodeError as e:
print(f"❌ Failed to parse JSON output: {e}")
print(f"Output: {stdout}")
return False
def main():
"""Run all tests."""
print("=== Testing CodexLens Embeddings Improvements ===\n")
results = []
# Test init command
results.append(("Init embeddings reporting", test_init_embeddings()))
# Test status command
results.append(("Status embeddings coverage", test_status_embeddings()))
# Print summary
print("\n=== Test Summary ===")
for name, passed in results:
status = "✅ PASS" if passed else "❌ FAIL"
print(f"{status}: {name}")
# Return exit code
all_passed = all(passed for _, passed in results)
return 0 if all_passed else 1
if __name__ == "__main__":
sys.exit(main())

View File

@@ -0,0 +1 @@
import './ccw/tests/visual/ui-generate-preview.visual.test.ts';

View File

@@ -0,0 +1 @@
import './ccw/tests/visual/ui-instantiate-prototypes.visual.test.ts';

View File

@@ -0,0 +1 @@
import './ccw/tests/visual/helpers/visual-tester.test.ts';