arkaos 4.24.0 → 4.26.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 (41) hide show
  1. package/README.md +1 -1
  2. package/THE-ARKAOS-GUIDE.md +1 -1
  3. package/VERSION +1 -1
  4. package/arka/SKILL.md +1 -1
  5. package/bin/arka +8 -0
  6. package/config/claude-agents/dba.md +3 -3
  7. package/config/claude-agents/shadcn-padronizer.md +3 -3
  8. package/departments/brand/agents/design-ops/shadcn-padronizer.yaml +1 -1
  9. package/departments/dev/agents/dba.yaml +1 -1
  10. package/departments/dev/skills/build-fix/SKILL.md +122 -0
  11. package/departments/dev/skills/opensource-release/SKILL.md +96 -0
  12. package/departments/dev/skills/react-review/SKILL.md +86 -0
  13. package/departments/ops/skills/harness-tune/SKILL.md +79 -0
  14. package/departments/ops/skills/hookify/SKILL.md +89 -0
  15. package/departments/ops/skills/session-retro/SKILL.md +81 -0
  16. package/departments/ops/skills/workspace-audit/SKILL.md +88 -0
  17. package/departments/pm/skills/epic-coordination/SKILL.md +79 -0
  18. package/harness/codex/AGENTS.md +139 -0
  19. package/harness/copilot/copilot-instructions.md +139 -0
  20. package/harness/cursor/rules/arkaos-stack-laravel.mdc +16 -0
  21. package/harness/cursor/rules/arkaos-stack-node.mdc +14 -0
  22. package/harness/cursor/rules/arkaos-stack-nuxt.mdc +15 -0
  23. package/harness/cursor/rules/arkaos-stack-php.mdc +14 -0
  24. package/harness/cursor/rules/arkaos-stack-python.mdc +14 -0
  25. package/harness/cursor/rules/arkaos-stack-react.mdc +14 -0
  26. package/harness/cursor/rules/arkaos-stack-vue.mdc +14 -0
  27. package/harness/cursor/rules/arkaos.mdc +72 -0
  28. package/harness/gemini/GEMINI.md +139 -0
  29. package/harness/opencode/AGENTS.md +139 -0
  30. package/harness/zed/.rules +139 -0
  31. package/installer/adapters/codex-cli.js +16 -27
  32. package/installer/adapters/cursor.js +22 -26
  33. package/installer/adapters/gemini-cli.js +15 -30
  34. package/installer/cli.js +1 -1
  35. package/installer/doctor.js +225 -0
  36. package/installer/harness-bundle.js +39 -0
  37. package/knowledge/agents-registry-v2.json +3 -3
  38. package/knowledge/skills-manifest.json +105 -1
  39. package/package.json +2 -1
  40. package/pyproject.toml +1 -1
  41. package/scripts/harness_gen.py +287 -0
