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

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 +705 -393
  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,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 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()
@@ -1,41 +0,0 @@
1
- #!/usr/bin/env python3
2
- """Set team preference via the FlyDocs Relay API.
3
-
4
- Stores the team preference on the relay (for server-side scoping)
5
- and updates the local config (for display/reference).
6
- """
7
-
8
- import argparse
9
- import json
10
- import sys
11
- from pathlib import Path
12
-
13
- sys.path.insert(0, str(Path(__file__).parent))
14
- from flydocs_api import get_client, output_json, fail
15
-
16
-
17
- def main():
18
- parser = argparse.ArgumentParser(description="Set team preference")
19
- parser.add_argument("team_id", help="Linear team UUID")
20
- args = parser.parse_args()
21
-
22
- client = get_client()
23
- result = client.post("/auth/team", {"teamId": args.team_id})
24
-
25
- # Update local config with team ID
26
- config_path = client.config_path
27
- if config_path.exists():
28
- with open(config_path, "r") as f:
29
- config = json.load(f)
30
- if "provider" not in config:
31
- config["provider"] = {"type": "linear", "teamId": None}
32
- config["provider"]["teamId"] = args.team_id
33
- with open(config_path, "w") as f:
34
- json.dump(config, f, indent=2)
35
- f.write("\n")
36
-
37
- output_json(result)
38
-
39
-
40
- if __name__ == "__main__":
41
- main()
@@ -1,26 +0,0 @@
1
- #!/usr/bin/env python3
2
- """Transition an issue to a new status with a required comment."""
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: transition.py <ref> <STATUS> <comment>")
12
-
13
- ref, status, comment = sys.argv[1], sys.argv[2].upper(), sys.argv[3]
14
- client = get_client()
15
-
16
- result = client.post(f"/issues/{ref}/transition", {
17
- "status": status,
18
- "comment": comment,
19
- })
20
-
21
- output_json({
22
- "success": result.get("success", True),
23
- "issue": result.get("issue", ref),
24
- "previousStatus": result.get("previousStatus", ""),
25
- "newStatus": result.get("newStatus", status),
26
- })