@neyugn/agent-kits 0.3.5 → 0.3.6

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.
@@ -20,7 +20,7 @@ AGT-Kit is a portable, modular AI agent system consisting of:
20
20
 
21
21
  ### Modular Skill Loading
22
22
 
23
- Agent activated → Check frontmatter `skills:`Read SKILL.md → Apply.
23
+ Agent activated → Check ARCHITECTURE.md for assigned skills → Use `skill` tool to load each → Apply.
24
24
 
25
25
  - **Priority:** P0 (AGENTS.md) > P1 (Agent.md) > P2 (SKILL.md). All binding.
26
26
  - **Enforcement:** Never skip reading. "Read → Understand → Apply" mandatory.
@@ -101,12 +101,12 @@ Agent activated → Check frontmatter `skills:` → Read SKILL.md → Apply.
101
101
 
102
102
  ### Routing Checklist
103
103
 
104
- | Step | Check | If Unchecked |
105
- | ---- | ------------------------------- | --------------------------------- |
106
- | 1 | Correct agent identified? | → Analyze domain |
107
- | 2 | Read agent's .md file? | → Open `.agent/agents/{agent}.md` |
108
- | 3 | Announced @agent? | → Add announcement |
109
- | 4 | Loaded skills from frontmatter? | → Check `skills:` field |
104
+ | Step | Check | If Unchecked |
105
+ | ---- | --------------------------------- | ------------------------------------------- |
106
+ | 1 | Correct agent identified? | → Analyze domain |
107
+ | 2 | Read agent's .md file? | → Open `.agent/agents/{agent}.md` |
108
+ | 3 | Announced @agent? | → Add announcement |
109
+ | 4 | Loaded skills from ARCHITECTURE? | → Check ARCHITECTURE.md agent-skills table |
110
110
 
111
111
  ❌ Code without agent = PROTOCOL VIOLATION
112
112
  ❌ Skip announcement = USER CANNOT VERIFY
@@ -129,7 +129,24 @@ Agent activated → Check frontmatter `skills:` → Read SKILL.md → Apply.
129
129
  ## 🛠️ SKILL LOADING PROTOCOL
130
130
 
131
131
  ```
132
- User Request → Check ProfileSkill Description MatchLoad SKILL.md → Apply
132
+ Agent activated → Check ARCHITECTURE.md for assigned skills Use `skill` toolApply SKILL.md content
133
+ ```
134
+
135
+ ### How Skills Work in OpenCode
136
+
137
+ OpenCode has a **native `skill` tool** that automatically discovers all available skills.
138
+
139
+ 1. **Discovery**: OpenCode scans `.agent/skills/*/SKILL.md` and lists them in the `skill` tool description
140
+ 2. **Selection**: Check ARCHITECTURE.md → find your agent → note the "Skills Used" column
141
+ 3. **Loading**: Call `skill({ name: "skill-name" })` to load each assigned skill
142
+ 4. **Apply**: Follow the loaded SKILL.md instructions
143
+
144
+ ```
145
+ # Example: frontend-specialist activated
146
+ 1. Read ARCHITECTURE.md → Skills: clean-code, react-patterns, typescript-patterns, ...
147
+ 2. Call skill({ name: "clean-code" }) → Read content
148
+ 3. Call skill({ name: "react-patterns" }) → Read content
149
+ 4. Apply all loaded skill rules to the task
133
150
  ```
134
151
 
135
152
  ### Profile-Aware Loading
@@ -148,6 +165,31 @@ User Request → Check Profile → Skill Description Match → Load SKILL.md →
148
165
  - Behave as if no filtering is applied
