jac-coder 0.1.0__py3-none-any.whl
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.
- jac_coder/__init__.jac +0 -0
- jac_coder/api.jac +82 -0
- jac_coder/cli_entry.py +25 -0
- jac_coder/config.jac +36 -0
- jac_coder/context.jac +17 -0
- jac_coder/data/examples/ai_agent.md +90 -0
- jac_coder/data/examples/blog_app.md +386 -0
- jac_coder/data/examples/core_patterns.md +321 -0
- jac_coder/data/examples/todo_app.md +321 -0
- jac_coder/data/reference/ai.md +131 -0
- jac_coder/data/reference/backend.md +215 -0
- jac_coder/data/reference/frontend.md +271 -0
- jac_coder/data/reference/osp.md +229 -0
- jac_coder/data/reference/pitfalls.md +141 -0
- jac_coder/data/reference/syntax.md +159 -0
- jac_coder/data/rules/core_jac.md +559 -0
- jac_coder/data/rules/fullstack.md +362 -0
- jac_coder/data/rules/workflow.md +88 -0
- jac_coder/events.jac +110 -0
- jac_coder/impl/api.impl.jac +399 -0
- jac_coder/impl/config.impl.jac +163 -0
- jac_coder/impl/context.impl.jac +117 -0
- jac_coder/impl/mcp_manager.impl.jac +380 -0
- jac_coder/impl/memory.impl.jac +247 -0
- jac_coder/impl/nodes.impl.jac +259 -0
- jac_coder/impl/permission.impl.jac +62 -0
- jac_coder/impl/walkers.impl.jac +298 -0
- jac_coder/mcp_manager.jac +35 -0
- jac_coder/memory.jac +15 -0
- jac_coder/nodes.jac +306 -0
- jac_coder/permission.jac +19 -0
- jac_coder/serve_entry.jac +30 -0
- jac_coder/server.jac +324 -0
- jac_coder/tool/__init__.jac +17 -0
- jac_coder/tool/checked.jac +10 -0
- jac_coder/tool/delegation.jac +23 -0
- jac_coder/tool/filesystem.jac +25 -0
- jac_coder/tool/git.jac +18 -0
- jac_coder/tool/guarded.jac +23 -0
- jac_coder/tool/impl/checked.impl.jac +38 -0
- jac_coder/tool/impl/delegation.impl.jac +157 -0
- jac_coder/tool/impl/filesystem.impl.jac +781 -0
- jac_coder/tool/impl/git.impl.jac +115 -0
- jac_coder/tool/impl/guarded.impl.jac +72 -0
- jac_coder/tool/impl/jac_analyzer.impl.jac +593 -0
- jac_coder/tool/impl/jac_docs.impl.jac +136 -0
- jac_coder/tool/impl/jac_tools.impl.jac +79 -0
- jac_coder/tool/impl/mcp.impl.jac +32 -0
- jac_coder/tool/impl/preview.impl.jac +233 -0
- jac_coder/tool/impl/question.impl.jac +29 -0
- jac_coder/tool/impl/scaffold.impl.jac +231 -0
- jac_coder/tool/impl/search.impl.jac +85 -0
- jac_coder/tool/impl/shell.impl.jac +89 -0
- jac_coder/tool/impl/task.impl.jac +12 -0
- jac_coder/tool/impl/think.impl.jac +4 -0
- jac_coder/tool/impl/todo.impl.jac +58 -0
- jac_coder/tool/impl/validate.impl.jac +236 -0
- jac_coder/tool/impl/web.impl.jac +91 -0
- jac_coder/tool/jac_analyzer.jac +21 -0
- jac_coder/tool/jac_docs.jac +9 -0
- jac_coder/tool/jac_tools.jac +11 -0
- jac_coder/tool/mcp.jac +17 -0
- jac_coder/tool/preview.jac +31 -0
- jac_coder/tool/question.jac +7 -0
- jac_coder/tool/scaffold.jac +10 -0
- jac_coder/tool/search.jac +14 -0
- jac_coder/tool/shell.jac +12 -0
- jac_coder/tool/task.jac +9 -0
- jac_coder/tool/think.jac +5 -0
- jac_coder/tool/todo.jac +12 -0
- jac_coder/tool/validate.jac +11 -0
- jac_coder/tool/vision.jac +17 -0
- jac_coder/tool/web.jac +10 -0
- jac_coder/util/__init__.jac +18 -0
- jac_coder/util/colors.jac +20 -0
- jac_coder/util/impl/sandbox.impl.jac +38 -0
- jac_coder/util/impl/tool_output.impl.jac +208 -0
- jac_coder/util/sandbox.jac +8 -0
- jac_coder/util/tool_output.jac +29 -0
- jac_coder/walkers.jac +67 -0
- jac_coder-0.1.0.dist-info/METADATA +9 -0
- jac_coder-0.1.0.dist-info/RECORD +85 -0
- jac_coder-0.1.0.dist-info/WHEEL +5 -0
- jac_coder-0.1.0.dist-info/entry_points.txt +3 -0
- jac_coder-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
impl validate_project(directory: str = "") -> str {
|
|
2
|
+
tool_status("validate_project", directory or os.getcwd());
|
|
3
|
+
|
|
4
|
+
project_dir = directory if directory else os.getcwd();
|
|
5
|
+
if not os.path.isdir(project_dir) {
|
|
6
|
+
tool_end("-> not found", error=True);
|
|
7
|
+
return f"Error: directory not found: {project_dir}";
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
# Find all .jac files (skip .cl.jac for now — jac check doesn't support them)
|
|
11
|
+
jac_files: list = [];
|
|
12
|
+
for (dirpath, dirnames, filenames) in os.walk(project_dir) {
|
|
13
|
+
# Skip hidden dirs, node_modules, __pycache__
|
|
14
|
+
dirnames[:] = [d for d in dirnames if not d.startswith(".") and d not in ["node_modules", "__pycache__", "venv"]];
|
|
15
|
+
for fname in filenames {
|
|
16
|
+
if fname.endswith(".jac") and not fname.endswith(".cl.jac") {
|
|
17
|
+
jac_files.append(os.path.join(dirpath, fname));
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if not jac_files {
|
|
23
|
+
tool_end("-> no .jac files");
|
|
24
|
+
return "No .jac files found in " + project_dir;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
# Phase 1: Auto-fix mechanical patterns before checking
|
|
28
|
+
auto_fixes_total = 0;
|
|
29
|
+
for fp in jac_files {
|
|
30
|
+
fixes = _auto_fix_mechanical(fp);
|
|
31
|
+
auto_fixes_total += fixes;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
# Phase 2: Run full jac check on each file, collect ALL errors
|
|
35
|
+
all_errors: list = [];
|
|
36
|
+
files_checked = 0;
|
|
37
|
+
files_clean = 0;
|
|
38
|
+
|
|
39
|
+
for fp in jac_files {
|
|
40
|
+
rel_path = os.path.relpath(fp, project_dir);
|
|
41
|
+
try {
|
|
42
|
+
result = subprocess.run(
|
|
43
|
+
["jac", "check", fp],
|
|
44
|
+
capture_output=True,
|
|
45
|
+
text=True,
|
|
46
|
+
timeout=30
|
|
47
|
+
);
|
|
48
|
+
files_checked += 1;
|
|
49
|
+
|
|
50
|
+
if result.returncode == 0 {
|
|
51
|
+
files_clean += 1;
|
|
52
|
+
} else {
|
|
53
|
+
output = (result.stderr or result.stdout or "").strip();
|
|
54
|
+
if output {
|
|
55
|
+
# Parse individual errors
|
|
56
|
+
for line in output.split("\n") {
|
|
57
|
+
line = line.strip();
|
|
58
|
+
if line.startswith("✖ Error:") or line.startswith("⚠ warning:") {
|
|
59
|
+
all_errors.append(f" {rel_path}: {line}");
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
} except subprocess.TimeoutExpired {
|
|
65
|
+
all_errors.append(f" {rel_path}: TIMEOUT (>30s)");
|
|
66
|
+
files_checked += 1;
|
|
67
|
+
} except Exception as e {
|
|
68
|
+
all_errors.append(f" {rel_path}: CHECK FAILED - {str(e)}");
|
|
69
|
+
files_checked += 1;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
# Phase 3: Build structured report
|
|
74
|
+
parts: list = [];
|
|
75
|
+
|
|
76
|
+
if auto_fixes_total > 0 {
|
|
77
|
+
parts.append(f"Auto-fixed {auto_fixes_total} mechanical issues (root()->root(), deprecated syntax).");
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
parts.append(f"Checked {files_checked} files: {files_clean} clean, {files_checked - files_clean} with issues.");
|
|
81
|
+
|
|
82
|
+
if all_errors {
|
|
83
|
+
# Categorize errors vs warnings
|
|
84
|
+
errors = [e for e in all_errors if "Error:" in e];
|
|
85
|
+
warnings = [e for e in all_errors if "warning:" in e];
|
|
86
|
+
|
|
87
|
+
if errors {
|
|
88
|
+
parts.append(f"\nErrors ({len(errors)}):");
|
|
89
|
+
for e in errors[:30] {
|
|
90
|
+
parts.append(e);
|
|
91
|
+
# Add fix hint
|
|
92
|
+
hint = _get_error_hint(e);
|
|
93
|
+
if hint {
|
|
94
|
+
parts.append(f" → {hint}");
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
if len(errors) > 30 {
|
|
98
|
+
parts.append(f" ... and {len(errors) - 30} more errors");
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if warnings {
|
|
103
|
+
parts.append(f"\nWarnings ({len(warnings)}):");
|
|
104
|
+
for w in warnings[:10] {
|
|
105
|
+
parts.append(w);
|
|
106
|
+
}
|
|
107
|
+
if len(warnings) > 10 {
|
|
108
|
+
parts.append(f" ... and {len(warnings) - 10} more warnings");
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
track_error();
|
|
113
|
+
tool_end(f"-> {len(errors)} errors, {len(warnings)} warnings", error=len(errors) > 0);
|
|
114
|
+
} else {
|
|
115
|
+
parts.append("\nAll files pass type-check.");
|
|
116
|
+
tool_end(f"-> all clean");
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return "\n".join(parts);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def _auto_fix_mechanical(file_path: str) -> int {
|
|
124
|
+
try {
|
|
125
|
+
with open(file_path, "r") as f {
|
|
126
|
+
content = f.read();
|
|
127
|
+
}
|
|
128
|
+
} except Exception {
|
|
129
|
+
return 0;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
original = content;
|
|
133
|
+
is_client = file_path.endswith(".cl.jac");
|
|
134
|
+
|
|
135
|
+
# Fix 1: bare root → root() (deprecated)
|
|
136
|
+
# Only match lowercase 'root' not followed by '(' — skip Root (type name)
|
|
137
|
+
content = re.sub(r'(?<!\w)root(?!\s*\()(?!\w)', 'root()', content);
|
|
138
|
+
|
|
139
|
+
# Fix 2: (?:Type) → [?:Type] (deprecated filter syntax)
|
|
140
|
+
content = re.sub(r'\(\?:(\w+)\)', r'[?:\1]', content);
|
|
141
|
+
|
|
142
|
+
# Fix 3: (?:Type, cond) → [?:Type, cond] (deprecated with condition)
|
|
143
|
+
content = re.sub(r'\(\?:(\w+),([^)]+)\)', r'[?:\1,\2]', content);
|
|
144
|
+
|
|
145
|
+
# Fix 4: def: pub → def:pub (no space after colon)
|
|
146
|
+
content = re.sub(r'\bdef:\s+pub\b', 'def:pub', content);
|
|
147
|
+
content = re.sub(r'\bdef:\s+priv\b', 'def:priv', content);
|
|
148
|
+
|
|
149
|
+
# Fix 5: for p: Type in → for p in (no type annotation on loop var)
|
|
150
|
+
content = re.sub(r'\bfor\s+(\w+)\s*:\s*\w+\s+in\b', r'for \1 in', content);
|
|
151
|
+
|
|
152
|
+
# Fix 6: import path slashes → dots (.cl.jac files)
|
|
153
|
+
if is_client {
|
|
154
|
+
def _fix_slashes(m: Any) -> str {
|
|
155
|
+
prefix = m.group(1);
|
|
156
|
+
path = m.group(2);
|
|
157
|
+
return prefix + path.replace("/", ".") + '"';
|
|
158
|
+
}
|
|
159
|
+
content = re.sub(
|
|
160
|
+
r'(import\s+from\s+")((?:[.]*)[^"]*)"',
|
|
161
|
+
_fix_slashes,
|
|
162
|
+
content
|
|
163
|
+
);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
# Fix 7: className → class (in .cl.jac JSX)
|
|
167
|
+
if is_client {
|
|
168
|
+
content = re.sub(r'\bclassName\s*=', 'class=', content);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
# Count fixes and write if changed
|
|
172
|
+
if content != original {
|
|
173
|
+
fix_count = 0;
|
|
174
|
+
# Count each type of change
|
|
175
|
+
for (old_pat, new_pat) in [
|
|
176
|
+
("root()", "root()"), ("def:pub", "def:pub"),
|
|
177
|
+
("[?:", "[?:"), ("class=", "class=")
|
|
178
|
+
] {
|
|
179
|
+
fix_count += content.count(new_pat) - original.count(new_pat);
|
|
180
|
+
}
|
|
181
|
+
fix_count = max(fix_count, 1);
|
|
182
|
+
try {
|
|
183
|
+
with open(file_path, "w") as f {
|
|
184
|
+
f.write(content);
|
|
185
|
+
}
|
|
186
|
+
} except Exception {
|
|
187
|
+
return 0;
|
|
188
|
+
}
|
|
189
|
+
return fix_count;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
return 0;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
def _get_error_hint(error_line: str) -> str {
|
|
197
|
+
e = error_line.lower();
|
|
198
|
+
|
|
199
|
+
if "has no attribute" in e {
|
|
200
|
+
return "Field was removed or renamed. Check the object definition.";
|
|
201
|
+
}
|
|
202
|
+
if "type is unknown" in e and "cannot access" in e {
|
|
203
|
+
return "Add type annotation to the variable or cast with str()/int()/list().";
|
|
204
|
+
}
|
|
205
|
+
if "cannot return" in e and "expected" in e {
|
|
206
|
+
return "Return type doesn't match. Add explicit type annotation or cast.";
|
|
207
|
+
}
|
|
208
|
+
if "missing from nonetype" in e {
|
|
209
|
+
return "Variable might be None. Add null check: if var { var.field }";
|
|
210
|
+
}
|
|
211
|
+
if "cannot assign" in e and "unknown" in e {
|
|
212
|
+
return "Add type annotation: var: str = expression;";
|
|
213
|
+
}
|
|
214
|
+
if "bare 'root()'" in e {
|
|
215
|
+
return "Use root() instead of root (auto-fixable by validate_project).";
|
|
216
|
+
}
|
|
217
|
+
if "deprecated" in e and "(?:" in e {
|
|
218
|
+
return "Use [?:Type] instead of [?:Type] (auto-fixable by validate_project).";
|
|
219
|
+
}
|
|
220
|
+
if "parameter" in e and "type annotation" in e {
|
|
221
|
+
return "Add type annotation to parameter: param: Type.";
|
|
222
|
+
}
|
|
223
|
+
if "expected 'with'" in e and "can" in e {
|
|
224
|
+
return "Use 'def' for regular methods in obj. 'can' is only for walker/node abilities with 'with Type entry'.";
|
|
225
|
+
}
|
|
226
|
+
if "import:py" in e or "import: py" in e {
|
|
227
|
+
return "import:py is deprecated. Use 'import from module { name }' instead.";
|
|
228
|
+
}
|
|
229
|
+
if "self" in e and "missing" in e and "type" in e {
|
|
230
|
+
return "self is implicit in Jac — remove it from method parameters.";
|
|
231
|
+
}
|
|
232
|
+
if "pass" in e and "unexpected" in e {
|
|
233
|
+
return "'pass' is not a keyword in Jac. Use a no-op statement or empty block.";
|
|
234
|
+
}
|
|
235
|
+
return "";
|
|
236
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
impl web_fetch(url: str, format: str = "text") -> str {
|
|
2
|
+
tool_status("web_fetch", url);
|
|
3
|
+
if not url {
|
|
4
|
+
return "Error: url is required";
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
if url.startswith("http://") {
|
|
8
|
+
url = "https://" + url[7:];
|
|
9
|
+
}
|
|
10
|
+
if not url.startswith("https://") {
|
|
11
|
+
url = "https://" + url;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
try {
|
|
15
|
+
import httpx;
|
|
16
|
+
client = httpx.Client(timeout=30.0, follow_redirects=True);
|
|
17
|
+
response = client.get(
|
|
18
|
+
url, headers={"User-Agent": "JacCoder/0.1 (AI Coding Agent)"}
|
|
19
|
+
);
|
|
20
|
+
response.raise_for_status();
|
|
21
|
+
content = response.text;
|
|
22
|
+
} except Exception as e {
|
|
23
|
+
tool_end("-> error", error=True);
|
|
24
|
+
return f"Error fetching URL: {e}";
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if format == "html" {
|
|
28
|
+
(output, _) = truncate_output(content);
|
|
29
|
+
tool_end(f"-> {len(content)} chars");
|
|
30
|
+
return output;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
try {
|
|
34
|
+
import markdownify;
|
|
35
|
+
output = markdownify.markdownify(content, strip=["img", "script", "style"]);
|
|
36
|
+
} except ImportError {
|
|
37
|
+
output = re.sub(r'<[^>]+>', '', content);
|
|
38
|
+
output = re.sub(r'\s+', ' ', output).strip();
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
(output, _) = truncate_output(output);
|
|
42
|
+
tool_end(f"-> {len(output)} chars");
|
|
43
|
+
return output;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
impl web_search(query: str, num_results: int = 5) -> str {
|
|
48
|
+
tool_status("web_search", query);
|
|
49
|
+
if not query {
|
|
50
|
+
return "Error: query is required";
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
num_results = min(num_results, 10);
|
|
54
|
+
|
|
55
|
+
try {
|
|
56
|
+
import httpx;
|
|
57
|
+
client = httpx.Client(timeout=15.0, follow_redirects=True);
|
|
58
|
+
response = client.get(
|
|
59
|
+
"https://html.duckduckgo.com/html/",
|
|
60
|
+
params={"q": query},
|
|
61
|
+
headers={"User-Agent": "JacCoder/0.1"}
|
|
62
|
+
);
|
|
63
|
+
response.raise_for_status();
|
|
64
|
+
|
|
65
|
+
results = [];
|
|
66
|
+
result_pattern = r'<a[^>]*class="result__a"[^>]*href="([^"]*)"[^>]*>(.*?)</a>';
|
|
67
|
+
snippet_pattern = r'<a[^>]*class="result__snippet"[^>]*>(.*?)</a>';
|
|
68
|
+
urls = re.findall(result_pattern, response.text, re.DOTALL);
|
|
69
|
+
snippets = re.findall(snippet_pattern, response.text, re.DOTALL);
|
|
70
|
+
|
|
71
|
+
for i in range(min(len(urls), num_results)) {
|
|
72
|
+
(url, title) = urls[i];
|
|
73
|
+
title = re.sub(r'<[^>]+>', '', title).strip();
|
|
74
|
+
snippet = re.sub(r'<[^>]+>', '', snippets[i]).strip()
|
|
75
|
+
if i < len(snippets)
|
|
76
|
+
else "";
|
|
77
|
+
results.append(f"**{title}**\n{url}\n{snippet}");
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if not results {
|
|
81
|
+
tool_end("-> 0 results");
|
|
82
|
+
return "No results found.";
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
tool_end(f"-> {len(results)} results");
|
|
86
|
+
return "\n\n---\n\n".join(results);
|
|
87
|
+
} except Exception as e {
|
|
88
|
+
tool_end("-> error", error=True);
|
|
89
|
+
return f"Search error: {e}";
|
|
90
|
+
}
|
|
91
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import os;
|
|
2
|
+
import sys;
|
|
3
|
+
import time;
|
|
4
|
+
|
|
5
|
+
import from jaclang.jac0core { unitree as uni }
|
|
6
|
+
import from jaclang.jac0core.program { JacProgram }
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
"""Analyze a Jac project via compiler AST."""
|
|
10
|
+
def analyze_project(directory: str) -> str;
|
|
11
|
+
|
|
12
|
+
sem analyze_project = "Compiler-level analysis of an entire Jac project. Returns all nodes (with fields and types), walkers (with entry points showing which nodes they visit), edges, client .cl.jac components (with state), objects, import map, and export map. Use this to understand a project before writing code — gives accurate structural knowledge in one call instead of reading files manually.";
|
|
13
|
+
sem analyze_project.directory = "Root directory of the Jac project to analyze";
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
"""Find a symbol's definition and usages."""
|
|
17
|
+
def find_symbol(name: str, directory: str = "") -> str;
|
|
18
|
+
|
|
19
|
+
sem find_symbol = "Look up any Jac symbol (node, walker, edge, obj, enum, component) by name. Returns: definition file and line, all fields with types, all abilities (including which use by llm()), every file that imports or references it, walker entry points that visit it, and the exact import statement to use. Use this when you need details about a specific type or need to know the correct import path.";
|
|
20
|
+
sem find_symbol.name = "The symbol name to look up (e.g. 'User', 'create_post', 'LoginForm')";
|
|
21
|
+
sem find_symbol.directory = "Root directory of the Jac project to search";
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import subprocess;
|
|
2
|
+
|
|
3
|
+
import from jac_coder.util.sandbox { sandbox_path }
|
|
4
|
+
import from jac_coder.util.tool_output { truncate_output, tool_status, tool_end }
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
"""Type-check a .jac file."""
|
|
8
|
+
def jac_check(file: str) -> str;
|
|
9
|
+
|
|
10
|
+
"""Run a Jac file."""
|
|
11
|
+
def jac_run(file: str, args: str = "") -> str;
|
jac_coder/tool/mcp.jac
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import from jac_coder.mcp_manager { mcp_call_tool }
|
|
2
|
+
import from jac_coder.util.tool_output { tool_status, tool_end, truncate_output }
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
"""Call a tool provided by a connected MCP server.
|
|
6
|
+
|
|
7
|
+
Use this when the user's context includes available MCP tools.
|
|
8
|
+
Check the 'Available MCP tools' section in your context first,
|
|
9
|
+
then call the appropriate tool with the correct server_name and tool_name.
|
|
10
|
+
|
|
11
|
+
Arguments:
|
|
12
|
+
server_name — the MCP server name (from Available MCP tools list)
|
|
13
|
+
tool_name — the tool to call (from Available MCP tools list)
|
|
14
|
+
arguments_json — JSON string of arguments matching the tool's input schema.
|
|
15
|
+
Use \"{}\" if the tool takes no arguments.
|
|
16
|
+
Example: \"{\\\"code\\\": \\\"glob x: int = 1;\\\"}\" """
|
|
17
|
+
def mcp_call(server_name: str, tool_name: str, arguments_json: str = "{}") -> str;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import os;
|
|
2
|
+
|
|
3
|
+
import from jac_coder.util.tool_output { truncate_output, tool_status, tool_end }
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
"""Set the preview URL for the current thread/request."""
|
|
7
|
+
def set_preview_url(url: str) -> None;
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
"""Quick screenshot + brief AI description of the rendered UI.
|
|
11
|
+
Use for a quick look at the preview — does NOT evaluate against intent.
|
|
12
|
+
Prefer evaluate_preview() for verification after changes.
|
|
13
|
+
Do NOT call this before evaluate_preview — it takes its own screenshot.
|
|
14
|
+
Only use after writing or modifying .cl.jac (client-side) files.
|
|
15
|
+
delay is in seconds (default 2s, max 5s)."""
|
|
16
|
+
def capture_preview(
|
|
17
|
+
viewport: str = "desktop",
|
|
18
|
+
delay: int = 2
|
|
19
|
+
) -> str;
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
"""Screenshot + PASS/PARTIAL/FAIL evaluation of the live preview against the
|
|
23
|
+
user's intended design. This is the PRIMARY preview verification tool.
|
|
24
|
+
Takes its own screenshot — do NOT call capture_preview first.
|
|
25
|
+
Call this ONCE after modifying .cl.jac files to verify the UI matches intent.
|
|
26
|
+
delay is in seconds (default 2s, max 5s)."""
|
|
27
|
+
def evaluate_preview(
|
|
28
|
+
intent: str,
|
|
29
|
+
viewport: str = "desktop",
|
|
30
|
+
delay: int = 2
|
|
31
|
+
) -> str;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import os;
|
|
2
|
+
|
|
3
|
+
import from pathlib { Path }
|
|
4
|
+
import from jac_coder.events { emit_event }
|
|
5
|
+
import from jac_coder.util.sandbox { sandbox_path }
|
|
6
|
+
import from jac_coder.util.tool_output { truncate_output, tool_status, tool_end }
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
"""Scaffold a new Jac project."""
|
|
10
|
+
def scaffold_project(project_type: str, name: str, directory: str) -> str;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import subprocess;
|
|
2
|
+
|
|
3
|
+
import from pathlib { Path }
|
|
4
|
+
import from jac_coder.util.sandbox { sandbox_path }
|
|
5
|
+
import from jac_coder.util.tool_output { truncate_output, tool_status, tool_end }
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
"""Regex search across files."""
|
|
9
|
+
def grep_search(
|
|
10
|
+
pattern: str, path: str = ".", file_pattern: str = "", max_results: int = 50
|
|
11
|
+
) -> str;
|
|
12
|
+
|
|
13
|
+
"""Find files by glob pattern."""
|
|
14
|
+
def find_files(pattern: str, directory: str = ".") -> str;
|
jac_coder/tool/shell.jac
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import os;
|
|
2
|
+
import signal;
|
|
3
|
+
import subprocess;
|
|
4
|
+
|
|
5
|
+
import from jac_coder.util.sandbox { sandbox_path, get_sandbox_root }
|
|
6
|
+
import from jac_coder.util.tool_output { truncate_output, tool_status, tool_end }
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
"""Execute a shell command."""
|
|
10
|
+
def bash_exec(
|
|
11
|
+
command: str, workdir: str = ".", timeout: int = 120, background: bool = False
|
|
12
|
+
) -> str;
|
jac_coder/tool/task.jac
ADDED
jac_coder/tool/think.jac
ADDED
jac_coder/tool/todo.jac
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import json;
|
|
2
|
+
|
|
3
|
+
import from jac_coder.util.tool_output { tool_status, tool_end }
|
|
4
|
+
|
|
5
|
+
glob _todos: dict = {};
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
"""Create or update a todo list."""
|
|
9
|
+
def update_todos(todos_json: str) -> str;
|
|
10
|
+
|
|
11
|
+
"""Get current todos."""
|
|
12
|
+
def get_todos(session_id: str = "default") -> list;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import os;
|
|
2
|
+
import re;
|
|
3
|
+
import subprocess;
|
|
4
|
+
|
|
5
|
+
import from jac_coder.util.tool_output { tool_status, tool_end }
|
|
6
|
+
import from jac_coder.events { track_file, track_error }
|
|
7
|
+
|
|
8
|
+
"""Batch validate and auto-fix all modified .jac files in a project."""
|
|
9
|
+
def validate_project(directory: str = "") -> str;
|
|
10
|
+
sem validate_project = "Run full type-check on all modified .jac files, auto-fix mechanical patterns (root()->root(), deprecated syntax), and return a structured error report with fix hints. Call this AFTER writing all files — not during.";
|
|
11
|
+
sem validate_project.directory = "Project directory to validate. If empty, uses current working directory.";
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import os;
|
|
2
|
+
|
|
3
|
+
import from byllm.lib { Model, Image }
|
|
4
|
+
|
|
5
|
+
# Vision model for preview analysis — separate from the main coding LLM
|
|
6
|
+
glob vision_model_name: str = os.environ.get("JAC_VISION_MODEL", "gpt-4o");
|
|
7
|
+
glob vision_llm = Model(model_name=vision_model_name);
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
"""Analyze a single UI screenshot and return a text description."""
|
|
11
|
+
def analyze_screenshot(screenshot: Image, prompt: str) -> str
|
|
12
|
+
by vision_llm(temperature=0.1, max_tokens=300);
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
"""Compare two UI screenshots (before/after) and return an evaluation."""
|
|
16
|
+
def compare_screenshots(before: Image, after: Image, prompt: str) -> str
|
|
17
|
+
by vision_llm(temperature=0.1, max_tokens=1024);
|
jac_coder/tool/web.jac
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import re;
|
|
2
|
+
|
|
3
|
+
import from jac_coder.util.tool_output { truncate_output, tool_status, tool_end }
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
"""Fetch a URL as readable text."""
|
|
7
|
+
def web_fetch(url: str, format: str = "text") -> str;
|
|
8
|
+
|
|
9
|
+
"""Search the web."""
|
|
10
|
+
def web_search(query: str, num_results: int = 5) -> str;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import from jac_coder.util.tool_output {
|
|
2
|
+
truncate_output,
|
|
3
|
+
tool_status,
|
|
4
|
+
tool_end,
|
|
5
|
+
smart_path
|
|
6
|
+
}
|
|
7
|
+
import from jac_coder.util.colors {
|
|
8
|
+
RESET,
|
|
9
|
+
BOLD,
|
|
10
|
+
DIM,
|
|
11
|
+
RED,
|
|
12
|
+
GREEN,
|
|
13
|
+
YELLOW,
|
|
14
|
+
MAGENTA,
|
|
15
|
+
CYAN,
|
|
16
|
+
WHITE,
|
|
17
|
+
AGENT_COLORS
|
|
18
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
glob RESET: str = "\033[0m";
|
|
2
|
+
glob BOLD: str = "\033[1m";
|
|
3
|
+
glob DIM: str = "\033[2m";
|
|
4
|
+
|
|
5
|
+
glob RED: str = "\033[31m";
|
|
6
|
+
glob GREEN: str = "\033[32m";
|
|
7
|
+
glob YELLOW: str = "\033[33m";
|
|
8
|
+
glob MAGENTA: str = "\033[35m";
|
|
9
|
+
glob CYAN: str = "\033[36m";
|
|
10
|
+
glob WHITE: str = "\033[97m";
|
|
11
|
+
|
|
12
|
+
glob AGENT_COLORS: dict = {
|
|
13
|
+
"main": CYAN,
|
|
14
|
+
"build": GREEN,
|
|
15
|
+
"client": GREEN,
|
|
16
|
+
"server": YELLOW,
|
|
17
|
+
"integration": WHITE,
|
|
18
|
+
"plan": CYAN,
|
|
19
|
+
"explore": MAGENTA
|
|
20
|
+
};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
impl set_sandbox_root(sroot: str) -> None {
|
|
2
|
+
_tls.sandbox_root = os.path.realpath(sroot) if sroot else "";
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
impl get_sandbox_root() -> str {
|
|
7
|
+
return getattr(_tls, "sandbox_root", "");
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
impl sandbox_path(path: str) -> tuple {
|
|
12
|
+
sroot = get_sandbox_root();
|
|
13
|
+
if not sroot {
|
|
14
|
+
return (path, "");
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
if not path or path == "." {
|
|
18
|
+
return (sroot, "");
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if not os.path.isabs(path) {
|
|
22
|
+
path = os.path.join(sroot, path);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
real = os.path.realpath(path);
|
|
26
|
+
|
|
27
|
+
try {
|
|
28
|
+
common = os.path.commonpath([sroot, real]);
|
|
29
|
+
} except ValueError {
|
|
30
|
+
return ("", "Error: Access denied — path outside project directory.");
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if common != sroot {
|
|
34
|
+
return ("", "Error: Access denied — path outside project directory.");
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return (real, "");
|
|
38
|
+
}
|