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,362 @@
|
|
|
1
|
+
# Fullstack Jac Pitfalls — .cl.jac Frontend + Backend Integration
|
|
2
|
+
|
|
3
|
+
> These rules apply when building fullstack Jaseci apps with .cl.jac frontend components.
|
|
4
|
+
> Everything in core_jac.md ALSO applies. This file covers the additional fullstack gotchas.
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## File Types
|
|
9
|
+
|
|
10
|
+
- `.jac` — Backend code (nodes, endpoints, walkers). Uses Python runtime.
|
|
11
|
+
- `.cl.jac` — Frontend code (React components). Compiles to JavaScript.
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## BEFORE WRITING ANY CODE
|
|
16
|
+
|
|
17
|
+
**Read `jac.toml` FIRST.** Only use npm packages listed in `[dependencies.npm]`. Do NOT import unlisted libraries. NEVER assume any UI library is available. Use plain Tailwind CSS + inline SVG for styling and icons.
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## Backend Rules (.jac)
|
|
22
|
+
|
|
23
|
+
### 1. ALL backend code goes in `main.jac`
|
|
24
|
+
|
|
25
|
+
Do NOT split backend into multiple files. Nodes, endpoints, walkers — all in `main.jac`.
|
|
26
|
+
|
|
27
|
+
### 2. Endpoint types: `def:pub` vs `def:priv` vs `walker:pub`
|
|
28
|
+
|
|
29
|
+
```jac
|
|
30
|
+
# Public — anyone can call
|
|
31
|
+
def:pub get_items() -> list {
|
|
32
|
+
return [{"id": str(i.id), "title": i.title} for i in [root-->][?:Item]];
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
# Private — requires login, per-user isolated root
|
|
36
|
+
def:priv get_my_items() -> list {
|
|
37
|
+
return [{"id": str(i.id), "title": i.title} for i in [root-->][?:Item]];
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
# Walker — for complex graph traversal
|
|
41
|
+
walker :pub get_details {
|
|
42
|
+
has item_id: str;
|
|
43
|
+
can find with Root entry {
|
|
44
|
+
for i in [-->][?:Item] {
|
|
45
|
+
if str(i.id) == self.item_id { visit i; return; }
|
|
46
|
+
}
|
|
47
|
+
report {"error": "not found"};
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### 3. Return dicts/lists from endpoints — NOT nodes directly
|
|
53
|
+
|
|
54
|
+
```jac
|
|
55
|
+
# WRONG — returns node objects
|
|
56
|
+
def:pub get_items() -> list { return [root-->][?:Item]; }
|
|
57
|
+
|
|
58
|
+
# RIGHT — serialize to dicts
|
|
59
|
+
def:pub get_items() -> list {
|
|
60
|
+
return [{"id": str(i.id), "title": i.title} for i in [root-->][?:Item]];
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### 4. Node CRUD pattern
|
|
65
|
+
|
|
66
|
+
```jac
|
|
67
|
+
node Item {
|
|
68
|
+
has title: str;
|
|
69
|
+
has done: bool = False;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
# Create + connect to root (auto-persists in SQLite)
|
|
73
|
+
root ++> Item(title="Buy milk");
|
|
74
|
+
|
|
75
|
+
# Query
|
|
76
|
+
items = [root-->][?:Item];
|
|
77
|
+
found = [root-->][?:Item](?title == "Buy milk")[0];
|
|
78
|
+
|
|
79
|
+
# Delete
|
|
80
|
+
del found;
|
|
81
|
+
# or: root del--> found;
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### 5. Frontend entry point in main.jac
|
|
85
|
+
|
|
86
|
+
```jac
|
|
87
|
+
cl import from .components.Layout { Layout }
|
|
88
|
+
cl {
|
|
89
|
+
def:pub app() -> JsxElement { return <Layout />; }
|
|
90
|
+
}
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
## Frontend Rules (.cl.jac)
|
|
96
|
+
|
|
97
|
+
### 6. Component props — ALWAYS use `props: dict`
|
|
98
|
+
|
|
99
|
+
```jac
|
|
100
|
+
# WRONG — individual params on JsxElement components
|
|
101
|
+
def:pub Header(title: str, theme: str) -> JsxElement { ... }
|
|
102
|
+
|
|
103
|
+
# RIGHT — always use props: dict, destructure inside
|
|
104
|
+
def:pub Header(props: dict) -> JsxElement {
|
|
105
|
+
title = props.title or "";
|
|
106
|
+
theme = props.theme or "light";
|
|
107
|
+
return <header>{title}</header>;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
# EXCEPTION: app(), layout(), page() take no props — that's fine
|
|
111
|
+
def:pub app() -> JsxElement { return <Layout />; }
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
Components returning `JsxElement` MUST accept `props: dict` as the single parameter (or no params). NEVER use individual typed params like `(title: str, count: int)`.
|
|
115
|
+
|
|
116
|
+
Also use `className`, NOT `class` for HTML attributes in JSX:
|
|
117
|
+
```jac
|
|
118
|
+
# WRONG
|
|
119
|
+
<div class="container">
|
|
120
|
+
|
|
121
|
+
# RIGHT
|
|
122
|
+
<div className="container">
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### 7. Jac syntax, NOT JavaScript
|
|
126
|
+
|
|
127
|
+
```
|
|
128
|
+
WRONG (JS) → RIGHT (Jac)
|
|
129
|
+
──────────────────────────────────────────────────
|
|
130
|
+
const x = 5; → x = 5;
|
|
131
|
+
let items = []; → has items: list = [];
|
|
132
|
+
x ? a : b → a if x else b
|
|
133
|
+
() => { ... } → def handler() -> None { ... }
|
|
134
|
+
`hello ${name}` → f"hello {name}"
|
|
135
|
+
x === y → x == y
|
|
136
|
+
console.log(x) → print(x)
|
|
137
|
+
x.length → len(x)
|
|
138
|
+
parseInt(x) → int(x)
|
|
139
|
+
x.toString() → str(x)
|
|
140
|
+
items.map(fn) → {[<Item /> for item in items]}
|
|
141
|
+
items.filter(fn) → [i for i in items if cond]
|
|
142
|
+
items.push(x) → items = items + [x];
|
|
143
|
+
useEffect(() => {}, []) → can with entry { ... }
|
|
144
|
+
useState(0) → has count: int = 0;
|
|
145
|
+
!condition → not condition
|
|
146
|
+
new Date() → Reflect.construct(Date, [])
|
|
147
|
+
window.open(url) → globalThis.open(url, "_blank")
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
### 7. State — `has` auto-generates useState
|
|
151
|
+
|
|
152
|
+
```jac
|
|
153
|
+
has count: int = 0; # auto-creates count + setCount
|
|
154
|
+
has name: str = "";
|
|
155
|
+
has items: list = [];
|
|
156
|
+
|
|
157
|
+
count = count + 1; # calls setCount internally
|
|
158
|
+
items = items + [newItem]; # new reference = re-render
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
**NEVER define `setX` yourself. NEVER use `.append()` for state** — it mutates in place (no re-render). Always `items = items + [x]`.
|
|
162
|
+
|
|
163
|
+
### 8. Effects — `can with entry/exit`
|
|
164
|
+
|
|
165
|
+
```jac
|
|
166
|
+
can with entry { ... } # mount (useEffect(fn, []))
|
|
167
|
+
async can with entry { ... } # async mount
|
|
168
|
+
can with [dep1, dep2] entry { ... } # dependency watch
|
|
169
|
+
can with exit { ... } # cleanup/unmount
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
**NEVER use `useEffect(lambda...)` — that is OLD syntax.**
|
|
173
|
+
|
|
174
|
+
### 9. Event handlers — NEVER inline in JSX
|
|
175
|
+
|
|
176
|
+
```jac
|
|
177
|
+
# WRONG — lambda in JSX
|
|
178
|
+
<button onClick={lambda -> None { count = count + 1; }}>
|
|
179
|
+
|
|
180
|
+
# WRONG — inline def
|
|
181
|
+
<button onClick={def(e: any) -> None { count = count + 1; }}>
|
|
182
|
+
|
|
183
|
+
# RIGHT — named function ABOVE return, passed by name
|
|
184
|
+
def handle_click() -> None {
|
|
185
|
+
count = count + 1;
|
|
186
|
+
}
|
|
187
|
+
return <button onClick={handle_click}>Click</button>;
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
ALL event handlers must be named `def` functions defined BEFORE `return`.
|
|
191
|
+
|
|
192
|
+
### 10. Functions MUST be defined BEFORE `return`
|
|
193
|
+
|
|
194
|
+
```jac
|
|
195
|
+
# WRONG — helper after return (unreachable)
|
|
196
|
+
return <div>{render_item(data)}</div>;
|
|
197
|
+
def render_item(d: dict) -> JsxElement { ... }
|
|
198
|
+
|
|
199
|
+
# RIGHT — define above
|
|
200
|
+
def render_item(d: dict) -> JsxElement { ... }
|
|
201
|
+
return <div>{render_item(data)}</div>;
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
### 11. No comments inside JSX
|
|
205
|
+
|
|
206
|
+
```jac
|
|
207
|
+
# WRONG — all of these crash
|
|
208
|
+
return <div>
|
|
209
|
+
{# comment}
|
|
210
|
+
<!-- HTML comment -->
|
|
211
|
+
{/* JS comment */}
|
|
212
|
+
</div>;
|
|
213
|
+
|
|
214
|
+
# RIGHT — comments ABOVE JSX
|
|
215
|
+
# Render the list
|
|
216
|
+
return <div>...</div>;
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
### 12. `sv import` — positional args ONLY
|
|
220
|
+
|
|
221
|
+
In .cl.jac, kwargs compile to a SINGLE dict arg. Server gets wrong data.
|
|
222
|
+
|
|
223
|
+
```jac
|
|
224
|
+
# WRONG — kwargs
|
|
225
|
+
resp = await calc(a=2, b=4, op="add");
|
|
226
|
+
# Server receives: {"a": {"a":2, "b":4, "op":"add"}}
|
|
227
|
+
|
|
228
|
+
# RIGHT — positional, order matches def:pub signature
|
|
229
|
+
resp = await calc(2, 4, "add");
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
### 13. JS constructors need `Reflect.construct`
|
|
233
|
+
|
|
234
|
+
In .cl.jac, `ClassName()` without `new` returns wrong type or throws. Jac has no `new` keyword.
|
|
235
|
+
|
|
236
|
+
```jac
|
|
237
|
+
# WRONG
|
|
238
|
+
year = Date().getFullYear(); # CRASH: string has no method getFullYear
|
|
239
|
+
ws = WebSocket("ws://localhost"); # CRASH: must be called with new
|
|
240
|
+
|
|
241
|
+
# RIGHT
|
|
242
|
+
year = Reflect.construct(Date, []).getFullYear();
|
|
243
|
+
ws = Reflect.construct(WebSocket, ["ws://localhost"]);
|
|
244
|
+
|
|
245
|
+
# Safe statics (no Reflect needed):
|
|
246
|
+
Date.now(); JSON.parse(); Math.random();
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
Classes that ALWAYS need Reflect.construct: Date, WebSocket, TextDecoder, TextEncoder, URL, URLSearchParams, FormData, AbortController, RegExp, Error, Worker, Headers, Request, Response.
|
|
250
|
+
|
|
251
|
+
### 14. Browser global name conflicts
|
|
252
|
+
|
|
253
|
+
NEVER define functions named: `open`, `close`, `print`, `fetch`, `focus`, `blur`, `scroll`, `alert`, `confirm`, `prompt`, `stop`, `find`
|
|
254
|
+
|
|
255
|
+
Use `handleOpen`, `handleClose`, `handleFetch`, etc.
|
|
256
|
+
|
|
257
|
+
### 15. Display numbers/booleans with `str()`
|
|
258
|
+
|
|
259
|
+
```jac
|
|
260
|
+
# WRONG — may render nothing
|
|
261
|
+
<span>{count}</span>
|
|
262
|
+
|
|
263
|
+
# RIGHT
|
|
264
|
+
<span>{str(count)}</span>
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
### 16. ALWAYS guard everything defensively
|
|
268
|
+
|
|
269
|
+
Undefined access CRASHES the whole app in .cl.jac.
|
|
270
|
+
|
|
271
|
+
```jac
|
|
272
|
+
# State — always init with correct types, NEVER None
|
|
273
|
+
has items: list = [];
|
|
274
|
+
has user: dict = {};
|
|
275
|
+
has loading: bool = True;
|
|
276
|
+
|
|
277
|
+
# Dict access — use "key" in dict, not .get()
|
|
278
|
+
name = item["name"] if "name" in item else "";
|
|
279
|
+
|
|
280
|
+
# Async results — always wrap in try/except
|
|
281
|
+
async can with entry {
|
|
282
|
+
try {
|
|
283
|
+
result = await get_items();
|
|
284
|
+
items = result or [];
|
|
285
|
+
} except Exception as e {
|
|
286
|
+
error = str(e);
|
|
287
|
+
}
|
|
288
|
+
loading = False;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
# Props — always default
|
|
292
|
+
items = props.items or [];
|
|
293
|
+
title = props.title or "";
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
---
|
|
297
|
+
|
|
298
|
+
## Import Rules (CRITICAL — memorize the pattern)
|
|
299
|
+
|
|
300
|
+
### In .jac files (backend)
|
|
301
|
+
|
|
302
|
+
```jac
|
|
303
|
+
import from datetime { datetime } # Python module
|
|
304
|
+
cl import from .components.Layout { Layout } # Client component (NO quotes, cl prefix)
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
### In .cl.jac files (frontend)
|
|
308
|
+
|
|
309
|
+
```jac
|
|
310
|
+
# Client-to-client — WITH quotes, DOTS not slashes
|
|
311
|
+
import from ".Header" { Header } # same directory
|
|
312
|
+
import from "..hooks.useTodos" { useTodos } # parent directory
|
|
313
|
+
import from "...lib.utils" { cn } # 2 levels up
|
|
314
|
+
|
|
315
|
+
# Server calls — sv import, NO quotes
|
|
316
|
+
sv import from ..main { get_todos, add_todo }
|
|
317
|
+
|
|
318
|
+
# NPM packages — WITH quotes
|
|
319
|
+
import from "clsx" { cn }
|
|
320
|
+
|
|
321
|
+
# Runtime — cl import, WITH quotes
|
|
322
|
+
cl import from "@jac/runtime" { jacLogin, Outlet }
|
|
323
|
+
|
|
324
|
+
# CSS — WITH quotes
|
|
325
|
+
import "..styles.global.css";
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
### Dot level cheat sheet
|
|
329
|
+
|
|
330
|
+
```
|
|
331
|
+
. = same directory
|
|
332
|
+
.. = 1 level up (parent)
|
|
333
|
+
... = 2 levels up
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
### NEVER do
|
|
337
|
+
|
|
338
|
+
- `cl sv import` — never combine prefixes
|
|
339
|
+
- `"..main"` with quotes on sv import — sv import has NO quotes
|
|
340
|
+
- Slashes in import paths — always dots
|
|
341
|
+
- File extensions in imports — never `.cl`, `.jac`, `.cl.jac`
|
|
342
|
+
|
|
343
|
+
---
|
|
344
|
+
|
|
345
|
+
## Project Structure
|
|
346
|
+
|
|
347
|
+
```
|
|
348
|
+
project-root/
|
|
349
|
+
├── jac.toml # config + deps
|
|
350
|
+
├── main.jac # ALL backend: nodes + endpoints + cl { app() }
|
|
351
|
+
├── components/ # .cl.jac components (one per file)
|
|
352
|
+
│ ├── Layout.cl.jac # Root layout — imports child components
|
|
353
|
+
│ ├── Header.cl.jac # Nav/header
|
|
354
|
+
│ └── ItemList.cl.jac # Data display (calls hook)
|
|
355
|
+
├── hooks/ # .cl.jac data hooks (sv import + state)
|
|
356
|
+
│ └── useItems.cl.jac
|
|
357
|
+
├── pages/ # .jac file-based routing (optional)
|
|
358
|
+
├── lib/ # .cl.jac shared utilities
|
|
359
|
+
└── styles/ # CSS files
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
Layout.cl.jac is the root layout. Data logic lives in hooks/, called by child components — NOT by Layout.
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# Coding Workflow & Validation Discipline
|
|
2
|
+
|
|
3
|
+
> Follow this workflow EVERY TIME you write Jac code. No shortcuts.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Before Writing Code
|
|
8
|
+
|
|
9
|
+
1. **Read `jac.toml`** — check npm deps, project config, entry point.
|
|
10
|
+
2. **Call `jac_docs(query)`** — look up syntax for what you're building. Do NOT guess.
|
|
11
|
+
3. **If existing project**: call `analyze_project(directory)` to understand structure.
|
|
12
|
+
4. **Call `jac_docs` again** before EACH new file type (`.jac` vs `.cl.jac` have different syntax).
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## Build Order (fullstack apps)
|
|
17
|
+
|
|
18
|
+
1. `main.jac` backend first — nodes + endpoints
|
|
19
|
+
2. Hooks — `hooks/useX.cl.jac` (sv import from backend)
|
|
20
|
+
3. Leaf components — `Header.cl.jac`, `ItemCard.cl.jac` (no data logic)
|
|
21
|
+
4. Container components — `ItemList.cl.jac` (calls hooks, renders leaf components)
|
|
22
|
+
5. Layout — `Layout.cl.jac` LAST (imports child components)
|
|
23
|
+
6. App entry — `cl { def:pub app() }` in `main.jac`
|
|
24
|
+
|
|
25
|
+
**Build dependencies bottom-up.** Never import a file that doesn't exist yet.
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## While Writing Code
|
|
30
|
+
|
|
31
|
+
- Use `write_code` / `edit_code` — they auto-run `jac_check` per file for syntax errors.
|
|
32
|
+
- Do NOT call `jac_check` or `jac check` manually.
|
|
33
|
+
- Do NOT use `run_command` for jac check.
|
|
34
|
+
- If `write_code`/`edit_code` report errors → call `jac_docs(query)` to look up correct syntax → fix → retry.
|
|
35
|
+
- Call `jac_docs` at least once every 3-4 tool calls. If unsure about ANY syntax, look it up immediately.
|
|
36
|
+
- Use ABSOLUTE file paths for all operations.
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## After Writing ALL Files
|
|
41
|
+
|
|
42
|
+
### Step 1: Cross-file import check
|
|
43
|
+
|
|
44
|
+
Before validating, manually verify:
|
|
45
|
+
- Every `import from "..."` / `sv import from ...` references a file that exists
|
|
46
|
+
- sv import function names match actual `def:pub` names in `main.jac`
|
|
47
|
+
- Component imports use correct dot-levels (`.` same dir, `..` one up, `...` two up)
|
|
48
|
+
- No duplicate component names or endpoint names across files
|
|
49
|
+
|
|
50
|
+
### Step 2: Validate
|
|
51
|
+
|
|
52
|
+
Call `validate_project(directory)` ONCE after all files are written.
|
|
53
|
+
- It batch type-checks all `.jac` files
|
|
54
|
+
- It auto-fixes common patterns (root→root(), (?:)→[?:])
|
|
55
|
+
- It returns all type errors with hints
|
|
56
|
+
|
|
57
|
+
### Step 3: Fix and re-validate
|
|
58
|
+
|
|
59
|
+
- Read the report from `validate_project`
|
|
60
|
+
- Fix all remaining errors
|
|
61
|
+
- Call `validate_project()` again to confirm clean
|
|
62
|
+
|
|
63
|
+
### Step 4: Preview log check (fullstack apps)
|
|
64
|
+
|
|
65
|
+
- Read the LAST 50 lines of `.jac-preview.log`
|
|
66
|
+
- Look for: `[Client]`, `ERROR`, `Exception`, `undefined`, `module not found`
|
|
67
|
+
- Common runtime errors:
|
|
68
|
+
- "module not found" → file doesn't exist OR stale HMR cache
|
|
69
|
+
- "undefined" → missing prop, uninitialized state, wrong import
|
|
70
|
+
- "Function X failed" → sv import name doesn't match backend def:pub
|
|
71
|
+
- "422" → request body schema mismatch
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
|
|
75
|
+
## When Stuck
|
|
76
|
+
|
|
77
|
+
1. Call `jac_docs(query)` — look up the correct syntax
|
|
78
|
+
2. Call `analyze_project` or `find_symbol` — check existing patterns
|
|
79
|
+
3. If same error 2-3 times → `ask_question` to ask the user
|
|
80
|
+
4. If editing same file 3+ times → step back, check assumptions
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
## HMR "module not found" Fix
|
|
85
|
+
|
|
86
|
+
1. File exists but compiled JS is stale
|
|
87
|
+
2. Do trivial edit on the TARGET file (not the importing file) — add a blank line
|
|
88
|
+
3. HMR recompiles, import resolves
|
jac_coder/events.jac
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import time;
|
|
2
|
+
import threading;
|
|
3
|
+
|
|
4
|
+
glob _tl = threading.local();
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def _callbacks() -> list {
|
|
8
|
+
if not hasattr(_tl, "callbacks") {
|
|
9
|
+
_tl.callbacks = [];
|
|
10
|
+
}
|
|
11
|
+
return _tl.callbacks;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def register_event_callback(cb: Any) -> None {
|
|
16
|
+
_callbacks().append(cb);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
def clear_event_callbacks() -> None {
|
|
20
|
+
_tl.callbacks = [];
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
def next_step() -> int {
|
|
24
|
+
if not hasattr(_tl, "step_counter") {
|
|
25
|
+
_tl.step_counter = 0;
|
|
26
|
+
}
|
|
27
|
+
if not hasattr(_tl, "turn_tools") {
|
|
28
|
+
_tl.turn_tools = 0;
|
|
29
|
+
}
|
|
30
|
+
_tl.step_counter += 1;
|
|
31
|
+
_tl.turn_tools += 1;
|
|
32
|
+
return _tl.step_counter;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
def reset_steps() -> None {
|
|
36
|
+
_tl.step_counter = 0;
|
|
37
|
+
_tl.active_tool = {};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
def get_active_tool() -> dict {
|
|
41
|
+
return getattr(_tl, "active_tool", {});
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
def set_active_tool(info: dict) -> None {
|
|
45
|
+
_tl.active_tool = info;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
def set_agent_prefix(prefix: str) -> None {
|
|
49
|
+
_tl.agent_prefix = prefix;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
def get_agent_prefix() -> str {
|
|
53
|
+
return getattr(_tl, "agent_prefix", "");
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
def start_turn() -> None {
|
|
57
|
+
_tl.turn_start = time.time();
|
|
58
|
+
_tl.turn_tools = 0;
|
|
59
|
+
_tl.turn_errors = 0;
|
|
60
|
+
_tl.turn_files = [];
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
def track_file(path: str) -> None {
|
|
64
|
+
if not hasattr(_tl, "turn_files") {
|
|
65
|
+
_tl.turn_files = [];
|
|
66
|
+
}
|
|
67
|
+
if path and path not in _tl.turn_files {
|
|
68
|
+
_tl.turn_files.append(path);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
def track_error() -> None {
|
|
73
|
+
if not hasattr(_tl, "turn_errors") {
|
|
74
|
+
_tl.turn_errors = 0;
|
|
75
|
+
}
|
|
76
|
+
_tl.turn_errors += 1;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
def emit_turn_summary() -> dict {
|
|
80
|
+
turn_start = getattr(_tl, "turn_start", 0.0);
|
|
81
|
+
turn_tools = getattr(_tl, "turn_tools", 0);
|
|
82
|
+
turn_errors = getattr(_tl, "turn_errors", 0);
|
|
83
|
+
turn_files = getattr(_tl, "turn_files", []);
|
|
84
|
+
duration_s = round(time.time() - turn_start, 1) if turn_start > 0 else 0.0;
|
|
85
|
+
summary = {
|
|
86
|
+
"tools_count": turn_tools,
|
|
87
|
+
"files_modified": list(turn_files),
|
|
88
|
+
"errors": turn_errors,
|
|
89
|
+
"duration_s": duration_s
|
|
90
|
+
};
|
|
91
|
+
emit_event("turn_summary", summary);
|
|
92
|
+
return summary;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
def emit_event(event_type: str, data: dict) -> None {
|
|
96
|
+
cbs = _callbacks();
|
|
97
|
+
if not cbs {
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
data["timestamp"] = time.time();
|
|
101
|
+
data["event_type"] = event_type;
|
|
102
|
+
for cb in list(cbs) {
|
|
103
|
+
try {
|
|
104
|
+
cb(event_type, data);
|
|
105
|
+
} except Exception as e {
|
|
106
|
+
import sys;
|
|
107
|
+
sys.stderr.write(f"[events] callback error: {e}\n");
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|