@yottameta/yotta-dev-mcp-plugin 0.1.1 → 0.2.0
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.
- package/.agents/plugins/marketplace.json +3 -3
- package/.claude-plugin/marketplace.json +3 -3
- package/package.json +1 -1
- package/plugin.json +2 -2
- package/skills/yotta-dev-mcp/SKILL.md +19 -5
- package/skills/yotta-dev-mcp/references/adapters.md +69 -0
- package/skills/yotta-dev-mcp/references/architecture-contract.md +256 -0
- package/skills/yotta-dev-mcp/references/tools.md +147 -1
- package/skills/yotta-dev-mcp/scripts/dev_adapters.py +609 -0
- package/skills/yotta-dev-mcp/scripts/dev_architecture.py +379 -0
- package/skills/yotta-dev-mcp/scripts/dev_common.py +144 -0
- package/skills/yotta-dev-mcp/scripts/dev_contract.py +830 -0
- package/skills/yotta-dev-mcp/scripts/dev_engine.py +258 -134
- package/skills/yotta-dev-mcp/scripts/dev_impact.py +556 -0
- package/skills/yotta-dev-mcp/scripts/dev_model.py +435 -0
- package/skills/yotta-dev-mcp/scripts/dev_selftest.py +534 -0
- package/skills/yotta-dev-mcp/scripts/dev_verify.py +450 -0
- package/skills/yotta-dev-mcp/scripts/yotta_dev_mcp.py +234 -5
- package/skills/yotta-dev-mcp/server.json +3 -3
|
@@ -8,7 +8,6 @@ tool explicitly documents a write.
|
|
|
8
8
|
"""
|
|
9
9
|
|
|
10
10
|
import argparse
|
|
11
|
-
import ast
|
|
12
11
|
import json
|
|
13
12
|
import math
|
|
14
13
|
import os
|
|
@@ -18,133 +17,27 @@ import subprocess
|
|
|
18
17
|
import sys
|
|
19
18
|
from pathlib import Path
|
|
20
19
|
|
|
20
|
+
import dev_contract
|
|
21
|
+
from dev_adapters import run_adapter as _run_adapter_impl
|
|
22
|
+
from dev_architecture import architecture_review as _architecture_review_impl
|
|
23
|
+
from dev_common import (
|
|
24
|
+
ANSI_RE, ERROR_RE, MAX_FILE_BYTES, VERIFY_LEVELS, _iter_files, _json_safe,
|
|
25
|
+
_language, _read_text, _rel,
|
|
26
|
+
_frontmatter_name, _frontmatter_version, source_exts,
|
|
27
|
+
)
|
|
28
|
+
from dev_impact import impact_analysis as _impact_analysis_impl
|
|
29
|
+
from dev_verify import verify_change as _verify_change_impl
|
|
30
|
+
from dev_selftest import self_test
|
|
31
|
+
from dev_model import _repo_map_js, _repo_map_python, system_model as _system_model_impl
|
|
21
32
|
from dev_rules import REVIEW_RULES
|
|
22
33
|
|
|
23
|
-
VERSION = "0.
|
|
34
|
+
VERSION = "0.2.0"
|
|
24
35
|
|
|
25
|
-
IGNORE_DIRS = {
|
|
26
|
-
".git", ".hg", ".svn", "node_modules", "__pycache__", ".venv", "venv",
|
|
27
|
-
"dist", "build", ".next", ".nuxt", ".cache", ".tmp", ".tmp2",
|
|
28
|
-
}
|
|
29
|
-
SOURCE_EXTS = {
|
|
30
|
-
".py", ".js", ".jsx", ".mjs", ".cjs", ".ts", ".tsx", ".sh", ".ps1",
|
|
31
|
-
".go", ".rs", ".java", ".kt", ".kts", ".rb", ".php",
|
|
32
|
-
}
|
|
33
|
-
TEXT_EXTS = SOURCE_EXTS | {".json", ".md", ".txt", ".yml", ".yaml", ".toml", ".ini", ".cfg"}
|
|
34
|
-
MAX_FILE_BYTES = 2 * 1024 * 1024
|
|
35
|
-
|
|
36
|
-
ANSI_RE = re.compile(r"\x1b\[[0-9;]*[A-Za-z]")
|
|
37
|
-
ERROR_RE = re.compile(
|
|
38
|
-
r"(?i)(traceback|exception|\berror\b|\bfailed\b|\bfail\b|fatal|panic|"
|
|
39
|
-
r"assertion|npm err!|\berr\b|\bwarn(?:ing)?\b)"
|
|
40
|
-
)
|
|
41
36
|
|
|
42
37
|
|
|
43
|
-
def _json_safe(value):
|
|
44
|
-
if isinstance(value, Path):
|
|
45
|
-
return str(value)
|
|
46
|
-
if isinstance(value, dict):
|
|
47
|
-
return {str(k): _json_safe(v) for k, v in value.items()}
|
|
48
|
-
if isinstance(value, list):
|
|
49
|
-
return [_json_safe(v) for v in value]
|
|
50
|
-
return value
|
|
51
38
|
|
|
52
39
|
|
|
53
|
-
def _read_text(path):
|
|
54
|
-
path = Path(path)
|
|
55
|
-
if path.stat().st_size > MAX_FILE_BYTES:
|
|
56
|
-
raise ValueError("文件超过大小上限: %s" % path)
|
|
57
|
-
data = path.read_bytes()
|
|
58
|
-
if b"\x00" in data[:4096]:
|
|
59
|
-
raise ValueError("二进制文件不参与文本扫描: %s" % path)
|
|
60
|
-
return data.decode("utf-8", errors="replace")
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
def _iter_files(root, extensions=None, max_files=5000, all_files=False):
|
|
64
|
-
root = Path(root)
|
|
65
|
-
extensions = set(extensions or TEXT_EXTS)
|
|
66
|
-
count = 0
|
|
67
|
-
for current, dirs, files in os.walk(str(root)):
|
|
68
|
-
dirs[:] = sorted(d for d in dirs if d not in IGNORE_DIRS)
|
|
69
|
-
for name in sorted(files):
|
|
70
|
-
path = Path(current) / name
|
|
71
|
-
if path.is_symlink():
|
|
72
|
-
continue
|
|
73
|
-
if not all_files and extensions and path.suffix.lower() not in extensions:
|
|
74
|
-
continue
|
|
75
|
-
if path.stat().st_size > MAX_FILE_BYTES:
|
|
76
|
-
continue
|
|
77
|
-
yield path
|
|
78
|
-
count += 1
|
|
79
|
-
if count >= max_files:
|
|
80
|
-
return
|
|
81
|
-
|
|
82
40
|
|
|
83
|
-
def _rel(root, path):
|
|
84
|
-
try:
|
|
85
|
-
return str(Path(path).resolve().relative_to(Path(root).resolve())).replace("\\", "/")
|
|
86
|
-
except ValueError:
|
|
87
|
-
return str(Path(path)).replace("\\", "/")
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
def _language(path):
|
|
91
|
-
ext = Path(path).suffix.lower()
|
|
92
|
-
if ext == ".py":
|
|
93
|
-
return "python"
|
|
94
|
-
if ext in (".js", ".jsx", ".mjs", ".cjs"):
|
|
95
|
-
return "javascript"
|
|
96
|
-
if ext in (".ts", ".tsx"):
|
|
97
|
-
return "typescript"
|
|
98
|
-
if ext in (".sh", ".ps1"):
|
|
99
|
-
return "shell"
|
|
100
|
-
return ext.lstrip(".") or "text"
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
def _resolve_python_import(source_rel, node):
|
|
104
|
-
source = Path(source_rel)
|
|
105
|
-
if isinstance(node, ast.Import):
|
|
106
|
-
return [alias.name for alias in node.names]
|
|
107
|
-
if not isinstance(node, ast.ImportFrom):
|
|
108
|
-
return []
|
|
109
|
-
if node.level:
|
|
110
|
-
parts = list(source.with_suffix("").parts[:-1])
|
|
111
|
-
if node.level > 1:
|
|
112
|
-
parts = parts[:-(node.level - 1)] if len(parts) >= node.level - 1 else []
|
|
113
|
-
prefix = "/".join(parts)
|
|
114
|
-
module = node.module or ""
|
|
115
|
-
target = (prefix + "/" + module.replace(".", "/")).strip("/")
|
|
116
|
-
return [target + ".py" if target else source_rel]
|
|
117
|
-
if node.module:
|
|
118
|
-
return [node.module]
|
|
119
|
-
return []
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
def _repo_map_python(path, rel):
|
|
123
|
-
text = _read_text(path)
|
|
124
|
-
imports = []
|
|
125
|
-
try:
|
|
126
|
-
tree = ast.parse(text)
|
|
127
|
-
except SyntaxError:
|
|
128
|
-
return imports
|
|
129
|
-
for node in ast.walk(tree):
|
|
130
|
-
if isinstance(node, (ast.Import, ast.ImportFrom)):
|
|
131
|
-
for target in _resolve_python_import(rel, node):
|
|
132
|
-
imports.append({"source": rel, "target": target, "line": getattr(node, "lineno", 1)})
|
|
133
|
-
return imports
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
def _repo_map_js(path, rel):
|
|
137
|
-
text = _read_text(path)
|
|
138
|
-
imports = []
|
|
139
|
-
patterns = [
|
|
140
|
-
re.compile(r"""(?:from\s+|import\s*\()\s*['"]([^'"]+)['"]"""),
|
|
141
|
-
re.compile(r"""require\s*\(\s*['"]([^'"]+)['"]\s*\)"""),
|
|
142
|
-
]
|
|
143
|
-
for lineno, line in enumerate(text.splitlines(), 1):
|
|
144
|
-
for pattern in patterns:
|
|
145
|
-
for match in pattern.finditer(line):
|
|
146
|
-
imports.append({"source": rel, "target": match.group(1), "line": lineno})
|
|
147
|
-
return imports
|
|
148
41
|
|
|
149
42
|
|
|
150
43
|
def repo_map(path, max_files=2000):
|
|
@@ -191,10 +84,6 @@ def repo_map(path, max_files=2000):
|
|
|
191
84
|
}
|
|
192
85
|
|
|
193
86
|
|
|
194
|
-
def source_exts():
|
|
195
|
-
return set(SOURCE_EXTS)
|
|
196
|
-
|
|
197
|
-
|
|
198
87
|
def _classify_match(line, query):
|
|
199
88
|
escaped = re.escape(query)
|
|
200
89
|
if re.search(r"^\s*(?:async\s+)?def\s+%s\b" % escaped, line):
|
|
@@ -384,16 +273,6 @@ def review_diff(diff_text=None, path=None, base=None, max_findings=200):
|
|
|
384
273
|
return {"files": files, "findings": findings[:max_findings], "truncated": truncated}
|
|
385
274
|
|
|
386
275
|
|
|
387
|
-
def _frontmatter_version(text):
|
|
388
|
-
match = re.search(r"(?m)^version:\s*[\"']?([^\"'\r\n]+)", text)
|
|
389
|
-
return match.group(1).strip() if match else None
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
def _frontmatter_name(text):
|
|
393
|
-
match = re.search(r"(?m)^name:\s*[\"']?([^\"'\r\n]+)", text)
|
|
394
|
-
return match.group(1).strip() if match else None
|
|
395
|
-
|
|
396
|
-
|
|
397
276
|
def _default_skill_dirs():
|
|
398
277
|
home = Path.home()
|
|
399
278
|
candidates = [
|
|
@@ -956,9 +835,165 @@ def workflow_state(root, action="read", date=None, text=None, file=None, apply=F
|
|
|
956
835
|
}
|
|
957
836
|
|
|
958
837
|
|
|
838
|
+
|
|
839
|
+
|
|
840
|
+
|
|
841
|
+
|
|
842
|
+
|
|
843
|
+
|
|
844
|
+
|
|
845
|
+
|
|
846
|
+
|
|
847
|
+
|
|
848
|
+
|
|
849
|
+
|
|
850
|
+
|
|
851
|
+
|
|
852
|
+
|
|
853
|
+
|
|
854
|
+
|
|
855
|
+
|
|
856
|
+
|
|
857
|
+
|
|
858
|
+
|
|
859
|
+
|
|
860
|
+
|
|
861
|
+
|
|
862
|
+
|
|
863
|
+
|
|
864
|
+
|
|
865
|
+
|
|
866
|
+
|
|
867
|
+
|
|
868
|
+
|
|
869
|
+
|
|
870
|
+
|
|
871
|
+
|
|
872
|
+
|
|
873
|
+
|
|
874
|
+
|
|
875
|
+
|
|
876
|
+
|
|
877
|
+
|
|
878
|
+
|
|
879
|
+
|
|
880
|
+
|
|
881
|
+
|
|
882
|
+
|
|
883
|
+
|
|
884
|
+
|
|
885
|
+
|
|
886
|
+
|
|
887
|
+
|
|
888
|
+
|
|
889
|
+
|
|
890
|
+
|
|
891
|
+
|
|
892
|
+
|
|
893
|
+
|
|
894
|
+
|
|
895
|
+
|
|
896
|
+
|
|
897
|
+
|
|
898
|
+
|
|
899
|
+
|
|
900
|
+
|
|
901
|
+
|
|
902
|
+
|
|
903
|
+
|
|
904
|
+
|
|
905
|
+
|
|
906
|
+
|
|
907
|
+
|
|
908
|
+
|
|
909
|
+
|
|
910
|
+
|
|
911
|
+
|
|
912
|
+
|
|
913
|
+
|
|
914
|
+
|
|
915
|
+
|
|
916
|
+
|
|
917
|
+
|
|
918
|
+
|
|
919
|
+
|
|
920
|
+
|
|
921
|
+
|
|
922
|
+
|
|
923
|
+
|
|
924
|
+
|
|
925
|
+
|
|
926
|
+
|
|
927
|
+
|
|
928
|
+
|
|
929
|
+
|
|
930
|
+
|
|
931
|
+
|
|
932
|
+
|
|
933
|
+
|
|
934
|
+
|
|
935
|
+
|
|
936
|
+
|
|
937
|
+
|
|
938
|
+
|
|
939
|
+
|
|
940
|
+
def system_model(path, max_files=2000, contract_file=None):
|
|
941
|
+
"""Build the deterministic system model through the stable engine facade."""
|
|
942
|
+
return _system_model_impl(
|
|
943
|
+
path, max_files=max_files, contract_file=contract_file,
|
|
944
|
+
)
|
|
945
|
+
|
|
946
|
+
|
|
947
|
+
def architecture_review(path, max_files=2000, contract_file=None):
|
|
948
|
+
"""Review architecture through the stable engine facade."""
|
|
949
|
+
return _architecture_review_impl(
|
|
950
|
+
path, max_files=max_files, contract_file=contract_file,
|
|
951
|
+
)
|
|
952
|
+
|
|
953
|
+
|
|
954
|
+
def impact_analysis(path, changed_files=None, diff=None, symbols=None, depth=3,
|
|
955
|
+
max_files=2000, contract_file=None):
|
|
956
|
+
"""Build the change impact cone through the stable engine facade."""
|
|
957
|
+
return _impact_analysis_impl(
|
|
958
|
+
path, changed_files=changed_files, diff=diff, symbols=symbols,
|
|
959
|
+
depth=depth, max_files=max_files, contract_file=contract_file,
|
|
960
|
+
)
|
|
961
|
+
|
|
962
|
+
|
|
963
|
+
def verify_change(path, changed_files=None, diff=None, symbols=None, depth=3,
|
|
964
|
+
levels=None, allow_execute=False, timeout=120,
|
|
965
|
+
max_files=2000, contract_file=None, policy_file=None):
|
|
966
|
+
"""Run the verification ladder through the stable engine facade."""
|
|
967
|
+
return _verify_change_impl(
|
|
968
|
+
path, changed_files=changed_files, diff=diff, symbols=symbols,
|
|
969
|
+
depth=depth, levels=levels, allow_execute=allow_execute, timeout=timeout,
|
|
970
|
+
max_files=max_files, contract_file=contract_file, policy_file=policy_file,
|
|
971
|
+
)
|
|
972
|
+
|
|
973
|
+
|
|
974
|
+
def run_adapter(path, action="list", adapter=None, allow_execute=False, timeout=120,
|
|
975
|
+
max_chars=120000, token_budget=None, target="."):
|
|
976
|
+
"""Probe or explicitly run one optional external adapter.
|
|
977
|
+
|
|
978
|
+
The implementation lives in dev_adapters.py; this facade keeps the public
|
|
979
|
+
dispatch signature and fail-closed default visible to self_test.
|
|
980
|
+
"""
|
|
981
|
+
return _run_adapter_impl(
|
|
982
|
+
path, action=action, adapter=adapter, allow_execute=allow_execute,
|
|
983
|
+
timeout=timeout, max_chars=max_chars, token_budget=token_budget,
|
|
984
|
+
target=target,
|
|
985
|
+
)
|
|
986
|
+
|
|
987
|
+
|
|
959
988
|
def dispatch(name, arguments):
|
|
960
989
|
handlers = {
|
|
961
990
|
"repo_map": repo_map,
|
|
991
|
+
"system_model": system_model,
|
|
992
|
+
"architecture_review": architecture_review,
|
|
993
|
+
"impact_analysis": impact_analysis,
|
|
994
|
+
"verify_change": verify_change,
|
|
995
|
+
"self_test": self_test,
|
|
996
|
+
"run_adapter": run_adapter,
|
|
962
997
|
"find_code": find_code,
|
|
963
998
|
"compress_output": compress_output,
|
|
964
999
|
"review_code": review_code,
|
|
@@ -982,6 +1017,62 @@ def main():
|
|
|
982
1017
|
sub = parser.add_subparsers(dest="command")
|
|
983
1018
|
repo = sub.add_parser("repo-map")
|
|
984
1019
|
repo.add_argument("path")
|
|
1020
|
+
model = sub.add_parser("system-model")
|
|
1021
|
+
model.add_argument("path")
|
|
1022
|
+
model.add_argument("--contract", help="contract path relative to the repository root")
|
|
1023
|
+
review = sub.add_parser("architecture-review")
|
|
1024
|
+
review.add_argument("path")
|
|
1025
|
+
review.add_argument("--contract", help="contract path relative to the repository root")
|
|
1026
|
+
impact = sub.add_parser("impact-analysis")
|
|
1027
|
+
impact.add_argument("path")
|
|
1028
|
+
impact.add_argument("--changed", action="append",
|
|
1029
|
+
help="repository-relative changed file (repeatable)")
|
|
1030
|
+
impact.add_argument("--diff-file", help="read a unified diff from this file")
|
|
1031
|
+
impact.add_argument("--symbol", action="append",
|
|
1032
|
+
help="target symbol whose definition site is the change (repeatable)")
|
|
1033
|
+
impact.add_argument("--depth", type=int, default=3,
|
|
1034
|
+
help="reverse-dependency depth, 1-10 (default 3)")
|
|
1035
|
+
impact.add_argument("--contract", help="contract path relative to the repository root")
|
|
1036
|
+
verify = sub.add_parser("verify-change")
|
|
1037
|
+
verify.add_argument("path")
|
|
1038
|
+
verify.add_argument("--changed", action="append",
|
|
1039
|
+
help="repository-relative changed file (repeatable)")
|
|
1040
|
+
verify.add_argument("--diff-file", help="read a unified diff from this file")
|
|
1041
|
+
verify.add_argument("--symbol", action="append",
|
|
1042
|
+
help="target symbol whose definition site is the change (repeatable)")
|
|
1043
|
+
verify.add_argument("--level", action="append", choices=VERIFY_LEVELS,
|
|
1044
|
+
help="additional verification level (repeatable)")
|
|
1045
|
+
verify.add_argument("--depth", type=int, default=3,
|
|
1046
|
+
help="reverse-dependency depth, 1-10 (default 3)")
|
|
1047
|
+
verify.add_argument("--contract", help="contract path relative to the repository root")
|
|
1048
|
+
verify.add_argument("--policy-file", help="verification policy path relative to the root")
|
|
1049
|
+
verify.add_argument("--allow-execute", action="store_true",
|
|
1050
|
+
help="run whitelisted L2-L4 policy checks")
|
|
1051
|
+
verify.add_argument("--timeout", type=int, default=120,
|
|
1052
|
+
help="upper bound for each check in seconds")
|
|
1053
|
+
self_test_parser = sub.add_parser("self-test")
|
|
1054
|
+
self_test_parser.add_argument("path")
|
|
1055
|
+
self_test_parser.add_argument("--mode", choices=("auto", "source", "installed"),
|
|
1056
|
+
default="auto")
|
|
1057
|
+
self_test_parser.add_argument("--allow-execute", action="store_true",
|
|
1058
|
+
help="run the target test suite")
|
|
1059
|
+
self_test_parser.add_argument("--timeout", type=int, default=120,
|
|
1060
|
+
help="test timeout in seconds")
|
|
1061
|
+
adapter = sub.add_parser("adapter")
|
|
1062
|
+
adapter.add_argument("path")
|
|
1063
|
+
adapter.add_argument("--action", choices=("list", "run"), default="list")
|
|
1064
|
+
adapter.add_argument("--adapter",
|
|
1065
|
+
choices=("import-linter", "dependency-cruiser", "repomix"))
|
|
1066
|
+
adapter.add_argument("--allow-execute", action="store_true",
|
|
1067
|
+
help="run the selected adapter; required for action=run")
|
|
1068
|
+
adapter.add_argument("--timeout", type=int, default=120,
|
|
1069
|
+
help="adapter timeout in seconds")
|
|
1070
|
+
adapter.add_argument("--max-chars", type=int, default=120000,
|
|
1071
|
+
help="Repomix output cap")
|
|
1072
|
+
adapter.add_argument("--token-budget", type=int,
|
|
1073
|
+
help="optional Repomix token budget")
|
|
1074
|
+
adapter.add_argument("--target", default=".",
|
|
1075
|
+
help="repository-relative adapter target, default .")
|
|
985
1076
|
find = sub.add_parser("find-code")
|
|
986
1077
|
find.add_argument("path")
|
|
987
1078
|
find.add_argument("query")
|
|
@@ -995,6 +1086,39 @@ def main():
|
|
|
995
1086
|
args = parser.parse_args()
|
|
996
1087
|
if args.command == "repo-map":
|
|
997
1088
|
result = repo_map(args.path)
|
|
1089
|
+
elif args.command == "system-model":
|
|
1090
|
+
result = system_model(args.path, contract_file=args.contract)
|
|
1091
|
+
elif args.command == "architecture-review":
|
|
1092
|
+
result = architecture_review(args.path, contract_file=args.contract)
|
|
1093
|
+
elif args.command == "impact-analysis":
|
|
1094
|
+
diff_text = None
|
|
1095
|
+
if args.diff_file:
|
|
1096
|
+
diff_text = Path(args.diff_file).read_text(encoding="utf-8")
|
|
1097
|
+
result = impact_analysis(args.path, changed_files=args.changed, diff=diff_text,
|
|
1098
|
+
symbols=args.symbol, depth=args.depth,
|
|
1099
|
+
contract_file=args.contract)
|
|
1100
|
+
elif args.command == "verify-change":
|
|
1101
|
+
diff_text = None
|
|
1102
|
+
if args.diff_file:
|
|
1103
|
+
diff_text = Path(args.diff_file).read_text(encoding="utf-8")
|
|
1104
|
+
result = verify_change(
|
|
1105
|
+
args.path, changed_files=args.changed, diff=diff_text,
|
|
1106
|
+
symbols=args.symbol, depth=args.depth, levels=args.level,
|
|
1107
|
+
allow_execute=args.allow_execute, timeout=args.timeout,
|
|
1108
|
+
contract_file=args.contract, policy_file=args.policy_file,
|
|
1109
|
+
)
|
|
1110
|
+
elif args.command == "self-test":
|
|
1111
|
+
result = self_test(
|
|
1112
|
+
args.path, mode=args.mode, allow_execute=args.allow_execute,
|
|
1113
|
+
timeout=args.timeout,
|
|
1114
|
+
)
|
|
1115
|
+
elif args.command == "adapter":
|
|
1116
|
+
result = run_adapter(
|
|
1117
|
+
args.path, action=args.action, adapter=args.adapter,
|
|
1118
|
+
allow_execute=args.allow_execute, timeout=args.timeout,
|
|
1119
|
+
max_chars=args.max_chars, token_budget=args.token_budget,
|
|
1120
|
+
target=args.target,
|
|
1121
|
+
)
|
|
998
1122
|
elif args.command == "find-code":
|
|
999
1123
|
result = find_code(args.path, args.query)
|
|
1000
1124
|
elif args.command == "compress-output":
|