arkaos 5.1.0 → 5.2.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/CONSTITUTION.md +1 -1
- package/THE-ARKAOS-GUIDE.md +1 -1
- package/VERSION +1 -1
- package/arka/skills/comfyui/SKILL.md +2 -2
- package/core/governance/evidence_checks.py +14 -1
- package/core/sync/features/forge.yaml +1 -1
- package/core/sync/features/spec-gate.yaml +1 -1
- package/core/sync/features/workflow-tiers.yaml +1 -1
- package/departments/brand/SKILL.md +1 -1
- package/departments/dev/SKILL.md +1 -1
- package/harness/codex/AGENTS.md +1 -1
- package/harness/copilot/copilot-instructions.md +1 -1
- package/harness/cursor/rules/arkaos.mdc +2 -2
- package/harness/gemini/GEMINI.md +1 -1
- package/harness/opencode/AGENTS.md +1 -1
- package/harness/opencode/agents/arka-architect-gabriel.md +1 -1
- package/harness/opencode/agents/arka-brand-director-valentina.md +1 -1
- package/harness/opencode/agents/arka-cfo-helena.md +1 -1
- package/harness/opencode/agents/arka-chief-of-staff-afonso.md +1 -1
- package/harness/opencode/agents/arka-community-strategist-beatriz.md +1 -1
- package/harness/opencode/agents/arka-content-strategist-rafael.md +1 -1
- package/harness/opencode/agents/arka-conversion-strategist-ines.md +1 -1
- package/harness/opencode/agents/arka-coo-sofia.md +1 -1
- package/harness/opencode/agents/arka-copy-director-eduardo.md +1 -1
- package/harness/opencode/agents/arka-cqo-marta.md +1 -1
- package/harness/opencode/agents/arka-cto-marco.md +1 -1
- package/harness/opencode/agents/arka-design-ops-lead-iris.md +1 -1
- package/harness/opencode/agents/arka-ecom-director-ricardo.md +1 -1
- package/harness/opencode/agents/arka-knowledge-director-clara.md +1 -1
- package/harness/opencode/agents/arka-leadership-director-rodrigo.md +1 -1
- package/harness/opencode/agents/arka-marketing-director-luna.md +1 -1
- package/harness/opencode/agents/arka-ops-lead-daniel.md +1 -1
- package/harness/opencode/agents/arka-pm-director-carolina.md +1 -1
- package/harness/opencode/agents/arka-revops-lead-vicente.md +1 -1
- package/harness/opencode/agents/arka-saas-strategist-tiago.md +1 -1
- package/harness/opencode/agents/arka-sales-director-miguel.md +1 -1
- package/harness/opencode/agents/arka-strategy-director-tomas.md +1 -1
- package/harness/opencode/agents/arka-tech-director-francisca.md +1 -1
- package/harness/opencode/agents/arka-tech-lead-paulo.md +1 -1
- package/harness/opencode/agents/arka-video-producer-simao.md +1 -1
- package/harness/zed/.rules +1 -1
- package/installer/claude-md.js +46 -5
- package/knowledge/skills-manifest.json +1 -1
- package/package.json +1 -1
- package/pyproject.toml +1 -1
- package/scripts/skills_catalog_gen.py +225 -0
- package/scripts/workflows_catalog_gen.py +161 -0
- package/knowledge/commands-registry.json.bak +0 -6462
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
"""Skills catalog generator — every SKILL.md in the system, one page.
|
|
2
|
+
|
|
3
|
+
Documents the complete skill surface: 318 department skills plus the 15
|
|
4
|
+
``/arka`` meta skills (333 total). Single source of truth: the SKILL.md
|
|
5
|
+
files themselves (``departments/*/skills/*/SKILL.md`` and
|
|
6
|
+
``arka/skills/*/SKILL.md``), never hand-typed counts. This script
|
|
7
|
+
GENERATES:
|
|
8
|
+
|
|
9
|
+
- ``docs/SKILLS-CATALOG.md`` — per-department catalog with slug, path,
|
|
10
|
+
and description for every skill.
|
|
11
|
+
|
|
12
|
+
Regenerate + commit after touching any SKILL.md set, department
|
|
13
|
+
layout, or VERSION:
|
|
14
|
+
|
|
15
|
+
python scripts/skills_catalog_gen.py
|
|
16
|
+
|
|
17
|
+
``tests/python/test_skills_catalog_gen.py`` drift-gates the committed
|
|
18
|
+
file against a fresh in-memory run, byte for byte.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
from __future__ import annotations
|
|
22
|
+
|
|
23
|
+
import re
|
|
24
|
+
import sys
|
|
25
|
+
from pathlib import Path
|
|
26
|
+
|
|
27
|
+
try:
|
|
28
|
+
import yaml
|
|
29
|
+
except ImportError: # pragma: no cover - venv-only dependency
|
|
30
|
+
yaml = None
|
|
31
|
+
|
|
32
|
+
REPO_ROOT = Path(__file__).resolve().parents[1]
|
|
33
|
+
OUTPUT = REPO_ROOT / "docs" / "SKILLS-CATALOG.md"
|
|
34
|
+
|
|
35
|
+
# Canonical department order — mirrors scripts/tools/docs_stats.py
|
|
36
|
+
DEPARTMENT_ORDER = [
|
|
37
|
+
"dev",
|
|
38
|
+
"marketing",
|
|
39
|
+
"brand",
|
|
40
|
+
"finance",
|
|
41
|
+
"strategy",
|
|
42
|
+
"ecom",
|
|
43
|
+
"kb",
|
|
44
|
+
"ops",
|
|
45
|
+
"pm",
|
|
46
|
+
"saas",
|
|
47
|
+
"landing",
|
|
48
|
+
"content",
|
|
49
|
+
"community",
|
|
50
|
+
"sales",
|
|
51
|
+
"leadership",
|
|
52
|
+
"org",
|
|
53
|
+
]
|
|
54
|
+
|
|
55
|
+
DEPT_TITLES = {
|
|
56
|
+
"dev": "Development",
|
|
57
|
+
"marketing": "Marketing",
|
|
58
|
+
"brand": "Brand & Design",
|
|
59
|
+
"finance": "Finance",
|
|
60
|
+
"strategy": "Strategy",
|
|
61
|
+
"ecom": "E-Commerce",
|
|
62
|
+
"kb": "Knowledge",
|
|
63
|
+
"ops": "Operations",
|
|
64
|
+
"pm": "Project Management",
|
|
65
|
+
"saas": "SaaS",
|
|
66
|
+
"landing": "Landing Pages",
|
|
67
|
+
"content": "Content",
|
|
68
|
+
"community": "Communities",
|
|
69
|
+
"sales": "Sales",
|
|
70
|
+
"leadership": "Leadership",
|
|
71
|
+
"org": "Organization",
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def _clean_description(raw: str | None, fallback: str) -> str:
|
|
76
|
+
"""First sentence(s) of the description, single line, truncated."""
|
|
77
|
+
if not raw:
|
|
78
|
+
return fallback
|
|
79
|
+
text = raw.replace("TRIGGER:", ". TRIGGER:").split("TRIGGER:")[0]
|
|
80
|
+
text = re.sub(r"\s+", " ", text).strip()
|
|
81
|
+
text = re.sub(r"\s+\.", ".", text)
|
|
82
|
+
if len(text) > 180:
|
|
83
|
+
text = text[:177].rstrip() + "..."
|
|
84
|
+
return text or fallback
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def _skill_entry(path: Path) -> dict:
|
|
88
|
+
"""slug, name, description from a SKILL.md frontmatter."""
|
|
89
|
+
text = path.read_text(encoding="utf-8", errors="replace")
|
|
90
|
+
name = path.parent.name
|
|
91
|
+
description = path.stem
|
|
92
|
+
if yaml is not None and text.startswith("---"):
|
|
93
|
+
parts = text.split("---", 2)
|
|
94
|
+
if len(parts) >= 3:
|
|
95
|
+
try:
|
|
96
|
+
meta = yaml.safe_load(parts[1]) or {}
|
|
97
|
+
name = meta.get("name", name)
|
|
98
|
+
description = _clean_description(meta.get("description"), name)
|
|
99
|
+
except Exception:
|
|
100
|
+
pass
|
|
101
|
+
if description == name:
|
|
102
|
+
for line in text.splitlines():
|
|
103
|
+
line = line.strip()
|
|
104
|
+
if line and not line.startswith("#") and not line.startswith("---"):
|
|
105
|
+
description = _clean_description(line, name)
|
|
106
|
+
break
|
|
107
|
+
return {"slug": path.parent.name, "name": name, "description": description}
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def _dept_skills() -> dict[str, list[dict]]:
|
|
111
|
+
"""department slug -> hub entry first, then sorted sub-skill entries.
|
|
112
|
+
|
|
113
|
+
Matches docs_stats.py semantics: the department hub
|
|
114
|
+
(``departments/<dept>/SKILL.md``) IS counted as a skill, so the
|
|
115
|
+
grand total reconciles with the canonical 333 headline.
|
|
116
|
+
"""
|
|
117
|
+
result: dict[str, list[dict]] = {}
|
|
118
|
+
for dept_dir in (REPO_ROOT / "departments").iterdir():
|
|
119
|
+
if not dept_dir.is_dir() or "vendor" in dept_dir.parts:
|
|
120
|
+
continue
|
|
121
|
+
entries = []
|
|
122
|
+
hub = dept_dir / "SKILL.md"
|
|
123
|
+
if hub.is_file():
|
|
124
|
+
entry = _skill_entry(hub)
|
|
125
|
+
entry["slug"] = f"{dept_dir.name}-hub"
|
|
126
|
+
entry["name"] = f"{dept_dir.name} (hub)"
|
|
127
|
+
entries.append(entry)
|
|
128
|
+
skills_dir = dept_dir / "skills"
|
|
129
|
+
if skills_dir.is_dir():
|
|
130
|
+
for skill_md in sorted(skills_dir.glob("*/SKILL.md")):
|
|
131
|
+
if "vendor" in skill_md.parts:
|
|
132
|
+
continue
|
|
133
|
+
entries.append(_skill_entry(skill_md))
|
|
134
|
+
if entries:
|
|
135
|
+
result[dept_dir.name] = entries
|
|
136
|
+
return result
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def _arka_skills() -> list[dict]:
|
|
140
|
+
"""The 15 /arka meta skills: the master orchestrator plus the 14
|
|
141
|
+
sub-skills under arka/skills/*/SKILL.md."""
|
|
142
|
+
master = REPO_ROOT / "arka" / "SKILL.md"
|
|
143
|
+
entries = []
|
|
144
|
+
if master.is_file():
|
|
145
|
+
entry = _skill_entry(master)
|
|
146
|
+
entry["slug"] = "arka"
|
|
147
|
+
entry["name"] = "arka (master orchestrator)"
|
|
148
|
+
entries.append(entry)
|
|
149
|
+
skills_dir = REPO_ROOT / "arka" / "skills"
|
|
150
|
+
if skills_dir.is_dir():
|
|
151
|
+
for skill_md in sorted(skills_dir.glob("*/SKILL.md")):
|
|
152
|
+
if "vendor" in skill_md.parts:
|
|
153
|
+
continue
|
|
154
|
+
entries.append(_skill_entry(skill_md))
|
|
155
|
+
return entries
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
def generate() -> str:
|
|
159
|
+
"""Return the full SKILLS-CATALOG.md content."""
|
|
160
|
+
dept_skills = _dept_skills()
|
|
161
|
+
arka_skills = _arka_skills()
|
|
162
|
+
dept_total = sum(len(v) for v in dept_skills.values())
|
|
163
|
+
total = dept_total + len(arka_skills)
|
|
164
|
+
|
|
165
|
+
lines = [
|
|
166
|
+
"# ArkaOS Skills Catalog",
|
|
167
|
+
"",
|
|
168
|
+
"> Generated by `scripts/skills_catalog_gen.py`; do not edit. Regenerate after",
|
|
169
|
+
"> touching any SKILL.md, department layout, or VERSION.",
|
|
170
|
+
"",
|
|
171
|
+
f"**{total} skills** — {dept_total} department skills across "
|
|
172
|
+
f"{len(dept_skills)} departments, plus {len(arka_skills)} `/arka` meta skills.",
|
|
173
|
+
"",
|
|
174
|
+
"## Summary",
|
|
175
|
+
"",
|
|
176
|
+
"| Department | Skills |",
|
|
177
|
+
"| --- | --- |",
|
|
178
|
+
]
|
|
179
|
+
for slug in DEPARTMENT_ORDER:
|
|
180
|
+
if slug in dept_skills:
|
|
181
|
+
lines.append(f"| {DEPT_TITLES[slug]} (`{slug}`) | {len(dept_skills[slug])} |")
|
|
182
|
+
for slug in sorted(set(dept_skills) - set(DEPARTMENT_ORDER)):
|
|
183
|
+
lines.append(f"| {slug} | {len(dept_skills[slug])} |")
|
|
184
|
+
lines.append(f"| **Total departments** | **{dept_total}** |")
|
|
185
|
+
lines.append(f"| `/arka` meta skills | {len(arka_skills)} |")
|
|
186
|
+
lines.append(f"| **Grand total** | **{total}** |")
|
|
187
|
+
lines.append("")
|
|
188
|
+
|
|
189
|
+
lines.append("## Department skills")
|
|
190
|
+
lines.append("")
|
|
191
|
+
for slug in DEPARTMENT_ORDER:
|
|
192
|
+
entries = dept_skills.get(slug)
|
|
193
|
+
if not entries:
|
|
194
|
+
continue
|
|
195
|
+
lines.append(f"### {DEPT_TITLES[slug]} (`{slug}`)")
|
|
196
|
+
lines.append("")
|
|
197
|
+
lines.append("| Skill | Name | Description |")
|
|
198
|
+
lines.append("| --- | --- | --- |")
|
|
199
|
+
for entry in entries:
|
|
200
|
+
lines.append(
|
|
201
|
+
f"| `{entry['slug']}` | {entry['name']} | {entry['description']} |"
|
|
202
|
+
)
|
|
203
|
+
lines.append("")
|
|
204
|
+
|
|
205
|
+
lines.append("## `/arka` meta skills")
|
|
206
|
+
lines.append("")
|
|
207
|
+
lines.append("| Skill | Name | Description |")
|
|
208
|
+
lines.append("| --- | --- | --- |")
|
|
209
|
+
for entry in arka_skills:
|
|
210
|
+
lines.append(
|
|
211
|
+
f"| `{entry['slug']}` | {entry['name']} | {entry['description']} |"
|
|
212
|
+
)
|
|
213
|
+
lines.append("")
|
|
214
|
+
return "\n".join(lines)
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
def main() -> int:
|
|
218
|
+
content = generate()
|
|
219
|
+
OUTPUT.write_text(content, encoding="utf-8")
|
|
220
|
+
print(f"skills catalog generated: {OUTPUT.relative_to(REPO_ROOT)}")
|
|
221
|
+
return 0
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
if __name__ == "__main__":
|
|
225
|
+
sys.exit(main())
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
"""Workflows catalog generator — every workflow YAML in the system.
|
|
2
|
+
|
|
3
|
+
Documents the complete workflow surface: the enterprise, focused, and
|
|
4
|
+
specialist workflows defined under ``departments/*/workflows/*.yaml``.
|
|
5
|
+
Single source of truth: the workflow YAML files themselves — never
|
|
6
|
+
hand-typed counts. This script GENERATES:
|
|
7
|
+
|
|
8
|
+
- ``docs/WORKFLOWS-CATALOG.md`` — per-department workflow reference
|
|
9
|
+
with tier, command, phase count, gate types, and quality-gate flag.
|
|
10
|
+
|
|
11
|
+
Regenerate + commit after touching any workflow YAML, department
|
|
12
|
+
layout, or VERSION:
|
|
13
|
+
|
|
14
|
+
python scripts/workflows_catalog_gen.py
|
|
15
|
+
|
|
16
|
+
``tests/python/test_workflows_catalog_gen.py`` drift-gates the
|
|
17
|
+
committed file against a fresh in-memory run, byte for byte.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
from __future__ import annotations
|
|
21
|
+
|
|
22
|
+
import sys
|
|
23
|
+
from pathlib import Path
|
|
24
|
+
|
|
25
|
+
try:
|
|
26
|
+
import yaml
|
|
27
|
+
except ImportError: # pragma: no cover - venv-only dependency
|
|
28
|
+
yaml = None
|
|
29
|
+
|
|
30
|
+
REPO_ROOT = Path(__file__).resolve().parents[1]
|
|
31
|
+
OUTPUT = REPO_ROOT / "docs" / "WORKFLOWS-CATALOG.md"
|
|
32
|
+
|
|
33
|
+
DEPT_TITLES = {
|
|
34
|
+
"dev": "Development",
|
|
35
|
+
"marketing": "Marketing",
|
|
36
|
+
"brand": "Brand & Design",
|
|
37
|
+
"finance": "Finance",
|
|
38
|
+
"strategy": "Strategy",
|
|
39
|
+
"ecom": "E-Commerce",
|
|
40
|
+
"kb": "Knowledge",
|
|
41
|
+
"ops": "Operations",
|
|
42
|
+
"pm": "Project Management",
|
|
43
|
+
"saas": "SaaS",
|
|
44
|
+
"landing": "Landing Pages",
|
|
45
|
+
"content": "Content",
|
|
46
|
+
"community": "Communities",
|
|
47
|
+
"sales": "Sales",
|
|
48
|
+
"leadership": "Leadership",
|
|
49
|
+
"org": "Organization",
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
TIER_LABELS = {
|
|
53
|
+
"enterprise": "Enterprise (7-10 phases, mandatory Quality Gate)",
|
|
54
|
+
"focused": "Focused (3-4 phases)",
|
|
55
|
+
"specialist": "Specialist (1-2 phases)",
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def _workflow_entry(path: Path) -> dict | None:
|
|
60
|
+
"""id, name, description, tier, command, phases, gates from YAML."""
|
|
61
|
+
try:
|
|
62
|
+
data = yaml.safe_load(path.read_text(encoding="utf-8")) or {}
|
|
63
|
+
except Exception:
|
|
64
|
+
return None
|
|
65
|
+
if not isinstance(data, dict) or "id" not in data:
|
|
66
|
+
return None
|
|
67
|
+
phases = data.get("phases", []) or []
|
|
68
|
+
gate_types = []
|
|
69
|
+
for phase in phases:
|
|
70
|
+
gate = phase.get("gate") if isinstance(phase, dict) else None
|
|
71
|
+
if isinstance(gate, dict) and gate.get("type"):
|
|
72
|
+
gate_types.append(str(gate["type"]))
|
|
73
|
+
return {
|
|
74
|
+
"id": str(data["id"]),
|
|
75
|
+
"name": str(data.get("name", data["id"])),
|
|
76
|
+
"description": str(data.get("description", "")).strip(),
|
|
77
|
+
"tier": str(data.get("tier", "specialist")),
|
|
78
|
+
"command": str(data.get("command", "")),
|
|
79
|
+
"phases": len(phases),
|
|
80
|
+
"gates": sorted(set(gate_types)),
|
|
81
|
+
"quality_gate": bool(data.get("quality_gate_required", False)),
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def _dept_workflows() -> dict[str, list[dict]]:
|
|
86
|
+
result: dict[str, list[dict]] = {}
|
|
87
|
+
for dept_dir in (REPO_ROOT / "departments").iterdir():
|
|
88
|
+
if not dept_dir.is_dir() or "vendor" in dept_dir.parts:
|
|
89
|
+
continue
|
|
90
|
+
wf_dir = dept_dir / "workflows"
|
|
91
|
+
if not wf_dir.is_dir():
|
|
92
|
+
continue
|
|
93
|
+
entries = []
|
|
94
|
+
for wf_yaml in sorted(wf_dir.glob("*.yaml")):
|
|
95
|
+
entry = _workflow_entry(wf_yaml)
|
|
96
|
+
if entry:
|
|
97
|
+
entries.append(entry)
|
|
98
|
+
if entries:
|
|
99
|
+
result[dept_dir.name] = entries
|
|
100
|
+
return result
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def generate() -> str:
|
|
104
|
+
"""Return the full WORKFLOWS-CATALOG.md content."""
|
|
105
|
+
dept_workflows = _dept_workflows()
|
|
106
|
+
total = sum(len(v) for v in dept_workflows.values())
|
|
107
|
+
tiers: dict[str, int] = {}
|
|
108
|
+
for entries in dept_workflows.values():
|
|
109
|
+
for entry in entries:
|
|
110
|
+
tiers[entry["tier"]] = tiers.get(entry["tier"], 0) + 1
|
|
111
|
+
|
|
112
|
+
lines = [
|
|
113
|
+
"# ArkaOS Workflows Catalog",
|
|
114
|
+
"",
|
|
115
|
+
"> Generated by `scripts/workflows_catalog_gen.py`; do not edit. Regenerate",
|
|
116
|
+
"> after touching any workflow YAML, department layout, or VERSION.",
|
|
117
|
+
"",
|
|
118
|
+
f"**{total} workflows** across {len(dept_workflows)} departments.",
|
|
119
|
+
"",
|
|
120
|
+
"## Summary",
|
|
121
|
+
"",
|
|
122
|
+
"| Department | Workflows |",
|
|
123
|
+
"| --- | --- |",
|
|
124
|
+
]
|
|
125
|
+
for slug in sorted(dept_workflows):
|
|
126
|
+
title = DEPT_TITLES.get(slug, slug)
|
|
127
|
+
lines.append(f"| {title} (`{slug}`) | {len(dept_workflows[slug])} |")
|
|
128
|
+
lines.append(f"| **Total** | **{total}** |")
|
|
129
|
+
lines.append("")
|
|
130
|
+
lines.append("| Tier | Count |")
|
|
131
|
+
lines.append("| --- | --- |")
|
|
132
|
+
for tier in sorted(tiers):
|
|
133
|
+
lines.append(f"| {TIER_LABELS.get(tier, tier)} | {tiers[tier]} |")
|
|
134
|
+
lines.append("")
|
|
135
|
+
|
|
136
|
+
for slug in sorted(dept_workflows):
|
|
137
|
+
title = DEPT_TITLES.get(slug, slug)
|
|
138
|
+
lines.append(f"## {title} (`{slug}`)")
|
|
139
|
+
lines.append("")
|
|
140
|
+
lines.append("| Workflow | Tier | Command | Phases | Gates | QG |")
|
|
141
|
+
lines.append("| --- | --- | --- | --- | --- | --- |")
|
|
142
|
+
for entry in dept_workflows[slug]:
|
|
143
|
+
gates = ", ".join(entry["gates"]) if entry["gates"] else "—"
|
|
144
|
+
qg = "yes" if entry["quality_gate"] else "—"
|
|
145
|
+
lines.append(
|
|
146
|
+
f"| `{entry['id']}` | {TIER_LABELS.get(entry['tier'], entry['tier'])} | "
|
|
147
|
+
f"`{entry['command']}` | {entry['phases']} | {gates} | {qg} |"
|
|
148
|
+
)
|
|
149
|
+
lines.append("")
|
|
150
|
+
return "\n".join(lines)
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def main() -> int:
|
|
154
|
+
content = generate()
|
|
155
|
+
OUTPUT.write_text(content, encoding="utf-8")
|
|
156
|
+
print(f"workflows catalog generated: {OUTPUT.relative_to(REPO_ROOT)}")
|
|
157
|
+
return 0
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
if __name__ == "__main__":
|
|
161
|
+
sys.exit(main())
|