claude-code-workflow 6.3.16 → 6.3.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/.claude/CLAUDE.md +2 -2
  2. package/.claude/agents/action-planning-agent.md +3 -0
  3. package/.claude/agents/cli-execution-agent.md +2 -0
  4. package/.claude/agents/cli-explore-agent.md +3 -0
  5. package/.claude/agents/cli-lite-planning-agent.md +3 -0
  6. package/.claude/agents/cli-planning-agent.md +3 -0
  7. package/.claude/agents/code-developer.md +3 -0
  8. package/.claude/agents/context-search-agent.md +3 -0
  9. package/.claude/agents/doc-generator.md +3 -0
  10. package/.claude/agents/issue-plan-agent.md +3 -0
  11. package/.claude/agents/issue-queue-agent.md +3 -0
  12. package/.claude/agents/universal-executor.md +3 -0
  13. package/.claude/commands/issue/new.md +323 -309
  14. package/.codex/AGENTS.md +6 -0
  15. package/.codex/prompts/issue-plan.md +46 -1
  16. package/.codex/prompts/issue-queue.md +45 -3
  17. package/ccw/dist/commands/cli.d.ts.map +1 -1
  18. package/ccw/dist/commands/cli.js +106 -14
  19. package/ccw/dist/commands/cli.js.map +1 -1
  20. package/ccw/dist/commands/issue.d.ts.map +1 -1
  21. package/ccw/dist/commands/issue.js +29 -4
  22. package/ccw/dist/commands/issue.js.map +1 -1
  23. package/ccw/dist/core/routes/litellm-api-routes.d.ts.map +1 -1
  24. package/ccw/dist/core/routes/litellm-api-routes.js +8 -3
  25. package/ccw/dist/core/routes/litellm-api-routes.js.map +1 -1
  26. package/ccw/dist/tools/cli-executor.d.ts +5 -0
  27. package/ccw/dist/tools/cli-executor.d.ts.map +1 -1
  28. package/ccw/dist/tools/cli-executor.js +24 -0
  29. package/ccw/dist/tools/cli-executor.js.map +1 -1
  30. package/ccw/dist/tools/codex-lens.d.ts.map +1 -1
  31. package/ccw/dist/tools/codex-lens.js +2 -19
  32. package/ccw/dist/tools/codex-lens.js.map +1 -1
  33. package/ccw/dist/utils/python-utils.d.ts +36 -0
  34. package/ccw/dist/utils/python-utils.d.ts.map +1 -0
  35. package/ccw/dist/utils/python-utils.js +115 -0
  36. package/ccw/dist/utils/python-utils.js.map +1 -0
  37. package/ccw/src/commands/cli.ts +120 -14
  38. package/ccw/src/commands/issue.ts +31 -4
  39. package/ccw/src/core/routes/litellm-api-routes.ts +10 -3
  40. package/ccw/src/templates/dashboard-css/33-cli-stream-viewer.css +8 -12
  41. package/ccw/src/tools/cli-executor.ts +28 -0
  42. package/ccw/src/tools/codex-lens.ts +2 -19
  43. package/ccw/src/utils/python-utils.ts +121 -0
  44. package/codex-lens/src/codexlens/__pycache__/config.cpython-313.pyc +0 -0
  45. package/codex-lens/src/codexlens/__pycache__/entities.cpython-313.pyc +0 -0
  46. package/codex-lens/src/codexlens/indexing/__pycache__/symbol_extractor.cpython-313.pyc +0 -0
  47. package/codex-lens/src/codexlens/parsers/__pycache__/factory.cpython-313.pyc +0 -0
  48. package/codex-lens/src/codexlens/parsers/__pycache__/treesitter_parser.cpython-313.pyc +0 -0
  49. package/codex-lens/src/codexlens/search/__pycache__/chain_search.cpython-313.pyc +0 -0
  50. package/codex-lens/src/codexlens/search/__pycache__/enrichment.cpython-313.pyc +0 -0
  51. package/codex-lens/src/codexlens/search/__pycache__/graph_expander.cpython-313.pyc +0 -0
  52. package/codex-lens/src/codexlens/search/__pycache__/hybrid_search.cpython-313.pyc +0 -0
  53. package/codex-lens/src/codexlens/search/__pycache__/ranking.cpython-313.pyc +0 -0
  54. package/codex-lens/src/codexlens/search/enrichment.py +150 -150
  55. package/codex-lens/src/codexlens/semantic/__pycache__/reranker.cpython-313.pyc +0 -0
  56. package/codex-lens/src/codexlens/storage/__pycache__/dir_index.cpython-313.pyc +0 -0
  57. package/codex-lens/src/codexlens/storage/__pycache__/index_tree.cpython-313.pyc +0 -0
  58. package/codex-lens/src/codexlens/storage/migrations/__pycache__/migration_006_enhance_relationships.cpython-313.pyc +0 -0
  59. package/codex-lens/src/codexlens/storage/migrations/__pycache__/migration_007_add_graph_neighbors.cpython-313.pyc +0 -0
  60. package/codex-lens/src/codexlens/storage/migrations/__pycache__/migration_008_add_merkle_hashes.cpython-313.pyc +0 -0
  61. package/package.json +1 -1
