@flydocs/cli 0.6.0-alpha.3 → 0.6.0-alpha.31

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 (151) hide show
  1. package/dist/cli.js +2053 -469
  2. package/package.json +1 -1
  3. package/template/.claude/CLAUDE.md +43 -48
  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 +359 -72
  12. package/template/.claude/commands/flydocs-upgrade.md +26 -27
  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 +212 -0
  25. package/template/.claude/hooks/post-pr-check.py +108 -0
  26. package/template/.claude/hooks/post-transition-check.py +281 -0
  27. package/template/.claude/hooks/prompt-submit.py +554 -0
  28. package/template/.claude/hooks/session-start.py +262 -0
  29. package/template/.claude/hooks/stop-gate.py +162 -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 +260 -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 +724 -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 +738 -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/test_enforcement.py +225 -0
  56. package/template/.claude/skills/flydocs-workflow/scripts/workspace.py +902 -0
  57. package/template/.claude/skills/flydocs-workflow/session.md +87 -29
  58. package/template/.claude/skills/flydocs-workflow/stages/activate.md +18 -7
  59. package/template/.claude/skills/flydocs-workflow/stages/capture.md +10 -5
  60. package/template/.claude/skills/flydocs-workflow/stages/close.md +4 -3
  61. package/template/.claude/skills/flydocs-workflow/stages/implement.md +33 -9
  62. package/template/.claude/skills/flydocs-workflow/stages/refine.md +22 -6
  63. package/template/.claude/skills/flydocs-workflow/stages/review.md +16 -4
  64. package/template/.claude/skills/flydocs-workflow/stages/validate.md +3 -1
  65. package/template/.claude/skills/flydocs-workflow/templates/pr/default.md +33 -0
  66. package/template/.cursor/agents/implementation-agent.md +1 -1
  67. package/template/.cursor/agents/pm-agent.md +2 -2
  68. package/template/.cursor/hooks.json +10 -3
  69. package/template/.env.example +6 -6
  70. package/template/.flydocs/config.json +5 -18
  71. package/template/.flydocs/templates/README.md +13 -14
  72. package/template/.flydocs/templates/bug.md +17 -153
  73. package/template/.flydocs/templates/chore.md +10 -98
  74. package/template/.flydocs/templates/feature.md +12 -158
  75. package/template/.flydocs/templates/idea.md +11 -111
  76. package/template/.flydocs/templates/quick-capture.md +4 -8
  77. package/template/.flydocs/version +1 -1
  78. package/template/AGENTS.md +44 -32
  79. package/template/CHANGELOG.md +37 -0
  80. package/template/flydocs/README.md +1 -3
  81. package/template/flydocs/context/project.md +6 -3
  82. package/template/flydocs/design-system/README.md +3 -3
  83. package/template/flydocs/knowledge/INDEX.md +38 -53
  84. package/template/flydocs/knowledge/README.md +60 -9
  85. package/template/flydocs/knowledge/templates/decision.md +47 -0
  86. package/template/flydocs/knowledge/templates/feature.md +35 -0
  87. package/template/flydocs/knowledge/templates/note.md +25 -0
  88. package/template/manifest.json +24 -20
  89. package/template/.claude/skills/flydocs-cloud/SKILL.md +0 -113
  90. package/template/.claude/skills/flydocs-cloud/cursor-rule.mdc +0 -50
  91. package/template/.claude/skills/flydocs-cloud/scripts/assign.py +0 -22
  92. package/template/.claude/skills/flydocs-cloud/scripts/assign_cycle.py +0 -28
  93. package/template/.claude/skills/flydocs-cloud/scripts/assign_milestone.py +0 -22
  94. package/template/.claude/skills/flydocs-cloud/scripts/comment.py +0 -29
  95. package/template/.claude/skills/flydocs-cloud/scripts/create_issue.py +0 -66
  96. package/template/.claude/skills/flydocs-cloud/scripts/create_milestone.py +0 -35
  97. package/template/.claude/skills/flydocs-cloud/scripts/create_project.py +0 -33
  98. package/template/.claude/skills/flydocs-cloud/scripts/create_team.py +0 -39
  99. package/template/.claude/skills/flydocs-cloud/scripts/estimate.py +0 -29
  100. package/template/.claude/skills/flydocs-cloud/scripts/flydocs_api.py +0 -210
  101. package/template/.claude/skills/flydocs-cloud/scripts/get_issue.py +0 -24
  102. package/template/.claude/skills/flydocs-cloud/scripts/link.py +0 -28
  103. package/template/.claude/skills/flydocs-cloud/scripts/list_cycles.py +0 -28
  104. package/template/.claude/skills/flydocs-cloud/scripts/list_issues.py +0 -44
  105. package/template/.claude/skills/flydocs-cloud/scripts/list_labels.py +0 -19
  106. package/template/.claude/skills/flydocs-cloud/scripts/list_milestones.py +0 -28
  107. package/template/.claude/skills/flydocs-cloud/scripts/list_projects.py +0 -31
  108. package/template/.claude/skills/flydocs-cloud/scripts/list_providers.py +0 -19
  109. package/template/.claude/skills/flydocs-cloud/scripts/list_teams.py +0 -19
  110. package/template/.claude/skills/flydocs-cloud/scripts/priority.py +0 -29
  111. package/template/.claude/skills/flydocs-cloud/scripts/project_update.py +0 -45
  112. package/template/.claude/skills/flydocs-cloud/scripts/set_labels.py +0 -68
  113. package/template/.claude/skills/flydocs-cloud/scripts/set_provider.py +0 -46
  114. package/template/.claude/skills/flydocs-cloud/scripts/set_team.py +0 -41
  115. package/template/.claude/skills/flydocs-cloud/scripts/transition.py +0 -26
  116. package/template/.claude/skills/flydocs-cloud/scripts/update_description.py +0 -36
  117. package/template/.claude/skills/flydocs-cloud/scripts/update_issue.py +0 -82
  118. package/template/.claude/skills/flydocs-context-graph/SKILL.md +0 -87
  119. package/template/.claude/skills/flydocs-context-graph/schema.md +0 -78
  120. package/template/.claude/skills/flydocs-context-graph/scripts/graph_context.py +0 -338
  121. package/template/.claude/skills/flydocs-context7/SKILL.md +0 -105
  122. package/template/.claude/skills/flydocs-context7/cursor-rule.mdc +0 -49
  123. package/template/.claude/skills/flydocs-context7/scripts/context7.py +0 -293
  124. package/template/.claude/skills/flydocs-estimates/SKILL.md +0 -384
  125. package/template/.claude/skills/flydocs-figma/SKILL.md +0 -377
  126. package/template/.claude/skills/flydocs-figma/references/PROMPTING.md +0 -108
  127. package/template/.claude/skills/flydocs-figma/references/TROUBLESHOOTING.md +0 -112
  128. package/template/.claude/skills/flydocs-local/SKILL.md +0 -103
  129. package/template/.claude/skills/flydocs-local/cursor-rule.mdc +0 -43
  130. package/template/.claude/skills/flydocs-local/scripts/assign.py +0 -20
  131. package/template/.claude/skills/flydocs-local/scripts/comment.py +0 -27
  132. package/template/.claude/skills/flydocs-local/scripts/create_issue.py +0 -44
  133. package/template/.claude/skills/flydocs-local/scripts/estimate.py +0 -37
  134. package/template/.claude/skills/flydocs-local/scripts/get_issue.py +0 -20
  135. package/template/.claude/skills/flydocs-local/scripts/link.py +0 -41
  136. package/template/.claude/skills/flydocs-local/scripts/list_issues.py +0 -34
  137. package/template/.claude/skills/flydocs-local/scripts/priority.py +0 -37
  138. package/template/.claude/skills/flydocs-local/scripts/project_update.py +0 -67
  139. package/template/.claude/skills/flydocs-local/scripts/status_summary.py +0 -16
  140. package/template/.claude/skills/flydocs-local/scripts/transition.py +0 -24
  141. package/template/.claude/skills/flydocs-local/scripts/update_description.py +0 -35
  142. package/template/.claude/skills/flydocs-local/scripts/update_issue.py +0 -84
  143. package/template/.flydocs/hooks/auto-approve.py +0 -71
  144. package/template/.flydocs/hooks/prompt-submit.py +0 -277
  145. package/template/.flydocs/scripts/skill_manager.py +0 -541
  146. /package/template/{.flydocs → .claude}/hooks/post-edit.py +0 -0
  147. /package/template/.claude/skills/{flydocs-estimates/references → flydocs-workflow/reference}/provider-costs.md +0 -0
  148. /package/template/.claude/skills/flydocs-workflow/templates/{bug.md → issues/bug.md} +0 -0
  149. /package/template/.claude/skills/flydocs-workflow/templates/{chore.md → issues/chore.md} +0 -0
  150. /package/template/.claude/skills/flydocs-workflow/templates/{feature.md → issues/feature.md} +0 -0
  151. /package/template/.claude/skills/flydocs-workflow/templates/{idea.md → issues/idea.md} +0 -0
