@flydocs/cli 0.6.0-alpha.2 → 0.6.0-alpha.20

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 (148) hide show
  1. package/dist/cli.js +678 -392
  2. package/package.json +1 -1
  3. package/template/.claude/CLAUDE.md +62 -63
  4. package/template/.claude/agents/implementation-agent.md +1 -1
  5. package/template/.claude/agents/pm-agent.md +1 -1
  6. package/template/.claude/commands/activate.md +1 -1
  7. package/template/.claude/commands/attach.md +1 -1
  8. package/template/.claude/commands/block.md +2 -2
  9. package/template/.claude/commands/capture.md +1 -1
  10. package/template/.claude/commands/close.md +1 -1
  11. package/template/.claude/commands/flydocs-setup.md +387 -74
  12. package/template/.claude/commands/flydocs-upgrade.md +48 -37
  13. package/template/.claude/commands/implement.md +1 -1
  14. package/template/.claude/commands/knowledge.md +61 -0
  15. package/template/.claude/commands/new-project.md +1 -1
  16. package/template/.claude/commands/onboard.md +275 -0
  17. package/template/.claude/commands/project-update.md +1 -1
  18. package/template/.claude/commands/refine.md +1 -1
  19. package/template/.claude/commands/review.md +1 -1
  20. package/template/.claude/commands/start-session.md +1 -1
  21. package/template/.claude/commands/status.md +1 -1
  22. package/template/.claude/commands/validate.md +1 -1
  23. package/template/.claude/commands/wrap-session.md +1 -1
  24. package/template/.claude/hooks/auto-approve.py +132 -0
  25. package/template/.claude/hooks/post-pr-check.py +108 -0
  26. package/template/.claude/hooks/post-transition-check.py +94 -0
  27. package/template/.claude/hooks/prompt-submit.py +513 -0
  28. package/template/.claude/hooks/session-start.py +146 -0
  29. package/template/.claude/hooks/stop-gate.py +109 -0
  30. package/template/.claude/settings.json +41 -4
  31. package/template/.claude/skills/README.md +23 -25
  32. package/template/.claude/skills/flydocs-workflow/SKILL.md +134 -42
  33. package/template/.claude/skills/flydocs-workflow/cursor-rule.mdc +9 -8
  34. package/template/.claude/skills/flydocs-workflow/reference/comment-templates.md +1 -0
  35. package/template/.claude/skills/flydocs-workflow/reference/golden-rules.md +28 -17
  36. package/template/.claude/skills/flydocs-workflow/reference/graph-schema.md +116 -0
  37. package/template/.claude/skills/flydocs-workflow/reference/pr-workflow.md +120 -0
  38. package/template/.claude/skills/flydocs-workflow/reference/priority-estimates.md +37 -15
  39. package/template/.claude/skills/flydocs-workflow/reference/service-descriptor-schema.md +251 -0
  40. package/template/.claude/skills/flydocs-workflow/reference/status-workflow.md +26 -26
  41. package/template/.claude/skills/flydocs-workflow/scripts/_local/__init__.py +0 -0
  42. package/template/.claude/skills/{flydocs-local/scripts/flydocs_api.py → flydocs-workflow/scripts/_local/file_store.py} +137 -47
  43. package/template/.claude/skills/flydocs-workflow/scripts/flydocs_api.py +693 -0
  44. package/template/{.flydocs → .claude/skills/flydocs-workflow}/scripts/generate_manifest.py +4 -4
  45. package/template/.claude/skills/{flydocs-context-graph → flydocs-workflow}/scripts/graph_build.py +132 -1
  46. package/template/.claude/skills/{flydocs-context-graph → flydocs-workflow}/scripts/graph_query.py +18 -5
  47. package/template/.claude/skills/{flydocs-context-graph → flydocs-workflow}/scripts/graph_session.py +1 -10
  48. package/template/.claude/skills/{flydocs-context-graph → flydocs-workflow}/scripts/graph_update.py +4 -4
  49. package/template/.claude/skills/{flydocs-context-graph → flydocs-workflow}/scripts/graph_utils.py +2 -1
  50. package/template/.claude/skills/flydocs-workflow/scripts/issues.py +489 -0
  51. package/template/.claude/skills/flydocs-workflow/scripts/projects.py +144 -0
  52. package/template/.claude/skills/flydocs-workflow/scripts/pull_services.py +128 -0
  53. package/template/.claude/skills/flydocs-workflow/scripts/push_service.py +132 -0
  54. package/template/.claude/skills/flydocs-workflow/scripts/session.py +54 -0
  55. package/template/.claude/skills/flydocs-workflow/scripts/workspace.py +860 -0
  56. package/template/.claude/skills/flydocs-workflow/session.md +63 -25
  57. package/template/.claude/skills/flydocs-workflow/stages/activate.md +18 -7
  58. package/template/.claude/skills/flydocs-workflow/stages/capture.md +10 -5
  59. package/template/.claude/skills/flydocs-workflow/stages/close.md +4 -3
  60. package/template/.claude/skills/flydocs-workflow/stages/implement.md +33 -9
  61. package/template/.claude/skills/flydocs-workflow/stages/refine.md +22 -6
  62. package/template/.claude/skills/flydocs-workflow/stages/review.md +16 -4
  63. package/template/.claude/skills/flydocs-workflow/stages/validate.md +3 -1
  64. package/template/.claude/skills/flydocs-workflow/templates/pr/default.md +33 -0
  65. package/template/.cursor/agents/implementation-agent.md +1 -1
  66. package/template/.cursor/agents/pm-agent.md +2 -2
  67. package/template/.cursor/hooks.json +10 -3
  68. package/template/.env.example +6 -6
  69. package/template/.flydocs/config.json +5 -18
  70. package/template/.flydocs/templates/README.md +13 -14
  71. package/template/.flydocs/templates/quick-capture.md +4 -8
  72. package/template/.flydocs/version +1 -1
  73. package/template/AGENTS.md +39 -32
  74. package/template/CHANGELOG.md +39 -0
  75. package/template/flydocs/README.md +1 -3
  76. package/template/flydocs/context/project.md +6 -3
  77. package/template/flydocs/design-system/README.md +3 -3
  78. package/template/flydocs/knowledge/INDEX.md +38 -53
  79. package/template/flydocs/knowledge/README.md +60 -9
  80. package/template/flydocs/knowledge/templates/decision.md +47 -0
  81. package/template/flydocs/knowledge/templates/feature.md +35 -0
  82. package/template/flydocs/knowledge/templates/note.md +25 -0
  83. package/template/manifest.json +24 -20
  84. package/template/.claude/skills/flydocs-cloud/SKILL.md +0 -111
  85. package/template/.claude/skills/flydocs-cloud/cursor-rule.mdc +0 -50
  86. package/template/.claude/skills/flydocs-cloud/scripts/assign.py +0 -22
  87. package/template/.claude/skills/flydocs-cloud/scripts/assign_cycle.py +0 -28
  88. package/template/.claude/skills/flydocs-cloud/scripts/assign_milestone.py +0 -22
  89. package/template/.claude/skills/flydocs-cloud/scripts/comment.py +0 -29
  90. package/template/.claude/skills/flydocs-cloud/scripts/create_issue.py +0 -63
  91. package/template/.claude/skills/flydocs-cloud/scripts/create_milestone.py +0 -35
  92. package/template/.claude/skills/flydocs-cloud/scripts/create_project.py +0 -33
  93. package/template/.claude/skills/flydocs-cloud/scripts/create_team.py +0 -39
  94. package/template/.claude/skills/flydocs-cloud/scripts/estimate.py +0 -29
  95. package/template/.claude/skills/flydocs-cloud/scripts/flydocs_api.py +0 -210
  96. package/template/.claude/skills/flydocs-cloud/scripts/get_issue.py +0 -24
  97. package/template/.claude/skills/flydocs-cloud/scripts/link.py +0 -28
  98. package/template/.claude/skills/flydocs-cloud/scripts/list_cycles.py +0 -28
  99. package/template/.claude/skills/flydocs-cloud/scripts/list_issues.py +0 -44
  100. package/template/.claude/skills/flydocs-cloud/scripts/list_labels.py +0 -19
  101. package/template/.claude/skills/flydocs-cloud/scripts/list_milestones.py +0 -28
  102. package/template/.claude/skills/flydocs-cloud/scripts/list_projects.py +0 -31
  103. package/template/.claude/skills/flydocs-cloud/scripts/list_teams.py +0 -19
  104. package/template/.claude/skills/flydocs-cloud/scripts/priority.py +0 -29
  105. package/template/.claude/skills/flydocs-cloud/scripts/project_update.py +0 -45
  106. package/template/.claude/skills/flydocs-cloud/scripts/set_labels.py +0 -68
  107. package/template/.claude/skills/flydocs-cloud/scripts/set_team.py +0 -41
  108. package/template/.claude/skills/flydocs-cloud/scripts/transition.py +0 -26
  109. package/template/.claude/skills/flydocs-cloud/scripts/update_description.py +0 -36
  110. package/template/.claude/skills/flydocs-cloud/scripts/update_issue.py +0 -82
  111. package/template/.claude/skills/flydocs-context-graph/SKILL.md +0 -87
  112. package/template/.claude/skills/flydocs-context-graph/schema.md +0 -78
  113. package/template/.claude/skills/flydocs-context-graph/scripts/graph_context.py +0 -338
  114. package/template/.claude/skills/flydocs-context7/SKILL.md +0 -105
  115. package/template/.claude/skills/flydocs-context7/cursor-rule.mdc +0 -49
  116. package/template/.claude/skills/flydocs-context7/scripts/context7.py +0 -293
  117. package/template/.claude/skills/flydocs-estimates/SKILL.md +0 -384
  118. package/template/.claude/skills/flydocs-figma/SKILL.md +0 -377
  119. package/template/.claude/skills/flydocs-figma/references/PROMPTING.md +0 -108
  120. package/template/.claude/skills/flydocs-figma/references/TROUBLESHOOTING.md +0 -112
  121. package/template/.claude/skills/flydocs-local/SKILL.md +0 -103
  122. package/template/.claude/skills/flydocs-local/cursor-rule.mdc +0 -43
  123. package/template/.claude/skills/flydocs-local/scripts/assign.py +0 -20
  124. package/template/.claude/skills/flydocs-local/scripts/comment.py +0 -27
  125. package/template/.claude/skills/flydocs-local/scripts/create_issue.py +0 -44
  126. package/template/.claude/skills/flydocs-local/scripts/estimate.py +0 -37
  127. package/template/.claude/skills/flydocs-local/scripts/get_issue.py +0 -20
  128. package/template/.claude/skills/flydocs-local/scripts/link.py +0 -41
  129. package/template/.claude/skills/flydocs-local/scripts/list_issues.py +0 -34
  130. package/template/.claude/skills/flydocs-local/scripts/priority.py +0 -37
  131. package/template/.claude/skills/flydocs-local/scripts/project_update.py +0 -67
  132. package/template/.claude/skills/flydocs-local/scripts/status_summary.py +0 -16
  133. package/template/.claude/skills/flydocs-local/scripts/transition.py +0 -24
  134. package/template/.claude/skills/flydocs-local/scripts/update_description.py +0 -35
  135. package/template/.claude/skills/flydocs-local/scripts/update_issue.py +0 -84
  136. package/template/.flydocs/hooks/auto-approve.py +0 -71
  137. package/template/.flydocs/hooks/prompt-submit.py +0 -277
  138. package/template/.flydocs/scripts/skill_manager.py +0 -541
  139. package/template/.flydocs/templates/bug.md +0 -166
  140. package/template/.flydocs/templates/chore.md +0 -110
  141. package/template/.flydocs/templates/feature.md +0 -173
  142. package/template/.flydocs/templates/idea.md +0 -122
  143. /package/template/{.flydocs → .claude}/hooks/post-edit.py +0 -0
  144. /package/template/.claude/skills/{flydocs-estimates/references → flydocs-workflow/reference}/provider-costs.md +0 -0
  145. /package/template/.claude/skills/flydocs-workflow/templates/{bug.md → issues/bug.md} +0 -0
  146. /package/template/.claude/skills/flydocs-workflow/templates/{chore.md → issues/chore.md} +0 -0
  147. /package/template/.claude/skills/flydocs-workflow/templates/{feature.md → issues/feature.md} +0 -0
  148. /package/template/.claude/skills/flydocs-workflow/templates/{idea.md → issues/idea.md} +0 -0
