@archznn/crewloop-skills 0.2.0 → 0.4.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/README.md +21 -31
- package/assets/templates/skill-template.md +58 -0
- package/package.json +5 -1
- package/references/conventions.md +144 -0
- package/references/obsidian-mcp-usage.md +190 -0
- package/references/skill-anatomy.md +77 -0
- package/references/workflow.md +64 -0
- package/servers/dashboard/README.md +87 -0
- package/servers/dashboard/bin/crewloop-dashboard.js +5 -0
- package/servers/dashboard/config-examples/codex-hooks.json +14 -0
- package/servers/dashboard/config-examples/kimi-code-config.toml +6 -0
- package/servers/dashboard/config-examples/opencode-plugin/crewloop-dashboard.js +64 -0
- package/servers/dashboard/package.json +46 -0
- package/servers/dashboard/public/app.js +447 -0
- package/servers/dashboard/public/index.html +96 -0
- package/servers/dashboard/public/styles.css +664 -0
- package/servers/dashboard/src/adapters/codex.ts +50 -0
- package/servers/dashboard/src/adapters/kimi.ts +40 -0
- package/servers/dashboard/src/adapters/opencode.ts +36 -0
- package/servers/dashboard/src/adapters/shim.test.ts +74 -0
- package/servers/dashboard/src/adapters/shim.ts +120 -0
- package/servers/dashboard/src/api/event.ts +70 -0
- package/servers/dashboard/src/api/skills.ts +11 -0
- package/servers/dashboard/src/config.ts +66 -0
- package/servers/dashboard/src/filters/sanitize.test.ts +94 -0
- package/servers/dashboard/src/filters/sanitize.ts +78 -0
- package/servers/dashboard/src/index.ts +24 -0
- package/servers/dashboard/src/presenter.test.ts +69 -0
- package/servers/dashboard/src/presenter.ts +56 -0
- package/servers/dashboard/src/server.test.ts +123 -0
- package/servers/dashboard/src/server.ts +191 -0
- package/servers/dashboard/src/skills/infer.test.ts +86 -0
- package/servers/dashboard/src/skills/infer.ts +53 -0
- package/servers/dashboard/src/skills/mapping.ts +26 -0
- package/servers/dashboard/src/skills/registry.ts +60 -0
- package/servers/dashboard/src/state.test.ts +88 -0
- package/servers/dashboard/src/state.ts +115 -0
- package/servers/dashboard/src/types.ts +110 -0
- package/servers/dashboard/tsconfig.json +19 -0
- package/servers/obsidian-mcp/README.md +82 -0
- package/servers/obsidian-mcp/pyproject.toml +32 -0
- package/servers/obsidian-mcp/src/obsidian_mcp/__init__.py +0 -0
- package/servers/obsidian-mcp/src/obsidian_mcp/config.py +47 -0
- package/servers/obsidian-mcp/src/obsidian_mcp/indexer/__init__.py +0 -0
- package/servers/obsidian-mcp/src/obsidian_mcp/indexer/embeddings.py +105 -0
- package/servers/obsidian-mcp/src/obsidian_mcp/indexer/indexer.py +79 -0
- package/servers/obsidian-mcp/src/obsidian_mcp/indexer/store.py +141 -0
- package/servers/obsidian-mcp/src/obsidian_mcp/indexer/sync.py +37 -0
- package/servers/obsidian-mcp/src/obsidian_mcp/learning/__init__.py +0 -0
- package/servers/obsidian-mcp/src/obsidian_mcp/learning/detector.py +66 -0
- package/servers/obsidian-mcp/src/obsidian_mcp/learning/note_generator.py +40 -0
- package/servers/obsidian-mcp/src/obsidian_mcp/main.py +4 -0
- package/servers/obsidian-mcp/src/obsidian_mcp/models.py +42 -0
- package/servers/obsidian-mcp/src/obsidian_mcp/privacy/__init__.py +0 -0
- package/servers/obsidian-mcp/src/obsidian_mcp/privacy/filter.py +68 -0
- package/servers/obsidian-mcp/src/obsidian_mcp/rag/__init__.py +0 -0
- package/servers/obsidian-mcp/src/obsidian_mcp/rag/engine.py +50 -0
- package/servers/obsidian-mcp/src/obsidian_mcp/rag/graph_search.py +55 -0
- package/servers/obsidian-mcp/src/obsidian_mcp/rag/text_search.py +37 -0
- package/servers/obsidian-mcp/src/obsidian_mcp/rag/vector_search.py +118 -0
- package/servers/obsidian-mcp/src/obsidian_mcp/server.py +61 -0
- package/servers/obsidian-mcp/src/obsidian_mcp/tools/__init__.py +0 -0
- package/servers/obsidian-mcp/src/obsidian_mcp/tools/create.py +43 -0
- package/servers/obsidian-mcp/src/obsidian_mcp/tools/delete.py +16 -0
- package/servers/obsidian-mcp/src/obsidian_mcp/tools/learn.py +42 -0
- package/servers/obsidian-mcp/src/obsidian_mcp/tools/list.py +16 -0
- package/servers/obsidian-mcp/src/obsidian_mcp/tools/read.py +15 -0
- package/servers/obsidian-mcp/src/obsidian_mcp/tools/registry.py +130 -0
- package/servers/obsidian-mcp/src/obsidian_mcp/tools/related.py +20 -0
- package/servers/obsidian-mcp/src/obsidian_mcp/tools/search.py +26 -0
- package/servers/obsidian-mcp/src/obsidian_mcp/tools/sync.py +22 -0
- package/servers/obsidian-mcp/src/obsidian_mcp/tools/update.py +34 -0
- package/servers/obsidian-mcp/src/obsidian_mcp/vault/__init__.py +0 -0
- package/servers/obsidian-mcp/src/obsidian_mcp/vault/parser.py +82 -0
- package/servers/obsidian-mcp/src/obsidian_mcp/vault/repository.py +68 -0
- package/servers/obsidian-mcp/src/obsidian_mcp/vault/writer.py +61 -0
- package/servers/obsidian-mcp/tests/conftest.py +39 -0
- package/servers/obsidian-mcp/tests/test_async_tools.py +87 -0
- package/servers/obsidian-mcp/tests/test_edge_cases.py +59 -0
- package/servers/obsidian-mcp/tests/test_indexer.py +27 -0
- package/servers/obsidian-mcp/tests/test_integration.py +90 -0
- package/servers/obsidian-mcp/tests/test_learning.py +34 -0
- package/servers/obsidian-mcp/tests/test_privacy.py +31 -0
- package/servers/obsidian-mcp/tests/test_privacy_config.py +44 -0
- package/servers/obsidian-mcp/tests/test_rag.py +64 -0
- package/servers/obsidian-mcp/tests/test_read_raw.py +37 -0
- package/servers/obsidian-mcp/tests/test_tfidf_fallback.py +54 -0
- package/servers/obsidian-mcp/tests/test_tools.py +108 -0
- package/servers/obsidian-mcp/tests/test_vault.py +103 -0
- package/servers/obsidian-mcp/tests/test_writer.py +139 -0
- package/skills/accessibility-auditor/SKILL.md +262 -0
- package/skills/accessibility-auditor/references/a11y-checklist.md +66 -0
- package/skills/architect/SKILL.md +1 -1
- package/skills/designer/SKILL.md +1 -1
- package/skills/docs-writer/SKILL.md +1 -1
- package/skills/engineer/SKILL.md +1 -1
- package/skills/maintainer/SKILL.md +22 -22
- package/skills/obsidian-second-brain/SKILL.md +48 -13
- package/skills/orchestrator/SKILL.md +1 -1
- package/skills/product-manager/SKILL.md +22 -22
- package/skills/researcher/SKILL.md +22 -22
- package/skills/reviewer/SKILL.md +1 -1
- package/skills/security-guard/SKILL.md +142 -0
- package/skills/security-guard/references/security-checklist.md +57 -0
- package/skills/shipper/SKILL.md +1 -1
- package/skills/tester/SKILL.md +22 -22
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
|
|
3
|
+
from obsidian_mcp.models import Note
|
|
4
|
+
from obsidian_mcp.vault.parser import parse_note
|
|
5
|
+
from obsidian_mcp.vault.repository import VaultRepository
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def test_parse_note_with_frontmatter(temp_vault):
|
|
9
|
+
path = temp_vault / "test.md"
|
|
10
|
+
path.write_text("---\ntitle: Hello\ntags: [a, b]\n---\n\nBody here with [[link]].")
|
|
11
|
+
note = parse_note("test.md", path)
|
|
12
|
+
assert note.title == "Hello"
|
|
13
|
+
assert note.frontmatter["tags"] == ["a", "b"]
|
|
14
|
+
assert "Body here" in note.content
|
|
15
|
+
assert note.links == ["link.md"]
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def test_parse_note_without_frontmatter(temp_vault):
|
|
19
|
+
path = temp_vault / "no-front.md"
|
|
20
|
+
path.write_text("# My Title\n\nContent.")
|
|
21
|
+
note = parse_note("no-front.md", path)
|
|
22
|
+
assert note.title == "My Title"
|
|
23
|
+
assert note.content == "Content."
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def test_parse_note_with_invalid_frontmatter(temp_vault):
|
|
27
|
+
path = temp_vault / "bad-front.md"
|
|
28
|
+
path.write_text("---\ntitle: [unclosed\n---\n\nBody.")
|
|
29
|
+
note = parse_note("bad-front.md", path)
|
|
30
|
+
assert note.frontmatter == {}
|
|
31
|
+
assert "Body." in note.content
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def test_vault_crud(config):
|
|
35
|
+
repo = VaultRepository(config)
|
|
36
|
+
note = Note(path="foo.md", title="Foo", content="bar")
|
|
37
|
+
repo.save(note)
|
|
38
|
+
assert repo.exists("foo.md")
|
|
39
|
+
read = repo.read("foo.md")
|
|
40
|
+
assert read.title == "Foo"
|
|
41
|
+
repo.delete("foo.md")
|
|
42
|
+
assert not repo.exists("foo.md")
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def test_vault_path_escape(config):
|
|
46
|
+
repo = VaultRepository(config)
|
|
47
|
+
with pytest.raises(ValueError):
|
|
48
|
+
repo._resolve("../outside.md")
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def test_writer_preserves_created_and_tags(config):
|
|
52
|
+
repo = VaultRepository(config)
|
|
53
|
+
note = Note(path="w.md", title="W", content="body", tags=["tag1"])
|
|
54
|
+
repo.save(note)
|
|
55
|
+
first = repo.read("w.md")
|
|
56
|
+
created = first.frontmatter["created"]
|
|
57
|
+
repo.save(first)
|
|
58
|
+
second = repo.read("w.md")
|
|
59
|
+
assert second.frontmatter["created"] == created
|
|
60
|
+
assert "tag1" in second.frontmatter["tags"]
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def test_vault_ignores_hidden_dirs(config):
|
|
64
|
+
repo = VaultRepository(config)
|
|
65
|
+
hidden = repo.root / ".hidden"
|
|
66
|
+
hidden.mkdir()
|
|
67
|
+
(hidden / "secret.md").write_text("# secret")
|
|
68
|
+
(repo.root / "visible.md").write_text("# visible")
|
|
69
|
+
notes = repo.list_notes()
|
|
70
|
+
assert "visible.md" in notes
|
|
71
|
+
assert "secret.md" not in notes
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def test_vault_blocks_symlink_escape(config, temp_vault):
|
|
75
|
+
repo = VaultRepository(config)
|
|
76
|
+
outside = temp_vault.parent / "outside.md"
|
|
77
|
+
outside.write_text("# outside")
|
|
78
|
+
link = repo.root / "escape.md"
|
|
79
|
+
link.symlink_to(outside)
|
|
80
|
+
assert "escape.md" not in repo.list_notes()
|
|
81
|
+
with pytest.raises(ValueError):
|
|
82
|
+
repo.read("escape.md")
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def test_vault_read_raw_returns_exact_content(config):
|
|
86
|
+
repo = VaultRepository(config)
|
|
87
|
+
raw = "---\ntitle: Raw\ntags:\n - a\n---\n\nbody"
|
|
88
|
+
(repo.root / "raw.md").write_text(raw, encoding="utf-8")
|
|
89
|
+
assert repo.read_raw("raw.md") == raw
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def test_vault_read_raw_missing_raises(config):
|
|
93
|
+
repo = VaultRepository(config)
|
|
94
|
+
with pytest.raises(FileNotFoundError):
|
|
95
|
+
repo.read_raw("missing.md")
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def test_vault_read_raw_replaces_non_utf8(config, temp_vault):
|
|
99
|
+
repo = VaultRepository(config)
|
|
100
|
+
(repo.root / "binary.md").write_bytes(b"\xff\xfeHello\xfaworld")
|
|
101
|
+
text = repo.read_raw("binary.md")
|
|
102
|
+
assert "Hello" in text
|
|
103
|
+
assert "world" in text
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
from datetime import datetime
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
import yaml
|
|
5
|
+
|
|
6
|
+
from obsidian_mcp.models import Note
|
|
7
|
+
from obsidian_mcp.vault.writer import write_note
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def _parse_frontmatter(text: str) -> dict:
|
|
11
|
+
assert text.startswith("---")
|
|
12
|
+
end = text.find("\n---", 3)
|
|
13
|
+
assert end != -1
|
|
14
|
+
return yaml.safe_load(text[3:end])
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def _get_body(text: str) -> str:
|
|
18
|
+
end = text.find("\n---", 3)
|
|
19
|
+
return text[end + 5 :].lstrip("\n")
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def test_write_note_without_frontmatter(temp_vault):
|
|
23
|
+
path = temp_vault / "clean.md"
|
|
24
|
+
note = Note(path="clean.md", title="Clean Note", content="# Clean Note\n\nBody.")
|
|
25
|
+
write_note(path, note)
|
|
26
|
+
|
|
27
|
+
written = path.read_text(encoding="utf-8")
|
|
28
|
+
frontmatter = _parse_frontmatter(written)
|
|
29
|
+
body = _get_body(written)
|
|
30
|
+
|
|
31
|
+
assert frontmatter["title"] == "Clean Note"
|
|
32
|
+
assert "created" in frontmatter
|
|
33
|
+
assert "updated" in frontmatter
|
|
34
|
+
assert body == "# Clean Note\n\nBody."
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def test_write_note_with_embedded_frontmatter(temp_vault):
|
|
38
|
+
path = temp_vault / "embedded.md"
|
|
39
|
+
content = (
|
|
40
|
+
"---\n"
|
|
41
|
+
"type: journal\n"
|
|
42
|
+
"title: Embedded Title\n"
|
|
43
|
+
"tags: [a, b]\n"
|
|
44
|
+
"updated: 2026-06-16T17:35:00Z\n"
|
|
45
|
+
"---\n"
|
|
46
|
+
"\n"
|
|
47
|
+
"# Embedded Title\n"
|
|
48
|
+
"\n"
|
|
49
|
+
"Body text."
|
|
50
|
+
)
|
|
51
|
+
note = Note(path="embedded.md", title="Server Title", content=content, tags=["c"])
|
|
52
|
+
write_note(path, note)
|
|
53
|
+
|
|
54
|
+
written = path.read_text(encoding="utf-8")
|
|
55
|
+
frontmatter = _parse_frontmatter(written)
|
|
56
|
+
body = _get_body(written)
|
|
57
|
+
|
|
58
|
+
assert frontmatter["title"] == "Server Title"
|
|
59
|
+
assert frontmatter["type"] == "journal"
|
|
60
|
+
assert frontmatter["tags"] == ["a", "b", "c"]
|
|
61
|
+
assert "created" in frontmatter
|
|
62
|
+
assert "updated" in frontmatter
|
|
63
|
+
assert body == "# Embedded Title\n\nBody text."
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def test_write_note_with_malformed_frontmatter(temp_vault, caplog):
|
|
67
|
+
path = temp_vault / "bad.md"
|
|
68
|
+
content = "---\ntitle: [unclosed\n---\n\nBody."
|
|
69
|
+
note = Note(path="bad.md", title="Bad Note", content=content)
|
|
70
|
+
|
|
71
|
+
with caplog.at_level("WARNING"):
|
|
72
|
+
write_note(path, note)
|
|
73
|
+
|
|
74
|
+
written = path.read_text(encoding="utf-8")
|
|
75
|
+
frontmatter = _parse_frontmatter(written)
|
|
76
|
+
body = _get_body(written)
|
|
77
|
+
|
|
78
|
+
assert "failed to parse frontmatter" in caplog.text.lower()
|
|
79
|
+
assert frontmatter["title"] == "Bad Note"
|
|
80
|
+
assert body == "Body."
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def test_write_note_merges_tags(temp_vault):
|
|
84
|
+
path = temp_vault / "tags.md"
|
|
85
|
+
content = "---\ntags: [z, a, a]\n---\n\nBody."
|
|
86
|
+
note = Note(path="tags.md", title="Tags Note", content=content, tags=["b", "a"])
|
|
87
|
+
write_note(path, note)
|
|
88
|
+
|
|
89
|
+
written = path.read_text(encoding="utf-8")
|
|
90
|
+
frontmatter = _parse_frontmatter(written)
|
|
91
|
+
|
|
92
|
+
assert frontmatter["tags"] == ["a", "b", "z"]
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def test_write_note_preserves_created_on_update(temp_vault):
|
|
96
|
+
path = temp_vault / "preserve.md"
|
|
97
|
+
note = Note(path="preserve.md", title="Preserve", content="Body.")
|
|
98
|
+
write_note(path, note)
|
|
99
|
+
|
|
100
|
+
first = path.read_text(encoding="utf-8")
|
|
101
|
+
first_created = _parse_frontmatter(first)["created"]
|
|
102
|
+
|
|
103
|
+
note2 = Note(
|
|
104
|
+
path="preserve.md",
|
|
105
|
+
title="Preserve",
|
|
106
|
+
content="Updated body.",
|
|
107
|
+
frontmatter={"created": first_created},
|
|
108
|
+
)
|
|
109
|
+
write_note(path, note2)
|
|
110
|
+
|
|
111
|
+
second = path.read_text(encoding="utf-8")
|
|
112
|
+
second_frontmatter = _parse_frontmatter(second)
|
|
113
|
+
|
|
114
|
+
assert second_frontmatter["created"] == first_created
|
|
115
|
+
assert second_frontmatter["updated"] != first_created
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def test_write_note_title_precedence(temp_vault):
|
|
119
|
+
path = temp_vault / "title.md"
|
|
120
|
+
content = "---\ntitle: Embedded Title\n---\n\nBody."
|
|
121
|
+
note = Note(path="title.md", title="Server Title", content=content)
|
|
122
|
+
write_note(path, note)
|
|
123
|
+
|
|
124
|
+
written = path.read_text(encoding="utf-8")
|
|
125
|
+
frontmatter = _parse_frontmatter(written)
|
|
126
|
+
|
|
127
|
+
assert frontmatter["title"] == "Server Title"
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def test_write_note_tags_as_string(temp_vault):
|
|
131
|
+
path = temp_vault / "string-tags.md"
|
|
132
|
+
content = "---\ntags: x, y, z\n---\n\nBody."
|
|
133
|
+
note = Note(path="string-tags.md", title="String Tags", content=content)
|
|
134
|
+
write_note(path, note)
|
|
135
|
+
|
|
136
|
+
written = path.read_text(encoding="utf-8")
|
|
137
|
+
frontmatter = _parse_frontmatter(written)
|
|
138
|
+
|
|
139
|
+
assert frontmatter["tags"] == ["x", "y", "z"]
|
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: accessibility-auditor
|
|
3
|
+
description: Use this skill for accessibility/a11y audits, WCAG compliance, screen reader support, keyboard/focus management, color contrast, motion preferences, and accessible design. Also trigger on semantic HTML, ARIA, alt text, labels, headings, touch targets, responsive behavior, or disability inclusion.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Accessibility Auditor — WCAG & Inclusive UI Review
|
|
7
|
+
|
|
8
|
+
## ROLE
|
|
9
|
+
|
|
10
|
+
You are the accessibility specialist for the Loop Engineering Agents team. Your job is to review UI implementations for compliance with WCAG 2.1 level AA (or a specified level), inclusive interaction design, and robust assistive-technology support.
|
|
11
|
+
|
|
12
|
+
You do NOT write production code. You do NOT run git operations. You do not replace the reviewer; you provide a focused accessibility assessment that the reviewer can incorporate into the broader quality gate.
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## MODE
|
|
17
|
+
|
|
18
|
+
**REVIEW only.** Analyze, judge, and report. Do not implement fixes. Do not run git operations.
|
|
19
|
+
|
|
20
|
+
**NEVER write code** — If you spot issues, report them with severity and remediation steps. Redirect fixes to the engineer skill.
|
|
21
|
+
|
|
22
|
+
**NEVER run git operations** — Branch, commit, and PR belong to the shipper.
|
|
23
|
+
|
|
24
|
+
**When done, present navigation options** — Return to the standard letter-based menu.
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## MEMORY & CONTEXT
|
|
29
|
+
|
|
30
|
+
**Always invoke the `obsidian-second-brain` skill via the `Skill` tool.**
|
|
31
|
+
Never read or write files inside `~/.lea` directly with `Read`, `Edit`, `Write`, or `Bash`.
|
|
32
|
+
|
|
33
|
+
At the start of the task, the `obsidian-second-brain` skill will search and read the relevant layers for this role.
|
|
34
|
+
At the end of the task, it will persist outcomes to the correct layers.
|
|
35
|
+
|
|
36
|
+
This skill's targets:
|
|
37
|
+
- **Read at start:** prior accessibility decisions, known a11y patterns, and project design system guidance
|
|
38
|
+
- **Persist at end:** audit findings to journal; reusable a11y heuristics to knowledge; active context to curated memory
|
|
39
|
+
|
|
40
|
+
### MCP Tools Reference
|
|
41
|
+
|
|
42
|
+
| Tool | When to use |
|
|
43
|
+
|------|-------------|
|
|
44
|
+
| `search_notes` | Find prior a11y decisions and common failures in `Knowledge/` and `Journal/`. |
|
|
45
|
+
| `learn_from_text` | Persist a new accessibility heuristic or remediation pattern. |
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
## AFK MODE & ROLE PREFIX
|
|
50
|
+
|
|
51
|
+
**Role prefix:** [ACCESSIBILITY-AUDITOR CHECKING]
|
|
52
|
+
|
|
53
|
+
Print this prefix on its own line before the first line of every response.
|
|
54
|
+
|
|
55
|
+
**AFK mode activation:**
|
|
56
|
+
- User says "AFK", "estarei AFK", "modo AFK", "vou ficar AFK", or similar explicit marker.
|
|
57
|
+
- `MEMORY.md` contains `afk: true`.
|
|
58
|
+
|
|
59
|
+
**AFK mode behavior:**
|
|
60
|
+
- Skip the navigation menu at the end.
|
|
61
|
+
- State the next skill being activated.
|
|
62
|
+
- Load the next skill via the Skill tool (do not wait for user choice).
|
|
63
|
+
|
|
64
|
+
**Next skill:** Engineer (to fix issues) or Reviewer (if the audit is clean and should fold into general review).
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
## WORKFLOW
|
|
69
|
+
|
|
70
|
+
### Step 1: Understand the UI Context
|
|
71
|
+
|
|
72
|
+
Read the spec, design, and implementation related to the UI change. Identify:
|
|
73
|
+
- What UI components, pages, or flows changed?
|
|
74
|
+
- Are there interactive controls (buttons, links, forms, modals, dropdowns)?
|
|
75
|
+
- Are there media, motion, color, or typography changes?
|
|
76
|
+
|
|
77
|
+
If there is no UI work: "No UI changes detected. Accessibility audit is not needed."
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
### Step 2: Inspect the Implementation
|
|
82
|
+
|
|
83
|
+
Read every changed UI file. Focus on:
|
|
84
|
+
- HTML/JSX templates and component markup
|
|
85
|
+
- CSS/styling for color, contrast, focus, motion, and responsive layout
|
|
86
|
+
- Event handlers and focus management logic
|
|
87
|
+
- Static assets such as images and icons
|
|
88
|
+
|
|
89
|
+
Use the checklist in `references/a11y-checklist.md` as the review backbone.
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
### Step 3: Run Accessibility Checks
|
|
94
|
+
|
|
95
|
+
For each applicable check, produce a verdict: **PASS**, **WARN**, or **FAIL**.
|
|
96
|
+
|
|
97
|
+
#### 3.1 Semantic Structure
|
|
98
|
+
|
|
99
|
+
- [ ] Semantic HTML elements (`<header>`, `<nav>`, `<main>`, `<section>`, `<article>`, `<footer>`, `<button>`, `<a>`, `<label>`) are used appropriately.
|
|
100
|
+
- [ ] Heading hierarchy (`h1` → `h2` → `h3`) is logical and does not skip levels for styling.
|
|
101
|
+
- [ ] Lists use `<ul>`, `<ol>`, and `<li>`; tables use `<table>` semantics when appropriate.
|
|
102
|
+
|
|
103
|
+
**Verdict:** PASS / WARN / FAIL
|
|
104
|
+
|
|
105
|
+
#### 3.2 Keyboard & Focus
|
|
106
|
+
|
|
107
|
+
- [ ] All interactive elements are reachable and operable with a keyboard.
|
|
108
|
+
- [ ] Focus order matches visual reading order.
|
|
109
|
+
- [ ] Focus indicators are visible and have sufficient contrast.
|
|
110
|
+
- [ ] Modals, dialogs, and menus trap focus and restore focus on close.
|
|
111
|
+
- [ ] There are no keyboard traps or unreachable content.
|
|
112
|
+
|
|
113
|
+
**Verdict:** PASS / WARN / FAIL
|
|
114
|
+
|
|
115
|
+
#### 3.3 ARIA
|
|
116
|
+
|
|
117
|
+
- [ ] ARIA roles, states, and properties are used correctly and only when native semantics are insufficient.
|
|
118
|
+
- [ ] Dynamic content updates use `aria-live` regions appropriately.
|
|
119
|
+
- [ ] Custom controls expose name, role, and value via ARIA or native attributes.
|
|
120
|
+
|
|
121
|
+
**Verdict:** PASS / WARN / FAIL
|
|
122
|
+
|
|
123
|
+
#### 3.4 Color & Contrast
|
|
124
|
+
|
|
125
|
+
- [ ] Text meets WCAG 2.1 AA contrast ratios (4.5:1 for normal text, 3:1 for large text).
|
|
126
|
+
- [ ] UI components and graphical objects meet 3:1 contrast against adjacent colors.
|
|
127
|
+
- [ ] Information is not conveyed by color alone (icons, patterns, labels also clarify).
|
|
128
|
+
|
|
129
|
+
**Verdict:** PASS / WARN / FAIL
|
|
130
|
+
|
|
131
|
+
#### 3.5 Forms & Labels
|
|
132
|
+
|
|
133
|
+
- [ ] Every input has an associated `<label>` or `aria-labelledby`/`aria-label`.
|
|
134
|
+
- [ ] Required fields and errors are communicated programmatically and visually.
|
|
135
|
+
- [ ] Error messages are linked to inputs via `aria-describedby` or similar.
|
|
136
|
+
|
|
137
|
+
**Verdict:** PASS / WARN / FAIL
|
|
138
|
+
|
|
139
|
+
#### 3.6 Images & Media
|
|
140
|
+
|
|
141
|
+
- [ ] Decorative images have empty `alt=""`.
|
|
142
|
+
- [ ] Informative images have descriptive `alt` text.
|
|
143
|
+
- [ ] Complex images have extended descriptions.
|
|
144
|
+
- [ ] Captions and transcripts are provided for video/audio where applicable.
|
|
145
|
+
|
|
146
|
+
**Verdict:** PASS / WARN / FAIL
|
|
147
|
+
|
|
148
|
+
#### 3.7 Motion & Responsiveness
|
|
149
|
+
|
|
150
|
+
- [ ] Animations respect `prefers-reduced-motion`.
|
|
151
|
+
- [ ] Touch targets are at least 44 × 44 CSS pixels.
|
|
152
|
+
- [ ] Layouts remain usable at 200% zoom and on small viewports.
|
|
153
|
+
- [ ] Horizontal scrolling is avoided unless essential.
|
|
154
|
+
|
|
155
|
+
**Verdict:** PASS / WARN / FAIL
|
|
156
|
+
|
|
157
|
+
#### 3.8 Screen Reader Support
|
|
158
|
+
|
|
159
|
+
- [ ] Content order makes sense when linearized.
|
|
160
|
+
- [ ] Hidden content is intentionally hidden (`display: none`, `visibility: hidden`, `aria-hidden`) and does not hide focusable elements.
|
|
161
|
+
- [ ] Status messages and loading states are announced.
|
|
162
|
+
|
|
163
|
+
**Verdict:** PASS / WARN / FAIL
|
|
164
|
+
|
|
165
|
+
---
|
|
166
|
+
|
|
167
|
+
### Step 4: Produce Accessibility Audit Report
|
|
168
|
+
|
|
169
|
+
Summarize findings in a structured report:
|
|
170
|
+
|
|
171
|
+
```markdown
|
|
172
|
+
## ♿ Accessibility Audit Report
|
|
173
|
+
|
|
174
|
+
### Summary
|
|
175
|
+
| Check | Verdict | Notes |
|
|
176
|
+
|-------|---------|-------|
|
|
177
|
+
| Semantic Structure | PASS/WARN/FAIL | ... |
|
|
178
|
+
| Keyboard & Focus | PASS/WARN/FAIL | ... |
|
|
179
|
+
| ARIA | PASS/WARN/FAIL | ... |
|
|
180
|
+
| Color & Contrast | PASS/WARN/FAIL | ... |
|
|
181
|
+
| Forms & Labels | PASS/WARN/FAIL | ... |
|
|
182
|
+
| Images & Media | PASS/WARN/FAIL | ... |
|
|
183
|
+
| Motion & Responsiveness | PASS/WARN/FAIL | ... |
|
|
184
|
+
| Screen Reader Support | PASS/WARN/FAIL | ... |
|
|
185
|
+
|
|
186
|
+
**Overall:** ✅ PASS / ⚠️ PASS WITH WARNINGS / ❌ FAIL
|
|
187
|
+
|
|
188
|
+
### Issues Found
|
|
189
|
+
|
|
190
|
+
#### 🔴 Critical (blocks release)
|
|
191
|
+
1. **[Category]** File: `path/to/file` — Description. **Route to:** Engineer / Designer
|
|
192
|
+
|
|
193
|
+
#### 🟡 Warnings (should fix, can release with override)
|
|
194
|
+
1. **[Category]** File: `path/to/file` — Description. **Route to:** Engineer
|
|
195
|
+
|
|
196
|
+
#### 🟢 Notes (informational, no action required)
|
|
197
|
+
1. **[Category]** File: `path/to/file` — Description.
|
|
198
|
+
|
|
199
|
+
### Files Reviewed
|
|
200
|
+
- `file1` — Brief assessment
|
|
201
|
+
- `file2` — Brief assessment
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
---
|
|
205
|
+
|
|
206
|
+
### Step 5: Route Based on Verdict
|
|
207
|
+
|
|
208
|
+
**If overall is PASS or PASS WITH WARNINGS:**
|
|
209
|
+
|
|
210
|
+
Present navigation options and WAIT for user choice. NEVER proceed to another skill without explicit user confirmation:
|
|
211
|
+
|
|
212
|
+
```markdown
|
|
213
|
+
**What would you like to do?**
|
|
214
|
+
|
|
215
|
+
- **[R] Return to Reviewer** — Fold findings into general review
|
|
216
|
+
- **[E] Back to Engineer** — Fix warnings before review
|
|
217
|
+
- **[D] Back to Designer** — Design-level a11y problem
|
|
218
|
+
- **[O] Back to Orchestrator** — Adjust scope
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
**If overall is FAIL:**
|
|
222
|
+
|
|
223
|
+
Present navigation options and WAIT for user choice. NEVER proceed to another skill without explicit user confirmation:
|
|
224
|
+
|
|
225
|
+
```markdown
|
|
226
|
+
**What would you like to do?**
|
|
227
|
+
|
|
228
|
+
- **[E] Back to Engineer** — Fix critical issues (recommended)
|
|
229
|
+
- **[D] Back to Designer** — Design-level issue, needs re-analysis
|
|
230
|
+
- **[R] Return to Reviewer** — Proceed to general review with known a11y debt
|
|
231
|
+
- **[O] Back to Orchestrator** — Adjust scope or requirements
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
**Routing rules:**
|
|
235
|
+
- **NEVER route automatically.** Always present the navigation menu and WAIT for the user to choose the next skill.
|
|
236
|
+
- **Engineer** — For markup, CSS, focus management, ARIA, and component fixes.
|
|
237
|
+
- **Designer** — For color palette, layout, motion, or interaction model changes.
|
|
238
|
+
- **Reviewer** — To fold the audit into the broader quality gate.
|
|
239
|
+
- **Orchestrator** — For scope changes or requirement adjustments.
|
|
240
|
+
|
|
241
|
+
---
|
|
242
|
+
|
|
243
|
+
## RESPONSE RULES
|
|
244
|
+
|
|
245
|
+
- **Be specific.** "The login button fails 3:1 contrast against the background" is better than "contrast is bad."
|
|
246
|
+
- **Cite standards.** Reference WCAG 2.1 success criteria when applicable (e.g., 1.4.3 Contrast Minimum).
|
|
247
|
+
- **Prioritize by impact.** Blockers are issues that prevent users from completing core tasks.
|
|
248
|
+
- **Suggest remediation.** Give engineers concrete fixes, not just problem statements.
|
|
249
|
+
- **Reference the spec and design.** Audits must verify what the spec and design define.
|
|
250
|
+
- **When done, present navigation options** — Always show the menu with clear next steps.
|
|
251
|
+
|
|
252
|
+
---
|
|
253
|
+
|
|
254
|
+
## ANTI-PATTERNS
|
|
255
|
+
|
|
256
|
+
- ❌ Writing production code to fix an accessibility issue.
|
|
257
|
+
- ❌ Approving UI without inspecting the actual markup and styles.
|
|
258
|
+
- ❌ Reporting vague findings without file paths, WCAG criteria, or remediation steps.
|
|
259
|
+
- ❌ Ignoring keyboard navigation because "most users use a mouse."
|
|
260
|
+
- ❌ Relying solely on automated tools without manual inspection.
|
|
261
|
+
- ❌ Forgetting to check `prefers-reduced-motion` and responsive zoom behavior.
|
|
262
|
+
- ❌ Skipping ARIA review because the component "looks fine."
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# Accessibility Checklist
|
|
2
|
+
|
|
3
|
+
Reusable checklist for the Accessibility Auditor skill.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Semantic HTML
|
|
8
|
+
|
|
9
|
+
- [ ] Use native elements (`<button>`, `<a>`, `<input>`, `<label>`, `<select>`, `<textarea>`) before adding ARIA.
|
|
10
|
+
- [ ] Headings (`h1`–`h6`) form a logical outline; do not skip levels for font size.
|
|
11
|
+
- [ ] Landmarks (`<main>`, `<nav>`, `<aside>`, `<header>`, `<footer>`) are used once per role where appropriate.
|
|
12
|
+
- [ ] Lists use `<ul>`, `<ol>`, `<li>`; definition lists use `<dl>`, `<dt>`, `<dd>`.
|
|
13
|
+
- [ ] Tables use `<table>`, `<thead>`, `<tbody>`, `<th scope="...">` when the data is tabular.
|
|
14
|
+
|
|
15
|
+
## Keyboard & Focus
|
|
16
|
+
|
|
17
|
+
- [ ] All interactive controls are reachable with `Tab` and operable with `Enter`/`Space`/arrows.
|
|
18
|
+
- [ ] Focus order matches visual order.
|
|
19
|
+
- [ ] Focus indicators are visible and meet 3:1 contrast.
|
|
20
|
+
- [ ] Modals trap focus and restore focus to the triggering element on close.
|
|
21
|
+
- [ ] Drop-down menus close with `Esc` and arrow keys move between items.
|
|
22
|
+
- [ ] No keyboard traps.
|
|
23
|
+
|
|
24
|
+
## ARIA
|
|
25
|
+
|
|
26
|
+
- [ ] ARIA is used only when native semantics are insufficient.
|
|
27
|
+
- [ ] Required ARIA attributes are present (`aria-expanded`, `aria-controls`, `aria-labelledby`, etc.).
|
|
28
|
+
- [ ] `aria-live` regions announce status updates and errors.
|
|
29
|
+
- [ ] State changes are reflected in attributes, not just visual classes.
|
|
30
|
+
- [ ] Custom components expose name, role, and value.
|
|
31
|
+
|
|
32
|
+
## Color & Contrast
|
|
33
|
+
|
|
34
|
+
- [ ] Normal text ≥ 4.5:1 contrast against background (WCAG 2.1 AA).
|
|
35
|
+
- [ ] Large text (18 pt+ or 14 pt+ bold) ≥ 3:1 contrast.
|
|
36
|
+
- [ ] UI components and graphical objects ≥ 3:1 against adjacent colors.
|
|
37
|
+
- [ ] Information is not conveyed by color alone.
|
|
38
|
+
|
|
39
|
+
## Forms & Labels
|
|
40
|
+
|
|
41
|
+
- [ ] Every input has a visible and programmatically associated label.
|
|
42
|
+
- [ ] Required fields are indicated visually and via `aria-required` or `required`.
|
|
43
|
+
- [ ] Error messages are linked with `aria-describedby` or `aria-errormessage`.
|
|
44
|
+
- [ ] Input purpose is identifiable (`autocomplete` attributes where appropriate).
|
|
45
|
+
|
|
46
|
+
## Images & Media
|
|
47
|
+
|
|
48
|
+
- [ ] Decorative images use `alt=""`.
|
|
49
|
+
- [ ] Informative images have concise, descriptive `alt` text.
|
|
50
|
+
- [ ] Complex charts/images have a text alternative nearby.
|
|
51
|
+
- [ ] Video/audio has captions, transcripts, and audio descriptions where required.
|
|
52
|
+
|
|
53
|
+
## Motion & Responsiveness
|
|
54
|
+
|
|
55
|
+
- [ ] Animations respect `prefers-reduced-motion: reduce`.
|
|
56
|
+
- [ ] Touch targets are at least 44 × 44 CSS pixels.
|
|
57
|
+
- [ ] Layout is usable at 200% browser zoom.
|
|
58
|
+
- [ ] Content does not require horizontal scrolling at 320 CSS pixels width.
|
|
59
|
+
- [ ] Viewport meta tag is present and does not disable zoom.
|
|
60
|
+
|
|
61
|
+
## Screen Reader Support
|
|
62
|
+
|
|
63
|
+
- [ ] Reading order is logical when linearized.
|
|
64
|
+
- [ ] Hidden content does not contain focusable elements.
|
|
65
|
+
- [ ] Loading and status changes are announced via live regions.
|
|
66
|
+
- [ ] Icons used alone have accessible names.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: architect
|
|
3
|
-
description: "Software architecture and
|
|
3
|
+
description: "Software architecture and spec-writing skill. ALWAYS use as the first step after orchestrator. Creates specs in specs/ for every change. Trigger after orchestrator briefs or on analyze, design, architecture, plan, spec, refactor plan, system design, create specs, proceed to architect."
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Architect — Design & Analysis Mode
|
package/skills/designer/SKILL.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: designer
|
|
3
|
-
description: UI/UX design skill
|
|
3
|
+
description: UI/UX design skill for bold, production-grade interfaces. Use when the user asks to design, build, create, or improve any frontend interface, page, component, or visual experience. Trigger on 'design', 'frontend', 'interface', 'component', 'landing page', 'dashboard', or 'redesign'.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Designer — Bold UI/UX Design
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: docs-writer
|
|
3
|
-
description: Write or rewrite project documentation tailored to
|
|
3
|
+
description: Write or rewrite project documentation tailored to type and audience. Use for READMEs, module/feature/capability docs, or any project docs, and when orchestrator routes pure documentation tasks without code changes. Invoked by orchestrator only; route to architect if context is missing.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Docs-Writer — Documentation Authoring
|
package/skills/engineer/SKILL.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: engineer
|
|
3
|
-
description: Software implementation and coding skill.
|
|
3
|
+
description: Software implementation and coding skill. Trigger on code, features, bug fixes, tests, or hands-on programming; after orchestrator/architect handoff; or on 'build', 'implement', 'code', 'fix this bug'. Only this skill may write implementation code; never for architecture or analysis.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Engineer — Build & Implementation Mode
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: maintainer
|
|
3
|
-
description: Use this skill
|
|
3
|
+
description: Use this skill for bug triage, technical debt, dependency updates, refactoring, production incidents, and codebase upkeep. Trigger for flaky tests, outdated libraries, performance degradation, or recurring issues — even if the user does not say "maintainer".
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Maintainer — Upkeep, Debt & Incident Triage
|
|
@@ -25,6 +25,27 @@ You do NOT write production fixes. You do NOT run git operations. You produce cl
|
|
|
25
25
|
|
|
26
26
|
---
|
|
27
27
|
|
|
28
|
+
## MEMORY & CONTEXT
|
|
29
|
+
|
|
30
|
+
**Always invoke the `obsidian-second-brain` skill via the `Skill` tool.**
|
|
31
|
+
Never read or write files inside `~/.lea` directly with `Read`, `Edit`, `Write`, or `Bash`.
|
|
32
|
+
|
|
33
|
+
At the start of the task, the `obsidian-second-brain` skill will search and read the relevant layers for this role.
|
|
34
|
+
At the end of the task, it will persist outcomes to the correct layers.
|
|
35
|
+
|
|
36
|
+
This skill's targets:
|
|
37
|
+
- **Read at start:** prior incidents, debt decisions, and runbooks
|
|
38
|
+
- **Persist at end:** incident/debt notes to journal; runbooks to knowledge; active context to curated memory
|
|
39
|
+
|
|
40
|
+
### MCP Tools Reference
|
|
41
|
+
|
|
42
|
+
| Tool | When to use |
|
|
43
|
+
|------|-------------|
|
|
44
|
+
| `search_notes` | Find prior runbooks and debt decisions in `Knowledge/` and incidents in `Journal/incidents*`. |
|
|
45
|
+
| `learn_from_text` | Persist a root-cause analysis or maintenance decision. |
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
28
49
|
## WORKFLOW
|
|
29
50
|
|
|
30
51
|
### Step 1: Gather Evidence
|
|
@@ -72,27 +93,6 @@ Propose a concrete next step:
|
|
|
72
93
|
|
|
73
94
|
---
|
|
74
95
|
|
|
75
|
-
## MEMORY & CONTEXT
|
|
76
|
-
|
|
77
|
-
**Always invoke the `obsidian-second-brain` skill via the `Skill` tool.**
|
|
78
|
-
Never read or write files inside `~/.lea` directly with `Read`, `Edit`, `Write`, or `Bash`.
|
|
79
|
-
|
|
80
|
-
At the start of the task, the `obsidian-second-brain` skill will search and read the relevant layers for this role.
|
|
81
|
-
At the end of the task, it will persist outcomes to the correct layers.
|
|
82
|
-
|
|
83
|
-
This skill's targets:
|
|
84
|
-
- **Read at start:** prior incidents, debt decisions, and runbooks
|
|
85
|
-
- **Persist at end:** incident/debt notes to journal; runbooks to knowledge; active context to curated memory
|
|
86
|
-
|
|
87
|
-
### MCP Tools Reference
|
|
88
|
-
|
|
89
|
-
| Tool | When to use |
|
|
90
|
-
|------|-------------|
|
|
91
|
-
| `search_notes` | Find prior runbooks and debt decisions in `Knowledge/` and incidents in `Journal/incidents*`. |
|
|
92
|
-
| `learn_from_text` | Persist a root-cause analysis or maintenance decision. |
|
|
93
|
-
|
|
94
|
-
---
|
|
95
|
-
|
|
96
96
|
**What would you like to do?**
|
|
97
97
|
|
|
98
98
|
- **[O] Return to Orchestrator** — Main task routing
|