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,10 @@
|
|
|
1
|
+
import from jac_coder.tool.guarded { write_file_guarded, edit_file_guarded }
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
"""Write a file with auto-validation."""
|
|
5
|
+
def write_code(file_path: str, content: str) -> str;
|
|
6
|
+
|
|
7
|
+
"""Find-and-replace with auto-validation."""
|
|
8
|
+
def edit_code(
|
|
9
|
+
file_path: str, old_string: str, new_string: str, replace_all: bool = False
|
|
10
|
+
) -> str;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import threading;
|
|
2
|
+
|
|
3
|
+
import from jac_coder.config { get_config }
|
|
4
|
+
import from jac_coder.util.tool_output { tool_status, tool_end }
|
|
5
|
+
import from jac_coder.events { track_file, track_error, emit_event }
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
glob _tl: Any = threading.local();
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def init_budget(total: int = 90) -> None;
|
|
12
|
+
def get_remaining_budget() -> int;
|
|
13
|
+
def consume_budget(used: int) -> None;
|
|
14
|
+
def get_recent_results_summary() -> str;
|
|
15
|
+
def get_project_summary_for_subagent() -> str;
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
"""Spawn a SubAgent for a task."""
|
|
19
|
+
def spawn_agent(task: str, mode: str = "worker") -> str;
|
|
20
|
+
|
|
21
|
+
sem spawn_agent = "Delegate a task to a focused SubAgent. mode='worker' for code changes (read+write), 'explorer' for investigation (read-only). Write a detailed instruction string including file paths, patterns to follow, and what success looks like.";
|
|
22
|
+
sem spawn_agent.task = "Detailed instruction for the SubAgent — include file paths, context, and success criteria";
|
|
23
|
+
sem spawn_agent.mode = "Capability scope: 'worker' (can modify files) or 'explorer' (read-only)";
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import os;
|
|
2
|
+
import re;
|
|
3
|
+
import time;
|
|
4
|
+
import subprocess;
|
|
5
|
+
import mimetypes;
|
|
6
|
+
|
|
7
|
+
import from pathlib { Path }
|
|
8
|
+
import from jac_coder.events { emit_event }
|
|
9
|
+
import from jac_coder.util.sandbox { sandbox_path }
|
|
10
|
+
import from jac_coder.util.tool_output { truncate_output, tool_status, tool_end }
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
"""Read a file with line numbers."""
|
|
14
|
+
def read_file(file_path: str, offset: int = 0, limit: int = 2000) -> str;
|
|
15
|
+
|
|
16
|
+
"""Write content to a file."""
|
|
17
|
+
def write_file(file_path: str, content: str) -> str;
|
|
18
|
+
|
|
19
|
+
"""Find-and-replace in a file."""
|
|
20
|
+
def edit_file(
|
|
21
|
+
file_path: str, old_string: str, new_string: str, replace_all: bool = False
|
|
22
|
+
) -> str;
|
|
23
|
+
|
|
24
|
+
"""List files matching a glob pattern."""
|
|
25
|
+
def list_files(directory: str = ".", pattern: str = "**/*") -> str;
|
jac_coder/tool/git.jac
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import os;
|
|
2
|
+
import subprocess;
|
|
3
|
+
|
|
4
|
+
import from jac_coder.util.tool_output { truncate_output, tool_status, tool_end }
|
|
5
|
+
import from jac_coder.util.sandbox { sandbox_path }
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
"""Show working tree status."""
|
|
9
|
+
def git_status() -> str;
|
|
10
|
+
|
|
11
|
+
"""Show diff of changes."""
|
|
12
|
+
def git_diff(ref: str = "HEAD") -> str;
|
|
13
|
+
|
|
14
|
+
"""Stage files and create a commit."""
|
|
15
|
+
def git_commit(message: str, files: list[str] = []) -> str;
|
|
16
|
+
|
|
17
|
+
"""Show recent commit history."""
|
|
18
|
+
def git_log(count: int = 10) -> str;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import os;
|
|
2
|
+
|
|
3
|
+
import from jac_coder.tool.shell { bash_exec }
|
|
4
|
+
import from jac_coder.util.colors { YELLOW, DIM, RESET }
|
|
5
|
+
import from jac_coder.permission { permission_engine }
|
|
6
|
+
import from jac_coder.tool.filesystem { write_file, edit_file }
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def _check_permission(tool_id: str, resource: str) -> str;
|
|
10
|
+
def _ask_user(tool_id: str, resource: str) -> str;
|
|
11
|
+
|
|
12
|
+
"""Write a file with permission check."""
|
|
13
|
+
def write_file_guarded(file_path: str, content: str) -> str;
|
|
14
|
+
|
|
15
|
+
"""Find-and-replace with permission check."""
|
|
16
|
+
def edit_file_guarded(
|
|
17
|
+
file_path: str, old_string: str, new_string: str, replace_all: bool = False
|
|
18
|
+
) -> str;
|
|
19
|
+
|
|
20
|
+
"""Run a shell command with permission check."""
|
|
21
|
+
def run_command(
|
|
22
|
+
command: str, workdir: str = ".", timeout: int = 120, background: bool = False
|
|
23
|
+
) -> str;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
def _enforce_check(result: str, file_path: str) -> str {
|
|
2
|
+
if "Error:" in result
|
|
3
|
+
and (
|
|
4
|
+
"Permission denied" in result
|
|
5
|
+
or "File not found" in result
|
|
6
|
+
or "old_string not found" in result
|
|
7
|
+
) {
|
|
8
|
+
return result;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
if not file_path.endswith(".jac") {
|
|
12
|
+
return result;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
if "VALIDATION ERRORS" in result {
|
|
16
|
+
return result + "\n\n[ACTION REQUIRED: Call jac_docs(query) to look up the correct Jac syntax BEFORE attempting to fix. Do NOT guess — look it up first, then fix.]";
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
if "RUNTIME ERRORS" in result {
|
|
20
|
+
return result + "\n\n[ACTION REQUIRED: Call jac_docs(query) to look up the correct Jac syntax BEFORE attempting to fix. Do NOT guess — look it up first, then fix.]";
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return result;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
impl write_code(file_path: str, content: str) -> str {
|
|
28
|
+
result = write_file_guarded(file_path, content);
|
|
29
|
+
return _enforce_check(result, file_path);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
impl edit_code(
|
|
34
|
+
file_path: str, old_string: str, new_string: str, replace_all: bool = False
|
|
35
|
+
) -> str {
|
|
36
|
+
result = edit_file_guarded(file_path, old_string, new_string, replace_all);
|
|
37
|
+
return _enforce_check(result, file_path);
|
|
38
|
+
}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
impl init_budget(total: int = 90) -> None {
|
|
2
|
+
_tl.budget = total;
|
|
3
|
+
_tl.budget_initialized = True;
|
|
4
|
+
_tl.recent_results = [];
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
impl get_remaining_budget() -> int {
|
|
9
|
+
return getattr(_tl, "budget", 0);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
impl consume_budget(used: int) -> None {
|
|
14
|
+
_tl.budget = max(0, getattr(_tl, "budget", 0) - used);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
impl get_recent_results_summary() -> str {
|
|
19
|
+
results = getattr(_tl, "recent_results", []);
|
|
20
|
+
if not results {
|
|
21
|
+
return "";
|
|
22
|
+
}
|
|
23
|
+
parts: list = [];
|
|
24
|
+
for r in results[-3:] {
|
|
25
|
+
line = f"- [{r.get('mode', 'worker')}] {r.get('task_preview', '')[:100]}";
|
|
26
|
+
fmod = r.get("files_modified", []);
|
|
27
|
+
if fmod {
|
|
28
|
+
line += f" -> modified: {', '.join(fmod)}";
|
|
29
|
+
}
|
|
30
|
+
errs = r.get("errors", []);
|
|
31
|
+
if errs {
|
|
32
|
+
line += f" -> errors: {', '.join(errs[:2])}";
|
|
33
|
+
}
|
|
34
|
+
parts.append(line);
|
|
35
|
+
}
|
|
36
|
+
return "Previous SubAgent results:\n" + "\n".join(parts);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
impl get_project_summary_for_subagent() -> str {
|
|
41
|
+
try {
|
|
42
|
+
import jac_coder.nodes as _nodes;
|
|
43
|
+
sessions = [root()-->(?:(_nodes.Session))](?status=="active");
|
|
44
|
+
if sessions {
|
|
45
|
+
sessions.sort(key=lambda s : s.updated_at, reverse=True);
|
|
46
|
+
proj_summary = sessions[0].project_summary;
|
|
47
|
+
if proj_summary {
|
|
48
|
+
return proj_summary;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
} except Exception { }
|
|
52
|
+
return "";
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
impl spawn_agent(task: str, mode: str = "worker") -> str {
|
|
57
|
+
tool_status("spawn_agent", f"[{mode}] {task[:80]}...");
|
|
58
|
+
|
|
59
|
+
if not task {
|
|
60
|
+
tool_end("-> no task", error=True);
|
|
61
|
+
return "Error: task description is required.";
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if mode not in ["worker", "explorer"] {
|
|
65
|
+
mode = "worker";
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
# Initialize per-thread budget on first SubAgent call in this chat() turn
|
|
69
|
+
if not getattr(_tl, "budget_initialized", False) {
|
|
70
|
+
cfg = get_config();
|
|
71
|
+
init_budget(cfg.max_react_iterations * 3);
|
|
72
|
+
}
|
|
73
|
+
remaining = get_remaining_budget();
|
|
74
|
+
if remaining <= 0 {
|
|
75
|
+
tool_end("-> budget exhausted", error=True);
|
|
76
|
+
return "Error: Iteration budget exhausted. Cannot spawn more SubAgents.";
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
# Enrich task with session context
|
|
80
|
+
project_ctx = get_project_summary_for_subagent();
|
|
81
|
+
recent_work = get_recent_results_summary();
|
|
82
|
+
|
|
83
|
+
enriched_parts: list = [task];
|
|
84
|
+
if project_ctx {
|
|
85
|
+
enriched_parts.append(f"\n\n--- Project Context ---\n{project_ctx}");
|
|
86
|
+
}
|
|
87
|
+
if recent_work {
|
|
88
|
+
enriched_parts.append(f"\n\n--- Recent SubAgent Work ---\n{recent_work}");
|
|
89
|
+
}
|
|
90
|
+
enriched_task = "".join(enriched_parts);
|
|
91
|
+
|
|
92
|
+
# Spawn SubAgent walker on the MainAgent node
|
|
93
|
+
import from jac_coder.nodes { WorkerAgent, ExplorerAgent }
|
|
94
|
+
import from jac_coder.walkers { ensure_main_agent }
|
|
95
|
+
;
|
|
96
|
+
|
|
97
|
+
try {
|
|
98
|
+
main_agent = ensure_main_agent();
|
|
99
|
+
|
|
100
|
+
# Spawn the walker on MainAgent — entry ability handles streaming + event forwarding
|
|
101
|
+
if mode == "worker" {
|
|
102
|
+
result = main_agent spawn WorkerAgent(task=enriched_task);
|
|
103
|
+
} else {
|
|
104
|
+
result = main_agent spawn ExplorerAgent(task=enriched_task);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
# Extract result from walker's has fields
|
|
108
|
+
content = result.result_content;
|
|
109
|
+
used_tools = result.result_tools;
|
|
110
|
+
fmod = result.result_files;
|
|
111
|
+
|
|
112
|
+
has_errs = False;
|
|
113
|
+
iters_used = len(used_tools) if used_tools else 5;
|
|
114
|
+
|
|
115
|
+
# Track budget and state
|
|
116
|
+
consume_budget(iters_used);
|
|
117
|
+
for fp in fmod {
|
|
118
|
+
track_file(fp);
|
|
119
|
+
}
|
|
120
|
+
if has_errs {
|
|
121
|
+
track_error();
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
# Record result for subsequent SubAgents in this turn
|
|
125
|
+
_tl.recent_results.append(
|
|
126
|
+
{
|
|
127
|
+
"mode": mode,
|
|
128
|
+
"task_preview": task[:200],
|
|
129
|
+
"files_modified": fmod,
|
|
130
|
+
"errors": ["error"] if has_errs else [],
|
|
131
|
+
"content_preview": content[:300] if content else ""
|
|
132
|
+
}
|
|
133
|
+
);
|
|
134
|
+
|
|
135
|
+
# Build result string
|
|
136
|
+
result_parts: list = [];
|
|
137
|
+
if content {
|
|
138
|
+
result_parts.append(content);
|
|
139
|
+
}
|
|
140
|
+
if fmod {
|
|
141
|
+
result_parts.append(f"\nFiles modified: {', '.join(fmod)}");
|
|
142
|
+
}
|
|
143
|
+
if has_errs {
|
|
144
|
+
result_parts.append(
|
|
145
|
+
"\nNote: Some errors were encountered during execution."
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
tool_end(f"-> {mode} done, {len(fmod)} files");
|
|
150
|
+
return "\n".join(result_parts) if result_parts else "SubAgent completed.";
|
|
151
|
+
} except Exception as e {
|
|
152
|
+
consume_budget(5);
|
|
153
|
+
tool_end(f"-> error: {e}", error=True);
|
|
154
|
+
track_error();
|
|
155
|
+
return f"Error: SubAgent failed - {str(e)}";
|
|
156
|
+
}
|
|
157
|
+
}
|