@flydocs/cli 0.5.0-beta.6 → 0.5.0-beta.8
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/dist/cli.js +306 -237
- package/package.json +1 -1
- package/template/.claude/CLAUDE.md +1 -0
- package/template/.claude/agents/implementation-agent.md +1 -1
- package/template/.claude/agents/research-agent.md +1 -1
- package/template/.claude/commands/flydocs-setup.md +3 -3
- package/template/.claude/commands/flydocs-update.md +3 -4
- package/template/.claude/settings.json +0 -10
- package/template/.claude/skills/README.md +36 -41
- package/template/.claude/skills/flydocs-context7/SKILL.md +105 -0
- package/template/.claude/skills/flydocs-context7/cursor-rule.mdc +49 -0
- package/template/.claude/skills/flydocs-context7/scripts/context7.py +293 -0
- package/template/.cursor/hooks.json +0 -5
- package/template/.env.example +11 -2
- package/template/.flydocs/config.json +3 -8
- package/template/.flydocs/hooks/auto-approve.py +2 -2
- package/template/.flydocs/hooks/post-edit.py +13 -0
- package/template/.flydocs/templates/instructions.md +17 -69
- package/template/.flydocs/version +1 -1
- package/template/AGENTS.md +1 -0
- package/template/CHANGELOG.md +78 -0
- package/template/flydocs/README.md +36 -55
- package/template/flydocs/design-system/README.md +21 -13
- package/template/manifest.json +5 -16
- package/template/.cursor/mcp.json +0 -16
- package/template/.flydocs/hooks/prefer-scripts.py +0 -89
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Context7 API client for fetching library documentation.
|
|
3
|
+
|
|
4
|
+
Queries the Context7 REST API to search for libraries and retrieve
|
|
5
|
+
LLM-ready documentation. Replaces the Context7 MCP server with a
|
|
6
|
+
lightweight script that follows the FlyDocs mechanism pattern.
|
|
7
|
+
|
|
8
|
+
Usage:
|
|
9
|
+
python3 .claude/skills/flydocs-context7/scripts/context7.py \\
|
|
10
|
+
search <library_name> [query]
|
|
11
|
+
|
|
12
|
+
python3 .claude/skills/flydocs-context7/scripts/context7.py \\
|
|
13
|
+
docs <library_id> <query> [--type txt|json] [--tokens N]
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
import argparse
|
|
17
|
+
import json
|
|
18
|
+
import os
|
|
19
|
+
import sys
|
|
20
|
+
import urllib.error
|
|
21
|
+
import urllib.parse
|
|
22
|
+
import urllib.request
|
|
23
|
+
from pathlib import Path
|
|
24
|
+
|
|
25
|
+
BASE_URL = "https://context7.com/api/v2"
|
|
26
|
+
DEFAULT_TIMEOUT = 10
|
|
27
|
+
DEFAULT_TOKENS = 5000
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
# --- Helpers ---
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def fail(message):
|
|
34
|
+
"""Print error to stderr and exit 1."""
|
|
35
|
+
print(message, file=sys.stderr)
|
|
36
|
+
sys.exit(1)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def output_json(data):
|
|
40
|
+
"""Print JSON to stdout."""
|
|
41
|
+
print(json.dumps(data, indent=2))
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def find_project_root(start=None):
|
|
45
|
+
"""Walk up from start (or script location) to find the directory containing .flydocs/."""
|
|
46
|
+
current = Path(start) if start else Path(__file__).resolve().parent
|
|
47
|
+
for parent in [current] + list(current.parents):
|
|
48
|
+
if (parent / ".flydocs").is_dir():
|
|
49
|
+
return parent
|
|
50
|
+
return None
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def read_env_file(root):
|
|
54
|
+
"""Read .env file at project root and return a dict of key=value pairs."""
|
|
55
|
+
env_path = root / ".env"
|
|
56
|
+
if not env_path.is_file():
|
|
57
|
+
return {}
|
|
58
|
+
|
|
59
|
+
env_vars = {}
|
|
60
|
+
try:
|
|
61
|
+
with open(env_path, "r", encoding="utf-8") as f:
|
|
62
|
+
for line in f:
|
|
63
|
+
line = line.strip()
|
|
64
|
+
# Skip empty lines and comments
|
|
65
|
+
if not line or line.startswith("#"):
|
|
66
|
+
continue
|
|
67
|
+
if "=" not in line:
|
|
68
|
+
continue
|
|
69
|
+
key, _, value = line.partition("=")
|
|
70
|
+
key = key.strip()
|
|
71
|
+
value = value.strip()
|
|
72
|
+
# Strip surrounding quotes if present
|
|
73
|
+
if len(value) >= 2 and value[0] == value[-1] and value[0] in ('"', "'"):
|
|
74
|
+
value = value[1:-1]
|
|
75
|
+
env_vars[key] = value
|
|
76
|
+
except OSError:
|
|
77
|
+
pass
|
|
78
|
+
|
|
79
|
+
return env_vars
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def resolve_api_key(args_key):
|
|
83
|
+
"""Resolve the API key from flag, env var, or .env file. Returns None if unavailable."""
|
|
84
|
+
# 1. Explicit flag
|
|
85
|
+
if args_key:
|
|
86
|
+
return args_key
|
|
87
|
+
|
|
88
|
+
# 2. Environment variable
|
|
89
|
+
env_key = os.environ.get("CONTEXT7_API_KEY")
|
|
90
|
+
if env_key:
|
|
91
|
+
return env_key
|
|
92
|
+
|
|
93
|
+
# 3. .env file at project root
|
|
94
|
+
root = find_project_root()
|
|
95
|
+
if root:
|
|
96
|
+
env_vars = read_env_file(root)
|
|
97
|
+
dotenv_key = env_vars.get("CONTEXT7_API_KEY")
|
|
98
|
+
if dotenv_key:
|
|
99
|
+
return dotenv_key
|
|
100
|
+
|
|
101
|
+
# 4. Anonymous (no key)
|
|
102
|
+
return None
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def api_request(path, params, api_key=None):
|
|
106
|
+
"""Make a GET request to the Context7 API and return parsed response data.
|
|
107
|
+
|
|
108
|
+
Handles HTTP errors, timeouts, and network failures with clear messages.
|
|
109
|
+
"""
|
|
110
|
+
query_string = urllib.parse.urlencode(params)
|
|
111
|
+
url = f"{BASE_URL}/{path}?{query_string}"
|
|
112
|
+
|
|
113
|
+
request = urllib.request.Request(url)
|
|
114
|
+
request.add_header("User-Agent", "flydocs-context7/1.0")
|
|
115
|
+
|
|
116
|
+
if api_key:
|
|
117
|
+
request.add_header("Authorization", f"Bearer {api_key}")
|
|
118
|
+
|
|
119
|
+
try:
|
|
120
|
+
with urllib.request.urlopen(request, timeout=DEFAULT_TIMEOUT) as response:
|
|
121
|
+
body = response.read().decode("utf-8")
|
|
122
|
+
content_type = response.headers.get("Content-Type", "")
|
|
123
|
+
if "application/json" in content_type:
|
|
124
|
+
return json.loads(body)
|
|
125
|
+
return body
|
|
126
|
+
except urllib.error.HTTPError as e:
|
|
127
|
+
if e.code == 429:
|
|
128
|
+
fail("Rate limited by Context7 API. Wait a moment and try again.")
|
|
129
|
+
body_text = ""
|
|
130
|
+
try:
|
|
131
|
+
body_text = e.read().decode("utf-8", errors="replace")
|
|
132
|
+
except Exception:
|
|
133
|
+
pass
|
|
134
|
+
fail(f"Context7 API error (HTTP {e.code}): {e.reason}\n{body_text}".strip())
|
|
135
|
+
except urllib.error.URLError as e:
|
|
136
|
+
if "timed out" in str(e.reason).lower():
|
|
137
|
+
fail("Context7 API request timed out (10s). The service may be slow or unavailable.")
|
|
138
|
+
fail(f"Cannot reach Context7 API. Check your network connection.\nDetails: {e.reason}")
|
|
139
|
+
except OSError as e:
|
|
140
|
+
fail(f"Network error connecting to Context7 API: {e}")
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
# --- Commands ---
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def cmd_search(args):
|
|
147
|
+
"""Search for libraries by name and optional query."""
|
|
148
|
+
params = {"libraryName": args.library_name}
|
|
149
|
+
if args.query:
|
|
150
|
+
params["query"] = args.query
|
|
151
|
+
|
|
152
|
+
api_key = resolve_api_key(args.key)
|
|
153
|
+
data = api_request("libs/search", params, api_key=api_key)
|
|
154
|
+
|
|
155
|
+
# Normalize response — API wraps results in {"results": [...]}
|
|
156
|
+
if isinstance(data, dict) and "results" in data:
|
|
157
|
+
results = data["results"]
|
|
158
|
+
elif isinstance(data, list):
|
|
159
|
+
results = data
|
|
160
|
+
else:
|
|
161
|
+
results = []
|
|
162
|
+
|
|
163
|
+
if args.type == "json" or args.type is None:
|
|
164
|
+
# Default for search is JSON
|
|
165
|
+
compact = []
|
|
166
|
+
for lib in results:
|
|
167
|
+
compact.append({
|
|
168
|
+
"id": lib.get("id", ""),
|
|
169
|
+
"name": lib.get("title", lib.get("name", "")),
|
|
170
|
+
"description": lib.get("description", ""),
|
|
171
|
+
"trustScore": lib.get("trustScore", 0),
|
|
172
|
+
})
|
|
173
|
+
output_json(compact)
|
|
174
|
+
else:
|
|
175
|
+
# txt format for search — one line per result
|
|
176
|
+
for lib in results:
|
|
177
|
+
name = lib.get("title", lib.get("name", "unknown"))
|
|
178
|
+
lib_id = lib.get("id", "")
|
|
179
|
+
desc = lib.get("description", "")
|
|
180
|
+
score = lib.get("trustScore", 0)
|
|
181
|
+
print(f"{lib_id} {name} (trust: {score})")
|
|
182
|
+
if desc:
|
|
183
|
+
print(f" {desc}")
|
|
184
|
+
print()
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
def cmd_docs(args):
|
|
188
|
+
"""Fetch documentation for a specific library."""
|
|
189
|
+
params = {
|
|
190
|
+
"libraryId": args.library_id,
|
|
191
|
+
"query": args.query,
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
output_type = args.type if args.type else "txt"
|
|
195
|
+
params["type"] = output_type
|
|
196
|
+
|
|
197
|
+
if args.tokens:
|
|
198
|
+
params["tokens"] = str(args.tokens)
|
|
199
|
+
|
|
200
|
+
api_key = resolve_api_key(args.key)
|
|
201
|
+
data = api_request("context", params, api_key=api_key)
|
|
202
|
+
|
|
203
|
+
if output_type == "json":
|
|
204
|
+
if isinstance(data, str):
|
|
205
|
+
# API returned text when we asked for JSON; wrap it
|
|
206
|
+
output_json({"content": data})
|
|
207
|
+
else:
|
|
208
|
+
output_json(data)
|
|
209
|
+
else:
|
|
210
|
+
# txt — print directly
|
|
211
|
+
if isinstance(data, str):
|
|
212
|
+
print(data)
|
|
213
|
+
elif isinstance(data, dict):
|
|
214
|
+
content = data.get("content", data.get("text", ""))
|
|
215
|
+
if content:
|
|
216
|
+
print(content)
|
|
217
|
+
else:
|
|
218
|
+
output_json(data)
|
|
219
|
+
else:
|
|
220
|
+
output_json(data)
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
# --- Main ---
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
def main():
|
|
227
|
+
parser = argparse.ArgumentParser(
|
|
228
|
+
description="Context7 API client — search libraries and fetch documentation"
|
|
229
|
+
)
|
|
230
|
+
parser.add_argument(
|
|
231
|
+
"--key", type=str, default=None,
|
|
232
|
+
help="API key override (default: CONTEXT7_API_KEY env var or .env file)"
|
|
233
|
+
)
|
|
234
|
+
|
|
235
|
+
subparsers = parser.add_subparsers(dest="command", required=True)
|
|
236
|
+
|
|
237
|
+
# search subcommand
|
|
238
|
+
search_parser = subparsers.add_parser(
|
|
239
|
+
"search",
|
|
240
|
+
help="Search for libraries by name"
|
|
241
|
+
)
|
|
242
|
+
search_parser.add_argument(
|
|
243
|
+
"library_name",
|
|
244
|
+
help="Library name to search for (e.g., 'react', 'next.js')"
|
|
245
|
+
)
|
|
246
|
+
search_parser.add_argument(
|
|
247
|
+
"query", nargs="?", default=None,
|
|
248
|
+
help="Optional search query to refine results"
|
|
249
|
+
)
|
|
250
|
+
search_parser.add_argument(
|
|
251
|
+
"--type", choices=["txt", "json"], default=None,
|
|
252
|
+
help="Output format (default: json for search)"
|
|
253
|
+
)
|
|
254
|
+
search_parser.add_argument(
|
|
255
|
+
"--tokens", type=int, default=DEFAULT_TOKENS,
|
|
256
|
+
help=f"Max tokens budget hint (default: {DEFAULT_TOKENS})"
|
|
257
|
+
)
|
|
258
|
+
|
|
259
|
+
# docs subcommand
|
|
260
|
+
docs_parser = subparsers.add_parser(
|
|
261
|
+
"docs",
|
|
262
|
+
help="Fetch documentation for a library"
|
|
263
|
+
)
|
|
264
|
+
docs_parser.add_argument(
|
|
265
|
+
"library_id",
|
|
266
|
+
help="Library ID from search results (e.g., '/vercel/next.js')"
|
|
267
|
+
)
|
|
268
|
+
docs_parser.add_argument(
|
|
269
|
+
"query",
|
|
270
|
+
help="Documentation query (e.g., 'server components', 'routing')"
|
|
271
|
+
)
|
|
272
|
+
docs_parser.add_argument(
|
|
273
|
+
"--type", choices=["txt", "json"], default=None,
|
|
274
|
+
help="Output format (default: txt for docs)"
|
|
275
|
+
)
|
|
276
|
+
docs_parser.add_argument(
|
|
277
|
+
"--tokens", type=int, default=DEFAULT_TOKENS,
|
|
278
|
+
help=f"Max tokens budget hint (default: {DEFAULT_TOKENS})"
|
|
279
|
+
)
|
|
280
|
+
|
|
281
|
+
args = parser.parse_args()
|
|
282
|
+
|
|
283
|
+
if args.command == "search":
|
|
284
|
+
cmd_search(args)
|
|
285
|
+
elif args.command == "docs":
|
|
286
|
+
cmd_docs(args)
|
|
287
|
+
else:
|
|
288
|
+
parser.print_help()
|
|
289
|
+
sys.exit(1)
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
if __name__ == "__main__":
|
|
293
|
+
main()
|
package/template/.env.example
CHANGED
|
@@ -22,8 +22,9 @@
|
|
|
22
22
|
#
|
|
23
23
|
|
|
24
24
|
# ===========================================
|
|
25
|
-
#
|
|
25
|
+
# CLOUD TIER ONLY: Linear API Key
|
|
26
26
|
# ===========================================
|
|
27
|
+
# Only needed if you use cloud tier (tier: "cloud" in .flydocs/config.json)
|
|
27
28
|
# Get from: Linear → Settings → API → Personal API Keys
|
|
28
29
|
# Format: lin_api_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
|
29
30
|
LINEAR_API_KEY=
|
|
@@ -32,9 +33,17 @@ LINEAR_API_KEY=
|
|
|
32
33
|
# OPTIONAL: Figma Access Token
|
|
33
34
|
# ===========================================
|
|
34
35
|
# Get from: Figma → Settings → Personal Access Tokens
|
|
35
|
-
# Only needed if using
|
|
36
|
+
# Only needed if using the flydocs-figma skill for design integration
|
|
36
37
|
FIGMA_ACCESS_TOKEN=
|
|
37
38
|
|
|
39
|
+
# ===========================================
|
|
40
|
+
# OPTIONAL: Context7 API Key
|
|
41
|
+
# ===========================================
|
|
42
|
+
# Enables higher rate limits for library documentation lookup
|
|
43
|
+
# Get from: context7.com/dashboard (free account)
|
|
44
|
+
# Works without a key at lower rate limits (~1,000 calls/month)
|
|
45
|
+
CONTEXT7_API_KEY=
|
|
46
|
+
|
|
38
47
|
# ===========================================
|
|
39
48
|
# NOTE: Team ID and Project ID are discovered automatically
|
|
40
49
|
# ===========================================
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "0.5.0-beta.
|
|
2
|
+
"version": "0.5.0-beta.8",
|
|
3
3
|
"sourceRepo": "github.com/plastrlab/flydocs-core",
|
|
4
|
-
"tier": "
|
|
4
|
+
"tier": "local",
|
|
5
5
|
"setupComplete": false,
|
|
6
6
|
"paths": {
|
|
7
7
|
"content": "flydocs"
|
|
8
8
|
},
|
|
9
9
|
"provider": {
|
|
10
|
-
"type":
|
|
10
|
+
"type": null,
|
|
11
11
|
"teamId": null
|
|
12
12
|
},
|
|
13
13
|
"workspace": {
|
|
@@ -46,11 +46,6 @@
|
|
|
46
46
|
"auth": [],
|
|
47
47
|
"styling": []
|
|
48
48
|
},
|
|
49
|
-
"mcp": {
|
|
50
|
-
"preferred": [],
|
|
51
|
-
"fallbackOnly": ["linear"],
|
|
52
|
-
"stackSpecific": {}
|
|
53
|
-
},
|
|
54
49
|
"skills": {
|
|
55
50
|
"installed": [],
|
|
56
51
|
"custom": []
|
|
@@ -19,9 +19,9 @@ import re
|
|
|
19
19
|
|
|
20
20
|
|
|
21
21
|
# Pattern matches any flydocs skill scripts directory
|
|
22
|
-
# Covers: flydocs-local, flydocs-cloud, flydocs-workflow
|
|
22
|
+
# Covers: flydocs-local, flydocs-cloud, flydocs-workflow, flydocs-context-graph
|
|
23
23
|
APPROVED_PATTERN = re.compile(
|
|
24
|
-
r'
|
|
24
|
+
r'python3?\s+(?:["\']?(?:\$CLAUDE_PROJECT_DIR|\$\{CLAUDE_PROJECT_DIR\}|\.)["\']?/)?\.?claude/skills/flydocs-(?:local|cloud|workflow|context-graph|context7)/scripts/\w+\.py'
|
|
25
25
|
)
|
|
26
26
|
|
|
27
27
|
|
|
@@ -11,6 +11,7 @@ Exit codes:
|
|
|
11
11
|
"""
|
|
12
12
|
|
|
13
13
|
import json
|
|
14
|
+
import os
|
|
14
15
|
import subprocess
|
|
15
16
|
import sys
|
|
16
17
|
from pathlib import Path
|
|
@@ -59,6 +60,18 @@ def main() -> None:
|
|
|
59
60
|
print('{}')
|
|
60
61
|
sys.exit(0)
|
|
61
62
|
|
|
63
|
+
# Validate file_path is within project directory
|
|
64
|
+
project_dir = os.environ.get('CLAUDE_PROJECT_DIR', os.getcwd())
|
|
65
|
+
try:
|
|
66
|
+
resolved = os.path.realpath(file_path)
|
|
67
|
+
project_resolved = os.path.realpath(project_dir)
|
|
68
|
+
if not resolved.startswith(project_resolved + os.sep) and resolved != project_resolved:
|
|
69
|
+
print('{}')
|
|
70
|
+
sys.exit(0)
|
|
71
|
+
except (OSError, ValueError):
|
|
72
|
+
print('{}')
|
|
73
|
+
sys.exit(0)
|
|
74
|
+
|
|
62
75
|
# Get file extension and format
|
|
63
76
|
ext = get_file_extension(file_path)
|
|
64
77
|
format_file(file_path, ext)
|
|
@@ -22,8 +22,9 @@
|
|
|
22
22
|
<!-- Skills installed based on detected stack -->
|
|
23
23
|
|
|
24
24
|
{{#each skills}}
|
|
25
|
+
|
|
25
26
|
- `{{this}}` - See `.claude/skills/{{this}}/SKILL.md`
|
|
26
|
-
{{/each}}
|
|
27
|
+
{{/each}}
|
|
27
28
|
|
|
28
29
|
---
|
|
29
30
|
|
|
@@ -72,10 +73,10 @@ src/
|
|
|
72
73
|
|
|
73
74
|
<!-- Link to ADRs or explain key architectural choices -->
|
|
74
75
|
|
|
75
|
-
| Decision
|
|
76
|
-
|
|
76
|
+
| Decision | Rationale | Date |
|
|
77
|
+
| -------------------------------- | ---------------------------------------- | -------- |
|
|
77
78
|
| Use Server Components by default | Better performance, simpler mental model | {{date}} |
|
|
78
|
-
| Convex for real-time
|
|
79
|
+
| Convex for real-time | Built-in subscriptions, type safety | {{date}} |
|
|
79
80
|
|
|
80
81
|
---
|
|
81
82
|
|
|
@@ -105,59 +106,6 @@ async function Page() {
|
|
|
105
106
|
|
|
106
107
|
---
|
|
107
108
|
|
|
108
|
-
## MCP Configuration
|
|
109
|
-
|
|
110
|
-
<!-- Stack-specific MCP preferences for this project -->
|
|
111
|
-
|
|
112
|
-
### MCP Strategy
|
|
113
|
-
|
|
114
|
-
Configure in `.flydocs/config.json`:
|
|
115
|
-
|
|
116
|
-
```json
|
|
117
|
-
{
|
|
118
|
-
"mcp": {
|
|
119
|
-
"preferred": ["context7", "figma"],
|
|
120
|
-
"fallbackOnly": ["linear"],
|
|
121
|
-
"stackSpecific": {
|
|
122
|
-
"convex": {
|
|
123
|
-
"note": "Use Context7 for docs, CLI for schema changes"
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
```
|
|
129
|
-
|
|
130
|
-
- **preferred**: MCPs to use proactively (unique capabilities like Figma visuals, Context7 docs)
|
|
131
|
-
- **fallbackOnly**: MCPs with script alternatives (use scripts first, MCP if script fails)
|
|
132
|
-
- **stackSpecific**: Per-stack notes and usage guidance
|
|
133
|
-
|
|
134
|
-
### Stack-Specific MCP Notes
|
|
135
|
-
|
|
136
|
-
<!-- Uncomment and customize for your stack -->
|
|
137
|
-
|
|
138
|
-
<!--
|
|
139
|
-
#### Convex
|
|
140
|
-
- Use Context7 MCP for Convex API documentation
|
|
141
|
-
- Prefer `convex dev` CLI for schema changes over direct mutations
|
|
142
|
-
- Query patterns: Use `.collect()` for small datasets, pagination for large
|
|
143
|
-
-->
|
|
144
|
-
|
|
145
|
-
<!--
|
|
146
|
-
#### Supabase
|
|
147
|
-
- Use Supabase MCP for database operations when available
|
|
148
|
-
- Prefer RLS policies over application-level auth checks
|
|
149
|
-
- Use database functions for complex operations
|
|
150
|
-
-->
|
|
151
|
-
|
|
152
|
-
<!--
|
|
153
|
-
#### Firebase
|
|
154
|
-
- Use Firebase MCP for Firestore operations
|
|
155
|
-
- Prefer security rules over client-side validation
|
|
156
|
-
- Use batch operations for multiple writes
|
|
157
|
-
-->
|
|
158
|
-
|
|
159
|
-
---
|
|
160
|
-
|
|
161
109
|
## Skill Overrides
|
|
162
110
|
|
|
163
111
|
<!-- Override specific skill recommendations for this project -->
|
|
@@ -168,7 +116,7 @@ Configure in `.flydocs/config.json`:
|
|
|
168
116
|
|
|
169
117
|
```typescript
|
|
170
118
|
// This project uses shorter cache times due to real-time requirements
|
|
171
|
-
cacheLife(
|
|
119
|
+
cacheLife("seconds"); // Instead of skill default 'hours'
|
|
172
120
|
```
|
|
173
121
|
|
|
174
122
|
### convex
|
|
@@ -181,7 +129,7 @@ defineTable({
|
|
|
181
129
|
// ... fields
|
|
182
130
|
createdAt: v.number(),
|
|
183
131
|
updatedAt: v.number(),
|
|
184
|
-
createdBy: v.optional(v.id(
|
|
132
|
+
createdBy: v.optional(v.id("users")),
|
|
185
133
|
});
|
|
186
134
|
```
|
|
187
135
|
|
|
@@ -191,11 +139,11 @@ defineTable({
|
|
|
191
139
|
|
|
192
140
|
<!-- Document required env vars for this project -->
|
|
193
141
|
|
|
194
|
-
| Variable
|
|
195
|
-
|
|
196
|
-
| `DATABASE_URL`
|
|
197
|
-
| `NEXT_PUBLIC_API_URL` | Public API endpoint
|
|
198
|
-
| `AUTH_SECRET`
|
|
142
|
+
| Variable | Purpose | Required |
|
|
143
|
+
| --------------------- | ----------------------- | -------- |
|
|
144
|
+
| `DATABASE_URL` | Database connection | Yes |
|
|
145
|
+
| `NEXT_PUBLIC_API_URL` | Public API endpoint | Yes |
|
|
146
|
+
| `AUTH_SECRET` | Auth session encryption | Yes |
|
|
199
147
|
|
|
200
148
|
---
|
|
201
149
|
|
|
@@ -208,10 +156,10 @@ defineTable({
|
|
|
208
156
|
// Test location: Co-located with source files
|
|
209
157
|
|
|
210
158
|
// Example test structure
|
|
211
|
-
describe(
|
|
212
|
-
it(
|
|
159
|
+
describe("UserService", () => {
|
|
160
|
+
it("creates a user with valid data", async () => {
|
|
213
161
|
// Arrange
|
|
214
|
-
const input = { name:
|
|
162
|
+
const input = { name: "Test", email: "test@example.com" };
|
|
215
163
|
|
|
216
164
|
// Act
|
|
217
165
|
const result = await createUser(input);
|
|
@@ -224,5 +172,5 @@ describe('UserService', () => {
|
|
|
224
172
|
|
|
225
173
|
---
|
|
226
174
|
|
|
227
|
-
|
|
228
|
-
|
|
175
|
+
_Last updated: {{date}}_
|
|
176
|
+
_FlyDocs version: 6.3.0_
|
|
@@ -1 +1 @@
|
|
|
1
|
-
0.5.0-beta.
|
|
1
|
+
0.5.0-beta.8
|
package/template/AGENTS.md
CHANGED
|
@@ -95,6 +95,7 @@ Consult the relevant skill BEFORE writing code or making workflow decisions.
|
|
|
95
95
|
| Skill | Triggers | Entry |
|
|
96
96
|
| ----------------- | ----------------------------------------------------------------------------------------------------------------------- | ----------------------------------------- |
|
|
97
97
|
| flydocs-cloud | create issue, transition, comment, list issues, assign, update description, update issue, project update, Linear, cloud | .claude/skills/flydocs-cloud/SKILL.md |
|
|
98
|
+
| flydocs-context7 | context7, library docs, documentation lookup, framework docs, package docs, API reference | .claude/skills/flydocs-context7/SKILL.md |
|
|
98
99
|
| flydocs-estimates | estimate, cost, token usage, API cost, labor estimate, sizing, effort | .claude/skills/flydocs-estimates/SKILL.md |
|
|
99
100
|
| flydocs-figma | Figma, design, screenshot, token mapping, component from design, pixel-perfect, design system | .claude/skills/flydocs-figma/SKILL.md |
|
|
100
101
|
| flydocs-local | create issue, transition, comment, list issues, assign, update description, status summary, local | .claude/skills/flydocs-local/SKILL.md |
|