@@ -0,0 +1,287 @@
1
+ """Generate harness/ — multi-runtime instruction bundles from one source.
2
+
3
+ Docs-as-code (marketplace_gen / guide_gen precedent): the ArkaOS
4
+ contract — identity, department routing, agent index, evidence
5
+ expectations, stack conventions — is COMPILED from the generated
6
+ registries and emitted once per supported harness, in that harness's
7
+ native instruction format. Nothing here is hand-typed per target;
8
+ adding a harness is one small emitter, not a maintained directory.
9
+
10
+ Targets (v1): codex (AGENTS.md), opencode (AGENTS.md), gemini
11
+ (GEMINI.md), zed (.rules), copilot (copilot-instructions.md), cursor
12
+ (rules/*.mdc with path-scoped stack rules).
13
+
14
+ Honesty note baked into every bundle: these are instruction-level
15
+ exports — the full ArkaOS engine (Synapse, hooks, Quality Gate
16
+ enforcement) runs on runtimes with an adapter; instructions carry the
17
+ contract, not the cognition.
18
+
19
+ Sources (all generated + drift-locked elsewhere):
20
+ - ``knowledge/agents-registry-v2.json`` — agents (name/role/dept/tier)
21
+ - ``knowledge/commands-registry.json`` — per-department command counts
22
+ - ``config/standards/stack-rules/*.md`` — path-scoped stack conventions
23
+ - ``VERSION`` + ``scripts/tools/docs_stats.py`` counters
24
+
25
+ Run: ``~/.arkaos/bin/arka-py scripts/harness_gen.py``
26
+
27
+ ``tests/python/test_harness_gen.py`` drift-gates the committed tree
28
+ byte for byte and enforces the size budget — an instruction file that
29
+ blows the target's context is the documented anti-pattern.
30
+ """
31
+
32
+ from __future__ import annotations
33
+
34
+ import json
35
+ import sys
36
+ from pathlib import Path
37
+
38
+ sys.path.insert(0, str(Path(__file__).resolve().parent / "tools"))
39
+
40
+ from docs_stats import (
41
+ count_agents,
42
+ count_departments,
43
+ count_skills,
44
+ read_version,
45
+ repo_root,
46
+ )
47
+
48
+ ROOT = repo_root()
49
+ HARNESS_DIR = ROOT / "harness"
50
+ STACK_RULES_DIR = ROOT / "config" / "standards" / "stack-rules"
51
+
52
+ # Department prefix -> lead agent name. Leads are tier-1 agents; where a
53
+ # department has several squad leads the ORCHESTRATING lead is listed
54
+ # (same mapping the routing table in arka/SKILL.md uses).
55
+ DEPT_LEADS = {
56
+ "dev": "Paulo", "mkt": "Luna", "brand": "Valentina", "fin": "Helena",
57
+ "strat": "Tomas", "ecom": "Ricardo", "kb": "Clara", "ops": "Daniel",
58
+ "pm": "Carolina", "saas": "Tiago", "landing": "Ines",
59
+ "content": "Rafael", "community": "Beatriz", "sales": "Miguel",
60
+ "lead": "Rodrigo", "org": "Sofia",
61
+ }
62
+
63
+ # Registry department slug -> routing prefix (registry stores directory
64
+ # names; the user-facing prefixes differ for four departments).
65
+ DIR_TO_PREFIX = {
66
+ "marketing": "mkt", "strategy": "strat", "finance": "fin",
67
+ "leadership": "lead",
68
+ }
69
+
70
+ # Size budget per emitted main file. Instruction files that outgrow the
71
+ # target's context stop being read — the budget is the feature.
72
+ MAIN_FILE_BUDGET_BYTES = 16_000
73
+
74
+ # Stack slug -> display name for user-facing frontmatter (title() would
75
+ # render "Php").
76
+ STACK_DISPLAY = {
77
+ "laravel": "Laravel", "node": "Node", "nuxt": "Nuxt", "php": "PHP",
78
+ "python": "Python", "react": "React", "vue": "Vue",
79
+ }
80
+
81
+
82
+ def _load_agents() -> list[dict]:
83
+ data = json.loads(
84
+ (ROOT / "knowledge" / "agents-registry-v2.json").read_text())
85
+ return data["agents"]
86
+
87
+
88
+ def _load_command_counts() -> dict[str, int]:
89
+ data = json.loads(
90
+ (ROOT / "knowledge" / "commands-registry.json").read_text())
91
+ return data["_meta"]["departments"]
92
+
93
+
94
+ def _split_frontmatter(text: str) -> tuple[list[str], str]:
95
+ """Return (paths globs, body) from a stack-rules file."""
96
+ if not text.startswith("---"):
97
+ return [], text
98
+ _, fm, body = text.split("---", 2)
99
+ globs = [
100
+ line.strip().lstrip("- ").strip('"')
101
+ for line in fm.strip().splitlines()
102
+ if line.strip().startswith("- ")
103
+ ]
104
+ return globs, body.strip()
105
+
106
+
107
+ def _stack_rules() -> list[tuple[str, list[str], str]]:
108
+ rules = []
109
+ for path in sorted(STACK_RULES_DIR.glob("*.md")):
110
+ globs, body = _split_frontmatter(path.read_text())
111
+ rules.append((path.stem, globs, body))
112
+ return rules
113
+
114
+
115
+ def _department_table(counts: dict[str, int]) -> str:
116
+ # commands-registry keys one department by directory name where the
117
+ # prefix differs ("leadership" for /lead).
118
+ prefix_to_registry = {"lead": "leadership"}
119
+ rows = ["| Prefix | Lead | Commands |", "|---|---|---|"]
120
+ for prefix, lead in DEPT_LEADS.items():
121
+ key = prefix_to_registry.get(prefix, prefix)
122
+ rows.append(f"| `/{prefix}` | {lead} | {counts.get(key, 0)} |")
123
+ return "\n".join(rows)
124
+
125
+
126
+ def _agent_index(agents: list[dict]) -> str:
127
+ by_dept: dict[str, list[dict]] = {}
128
+ for agent in agents:
129
+ prefix = DIR_TO_PREFIX.get(agent["department"], agent["department"])
130
+ by_dept.setdefault(prefix, []).append(agent)
131
+ lines = []
132
+ for prefix in sorted(by_dept):
133
+ members = sorted(
134
+ by_dept[prefix], key=lambda a: (a["tier"], a["name"]))
135
+ listing = " · ".join(
136
+ f"{a['name']} ({a['role']})" for a in members)
137
+ lines.append(f"- **{prefix}**: {listing}")
138
+ return "\n".join(lines)
139
+
140
+
141
+ def _stack_section(rules: list[tuple[str, list[str], str]]) -> str:
142
+ # Rule bodies carry their own "## <Stack> Conventions" title; demote
143
+ # one level so they nest correctly under "## Stack conventions".
144
+ parts = []
145
+ for _stack, _globs, body in rules:
146
+ demoted = "\n".join(
147
+ f"#{line}" if line.startswith("## ") else line
148
+ for line in body.splitlines()
149
+ )
150
+ parts.append(demoted)
151
+ return "\n\n".join(parts)
152
+
153
+
154
+ def _contract_body() -> str:
155
+ version = read_version(ROOT)
156
+ departments = count_departments(ROOT)
157
+ agents_total = count_agents(ROOT)["files"]
158
+ skills = count_skills(ROOT)["core"]
159
+ counts = _load_command_counts()
160
+ agents = _load_agents()
161
+ return f"""# ArkaOS — The Operating System for AI Agent Teams
162
+
163
+ > v{version} — {agents_total} agents, {departments} departments, \
164
+ {skills} skills. Generated by `scripts/harness_gen.py`; do not edit.
165
+
166
+ You are operating within ArkaOS. Every request routes through the
167
+ appropriate department squad — never respond as a generic assistant.
168
+
169
+ ## How to work
170
+
171
+ 1. **Route** every request to a department (table below) and say so:
172
+ `[arka:routing] <dept> -> <lead>`.
173
+ 2. **Plan before code.** State the plan and wait for explicit approval
174
+ on non-trivial work.
175
+ 3. **Evidence over narration.** Run the real tests and report the real
176
+ exit code; a claim about code that was never executed is not a
177
+ result.
178
+ 4. **Quality Gate.** Before delivering, review the work as a critical
179
+ second reader (copy AND technical) and say honestly what is
180
+ unfinished. Nothing ships with known defects undisclosed.
181
+
182
+ ## Departments
183
+
184
+ {_department_table(counts)}
185
+
186
+ ## Agents
187
+
188
+ Adopt the matching persona when executing department work:
189
+
190
+ {_agent_index(agents)}
191
+
192
+ ## Stack Conventions
193
+
194
+ Apply the section matching the files you touch.
195
+
196
+ {_stack_section(_stack_rules())}
197
+
198
+ ## Scope of this file
199
+
200
+ Instruction-level export for this runtime. The full ArkaOS engine —
201
+ context injection, hooks, enforced quality gates, knowledge base — runs
202
+ on runtimes with a native adapter (`npx arkaos install`). This file
203
+ carries the contract so the team behaves like ArkaOS anywhere.
204
+ """
205
+
206
+
207
+ def _cursor_files() -> dict[str, str]:
208
+ """Cursor gets native path-scoped rules instead of one flat file."""
209
+ version = read_version(ROOT)
210
+ files: dict[str, str] = {}
211
+ body = _contract_body()
212
+ # Strip the stack section from the always-on rule — stacks ship as
213
+ # individually scoped .mdc files below, Cursor's native strength.
214
+ main = body.split("## Stack Conventions")[0].rstrip()
215
+ main += (
216
+ "\n\n## Stack Conventions\n\nPath-scoped rules in this directory "
217
+ "(`arkaos-stack-*.mdc`) apply automatically to matching files.\n"
218
+ )
219
+ files["rules/arkaos.mdc"] = (
220
+ f"---\ndescription: ArkaOS v{version} agent-team contract\n"
221
+ f"alwaysApply: true\n---\n\n{main}\n"
222
+ )
223
+ for stack, globs, rule_body in _stack_rules():
224
+ globs_line = ", ".join(globs)
225
+ display = STACK_DISPLAY.get(stack, stack.title())
226
+ files[f"rules/arkaos-stack-{stack}.mdc"] = (
227
+ f"---\ndescription: ArkaOS {display} stack conventions\n"
228
+ f"globs: {globs_line}\nalwaysApply: false\n---\n\n"
229
+ f"{rule_body}\n"
230
+ )
231
+ return files
232
+
233
+
234
+ def generate() -> dict[str, str]:
235
+ """Return {relative path under harness/: content}."""
236
+ body = _contract_body()
237
+ files: dict[str, str] = {
238
+ "codex/AGENTS.md": body,
239
+ "opencode/AGENTS.md": body,
240
+ "gemini/GEMINI.md": body,
241
+ "zed/.rules": body,
242
+ "copilot/copilot-instructions.md": body,
243
+ }
244
+ for rel, content in _cursor_files().items():
245
+ files[f"cursor/{rel}"] = content
246
+ return files
247
+
248
+
249
+ def check_budget(files: dict[str, str]) -> list[str]:
250
+ over = []
251
+ for rel, content in files.items():
252
+ size = len(content.encode("utf-8"))
253
+ if size > MAIN_FILE_BUDGET_BYTES:
254
+ over.append(f"{rel}: {size} bytes > {MAIN_FILE_BUDGET_BYTES}")
255
+ return over
256
+
257
+
258
+ def write_bundle(files: dict[str, str], harness_dir: Path) -> None:
259
+ """Write the bundle, removing files a previous run left behind."""
260
+ if harness_dir.exists():
261
+ for stale in harness_dir.rglob("*"):
262
+ if stale.is_file():
263
+ rel = str(stale.relative_to(harness_dir))
264
+ if rel not in files:
265
+ stale.unlink()
266
+ for rel, content in files.items():
267
+ target = harness_dir / rel
268
+ target.parent.mkdir(parents=True, exist_ok=True)
269
+ target.write_text(content)
270
+
271
+
272
+ def main() -> int:
273
+ files = generate()
274
+ over = check_budget(files)
275
+ if over:
276
+ print("BUDGET EXCEEDED:\n " + "\n ".join(over))
277
+ return 1
278
+ write_bundle(files, HARNESS_DIR)
279
+ total = sum(len(c.encode()) for c in files.values())
280
+ print(
281
+ f"harness generated: {len(files)} files, 6 targets, "
282
+ f"{total} bytes total")
283
+ return 0
284
+
285
+
286
+ if __name__ == "__main__":
287
+ raise SystemExit(main())