@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.
- package/dist/cli.js +705 -393
- package/package.json +1 -1
- package/template/.claude/CLAUDE.md +62 -63
- package/template/.claude/agents/implementation-agent.md +1 -1
- package/template/.claude/agents/pm-agent.md +1 -1
- package/template/.claude/commands/activate.md +1 -1
- package/template/.claude/commands/attach.md +1 -1
- package/template/.claude/commands/block.md +2 -2
- package/template/.claude/commands/capture.md +1 -1
- package/template/.claude/commands/close.md +1 -1
- package/template/.claude/commands/flydocs-setup.md +387 -74
- package/template/.claude/commands/flydocs-upgrade.md +48 -37
- package/template/.claude/commands/implement.md +1 -1
- package/template/.claude/commands/knowledge.md +61 -0
- package/template/.claude/commands/new-project.md +1 -1
- package/template/.claude/commands/onboard.md +275 -0
- package/template/.claude/commands/project-update.md +1 -1
- package/template/.claude/commands/refine.md +1 -1
- package/template/.claude/commands/review.md +1 -1
- package/template/.claude/commands/start-session.md +1 -1
- package/template/.claude/commands/status.md +1 -1
- package/template/.claude/commands/validate.md +1 -1
- package/template/.claude/commands/wrap-session.md +1 -1
- package/template/.claude/hooks/auto-approve.py +132 -0
- package/template/.claude/hooks/post-pr-check.py +108 -0
- package/template/.claude/hooks/post-transition-check.py +94 -0
- package/template/.claude/hooks/prompt-submit.py +513 -0
- package/template/.claude/hooks/session-start.py +146 -0
- package/template/.claude/hooks/stop-gate.py +109 -0
- package/template/.claude/settings.json +41 -4
- package/template/.claude/skills/README.md +23 -25
- package/template/.claude/skills/flydocs-workflow/SKILL.md +134 -42
- package/template/.claude/skills/flydocs-workflow/cursor-rule.mdc +9 -8
- package/template/.claude/skills/flydocs-workflow/reference/comment-templates.md +1 -0
- package/template/.claude/skills/flydocs-workflow/reference/golden-rules.md +28 -17
- package/template/.claude/skills/flydocs-workflow/reference/graph-schema.md +116 -0
- package/template/.claude/skills/flydocs-workflow/reference/pr-workflow.md +120 -0
- package/template/.claude/skills/flydocs-workflow/reference/priority-estimates.md +37 -15
- package/template/.claude/skills/flydocs-workflow/reference/service-descriptor-schema.md +251 -0
- package/template/.claude/skills/flydocs-workflow/reference/status-workflow.md +26 -26
- package/template/.claude/skills/flydocs-workflow/scripts/_local/__init__.py +0 -0
- package/template/.claude/skills/{flydocs-local/scripts/flydocs_api.py → flydocs-workflow/scripts/_local/file_store.py} +137 -47
- package/template/.claude/skills/flydocs-workflow/scripts/flydocs_api.py +693 -0
- package/template/{.flydocs → .claude/skills/flydocs-workflow}/scripts/generate_manifest.py +4 -4
- package/template/.claude/skills/{flydocs-context-graph → flydocs-workflow}/scripts/graph_build.py +132 -1
- package/template/.claude/skills/{flydocs-context-graph → flydocs-workflow}/scripts/graph_query.py +18 -5
- package/template/.claude/skills/{flydocs-context-graph → flydocs-workflow}/scripts/graph_session.py +1 -10
- package/template/.claude/skills/{flydocs-context-graph → flydocs-workflow}/scripts/graph_update.py +4 -4
- package/template/.claude/skills/{flydocs-context-graph → flydocs-workflow}/scripts/graph_utils.py +2 -1
- package/template/.claude/skills/flydocs-workflow/scripts/issues.py +489 -0
- package/template/.claude/skills/flydocs-workflow/scripts/projects.py +144 -0
- package/template/.claude/skills/flydocs-workflow/scripts/pull_services.py +128 -0
- package/template/.claude/skills/flydocs-workflow/scripts/push_service.py +132 -0
- package/template/.claude/skills/flydocs-workflow/scripts/session.py +54 -0
- package/template/.claude/skills/flydocs-workflow/scripts/workspace.py +860 -0
- package/template/.claude/skills/flydocs-workflow/session.md +63 -25
- package/template/.claude/skills/flydocs-workflow/stages/activate.md +18 -7
- package/template/.claude/skills/flydocs-workflow/stages/capture.md +10 -5
- package/template/.claude/skills/flydocs-workflow/stages/close.md +4 -3
- package/template/.claude/skills/flydocs-workflow/stages/implement.md +33 -9
- package/template/.claude/skills/flydocs-workflow/stages/refine.md +22 -6
- package/template/.claude/skills/flydocs-workflow/stages/review.md +16 -4
- package/template/.claude/skills/flydocs-workflow/stages/validate.md +3 -1
- package/template/.claude/skills/flydocs-workflow/templates/pr/default.md +33 -0
- package/template/.cursor/agents/implementation-agent.md +1 -1
- package/template/.cursor/agents/pm-agent.md +2 -2
- package/template/.cursor/hooks.json +10 -3
- package/template/.env.example +6 -6
- package/template/.flydocs/config.json +5 -18
- package/template/.flydocs/templates/README.md +13 -14
- package/template/.flydocs/templates/quick-capture.md +4 -8
- package/template/.flydocs/version +1 -1
- package/template/AGENTS.md +39 -32
- package/template/CHANGELOG.md +39 -0
- package/template/flydocs/README.md +1 -3
- package/template/flydocs/context/project.md +6 -3
- package/template/flydocs/design-system/README.md +3 -3
- 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 +24 -20
- package/template/.claude/skills/flydocs-cloud/SKILL.md +0 -111
- package/template/.claude/skills/flydocs-cloud/cursor-rule.mdc +0 -50
- package/template/.claude/skills/flydocs-cloud/scripts/assign.py +0 -22
- package/template/.claude/skills/flydocs-cloud/scripts/assign_cycle.py +0 -28
- package/template/.claude/skills/flydocs-cloud/scripts/assign_milestone.py +0 -22
- package/template/.claude/skills/flydocs-cloud/scripts/comment.py +0 -29
- package/template/.claude/skills/flydocs-cloud/scripts/create_issue.py +0 -63
- package/template/.claude/skills/flydocs-cloud/scripts/create_milestone.py +0 -35
- package/template/.claude/skills/flydocs-cloud/scripts/create_project.py +0 -33
- package/template/.claude/skills/flydocs-cloud/scripts/create_team.py +0 -39
- package/template/.claude/skills/flydocs-cloud/scripts/estimate.py +0 -29
- package/template/.claude/skills/flydocs-cloud/scripts/flydocs_api.py +0 -210
- package/template/.claude/skills/flydocs-cloud/scripts/get_issue.py +0 -24
- package/template/.claude/skills/flydocs-cloud/scripts/link.py +0 -28
- package/template/.claude/skills/flydocs-cloud/scripts/list_cycles.py +0 -28
- package/template/.claude/skills/flydocs-cloud/scripts/list_issues.py +0 -44
- package/template/.claude/skills/flydocs-cloud/scripts/list_labels.py +0 -19
- package/template/.claude/skills/flydocs-cloud/scripts/list_milestones.py +0 -28
- package/template/.claude/skills/flydocs-cloud/scripts/list_projects.py +0 -31
- package/template/.claude/skills/flydocs-cloud/scripts/list_teams.py +0 -19
- package/template/.claude/skills/flydocs-cloud/scripts/priority.py +0 -29
- package/template/.claude/skills/flydocs-cloud/scripts/project_update.py +0 -45
- package/template/.claude/skills/flydocs-cloud/scripts/set_labels.py +0 -68
- package/template/.claude/skills/flydocs-cloud/scripts/set_team.py +0 -41
- package/template/.claude/skills/flydocs-cloud/scripts/transition.py +0 -26
- package/template/.claude/skills/flydocs-cloud/scripts/update_description.py +0 -36
- package/template/.claude/skills/flydocs-cloud/scripts/update_issue.py +0 -82
- package/template/.claude/skills/flydocs-context-graph/SKILL.md +0 -87
- package/template/.claude/skills/flydocs-context-graph/schema.md +0 -78
- package/template/.claude/skills/flydocs-context-graph/scripts/graph_context.py +0 -338
- package/template/.claude/skills/flydocs-context7/SKILL.md +0 -105
- package/template/.claude/skills/flydocs-context7/cursor-rule.mdc +0 -49
- package/template/.claude/skills/flydocs-context7/scripts/context7.py +0 -293
- package/template/.claude/skills/flydocs-estimates/SKILL.md +0 -384
- package/template/.claude/skills/flydocs-figma/SKILL.md +0 -377
- package/template/.claude/skills/flydocs-figma/references/PROMPTING.md +0 -108
- package/template/.claude/skills/flydocs-figma/references/TROUBLESHOOTING.md +0 -112
- package/template/.claude/skills/flydocs-local/SKILL.md +0 -103
- package/template/.claude/skills/flydocs-local/cursor-rule.mdc +0 -43
- package/template/.claude/skills/flydocs-local/scripts/assign.py +0 -20
- package/template/.claude/skills/flydocs-local/scripts/comment.py +0 -27
- package/template/.claude/skills/flydocs-local/scripts/create_issue.py +0 -44
- package/template/.claude/skills/flydocs-local/scripts/estimate.py +0 -37
- package/template/.claude/skills/flydocs-local/scripts/get_issue.py +0 -20
- package/template/.claude/skills/flydocs-local/scripts/link.py +0 -41
- package/template/.claude/skills/flydocs-local/scripts/list_issues.py +0 -34
- package/template/.claude/skills/flydocs-local/scripts/priority.py +0 -37
- package/template/.claude/skills/flydocs-local/scripts/project_update.py +0 -67
- package/template/.claude/skills/flydocs-local/scripts/status_summary.py +0 -16
- package/template/.claude/skills/flydocs-local/scripts/transition.py +0 -24
- package/template/.claude/skills/flydocs-local/scripts/update_description.py +0 -35
- package/template/.claude/skills/flydocs-local/scripts/update_issue.py +0 -84
- package/template/.flydocs/hooks/auto-approve.py +0 -71
- package/template/.flydocs/hooks/prompt-submit.py +0 -277
- package/template/.flydocs/scripts/skill_manager.py +0 -541
- package/template/.flydocs/templates/bug.md +0 -166
- package/template/.flydocs/templates/chore.md +0 -110
- package/template/.flydocs/templates/feature.md +0 -173
- package/template/.flydocs/templates/idea.md +0 -122
- /package/template/{.flydocs → .claude}/hooks/post-edit.py +0 -0
- /package/template/.claude/skills/{flydocs-estimates/references → flydocs-workflow/reference}/provider-costs.md +0 -0
- /package/template/.claude/skills/flydocs-workflow/templates/{bug.md → issues/bug.md} +0 -0
- /package/template/.claude/skills/flydocs-workflow/templates/{chore.md → issues/chore.md} +0 -0
- /package/template/.claude/skills/flydocs-workflow/templates/{feature.md → issues/feature.md} +0 -0
- /package/template/.claude/skills/flydocs-workflow/templates/{idea.md → issues/idea.md} +0 -0
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Pull workspace service descriptors from the relay.
|
|
3
|
+
|
|
4
|
+
Fetches GET /api/relay/workspace/services and caches the composite
|
|
5
|
+
at .flydocs/cache/workspace-services.json for local use.
|
|
6
|
+
|
|
7
|
+
Usage:
|
|
8
|
+
python3 .claude/skills/flydocs-workflow/scripts/pull_services.py [--root PATH]
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
import argparse
|
|
12
|
+
import json
|
|
13
|
+
import os
|
|
14
|
+
import sys
|
|
15
|
+
import urllib.error
|
|
16
|
+
import urllib.request
|
|
17
|
+
from datetime import datetime, timezone
|
|
18
|
+
from pathlib import Path
|
|
19
|
+
|
|
20
|
+
sys.path.insert(0, str(Path(__file__).parent))
|
|
21
|
+
from graph_utils import find_project_root, fail
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def load_api_key(root):
|
|
25
|
+
"""Load FLYDOCS_API_KEY from environment or .env files."""
|
|
26
|
+
if os.environ.get("FLYDOCS_API_KEY"):
|
|
27
|
+
return os.environ["FLYDOCS_API_KEY"]
|
|
28
|
+
for name in [".env.local", ".env"]:
|
|
29
|
+
env_file = root / name
|
|
30
|
+
if env_file.exists():
|
|
31
|
+
with open(env_file, "r") as f:
|
|
32
|
+
for line in f:
|
|
33
|
+
line = line.strip()
|
|
34
|
+
if line.startswith("#") or "=" not in line:
|
|
35
|
+
continue
|
|
36
|
+
k, _, v = line.partition("=")
|
|
37
|
+
if k.strip() == "FLYDOCS_API_KEY":
|
|
38
|
+
v = v.strip().strip("\"'")
|
|
39
|
+
return v if v else None
|
|
40
|
+
return None
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def load_config(root):
|
|
44
|
+
"""Load .flydocs/config.json."""
|
|
45
|
+
config_path = root / ".flydocs" / "config.json"
|
|
46
|
+
if config_path.exists():
|
|
47
|
+
with open(config_path, "r") as f:
|
|
48
|
+
return json.load(f)
|
|
49
|
+
return {}
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def resolve_base_url(config):
|
|
53
|
+
"""Resolve relay base URL."""
|
|
54
|
+
env_url = os.environ.get("FLYDOCS_RELAY_URL")
|
|
55
|
+
if env_url:
|
|
56
|
+
return env_url.rstrip("/")
|
|
57
|
+
config_url = config.get("relay", {}).get("url")
|
|
58
|
+
if config_url:
|
|
59
|
+
return config_url.rstrip("/")
|
|
60
|
+
return "https://app.flydocs.ai/api/relay"
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def main():
|
|
64
|
+
parser = argparse.ArgumentParser(description="Pull workspace service descriptors")
|
|
65
|
+
parser.add_argument("--root", type=str, default=None, help="Project root")
|
|
66
|
+
args = parser.parse_args()
|
|
67
|
+
|
|
68
|
+
root = Path(args.root) if args.root else find_project_root()
|
|
69
|
+
if not root:
|
|
70
|
+
fail("Could not find project root (no .flydocs/ directory found)")
|
|
71
|
+
|
|
72
|
+
config = load_config(root)
|
|
73
|
+
api_key = load_api_key(root)
|
|
74
|
+
if not api_key:
|
|
75
|
+
fail("FLYDOCS_API_KEY not found. Set in environment or .env file.")
|
|
76
|
+
|
|
77
|
+
workspace_id = config.get("workspaceId")
|
|
78
|
+
if not workspace_id:
|
|
79
|
+
fail("workspaceId not found in .flydocs/config.json. Run /flydocs-setup first.")
|
|
80
|
+
|
|
81
|
+
base_url = resolve_base_url(config)
|
|
82
|
+
|
|
83
|
+
# Fetch workspace services composite
|
|
84
|
+
url = f"{base_url}/workspace/services"
|
|
85
|
+
headers = {
|
|
86
|
+
"Authorization": f"Bearer {api_key}",
|
|
87
|
+
"X-Workspace": workspace_id,
|
|
88
|
+
"Accept": "application/json",
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
try:
|
|
92
|
+
req = urllib.request.Request(url, headers=headers, method="GET")
|
|
93
|
+
with urllib.request.urlopen(req, timeout=15) as resp:
|
|
94
|
+
result = json.loads(resp.read().decode("utf-8"))
|
|
95
|
+
except urllib.error.HTTPError as e:
|
|
96
|
+
error_body = e.read().decode("utf-8") if e.fp else ""
|
|
97
|
+
try:
|
|
98
|
+
error_data = json.loads(error_body) if error_body else {}
|
|
99
|
+
except json.JSONDecodeError:
|
|
100
|
+
error_data = {"error": error_body}
|
|
101
|
+
fail(f"Relay API error ({e.code}): {error_data.get('error', str(e))}")
|
|
102
|
+
except (urllib.error.URLError, TimeoutError) as e:
|
|
103
|
+
fail(f"Network error: {e}")
|
|
104
|
+
|
|
105
|
+
# Cache the composite locally
|
|
106
|
+
cache_dir = root / ".flydocs" / "cache"
|
|
107
|
+
cache_dir.mkdir(parents=True, exist_ok=True)
|
|
108
|
+
cache_file = cache_dir / "workspace-services.json"
|
|
109
|
+
|
|
110
|
+
cached = {
|
|
111
|
+
"fetchedAt": datetime.now(timezone.utc).isoformat(),
|
|
112
|
+
"services": result,
|
|
113
|
+
}
|
|
114
|
+
cache_file.write_text(json.dumps(cached, indent=2) + "\n", encoding="utf-8")
|
|
115
|
+
|
|
116
|
+
# Report
|
|
117
|
+
repos = result if isinstance(result, list) else result.get("repos", [])
|
|
118
|
+
with_descriptor = sum(1 for r in repos if r.get("serviceDescriptor"))
|
|
119
|
+
print(json.dumps({
|
|
120
|
+
"success": True,
|
|
121
|
+
"totalRepos": len(repos),
|
|
122
|
+
"withDescriptor": with_descriptor,
|
|
123
|
+
"cachedAt": cache_file.name,
|
|
124
|
+
}, indent=2))
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
if __name__ == "__main__":
|
|
128
|
+
main()
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Push this repo's service descriptor to the relay.
|
|
3
|
+
|
|
4
|
+
Reads flydocs/context/service.json, strips the local-only `structure` section,
|
|
5
|
+
and pushes to PUT /api/relay/workspace/service.
|
|
6
|
+
|
|
7
|
+
Usage:
|
|
8
|
+
python3 .claude/skills/flydocs-workflow/scripts/push_service.py [--root PATH]
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
import argparse
|
|
12
|
+
import json
|
|
13
|
+
import os
|
|
14
|
+
import sys
|
|
15
|
+
import urllib.error
|
|
16
|
+
import urllib.request
|
|
17
|
+
from pathlib import Path
|
|
18
|
+
|
|
19
|
+
sys.path.insert(0, str(Path(__file__).parent))
|
|
20
|
+
from graph_utils import find_project_root, fail
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def load_api_key(root):
|
|
24
|
+
"""Load FLYDOCS_API_KEY from environment or .env files."""
|
|
25
|
+
if os.environ.get("FLYDOCS_API_KEY"):
|
|
26
|
+
return os.environ["FLYDOCS_API_KEY"]
|
|
27
|
+
for name in [".env.local", ".env"]:
|
|
28
|
+
env_file = root / name
|
|
29
|
+
if env_file.exists():
|
|
30
|
+
with open(env_file, "r") as f:
|
|
31
|
+
for line in f:
|
|
32
|
+
line = line.strip()
|
|
33
|
+
if line.startswith("#") or "=" not in line:
|
|
34
|
+
continue
|
|
35
|
+
k, _, v = line.partition("=")
|
|
36
|
+
if k.strip() == "FLYDOCS_API_KEY":
|
|
37
|
+
v = v.strip().strip("\"'")
|
|
38
|
+
return v if v else None
|
|
39
|
+
return None
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def load_config(root):
|
|
43
|
+
"""Load .flydocs/config.json."""
|
|
44
|
+
config_path = root / ".flydocs" / "config.json"
|
|
45
|
+
if config_path.exists():
|
|
46
|
+
with open(config_path, "r") as f:
|
|
47
|
+
return json.load(f)
|
|
48
|
+
return {}
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def resolve_base_url(config):
|
|
52
|
+
"""Resolve relay base URL."""
|
|
53
|
+
env_url = os.environ.get("FLYDOCS_RELAY_URL")
|
|
54
|
+
if env_url:
|
|
55
|
+
return env_url.rstrip("/")
|
|
56
|
+
config_url = config.get("relay", {}).get("url")
|
|
57
|
+
if config_url:
|
|
58
|
+
return config_url.rstrip("/")
|
|
59
|
+
return "https://app.flydocs.ai/api/relay"
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def main():
|
|
63
|
+
parser = argparse.ArgumentParser(description="Push service descriptor to relay")
|
|
64
|
+
parser.add_argument("--root", type=str, default=None, help="Project root")
|
|
65
|
+
args = parser.parse_args()
|
|
66
|
+
|
|
67
|
+
root = Path(args.root) if args.root else find_project_root()
|
|
68
|
+
if not root:
|
|
69
|
+
fail("Could not find project root (no .flydocs/ directory found)")
|
|
70
|
+
|
|
71
|
+
# Load service descriptor
|
|
72
|
+
service_file = root / "flydocs" / "context" / "service.json"
|
|
73
|
+
if not service_file.exists():
|
|
74
|
+
fail("No service descriptor found at flydocs/context/service.json. Run /flydocs-setup first.")
|
|
75
|
+
|
|
76
|
+
descriptor = json.loads(service_file.read_text(encoding="utf-8"))
|
|
77
|
+
|
|
78
|
+
# Strip local-only structure section before pushing
|
|
79
|
+
export_descriptor = {k: v for k, v in descriptor.items() if k != "structure"}
|
|
80
|
+
|
|
81
|
+
# Load config and credentials
|
|
82
|
+
config = load_config(root)
|
|
83
|
+
api_key = load_api_key(root)
|
|
84
|
+
if not api_key:
|
|
85
|
+
fail("FLYDOCS_API_KEY not found. Set in environment or .env file.")
|
|
86
|
+
|
|
87
|
+
workspace_id = config.get("workspaceId")
|
|
88
|
+
if not workspace_id:
|
|
89
|
+
fail("workspaceId not found in .flydocs/config.json. Run /flydocs-setup first.")
|
|
90
|
+
|
|
91
|
+
repo_slug = config.get("workspace", {}).get("repoSlug")
|
|
92
|
+
if not repo_slug:
|
|
93
|
+
repo_slug = descriptor.get("repoSlug")
|
|
94
|
+
if not repo_slug:
|
|
95
|
+
fail("repoSlug not found in config or service descriptor.")
|
|
96
|
+
|
|
97
|
+
base_url = resolve_base_url(config)
|
|
98
|
+
|
|
99
|
+
# Push descriptor
|
|
100
|
+
url = f"{base_url}/workspace/service"
|
|
101
|
+
headers = {
|
|
102
|
+
"Authorization": f"Bearer {api_key}",
|
|
103
|
+
"X-Workspace": workspace_id,
|
|
104
|
+
"X-Repo": repo_slug,
|
|
105
|
+
"Content-Type": "application/json",
|
|
106
|
+
"Accept": "application/json",
|
|
107
|
+
}
|
|
108
|
+
data = json.dumps({"descriptor": export_descriptor}).encode("utf-8")
|
|
109
|
+
|
|
110
|
+
try:
|
|
111
|
+
req = urllib.request.Request(url, data=data, headers=headers, method="PUT")
|
|
112
|
+
with urllib.request.urlopen(req, timeout=15) as resp:
|
|
113
|
+
result = json.loads(resp.read().decode("utf-8"))
|
|
114
|
+
print(json.dumps({
|
|
115
|
+
"success": True,
|
|
116
|
+
"repoSlug": repo_slug,
|
|
117
|
+
"fieldsExported": list(export_descriptor.keys()),
|
|
118
|
+
"response": result,
|
|
119
|
+
}, indent=2))
|
|
120
|
+
except urllib.error.HTTPError as e:
|
|
121
|
+
error_body = e.read().decode("utf-8") if e.fp else ""
|
|
122
|
+
try:
|
|
123
|
+
error_data = json.loads(error_body) if error_body else {}
|
|
124
|
+
except json.JSONDecodeError:
|
|
125
|
+
error_data = {"error": error_body}
|
|
126
|
+
fail(f"Relay API error ({e.code}): {error_data.get('error', str(e))}")
|
|
127
|
+
except (urllib.error.URLError, TimeoutError) as e:
|
|
128
|
+
fail(f"Network error: {e}")
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
if __name__ == "__main__":
|
|
132
|
+
main()
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Session operations dispatcher — project updates and status summary."""
|
|
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, resolve_text_input
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def cmd_project_update(args):
|
|
13
|
+
body = resolve_text_input(text_arg=args.body, file_arg=args.body_file)
|
|
14
|
+
if not body:
|
|
15
|
+
fail("Provide body via --body, --body-file, or stdin")
|
|
16
|
+
client = get_client()
|
|
17
|
+
# Resolve project: explicit flag > activeProjects[0] > relay discovery
|
|
18
|
+
project_id = args.project
|
|
19
|
+
if not project_id and client.is_cloud:
|
|
20
|
+
active = client.config.get("workspace", {}).get("activeProjects", [])
|
|
21
|
+
if active:
|
|
22
|
+
project_id = active[0]
|
|
23
|
+
result = client.project_update(args.health, body, project_id=project_id)
|
|
24
|
+
output_json(result)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def cmd_status_summary(args):
|
|
28
|
+
client = get_client()
|
|
29
|
+
result = client.status_summary()
|
|
30
|
+
output_json(result)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def main():
|
|
34
|
+
parser = argparse.ArgumentParser(description="FlyDocs session operations")
|
|
35
|
+
sub = parser.add_subparsers(dest="command", required=True)
|
|
36
|
+
|
|
37
|
+
p = sub.add_parser("project-update", help="Post a project update")
|
|
38
|
+
p.add_argument("--health", required=True, choices=["onTrack", "atRisk", "offTrack"])
|
|
39
|
+
p.add_argument("--body", default=None)
|
|
40
|
+
p.add_argument("--body-file", default=None, dest="body_file")
|
|
41
|
+
p.add_argument("--project", default=None, help="Target project ID (defaults from activeProjects)")
|
|
42
|
+
|
|
43
|
+
sub.add_parser("status-summary", help="Show issue status counts")
|
|
44
|
+
|
|
45
|
+
args = parser.parse_args()
|
|
46
|
+
commands = {
|
|
47
|
+
"project-update": cmd_project_update,
|
|
48
|
+
"status-summary": cmd_status_summary,
|
|
49
|
+
}
|
|
50
|
+
commands[args.command](args)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
if __name__ == "__main__":
|
|
54
|
+
main()
|