149
166
  ```
150
167
 
168
+ ### Skill Permissions (OpenCode)
169
+
170
+ Control skill access in `opencode.json`:
171
+
172
+ ```json
173
+ {
174
+ "permission": {
175
+ "skill": {
176
+ "*": "allow",
177
+ "internal-*": "deny"
178
+ }
179
+ }
180
+ }
181
+ ```
182
+
183
+ Or per-agent in agent frontmatter:
184
+
185
+ ```yaml
186
+ ---
187
+ permission:
188
+ skill:
189
+ "documents-*": "allow"
190
+ ---
191
+ ```
192
+
151
193
  ### Core Skills (Always Available)
152
194
 
153
195
  These skills are NEVER disabled regardless of profile:
@@ -90,6 +90,46 @@ def parse_frontmatter(file_path: Path) -> Dict[str, Any]:
90
90
  return {}
91
91
 
92
92
 
93
+ def parse_skills_from_architecture(agent_dir: Path) -> Dict[str, List[str]]:
94
+ """Parse agent-skill mappings from ARCHITECTURE.md tables.
95
+
96
+ Falls back to this when agents don't have skills in frontmatter
97
+ (e.g., OpenCode strips the skills field during transformation).
98
+ """
99
+ arch_file = agent_dir / "ARCHITECTURE.md"
100
+ if not arch_file.exists():
101
+ return {}
102
+
103
+ mapping = {}
104
+ try:
105
+ content = arch_file.read_text()
106
+ # Match table rows: | `agent-name` | description | skills-list |
107
+ # Pattern: | `name` | ... | skill1, skill2, ... |
108
+ for line in content.split('\n'):
109
+ if not line.startswith('|'):
110
+ continue
111
+ cells = [c.strip() for c in line.split('|')]
112
+ # Filter out empty cells from leading/trailing pipes
113
+ cells = [c for c in cells if c]
114
+ if len(cells) >= 3:
115
+ # First cell might be agent name wrapped in backticks
116
+ agent_name = cells[0].strip('`').strip()
117
+ # Last cell contains skills
118
+ skills_str = cells[-1]
119
+ # Skip header/separator rows
120
+ if agent_name.startswith('-') or agent_name == 'Agent':
121
+ continue
122
+ # Parse comma-separated skills
123
+ skills = [s.strip().strip('`') for s in skills_str.split(',') if s.strip()]
124
+ # Only add if skills look valid (not header text)
125
+ if skills and not any(s in ['Skills Used', '---', 'Skills'] for s in skills):
126
+ mapping[agent_name] = skills
127
+ except Exception:
128
+ pass
129
+
130
+ return mapping
131
+
132
+
93
133
  def get_agents(agent_dir: Path) -> List[Dict[str, Any]]:
94
134
  """Get list of agents with their metadata."""
95
135
  agents = []
@@ -98,10 +138,12 @@ def get_agents(agent_dir: Path) -> List[Dict[str, Any]]:
98
138
  if not agents_path.exists():
99
139
  return agents
100
140
 
141
+ # Parse agent-skill mappings from ARCHITECTURE.md (single source of truth)
142
+ arch_skills = parse_skills_from_architecture(agent_dir)
143
+
101
144
  for agent_file in agents_path.glob("*.md"):
102
145
  frontmatter = parse_frontmatter(agent_file)
103
- skills = frontmatter.get("skills", "").replace("[", "").replace("]", "").split(",")
104
- skills = [s.strip() for s in skills if s.strip()]
146
+ skills = arch_skills.get(agent_file.stem, [])
105
147
 
106
148
  agents.append({
107
149
  "name": agent_file.stem,
@@ -146,22 +188,33 @@ def get_skills(agent_dir: Path) -> List[Dict[str, Any]]:
146
188
 
147
189
 
148
190
  def get_workflows(agent_dir: Path) -> List[Dict[str, Any]]:
149
- """Get list of workflows."""
150
- workflows = []
151
- workflows_path = agent_dir / "workflows"
191
+ """Get list of workflows/commands.
152
192
 
153
- if not workflows_path.exists():
154
- return workflows
193
+ Checks both 'workflows/' (Cursor, Gemini) and 'commands/' (OpenCode)
194
+ directories since different AI tools use different naming.
195
+ """
196
+ workflows = []
155
197
 
156
- for workflow_file in workflows_path.glob("*.md"):
157
- frontmatter = parse_frontmatter(workflow_file)
198
+ # Check both possible directories
199
+ for dir_name in ["workflows", "commands"]:
200
+ workflows_path = agent_dir / dir_name
158
201
 
159
- workflows.append({
160
- "name": workflow_file.stem,
161
- "command": f"/{workflow_file.stem}",
162
- "file": str(workflow_file.relative_to(agent_dir)),
163
- "description": frontmatter.get("description", ""),
164
- })
202
+ if not workflows_path.exists():
203
+ continue
204
+
205
+ for workflow_file in workflows_path.glob("*.md"):
206
+ frontmatter = parse_frontmatter(workflow_file)
207
+
208
+ workflows.append({
209
+ "name": workflow_file.stem,
210
+ "command": f"/{workflow_file.stem}",
211
+ "file": str(workflow_file.relative_to(agent_dir)),
212
+ "description": frontmatter.get("description", ""),
213
+ })
214
+
215
+ # Only use the first directory found (don't double-count)
216
+ if workflows:
217
+ break
165
218
 
166
219
  return sorted(workflows, key=lambda x: x["name"])
167
220
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neyugn/agent-kits",
3
- "version": "0.3.5",
3
+ "version": "0.3.6",
4
4
  "description": "Universal AI Agent Toolkit - Skills, Agents, and Workflows for any AI coding assistant",
5
5
  "type": "module",
6
6
  "bin": {