@@ -1,541 +0,0 @@
1
- #!/usr/bin/env python3
2
- """FlyDocs Skill Manager — search, add, remove, and list community skills.
3
-
4
- Usage:
5
- python3 skill_manager.py search <keyword>
6
- python3 skill_manager.py add <source>
7
- python3 skill_manager.py remove <name>
8
- python3 skill_manager.py list
9
-
10
- Source formats for add:
11
- Known name: react-best-practices
12
- Repo reference: vercel-labs/agent-skills:react-best-practices
13
- GitHub URL: https://github.com/owner/repo/tree/main/skills/name
14
- """
15
-
16
- import argparse
17
- import json
18
- import os
19
- import re
20
- import shutil
21
- import subprocess
22
- import sys
23
- import urllib.error
24
- import urllib.request
25
-
26
-
27
- RED = "\033[0;31m"
28
- GREEN = "\033[0;32m"
29
- BLUE = "\033[0;34m"
30
- YELLOW = "\033[1;33m"
31
- CYAN = "\033[0;36m"
32
- NC = "\033[0m"
33
-
34
- # ─── Curated community skills catalog ────────────────────────────────────────
35
- # Mirrors install.sh COMMUNITY_SKILLS_MAP. Designed to swap with real API later.
36
-
37
- COMMUNITY_SKILLS = [
38
- {
39
- "name": "react-best-practices",
40
- "repo": "vercel-labs/agent-skills",
41
- "description": "React/Next.js performance optimization (40+ rules from Vercel)",
42
- "tags": ["react", "nextjs", "performance", "components"],
43
- },
44
- {
45
- "name": "web-design-guidelines",
46
- "repo": "vercel-labs/agent-skills",
47
- "description": "UI/UX accessibility and design patterns (100+ rules)",
48
- "tags": ["design", "accessibility", "ux", "ui", "nextjs", "web"],
49
- },
50
- {
51
- "name": "convex-best-practices",
52
- "repo": "waynesutton/convexskills",
53
- "description": "Convex database patterns and best practices",
54
- "tags": ["convex", "database", "backend"],
55
- },
56
- {
57
- "name": "convex-functions",
58
- "repo": "waynesutton/convexskills",
59
- "description": "Convex query/mutation/action patterns",
60
- "tags": ["convex", "functions", "query", "mutation"],
61
- },
62
- {
63
- "name": "tailwind-v4",
64
- "repo": "blencorp/claude-code-kit",
65
- "description": "Tailwind CSS v4 patterns and configuration",
66
- "tags": ["tailwind", "css", "styling"],
67
- },
68
- {
69
- "name": "clerk-auth",
70
- "repo": "blencorp/claude-code-kit",
71
- "description": "Clerk authentication patterns",
72
- "tags": ["clerk", "auth", "authentication"],
73
- },
74
- ]
75
-
76
-
77
- # ─── Helpers ──────────────────────────────────────────────────────────────────
78
-
79
-
80
- def find_project_root():
81
- """Find the project root by walking up to .flydocs/config.json."""
82
- path = os.getcwd()
83
- while True:
84
- if os.path.isfile(os.path.join(path, ".flydocs", "config.json")):
85
- return path
86
- parent = os.path.dirname(path)
87
- if parent == path:
88
- break
89
- path = parent
90
- return os.getcwd()
91
-
92
-
93
- def load_config(root):
94
- """Load .flydocs/config.json."""
95
- config_path = os.path.join(root, ".flydocs", "config.json")
96
- if not os.path.isfile(config_path):
97
- print(f"{RED}✗{NC} No .flydocs/config.json found. Is FlyDocs installed?", file=sys.stderr)
98
- sys.exit(1)
99
- with open(config_path, "r", encoding="utf-8") as f:
100
- return json.load(f)
101
-
102
-
103
- def save_config(root, config):
104
- """Save .flydocs/config.json."""
105
- config_path = os.path.join(root, ".flydocs", "config.json")
106
- with open(config_path, "w", encoding="utf-8") as f:
107
- json.dump(config, f, indent=2)
108
- f.write("\n")
109
-
110
-
111
- def parse_frontmatter(text):
112
- """Parse YAML frontmatter from a SKILL.md file.
113
-
114
- Duplicated from generate_manifest.py — each script is self-contained.
115
- """
116
- match = re.match(r"^---\s*\n(.*?)\n---", text, re.DOTALL)
117
- if not match:
118
- return None
119
-
120
- block = match.group(1)
121
- result = {}
122
- current_key = None
123
- current_mode = None # "scalar", "block", "list"
124
- current_lines = []
125
-
126
- for line in block.split("\n"):
127
- key_match = re.match(r"^(\w[\w-]*):\s*(.*)", line)
128
- if key_match and not line.startswith(" "):
129
- if current_key is not None:
130
- result[current_key] = _flush(current_mode, current_lines)
131
-
132
- current_key = key_match.group(1)
133
- value = key_match.group(2).strip()
134
-
135
- if value in ("|", ">"):
136
- current_mode = "block"
137
- current_lines = []
138
- elif value == "":
139
- current_mode = "list"
140
- current_lines = []
141
- else:
142
- current_mode = "scalar"
143
- current_lines = [value.strip("\"'")]
144
- continue
145
-
146
- stripped = line.strip()
147
- if current_key is not None:
148
- if current_mode == "list" and stripped.startswith("- "):
149
- current_lines.append(stripped[2:].strip().strip("\"'"))
150
- elif current_mode == "block":
151
- current_lines.append(line.lstrip())
152
-
153
- if current_key is not None:
154
- result[current_key] = _flush(current_mode, current_lines)
155
-
156
- return result
157
-
158
-
159
- def _flush(mode, lines):
160
- if mode == "scalar":
161
- return lines[0] if lines else ""
162
- elif mode == "list":
163
- return lines
164
- elif mode == "block":
165
- return "\n".join(lines).strip()
166
- return ""
167
-
168
-
169
- def is_platform_skill(name):
170
- """Check if a skill name is platform-managed."""
171
- return name.startswith("flydocs-")
172
-
173
-
174
- def regenerate_manifest(root):
175
- """Run generate_manifest.py to update CLAUDE.md/AGENTS.md."""
176
- script = os.path.join(root, ".flydocs", "scripts", "generate_manifest.py")
177
- if not os.path.isfile(script):
178
- print(f"{YELLOW}⚠{NC} generate_manifest.py not found — skipping manifest update.")
179
- return
180
-
181
- try:
182
- subprocess.run(
183
- [sys.executable, script, "--root", root],
184
- check=True,
185
- capture_output=True,
186
- text=True,
187
- )
188
- print(f"{GREEN}✓{NC} Regenerated skill manifest")
189
- except subprocess.CalledProcessError as e:
190
- print(f"{YELLOW}⚠{NC} Manifest regeneration failed: {e.stderr.strip()}")
191
-
192
-
193
- # ─── search ──────────────────────────────────────────────────────────────────
194
-
195
-
196
- def cmd_search(args):
197
- """Search curated community skills catalog."""
198
- keyword = args.keyword.lower()
199
- matches = []
200
-
201
- for skill in COMMUNITY_SKILLS:
202
- searchable = f"{skill['name']} {skill['description']} {' '.join(skill['tags'])}".lower()
203
- if keyword in searchable:
204
- matches.append(skill)
205
-
206
- if not matches:
207
- print(f"\n No skills found matching '{args.keyword}'.")
208
- print(f" Browse more at: https://skills.sh/\n")
209
- return
210
-
211
- print(f"\n Found {len(matches)} skill(s) matching '{args.keyword}':\n")
212
- for skill in matches:
213
- print(f" {CYAN}{skill['name']}{NC}")
214
- print(f" {skill['description']}")
215
- print(f" Source: {skill['repo']}")
216
- print(f" Tags: {', '.join(skill['tags'])}")
217
- print()
218
-
219
- print(f" Install with: flydocs skills add <name>")
220
- print(f" Browse more at: https://skills.sh/\n")
221
-
222
-
223
- # ─── add ─────────────────────────────────────────────────────────────────────
224
-
225
-
226
- def parse_source(source):
227
- """Parse a skill source into (repo, skill_name).
228
-
229
- Accepts:
230
- - Known name: "react-best-practices"
231
- - Repo reference: "vercel-labs/agent-skills:react-best-practices"
232
- - GitHub URL: "https://github.com/owner/repo/tree/main/skills/name"
233
- """
234
- # GitHub URL
235
- url_match = re.match(
236
- r"https?://github\.com/([^/]+/[^/]+)/tree/[^/]+/skills/([^/]+)/?$",
237
- source,
238
- )
239
- if url_match:
240
- return url_match.group(1), url_match.group(2)
241
-
242
- # Repo reference (owner/repo:skill-name)
243
- if ":" in source:
244
- repo, name = source.rsplit(":", 1)
245
- if "/" in repo:
246
- return repo, name
247
-
248
- # Known name — lookup in curated catalog
249
- for skill in COMMUNITY_SKILLS:
250
- if skill["name"] == source:
251
- return skill["repo"], skill["name"]
252
-
253
- print(f"{RED}✗{NC} Unknown skill: {source}", file=sys.stderr)
254
- print(f" Try: flydocs skills search <keyword>", file=sys.stderr)
255
- print(f" Or use full reference: owner/repo:skill-name", file=sys.stderr)
256
- sys.exit(1)
257
-
258
-
259
- def download_skill_tree(repo, skill_name, target_dir):
260
- """Download a skill directory tree via GitHub Contents API (recursive)."""
261
- api_url = f"https://api.github.com/repos/{repo}/contents/skills/{skill_name}"
262
-
263
- def fetch_json(url):
264
- req = urllib.request.Request(url, headers={"User-Agent": "flydocs-cli"})
265
- try:
266
- with urllib.request.urlopen(req, timeout=15) as resp:
267
- return json.loads(resp.read().decode("utf-8"))
268
- except urllib.error.HTTPError as e:
269
- if e.code == 404:
270
- return None
271
- raise
272
-
273
- def download_recursive(url, dest_dir):
274
- items = fetch_json(url)
275
- if items is None:
276
- return False
277
-
278
- # Single file response comes as dict, directory as list
279
- if isinstance(items, dict):
280
- items = [items]
281
-
282
- os.makedirs(dest_dir, exist_ok=True)
283
-
284
- for item in items:
285
- dest_path = os.path.join(dest_dir, item["name"])
286
- if item["type"] == "file":
287
- if item.get("download_url"):
288
- req = urllib.request.Request(
289
- item["download_url"],
290
- headers={"User-Agent": "flydocs-cli"},
291
- )
292
- with urllib.request.urlopen(req, timeout=15) as resp:
293
- content = resp.read()
294
- with open(dest_path, "wb") as f:
295
- f.write(content)
296
- elif item["type"] == "dir":
297
- download_recursive(item["url"], dest_path)
298
-
299
- return True
300
-
301
- return download_recursive(api_url, target_dir)
302
-
303
-
304
- def cmd_add(args):
305
- """Install a community skill."""
306
- root = find_project_root()
307
- config = load_config(root)
308
- source = args.source
309
-
310
- # Platform guard (check before parse_source which would reject as "unknown")
311
- if is_platform_skill(source):
312
- print(f"{RED}✗{NC} Cannot install platform skill '{source}'.")
313
- print(f" Platform skills (flydocs-*) are managed by the installer.")
314
- sys.exit(1)
315
-
316
- repo, skill_name = parse_source(source)
317
-
318
- # Platform guard (for repo references that resolve to a flydocs-* name)
319
- if is_platform_skill(skill_name):
320
- print(f"{RED}✗{NC} Cannot install platform skill '{skill_name}'.")
321
- print(f" Platform skills (flydocs-*) are managed by the installer.")
322
- sys.exit(1)
323
-
324
- # Already installed check
325
- skills_dir = os.path.join(root, ".claude", "skills", skill_name)
326
- if os.path.isdir(skills_dir):
327
- print(f"{YELLOW}⚠{NC} Skill '{skill_name}' is already installed.")
328
- print(f" Remove first: flydocs skills remove {skill_name}")
329
- sys.exit(1)
330
-
331
- print(f"\n{BLUE}→{NC} Installing {CYAN}{skill_name}{NC} from {repo}...\n")
332
-
333
- # Download
334
- try:
335
- success = download_skill_tree(repo, skill_name, skills_dir)
336
- except Exception as e:
337
- if os.path.isdir(skills_dir):
338
- shutil.rmtree(skills_dir)
339
- print(f"{RED}✗{NC} Download failed: {e}")
340
- sys.exit(1)
341
-
342
- if not success:
343
- if os.path.isdir(skills_dir):
344
- shutil.rmtree(skills_dir)
345
- print(f"{RED}✗{NC} Skill not found at {repo}/skills/{skill_name}")
346
- sys.exit(1)
347
-
348
- # Validate SKILL.md exists
349
- skill_md = os.path.join(skills_dir, "SKILL.md")
350
- if not os.path.isfile(skill_md):
351
- shutil.rmtree(skills_dir)
352
- print(f"{RED}✗{NC} Invalid skill: SKILL.md not found.")
353
- sys.exit(1)
354
-
355
- # Validate frontmatter
356
- with open(skill_md, "r", encoding="utf-8") as f:
357
- content = f.read()
358
-
359
- fm = parse_frontmatter(content)
360
- if fm is None or not fm.get("name") or not fm.get("description"):
361
- shutil.rmtree(skills_dir)
362
- print(f"{RED}✗{NC} Invalid skill: SKILL.md missing required frontmatter (name, description).")
363
- sys.exit(1)
364
-
365
- if not fm.get("triggers"):
366
- print(f"{YELLOW}⚠{NC} SKILL.md has no triggers — skill won't appear in manifest index.")
367
-
368
- print(f"{GREEN}✓{NC} Downloaded skill files")
369
-
370
- # Copy cursor-rule.mdc if present
371
- cursor_rule = os.path.join(skills_dir, "cursor-rule.mdc")
372
- if os.path.isfile(cursor_rule):
373
- cursor_rules_dir = os.path.join(root, ".cursor", "rules")
374
- os.makedirs(cursor_rules_dir, exist_ok=True)
375
- dest = os.path.join(cursor_rules_dir, f"{skill_name}.mdc")
376
- shutil.copy2(cursor_rule, dest)
377
- print(f"{GREEN}✓{NC} Installed cursor rule")
378
-
379
- # Update config.json skills.installed[]
380
- installed = config.get("skills", {}).get("installed", [])
381
- entry = f"{repo}/{skill_name}"
382
- if entry not in installed:
383
- installed.append(entry)
384
- if "skills" not in config:
385
- config["skills"] = {"installed": [], "custom": []}
386
- config["skills"]["installed"] = installed
387
- save_config(root, config)
388
- print(f"{GREEN}✓{NC} Updated config.json")
389
-
390
- # Regenerate manifest
391
- regenerate_manifest(root)
392
-
393
- print(f"\n{GREEN}✓{NC} Installed {CYAN}{skill_name}{NC}\n")
394
-
395
-
396
- # ─── remove ──────────────────────────────────────────────────────────────────
397
-
398
-
399
- def cmd_remove(args):
400
- """Remove an installed community skill."""
401
- root = find_project_root()
402
- config = load_config(root)
403
- skill_name = args.name
404
-
405
- # Platform guard
406
- if is_platform_skill(skill_name):
407
- print(f"{RED}✗{NC} Cannot remove platform skill '{skill_name}'.")
408
- print(f" Platform skills (flydocs-*) are managed by the installer.")
409
- sys.exit(1)
410
-
411
- skills_dir = os.path.join(root, ".claude", "skills", skill_name)
412
- if not os.path.isdir(skills_dir):
413
- print(f"{RED}✗{NC} Skill '{skill_name}' is not installed.")
414
- sys.exit(1)
415
-
416
- # Remove skill directory
417
- shutil.rmtree(skills_dir)
418
- print(f"{GREEN}✓{NC} Removed skill directory")
419
-
420
- # Remove cursor rule
421
- cursor_rule = os.path.join(root, ".cursor", "rules", f"{skill_name}.mdc")
422
- if os.path.isfile(cursor_rule):
423
- os.remove(cursor_rule)
424
- print(f"{GREEN}✓{NC} Removed cursor rule")
425
-
426
- # Update config.json
427
- installed = config.get("skills", {}).get("installed", [])
428
- config["skills"]["installed"] = [
429
- s for s in installed if not s.endswith(f"/{skill_name}")
430
- ]
431
- save_config(root, config)
432
- print(f"{GREEN}✓{NC} Updated config.json")
433
-
434
- # Regenerate manifest
435
- regenerate_manifest(root)
436
-
437
- print(f"\n{GREEN}✓{NC} Removed {CYAN}{skill_name}{NC}\n")
438
-
439
-
440
- # ─── list ────────────────────────────────────────────────────────────────────
441
-
442
-
443
- def cmd_list(args):
444
- """List all installed skills with type and trigger count."""
445
- root = find_project_root()
446
- skills_dir = os.path.join(root, ".claude", "skills")
447
-
448
- if not os.path.isdir(skills_dir):
449
- print(f"\n No skills installed.\n")
450
- return
451
-
452
- platform = []
453
- community = []
454
-
455
- for entry in sorted(os.listdir(skills_dir)):
456
- skill_file = os.path.join(skills_dir, entry, "SKILL.md")
457
- if not os.path.isfile(skill_file):
458
- continue
459
-
460
- with open(skill_file, "r", encoding="utf-8") as f:
461
- content = f.read()
462
-
463
- fm = parse_frontmatter(content)
464
- name = fm.get("name", entry) if fm else entry
465
- triggers = fm.get("triggers", []) if fm else []
466
- if isinstance(triggers, str):
467
- triggers = [t.strip() for t in triggers.split(",") if t.strip()]
468
-
469
- info = {
470
- "name": name,
471
- "dir": entry,
472
- "type": "platform" if is_platform_skill(entry) else "community",
473
- "triggers": len(triggers),
474
- }
475
-
476
- if info["type"] == "platform":
477
- platform.append(info)
478
- else:
479
- community.append(info)
480
-
481
- total = len(platform) + len(community)
482
- if total == 0:
483
- print(f"\n No skills installed.\n")
484
- return
485
-
486
- print(f"\n {total} skill(s) installed:\n")
487
-
488
- if platform:
489
- print(f" {YELLOW}Platform{NC}")
490
- for s in platform:
491
- print(f" {CYAN}{s['name']}{NC} ({s['triggers']} triggers)")
492
-
493
- if community:
494
- if platform:
495
- print()
496
- print(f" {YELLOW}Community{NC}")
497
- for s in community:
498
- print(f" {CYAN}{s['name']}{NC} ({s['triggers']} triggers)")
499
-
500
- print()
501
-
502
-
503
- # ─── main ────────────────────────────────────────────────────────────────────
504
-
505
-
506
- def main():
507
- parser = argparse.ArgumentParser(
508
- prog="flydocs skills",
509
- description="Manage FlyDocs skills — search, install, and remove community skills.",
510
- )
511
- sub = parser.add_subparsers(dest="command")
512
-
513
- search_p = sub.add_parser("search", help="Search available community skills")
514
- search_p.add_argument("keyword", help="Keyword to search for")
515
-
516
- add_p = sub.add_parser("add", help="Install a community skill")
517
- add_p.add_argument("source", help="Skill name, repo:name, or GitHub URL")
518
-
519
- remove_p = sub.add_parser("remove", help="Remove an installed community skill")
520
- remove_p.add_argument("name", help="Skill directory name")
521
-
522
- sub.add_parser("list", help="List installed skills")
523
-
524
- args = parser.parse_args()
525
-
526
- if args.command is None:
527
- parser.print_help()
528
- sys.exit(1)
529
-
530
- commands = {
531
- "search": cmd_search,
532
- "add": cmd_add,
533
- "remove": cmd_remove,
534
- "list": cmd_list,
535
- }
536
-
537
- commands[args.command](args)
538
-
539
-
540
- if __name__ == "__main__":
541
- main()
@@ -1,166 +0,0 @@
1
- <!-- AGENT INSTRUCTIONS
2
- When creating or refining a bug issue:
3
-
4
- 1. CONTEXT SECTION:
5
- - Capture when/how the bug was discovered
6
- - Assess user impact (how many affected, how severe)
7
- - Note frequency (always, sometimes, specific conditions)
8
-
9
- 2. BUG DESCRIPTION:
10
- - Expected vs Actual must be crystal clear
11
- - Steps to reproduce must be detailed enough for anyone to follow
12
- - Include specific data conditions if relevant
13
- - Note environment details (browser, OS, user role)
14
-
15
- 3. ACCEPTANCE CRITERIA:
16
- - "Fix Verification" should mirror the reproduction steps
17
- - Include regression concerns (what else might break)
18
- - Tests section should include regression test requirement
19
-
20
- 4. TECHNICAL NOTES:
21
- - Start with hypothesis if cause unknown
22
- - Update "Confirmed Cause" after investigation
23
- - Include specific file/function where bug exists
24
- - Assess risk of the fix
25
-
26
- 5. SEVERITY GUIDANCE:
27
- - Critical: System unusable, data loss, security issue
28
- - High: Major feature broken, no workaround
29
- - Medium: Feature impaired but workaround exists
30
- - Low: Minor issue, cosmetic, edge case
31
-
32
- Remove these instructions when creating the final issue.
33
- -->
34
-
35
- ## Context
36
-
37
- **When Discovered:** [Date or event when bug was found]
38
- **Discovered By:** [User, developer, automated test, etc.]
39
- **Impact:** [How this affects users - be specific about scope]
40
- **Frequency:** [Always | Sometimes | Rarely | Under specific conditions]
41
-
42
- [Additional context about the bug]
43
-
44
- ---
45
-
46
- ## Bug Description
47
-
48
- ### Expected Behavior
49
- [Describe what SHOULD happen - the correct behavior]
50
-
51
- ### Actual Behavior
52
- [Describe what ACTUALLY happens - the broken behavior]
53
-
54
- ### Steps to Reproduce
55
- 1. [First action - be specific]
56
- 2. [Second action - include any data/conditions needed]
57
- 3. [Third action]
58
- 4. [Observe the bug]
59
-
60
- ### Environment
61
- - **Browser/Platform:** [Chrome, Safari, Mobile, etc.]
62
- - **OS:** [macOS, Windows, iOS, Android, etc.]
63
- - **User Role:** [Which user type experiences this]
64
- - **Data Conditions:** [Specific data state that triggers bug]
65
-
66
- ### Screenshots/Evidence
67
- <!-- Add as attachments -->
68
-
69
- ---
70
-
71
- ## Acceptance Criteria
72
-
73
- ### Fix Verification
74
- - [ ] Bug no longer reproducible using original steps
75
- - [ ] Expected behavior now works correctly
76
- - [ ] Fix doesn't break related functionality
77
- - [ ] No new error messages or console errors
78
-
79
- ### Tests
80
- - [ ] Regression test added to prevent recurrence
81
- - [ ] Related edge cases tested
82
- - [ ] All tests passing
83
-
84
- ### Documentation
85
- - [ ] Root cause documented (if significant pattern)
86
- - [ ] Prevention notes added to knowledge base (if applicable)
87
- - [ ] N/A - No significant documentation needed
88
-
89
- ---
90
-
91
- ## Technical Notes
92
-
93
- ### Root Cause Analysis
94
- **Hypothesis:** [What you think is causing the bug]
95
-
96
- **Investigation Findings:**
97
- - [Finding 1 from debugging]
98
- - [Finding 2 from debugging]
99
-
100
- **Confirmed Cause:** [What's actually wrong - file, function, logic error]
101
-
102
- ### Fix Approach
103
- [Describe the fix strategy - what needs to change]
104
-
105
- **Files to Modify:**
106
- - `path/to/file.tsx` - [What needs to change]
107
-
108
- **Risk Assessment:**
109
- - **Regression Risk:** Low | Medium | High
110
- - **Testing Required:** [What needs to be tested]
111
-
112
- ---
113
-
114
- ## Dependencies
115
-
116
- **Related Systems:**
117
- - [System or feature where bug occurs]
118
-
119
- **Blocks:**
120
- - [Work that can't proceed until bug is fixed]
121
- - OR: Nothing blocked
122
-
123
- **Related Bugs:**
124
- - [Link to related bug if applicable]
125
- - OR: No related issues
126
-
127
- ---
128
-
129
- ## AI Effort Estimate
130
- <!--
131
- AGENT: Fill this section during /refine using the flydocs-estimates skill.
132
- See .claude/skills/flydocs-estimates/SKILL.md for calculation details.
133
- Update Actuals section during /close for calibration.
134
- -->
135
-
136
- ### Sizing Factors
137
-
138
- | Factor | Value | Multiplier |
139
- |--------|-------|------------|
140
- | **Task Type** | bug | base: 20k |
141
- | **Scope** | [S/M/L/XL] | ×[0.5/1.0/2.0/4.0] |
142
- | **Novelty** | [existing/partial/greenfield] | ×[0.7/1.2/2.0] |
143
- | **Clarity** | [defined/discovery/exploratory] | ×[0.8/1.5/2.5] |
144
- | **Codebase** | [simple/moderate/complex] | ×[0.8/1.0/1.5] |
145
-
146
- ### Estimate
147
-
148
- **Provider**: [Claude Sonnet 4]
149
- **Calculated Tokens**: ~[X]k
150
- **Confidence**: ±[40-60]%
151
- **Token Range**: [low]k - [high]k
152
- **Cost Range**: $[low] - $[high]
153
-
154
- ### Actuals (fill on /close)
155
- **Actual Tokens**: [fill after completion]
156
- **Variance**: [+/-X]% [under/over estimate]
157
- **Notes**: [what drove variance - investigation time, fix complexity, retries]
158
-
159
- ---
160
-
161
- _Reported: YYYY-MM-DD_
162
-
163
-
164
-
165
-
166
-