@flydocs/cli 0.5.0-beta.9 → 0.6.0-alpha.10
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/README.md +6 -0
- package/dist/cli.js +1553 -414
- package/package.json +1 -1
- package/template/.claude/CLAUDE.md +11 -9
- package/template/.claude/agents/implementation-agent.md +0 -1
- package/template/.claude/agents/pm-agent.md +0 -1
- package/template/.claude/agents/research-agent.md +0 -1
- package/template/.claude/agents/review-agent.md +0 -1
- package/template/.claude/commands/flydocs-setup.md +202 -35
- package/template/.claude/commands/flydocs-upgrade.md +342 -0
- package/template/.claude/commands/knowledge.md +61 -0
- package/template/.claude/skills/flydocs-cloud/SKILL.md +66 -39
- package/template/.claude/skills/flydocs-cloud/cursor-rule.mdc +5 -5
- package/template/.claude/skills/flydocs-cloud/scripts/assign.py +17 -27
- package/template/.claude/skills/flydocs-cloud/scripts/assign_cycle.py +14 -30
- package/template/.claude/skills/flydocs-cloud/scripts/assign_milestone.py +10 -32
- package/template/.claude/skills/flydocs-cloud/scripts/comment.py +15 -25
- package/template/.claude/skills/flydocs-cloud/scripts/create_issue.py +42 -59
- package/template/.claude/skills/flydocs-cloud/scripts/create_milestone.py +26 -37
- package/template/.claude/skills/flydocs-cloud/scripts/create_project.py +24 -31
- package/template/.claude/skills/flydocs-cloud/scripts/create_team.py +39 -0
- package/template/.claude/skills/flydocs-cloud/scripts/delete_milestone.py +21 -0
- package/template/.claude/skills/flydocs-cloud/scripts/estimate.py +17 -22
- package/template/.claude/skills/flydocs-cloud/scripts/flydocs_api.py +113 -169
- package/template/.claude/skills/flydocs-cloud/scripts/get_estimate_scale.py +23 -0
- package/template/.claude/skills/flydocs-cloud/scripts/get_issue.py +6 -59
- package/template/.claude/skills/flydocs-cloud/scripts/link.py +16 -35
- package/template/.claude/skills/flydocs-cloud/scripts/list_cycles.py +21 -28
- package/template/.claude/skills/flydocs-cloud/scripts/list_issues.py +16 -77
- package/template/.claude/skills/flydocs-cloud/scripts/list_labels.py +19 -0
- package/template/.claude/skills/flydocs-cloud/scripts/list_milestones.py +21 -33
- package/template/.claude/skills/flydocs-cloud/scripts/list_projects.py +24 -38
- package/template/.claude/skills/flydocs-cloud/scripts/list_providers.py +19 -0
- package/template/.claude/skills/flydocs-cloud/scripts/list_statuses.py +19 -0
- package/template/.claude/skills/flydocs-cloud/scripts/list_teams.py +19 -0
- package/template/.claude/skills/flydocs-cloud/scripts/priority.py +10 -19
- package/template/.claude/skills/flydocs-cloud/scripts/project_update.py +36 -50
- package/template/.claude/skills/flydocs-cloud/scripts/refresh_labels.py +87 -0
- package/template/.claude/skills/flydocs-cloud/scripts/set_identity.py +38 -0
- package/template/.claude/skills/flydocs-cloud/scripts/set_labels.py +68 -0
- package/template/.claude/skills/flydocs-cloud/scripts/set_preferences.py +49 -0
- package/template/.claude/skills/flydocs-cloud/scripts/set_provider.py +46 -0
- package/template/.claude/skills/flydocs-cloud/scripts/set_status_mapping.py +69 -0
- package/template/.claude/skills/flydocs-cloud/scripts/set_team.py +42 -0
- package/template/.claude/skills/flydocs-cloud/scripts/transition.py +11 -52
- package/template/.claude/skills/flydocs-cloud/scripts/update_description.py +16 -27
- package/template/.claude/skills/flydocs-cloud/scripts/update_issue.py +43 -54
- package/template/.claude/skills/flydocs-cloud/scripts/update_milestone.py +42 -0
- package/template/.claude/skills/flydocs-cloud/scripts/validate_setup.py +139 -0
- package/template/.claude/skills/flydocs-local/SKILL.md +1 -1
- package/template/.claude/skills/flydocs-local/scripts/assign.py +13 -4
- package/template/.claude/skills/flydocs-local/scripts/flydocs_api.py +5 -2
- package/template/.claude/skills/flydocs-workflow/SKILL.md +23 -18
- package/template/.claude/skills/flydocs-workflow/reference/comment-templates.md +1 -0
- package/template/.claude/skills/flydocs-workflow/reference/pr-workflow.md +105 -0
- package/template/.claude/skills/flydocs-workflow/reference/priority-estimates.md +37 -15
- package/template/.claude/skills/flydocs-workflow/session.md +24 -16
- package/template/.claude/skills/flydocs-workflow/stages/capture.md +8 -3
- package/template/.claude/skills/flydocs-workflow/stages/close.md +4 -3
- package/template/.claude/skills/flydocs-workflow/stages/implement.md +28 -4
- package/template/.claude/skills/flydocs-workflow/stages/refine.md +20 -4
- package/template/.claude/skills/flydocs-workflow/stages/review.md +14 -2
- package/template/.env.example +16 -7
- package/template/.flydocs/config.json +4 -18
- package/template/.flydocs/hooks/prompt-submit.py +27 -4
- package/template/.flydocs/version +1 -1
- package/template/AGENTS.md +8 -8
- package/template/CHANGELOG.md +183 -0
- package/template/flydocs/knowledge/INDEX.md +38 -53
- package/template/flydocs/knowledge/README.md +60 -9
- package/template/flydocs/knowledge/templates/decision.md +47 -0
- package/template/flydocs/knowledge/templates/feature.md +35 -0
- package/template/flydocs/knowledge/templates/note.md +25 -0
- package/template/manifest.json +12 -4
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
|
-
"""Assign an issue
|
|
2
|
+
"""Assign a cycle to an issue via the FlyDocs Relay API."""
|
|
3
3
|
|
|
4
4
|
import sys
|
|
5
5
|
from pathlib import Path
|
|
@@ -11,34 +11,18 @@ if len(sys.argv) < 2:
|
|
|
11
11
|
fail("Usage: assign_cycle.py <ref> [cycle_id]")
|
|
12
12
|
|
|
13
13
|
ref = sys.argv[1]
|
|
14
|
+
cycle_id = sys.argv[2] if len(sys.argv) >= 3 else None
|
|
15
|
+
|
|
14
16
|
client = get_client()
|
|
15
17
|
|
|
16
|
-
|
|
17
|
-
if
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
cycle_id = cycle["id"]
|
|
28
|
-
cycle_name = cycle["name"]
|
|
29
|
-
|
|
30
|
-
result = client.query(
|
|
31
|
-
"""mutation($id: String!, $cycleId: String!) {
|
|
32
|
-
issueUpdate(id: $id, input: { cycleId: $cycleId }) {
|
|
33
|
-
success
|
|
34
|
-
issue { identifier }
|
|
35
|
-
}
|
|
36
|
-
}""",
|
|
37
|
-
{"id": issue_uuid, "cycleId": cycle_id},
|
|
38
|
-
)
|
|
39
|
-
|
|
40
|
-
if not result.get("data", {}).get("issueUpdate", {}).get("success"):
|
|
41
|
-
fail(f"Failed to assign cycle: {result}")
|
|
42
|
-
|
|
43
|
-
issue = result["data"]["issueUpdate"]["issue"]
|
|
44
|
-
output_json({"success": True, "issue": issue["identifier"], "cycle": cycle_name})
|
|
18
|
+
body: dict = {}
|
|
19
|
+
if cycle_id:
|
|
20
|
+
body["cycleId"] = cycle_id
|
|
21
|
+
|
|
22
|
+
result = client.put(f"/issues/{ref}/cycle", body)
|
|
23
|
+
|
|
24
|
+
output_json({
|
|
25
|
+
"success": result.get("success", True),
|
|
26
|
+
"issue": result.get("issue", ref),
|
|
27
|
+
"cycle": result.get("cycle", ""),
|
|
28
|
+
})
|
|
@@ -1,44 +1,22 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
|
-
"""Assign an issue
|
|
2
|
+
"""Assign a milestone to an issue via the FlyDocs Relay API."""
|
|
3
3
|
|
|
4
|
-
import argparse
|
|
5
4
|
import sys
|
|
6
5
|
from pathlib import Path
|
|
7
6
|
|
|
8
7
|
sys.path.insert(0, str(Path(__file__).parent))
|
|
9
8
|
from flydocs_api import get_client, output_json, fail
|
|
10
9
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
parser.add_argument("milestone_id", nargs="?", default=None,
|
|
14
|
-
help="Milestone UUID (positional)")
|
|
15
|
-
parser.add_argument("--milestone", dest="milestone_flag", default=None,
|
|
16
|
-
help="Milestone UUID (flag)")
|
|
17
|
-
args = parser.parse_args()
|
|
18
|
-
|
|
19
|
-
milestone_id = args.milestone_flag or args.milestone_id
|
|
20
|
-
if not milestone_id:
|
|
21
|
-
fail("Milestone ID required. Usage: assign_milestone.py <ref> <milestone_id> or --milestone <id>")
|
|
10
|
+
if len(sys.argv) < 3:
|
|
11
|
+
fail("Usage: assign_milestone.py <ref> <milestone_id>")
|
|
22
12
|
|
|
13
|
+
ref, milestone_id = sys.argv[1], sys.argv[2]
|
|
23
14
|
client = get_client()
|
|
24
15
|
|
|
25
|
-
|
|
26
|
-
if not issue_uuid:
|
|
27
|
-
fail(f"Issue not found: {args.ref}")
|
|
28
|
-
|
|
29
|
-
result = client.query(
|
|
30
|
-
"""mutation($id: String!, $milestoneId: String!) {
|
|
31
|
-
issueUpdate(id: $id, input: { projectMilestoneId: $milestoneId }) {
|
|
32
|
-
success
|
|
33
|
-
issue { identifier }
|
|
34
|
-
}
|
|
35
|
-
}""",
|
|
36
|
-
{"id": issue_uuid, "milestoneId": milestone_id},
|
|
37
|
-
)
|
|
38
|
-
|
|
39
|
-
update = result.get("data", {}).get("issueUpdate", {})
|
|
40
|
-
if not update.get("success"):
|
|
41
|
-
fail(f"Failed to assign milestone: {result}")
|
|
16
|
+
result = client.put(f"/issues/{ref}/milestone", {"milestoneId": milestone_id})
|
|
42
17
|
|
|
43
|
-
|
|
44
|
-
|
|
18
|
+
output_json({
|
|
19
|
+
"success": result.get("success", True),
|
|
20
|
+
"issue": result.get("issue", ref),
|
|
21
|
+
"milestone": result.get("milestone", ""),
|
|
22
|
+
})
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
|
-
"""Add a comment to an issue."""
|
|
2
|
+
"""Add a comment to an issue via the FlyDocs Relay API."""
|
|
3
3
|
|
|
4
4
|
import sys
|
|
5
5
|
from pathlib import Path
|
|
@@ -8,32 +8,22 @@ sys.path.insert(0, str(Path(__file__).parent))
|
|
|
8
8
|
from flydocs_api import get_client, output_json, fail
|
|
9
9
|
|
|
10
10
|
if len(sys.argv) < 2:
|
|
11
|
-
fail("Usage: comment.py <ref> [
|
|
11
|
+
fail("Usage: comment.py <ref> [comment] | stdin")
|
|
12
12
|
|
|
13
13
|
ref = sys.argv[1]
|
|
14
|
-
body = sys.argv[2] if len(sys.argv) > 2 else ""
|
|
15
|
-
if not body and not sys.stdin.isatty():
|
|
16
|
-
body = sys.stdin.read().strip()
|
|
17
|
-
if not body:
|
|
18
|
-
fail("Provide comment as argument or pipe via stdin")
|
|
19
|
-
client = get_client()
|
|
20
|
-
|
|
21
|
-
issue_uuid = client.resolve_issue_id(ref)
|
|
22
|
-
if not issue_uuid:
|
|
23
|
-
fail(f"Issue not found: {ref}")
|
|
24
14
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
{"id": issue_uuid, "body": body},
|
|
33
|
-
)
|
|
15
|
+
# Comment from arg > stdin
|
|
16
|
+
if len(sys.argv) >= 3:
|
|
17
|
+
body = sys.argv[2]
|
|
18
|
+
elif not sys.stdin.isatty():
|
|
19
|
+
body = sys.stdin.read().strip()
|
|
20
|
+
else:
|
|
21
|
+
fail("Provide comment as argument or via stdin")
|
|
34
22
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
fail(f"Failed to add comment: {result}")
|
|
23
|
+
client = get_client()
|
|
24
|
+
result = client.post(f"/issues/{ref}/comment", {"body": body})
|
|
38
25
|
|
|
39
|
-
output_json({
|
|
26
|
+
output_json({
|
|
27
|
+
"success": result.get("success", True),
|
|
28
|
+
"commentId": result.get("commentId", ""),
|
|
29
|
+
})
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
|
-
"""Create a new issue
|
|
2
|
+
"""Create a new issue via the FlyDocs Relay API."""
|
|
3
3
|
|
|
4
4
|
import argparse
|
|
5
5
|
import sys
|
|
@@ -15,10 +15,12 @@ def main():
|
|
|
15
15
|
parser.add_argument("--type", required=True, choices=["feature", "bug", "chore", "idea"], dest="issue_type")
|
|
16
16
|
parser.add_argument("--description", default="")
|
|
17
17
|
parser.add_argument("--description-file", default="", dest="description_file")
|
|
18
|
-
parser.add_argument("--priority", type=int, default=3,
|
|
19
|
-
parser.add_argument("--estimate", type=int, default=0,
|
|
18
|
+
parser.add_argument("--priority", type=int, default=3, help="Priority (0-4, relay translates per provider)")
|
|
19
|
+
parser.add_argument("--estimate", type=int, default=0, help="Estimate points (relay translates per provider)")
|
|
20
20
|
parser.add_argument("--assignee", default=None)
|
|
21
|
-
parser.add_argument("--project", default=None)
|
|
21
|
+
parser.add_argument("--project", default=None, help="Project ID")
|
|
22
|
+
parser.add_argument("--labels", default=None, help="Comma-separated ad-hoc label names")
|
|
23
|
+
parser.add_argument("--milestone", default=None, help="Milestone ID or name (resolved by name lookup)")
|
|
22
24
|
parser.add_argument("--triage", action="store_true")
|
|
23
25
|
args = parser.parse_args()
|
|
24
26
|
|
|
@@ -32,68 +34,49 @@ def main():
|
|
|
32
34
|
elif not description and not sys.stdin.isatty():
|
|
33
35
|
description = sys.stdin.read().strip()
|
|
34
36
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
issue_input = {
|
|
38
|
-
"teamId": client.team_id,
|
|
37
|
+
body: dict = {
|
|
39
38
|
"title": args.title,
|
|
40
|
-
"
|
|
39
|
+
"type": args.issue_type,
|
|
41
40
|
"priority": args.priority,
|
|
42
41
|
}
|
|
42
|
+
if description:
|
|
43
|
+
body["description"] = description
|
|
43
44
|
if args.estimate:
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
# Labels
|
|
47
|
-
label_ids = []
|
|
48
|
-
cat_id = client.get_category_label_id(args.issue_type)
|
|
49
|
-
if cat_id:
|
|
50
|
-
label_ids.append(cat_id)
|
|
51
|
-
if args.triage:
|
|
52
|
-
triage_id = client.get_other_label_id("triage")
|
|
53
|
-
if triage_id:
|
|
54
|
-
label_ids.append(triage_id)
|
|
55
|
-
if label_ids:
|
|
56
|
-
issue_input["labelIds"] = label_ids
|
|
57
|
-
|
|
58
|
-
# Project
|
|
59
|
-
project_id = args.project
|
|
60
|
-
if not project_id:
|
|
61
|
-
active = client.workspace.get("activeProjects", [])
|
|
62
|
-
if active:
|
|
63
|
-
project_id = active[0]
|
|
64
|
-
if project_id:
|
|
65
|
-
issue_input["projectId"] = project_id
|
|
66
|
-
|
|
67
|
-
# Assignee
|
|
45
|
+
body["estimate"] = args.estimate
|
|
68
46
|
if args.assignee:
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
issue { id identifier title url }
|
|
77
|
-
}
|
|
78
|
-
}"""
|
|
79
|
-
|
|
80
|
-
result = client.query(mutation, {"input": issue_input})
|
|
47
|
+
body["assignee"] = args.assignee
|
|
48
|
+
if args.project:
|
|
49
|
+
body["projectId"] = args.project
|
|
50
|
+
if args.labels:
|
|
51
|
+
body["labels"] = [l.strip() for l in args.labels.split(",") if l.strip()]
|
|
52
|
+
if args.triage:
|
|
53
|
+
body["triage"] = True
|
|
81
54
|
|
|
82
|
-
|
|
83
|
-
if not data.get("success"):
|
|
84
|
-
# Retry without labels if team mismatch (stale label IDs in config)
|
|
85
|
-
errors = result.get("errors", [])
|
|
86
|
-
label_error = any("label" in (e.get("message", "") or "").lower() for e in errors)
|
|
87
|
-
if label_error and "labelIds" in issue_input:
|
|
88
|
-
print("Label IDs rejected — retrying without labels...", file=sys.stderr)
|
|
89
|
-
del issue_input["labelIds"]
|
|
90
|
-
result = client.query(mutation, {"input": issue_input})
|
|
91
|
-
data = result.get("data", {}).get("issueCreate", {})
|
|
92
|
-
if not data.get("success"):
|
|
93
|
-
fail(f"Failed to create issue: {result}")
|
|
55
|
+
client = get_client()
|
|
94
56
|
|
|
95
|
-
|
|
96
|
-
|
|
57
|
+
if args.milestone:
|
|
58
|
+
milestone_id = args.milestone
|
|
59
|
+
# If it doesn't look like a UUID, resolve by name
|
|
60
|
+
if len(milestone_id) != 36 or "-" not in milestone_id:
|
|
61
|
+
milestones = client.get("/milestones")
|
|
62
|
+
match = None
|
|
63
|
+
for m in milestones:
|
|
64
|
+
if m["name"].lower() == milestone_id.lower():
|
|
65
|
+
match = m
|
|
66
|
+
break
|
|
67
|
+
if not match:
|
|
68
|
+
fail(f"Milestone not found: {milestone_id}")
|
|
69
|
+
milestone_id = match["id"]
|
|
70
|
+
body["milestoneId"] = milestone_id
|
|
71
|
+
|
|
72
|
+
result = client.post("/issues", body)
|
|
73
|
+
|
|
74
|
+
output_json({
|
|
75
|
+
"id": result["id"],
|
|
76
|
+
"identifier": result["identifier"],
|
|
77
|
+
"title": result["title"],
|
|
78
|
+
"url": result["url"],
|
|
79
|
+
})
|
|
97
80
|
|
|
98
81
|
|
|
99
82
|
if __name__ == "__main__":
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
|
-
"""Create a milestone."""
|
|
2
|
+
"""Create a milestone via the FlyDocs Relay API."""
|
|
3
3
|
|
|
4
4
|
import argparse
|
|
5
5
|
import sys
|
|
@@ -8,39 +8,28 @@ from pathlib import Path
|
|
|
8
8
|
sys.path.insert(0, str(Path(__file__).parent))
|
|
9
9
|
from flydocs_api import get_client, output_json, fail
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
parser
|
|
14
|
-
parser.add_argument("--
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
if
|
|
21
|
-
|
|
22
|
-
if
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
}
|
|
37
|
-
}""",
|
|
38
|
-
{"input": milestone_input},
|
|
39
|
-
)
|
|
40
|
-
|
|
41
|
-
data = result.get("data", {}).get("projectMilestoneCreate", {})
|
|
42
|
-
if not data.get("success"):
|
|
43
|
-
fail(f"Failed to create milestone: {result}")
|
|
44
|
-
|
|
45
|
-
ms = data["projectMilestone"]
|
|
46
|
-
output_json({"id": ms["id"], "name": ms["name"]})
|
|
11
|
+
|
|
12
|
+
def main():
|
|
13
|
+
parser = argparse.ArgumentParser(description="Create milestone")
|
|
14
|
+
parser.add_argument("--name", required=True)
|
|
15
|
+
parser.add_argument("--project", default=None, help="Project ID")
|
|
16
|
+
parser.add_argument("--target-date", default=None, dest="target_date")
|
|
17
|
+
args = parser.parse_args()
|
|
18
|
+
|
|
19
|
+
body: dict = {"name": args.name}
|
|
20
|
+
if args.project:
|
|
21
|
+
body["projectId"] = args.project
|
|
22
|
+
if args.target_date:
|
|
23
|
+
body["targetDate"] = args.target_date
|
|
24
|
+
|
|
25
|
+
client = get_client()
|
|
26
|
+
result = client.post("/milestones", body)
|
|
27
|
+
|
|
28
|
+
output_json({
|
|
29
|
+
"id": result["id"],
|
|
30
|
+
"name": result["name"],
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
if __name__ == "__main__":
|
|
35
|
+
main()
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
|
-
"""Create a
|
|
2
|
+
"""Create a project via the FlyDocs Relay API."""
|
|
3
3
|
|
|
4
4
|
import argparse
|
|
5
5
|
import sys
|
|
@@ -8,33 +8,26 @@ from pathlib import Path
|
|
|
8
8
|
sys.path.insert(0, str(Path(__file__).parent))
|
|
9
9
|
from flydocs_api import get_client, output_json, fail
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
parser.
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
)
|
|
34
|
-
|
|
35
|
-
data = result.get("data", {}).get("projectCreate", {})
|
|
36
|
-
if not data.get("success"):
|
|
37
|
-
fail(f"Failed to create project: {result}")
|
|
38
|
-
|
|
39
|
-
project = data["project"]
|
|
40
|
-
output_json({"id": project["id"], "name": project["name"], "url": project["url"]})
|
|
11
|
+
|
|
12
|
+
def main():
|
|
13
|
+
parser = argparse.ArgumentParser(description="Create project")
|
|
14
|
+
parser.add_argument("--name", required=True)
|
|
15
|
+
parser.add_argument("--description", default=None)
|
|
16
|
+
args = parser.parse_args()
|
|
17
|
+
|
|
18
|
+
body: dict = {"name": args.name}
|
|
19
|
+
if args.description:
|
|
20
|
+
body["description"] = args.description
|
|
21
|
+
|
|
22
|
+
client = get_client()
|
|
23
|
+
result = client.post("/projects", body)
|
|
24
|
+
|
|
25
|
+
output_json({
|
|
26
|
+
"id": result["id"],
|
|
27
|
+
"name": result["name"],
|
|
28
|
+
"url": result.get("url", ""),
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
if __name__ == "__main__":
|
|
33
|
+
main()
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Create a team/project 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 or project")
|
|
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 for sub-team (Linear only, ignored for Jira)")
|
|
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()
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Delete a milestone 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) < 2:
|
|
11
|
+
fail("Usage: delete_milestone.py <milestone_id>")
|
|
12
|
+
|
|
13
|
+
milestone_id = sys.argv[1]
|
|
14
|
+
|
|
15
|
+
client = get_client()
|
|
16
|
+
result = client.delete(f"/milestones/{milestone_id}")
|
|
17
|
+
|
|
18
|
+
output_json({
|
|
19
|
+
"success": result.get("success", True),
|
|
20
|
+
"id": result.get("id", milestone_id),
|
|
21
|
+
})
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
|
-
"""Set estimate on an issue.
|
|
2
|
+
"""Set estimate on an issue via the FlyDocs Relay API.
|
|
3
|
+
|
|
4
|
+
The relay validates the estimate against the provider's scale server-side.
|
|
5
|
+
Use get_estimate_scale.py to discover valid values before setting.
|
|
6
|
+
"""
|
|
3
7
|
|
|
4
8
|
import sys
|
|
5
9
|
from pathlib import Path
|
|
@@ -8,31 +12,22 @@ sys.path.insert(0, str(Path(__file__).parent))
|
|
|
8
12
|
from flydocs_api import get_client, output_json, fail
|
|
9
13
|
|
|
10
14
|
if len(sys.argv) < 3:
|
|
11
|
-
fail("Usage: estimate.py <ref> <
|
|
15
|
+
fail("Usage: estimate.py <ref> <points>")
|
|
12
16
|
|
|
13
17
|
ref = sys.argv[1]
|
|
14
18
|
try:
|
|
15
19
|
estimate = int(sys.argv[2])
|
|
16
20
|
except ValueError:
|
|
17
|
-
fail("Estimate must be a number
|
|
21
|
+
fail("Estimate must be a number")
|
|
22
|
+
|
|
23
|
+
if estimate < 0:
|
|
24
|
+
fail("Estimate must be a non-negative integer")
|
|
18
25
|
|
|
19
26
|
client = get_client()
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
result
|
|
25
|
-
""
|
|
26
|
-
|
|
27
|
-
success
|
|
28
|
-
issue { identifier estimate }
|
|
29
|
-
}
|
|
30
|
-
}""",
|
|
31
|
-
{"id": issue_uuid, "estimate": estimate},
|
|
32
|
-
)
|
|
33
|
-
|
|
34
|
-
if not result.get("data", {}).get("issueUpdate", {}).get("success"):
|
|
35
|
-
fail(f"Failed to set estimate: {result}")
|
|
36
|
-
|
|
37
|
-
issue = result["data"]["issueUpdate"]["issue"]
|
|
38
|
-
output_json({"success": True, "issue": issue["identifier"], "estimate": issue["estimate"]})
|
|
27
|
+
result = client.put(f"/issues/{ref}/estimate", {"estimate": estimate})
|
|
28
|
+
|
|
29
|
+
output_json({
|
|
30
|
+
"success": result.get("success", True),
|
|
31
|
+
"issue": result.get("issue", ref),
|
|
32
|
+
"estimate": result.get("estimate", estimate),
|
|
33
|
+
})
|