arkaos 4.9.0 → 4.10.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/VERSION +1 -1
- package/bin/arka +5 -1
- package/bin/arkaos +2 -2
- package/core/evals/__pycache__/__init__.cpython-313.pyc +0 -0
- package/core/evals/__pycache__/schema.cpython-313.pyc +0 -0
- package/core/evals/__pycache__/verdict_labels.cpython-313.pyc +0 -0
- package/core/registry/__pycache__/__init__.cpython-312.pyc +0 -0
- package/core/registry/__pycache__/generator.cpython-312.pyc +0 -0
- package/core/registry/__pycache__/generator.cpython-313.pyc +0 -0
- package/core/registry/generator.py +138 -110
- package/core/runtime/__pycache__/llm_cost_telemetry.cpython-314.pyc +0 -0
- package/departments/dev/skills/scaffold/SKILL.md +3 -3
- package/knowledge/commands-registry.json +4194 -2627
- package/knowledge/commands-registry.json.bak +4194 -2627
- package/package.json +1 -1
- package/pyproject.toml +1 -1
- package/scripts/__pycache__/dashboard-api.cpython-313.pyc +0 -0
- package/scripts/__pycache__/synapse-bridge.cpython-313.pyc +0 -0
- package/scripts/__pycache__/synapse-bridge.cpython-314.pyc +0 -0
- package/scripts/dashboard-api.py +2 -2
- package/scripts/synapse-bridge.py +1 -1
- package/scripts/tools/__pycache__/prompt_surface_benchmark.cpython-313.pyc +0 -0
- package/knowledge/commands-registry-v2.json +0 -3777
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
4.
|
|
1
|
+
4.10.0
|
package/bin/arka
CHANGED
|
@@ -137,7 +137,11 @@ case "${1:-}" in
|
|
|
137
137
|
REGISTRY="$REPO_DIR/knowledge/commands-registry.json"
|
|
138
138
|
case "${1:-}" in
|
|
139
139
|
rebuild)
|
|
140
|
-
|
|
140
|
+
# Shared resolver (venv-first) with plain python3 fallback —
|
|
141
|
+
# the generator is stdlib-only.
|
|
142
|
+
_ARKA_LIB="$REPO_DIR/config/hooks/_lib/arka_python.sh"
|
|
143
|
+
if [ -f "$_ARKA_LIB" ]; then . "$_ARKA_LIB"; else ARKA_PY="python3"; fi
|
|
144
|
+
(cd "$REPO_DIR" && "$ARKA_PY" -m core.registry.generator)
|
|
141
145
|
echo "Registry rebuilt: $(jq '._meta.total_commands' "$REGISTRY") commands."
|
|
142
146
|
;;
|
|
143
147
|
--json)
|
package/bin/arkaos
CHANGED
|
@@ -65,8 +65,8 @@ case "${1:-}" in
|
|
|
65
65
|
agents=$(jq -r '._meta.total_agents // 0' "$REPO_ROOT/knowledge/agents-registry-v2.json")
|
|
66
66
|
echo " Agents: ${agents}"
|
|
67
67
|
fi
|
|
68
|
-
if [ -n "$REPO_ROOT" ] && [ -f "$REPO_ROOT/knowledge/commands-registry
|
|
69
|
-
commands=$(jq -r '._meta.total_commands // 0' "$REPO_ROOT/knowledge/commands-registry
|
|
68
|
+
if [ -n "$REPO_ROOT" ] && [ -f "$REPO_ROOT/knowledge/commands-registry.json" ] && command -v jq &>/dev/null; then
|
|
69
|
+
commands=$(jq -r '._meta.total_commands // 0' "$REPO_ROOT/knowledge/commands-registry.json")
|
|
70
70
|
echo " Commands: ${commands}"
|
|
71
71
|
fi
|
|
72
72
|
echo ""
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -1,28 +1,47 @@
|
|
|
1
|
-
"""Commands registry generator.
|
|
2
|
-
|
|
3
|
-
Scans
|
|
4
|
-
|
|
1
|
+
"""Commands registry generator — single canonical source (M2 consolidation).
|
|
2
|
+
|
|
3
|
+
Scans every SKILL.md command table in the repo (arka orchestrator,
|
|
4
|
+
department SKILL.md files, and department sub-skills) and generates
|
|
5
|
+
``knowledge/commands-registry.json`` — the one registry consumed by
|
|
6
|
+
runtime routing (system-prompt.sh), Synapse L5 hints (synapse-bridge),
|
|
7
|
+
``bin/arka commands``, the dashboard API, and ``bin/arkaos`` status.
|
|
8
|
+
|
|
9
|
+
Replaces the retired bash ``bin/arka-registry-gen`` and the parallel
|
|
10
|
+
``commands-registry-v2.json`` (2026-07-09): one deterministic generator,
|
|
11
|
+
repo sources only — machine-local ``arka-ext-*``/``arka-pro-*`` skills
|
|
12
|
+
are intentionally NOT scanned so the committed file is reproducible;
|
|
13
|
+
``tests/python/test_commands_registry.py`` locks committed-vs-regen
|
|
14
|
+
drift the same way the agents registry is locked.
|
|
15
|
+
|
|
16
|
+
Department naming follows the command prefixes (``mkt``/``fin``/
|
|
17
|
+
``strat``), matching the runtime tables in CLAUDE.md.
|
|
5
18
|
"""
|
|
6
19
|
|
|
7
20
|
import json
|
|
8
21
|
import re
|
|
9
|
-
from datetime import datetime
|
|
22
|
+
from datetime import datetime, timezone
|
|
10
23
|
from pathlib import Path
|
|
11
|
-
from typing import Optional
|
|
12
24
|
|
|
25
|
+
# Directory name → command-prefix department slug.
|
|
26
|
+
DEPT_PREFIX = {
|
|
27
|
+
"marketing": "mkt",
|
|
28
|
+
"finance": "fin",
|
|
29
|
+
"strategy": "strat",
|
|
30
|
+
}
|
|
13
31
|
|
|
14
|
-
#
|
|
32
|
+
# Fallback keywords per department slug when a command has no entry in
|
|
33
|
+
# knowledge/commands-keywords.json — feeds Synapse L5 hint matching.
|
|
15
34
|
DEPARTMENT_KEYWORDS: dict[str, list[str]] = {
|
|
16
35
|
"dev": ["build", "code", "feature", "deploy", "test", "review", "scaffold", "debug",
|
|
17
36
|
"refactor", "api", "migration", "implement", "fix", "bug", "database", "architecture"],
|
|
18
|
-
"
|
|
19
|
-
|
|
37
|
+
"mkt": ["social", "content", "campaign", "post", "instagram", "linkedin", "twitter",
|
|
38
|
+
"tiktok", "seo", "marketing", "ads", "email", "growth", "analytics"],
|
|
20
39
|
"brand": ["brand", "logo", "colors", "palette", "mockup", "identity", "naming",
|
|
21
40
|
"positioning", "voice", "guidelines", "ux", "wireframe", "design", "ui"],
|
|
22
|
-
"
|
|
23
|
-
|
|
24
|
-
"
|
|
25
|
-
|
|
41
|
+
"fin": ["budget", "invoice", "revenue", "forecast", "profit", "financial", "invest",
|
|
42
|
+
"valuation", "cashflow", "expense", "pitch", "model", "scenario"],
|
|
43
|
+
"strat": ["strategy", "brainstorm", "market", "swot", "competitor", "roadmap",
|
|
44
|
+
"position", "blue ocean", "five forces", "tam", "moat", "growth"],
|
|
26
45
|
"ecom": ["store", "product", "shop", "shopify", "ecommerce", "catalog", "cart",
|
|
27
46
|
"checkout", "pricing", "marketplace", "rfm", "conversion", "cro"],
|
|
28
47
|
"kb": ["learn", "persona", "knowledge", "youtube", "transcribe", "research",
|
|
@@ -41,150 +60,159 @@ DEPARTMENT_KEYWORDS: dict[str, list[str]] = {
|
|
|
41
60
|
"circle", "gamification", "engagement", "moderate", "event"],
|
|
42
61
|
"sales": ["pipeline", "proposal", "discovery", "objection", "negotiate", "deal",
|
|
43
62
|
"close", "prospect", "spin", "challenger", "forecast"],
|
|
44
|
-
"
|
|
45
|
-
|
|
63
|
+
"leadership": ["leadership", "delegation", "1on1", "feedback", "culture", "hiring",
|
|
64
|
+
"performance review", "team build", "conflict", "okr"],
|
|
46
65
|
"org": ["org design", "hiring plan", "onboarding", "remote", "meeting",
|
|
47
66
|
"compensation", "decision", "team assess", "sop"],
|
|
48
67
|
}
|
|
49
68
|
|
|
69
|
+
# Metadata rules ported verbatim from the retired bin/arka-registry-gen.
|
|
70
|
+
_DEV_BRANCH_WORDS = {"feature", "api", "debug", "refactor", "db"}
|
|
71
|
+
_DEV_MODIFY_WORDS = {"scaffold", "deploy", "test"}
|
|
72
|
+
|
|
73
|
+
_TABLE_ROW_RE = re.compile(r"\|\s*`([^`]+)`\s*\|\s*([^|]+)\|")
|
|
74
|
+
_FRONTMATTER_NAME_RE = re.compile(r"^name:\s*(\S+)", re.MULTILINE)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def derive_command_id(command_text: str) -> str:
|
|
78
|
+
"""``/arka costs [period]`` → ``arka-costs``.
|
|
79
|
+
|
|
80
|
+
Drops everything from the first argument token (``<arg>``,
|
|
81
|
+
``[optional]``, ``--flag``) onward, so ids never embed argument
|
|
82
|
+
syntax (the old ``dev-onboard---ecosystem`` class of bug).
|
|
83
|
+
"""
|
|
84
|
+
tokens: list[str] = []
|
|
85
|
+
for token in command_text.lstrip("/").split():
|
|
86
|
+
if token.startswith(("<", "[", "--")):
|
|
87
|
+
break
|
|
88
|
+
tokens.append(token)
|
|
89
|
+
return "-".join(tokens).lower()
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def extract_lead_agent(text: str) -> str:
|
|
93
|
+
match = _FRONTMATTER_NAME_RE.search(text)
|
|
94
|
+
return match.group(1) if match else ""
|
|
95
|
+
|
|
50
96
|
|
|
51
97
|
def extract_commands_from_skill(skill_path: Path) -> list[dict]:
|
|
52
98
|
"""Extract commands from a SKILL.md file's command table.
|
|
53
99
|
|
|
54
|
-
Parses markdown tables with |
|
|
100
|
+
Parses markdown tables with | `/command` | Description | rows.
|
|
55
101
|
"""
|
|
56
102
|
if not skill_path.exists():
|
|
57
103
|
return []
|
|
58
104
|
|
|
59
|
-
text = skill_path.read_text()
|
|
105
|
+
text = skill_path.read_text(encoding="utf-8")
|
|
106
|
+
lead_agent = extract_lead_agent(text)
|
|
60
107
|
commands = []
|
|
61
|
-
|
|
62
|
-
# Find command tables (lines with | `/ ... ` | description | ...)
|
|
63
108
|
for line in text.split("\n"):
|
|
64
|
-
match =
|
|
65
|
-
if match:
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
109
|
+
match = _TABLE_ROW_RE.match(line)
|
|
110
|
+
if not match:
|
|
111
|
+
continue
|
|
112
|
+
command = match.group(1).strip()
|
|
113
|
+
description = match.group(2).strip()
|
|
114
|
+
if not command.startswith("/") or "Description" in description or description.startswith("-"):
|
|
115
|
+
continue
|
|
116
|
+
commands.append({
|
|
117
|
+
"command": command,
|
|
118
|
+
"description": description,
|
|
119
|
+
"lead_agent": lead_agent,
|
|
120
|
+
})
|
|
75
121
|
return commands
|
|
76
122
|
|
|
77
123
|
|
|
78
|
-
def
|
|
79
|
-
"""
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
124
|
+
def command_metadata(command_text: str) -> dict:
|
|
125
|
+
"""tier / requires_branch / modifies_code — bash-generator rules."""
|
|
126
|
+
if command_text.startswith("/dev "):
|
|
127
|
+
first_arg = command_text.split()[1] if len(command_text.split()) > 1 else ""
|
|
128
|
+
if first_arg in _DEV_BRANCH_WORDS:
|
|
129
|
+
return {"tier": 1, "requires_branch": True, "modifies_code": True}
|
|
130
|
+
if first_arg in _DEV_MODIFY_WORDS:
|
|
131
|
+
return {"tier": 2, "requires_branch": False, "modifies_code": True}
|
|
132
|
+
if first_arg == "security-audit":
|
|
133
|
+
return {"tier": 1, "requires_branch": False, "modifies_code": False}
|
|
134
|
+
return {"tier": 2, "requires_branch": False, "modifies_code": False}
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def _load_keywords_seed(base_dir: Path) -> dict:
|
|
138
|
+
seed_path = base_dir / "knowledge" / "commands-keywords.json"
|
|
139
|
+
if not seed_path.exists():
|
|
140
|
+
return {}
|
|
141
|
+
return json.loads(seed_path.read_text(encoding="utf-8"))
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def _department_slug(skill_path: Path) -> str:
|
|
145
|
+
# departments/<dir>/SKILL.md and departments/<dir>/skills/<s>/SKILL.md
|
|
146
|
+
parts = skill_path.parts
|
|
147
|
+
dir_name = parts[parts.index("departments") + 1]
|
|
148
|
+
return DEPT_PREFIX.get(dir_name, dir_name)
|
|
84
149
|
|
|
85
150
|
|
|
86
|
-
def
|
|
87
|
-
"""
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
151
|
+
def _skill_sources(base_dir: Path) -> list[tuple[Path, str]]:
|
|
152
|
+
"""(skill_path, department) in deterministic scan order.
|
|
153
|
+
|
|
154
|
+
Order matters: dedup keeps the FIRST occurrence of an id, so the
|
|
155
|
+
orchestrator wins over departments, departments over sub-skills.
|
|
156
|
+
"""
|
|
157
|
+
sources: list[tuple[Path, str]] = [(base_dir / "arka" / "SKILL.md", "arka")]
|
|
158
|
+
for path in sorted(base_dir.glob("departments/*/SKILL.md")):
|
|
159
|
+
sources.append((path, _department_slug(path)))
|
|
160
|
+
for path in sorted(base_dir.glob("departments/*/skills/*/SKILL.md")):
|
|
161
|
+
sources.append((path, _department_slug(path)))
|
|
162
|
+
return sources
|
|
94
163
|
|
|
95
164
|
|
|
96
165
|
def generate_commands_registry(
|
|
97
166
|
base_dir: str | Path,
|
|
98
167
|
output_path: str | Path,
|
|
99
168
|
) -> dict:
|
|
100
|
-
"""Generate commands-registry
|
|
101
|
-
|
|
102
|
-
Args:
|
|
103
|
-
base_dir: Project root directory.
|
|
104
|
-
output_path: Where to write the JSON registry.
|
|
105
|
-
|
|
106
|
-
Returns:
|
|
107
|
-
The registry dict.
|
|
108
|
-
"""
|
|
169
|
+
"""Generate the canonical commands-registry.json from SKILL.md files."""
|
|
109
170
|
base_dir = Path(base_dir)
|
|
110
171
|
output_path = Path(output_path)
|
|
172
|
+
keywords_seed = _load_keywords_seed(base_dir)
|
|
111
173
|
|
|
112
|
-
all_commands = []
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
"dev", "marketing", "brand", "finance", "strategy", "ecom", "kb",
|
|
117
|
-
"ops", "pm", "saas", "landing", "content", "community", "sales",
|
|
118
|
-
"leadership", "org", "quality",
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
# Scan department SKILL.md files
|
|
122
|
-
for skill_file in sorted(base_dir.glob("departments/*/SKILL.md")):
|
|
123
|
-
if skill_file.parent.name not in v2_departments:
|
|
124
|
-
continue
|
|
125
|
-
dept = determine_department(skill_file)
|
|
126
|
-
raw_commands = extract_commands_from_skill(skill_file)
|
|
127
|
-
keywords = DEPARTMENT_KEYWORDS.get(dept, [])
|
|
128
|
-
|
|
129
|
-
for cmd in raw_commands:
|
|
174
|
+
all_commands: list[dict] = []
|
|
175
|
+
seen_ids: set[str] = set()
|
|
176
|
+
for skill_path, dept in _skill_sources(base_dir):
|
|
177
|
+
for cmd in extract_commands_from_skill(skill_path):
|
|
130
178
|
command_text = cmd["command"]
|
|
131
|
-
|
|
132
|
-
cmd_id
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
)
|
|
138
|
-
|
|
179
|
+
cmd_id = derive_command_id(command_text)
|
|
180
|
+
if not cmd_id or cmd_id in seen_ids:
|
|
181
|
+
continue
|
|
182
|
+
seen_ids.add(cmd_id)
|
|
183
|
+
seed = keywords_seed.get(cmd_id, {})
|
|
184
|
+
keywords = seed.get("keywords") or DEPARTMENT_KEYWORDS.get(dept, [])[:8]
|
|
139
185
|
all_commands.append({
|
|
140
186
|
"id": cmd_id,
|
|
141
187
|
"command": command_text,
|
|
142
188
|
"department": dept,
|
|
143
189
|
"description": cmd["description"],
|
|
144
|
-
"
|
|
145
|
-
"
|
|
146
|
-
"
|
|
147
|
-
"
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
# Scan orchestrator SKILL.md
|
|
151
|
-
arka_skill = base_dir / "arka" / "SKILL.md"
|
|
152
|
-
if arka_skill.exists():
|
|
153
|
-
raw_commands = extract_commands_from_skill(arka_skill)
|
|
154
|
-
for cmd in raw_commands:
|
|
155
|
-
command_text = cmd["command"]
|
|
156
|
-
cmd_id = command_text.lstrip("/").replace(" ", "-").split("<")[0].rstrip("-")
|
|
157
|
-
all_commands.append({
|
|
158
|
-
"id": cmd_id,
|
|
159
|
-
"command": command_text,
|
|
160
|
-
"department": "arka",
|
|
161
|
-
"description": cmd["description"],
|
|
162
|
-
"keywords": [],
|
|
163
|
-
"tier": 3,
|
|
164
|
-
"modifies_code": False,
|
|
165
|
-
"requires_branch": False,
|
|
190
|
+
"lead_agent": cmd["lead_agent"],
|
|
191
|
+
"keywords": keywords,
|
|
192
|
+
"examples": seed.get("examples", []),
|
|
193
|
+
"source": "builtin",
|
|
194
|
+
**command_metadata(command_text),
|
|
166
195
|
})
|
|
167
196
|
|
|
168
|
-
# Build department summary
|
|
169
197
|
dept_counts: dict[str, int] = {}
|
|
170
198
|
for cmd in all_commands:
|
|
171
|
-
|
|
172
|
-
dept_counts[d] = dept_counts.get(d, 0) + 1
|
|
199
|
+
dept_counts[cmd["department"]] = dept_counts.get(cmd["department"], 0) + 1
|
|
173
200
|
|
|
174
201
|
registry = {
|
|
175
202
|
"_meta": {
|
|
176
|
-
"version": "
|
|
177
|
-
"generated": datetime.now().
|
|
203
|
+
"version": "3.0.0",
|
|
204
|
+
"generated": datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ"),
|
|
178
205
|
"total_commands": len(all_commands),
|
|
179
206
|
"generator": "core/registry/generator.py",
|
|
180
|
-
"departments": dept_counts,
|
|
207
|
+
"departments": dict(sorted(dept_counts.items())),
|
|
181
208
|
},
|
|
182
209
|
"commands": all_commands,
|
|
183
210
|
}
|
|
184
211
|
|
|
185
212
|
output_path.parent.mkdir(parents=True, exist_ok=True)
|
|
186
|
-
with open(output_path, "w") as f:
|
|
213
|
+
with open(output_path, "w", encoding="utf-8") as f:
|
|
187
214
|
json.dump(registry, f, indent=2, ensure_ascii=False)
|
|
215
|
+
f.write("\n")
|
|
188
216
|
|
|
189
217
|
return registry
|
|
190
218
|
|
|
@@ -193,7 +221,7 @@ if __name__ == "__main__":
|
|
|
193
221
|
base = Path(__file__).parent.parent.parent
|
|
194
222
|
reg = generate_commands_registry(
|
|
195
223
|
base,
|
|
196
|
-
base / "knowledge" / "commands-registry
|
|
224
|
+
base / "knowledge" / "commands-registry.json",
|
|
197
225
|
)
|
|
198
|
-
print(f"
|
|
226
|
+
print(f"Registry generated: {reg['_meta']['total_commands']} commands")
|
|
199
227
|
print(f"Departments: {reg['_meta']['departments']}")
|
|
Binary file
|
|
@@ -18,9 +18,9 @@ Create new projects from real git repositories with full automation: dependencie
|
|
|
18
18
|
## Commands
|
|
19
19
|
|
|
20
20
|
<!-- Column convention: command | DESCRIPTION | repo. The registry
|
|
21
|
-
generator (
|
|
22
|
-
description — a repo URL there ships garbled command
|
|
23
|
-
blocker, 2026-07-09). -->
|
|
21
|
+
generator (core/registry/generator.py) reads column 2 as the
|
|
22
|
+
user-facing description — a repo URL there ships garbled command
|
|
23
|
+
help (QG blocker, 2026-07-09). -->
|
|
24
24
|
| Command | Description | Git Repository |
|
|
25
25
|
|---------|-------------|----------------|
|
|
26
26
|
| `/dev scaffold laravel <name>` | Scaffold a Laravel app into ~/Herd from the starter repo | `https://${GIT_HOST}/laravel/laravel.git` (override with `ARKAOS_LARAVEL_STARTER_REPO` env) |
|