arkaos 2.25.0 → 2.28.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/arka/skills/bootstrap-agent/SKILL.md +76 -0
- package/arka/skills/design-ops/SKILL.md +68 -0
- package/arka/skills/design-ops/scripts/extract-colors.py +86 -0
- package/arka/skills/design-ops/scripts/shadcn-tokens.py +59 -0
- package/arka/skills/design-ops/scripts/wcag-contrast.py +92 -0
- package/arka/skills/research/SKILL.md +117 -0
- package/config/constitution.yaml +4 -0
- package/config/hooks/post-tool-use.sh +17 -0
- package/config/hooks/user-prompt-submit.sh +16 -0
- package/core/agents/__pycache__/loader.cpython-313.pyc +0 -0
- package/core/agents/__pycache__/schema.cpython-313.pyc +0 -0
- package/core/agents/loader.py +4 -1
- package/core/agents/schema.py +29 -0
- package/core/cognition/__pycache__/retrieval.cpython-313.pyc +0 -0
- package/core/cognition/capture/__pycache__/collector.cpython-313.pyc +0 -0
- package/core/cognition/retrieval.py +383 -0
- package/core/cognition/scheduler/__pycache__/daemon.cpython-313.pyc +0 -0
- package/core/obsidian/__pycache__/writer.cpython-313.pyc +0 -0
- package/core/runtime/__pycache__/path_resolver.cpython-313.pyc +0 -0
- package/departments/brand/agents/design-ops/design-ops-lead.yaml +78 -0
- package/departments/brand/agents/design-ops/extraction-script-writer.yaml +74 -0
- package/departments/brand/agents/design-ops/shadcn-padronizer.yaml +76 -0
- package/departments/brand/agents/design-ops/wcag-auditor.yaml +76 -0
- package/departments/dev/skills/mcp/SKILL.md +16 -0
- package/package.json +1 -1
- package/pyproject.toml +1 -1
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
2.
|
|
1
|
+
2.28.0
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: arka-bootstrap-agent
|
|
3
|
+
description: >
|
|
4
|
+
Formalises the "agents set up agents" pattern surfaced in the 2026-05-13
|
|
5
|
+
Nick Saraev × Greg Isenberg podcast on the Orgo agent business. When the
|
|
6
|
+
user asks to spin up a new specialist or an integration that requires
|
|
7
|
+
research-heavy setup, this skill orchestrates The Forge to dispatch
|
|
8
|
+
research subagents (Perplexity / Exa / Context7 / Firecrawl / XMCP),
|
|
9
|
+
synthesises the findings, and produces a ready-to-use agent YAML or
|
|
10
|
+
installation guide.
|
|
11
|
+
allowed-tools: [Agent, Read, Write, Bash]
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
# /arka bootstrap-agent — agents that set up agents
|
|
15
|
+
|
|
16
|
+
> Pattern source: Nick Saraev (Orgo) interview, 2026-05-13. ArkaOS already
|
|
17
|
+
> has The Forge for complexity-based planning; this skill is the explicit
|
|
18
|
+
> entry point users invoke when they want a new specialist or a setup
|
|
19
|
+
> playbook rather than a feature.
|
|
20
|
+
|
|
21
|
+
## Subcommands
|
|
22
|
+
|
|
23
|
+
| Command | What it does |
|
|
24
|
+
| --- | --- |
|
|
25
|
+
| `/arka bootstrap-agent specialist <slug>` | Generates a new Tier 2 agent YAML for a domain you describe. Dispatches research subagents to gather domain frameworks, conventions, and common pitfalls. Produces `departments/<dept>/agents/<slug>.yaml` with full 4-framework DNA. |
|
|
26
|
+
| `/arka bootstrap-agent integration <tool>` | Installation playbook for an external tool (e.g. Hermes, Composio, Agent Mail). Researches official docs + community recipes, outputs a step-by-step setup guide and any required MCP config. |
|
|
27
|
+
| `/arka bootstrap-agent persona <name>` | Builds an AI persona profile (DISC + Enneagram + OCEAN + MBTI) from learned content. Delegates to `/kb` (Clara) for source ingestion. |
|
|
28
|
+
|
|
29
|
+
## How it works
|
|
30
|
+
|
|
31
|
+
```
|
|
32
|
+
User: /arka bootstrap-agent specialist "design-tokens-engineer"
|
|
33
|
+
│
|
|
34
|
+
▼
|
|
35
|
+
1. The Forge classifies request complexity (usually "medium" — single
|
|
36
|
+
specialist, multiple research dimensions).
|
|
37
|
+
2. Spawns 3-5 research subagents in parallel:
|
|
38
|
+
- Perplexity MCP → real-time framework discovery
|
|
39
|
+
- Exa AI → semantic search for prior art
|
|
40
|
+
- Context7 → official documentation pulls
|
|
41
|
+
- Firecrawl → scrape competitor / canonical sources
|
|
42
|
+
- XMCP → Twitter / X conversations for community wisdom
|
|
43
|
+
3. Critic synthesis: a Tier 0 reviewer collapses overlap, surfaces
|
|
44
|
+
contradictions, prioritises by source weight.
|
|
45
|
+
4. Generates the deliverable (YAML / playbook / persona) and saves to
|
|
46
|
+
the canonical path.
|
|
47
|
+
5. Quality Gate (Marta + Eduardo + Francisca) approves before output
|
|
48
|
+
reaches the user.
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Why this exists
|
|
52
|
+
|
|
53
|
+
Per the Orgo interview, the recurring pattern in successful one-person
|
|
54
|
+
agent businesses is "use agents to set up agents". ArkaOS already had
|
|
55
|
+
the primitives — The Forge for planning, the Agent tool for subagent
|
|
56
|
+
dispatch, KB-first research — but no single entry point that made the
|
|
57
|
+
pattern *the* way to add capability. This skill is that entry.
|
|
58
|
+
|
|
59
|
+
## Boundaries
|
|
60
|
+
|
|
61
|
+
- This skill **generates**. It does not modify production code.
|
|
62
|
+
- The generated YAML lands in `departments/<dept>/agents/` for review;
|
|
63
|
+
it is **not** auto-registered until the user commits and the next
|
|
64
|
+
loader pass picks it up.
|
|
65
|
+
- Personas built via this skill go through `/kb` for the actual
|
|
66
|
+
content ingestion (YouTube / PDFs / articles). This skill orchestrates,
|
|
67
|
+
it does not bypass the knowledge pipeline.
|
|
68
|
+
|
|
69
|
+
## Cross-references
|
|
70
|
+
|
|
71
|
+
- Source pattern: 2026-05-13 Orgo podcast (Nick Saraev × Greg Isenberg)
|
|
72
|
+
- Related: `/arka forge` (planning engine, lower level)
|
|
73
|
+
- Related: `/kb persona` (content-driven persona builder)
|
|
74
|
+
- Related: `/arka research` (the same research-fan-out pattern for
|
|
75
|
+
one-off knowledge tasks rather than agent generation)
|
|
76
|
+
- Memory: [[project_next_level_conclave]]
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: arka-design-ops
|
|
3
|
+
description: >
|
|
4
|
+
Brand Design Ops sub-squad orchestrator. Routes design-system extraction,
|
|
5
|
+
WCAG audit, and shadcn padronisation work to Iris (lead) + Nia
|
|
6
|
+
(extraction) + Oren (WCAG) + Leo (shadcn). Inspired by the AIOX Squad
|
|
7
|
+
→ Sub-Squad pattern documented in the 2026-04-30 KB live and unblocked
|
|
8
|
+
for ArkaOS by the v2.27.0 Conclave roadmap.
|
|
9
|
+
allowed-tools: [Read, Write, Bash, Edit]
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# /brand design-ops — Design Ops sub-squad
|
|
13
|
+
|
|
14
|
+
> Sub-squad of `/brand` (Valentina). Operational specialists for the
|
|
15
|
+
> production rails of design systems: tokens, audits, components.
|
|
16
|
+
|
|
17
|
+
## Squad
|
|
18
|
+
|
|
19
|
+
| Agent | Role | Tier | Owns |
|
|
20
|
+
| --- | --- | --- | --- |
|
|
21
|
+
| **Iris** (`design-ops-lead`) | Lead | 1 | governance, escalation to Valentina |
|
|
22
|
+
| **Nia** (`extraction-script-writer`) | Specialist | 2 | reverse-engineer tokens from sites / figma |
|
|
23
|
+
| **Oren** (`wcag-auditor`) | Specialist | 2 | WCAG 2.2 AA conformance + reports |
|
|
24
|
+
| **Leo** (`shadcn-padronizer`) | Specialist | 2 | shadcn/ui canonical components |
|
|
25
|
+
|
|
26
|
+
## Subcommands
|
|
27
|
+
|
|
28
|
+
| Command | Owner | What it does |
|
|
29
|
+
| --- | --- | --- |
|
|
30
|
+
| `/brand design-ops extract <url>` | Nia | Extract color, typography, spacing tokens from a live URL or figma file. Writes JSON to `~/.arkaos/design-ops/<slug>/tokens.json`. |
|
|
31
|
+
| `/brand design-ops wcag <url\|path>` | Oren | Run WCAG 2.2 AA audit. Outputs issue table (severity / criterion / location / fix) + conformance score. |
|
|
32
|
+
| `/brand design-ops shadcn <component>` | Leo | Generate or refactor a component to the shadcn/ui canonical form (CVA variants, Radix primitives, theme tokens). |
|
|
33
|
+
| `/brand design-ops audit <project>` | Iris | Orchestrate full audit: tokens drift, WCAG conformance, component-library variance. Produces a single report. |
|
|
34
|
+
|
|
35
|
+
## Scripts (under `scripts/`)
|
|
36
|
+
|
|
37
|
+
The scripts library is intentionally small at v2.27.0 — three reference
|
|
38
|
+
implementations to anchor the pattern. Nia / Oren / Leo extend it as
|
|
39
|
+
ArkaOS evolves; AIOX's reference set is ~30 scripts and ArkaOS will
|
|
40
|
+
catch up incrementally.
|
|
41
|
+
|
|
42
|
+
| Script | Owner | Input | Output |
|
|
43
|
+
| --- | --- | --- | --- |
|
|
44
|
+
| `scripts/extract-colors.py` | Nia | URL or local HTML | `colors.json` (DTCG-compliant token list) |
|
|
45
|
+
| `scripts/wcag-contrast.py` | Oren | hex pairs OR CSS file | Issue list with contrast ratios + AA / AAA verdict |
|
|
46
|
+
| `scripts/shadcn-tokens.py` | Leo | tokens.json | shadcn CSS variables block + Tailwind config snippet |
|
|
47
|
+
|
|
48
|
+
Run any script with `python3 scripts/<name>.py --help` for usage.
|
|
49
|
+
|
|
50
|
+
## Output
|
|
51
|
+
|
|
52
|
+
All design-ops output writes to:
|
|
53
|
+
- `~/.arkaos/design-ops/<slug>/` — generated artefacts (JSON, CSS, reports)
|
|
54
|
+
- `${VAULT_PATH}/Projects/Design Ops/<slug>.md` — human-readable summary (Obsidian)
|
|
55
|
+
|
|
56
|
+
## Boundaries
|
|
57
|
+
|
|
58
|
+
- Strategy and visual direction stay with **Valentina** (`/brand`).
|
|
59
|
+
- Iris escalates anything that requires brand-level taste calls.
|
|
60
|
+
- Design-ops never opens PRs or pushes code; it produces specs, tokens,
|
|
61
|
+
audits. Implementation goes back to `/dev` via Paulo.
|
|
62
|
+
|
|
63
|
+
## Cross-references
|
|
64
|
+
|
|
65
|
+
- Pattern source: KB note `🧠 Knowledge Base/Frameworks/AIOX Squads` (2026-04-30 live)
|
|
66
|
+
- ADR: this skill is the v2.27.0 Conclave instantiation of the sub-squad pattern
|
|
67
|
+
- Parent: `/brand` (Valentina)
|
|
68
|
+
- Stack origin: v2.27.0 (PR5 of 6 from the 2026-05-13 Conclave roadmap)
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Extract colour tokens from CSS / HTML / JSON (Nia's reference script).
|
|
3
|
+
|
|
4
|
+
Scans the input for hex colours and named CSS variables, normalises to
|
|
5
|
+
lower-case 6-digit hex, deduplicates, and emits a DTCG-compliant
|
|
6
|
+
``colors.json`` token list.
|
|
7
|
+
|
|
8
|
+
Usage:
|
|
9
|
+
python3 extract-colors.py path/to/styles.css
|
|
10
|
+
python3 extract-colors.py --stdin < styles.css
|
|
11
|
+
cat styles.css | python3 extract-colors.py --stdin
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
import argparse
|
|
17
|
+
import json
|
|
18
|
+
import re
|
|
19
|
+
import sys
|
|
20
|
+
from pathlib import Path
|
|
21
|
+
|
|
22
|
+
HEX = re.compile(r"#([0-9a-fA-F]{3,8})\b")
|
|
23
|
+
RGB = re.compile(r"rgb(?:a)?\((\d+)\s*,\s*(\d+)\s*,\s*(\d+)")
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def normalise(hex_value: str) -> str | None:
|
|
27
|
+
h = hex_value.lower()
|
|
28
|
+
if len(h) == 3:
|
|
29
|
+
h = "".join(c * 2 for c in h)
|
|
30
|
+
elif len(h) == 4:
|
|
31
|
+
h = "".join(c * 2 for c in h[:3])
|
|
32
|
+
elif len(h) == 8:
|
|
33
|
+
h = h[:6]
|
|
34
|
+
if len(h) != 6:
|
|
35
|
+
return None
|
|
36
|
+
return f"#{h}"
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def rgb_to_hex(r: int, g: int, b: int) -> str:
|
|
40
|
+
return f"#{r:02x}{g:02x}{b:02x}"
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def extract(text: str) -> list[str]:
|
|
44
|
+
seen: set[str] = set()
|
|
45
|
+
out: list[str] = []
|
|
46
|
+
for m in HEX.finditer(text):
|
|
47
|
+
norm = normalise(m.group(1))
|
|
48
|
+
if norm and norm not in seen:
|
|
49
|
+
seen.add(norm)
|
|
50
|
+
out.append(norm)
|
|
51
|
+
for r, g, b in RGB.findall(text):
|
|
52
|
+
norm = rgb_to_hex(int(r), int(g), int(b))
|
|
53
|
+
if norm not in seen:
|
|
54
|
+
seen.add(norm)
|
|
55
|
+
out.append(norm)
|
|
56
|
+
return out
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def to_dtcg(colors: list[str]) -> dict:
|
|
60
|
+
tokens = {}
|
|
61
|
+
for i, c in enumerate(colors):
|
|
62
|
+
slug = f"color-{i:03d}"
|
|
63
|
+
tokens[slug] = {"$value": c, "$type": "color"}
|
|
64
|
+
return {"$schema": "https://design-tokens.org/draft", "colors": tokens}
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def main(argv: list[str]) -> int:
|
|
68
|
+
parser = argparse.ArgumentParser(description=__doc__)
|
|
69
|
+
parser.add_argument("source", nargs="?", help="path to CSS / HTML file")
|
|
70
|
+
parser.add_argument("--stdin", action="store_true", help="read from stdin")
|
|
71
|
+
args = parser.parse_args(argv)
|
|
72
|
+
|
|
73
|
+
if args.stdin or args.source == "-":
|
|
74
|
+
text = sys.stdin.read()
|
|
75
|
+
elif args.source:
|
|
76
|
+
text = Path(args.source).read_text(encoding="utf-8")
|
|
77
|
+
else:
|
|
78
|
+
parser.error("provide a source path or use --stdin")
|
|
79
|
+
|
|
80
|
+
colors = extract(text)
|
|
81
|
+
print(json.dumps(to_dtcg(colors), indent=2))
|
|
82
|
+
return 0
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
if __name__ == "__main__":
|
|
86
|
+
sys.exit(main(sys.argv[1:]))
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Convert DTCG colour tokens into shadcn/ui CSS variables (Leo's script).
|
|
3
|
+
|
|
4
|
+
Reads a DTCG-style ``colors.json`` (as produced by ``extract-colors.py``)
|
|
5
|
+
and emits the canonical shadcn/ui CSS variable block plus a matching
|
|
6
|
+
``tailwind.config.js`` ``theme.extend.colors`` snippet.
|
|
7
|
+
|
|
8
|
+
Usage:
|
|
9
|
+
python3 shadcn-tokens.py colors.json
|
|
10
|
+
python3 shadcn-tokens.py colors.json --prefix brand-
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
import argparse
|
|
16
|
+
import json
|
|
17
|
+
import sys
|
|
18
|
+
from pathlib import Path
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def to_css_var(slug: str, value: str, prefix: str) -> str:
|
|
22
|
+
return f" --{prefix}{slug}: {value};"
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def to_tailwind_entry(slug: str, prefix: str) -> str:
|
|
26
|
+
key = slug.replace("color-", "")
|
|
27
|
+
return f' "{prefix}{slug}": "var(--{prefix}{slug})",'
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def main(argv: list[str]) -> int:
|
|
31
|
+
parser = argparse.ArgumentParser(description=__doc__)
|
|
32
|
+
parser.add_argument("source", help="path to DTCG colors.json")
|
|
33
|
+
parser.add_argument("--prefix", default="", help="CSS variable prefix (e.g. 'brand-')")
|
|
34
|
+
args = parser.parse_args(argv)
|
|
35
|
+
|
|
36
|
+
data = json.loads(Path(args.source).read_text(encoding="utf-8"))
|
|
37
|
+
tokens = data.get("colors", {})
|
|
38
|
+
if not tokens:
|
|
39
|
+
print("no colours found in source", file=sys.stderr)
|
|
40
|
+
return 1
|
|
41
|
+
|
|
42
|
+
css_vars = [to_css_var(slug, t["$value"], args.prefix) for slug, t in tokens.items()]
|
|
43
|
+
tailwind = [to_tailwind_entry(slug, args.prefix) for slug in tokens]
|
|
44
|
+
|
|
45
|
+
print(":root {")
|
|
46
|
+
print("\n".join(css_vars))
|
|
47
|
+
print("}\n")
|
|
48
|
+
|
|
49
|
+
print("// tailwind.config.js — theme.extend.colors")
|
|
50
|
+
print("{")
|
|
51
|
+
print(' theme: { extend: { colors: {')
|
|
52
|
+
print("\n".join(tailwind))
|
|
53
|
+
print(" } } }")
|
|
54
|
+
print("}")
|
|
55
|
+
return 0
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
if __name__ == "__main__":
|
|
59
|
+
sys.exit(main(sys.argv[1:]))
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""WCAG 2.2 contrast ratio checker (Oren's reference script).
|
|
3
|
+
|
|
4
|
+
Compares foreground / background colours and reports whether the pair
|
|
5
|
+
passes WCAG 2.2 Level AA (4.5 : 1 normal text, 3 : 1 large text and UI
|
|
6
|
+
components) and AAA (7 : 1 normal, 4.5 : 1 large).
|
|
7
|
+
|
|
8
|
+
Usage:
|
|
9
|
+
python3 wcag-contrast.py "#1a1a1a" "#ffffff"
|
|
10
|
+
python3 wcag-contrast.py --json "#0f172a" "#ffffff" "#3b82f6" "#ffffff"
|
|
11
|
+
|
|
12
|
+
Outputs a single-line verdict or, with --json, a structured report.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
import argparse
|
|
18
|
+
import json
|
|
19
|
+
import sys
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def relative_luminance(rgb: tuple[int, int, int]) -> float:
|
|
23
|
+
"""WCAG relative luminance formula."""
|
|
24
|
+
def channel(c: int) -> float:
|
|
25
|
+
s = c / 255
|
|
26
|
+
return s / 12.92 if s <= 0.03928 else ((s + 0.055) / 1.055) ** 2.4
|
|
27
|
+
|
|
28
|
+
r, g, b = (channel(c) for c in rgb)
|
|
29
|
+
return 0.2126 * r + 0.7152 * g + 0.0722 * b
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def contrast_ratio(fg: tuple[int, int, int], bg: tuple[int, int, int]) -> float:
|
|
33
|
+
lf, lb = relative_luminance(fg), relative_luminance(bg)
|
|
34
|
+
lighter, darker = max(lf, lb), min(lf, lb)
|
|
35
|
+
return (lighter + 0.05) / (darker + 0.05)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def hex_to_rgb(hex_color: str) -> tuple[int, int, int]:
|
|
39
|
+
s = hex_color.lstrip("#")
|
|
40
|
+
if len(s) == 3:
|
|
41
|
+
s = "".join(c * 2 for c in s)
|
|
42
|
+
if len(s) != 6:
|
|
43
|
+
raise ValueError(f"Invalid hex colour: {hex_color}")
|
|
44
|
+
return int(s[0:2], 16), int(s[2:4], 16), int(s[4:6], 16)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def verdict(ratio: float) -> dict[str, bool]:
|
|
48
|
+
return {
|
|
49
|
+
"AA_normal": ratio >= 4.5,
|
|
50
|
+
"AA_large": ratio >= 3.0,
|
|
51
|
+
"AAA_normal": ratio >= 7.0,
|
|
52
|
+
"AAA_large": ratio >= 4.5,
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def check_pair(fg_hex: str, bg_hex: str) -> dict[str, object]:
|
|
57
|
+
fg = hex_to_rgb(fg_hex)
|
|
58
|
+
bg = hex_to_rgb(bg_hex)
|
|
59
|
+
ratio = round(contrast_ratio(fg, bg), 2)
|
|
60
|
+
return {
|
|
61
|
+
"fg": fg_hex,
|
|
62
|
+
"bg": bg_hex,
|
|
63
|
+
"ratio": ratio,
|
|
64
|
+
**verdict(ratio),
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def main(argv: list[str]) -> int:
|
|
69
|
+
parser = argparse.ArgumentParser(description=__doc__)
|
|
70
|
+
parser.add_argument("colors", nargs="*", help="alternating fg bg pairs")
|
|
71
|
+
parser.add_argument("--json", action="store_true", help="emit JSON report")
|
|
72
|
+
args = parser.parse_args(argv)
|
|
73
|
+
|
|
74
|
+
if len(args.colors) < 2 or len(args.colors) % 2 != 0:
|
|
75
|
+
parser.error("provide pairs of hex colours: <fg> <bg> [<fg> <bg> ...]")
|
|
76
|
+
|
|
77
|
+
pairs = list(zip(args.colors[::2], args.colors[1::2]))
|
|
78
|
+
results = [check_pair(fg, bg) for fg, bg in pairs]
|
|
79
|
+
|
|
80
|
+
if args.json:
|
|
81
|
+
print(json.dumps({"results": results}, indent=2))
|
|
82
|
+
return 0
|
|
83
|
+
|
|
84
|
+
for r in results:
|
|
85
|
+
flags = [k for k, v in r.items() if k.startswith("AA") and v]
|
|
86
|
+
flag_str = "+".join(flags) if flags else "FAILS_ALL"
|
|
87
|
+
print(f"{r['fg']} on {r['bg']}: {r['ratio']}:1 → {flag_str}")
|
|
88
|
+
return 0
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
if __name__ == "__main__":
|
|
92
|
+
sys.exit(main(sys.argv[1:]))
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: arka-research
|
|
3
|
+
description: >
|
|
4
|
+
Fan-out research workflow. Spawns 5 parallel subagents — Perplexity,
|
|
5
|
+
Exa AI, Context7, Firecrawl, XMCP — synthesises their findings into
|
|
6
|
+
a single report, and writes a Knowledge Base note to the Obsidian
|
|
7
|
+
vault. Inspired by the multi-source research pattern surfaced in the
|
|
8
|
+
2026-05-13 Orgo podcast (Nick Saraev): "I tell Claude Code, hey,
|
|
9
|
+
spawn five sub-agents — one for Perplexity, one for Exa, one for
|
|
10
|
+
Context7, one for Firecrawl, one for XMCP — and we get best practices."
|
|
11
|
+
allowed-tools: [Agent, Read, Write, mcp__obsidian__search_notes]
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
# /arka research — one prompt, five parallel sources
|
|
15
|
+
|
|
16
|
+
> Companion to `/arka bootstrap-agent`. Where bootstrap-agent generates
|
|
17
|
+
> capability, this skill generates *knowledge*.
|
|
18
|
+
|
|
19
|
+
## Usage
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
/arka research <topic>
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
`<topic>` is a free-form research question. Examples:
|
|
26
|
+
|
|
27
|
+
- `/arka research "Hermes agent setup with Telegram gateway"`
|
|
28
|
+
- `/arka research "Mistral Medium 3.5 cost vs Sonnet 4.6"`
|
|
29
|
+
- `/arka research "AIOX Squad de Saúde framework"`
|
|
30
|
+
|
|
31
|
+
## Sources fanned out
|
|
32
|
+
|
|
33
|
+
| Subagent | MCP / tool | Strength |
|
|
34
|
+
| --- | --- | --- |
|
|
35
|
+
| Perplexity | `mcp__perplexity__ask` | Real-time web with citations |
|
|
36
|
+
| Exa AI | `mcp__exa__search` | Semantic vector search across the web |
|
|
37
|
+
| Context7 | `mcp__context7__query-docs` | Up-to-date official library docs |
|
|
38
|
+
| Firecrawl | `mcp__firecrawl__firecrawl_scrape` | Site-specific deep scraping |
|
|
39
|
+
| XMCP | `mcp__x__search` | X / Twitter conversations and threads |
|
|
40
|
+
|
|
41
|
+
If any MCP is not registered in `.mcp.json`, that subagent is skipped
|
|
42
|
+
with a `[arka:source-skipped]` note in the report. KB-first protocol
|
|
43
|
+
still applies — Obsidian search runs before any external call.
|
|
44
|
+
|
|
45
|
+
## Workflow
|
|
46
|
+
|
|
47
|
+
```
|
|
48
|
+
1. KB-first: search Obsidian vault for prior notes on the topic.
|
|
49
|
+
If high-confidence matches exist, the report cites them first
|
|
50
|
+
and asks the user whether external research is still needed.
|
|
51
|
+
|
|
52
|
+
2. Fan out 5 subagents in parallel via the Agent tool. Each gets
|
|
53
|
+
the same topic, returns a focused short brief (≤500 words).
|
|
54
|
+
|
|
55
|
+
3. Critic synthesis (Tier 0 reviewer, opus): collapses overlap,
|
|
56
|
+
surfaces contradictions, ranks sources by weight (official docs >
|
|
57
|
+
recent community threads > older articles).
|
|
58
|
+
|
|
59
|
+
4. Synthesis report saved to:
|
|
60
|
+
${VAULT_PATH}/Knowledge Base/Research/<YYYY-MM-DD>-<slug>.md
|
|
61
|
+
with full citations and a "where to go next" section.
|
|
62
|
+
|
|
63
|
+
5. Returns a compact summary to the user (the full report links to
|
|
64
|
+
the Obsidian note).
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Output shape
|
|
68
|
+
|
|
69
|
+
```markdown
|
|
70
|
+
---
|
|
71
|
+
title: <topic>
|
|
72
|
+
date: <YYYY-MM-DD>
|
|
73
|
+
sources: [perplexity, exa, context7, firecrawl, xmcp]
|
|
74
|
+
status: synthesised
|
|
75
|
+
tags: [research, <topic-tag>]
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
# <Topic>
|
|
79
|
+
|
|
80
|
+
## TL;DR
|
|
81
|
+
<3-bullet summary>
|
|
82
|
+
|
|
83
|
+
## Findings by source
|
|
84
|
+
### Perplexity
|
|
85
|
+
...
|
|
86
|
+
### Exa AI
|
|
87
|
+
...
|
|
88
|
+
(etc.)
|
|
89
|
+
|
|
90
|
+
## Synthesis
|
|
91
|
+
<contradictions resolved, ranked by source weight>
|
|
92
|
+
|
|
93
|
+
## Next steps
|
|
94
|
+
<2-3 follow-up questions or actions>
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## Cost & budget
|
|
98
|
+
|
|
99
|
+
This is a heavy operation — 5 parallel LLM calls plus a synthesis call.
|
|
100
|
+
A typical run consumes 3-8k tokens. The `/arka costs` skill tracks the
|
|
101
|
+
spend and the `[arka:warn]` token-hygiene hook flags excessive use.
|
|
102
|
+
|
|
103
|
+
## Boundaries
|
|
104
|
+
|
|
105
|
+
- Read-only: this skill never modifies code or configuration. It only
|
|
106
|
+
writes to the Obsidian Knowledge Base.
|
|
107
|
+
- KB-first: always check the vault first. Many topics already have
|
|
108
|
+
notes from prior Dreaming / Research sessions.
|
|
109
|
+
- No external research without the user asking — agents do not
|
|
110
|
+
proactively fan out unless `/arka research` is invoked.
|
|
111
|
+
|
|
112
|
+
## Cross-references
|
|
113
|
+
|
|
114
|
+
- Source pattern: 2026-05-13 Orgo podcast
|
|
115
|
+
- Related: `/arka forge` (planning, not research)
|
|
116
|
+
- Related: `/kb research` (Clara, knowledge management)
|
|
117
|
+
- Related: `/arka bootstrap-agent` (capability-from-research)
|
package/config/constitution.yaml
CHANGED
|
@@ -135,6 +135,10 @@ enforcement_levels:
|
|
|
135
135
|
rule: "All ecosystem skills must follow the standard 7-phase workflow defined in config/standards/ecosystem-workflow.md"
|
|
136
136
|
enforcement: "Ecosystem SKILL.md files must reference or implement all 7 phases"
|
|
137
137
|
|
|
138
|
+
- id: sub-squad-hierarchy
|
|
139
|
+
rule: "Agents in a sub-squad MUST declare parent_squad (department slug) and sub_squad_role. Sub-squads inherit the parent department's authority chain; their lead escalates to the department squad lead, not directly to Tier 0."
|
|
140
|
+
enforcement: "Agent loader validates parent_squad + sub_squad_role coherence (sub_squad_role requires parent_squad). PR5 v2.27.0 introduced the pattern; AIOX Squad → Sub-Squad → Skills/Scripts is the reference architecture."
|
|
141
|
+
|
|
138
142
|
- id: forge-persistence
|
|
139
143
|
rule: "All forge plans (approved and rejected) must be persisted to Obsidian"
|
|
140
144
|
enforcement: "persistence.py exports on status change; audit detects missing exports"
|
|
@@ -391,6 +391,23 @@ if not match and all_deliverables:
|
|
|
391
391
|
fi
|
|
392
392
|
fi
|
|
393
393
|
|
|
394
|
+
# ─── Cognitive layer capture (PR4 v2.26.0 hooks-as-retrieval) ────────────
|
|
395
|
+
# Backgrounded so the hook returns immediately. The retrieval helper has
|
|
396
|
+
# its own internal time caps (ripgrep ≤ 1s, Python fallback capped at 500
|
|
397
|
+
# files) so a runaway extract can never block the next user prompt. We
|
|
398
|
+
# disown so the child cannot zombie if the hook process exits first.
|
|
399
|
+
if [ -n "$SESSION_ID_PTU" ] && [ -n "$TOOL_OUTPUT" ]; then
|
|
400
|
+
_ARKAOS_REPO=$(cat "$HOME/.arkaos/.repo-path" 2>/dev/null || echo "")
|
|
401
|
+
if [ -n "$_ARKAOS_REPO" ] && [ -d "$_ARKAOS_REPO" ]; then
|
|
402
|
+
(
|
|
403
|
+
PYTHONPATH="$_ARKAOS_REPO" python3 -m core.cognition.retrieval capture "$SESSION_ID_PTU" <<EOF >/dev/null 2>&1
|
|
404
|
+
$TOOL_OUTPUT
|
|
405
|
+
EOF
|
|
406
|
+
) &
|
|
407
|
+
disown 2>/dev/null || true
|
|
408
|
+
fi
|
|
409
|
+
fi
|
|
410
|
+
|
|
394
411
|
# ─── Log Metrics ─────────────────────────────────────────────────────────
|
|
395
412
|
_DURATION_MS=$(_hook_ms)
|
|
396
413
|
METRICS_FILE="$HOME/.arkaos/hook-metrics.json"
|
|
@@ -356,9 +356,25 @@ sequential-validation, full-visibility, arka-supremacy."
|
|
|
356
356
|
fi
|
|
357
357
|
fi
|
|
358
358
|
|
|
359
|
+
# ─── Cognitive context injection (PR4 v2.26.0 hooks-as-retrieval) ───────
|
|
360
|
+
# Read the context cache populated by the previous PostToolUse turn and
|
|
361
|
+
# inject any KB hits as an `[arka:context]` advisory. The Python helper
|
|
362
|
+
# has internal TTL filtering (10 min) and returns empty when there is no
|
|
363
|
+
# fresh cache, so this stays a no-op until the cognitive layer has data
|
|
364
|
+
# to share.
|
|
365
|
+
_ARKA_CONTEXT_HITS=""
|
|
366
|
+
if [ -n "$SESSION_ID" ]; then
|
|
367
|
+
_ARKAOS_REPO=$(cat "$HOME/.arkaos/.repo-path" 2>/dev/null || echo "")
|
|
368
|
+
if [ -n "$_ARKAOS_REPO" ] && [ -d "$_ARKAOS_REPO" ]; then
|
|
369
|
+
_ARKA_CONTEXT_HITS=$(PYTHONPATH="$_ARKAOS_REPO" python3 -m core.cognition.retrieval inject "$SESSION_ID" 2>/dev/null)
|
|
370
|
+
fi
|
|
371
|
+
fi
|
|
372
|
+
|
|
359
373
|
# ─── Output ──────────────────────────────────────────────────────────────
|
|
360
374
|
_OUT_CONTEXT="${_ARKA_GREETING:-}${_SYNC_NOTICE:-}${_ROUTE_REMINDER}${_WORKFLOW_DIRECTIVE} $python_result"
|
|
361
375
|
[ -n "$_HYGIENE" ] && _OUT_CONTEXT="$_OUT_CONTEXT $_HYGIENE"
|
|
376
|
+
[ -n "$_ARKA_CONTEXT_HITS" ] && _OUT_CONTEXT="$_OUT_CONTEXT
|
|
377
|
+
$_ARKA_CONTEXT_HITS"
|
|
362
378
|
# Escape for JSON
|
|
363
379
|
_OUT_JSON=$(python3 -c "import json,sys; print(json.dumps(sys.stdin.read()))" <<< "$_OUT_CONTEXT" 2>/dev/null)
|
|
364
380
|
if [ -n "$_OUT_JSON" ]; then
|
|
Binary file
|
|
Binary file
|
package/core/agents/loader.py
CHANGED
|
@@ -60,7 +60,10 @@ def load_all_agents(
|
|
|
60
60
|
for agents_dir in search_dirs:
|
|
61
61
|
if not agents_dir.is_dir():
|
|
62
62
|
continue
|
|
63
|
-
|
|
63
|
+
# Recursive rglob picks up sub-squad subdirectories (e.g.
|
|
64
|
+
# departments/brand/agents/design-ops/*.yaml) per the v2.27.0
|
|
65
|
+
# Sub-Squad pattern. Sorted output keeps load order stable.
|
|
66
|
+
for yaml_file in sorted(agents_dir.rglob("*.yaml")):
|
|
64
67
|
try:
|
|
65
68
|
agent = load_agent(yaml_file)
|
|
66
69
|
agents.append(agent)
|
package/core/agents/schema.py
CHANGED
|
@@ -273,6 +273,35 @@ class Agent(BaseModel):
|
|
|
273
273
|
description="Claude model override for dispatch. Falls back to tier default when None.",
|
|
274
274
|
)
|
|
275
275
|
|
|
276
|
+
parent_squad: Optional[str] = Field(
|
|
277
|
+
default=None,
|
|
278
|
+
description=(
|
|
279
|
+
"Slug of the parent squad when this agent belongs to a sub-squad. "
|
|
280
|
+
"Sub-squads are operational specialisations inside a department "
|
|
281
|
+
"(e.g. brand → design-ops). Optional and backwards-compatible: "
|
|
282
|
+
"agents without a parent_squad belong directly to their department."
|
|
283
|
+
),
|
|
284
|
+
)
|
|
285
|
+
|
|
286
|
+
sub_squad_role: Optional[str] = Field(
|
|
287
|
+
default=None,
|
|
288
|
+
description=(
|
|
289
|
+
"Role within the sub-squad when parent_squad is set "
|
|
290
|
+
"(e.g. 'lead', 'extraction-script-writer', 'wcag-auditor'). "
|
|
291
|
+
"Inspired by the AIOX Squad → Sub-Squad pattern (KB note "
|
|
292
|
+
"AIOX Squads, April 30 2026 live)."
|
|
293
|
+
),
|
|
294
|
+
)
|
|
295
|
+
|
|
296
|
+
@model_validator(mode="after")
|
|
297
|
+
def validate_sub_squad_coherence(self) -> "Agent":
|
|
298
|
+
"""sub_squad_role only makes sense when parent_squad is set."""
|
|
299
|
+
if self.sub_squad_role and not self.parent_squad:
|
|
300
|
+
raise ValueError(
|
|
301
|
+
"sub_squad_role requires parent_squad to be set"
|
|
302
|
+
)
|
|
303
|
+
return self
|
|
304
|
+
|
|
276
305
|
@model_validator(mode="after")
|
|
277
306
|
def auto_fill_memory_path(self) -> "Agent":
|
|
278
307
|
if not self.memory_path:
|
|
Binary file
|
|
Binary file
|