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.
Files changed (48) hide show
  1. package/CONSTITUTION.md +1 -1
  2. package/THE-ARKAOS-GUIDE.md +1 -1
  3. package/VERSION +1 -1
  4. package/arka/skills/comfyui/SKILL.md +2 -2
  5. package/core/governance/evidence_checks.py +14 -1
  6. package/core/sync/features/forge.yaml +1 -1
  7. package/core/sync/features/spec-gate.yaml +1 -1
  8. package/core/sync/features/workflow-tiers.yaml +1 -1
  9. package/departments/brand/SKILL.md +1 -1
  10. package/departments/dev/SKILL.md +1 -1
  11. package/harness/codex/AGENTS.md +1 -1
  12. package/harness/copilot/copilot-instructions.md +1 -1
  13. package/harness/cursor/rules/arkaos.mdc +2 -2
  14. package/harness/gemini/GEMINI.md +1 -1
  15. package/harness/opencode/AGENTS.md +1 -1
  16. package/harness/opencode/agents/arka-architect-gabriel.md +1 -1
  17. package/harness/opencode/agents/arka-brand-director-valentina.md +1 -1
  18. package/harness/opencode/agents/arka-cfo-helena.md +1 -1
  19. package/harness/opencode/agents/arka-chief-of-staff-afonso.md +1 -1
  20. package/harness/opencode/agents/arka-community-strategist-beatriz.md +1 -1
  21. package/harness/opencode/agents/arka-content-strategist-rafael.md +1 -1
  22. package/harness/opencode/agents/arka-conversion-strategist-ines.md +1 -1
  23. package/harness/opencode/agents/arka-coo-sofia.md +1 -1
  24. package/harness/opencode/agents/arka-copy-director-eduardo.md +1 -1
  25. package/harness/opencode/agents/arka-cqo-marta.md +1 -1
  26. package/harness/opencode/agents/arka-cto-marco.md +1 -1
  27. package/harness/opencode/agents/arka-design-ops-lead-iris.md +1 -1
  28. package/harness/opencode/agents/arka-ecom-director-ricardo.md +1 -1
  29. package/harness/opencode/agents/arka-knowledge-director-clara.md +1 -1
  30. package/harness/opencode/agents/arka-leadership-director-rodrigo.md +1 -1
  31. package/harness/opencode/agents/arka-marketing-director-luna.md +1 -1
  32. package/harness/opencode/agents/arka-ops-lead-daniel.md +1 -1
  33. package/harness/opencode/agents/arka-pm-director-carolina.md +1 -1
  34. package/harness/opencode/agents/arka-revops-lead-vicente.md +1 -1
  35. package/harness/opencode/agents/arka-saas-strategist-tiago.md +1 -1
  36. package/harness/opencode/agents/arka-sales-director-miguel.md +1 -1
  37. package/harness/opencode/agents/arka-strategy-director-tomas.md +1 -1
  38. package/harness/opencode/agents/arka-tech-director-francisca.md +1 -1
  39. package/harness/opencode/agents/arka-tech-lead-paulo.md +1 -1
  40. package/harness/opencode/agents/arka-video-producer-simao.md +1 -1
  41. package/harness/zed/.rules +1 -1
  42. package/installer/claude-md.js +46 -5
  43. package/knowledge/skills-manifest.json +1 -1
  44. package/package.json +1 -1
  45. package/pyproject.toml +1 -1
  46. package/scripts/skills_catalog_gen.py +225 -0
  47. package/scripts/workflows_catalog_gen.py +161 -0
  48. 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())