Skip to content

Commit 4917886

Browse files
committed
Revert "remove skills"
This reverts commit d019de0.
1 parent d019de0 commit 4917886

7 files changed

Lines changed: 634 additions & 0 deletions

File tree

skills/mcaf-memory/SKILL.md

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
---
2+
name: mcaf-memory
3+
description: "Maintain a persistent project memory file (`references/memory.md` in this skill folder) with stable facts, constraints, preferences, and glossary so agents don’t re-learn the same context every chat. Use when the user says “remember”, gives repeating feedback, or when important project constraints are discovered."
4+
compatibility: "Requires repository write access; stores memory in Markdown."
5+
---
6+
7+
# MCAF: Memory
8+
9+
## Output
10+
11+
- `references/memory.md` (create/update)
12+
13+
## When to use (triggers)
14+
15+
- The user says “remember / keep in mind / from now on”.
16+
- Repeated feedback about agent behaviour or workflow.
17+
- You discover stable project constraints (stack versions, environments, forbidden changes).
18+
- You need a shared glossary (“what does X mean in this project?”).
19+
20+
## Workflow
21+
22+
1. Open `references/memory.md`.
23+
2. Keep the “7 Questions (profile)” up to date:
24+
- fill the answers as short bullets
25+
- these answers must reflect the real workflow (TDD, docs, release discipline)
26+
3. Add/update only **stable** information:
27+
- code taste & standards (readability, naming, patterns, “never do this”)
28+
- testing taste & standards (TDD policy, levels, assertions, flakiness policy)
29+
- documentation taste & standards (ADR/feature docs expectations, diagrams)
30+
- architecture principles/constraints (boundaries, dependency rules)
31+
- glossary (project-specific terms)
32+
4. Keep it short and useful:
33+
- bullets, not essays
34+
- remove outdated entries when superseded
35+
- don’t duplicate the same rule in multiple places
36+
5. Decide the right home for information:
37+
- **Rules/commands for agents**`AGENTS.md`
38+
- **Architecture decisions**`docs/ADR/*`
39+
- **Behaviour/flows**`docs/Features/*`
40+
- **Long-lived context & preferences**`references/memory.md`
41+
6. Never store secrets or personal data.
42+
43+
## Guardrails
44+
45+
- If it changes often or is controversial → it probably belongs in a Feature doc or ADR, not memory.
46+
- Memory is not a dumping ground. If it grows, refactor into real docs and keep memory as an index.
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
# Project Memory (MCAF)
2+
3+
> Purpose: store **stable, repeatable context** about how we build software in this repo (taste, standards, constraints).
4+
> Keep it short and useful. No secrets. No one-off task instructions.
5+
6+
## The 7 Questions (profile)
7+
8+
Answer these once and keep them current. This is the fastest way for a new agent/engineer to “get” how you work.
9+
10+
1. **What are we building and for whom?** (product + users + non-goals)
11+
- Answer:
12+
2. **What is our quality bar?** (what cannot ship)
13+
- Answer:
14+
3. **How do we make decisions?** (ADRs, who decides, when)
15+
- Answer:
16+
4. **How do we design?** (architecture principles, boundaries, patterns)
17+
- Answer:
18+
5. **How do we build?** (workflow, branching, reviews, release discipline)
19+
- Answer:
20+
6. **How do we test?** (TDD/BDD, levels, what to mock, what never to mock)
21+
- Answer:
22+
7. **What does “done” mean here?** (Definition of Done, docs, verification evidence)
23+
- Answer:
24+
25+
## Code (taste + standards)
26+
27+
- Readability rules:
28+
- Naming rules:
29+
- Error handling rules:
30+
- Performance rules:
31+
- “Never do this” list:
32+
33+
## Testing (taste + standards)
34+
35+
- TDD policy (when mandatory / exceptions):
36+
- Test levels policy (integration/API/UI vs unit):
37+
- Assertions policy (what a “meaningful test” means here):
38+
- Flakiness policy (what to do when tests are flaky):
39+
40+
## Documentation (taste + standards)
41+
42+
- Doc tone (what “good” looks like here):
43+
- ADR policy (what requires an ADR, how strict):
44+
- Feature doc policy (when required, what must be included):
45+
- Diagram policy (Mermaid expectations):
46+
47+
## Architecture (principles + constraints)
48+
49+
- Boundaries and layering rules:
50+
- Dependency rules:
51+
- Cross-cutting patterns (logging, config, errors, auth, caching):
52+
53+
## Patterns (approved defaults)
54+
55+
- API style:
56+
- Data model style:
57+
- Concurrency / idempotency:
58+
- Observability:
59+
60+
## Ideas (explicitly tentative)
61+
62+
> Keep this list short. Promote ideas to ADRs/features when they become real.
63+
64+
- TODO
65+
66+
## Glossary (project-specific terms)
67+
68+
- TODO
69+
70+
## Out of scope
71+
72+
- Secrets, keys, tokens, credentials.
73+
- One-off task instructions (“just for this PR”).
74+
- Architecture decisions (use `docs/ADR/*`).
75+
- Feature behaviour and flows (use `docs/Features/*`).
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
---
2+
name: mcaf-skill-curation
3+
description: "Create, update, and validate repository skills under your agent’s skills directory (Codex: `.codex/skills/`, Claude Code: `.claude/skills/`) so they match the real codebase and `AGENTS.md` rules. Tune YAML `description` triggers, apply user feedback to skills, and generate `<available_skills>` metadata blocks for agent runtimes."
4+
compatibility: "Requires repository write access; uses a shell and Python 3 for validation/scripts."
5+
---
6+
7+
# MCAF: Skill Curation
8+
9+
## Outputs
10+
11+
- New/updated skill folders under your agent’s skills directory with valid `SKILL.md` frontmatter and lean workflows
12+
- Updated YAML `description` triggers for correct matching
13+
- A generated `<available_skills>` block (metadata only) for your agent runtime
14+
- A validation report (script output)
15+
16+
## Workflow
17+
18+
1. Read the repo’s sources of truth:
19+
- `AGENTS.md` (commands + hard rules)
20+
- `docs/Architecture/Overview.md` (modules/boundaries) if present
21+
2. Inventory skills and identify drift:
22+
- list all `*/SKILL.md` under your skills directory (Codex: `.codex/skills/*/SKILL.md`, Claude Code: `.claude/skills/*/SKILL.md`)
23+
- verify folder name == YAML `name`
24+
- look for “template cruft” inside skill folders (README/INSTALL/CHANGELOG) and remove it
25+
3. Update skills to match reality (no guessing):
26+
- ensure each skill references **real** commands (from `AGENTS.md`)
27+
- ensure each skill’s YAML `description` includes trigger keywords that match how users ask for the task
28+
- ensure workflows reference real modules/boundaries (from architecture overview)
29+
4. Create new skills for repeated/fragile workflows:
30+
- keep one workflow per skill (split mega-skills)
31+
- copy the closest existing skill and adapt (folder/name, triggers, outputs, workflow)
32+
- use `scripts/` for deterministic or fragile steps
33+
- use `references/` only for copy/paste templates and structured checklists (avoid “reading lists”)
34+
5. Validate skills (fix errors before shipping):
35+
- run the bundled validator from the repo root:
36+
- `python3 <skills-dir>/mcaf-skill-curation/scripts/validate_skills.py <skills-dir>`
37+
- Codex: `python3 .codex/skills/mcaf-skill-curation/scripts/validate_skills.py .codex/skills`
38+
- Claude: `python3 .claude/skills/mcaf-skill-curation/scripts/validate_skills.py .claude/skills`
39+
6. Generate a metadata-only skills block for your agent runtime:
40+
- run the bundled generator from the repo root:
41+
- `python3 <skills-dir>/mcaf-skill-curation/scripts/generate_available_skills.py <skills-dir> --absolute`
42+
- Codex: `python3 .codex/skills/mcaf-skill-curation/scripts/generate_available_skills.py .codex/skills --absolute`
43+
- Claude: `python3 .claude/skills/mcaf-skill-curation/scripts/generate_available_skills.py .claude/skills --absolute`
44+
- paste the output into your agent configuration (system/developer prompt)
45+
7. When user feedback is about skills:
46+
- update the relevant `<skills-dir>/<skill-name>/SKILL.md` (especially YAML `description` triggers) so the fix is permanent
47+
48+
## Bundled scripts
49+
50+
- `scripts/validate_skills.py` — validates frontmatter + folder/name rules and flags common spec violations.
51+
- `scripts/generate_available_skills.py` — prints an `<available_skills>` XML block from your skills directory `*/SKILL.md` metadata.
52+
53+
## Guardrails
54+
55+
- Don’t turn a skill into a wiki: keep `SKILL.md` procedural and short.
56+
- Don’t add extra docs inside skill folders (`README.md`, `INSTALLATION_GUIDE.md`, `CHANGELOG.md`, etc.).
57+
- Prefer updating triggers (`description`) over adding more and more body text.
58+
- YAML is strict: if a value contains `:` or other YAML-significant characters, wrap it in quotes.
59+
60+
## Examples (trigger phrases)
61+
62+
- "update our skills to match the repo"
63+
- "this skill triggers wrong, fix the description"
64+
- "generate available_skills block"
65+
- "validate skills frontmatter"
Binary file not shown.
Binary file not shown.
Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
#!/usr/bin/env python3
2+
3+
from __future__ import annotations
4+
5+
import argparse
6+
import html
7+
import re
8+
import sys
9+
from dataclasses import dataclass
10+
from pathlib import Path
11+
12+
try:
13+
import yaml # type: ignore
14+
except Exception: # pragma: no cover - optional dependency
15+
yaml = None
16+
17+
18+
@dataclass(frozen=True)
19+
class SkillMetadata:
20+
name: str
21+
description: str
22+
skill_md: Path
23+
24+
25+
class SkillMetadataError(Exception):
26+
pass
27+
28+
29+
def _parse_frontmatter(skill_md: Path) -> tuple[str, str]:
30+
text = skill_md.read_text(encoding="utf-8")
31+
lines = text.splitlines()
32+
if not lines or lines[0].strip() != "---":
33+
raise SkillMetadataError("Missing YAML frontmatter start ('---') at line 1.")
34+
35+
end_index = None
36+
for i in range(1, len(lines)):
37+
if lines[i].strip() == "---":
38+
end_index = i
39+
break
40+
if end_index is None:
41+
raise SkillMetadataError("Missing YAML frontmatter end ('---').")
42+
43+
frontmatter_text = "\n".join(lines[1:end_index]).strip()
44+
45+
if yaml is not None:
46+
try:
47+
data = yaml.safe_load(frontmatter_text) or {}
48+
except Exception as e:
49+
raise SkillMetadataError(f"Invalid YAML frontmatter: {e}") from e
50+
51+
if not isinstance(data, dict):
52+
raise SkillMetadataError("YAML frontmatter must be a mapping (key/value object).")
53+
54+
name_value = data.get("name")
55+
description_value = data.get("description")
56+
57+
if not isinstance(name_value, str) or not name_value.strip():
58+
raise SkillMetadataError("Missing YAML 'name'.")
59+
if not isinstance(description_value, str) or not description_value.strip():
60+
raise SkillMetadataError("Missing YAML 'description'.")
61+
62+
if "\n" in name_value or "\r" in name_value:
63+
raise SkillMetadataError("YAML 'name' must be a single line.")
64+
if "\n" in description_value or "\r" in description_value:
65+
raise SkillMetadataError("YAML 'description' must be a single line.")
66+
67+
return name_value, description_value
68+
69+
# Fallback parser (no PyYAML installed): only supports single-line `key: value` scalars.
70+
name_value: str | None = None
71+
description_value: str | None = None
72+
73+
for raw in lines[1:end_index]:
74+
line = raw.strip()
75+
if not line or line.startswith("#"):
76+
continue
77+
78+
match = re.match(r"^([A-Za-z0-9_-]+):\s*(.*)$", line)
79+
if not match:
80+
continue
81+
key, value = match.group(1), match.group(2).strip()
82+
83+
if value in {"|", ">", "|-", ">-"}:
84+
raise SkillMetadataError(
85+
f"Unsupported multi-line YAML value for '{key}'. Use a single-line scalar."
86+
)
87+
88+
if len(value) >= 2 and value[0] in {"'", '"'} and value[-1] == value[0]:
89+
value = value[1:-1]
90+
91+
if key == "name":
92+
name_value = value
93+
elif key == "description":
94+
description_value = value
95+
96+
if not name_value:
97+
raise SkillMetadataError("Missing YAML 'name'.")
98+
if not description_value:
99+
raise SkillMetadataError("Missing YAML 'description'.")
100+
101+
return name_value, description_value
102+
103+
104+
def _collect_skills(skills_root: Path) -> list[SkillMetadata]:
105+
skills: list[SkillMetadata] = []
106+
for child in sorted(skills_root.iterdir()):
107+
if not child.is_dir():
108+
continue
109+
skill_md = child / "SKILL.md"
110+
if not skill_md.exists():
111+
continue
112+
name, description = _parse_frontmatter(skill_md)
113+
skills.append(SkillMetadata(name=name, description=description, skill_md=skill_md))
114+
return sorted(skills, key=lambda s: s.name)
115+
116+
117+
def _detect_skills_dir() -> str:
118+
candidates = (Path(".codex/skills"), Path(".claude/skills"), Path("skills"))
119+
120+
def contains_skills(dir_path: Path) -> bool:
121+
if not dir_path.exists() or not dir_path.is_dir():
122+
return False
123+
for child in dir_path.iterdir():
124+
if not child.is_dir():
125+
continue
126+
if (child / "SKILL.md").is_file():
127+
return True
128+
return False
129+
130+
for candidate in candidates:
131+
if contains_skills(candidate):
132+
return str(candidate)
133+
134+
for candidate in candidates:
135+
if candidate.exists() and candidate.is_dir():
136+
return str(candidate)
137+
138+
return "skills"
139+
140+
141+
def main() -> int:
142+
parser = argparse.ArgumentParser(
143+
description="Generate an <available_skills> XML block from */SKILL.md metadata under a skills directory."
144+
)
145+
parser.add_argument(
146+
"skills_dir",
147+
nargs="?",
148+
default=_detect_skills_dir(),
149+
help="Path to skills directory (default: auto-detect .codex/skills, .claude/skills, or ./skills).",
150+
)
151+
parser.add_argument(
152+
"--absolute",
153+
action="store_true",
154+
help="Use absolute paths in <location> (recommended for filesystem-based agents).",
155+
)
156+
args = parser.parse_args()
157+
158+
skills_root = Path(args.skills_dir).resolve()
159+
if not skills_root.exists() or not skills_root.is_dir():
160+
print(f"ERROR: Skills directory does not exist: {skills_root}", file=sys.stderr)
161+
return 2
162+
163+
try:
164+
skills = _collect_skills(skills_root)
165+
except SkillMetadataError as e:
166+
print(f"ERROR: {e}", file=sys.stderr)
167+
return 2
168+
169+
if not skills:
170+
print(f"ERROR: No skills found under: {skills_root}", file=sys.stderr)
171+
return 2
172+
173+
print("<available_skills>")
174+
for skill in skills:
175+
location = str(skill.skill_md.resolve() if args.absolute else skill.skill_md.relative_to(Path.cwd()))
176+
print(" <skill>")
177+
print(f" <name>{html.escape(skill.name)}</name>")
178+
print(f" <description>{html.escape(skill.description)}</description>")
179+
print(f" <location>{html.escape(location)}</location>")
180+
print(" </skill>")
181+
print("</available_skills>")
182+
183+
return 0
184+
185+
186+
if __name__ == "__main__":
187+
raise SystemExit(main())

0 commit comments

Comments
 (0)