@@ -0,0 +1,121 @@
1
+ /**
2
+ * Python detection and version compatibility utilities
3
+ * Shared module for consistent Python discovery across the application
4
+ */
5
+
6
+ import { execSync } from 'child_process';
7
+
8
+ /**
9
+ * Parse Python version string to major.minor numbers
10
+ * @param versionStr - Version string like "Python 3.11.5"
11
+ * @returns Object with major and minor version numbers, or null if parsing fails
12
+ */
13
+ export function parsePythonVersion(versionStr: string): { major: number; minor: number } | null {
14
+ const match = versionStr.match(/Python\s+(\d+)\.(\d+)/);
15
+ if (match) {
16
+ return { major: parseInt(match[1], 10), minor: parseInt(match[2], 10) };
17
+ }
18
+ return null;
19
+ }
20
+
21
+ /**
22
+ * Check if Python version is compatible with onnxruntime (3.9-3.12)
23
+ * @param major - Major version number
24
+ * @param minor - Minor version number
25
+ * @returns true if compatible
26
+ */
27
+ export function isPythonVersionCompatible(major: number, minor: number): boolean {
28
+ // onnxruntime currently supports Python 3.9-3.12
29
+ return major === 3 && minor >= 9 && minor <= 12;
30
+ }
31
+
32
+ /**
33
+ * Detect available Python 3 executable
34
+ * Supports CCW_PYTHON environment variable for custom Python path
35
+ * On Windows, uses py launcher to find compatible versions
36
+ * @returns Python executable command
37
+ */
38
+ export function getSystemPython(): string {
39
+ // Check for user-specified Python via environment variable
40
+ const customPython = process.env.CCW_PYTHON;
41
+ if (customPython) {
42
+ try {
43
+ const version = execSync(`"${customPython}" --version 2>&1`, { encoding: 'utf8' });
44
+ if (version.includes('Python 3')) {
45
+ const parsed = parsePythonVersion(version);
46
+ if (parsed && !isPythonVersionCompatible(parsed.major, parsed.minor)) {
47
+ console.warn(`[Python] Warning: CCW_PYTHON points to Python ${parsed.major}.${parsed.minor}, which may not be compatible with onnxruntime (requires 3.9-3.12)`);
48
+ }
49
+ return `"${customPython}"`;
50
+ }
51
+ } catch {
52
+ console.warn(`[Python] Warning: CCW_PYTHON="${customPython}" is not a valid Python executable, falling back to system Python`);
53
+ }
54
+ }
55
+
56
+ // On Windows, try py launcher with specific versions first (3.12, 3.11, 3.10, 3.9)
57
+ if (process.platform === 'win32') {
58
+ const compatibleVersions = ['3.12', '3.11', '3.10', '3.9'];
59
+ for (const ver of compatibleVersions) {
60
+ try {
61
+ const version = execSync(`py -${ver} --version 2>&1`, { encoding: 'utf8' });
62
+ if (version.includes(`Python ${ver}`)) {
63
+ console.log(`[Python] Found compatible Python ${ver} via py launcher`);
64
+ return `py -${ver}`;
65
+ }
66
+ } catch {
67
+ // Version not installed, try next
68
+ }
69
+ }
70
+ }
71
+
72
+ const commands = process.platform === 'win32' ? ['python', 'py', 'python3'] : ['python3', 'python'];
73
+ let fallbackCmd: string | null = null;
74
+ let fallbackVersion: { major: number; minor: number } | null = null;
75
+
76
+ for (const cmd of commands) {
77
+ try {
78
+ const version = execSync(`${cmd} --version 2>&1`, { encoding: 'utf8' });
79
+ if (version.includes('Python 3')) {
80
+ const parsed = parsePythonVersion(version);
81
+ if (parsed) {
82
+ // Prefer compatible version (3.9-3.12)
83
+ if (isPythonVersionCompatible(parsed.major, parsed.minor)) {
84
+ return cmd;
85
+ }
86
+ // Keep track of first Python 3 found as fallback
87
+ if (!fallbackCmd) {
88
+ fallbackCmd = cmd;
89
+ fallbackVersion = parsed;
90
+ }
91
+ }
92
+ }
93
+ } catch {
94
+ // Try next command
95
+ }
96
+ }
97
+
98
+ // If no compatible version found, use fallback with warning
99
+ if (fallbackCmd && fallbackVersion) {
100
+ console.warn(`[Python] Warning: Only Python ${fallbackVersion.major}.${fallbackVersion.minor} found, which may not be compatible with onnxruntime (requires 3.9-3.12).`);
101
+ console.warn('[Python] To use a specific Python version, set CCW_PYTHON environment variable:');
102
+ console.warn(' Windows: set CCW_PYTHON=C:\\path\\to\\python.exe');
103
+ console.warn(' Unix: export CCW_PYTHON=/path/to/python3.11');
104
+ console.warn('[Python] Alternatively, use LiteLLM embedding backend which has no Python version restrictions.');
105
+ return fallbackCmd;
106
+ }
107
+
108
+ throw new Error('Python 3 not found. Please install Python 3.9-3.12 and ensure it is in PATH, or set CCW_PYTHON environment variable.');
109
+ }
110
+
111
+ /**
112
+ * Get the Python command for pip operations (uses -m pip for reliability)
113
+ * @returns Array of command arguments for spawn
114
+ */
115
+ export function getPipCommand(): { pythonCmd: string; pipArgs: string[] } {
116
+ const pythonCmd = getSystemPython();
117
+ return {
118
+ pythonCmd,
119
+ pipArgs: ['-m', 'pip']
120
+ };
121
+ }
@@ -1,150 +1,150 @@
1
- # codex-lens/src/codexlens/search/enrichment.py
2
- """Relationship enrichment for search results."""
3
- import sqlite3
4
- from pathlib import Path
5
- from typing import List, Dict, Any, Optional
6
-
7
-
8
- class RelationshipEnricher:
9
- """Enriches search results with code graph relationships."""
10
-
11
- def __init__(self, index_path: Path):
12
- """Initialize with path to index database.
13
-
14
- Args:
15
- index_path: Path to _index.db SQLite database
16
- """
17
- self.index_path = index_path
18
- self.db_conn: Optional[sqlite3.Connection] = None
19
- self._connect()
20
-
21
- def _connect(self) -> None:
22
- """Establish read-only database connection."""
23
- if self.index_path.exists():
24
- self.db_conn = sqlite3.connect(
25
- f"file:{self.index_path}?mode=ro",
26
- uri=True,
27
- check_same_thread=False
28
- )
29
- self.db_conn.row_factory = sqlite3.Row
30
-
31
- def enrich(self, results: List[Dict[str, Any]], limit: int = 10) -> List[Dict[str, Any]]:
32
- """Add relationship data to search results.
33
-
34
- Args:
35
- results: List of search result dictionaries
36
- limit: Maximum number of results to enrich
37
-
38
- Returns:
39
- Results with relationships field added
40
- """
41
- if not self.db_conn:
42
- return results
43
-
44
- for result in results[:limit]:
45
- file_path = result.get('file') or result.get('path')
46
- symbol_name = result.get('symbol')
47
- result['relationships'] = self._find_relationships(file_path, symbol_name)
48
- return results
49
-
50
- def _find_relationships(self, file_path: Optional[str], symbol_name: Optional[str]) -> List[Dict[str, Any]]:
51
- """Query relationships for a symbol.
52
-
53
- Args:
54
- file_path: Path to file containing the symbol
55
- symbol_name: Name of the symbol
56
-
57
- Returns:
58
- List of relationship dictionaries with type, direction, target/source, file, line
59
- """
60
- if not self.db_conn or not symbol_name:
61
- return []
62
-
63
- relationships = []
64
- cursor = self.db_conn.cursor()
65
-
66
- try:
67
- # Find symbol ID(s) by name and optionally file
68
- if file_path:
69
- cursor.execute(
70
- 'SELECT id FROM symbols WHERE name = ? AND file_path = ?',
71
- (symbol_name, file_path)
72
- )
73
- else:
74
- cursor.execute('SELECT id FROM symbols WHERE name = ?', (symbol_name,))
75
-
76
- symbol_ids = [row[0] for row in cursor.fetchall()]
77
-
78
- if not symbol_ids:
79
- return []
80
-
81
- # Query outgoing relationships (symbol is source)
82
- placeholders = ','.join('?' * len(symbol_ids))
83
- cursor.execute(f'''
84
- SELECT sr.relationship_type, sr.target_symbol_fqn, sr.file_path, sr.line
85
- FROM symbol_relationships sr
86
- WHERE sr.source_symbol_id IN ({placeholders})
87
- ''', symbol_ids)
88
-
89
- for row in cursor.fetchall():
90
- relationships.append({
91
- 'type': row[0],
92
- 'direction': 'outgoing',
93
- 'target': row[1],
94
- 'file': row[2],
95
- 'line': row[3],
96
- })
97
-
98
- # Query incoming relationships (symbol is target)
99
- # Match against symbol name or qualified name patterns
100
- cursor.execute('''
101
- SELECT sr.relationship_type, s.name AS source_name, sr.file_path, sr.line
102
- FROM symbol_relationships sr
103
- JOIN symbols s ON sr.source_symbol_id = s.id
104
- WHERE sr.target_symbol_fqn = ? OR sr.target_symbol_fqn LIKE ?
105
- ''', (symbol_name, f'%.{symbol_name}'))
106
-
107
- for row in cursor.fetchall():
108
- rel_type = row[0]
109
- # Convert to incoming type
110
- incoming_type = self._to_incoming_type(rel_type)
111
- relationships.append({
112
- 'type': incoming_type,
113
- 'direction': 'incoming',
114
- 'source': row[1],
115
- 'file': row[2],
116
- 'line': row[3],
117
- })
118
-
119
- except sqlite3.Error:
120
- return []
121
-
122
- return relationships
123
-
124
- def _to_incoming_type(self, outgoing_type: str) -> str:
125
- """Convert outgoing relationship type to incoming type.
126
-
127
- Args:
128
- outgoing_type: The outgoing relationship type (e.g., 'calls', 'imports')
129
-
130
- Returns:
131
- Corresponding incoming type (e.g., 'called_by', 'imported_by')
132
- """
133
- type_map = {
134
- 'calls': 'called_by',
135
- 'imports': 'imported_by',
136
- 'extends': 'extended_by',
137
- }
138
- return type_map.get(outgoing_type, f'{outgoing_type}_by')
139
-
140
- def close(self) -> None:
141
- """Close database connection."""
142
- if self.db_conn:
143
- self.db_conn.close()
144
- self.db_conn = None
145
-
146
- def __enter__(self) -> 'RelationshipEnricher':
147
- return self
148
-
149
- def __exit__(self, exc_type, exc_val, exc_tb) -> None:
150
- self.close()
1
+ # codex-lens/src/codexlens/search/enrichment.py
2
+ """Relationship enrichment for search results."""
3
+ import sqlite3
4
+ from pathlib import Path
5
+ from typing import List, Dict, Any, Optional
6
+
7
+
8
+ class RelationshipEnricher:
9
+ """Enriches search results with code graph relationships."""
10
+
11
+ def __init__(self, index_path: Path):
12
+ """Initialize with path to index database.
13
+
14
+ Args:
15
+ index_path: Path to _index.db SQLite database
16
+ """
17
+ self.index_path = index_path
18
+ self.db_conn: Optional[sqlite3.Connection] = None
19
+ self._connect()
20
+
21
+ def _connect(self) -> None:
22
+ """Establish read-only database connection."""
23
+ if self.index_path.exists():
24
+ self.db_conn = sqlite3.connect(
25
+ f"file:{self.index_path}?mode=ro",
26
+ uri=True,
27
+ check_same_thread=False
28
+ )
29
+ self.db_conn.row_factory = sqlite3.Row
30
+
31
+ def enrich(self, results: List[Dict[str, Any]], limit: int = 10) -> List[Dict[str, Any]]:
32
+ """Add relationship data to search results.
33
+
34
+ Args:
35
+ results: List of search result dictionaries
36
+ limit: Maximum number of results to enrich
37
+
38
+ Returns:
39
+ Results with relationships field added
40
+ """
41
+ if not self.db_conn:
42
+ return results
43
+
44
+ for result in results[:limit]:
45
+ file_path = result.get('file') or result.get('path')
46
+ symbol_name = result.get('symbol')
47
+ result['relationships'] = self._find_relationships(file_path, symbol_name)
48
+ return results
49
+
50
+ def _find_relationships(self, file_path: Optional[str], symbol_name: Optional[str]) -> List[Dict[str, Any]]:
51
+ """Query relationships for a symbol.
52
+
53
+ Args:
54
+ file_path: Path to file containing the symbol
55
+ symbol_name: Name of the symbol
56
+
57
+ Returns:
58
+ List of relationship dictionaries with type, direction, target/source, file, line
59
+ """
60
+ if not self.db_conn or not symbol_name:
61
+ return []
62
+
63
+ relationships = []
64
+ cursor = self.db_conn.cursor()
65
+
66
+ try:
67
+ # Find symbol ID(s) by name and optionally file
68
+ if file_path:
69
+ cursor.execute(
70
+ 'SELECT id FROM symbols WHERE name = ? AND file_path = ?',
71
+ (symbol_name, file_path)
72
+ )
73
+ else:
74
+ cursor.execute('SELECT id FROM symbols WHERE name = ?', (symbol_name,))
75
+
76
+ symbol_ids = [row[0] for row in cursor.fetchall()]
77
+
78
+ if not symbol_ids:
79
+ return []
80
+
81
+ # Query outgoing relationships (symbol is source)
82
+ placeholders = ','.join('?' * len(symbol_ids))
83
+ cursor.execute(f'''
84
+ SELECT sr.relationship_type, sr.target_symbol_fqn, sr.file_path, sr.line
85
+ FROM symbol_relationships sr
86
+ WHERE sr.source_symbol_id IN ({placeholders})
87
+ ''', symbol_ids)
88
+
89
+ for row in cursor.fetchall():
90
+ relationships.append({
91
+ 'type': row[0],
92
+ 'direction': 'outgoing',
93
+ 'target': row[1],
94
+ 'file': row[2],
95
+ 'line': row[3],
96
+ })
97
+
98
+ # Query incoming relationships (symbol is target)
99
+ # Match against symbol name or qualified name patterns
100
+ cursor.execute('''
101
+ SELECT sr.relationship_type, s.name AS source_name, sr.file_path, sr.line
102
+ FROM symbol_relationships sr
103
+ JOIN symbols s ON sr.source_symbol_id = s.id
104
+ WHERE sr.target_symbol_fqn = ? OR sr.target_symbol_fqn LIKE ?
105
+ ''', (symbol_name, f'%.{symbol_name}'))
106
+
107
+ for row in cursor.fetchall():
108
+ rel_type = row[0]
109
+ # Convert to incoming type
110
+ incoming_type = self._to_incoming_type(rel_type)
111
+ relationships.append({
112
+ 'type': incoming_type,
113
+ 'direction': 'incoming',
114
+ 'source': row[1],
115
+ 'file': row[2],
116
+ 'line': row[3],
117
+ })
118
+
119
+ except sqlite3.Error:
120
+ return []
121
+
122
+ return relationships
123
+
124
+ def _to_incoming_type(self, outgoing_type: str) -> str:
125
+ """Convert outgoing relationship type to incoming type.
126
+
127
+ Args:
128
+ outgoing_type: The outgoing relationship type (e.g., 'calls', 'imports')
129
+
130
+ Returns:
131
+ Corresponding incoming type (e.g., 'called_by', 'imported_by')
132
+ """
133
+ type_map = {
134
+ 'calls': 'called_by',
135
+ 'imports': 'imported_by',
136
+ 'extends': 'extended_by',
137
+ }
138
+ return type_map.get(outgoing_type, f'{outgoing_type}_by')
139
+
140
+ def close(self) -> None:
141
+ """Close database connection."""
142
+ if self.db_conn:
143
+ self.db_conn.close()
144
+ self.db_conn = None
145
+
146
+ def __enter__(self) -> 'RelationshipEnricher':
147
+ return self
148
+
149
+ def __exit__(self, exc_type, exc_val, exc_tb) -> None:
150
+ self.close()
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-code-workflow",
3
- "version": "6.3.16",
3
+ "version": "6.3.18",
4
4
  "description": "JSON-driven multi-agent development framework with intelligent CLI orchestration (Gemini/Qwen/Codex), context-first architecture, and automated workflow execution",
5
5
  "type": "module",
6
6
  "main": "ccw/src/index.js",