@@ -1,39 +0,0 @@
1
- #!/usr/bin/env python3
2
- """Create a team (or sub-team) via the FlyDocs Relay API."""
3
-
4
- import argparse
5
- import sys
6
- from pathlib import Path
7
-
8
- sys.path.insert(0, str(Path(__file__).parent))
9
- from flydocs_api import get_client, output_json
10
-
11
-
12
- def main():
13
- parser = argparse.ArgumentParser(description="Create team")
14
- parser.add_argument("--name", required=True)
15
- parser.add_argument("--key", default=None, help="Team key (e.g., PROD). Auto-generated if omitted.")
16
- parser.add_argument("--description", default=None)
17
- parser.add_argument("--parent", default=None, dest="parent_id", help="Parent team ID to create a sub-team")
18
- args = parser.parse_args()
19
-
20
- body: dict = {"name": args.name}
21
- if args.key:
22
- body["key"] = args.key
23
- if args.description:
24
- body["description"] = args.description
25
- if args.parent_id:
26
- body["parentId"] = args.parent_id
27
-
28
- client = get_client()
29
- result = client.post("/teams", body)
30
-
31
- output_json({
32
- "id": result["id"],
33
- "name": result["name"],
34
- "key": result.get("key", ""),
35
- })
36
-
37
-
38
- if __name__ == "__main__":
39
- main()
@@ -1,29 +0,0 @@
1
- #!/usr/bin/env python3
2
- """Set estimate on an issue via the FlyDocs Relay API."""
3
-
4
- import sys
5
- from pathlib import Path
6
-
7
- sys.path.insert(0, str(Path(__file__).parent))
8
- from flydocs_api import get_client, output_json, fail
9
-
10
- if len(sys.argv) < 3:
11
- fail("Usage: estimate.py <ref> <1-5>")
12
-
13
- ref = sys.argv[1]
14
- try:
15
- estimate = int(sys.argv[2])
16
- except ValueError:
17
- fail("Estimate must be a number (1-5)")
18
-
19
- if estimate not in (1, 2, 3, 5):
20
- fail("Estimate must be 1 (XS), 2 (S), 3 (M), or 5 (L)")
21
-
22
- client = get_client()
23
- result = client.put(f"/issues/{ref}/estimate", {"estimate": estimate})
24
-
25
- output_json({
26
- "success": result.get("success", True),
27
- "issue": result.get("issue", ref),
28
- "estimate": result.get("estimate", estimate),
29
- })
@@ -1,210 +0,0 @@
1
- """FlyDocs Cloud API — Relay REST client.
2
-
3
- Central module for all relay API operations.
4
- Loads config from .flydocs/config.json and API key from .env files.
5
-
6
- Usage:
7
- from flydocs_api import get_client
8
- client = get_client()
9
- result = client.post("/issues", {"title": "New issue", "type": "feature"})
10
- """
11
-
12
- import json
13
- import os
14
- import sys
15
- import time
16
- from pathlib import Path
17
- from typing import Optional
18
-
19
-
20
- class FlyDocsClient:
21
- """Client for FlyDocs Relay REST API with retry logic and config awareness."""
22
-
23
- DEFAULT_BASE_URL = "https://app.flydocs.ai/api/relay"
24
- LOCAL_BASE_URL = "http://localhost:3000/api/relay"
25
- MAX_RETRIES = 3
26
- RETRY_DELAY = 2
27
-
28
- def __init__(self):
29
- self.project_root = find_project_root()
30
- self.config_path = self.project_root / ".flydocs" / "config.json"
31
- self.log_path = self.project_root / ".flydocs" / "logs" / "relay-ops.jsonl"
32
-
33
- self.config = self._load_config()
34
- self.api_key = self._load_api_key()
35
- if not self.api_key:
36
- print("ERROR: FLYDOCS_API_KEY not found", file=sys.stderr)
37
- print("Set in environment or .env/.env.local file", file=sys.stderr)
38
- sys.exit(1)
39
-
40
- self.base_url = self._resolve_base_url()
41
-
42
- def _load_config(self) -> dict:
43
- if self.config_path.exists():
44
- with open(self.config_path, "r") as f:
45
- return json.load(f)
46
- return {}
47
-
48
- def _load_api_key(self) -> Optional[str]:
49
- if os.environ.get("FLYDOCS_API_KEY"):
50
- return os.environ["FLYDOCS_API_KEY"]
51
- for name in [".env.local", ".env"]:
52
- env_file = self.project_root / name
53
- if env_file.exists():
54
- key = self._parse_env_file(env_file, "FLYDOCS_API_KEY")
55
- if key:
56
- return key
57
- return None
58
-
59
- def _parse_env_file(self, path: Path, key: str) -> Optional[str]:
60
- with open(path, "r") as f:
61
- for line in f:
62
- line = line.strip()
63
- if line.startswith("#") or "=" not in line:
64
- continue
65
- k, _, v = line.partition("=")
66
- if k.strip() == key:
67
- v = v.strip().strip("\"'")
68
- return v if v else None
69
- return None
70
-
71
- def _resolve_base_url(self) -> str:
72
- """Resolve base URL: env var > config > default."""
73
- env_url = os.environ.get("FLYDOCS_RELAY_URL")
74
- if env_url:
75
- return env_url.rstrip("/")
76
- config_url = self.config.get("relay", {}).get("url")
77
- if config_url:
78
- return config_url.rstrip("/")
79
- return self.DEFAULT_BASE_URL
80
-
81
- def _request(self, method: str, path: str, body: Optional[dict] = None,
82
- params: Optional[dict] = None) -> dict:
83
- """Make an HTTP request to the relay API."""
84
- import urllib.request
85
- import urllib.error
86
- import urllib.parse
87
-
88
- url = f"{self.base_url}{path}"
89
- if params:
90
- filtered = {k: v for k, v in params.items() if v is not None and v != ""}
91
- if filtered:
92
- url += "?" + urllib.parse.urlencode(filtered, doseq=True)
93
-
94
- headers = {
95
- "Authorization": f"Bearer {self.api_key}",
96
- "Content-Type": "application/json",
97
- "Accept": "application/json",
98
- }
99
-
100
- data = json.dumps(body).encode("utf-8") if body else None
101
-
102
- for attempt in range(self.MAX_RETRIES):
103
- try:
104
- req = urllib.request.Request(url, data=data, headers=headers, method=method)
105
- with urllib.request.urlopen(req, timeout=15) as resp:
106
- response_body = resp.read().decode("utf-8")
107
- result = json.loads(response_body) if response_body else {}
108
- self._log_operation(method, path, resp.status, result)
109
- return result
110
- except urllib.error.HTTPError as e:
111
- error_body = e.read().decode("utf-8") if e.fp else ""
112
- try:
113
- error_data = json.loads(error_body) if error_body else {}
114
- except json.JSONDecodeError:
115
- error_data = {"error": error_body}
116
- self._log_operation(method, path, e.code, error_data)
117
-
118
- if e.code == 429 and attempt < self.MAX_RETRIES - 1:
119
- delay = self.RETRY_DELAY * (2 ** attempt)
120
- print(f"Rate limited, retrying in {delay}s...", file=sys.stderr)
121
- time.sleep(delay)
122
- continue
123
- if e.code >= 500 and attempt < self.MAX_RETRIES - 1:
124
- delay = self.RETRY_DELAY * (2 ** attempt)
125
- print(f"Server error ({e.code}), retrying in {delay}s...", file=sys.stderr)
126
- time.sleep(delay)
127
- continue
128
-
129
- error_msg = error_data.get("error", f"HTTP {e.code}")
130
- error_code = error_data.get("code", "UNKNOWN")
131
- provider = error_data.get("provider_error", "")
132
- msg = f"Relay API error ({error_code}): {error_msg}"
133
- if provider:
134
- msg += f" — provider: {provider}"
135
- fail(msg)
136
- except (urllib.error.URLError, TimeoutError):
137
- if attempt < self.MAX_RETRIES - 1:
138
- delay = self.RETRY_DELAY * (2 ** attempt)
139
- print(f"Network error, retrying in {delay}s...", file=sys.stderr)
140
- time.sleep(delay)
141
- continue
142
- fail("Network error: unable to reach relay API")
143
-
144
- fail("Max retries exceeded")
145
- return {} # unreachable but satisfies type checker
146
-
147
- def get(self, path: str, params: Optional[dict] = None) -> dict | list:
148
- """GET request to relay API."""
149
- return self._request("GET", path, params=params)
150
-
151
- def post(self, path: str, body: Optional[dict] = None) -> dict:
152
- """POST request to relay API."""
153
- return self._request("POST", path, body=body)
154
-
155
- def put(self, path: str, body: Optional[dict] = None) -> dict:
156
- """PUT request to relay API."""
157
- return self._request("PUT", path, body=body)
158
-
159
- def patch(self, path: str, body: Optional[dict] = None) -> dict:
160
- """PATCH request to relay API."""
161
- return self._request("PATCH", path, body=body)
162
-
163
- def _log_operation(self, method: str, path: str, status: int, result: dict | list):
164
- """Log operation metadata to local log file."""
165
- try:
166
- from datetime import datetime, timezone
167
- self.log_path.parent.mkdir(parents=True, exist_ok=True)
168
- entry = {
169
- "timestamp": datetime.now(timezone.utc).isoformat().replace("+00:00", "Z"),
170
- "method": method,
171
- "path": path,
172
- "status": status,
173
- "success": 200 <= status < 300,
174
- }
175
- with open(self.log_path, "a") as f:
176
- f.write(json.dumps(entry) + "\n")
177
- except Exception:
178
- pass # logging should never break operations
179
-
180
-
181
- def find_project_root() -> Path:
182
- """Walk up from cwd to find .flydocs/ directory."""
183
- current = Path.cwd()
184
- while current != current.parent:
185
- if (current / ".flydocs").is_dir():
186
- return current
187
- current = current.parent
188
- return Path.cwd()
189
-
190
-
191
- _client: Optional[FlyDocsClient] = None
192
-
193
-
194
- def get_client() -> FlyDocsClient:
195
- """Get or create singleton client."""
196
- global _client
197
- if _client is None:
198
- _client = FlyDocsClient()
199
- return _client
200
-
201
-
202
- def output_json(data: dict | list) -> None:
203
- """Print JSON to stdout — standard contract output."""
204
- print(json.dumps(data))
205
-
206
-
207
- def fail(message: str) -> None:
208
- """Print error to stderr and exit 1."""
209
- print(message, file=sys.stderr)
210
- sys.exit(1)
@@ -1,24 +0,0 @@
1
- #!/usr/bin/env python3
2
- """Get full details for an issue via the FlyDocs Relay API."""
3
-
4
- import argparse
5
- import sys
6
- from pathlib import Path
7
-
8
- sys.path.insert(0, str(Path(__file__).parent))
9
- from flydocs_api import get_client, output_json, fail
10
-
11
- parser = argparse.ArgumentParser(description="Get issue details")
12
- parser.add_argument("ref", help="Issue reference (e.g., ENG-123)")
13
- parser.add_argument("--fields", choices=["basic", "full"], default="full",
14
- help="basic = no comments, full = with comments (default)")
15
- args = parser.parse_args()
16
-
17
- params: dict = {}
18
- if args.fields == "basic":
19
- params["fields"] = "basic"
20
-
21
- client = get_client()
22
- result = client.get(f"/issues/{args.ref}", params=params)
23
-
24
- output_json(result)
@@ -1,28 +0,0 @@
1
- #!/usr/bin/env python3
2
- """Create a link between two issues via the FlyDocs Relay API."""
3
-
4
- import sys
5
- from pathlib import Path
6
-
7
- sys.path.insert(0, str(Path(__file__).parent))
8
- from flydocs_api import get_client, output_json, fail
9
-
10
- if len(sys.argv) < 4:
11
- fail("Usage: link.py <ref> <related_ref> <type>")
12
-
13
- ref, related_ref, link_type = sys.argv[1], sys.argv[2], sys.argv[3]
14
-
15
- valid_types = ("blocks", "related", "duplicate")
16
- if link_type not in valid_types:
17
- fail(f"Link type must be one of: {', '.join(valid_types)}")
18
-
19
- client = get_client()
20
- result = client.post(f"/issues/{ref}/link", {
21
- "relatedRef": related_ref,
22
- "type": link_type,
23
- })
24
-
25
- output_json({
26
- "success": result.get("success", True),
27
- "type": result.get("type", link_type),
28
- })
@@ -1,28 +0,0 @@
1
- #!/usr/bin/env python3
2
- """List cycles via the FlyDocs Relay API."""
3
-
4
- import argparse
5
- import sys
6
- from pathlib import Path
7
-
8
- sys.path.insert(0, str(Path(__file__).parent))
9
- from flydocs_api import get_client, output_json
10
-
11
-
12
- def main():
13
- parser = argparse.ArgumentParser(description="List cycles")
14
- parser.add_argument("--active", action="store_true")
15
- args = parser.parse_args()
16
-
17
- params: dict = {}
18
- if args.active:
19
- params["active"] = "true"
20
-
21
- client = get_client()
22
- result = client.get("/cycles", params=params)
23
-
24
- output_json(result)
25
-
26
-
27
- if __name__ == "__main__":
28
- main()
@@ -1,44 +0,0 @@
1
- #!/usr/bin/env python3
2
- """List issues with optional filters via the FlyDocs Relay API."""
3
-
4
- import argparse
5
- import sys
6
- from pathlib import Path
7
-
8
- sys.path.insert(0, str(Path(__file__).parent))
9
- from flydocs_api import get_client, output_json
10
-
11
-
12
- def main():
13
- parser = argparse.ArgumentParser(description="List issues")
14
- parser.add_argument("--status", default="", help="Single status or comma-separated: READY,IMPLEMENTING,BLOCKED")
15
- parser.add_argument("--active", action="store_true", help="All non-terminal states")
16
- parser.add_argument("--project", default="")
17
- parser.add_argument("--assignee", default="")
18
- parser.add_argument("--milestone", default="", help="Filter by milestone ID")
19
- parser.add_argument("--mine", action="store_true")
20
- parser.add_argument("--limit", type=int, default=50)
21
- args = parser.parse_args()
22
-
23
- params: dict = {"limit": str(args.limit)}
24
- if args.status:
25
- params["status"] = args.status.upper()
26
- if args.active:
27
- params["active"] = "true"
28
- if args.project:
29
- params["project"] = args.project
30
- if args.assignee:
31
- params["assignee"] = args.assignee
32
- if args.milestone:
33
- params["milestone"] = args.milestone
34
- if args.mine:
35
- params["mine"] = "true"
36
-
37
- client = get_client()
38
- result = client.get("/issues", params=params)
39
-
40
- output_json(result)
41
-
42
-
43
- if __name__ == "__main__":
44
- main()
@@ -1,19 +0,0 @@
1
- #!/usr/bin/env python3
2
- """List available team labels via the FlyDocs Relay API."""
3
-
4
- import sys
5
- from pathlib import Path
6
-
7
- sys.path.insert(0, str(Path(__file__).parent))
8
- from flydocs_api import get_client, output_json
9
-
10
-
11
- def main():
12
- client = get_client()
13
- result = client.get("/labels")
14
-
15
- output_json(result)
16
-
17
-
18
- if __name__ == "__main__":
19
- main()
@@ -1,28 +0,0 @@
1
- #!/usr/bin/env python3
2
- """List milestones via the FlyDocs Relay API."""
3
-
4
- import argparse
5
- import sys
6
- from pathlib import Path
7
-
8
- sys.path.insert(0, str(Path(__file__).parent))
9
- from flydocs_api import get_client, output_json
10
-
11
-
12
- def main():
13
- parser = argparse.ArgumentParser(description="List milestones")
14
- parser.add_argument("--all", action="store_true", dest="show_all")
15
- args = parser.parse_args()
16
-
17
- params: dict = {}
18
- if args.show_all:
19
- params["all"] = "true"
20
-
21
- client = get_client()
22
- result = client.get("/milestones", params=params)
23
-
24
- output_json(result)
25
-
26
-
27
- if __name__ == "__main__":
28
- main()
@@ -1,31 +0,0 @@
1
- #!/usr/bin/env python3
2
- """List projects via the FlyDocs Relay API."""
3
-
4
- import argparse
5
- import sys
6
- from pathlib import Path
7
-
8
- sys.path.insert(0, str(Path(__file__).parent))
9
- from flydocs_api import get_client, output_json
10
-
11
-
12
- def main():
13
- parser = argparse.ArgumentParser(description="List projects")
14
- parser.add_argument("--active", action="store_true")
15
- parser.add_argument("--all", action="store_true", dest="show_all")
16
- args = parser.parse_args()
17
-
18
- params: dict = {}
19
- if args.active:
20
- params["active"] = "true"
21
- if args.show_all:
22
- params["all"] = "true"
23
-
24
- client = get_client()
25
- result = client.get("/projects", params=params)
26
-
27
- output_json(result)
28
-
29
-
30
- if __name__ == "__main__":
31
- main()
@@ -1,19 +0,0 @@
1
- #!/usr/bin/env python3
2
- """List available providers via the FlyDocs Relay API."""
3
-
4
- import sys
5
- from pathlib import Path
6
-
7
- sys.path.insert(0, str(Path(__file__).parent))
8
- from flydocs_api import get_client, output_json
9
-
10
-
11
- def main():
12
- client = get_client()
13
- result = client.get("/providers")
14
-
15
- output_json(result)
16
-
17
-
18
- if __name__ == "__main__":
19
- main()
@@ -1,19 +0,0 @@
1
- #!/usr/bin/env python3
2
- """List available teams via the FlyDocs Relay API."""
3
-
4
- import sys
5
- from pathlib import Path
6
-
7
- sys.path.insert(0, str(Path(__file__).parent))
8
- from flydocs_api import get_client, output_json
9
-
10
-
11
- def main():
12
- client = get_client()
13
- result = client.get("/teams")
14
-
15
- output_json(result)
16
-
17
-
18
- if __name__ == "__main__":
19
- main()
@@ -1,29 +0,0 @@
1
- #!/usr/bin/env python3
2
- """Set priority on an issue via the FlyDocs Relay API."""
3
-
4
- import sys
5
- from pathlib import Path
6
-
7
- sys.path.insert(0, str(Path(__file__).parent))
8
- from flydocs_api import get_client, output_json, fail
9
-
10
- if len(sys.argv) < 3:
11
- fail("Usage: priority.py <ref> <0-4>")
12
-
13
- ref = sys.argv[1]
14
- try:
15
- priority = int(sys.argv[2])
16
- except ValueError:
17
- fail("Priority must be a number (0-4)")
18
-
19
- if priority not in range(5):
20
- fail("Priority must be 0 (none), 1 (urgent), 2 (high), 3 (medium), or 4 (low)")
21
-
22
- client = get_client()
23
- result = client.put(f"/issues/{ref}/priority", {"priority": priority})
24
-
25
- output_json({
26
- "success": result.get("success", True),
27
- "issue": result.get("issue", ref),
28
- "priority": result.get("priority", priority),
29
- })
@@ -1,45 +0,0 @@
1
- #!/usr/bin/env python3
2
- """Post a project update via the FlyDocs Relay API."""
3
-
4
- import argparse
5
- import sys
6
- from pathlib import Path
7
-
8
- sys.path.insert(0, str(Path(__file__).parent))
9
- from flydocs_api import get_client, output_json, fail
10
-
11
-
12
- def main():
13
- parser = argparse.ArgumentParser(description="Post project update")
14
- parser.add_argument("--health", required=True, choices=["onTrack", "atRisk", "offTrack"])
15
- parser.add_argument("--body", default=None)
16
- parser.add_argument("--body-file", default=None, dest="body_file")
17
- args = parser.parse_args()
18
-
19
- # Resolve body: --body-file > stdin > --body
20
- body = args.body
21
- if args.body_file:
22
- try:
23
- body = Path(args.body_file).read_text()
24
- except FileNotFoundError:
25
- fail(f"File not found: {args.body_file}")
26
- elif body is None and not sys.stdin.isatty():
27
- body = sys.stdin.read().strip()
28
-
29
- if not body:
30
- fail("Provide body via --body, --body-file, or stdin")
31
-
32
- client = get_client()
33
- result = client.post("/projects/update", {
34
- "health": args.health,
35
- "body": body,
36
- })
37
-
38
- output_json({
39
- "success": result.get("success", True),
40
- "id": result.get("id", ""),
41
- })
42
-
43
-
44
- if __name__ == "__main__":
45
- main()
@@ -1,68 +0,0 @@
1
- #!/usr/bin/env python3
2
- """Set label config on the relay API key.
3
-
4
- Configures default labels and type-to-label mapping for automatic label
5
- application during issue creation.
6
-
7
- Usage:
8
- set_labels.py --defaults '["app"]' --type-map '{"feature":["Feature"],...}'
9
- echo '{"defaults":["app"],"typeMap":{...}}' | set_labels.py
10
- """
11
-
12
- import argparse
13
- import json
14
- import sys
15
- from pathlib import Path
16
-
17
- sys.path.insert(0, str(Path(__file__).parent))
18
- from flydocs_api import get_client, output_json, fail
19
-
20
-
21
- def main():
22
- parser = argparse.ArgumentParser(description="Set label config on relay")
23
- parser.add_argument("--defaults", default=None, help="JSON array of default label names")
24
- parser.add_argument("--type-map", default=None, dest="type_map", help="JSON object mapping issue types to label arrays")
25
- args = parser.parse_args()
26
-
27
- # Build body from flags or stdin
28
- if args.defaults is not None or args.type_map is not None:
29
- body: dict = {}
30
- if args.defaults is not None:
31
- try:
32
- body["defaults"] = json.loads(args.defaults)
33
- except json.JSONDecodeError:
34
- fail("Invalid JSON for --defaults")
35
- if args.type_map is not None:
36
- try:
37
- body["typeMap"] = json.loads(args.type_map)
38
- except json.JSONDecodeError:
39
- fail("Invalid JSON for --type-map")
40
- elif not sys.stdin.isatty():
41
- try:
42
- body = json.loads(sys.stdin.read().strip())
43
- except json.JSONDecodeError:
44
- fail("Invalid JSON on stdin")
45
- else:
46
- fail("Provide --defaults/--type-map flags or pipe JSON via stdin")
47
-
48
- client = get_client()
49
- result = client.post("/auth/labels", body)
50
-
51
- # Store label config in local config as reference
52
- config_path = client.config_path
53
- if config_path.exists():
54
- with open(config_path, "r") as f:
55
- config = json.load(f)
56
- config["labels"] = {
57
- "defaults": body.get("defaults", []),
58
- "typeMap": body.get("typeMap", {}),
59
- }
60
- with open(config_path, "w") as f:
61
- json.dump(config, f, indent=2)
62
- f.write("\n")
63
-
64
- output_json(result)
65
-
66
-
67
- if __name__ == "__main__":
68
- main()