cmdop-coder 0.1.1__tar.gz

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.
@@ -0,0 +1,43 @@
1
+ # Logs
2
+ .benchmarks
3
+
4
+ # Dependencies
5
+ node_modules/
6
+ .pnpm-store/
7
+ **/node_modules/
8
+
9
+ # Python
10
+ __pycache__/
11
+ *.py[cod]
12
+ *$py.class
13
+ .venv/
14
+ venv/
15
+ .pytest_cache/
16
+
17
+ # Go
18
+ *.exe
19
+ *.exe~
20
+ *.dll
21
+ *.so
22
+ *.dylib
23
+
24
+ # IDE
25
+ .idea/
26
+ .vscode/
27
+ *.swp
28
+ *.swo
29
+ *~
30
+
31
+ # OS
32
+ .DS_Store
33
+ Thumbs.db
34
+
35
+ # Build outputs
36
+ dist/
37
+ build/
38
+ *.egg-info/
39
+
40
+ # Temporary files
41
+ *.log
42
+ *.tmp
43
+ .env.local
@@ -0,0 +1,32 @@
1
+ PYTHON := uv run --python .venv python
2
+
3
+ .PHONY: help install test lint install-skill publish release
4
+
5
+ help:
6
+ @echo "cmdop-coder"
7
+ @echo ""
8
+ @echo " make install Create venv and install in development mode"
9
+ @echo " make test Run tests"
10
+ @echo " make lint Run linter"
11
+ @echo " make install-skill Register skill via cmdop-skill install"
12
+ @echo " make publish Publish skill to CMDOP marketplace"
13
+ @echo " make release Bump + build + upload to PyPI"
14
+
15
+ install:
16
+ uv venv
17
+ uv pip install --python .venv -e '.[dev]'
18
+
19
+ test:
20
+ $(PYTHON) -m pytest tests/ -v
21
+
22
+ lint:
23
+ $(PYTHON) -m ruff check src/
24
+
25
+ install-skill:
26
+ cmdop-skill install .
27
+
28
+ publish:
29
+ cmdop-skill publish --path .
30
+
31
+ release:
32
+ cmdop-skill release .
@@ -0,0 +1,117 @@
1
+ Metadata-Version: 2.4
2
+ Name: cmdop-coder
3
+ Version: 0.1.1
4
+ Summary: CMDOP skill — code analysis with tree-sitter AST parsing
5
+ Project-URL: Homepage, https://cmdop.com/skills/cmdop-coder/
6
+ Author-email: CMDOP Team <team@cmdop.com>
7
+ License-Expression: MIT
8
+ Keywords: ast,cmdop,code,skill,tree-sitter
9
+ Classifier: Development Status :: 3 - Alpha
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: License :: OSI Approved :: MIT License
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3.10
14
+ Classifier: Programming Language :: Python :: 3.11
15
+ Classifier: Programming Language :: Python :: 3.12
16
+ Requires-Python: >=3.10
17
+ Requires-Dist: cmdop
18
+ Requires-Dist: cmdop-skill
19
+ Requires-Dist: pydantic>=2.0
20
+ Requires-Dist: tree-sitter-language-pack>=0.1
21
+ Requires-Dist: tree-sitter>=0.21
22
+ Provides-Extra: dev
23
+ Requires-Dist: pytest-asyncio>=0.21.0; extra == 'dev'
24
+ Requires-Dist: pytest-cov>=4.0.0; extra == 'dev'
25
+ Requires-Dist: pytest>=7.0.0; extra == 'dev'
26
+ Requires-Dist: ruff>=0.1.0; extra == 'dev'
27
+ Description-Content-Type: text/markdown
28
+
29
+ # cmdop-coder
30
+
31
+ > **[CMDOP Skill](https://cmdop.com/skills/cmdop-coder/)** — install and use via [CMDOP agent](https://cmdop.com):
32
+ > ```
33
+ > cmdop-skill install cmdop-coder
34
+ > ```
35
+
36
+ Code analysis using tree-sitter AST parsing. Extract functions, find symbols, get structural outlines. Supports 40+ languages.
37
+
38
+ ## Install
39
+
40
+ ```bash
41
+ pip install cmdop-coder
42
+ ```
43
+
44
+ Or as a CMDOP skill:
45
+
46
+ ```bash
47
+ cmdop-skill install path/to/cmdop-coder
48
+ ```
49
+
50
+ ## CLI
51
+
52
+ ### Extract functions
53
+
54
+ ```bash
55
+ cmdop-coder functions --path src/main.py
56
+ ```
57
+
58
+ ```json
59
+ {
60
+ "file": "src/main.py",
61
+ "language": "python",
62
+ "count": 3,
63
+ "functions": [
64
+ {"line": 5, "name": "hello", "signature": "def hello(name: str) -> str:"},
65
+ {"line": 9, "name": "fetch", "signature": "async def fetch(url: str) -> bytes:"}
66
+ ]
67
+ }
68
+ ```
69
+
70
+ ### Find symbol
71
+
72
+ ```bash
73
+ cmdop-coder symbols --symbol MyClass --path ./src
74
+ ```
75
+
76
+ ### Structural outline
77
+
78
+ ```bash
79
+ cmdop-coder outline --path internal/agent/core/agent.go
80
+ ```
81
+
82
+ ### File statistics
83
+
84
+ ```bash
85
+ cmdop-coder analyze --path service.py
86
+ ```
87
+
88
+ ## Python API
89
+
90
+ ```python
91
+ from cmdop_coder import extract_functions, find_symbol, get_outline, analyze_file
92
+
93
+ # Extract all functions from a file
94
+ result = extract_functions("src/main.py")
95
+ for fn in result.functions:
96
+ print(fn.line, fn.name, fn.signature)
97
+
98
+ # Find symbol across a directory
99
+ matches = find_symbol("MyClass", "./src")
100
+ for m in matches.matches:
101
+ print(m.file, m.line, m.text)
102
+
103
+ # Structural outline
104
+ outline = get_outline("main.go")
105
+ for item in outline.outline:
106
+ print(item.line, item.type, item.name)
107
+
108
+ # File statistics
109
+ stats = analyze_file("service.py")
110
+ print(stats.language, stats.total_lines, stats.function_count)
111
+ ```
112
+
113
+ ## Supported Languages
114
+
115
+ Go, Python, JavaScript, TypeScript, TSX, Rust, Java, C, C++, Ruby, PHP,
116
+ Swift, Kotlin, C#, CSS, HTML, JSON, YAML, TOML, Bash, SQL, Lua, Scala,
117
+ Elixir, Elm, Haskell, OCaml, HCL, Dockerfile and more.
@@ -0,0 +1,89 @@
1
+ # cmdop-coder
2
+
3
+ > **[CMDOP Skill](https://cmdop.com/skills/cmdop-coder/)** — install and use via [CMDOP agent](https://cmdop.com):
4
+ > ```
5
+ > cmdop-skill install cmdop-coder
6
+ > ```
7
+
8
+ Code analysis using tree-sitter AST parsing. Extract functions, find symbols, get structural outlines. Supports 40+ languages.
9
+
10
+ ## Install
11
+
12
+ ```bash
13
+ pip install cmdop-coder
14
+ ```
15
+
16
+ Or as a CMDOP skill:
17
+
18
+ ```bash
19
+ cmdop-skill install path/to/cmdop-coder
20
+ ```
21
+
22
+ ## CLI
23
+
24
+ ### Extract functions
25
+
26
+ ```bash
27
+ cmdop-coder functions --path src/main.py
28
+ ```
29
+
30
+ ```json
31
+ {
32
+ "file": "src/main.py",
33
+ "language": "python",
34
+ "count": 3,
35
+ "functions": [
36
+ {"line": 5, "name": "hello", "signature": "def hello(name: str) -> str:"},
37
+ {"line": 9, "name": "fetch", "signature": "async def fetch(url: str) -> bytes:"}
38
+ ]
39
+ }
40
+ ```
41
+
42
+ ### Find symbol
43
+
44
+ ```bash
45
+ cmdop-coder symbols --symbol MyClass --path ./src
46
+ ```
47
+
48
+ ### Structural outline
49
+
50
+ ```bash
51
+ cmdop-coder outline --path internal/agent/core/agent.go
52
+ ```
53
+
54
+ ### File statistics
55
+
56
+ ```bash
57
+ cmdop-coder analyze --path service.py
58
+ ```
59
+
60
+ ## Python API
61
+
62
+ ```python
63
+ from cmdop_coder import extract_functions, find_symbol, get_outline, analyze_file
64
+
65
+ # Extract all functions from a file
66
+ result = extract_functions("src/main.py")
67
+ for fn in result.functions:
68
+ print(fn.line, fn.name, fn.signature)
69
+
70
+ # Find symbol across a directory
71
+ matches = find_symbol("MyClass", "./src")
72
+ for m in matches.matches:
73
+ print(m.file, m.line, m.text)
74
+
75
+ # Structural outline
76
+ outline = get_outline("main.go")
77
+ for item in outline.outline:
78
+ print(item.line, item.type, item.name)
79
+
80
+ # File statistics
81
+ stats = analyze_file("service.py")
82
+ print(stats.language, stats.total_lines, stats.function_count)
83
+ ```
84
+
85
+ ## Supported Languages
86
+
87
+ Go, Python, JavaScript, TypeScript, TSX, Rust, Java, C, C++, Ruby, PHP,
88
+ Swift, Kotlin, C#, CSS, HTML, JSON, YAML, TOML, Bash, SQL, Lua, Scala,
89
+ Elixir, Elm, Haskell, OCaml, HCL, Dockerfile and more.
@@ -0,0 +1,61 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "cmdop-coder"
7
+ version = "0.1.1"
8
+ description = "CMDOP skill — code analysis with tree-sitter AST parsing"
9
+ readme = "README.md"
10
+ license = "MIT"
11
+ requires-python = ">=3.10"
12
+ authors = [
13
+ { name = "CMDOP Team", email = "team@cmdop.com" }
14
+ ]
15
+ keywords = ["cmdop", "skill", "code", "ast", "tree-sitter"]
16
+ classifiers = [
17
+ "Development Status :: 3 - Alpha",
18
+ "Intended Audience :: Developers",
19
+ "License :: OSI Approved :: MIT License",
20
+ "Programming Language :: Python :: 3",
21
+ "Programming Language :: Python :: 3.10",
22
+ "Programming Language :: Python :: 3.11",
23
+ "Programming Language :: Python :: 3.12",
24
+ ]
25
+
26
+ dependencies = [
27
+ "cmdop",
28
+ "cmdop-skill",
29
+ "pydantic>=2.0",
30
+ "tree-sitter>=0.21",
31
+ "tree-sitter-language-pack>=0.1",
32
+ ]
33
+
34
+ [project.optional-dependencies]
35
+ dev = [
36
+ "pytest>=7.0.0",
37
+ "pytest-asyncio>=0.21.0",
38
+ "pytest-cov>=4.0.0",
39
+ "ruff>=0.1.0",
40
+ ]
41
+
42
+ [project.scripts]
43
+ cmdop-coder = "cmdop_coder._skill:main"
44
+
45
+ [project.urls]
46
+ Homepage = "https://cmdop.com/skills/cmdop-coder/"
47
+
48
+ [tool.hatch.build.targets.wheel]
49
+ packages = ["src/cmdop_coder"]
50
+
51
+ [tool.ruff]
52
+ target-version = "py310"
53
+ line-length = 100
54
+
55
+ [tool.ruff.lint]
56
+ select = ["E", "F", "I", "N", "W", "UP", "B", "C4", "SIM"]
57
+ ignore = ["E501"]
58
+
59
+ [tool.pytest.ini_options]
60
+ asyncio_mode = "auto"
61
+ testpaths = ["tests"]
@@ -0,0 +1,6 @@
1
+ from cmdop_skill import SkillCategory, SkillConfig
2
+
3
+ config = SkillConfig(
4
+ category=SkillCategory.DEVELOPMENT,
5
+ visibility="public",
6
+ )
@@ -0,0 +1,24 @@
1
+ # cmdop-coder
2
+
3
+ Code analysis skill using tree-sitter AST parsing. Supports 40+ languages.
4
+
5
+ ## Commands
6
+
7
+ - `functions --path <FILE>` — extract function/method signatures with line numbers
8
+ - `symbols --symbol <NAME> [--path <DIR>]` — find all occurrences of a symbol
9
+ - `outline --path <FILE>` — structural outline (imports, classes, functions, types)
10
+ - `analyze --path <FILE>` — file statistics (lines, complexity, language)
11
+
12
+ ## Usage
13
+
14
+ ```
15
+ cmdop-coder functions --path src/main.py
16
+ cmdop-coder symbols --symbol MyClass --path ./src
17
+ cmdop-coder outline --path internal/agent/core/agent.go
18
+ cmdop-coder analyze --path package.json
19
+ ```
20
+
21
+ ## Supported Languages
22
+
23
+ Go, Python, JavaScript, TypeScript, Rust, Java, C, C++, Ruby, PHP, Swift,
24
+ Kotlin, C#, CSS, HTML, JSON, YAML, TOML, Bash, SQL, and more.
@@ -0,0 +1,28 @@
1
+ """cmdop-coder — code analysis skill using tree-sitter AST parsing."""
2
+
3
+ from cmdop_coder._analysis import analyze_file, extract_functions, find_symbol, get_outline
4
+ from cmdop_coder._models import (
5
+ AnalyzeResult,
6
+ FunctionInfo,
7
+ FunctionsResult,
8
+ OutlineItem,
9
+ OutlineResult,
10
+ SymbolMatch,
11
+ SymbolsResult,
12
+ )
13
+ from cmdop_coder._skill import skill
14
+
15
+ __all__ = [
16
+ "AnalyzeResult",
17
+ "FunctionInfo",
18
+ "FunctionsResult",
19
+ "OutlineItem",
20
+ "OutlineResult",
21
+ "SymbolMatch",
22
+ "SymbolsResult",
23
+ "analyze_file",
24
+ "extract_functions",
25
+ "find_symbol",
26
+ "get_outline",
27
+ "skill",
28
+ ]
@@ -0,0 +1,191 @@
1
+ """Code analysis functions using tree-sitter AST."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from pathlib import Path
6
+
7
+ from ._models import (
8
+ AnalyzeResult,
9
+ FunctionInfo,
10
+ FunctionsResult,
11
+ OutlineItem,
12
+ OutlineResult,
13
+ SymbolMatch,
14
+ SymbolsResult,
15
+ )
16
+ from ._parser import detect_language, parse_file
17
+
18
+ # Node types that represent function/method definitions per language
19
+ _FUNCTION_NODES: dict[str, list[str]] = {
20
+ "python": ["function_definition", "async_function_definition"],
21
+ "javascript": ["function_declaration", "arrow_function", "method_definition", "function_expression"],
22
+ "typescript": ["function_declaration", "arrow_function", "method_definition", "function_expression"],
23
+ "tsx": ["function_declaration", "arrow_function", "method_definition", "function_expression"],
24
+ "go": ["function_declaration", "method_declaration"],
25
+ "rust": ["function_item"],
26
+ "java": ["method_declaration", "constructor_declaration"],
27
+ "c": ["function_definition"],
28
+ "cpp": ["function_definition"],
29
+ "ruby": ["method", "singleton_method"],
30
+ "php": ["function_definition", "method_declaration"],
31
+ "swift": ["function_declaration"],
32
+ "kotlin": ["function_declaration"],
33
+ "c_sharp": ["method_declaration", "constructor_declaration"],
34
+ "lua": ["function_definition", "local_function"],
35
+ "scala": ["function_definition"],
36
+ "elixir": ["def", "defp"],
37
+ }
38
+
39
+ # Node types for outline (structural elements) per language
40
+ _OUTLINE_NODES: dict[str, list[str]] = {
41
+ "python": ["import_statement", "import_from_statement", "class_definition",
42
+ "function_definition", "async_function_definition"],
43
+ "javascript": ["import_declaration", "class_declaration", "function_declaration",
44
+ "lexical_declaration", "variable_declaration"],
45
+ "typescript": ["import_declaration", "class_declaration", "function_declaration",
46
+ "interface_declaration", "type_alias_declaration", "enum_declaration"],
47
+ "tsx": ["import_declaration", "class_declaration", "function_declaration",
48
+ "interface_declaration", "type_alias_declaration"],
49
+ "go": ["import_declaration", "type_declaration", "function_declaration",
50
+ "method_declaration", "var_declaration", "const_declaration"],
51
+ "rust": ["use_declaration", "struct_item", "enum_item", "function_item",
52
+ "impl_item", "trait_item", "mod_item"],
53
+ "java": ["import_declaration", "class_declaration", "interface_declaration",
54
+ "method_declaration"],
55
+ "c": ["preproc_include", "struct_specifier", "function_definition", "type_definition"],
56
+ "cpp": ["preproc_include", "class_specifier", "struct_specifier",
57
+ "function_definition", "namespace_definition"],
58
+ }
59
+
60
+ _IDENTIFIER_TYPES = frozenset({
61
+ "identifier", "name", "property_identifier",
62
+ "type_identifier", "field_identifier",
63
+ })
64
+
65
+
66
+ def _node_name(node: object, source: bytes) -> str:
67
+ """Extract the name identifier text from an AST node."""
68
+ for child in node.children: # type: ignore[union-attr]
69
+ if child.type in _IDENTIFIER_TYPES:
70
+ return source[child.start_byte:child.end_byte].decode(errors="replace")
71
+ raw = source[node.start_byte:node.start_byte + 60] # type: ignore[union-attr]
72
+ return raw.decode(errors="replace").split("\n")[0]
73
+
74
+
75
+ def _collect_nodes(node: object, types: frozenset[str], depth: int = 0, max_depth: int = 20) -> list[object]:
76
+ """Walk AST and collect nodes matching the given types."""
77
+ if depth > max_depth:
78
+ return []
79
+ results: list[object] = []
80
+ if node.type in types: # type: ignore[union-attr]
81
+ results.append(node)
82
+ for child in node.children: # type: ignore[union-attr]
83
+ results.extend(_collect_nodes(child, types, depth + 1, max_depth))
84
+ return results
85
+
86
+
87
+ def _first_line(node: object, source: bytes) -> str:
88
+ """Return the first line of a node's source text, truncated to 120 chars."""
89
+ raw = source[node.start_byte:node.end_byte] # type: ignore[union-attr]
90
+ return raw.decode(errors="replace").split("\n")[0][:120]
91
+
92
+
93
+ def extract_functions(path: str | Path) -> FunctionsResult:
94
+ """Extract function/method signatures with line numbers from a source file."""
95
+ tree, source, lang = parse_file(path)
96
+ types = frozenset(_FUNCTION_NODES.get(lang, ["function_definition", "function_declaration"]))
97
+ nodes = _collect_nodes(tree.root_node, types) # type: ignore[union-attr]
98
+
99
+ functions = [
100
+ FunctionInfo(
101
+ line=node.start_point[0] + 1, # type: ignore[union-attr]
102
+ name=_node_name(node, source),
103
+ signature=_first_line(node, source),
104
+ )
105
+ for node in nodes
106
+ ]
107
+ return FunctionsResult(file=str(path), language=lang, count=len(functions), functions=functions)
108
+
109
+
110
+ def find_symbol(symbol: str, path: str | Path = ".") -> SymbolsResult:
111
+ """Find all occurrences of a symbol across source files."""
112
+ p = Path(path)
113
+ candidates = [p] if p.is_file() else list(p.rglob("*"))
114
+ matches: list[SymbolMatch] = []
115
+
116
+ for f in candidates:
117
+ if not f.is_file() or detect_language(f) is None:
118
+ continue
119
+ try:
120
+ lines = f.read_bytes().decode(errors="replace").splitlines()
121
+ except OSError:
122
+ continue
123
+ for i, line in enumerate(lines, 1):
124
+ if symbol in line:
125
+ matches.append(SymbolMatch(file=str(f), line=i, text=line.strip()))
126
+
127
+ return SymbolsResult(symbol=symbol, count=len(matches), matches=matches)
128
+
129
+
130
+ def get_outline(path: str | Path) -> OutlineResult:
131
+ """Get structural outline of a source file (imports, classes, functions, types)."""
132
+ tree, source, lang = parse_file(path)
133
+ types = frozenset(_OUTLINE_NODES.get(lang, []))
134
+
135
+ if not types:
136
+ items = _fallback_outline(tree.root_node, source) # type: ignore[union-attr]
137
+ return OutlineResult(file=str(path), language=lang, count=len(items), outline=items)
138
+
139
+ nodes = _collect_nodes(tree.root_node, types, max_depth=3) # type: ignore[union-attr]
140
+ items = [
141
+ OutlineItem(
142
+ line=node.start_point[0] + 1, # type: ignore[union-attr]
143
+ type=node.type, # type: ignore[union-attr]
144
+ name=_node_name(node, source),
145
+ )
146
+ for node in nodes
147
+ ]
148
+ return OutlineResult(file=str(path), language=lang, count=len(items), outline=items)
149
+
150
+
151
+ def _fallback_outline(root: object, source: bytes) -> list[OutlineItem]:
152
+ """Return top-level nodes as outline when language has no specific config."""
153
+ return [
154
+ OutlineItem(
155
+ line=child.start_point[0] + 1, # type: ignore[union-attr]
156
+ type=child.type, # type: ignore[union-attr]
157
+ name=_first_line(child, source)[:60],
158
+ )
159
+ for child in root.children # type: ignore[union-attr]
160
+ ]
161
+
162
+
163
+ def analyze_file(path: str | Path) -> AnalyzeResult:
164
+ """Analyze a source file: language detection, line counts, function count."""
165
+ p = Path(path)
166
+ source = p.read_bytes()
167
+ lang = detect_language(p)
168
+ lines = source.decode(errors="replace").splitlines()
169
+
170
+ blank = sum(1 for ln in lines if not ln.strip())
171
+ comment = sum(1 for ln in lines if ln.strip().startswith(("#", "//", "/*", "*")))
172
+ code = len(lines) - blank - comment
173
+
174
+ function_count: int | None = None
175
+ if lang and lang in _FUNCTION_NODES:
176
+ try:
177
+ function_count = extract_functions(p).count
178
+ except Exception:
179
+ pass
180
+
181
+ return AnalyzeResult(
182
+ file=str(p),
183
+ language=lang or "unknown",
184
+ extension=p.suffix,
185
+ size_bytes=len(source),
186
+ total_lines=len(lines),
187
+ code_lines=code,
188
+ blank_lines=blank,
189
+ comment_lines=comment,
190
+ function_count=function_count,
191
+ )
@@ -0,0 +1,67 @@
1
+ """Data models for cmdop-coder results."""
2
+
3
+ from pydantic import BaseModel
4
+
5
+
6
+ class FunctionInfo(BaseModel):
7
+ """A single function or method extracted from source code."""
8
+
9
+ line: int
10
+ name: str
11
+ signature: str
12
+
13
+
14
+ class FunctionsResult(BaseModel):
15
+ """Result of extracting functions from a file."""
16
+
17
+ file: str
18
+ language: str
19
+ count: int
20
+ functions: list[FunctionInfo]
21
+
22
+
23
+ class SymbolMatch(BaseModel):
24
+ """A single occurrence of a symbol in source code."""
25
+
26
+ file: str
27
+ line: int
28
+ text: str
29
+
30
+
31
+ class SymbolsResult(BaseModel):
32
+ """Result of searching for a symbol across files."""
33
+
34
+ symbol: str
35
+ count: int
36
+ matches: list[SymbolMatch]
37
+
38
+
39
+ class OutlineItem(BaseModel):
40
+ """A structural element in a source file outline."""
41
+
42
+ line: int
43
+ type: str
44
+ name: str
45
+
46
+
47
+ class OutlineResult(BaseModel):
48
+ """Result of generating a structural outline for a file."""
49
+
50
+ file: str
51
+ language: str
52
+ count: int
53
+ outline: list[OutlineItem]
54
+
55
+
56
+ class AnalyzeResult(BaseModel):
57
+ """Statistics for a source file."""
58
+
59
+ file: str
60
+ language: str
61
+ extension: str
62
+ size_bytes: int
63
+ total_lines: int
64
+ code_lines: int
65
+ blank_lines: int
66
+ comment_lines: int
67
+ function_count: int | None = None