mirror of
https://github.com/catlog22/Claude-Code-Workflow.git
synced 2026-02-12 02:37:45 +08:00
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:
62
archive/1.18.0
Normal file
62
archive/1.18.0
Normal 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
|
||||
167
archive/IMPLEMENTATION_COMPLETION_REPORT.md
Normal file
167
archive/IMPLEMENTATION_COMPLETION_REPORT.md
Normal 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 执行时将不再输出重复的数据库迁移日志,提升用户体验。
|
||||
310
archive/IMPLEMENTATION_SUMMARY.md
Normal file
310
archive/IMPLEMENTATION_SUMMARY.md
Normal 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
|
||||
122
archive/PACKAGE_NAME_FIX_SUMMARY.md
Normal file
122
archive/PACKAGE_NAME_FIX_SUMMARY.md
Normal 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 加速(DirectML,Windows)
|
||||
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
330
archive/benchmark_search.py
Normal 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
62
archive/codex_prompt.md
Normal 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.
|
||||
77
archive/compare_reranker.py
Normal file
77
archive/compare_reranker.py
Normal 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
119
archive/compare_search.py
Normal 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)
|
||||
2
archive/gemini-workflow.test.ts
Normal file
2
archive/gemini-workflow.test.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
import './ccw/tests/integration/cli-executor/gemini-workflow.test.ts';
|
||||
|
||||
2
archive/multi-tool-workflow.test.ts
Normal file
2
archive/multi-tool-workflow.test.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
import './ccw/tests/integration/cli-executor/multi-tool-workflow.test.ts';
|
||||
|
||||
2
archive/orchestration.test.ts
Normal file
2
archive/orchestration.test.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
import './ccw/tests/integration/cli-executor/orchestration.test.ts';
|
||||
|
||||
2
archive/refactor_temp.py
Normal file
2
archive/refactor_temp.py
Normal file
@@ -0,0 +1,2 @@
|
||||
# Refactor script for GraphAnalyzer
|
||||
print("Starting refactor...")
|
||||
146
archive/test_embeddings_improvements.py
Normal file
146
archive/test_embeddings_improvements.py
Normal 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())
|
||||
1
archive/ui-generate-preview.visual.test.ts
Normal file
1
archive/ui-generate-preview.visual.test.ts
Normal file
@@ -0,0 +1 @@
|
||||
import './ccw/tests/visual/ui-generate-preview.visual.test.ts';
|
||||
1
archive/ui-instantiate-prototypes.visual.test.ts
Normal file
1
archive/ui-instantiate-prototypes.visual.test.ts
Normal file
@@ -0,0 +1 @@
|
||||
import './ccw/tests/visual/ui-instantiate-prototypes.visual.test.ts';
|
||||
1
archive/visual-tester.test.ts
Normal file
1
archive/visual-tester.test.ts
Normal file
@@ -0,0 +1 @@
|
||||
import './ccw/tests/visual/helpers/visual-tester.test.ts';
|
||||
Reference in New Issue
Block a user