akernel-runtime 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.
- akernel_runtime-0.1.0.dist-info/METADATA +270 -0
- akernel_runtime-0.1.0.dist-info/RECORD +40 -0
- akernel_runtime-0.1.0.dist-info/WHEEL +5 -0
- akernel_runtime-0.1.0.dist-info/entry_points.txt +2 -0
- akernel_runtime-0.1.0.dist-info/licenses/LICENSE +201 -0
- akernel_runtime-0.1.0.dist-info/licenses/NOTICE +4 -0
- akernel_runtime-0.1.0.dist-info/top_level.txt +1 -0
- context_kernel/__init__.py +4 -0
- context_kernel/__main__.py +5 -0
- context_kernel/agent_reports.py +188 -0
- context_kernel/benchmarks.py +493 -0
- context_kernel/budget.py +72 -0
- context_kernel/cli.py +2953 -0
- context_kernel/context.py +161 -0
- context_kernel/evals.py +347 -0
- context_kernel/global_memory.py +126 -0
- context_kernel/loop.py +1617 -0
- context_kernel/marketplace.py +194 -0
- context_kernel/marketplace_data/skills/context_budget.json +27 -0
- context_kernel/marketplace_data/skills/context_compaction.json +27 -0
- context_kernel/marketplace_data/skills/edit_file.json +27 -0
- context_kernel/marketplace_data/skills/index.json +66 -0
- context_kernel/marketplace_data/skills/long_task_planning.json +27 -0
- context_kernel/marketplace_data/skills/multi_file_bugfix.json +28 -0
- context_kernel/memory.py +515 -0
- context_kernel/models.py +144 -0
- context_kernel/planner.py +155 -0
- context_kernel/policy.py +271 -0
- context_kernel/project.py +317 -0
- context_kernel/providers.py +1264 -0
- context_kernel/report_costs.py +375 -0
- context_kernel/runner.py +78 -0
- context_kernel/skills.py +318 -0
- context_kernel/state_writer.py +108 -0
- context_kernel/storage.py +171 -0
- context_kernel/tasks.py +549 -0
- context_kernel/text.py +42 -0
- context_kernel/tokenizer.py +22 -0
- context_kernel/tools.py +544 -0
- context_kernel/verifier.py +77 -0
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
from .models import utc_now
|
|
8
|
+
from .storage import Workspace
|
|
9
|
+
from .tokenizer import estimate_tokens
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
PROJECT_PROFILE_VERSION = 1
|
|
13
|
+
IGNORED_DIRS = {
|
|
14
|
+
".akernel",
|
|
15
|
+
".git",
|
|
16
|
+
".mypy_cache",
|
|
17
|
+
".pytest_cache",
|
|
18
|
+
".ruff_cache",
|
|
19
|
+
".sandbox",
|
|
20
|
+
".venv",
|
|
21
|
+
"__pycache__",
|
|
22
|
+
"build",
|
|
23
|
+
"dist",
|
|
24
|
+
"node_modules",
|
|
25
|
+
"target",
|
|
26
|
+
}
|
|
27
|
+
KEY_FILE_NAMES = {
|
|
28
|
+
".cursorrules",
|
|
29
|
+
"AGENTS.md",
|
|
30
|
+
"CLAUDE.md",
|
|
31
|
+
"README.md",
|
|
32
|
+
"README.rst",
|
|
33
|
+
"pyproject.toml",
|
|
34
|
+
"setup.py",
|
|
35
|
+
"requirements.txt",
|
|
36
|
+
"pytest.ini",
|
|
37
|
+
"tox.ini",
|
|
38
|
+
"package.json",
|
|
39
|
+
"pnpm-lock.yaml",
|
|
40
|
+
"yarn.lock",
|
|
41
|
+
"Cargo.toml",
|
|
42
|
+
"go.mod",
|
|
43
|
+
}
|
|
44
|
+
INSTRUCTION_FILE_NAMES = [
|
|
45
|
+
"AGENTS.md",
|
|
46
|
+
".akernel/AGENTS.md",
|
|
47
|
+
"CLAUDE.md",
|
|
48
|
+
".cursorrules",
|
|
49
|
+
".github/copilot-instructions.md",
|
|
50
|
+
]
|
|
51
|
+
MAX_PROJECT_INSTRUCTION_CHARS = 4000
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def scan_project(workspace: Workspace, *, update_config: bool = True) -> dict[str, Any]:
|
|
55
|
+
files = list_project_files(workspace.root)
|
|
56
|
+
file_names = {path.name for path in files}
|
|
57
|
+
suffixes = {path.suffix.lower() for path in files}
|
|
58
|
+
top_level = {path.name for path in workspace.root.iterdir()} if workspace.root.exists() else set()
|
|
59
|
+
|
|
60
|
+
languages = detect_languages(file_names, suffixes)
|
|
61
|
+
package_managers = detect_package_managers(file_names)
|
|
62
|
+
commands = detect_commands(workspace.root, file_names, top_level, package_managers)
|
|
63
|
+
command_roots = detect_command_roots(languages, package_managers, commands)
|
|
64
|
+
key_files = detect_key_files(workspace.root, files)
|
|
65
|
+
instructions = detect_project_instructions(workspace.root)
|
|
66
|
+
|
|
67
|
+
profile = {
|
|
68
|
+
"version": PROJECT_PROFILE_VERSION,
|
|
69
|
+
"generated_at": utc_now(),
|
|
70
|
+
"root": str(workspace.root),
|
|
71
|
+
"languages": languages,
|
|
72
|
+
"package_managers": package_managers,
|
|
73
|
+
"commands": commands,
|
|
74
|
+
"command_roots": command_roots,
|
|
75
|
+
"key_files": key_files,
|
|
76
|
+
"instructions": instructions,
|
|
77
|
+
"file_summary": {
|
|
78
|
+
"scanned_files": len(files),
|
|
79
|
+
"top_level_entries": sorted(list(top_level))[:40],
|
|
80
|
+
},
|
|
81
|
+
"summary": summarize_project(languages, package_managers, commands, key_files),
|
|
82
|
+
}
|
|
83
|
+
Workspace.write_json(workspace.project_file, profile)
|
|
84
|
+
if update_config:
|
|
85
|
+
extend_command_policy(workspace, command_roots)
|
|
86
|
+
return profile
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def load_project_profile(workspace: Workspace) -> dict[str, Any] | None:
|
|
90
|
+
if not workspace.project_file.exists():
|
|
91
|
+
return None
|
|
92
|
+
return Workspace.read_json(workspace.project_file)
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def compact_project_profile(profile: dict[str, Any] | None, *, max_tokens: int = 220) -> dict[str, Any] | None:
|
|
96
|
+
if not profile:
|
|
97
|
+
return None
|
|
98
|
+
compact = {
|
|
99
|
+
"summary": profile.get("summary", ""),
|
|
100
|
+
"languages": profile.get("languages", [])[:6],
|
|
101
|
+
"package_managers": profile.get("package_managers", [])[:4],
|
|
102
|
+
"commands": profile.get("commands", {}),
|
|
103
|
+
"key_files": profile.get("key_files", [])[:12],
|
|
104
|
+
"command_roots": profile.get("command_roots", [])[:12],
|
|
105
|
+
"instructions": compact_project_instructions(profile.get("instructions", [])),
|
|
106
|
+
}
|
|
107
|
+
if estimate_tokens(compact) <= max_tokens:
|
|
108
|
+
return compact
|
|
109
|
+
compact["key_files"] = compact["key_files"][:6]
|
|
110
|
+
compact["commands"] = {
|
|
111
|
+
key: value
|
|
112
|
+
for key, value in compact["commands"].items()
|
|
113
|
+
if key in {"test", "lint", "build"}
|
|
114
|
+
}
|
|
115
|
+
compact["instructions"] = compact["instructions"][:2]
|
|
116
|
+
return compact
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def list_project_files(root: Path, *, max_files: int = 5000) -> list[Path]:
|
|
120
|
+
files: list[Path] = []
|
|
121
|
+
if not root.exists():
|
|
122
|
+
return files
|
|
123
|
+
stack = [root]
|
|
124
|
+
while stack and len(files) < max_files:
|
|
125
|
+
current = stack.pop()
|
|
126
|
+
try:
|
|
127
|
+
entries = sorted(current.iterdir(), key=lambda item: item.name.lower())
|
|
128
|
+
except OSError:
|
|
129
|
+
continue
|
|
130
|
+
for entry in entries:
|
|
131
|
+
if entry.name in IGNORED_DIRS:
|
|
132
|
+
continue
|
|
133
|
+
if entry.is_dir():
|
|
134
|
+
stack.append(entry)
|
|
135
|
+
elif entry.is_file():
|
|
136
|
+
files.append(entry)
|
|
137
|
+
if len(files) >= max_files:
|
|
138
|
+
break
|
|
139
|
+
return files
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def detect_languages(file_names: set[str], suffixes: set[str]) -> list[str]:
|
|
143
|
+
languages: list[str] = []
|
|
144
|
+
if {".py"}.intersection(suffixes) or {"pyproject.toml", "setup.py", "requirements.txt"}.intersection(file_names):
|
|
145
|
+
languages.append("python")
|
|
146
|
+
if {".js", ".jsx", ".ts", ".tsx"}.intersection(suffixes) or "package.json" in file_names:
|
|
147
|
+
languages.append("javascript/typescript")
|
|
148
|
+
if ".rs" in suffixes or "Cargo.toml" in file_names:
|
|
149
|
+
languages.append("rust")
|
|
150
|
+
if ".go" in suffixes or "go.mod" in file_names:
|
|
151
|
+
languages.append("go")
|
|
152
|
+
if ".java" in suffixes or {"pom.xml", "build.gradle", "build.gradle.kts"}.intersection(file_names):
|
|
153
|
+
languages.append("java")
|
|
154
|
+
return languages or ["unknown"]
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
def detect_package_managers(file_names: set[str]) -> list[str]:
|
|
158
|
+
managers: list[str] = []
|
|
159
|
+
if "pyproject.toml" in file_names:
|
|
160
|
+
managers.append("python/pyproject")
|
|
161
|
+
if "requirements.txt" in file_names:
|
|
162
|
+
managers.append("python/pip")
|
|
163
|
+
if "package.json" in file_names:
|
|
164
|
+
if "pnpm-lock.yaml" in file_names:
|
|
165
|
+
managers.append("node/pnpm")
|
|
166
|
+
elif "yarn.lock" in file_names:
|
|
167
|
+
managers.append("node/yarn")
|
|
168
|
+
else:
|
|
169
|
+
managers.append("node/npm")
|
|
170
|
+
if "Cargo.toml" in file_names:
|
|
171
|
+
managers.append("rust/cargo")
|
|
172
|
+
if "go.mod" in file_names:
|
|
173
|
+
managers.append("go")
|
|
174
|
+
return managers
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
def detect_commands(
|
|
178
|
+
root: Path,
|
|
179
|
+
file_names: set[str],
|
|
180
|
+
top_level: set[str],
|
|
181
|
+
package_managers: list[str],
|
|
182
|
+
) -> dict[str, str]:
|
|
183
|
+
commands: dict[str, str] = {}
|
|
184
|
+
if "pyproject.toml" in file_names or "requirements.txt" in file_names or "setup.py" in file_names:
|
|
185
|
+
if "pytest.ini" in file_names or any(path.name.startswith("test_") and path.suffix == ".py" for path in list_project_files(root / "tests", max_files=200)):
|
|
186
|
+
commands["test"] = "python -m pytest"
|
|
187
|
+
elif "tests" in top_level:
|
|
188
|
+
commands["test"] = "python -m unittest discover -s tests"
|
|
189
|
+
commands["install"] = "python -m pip install -e ."
|
|
190
|
+
|
|
191
|
+
package_json = root / "package.json"
|
|
192
|
+
if package_json.exists():
|
|
193
|
+
package_data = read_package_json(package_json)
|
|
194
|
+
scripts = package_data.get("scripts", {}) if isinstance(package_data, dict) else {}
|
|
195
|
+
runner = "pnpm" if "node/pnpm" in package_managers else "yarn" if "node/yarn" in package_managers else "npm"
|
|
196
|
+
if isinstance(scripts, dict):
|
|
197
|
+
if "test" in scripts:
|
|
198
|
+
commands.setdefault("test", f"{runner} test")
|
|
199
|
+
if "lint" in scripts:
|
|
200
|
+
commands["lint"] = f"{runner} run lint"
|
|
201
|
+
if "build" in scripts:
|
|
202
|
+
commands["build"] = f"{runner} run build"
|
|
203
|
+
commands.setdefault("install_node", f"{runner} install")
|
|
204
|
+
|
|
205
|
+
if "Cargo.toml" in file_names:
|
|
206
|
+
commands.setdefault("test", "cargo test")
|
|
207
|
+
commands.setdefault("build", "cargo build")
|
|
208
|
+
if "go.mod" in file_names:
|
|
209
|
+
commands.setdefault("test", "go test ./...")
|
|
210
|
+
commands.setdefault("build", "go build ./...")
|
|
211
|
+
return commands
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
def detect_command_roots(languages: list[str], package_managers: list[str], commands: dict[str, str]) -> list[str]:
|
|
215
|
+
roots = {"akernel", "akernel.exe", "git"}
|
|
216
|
+
if "python" in languages or any(manager.startswith("python/") for manager in package_managers):
|
|
217
|
+
roots.update({"py", "pytest", "python", "python.exe"})
|
|
218
|
+
if any(manager.startswith("node/") for manager in package_managers):
|
|
219
|
+
roots.update({"node", "node.exe", "npm", "npm.cmd", "npx", "npx.cmd", "pnpm", "pnpm.cmd", "yarn", "yarn.cmd"})
|
|
220
|
+
if "rust" in languages:
|
|
221
|
+
roots.update({"cargo", "cargo.exe", "rustc", "rustc.exe"})
|
|
222
|
+
if "go" in languages:
|
|
223
|
+
roots.update({"go", "go.exe"})
|
|
224
|
+
for command in commands.values():
|
|
225
|
+
root = command.split()[0].strip().casefold()
|
|
226
|
+
if root:
|
|
227
|
+
roots.add(root)
|
|
228
|
+
return sorted(roots)
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
def detect_key_files(root: Path, files: list[Path]) -> list[str]:
|
|
232
|
+
key_files: list[str] = []
|
|
233
|
+
for path in files:
|
|
234
|
+
relative = path.relative_to(root).as_posix()
|
|
235
|
+
if path.name in KEY_FILE_NAMES or relative.startswith(".github/workflows/"):
|
|
236
|
+
key_files.append(relative)
|
|
237
|
+
return sorted(key_files)[:24]
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
def detect_project_instructions(root: Path) -> list[dict[str, Any]]:
|
|
241
|
+
instructions: list[dict[str, Any]] = []
|
|
242
|
+
for name in INSTRUCTION_FILE_NAMES:
|
|
243
|
+
path = root / name
|
|
244
|
+
if not path.exists() or not path.is_file():
|
|
245
|
+
continue
|
|
246
|
+
try:
|
|
247
|
+
text = path.read_text(encoding="utf-8-sig", errors="replace")
|
|
248
|
+
except OSError:
|
|
249
|
+
continue
|
|
250
|
+
content = text.strip()
|
|
251
|
+
if not content:
|
|
252
|
+
continue
|
|
253
|
+
instructions.append(
|
|
254
|
+
{
|
|
255
|
+
"path": name.replace("\\", "/"),
|
|
256
|
+
"content": content[:MAX_PROJECT_INSTRUCTION_CHARS],
|
|
257
|
+
"truncated": len(content) > MAX_PROJECT_INSTRUCTION_CHARS,
|
|
258
|
+
"estimated_tokens": estimate_tokens(content[:MAX_PROJECT_INSTRUCTION_CHARS]),
|
|
259
|
+
}
|
|
260
|
+
)
|
|
261
|
+
return instructions
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
def compact_project_instructions(instructions: Any) -> list[dict[str, Any]]:
|
|
265
|
+
if not isinstance(instructions, list):
|
|
266
|
+
return []
|
|
267
|
+
compact: list[dict[str, Any]] = []
|
|
268
|
+
for item in instructions[:3]:
|
|
269
|
+
if not isinstance(item, dict):
|
|
270
|
+
continue
|
|
271
|
+
compact.append(
|
|
272
|
+
{
|
|
273
|
+
"path": str(item.get("path", "")),
|
|
274
|
+
"content": str(item.get("content", ""))[:1800],
|
|
275
|
+
"truncated": bool(item.get("truncated", False)),
|
|
276
|
+
}
|
|
277
|
+
)
|
|
278
|
+
return compact
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
def extend_command_policy(workspace: Workspace, command_roots: list[str]) -> None:
|
|
282
|
+
config = workspace.load_config()
|
|
283
|
+
policy = config.setdefault("command_policy", {})
|
|
284
|
+
existing = policy.get("allowed_roots", [])
|
|
285
|
+
merged = []
|
|
286
|
+
seen: set[str] = set()
|
|
287
|
+
for root in list(existing) + command_roots:
|
|
288
|
+
if not isinstance(root, str) or not root.strip():
|
|
289
|
+
continue
|
|
290
|
+
normalized = root.strip().casefold()
|
|
291
|
+
if normalized in seen:
|
|
292
|
+
continue
|
|
293
|
+
seen.add(normalized)
|
|
294
|
+
merged.append(normalized)
|
|
295
|
+
policy["allowed_roots"] = merged
|
|
296
|
+
workspace.save_config(config)
|
|
297
|
+
|
|
298
|
+
|
|
299
|
+
def read_package_json(path: Path) -> dict[str, Any]:
|
|
300
|
+
try:
|
|
301
|
+
data = json.loads(path.read_text(encoding="utf-8"))
|
|
302
|
+
except (OSError, json.JSONDecodeError):
|
|
303
|
+
return {}
|
|
304
|
+
return data if isinstance(data, dict) else {}
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
def summarize_project(
|
|
308
|
+
languages: list[str],
|
|
309
|
+
package_managers: list[str],
|
|
310
|
+
commands: dict[str, str],
|
|
311
|
+
key_files: list[str],
|
|
312
|
+
) -> str:
|
|
313
|
+
language_text = ", ".join(languages)
|
|
314
|
+
manager_text = ", ".join(package_managers) if package_managers else "none"
|
|
315
|
+
command_text = ", ".join(f"{name}=`{command}`" for name, command in commands.items()) or "none"
|
|
316
|
+
key_text = ", ".join(key_files[:6]) if key_files else "none"
|
|
317
|
+
return f"languages={language_text}; package_managers={manager_text}; commands={command_text}; key_files={key_text}"
|