@techwavedev/agi-agent-kit 1.1.7 → 1.2.1

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.

Potentially problematic release.


This version of @techwavedev/agi-agent-kit might be problematic. Click here for more details.

Files changed (111) hide show
  1. package/CHANGELOG.md +82 -1
  2. package/README.md +190 -12
  3. package/bin/init.js +30 -2
  4. package/package.json +6 -3
  5. package/templates/base/AGENTS.md +54 -23
  6. package/templates/base/README.md +325 -0
  7. package/templates/base/directives/memory_integration.md +95 -0
  8. package/templates/base/execution/memory_manager.py +309 -0
  9. package/templates/base/execution/session_boot.py +218 -0
  10. package/templates/base/execution/session_init.py +320 -0
  11. package/templates/base/skill-creator/SKILL_skillcreator.md +23 -36
  12. package/templates/base/skill-creator/scripts/init_skill.py +18 -135
  13. package/templates/skills/ec/README.md +31 -0
  14. package/templates/skills/ec/aws/SKILL.md +1020 -0
  15. package/templates/skills/ec/aws/defaults.yaml +13 -0
  16. package/templates/skills/ec/aws/references/common_patterns.md +80 -0
  17. package/templates/skills/ec/aws/references/mcp_servers.md +98 -0
  18. package/templates/skills/ec/aws-terraform/SKILL.md +349 -0
  19. package/templates/skills/ec/aws-terraform/references/best_practices.md +394 -0
  20. package/templates/skills/ec/aws-terraform/references/checkov_reference.md +337 -0
  21. package/templates/skills/ec/aws-terraform/scripts/configure_mcp.py +150 -0
  22. package/templates/skills/ec/confluent-kafka/SKILL.md +655 -0
  23. package/templates/skills/ec/confluent-kafka/references/ansible_playbooks.md +792 -0
  24. package/templates/skills/ec/confluent-kafka/references/ec_deployment.md +579 -0
  25. package/templates/skills/ec/confluent-kafka/references/kraft_migration.md +490 -0
  26. package/templates/skills/ec/confluent-kafka/references/troubleshooting.md +778 -0
  27. package/templates/skills/ec/confluent-kafka/references/upgrade_7x_to_8x.md +488 -0
  28. package/templates/skills/ec/confluent-kafka/scripts/kafka_health_check.py +435 -0
  29. package/templates/skills/ec/confluent-kafka/scripts/upgrade_preflight.py +568 -0
  30. package/templates/skills/ec/confluent-kafka/scripts/validate_config.py +455 -0
  31. package/templates/skills/ec/consul/SKILL.md +427 -0
  32. package/templates/skills/ec/consul/references/acl_setup.md +168 -0
  33. package/templates/skills/ec/consul/references/ha_config.md +196 -0
  34. package/templates/skills/ec/consul/references/troubleshooting.md +267 -0
  35. package/templates/skills/ec/consul/references/upgrades.md +213 -0
  36. package/templates/skills/ec/consul/scripts/consul_health_report.py +530 -0
  37. package/templates/skills/ec/consul/scripts/consul_status.py +264 -0
  38. package/templates/skills/ec/consul/scripts/generate_values.py +170 -0
  39. package/templates/skills/ec/documentation/SKILL.md +351 -0
  40. package/templates/skills/ec/documentation/references/best_practices.md +201 -0
  41. package/templates/skills/ec/documentation/scripts/analyze_code.py +307 -0
  42. package/templates/skills/ec/documentation/scripts/detect_changes.py +460 -0
  43. package/templates/skills/ec/documentation/scripts/generate_changelog.py +312 -0
  44. package/templates/skills/ec/documentation/scripts/sync_docs.py +272 -0
  45. package/templates/skills/ec/documentation/scripts/update_skill_docs.py +366 -0
  46. package/templates/skills/ec/gitlab/SKILL.md +529 -0
  47. package/templates/skills/ec/gitlab/references/agent_installation.md +416 -0
  48. package/templates/skills/ec/gitlab/references/api_reference.md +508 -0
  49. package/templates/skills/ec/gitlab/references/gitops_flux.md +465 -0
  50. package/templates/skills/ec/gitlab/references/troubleshooting.md +518 -0
  51. package/templates/skills/ec/gitlab/scripts/generate_agent_values.py +329 -0
  52. package/templates/skills/ec/gitlab/scripts/gitlab_agent_status.py +414 -0
  53. package/templates/skills/ec/jira/SKILL.md +484 -0
  54. package/templates/skills/ec/jira/references/jql_reference.md +148 -0
  55. package/templates/skills/ec/jira/scripts/add_comment.py +91 -0
  56. package/templates/skills/ec/jira/scripts/bulk_log_work.py +124 -0
  57. package/templates/skills/ec/jira/scripts/create_ticket.py +162 -0
  58. package/templates/skills/ec/jira/scripts/get_ticket.py +191 -0
  59. package/templates/skills/ec/jira/scripts/jira_client.py +383 -0
  60. package/templates/skills/ec/jira/scripts/log_work.py +154 -0
  61. package/templates/skills/ec/jira/scripts/search_tickets.py +104 -0
  62. package/templates/skills/ec/jira/scripts/update_comment.py +67 -0
  63. package/templates/skills/ec/jira/scripts/update_ticket.py +161 -0
  64. package/templates/skills/ec/karpenter/SKILL.md +301 -0
  65. package/templates/skills/ec/karpenter/references/ec2nodeclasses.md +421 -0
  66. package/templates/skills/ec/karpenter/references/migration.md +396 -0
  67. package/templates/skills/ec/karpenter/references/nodepools.md +400 -0
  68. package/templates/skills/ec/karpenter/references/troubleshooting.md +359 -0
  69. package/templates/skills/ec/karpenter/scripts/generate_ec2nodeclass.py +187 -0
  70. package/templates/skills/ec/karpenter/scripts/generate_nodepool.py +245 -0
  71. package/templates/skills/ec/karpenter/scripts/karpenter_status.py +359 -0
  72. package/templates/skills/ec/opensearch/SKILL.md +720 -0
  73. package/templates/skills/ec/opensearch/references/ml_neural_search.md +576 -0
  74. package/templates/skills/ec/opensearch/references/operator.md +532 -0
  75. package/templates/skills/ec/opensearch/references/query_dsl.md +532 -0
  76. package/templates/skills/ec/opensearch/scripts/configure_mcp.py +148 -0
  77. package/templates/skills/ec/victoriametrics/SKILL.md +598 -0
  78. package/templates/skills/ec/victoriametrics/references/kubernetes.md +531 -0
  79. package/templates/skills/ec/victoriametrics/references/prometheus_migration.md +333 -0
  80. package/templates/skills/ec/victoriametrics/references/troubleshooting.md +442 -0
  81. package/templates/skills/knowledge/SKILLS_CATALOG.md +274 -4
  82. package/templates/skills/knowledge/intelligent-routing/SKILL.md +237 -164
  83. package/templates/skills/knowledge/parallel-agents/SKILL.md +345 -73
  84. package/templates/skills/knowledge/plugin-discovery/SKILL.md +582 -0
  85. package/templates/skills/knowledge/plugin-discovery/scripts/platform_setup.py +1083 -0
  86. package/templates/skills/knowledge/design-md/README.md +0 -34
  87. package/templates/skills/knowledge/design-md/SKILL.md +0 -193
  88. package/templates/skills/knowledge/design-md/examples/DESIGN.md +0 -154
  89. package/templates/skills/knowledge/notebooklm-mcp/SKILL.md +0 -71
  90. package/templates/skills/knowledge/notebooklm-mcp/assets/example_asset.txt +0 -24
  91. package/templates/skills/knowledge/notebooklm-mcp/references/api_reference.md +0 -34
  92. package/templates/skills/knowledge/notebooklm-mcp/scripts/example.py +0 -19
  93. package/templates/skills/knowledge/react-components/README.md +0 -36
  94. package/templates/skills/knowledge/react-components/SKILL.md +0 -53
  95. package/templates/skills/knowledge/react-components/examples/gold-standard-card.tsx +0 -80
  96. package/templates/skills/knowledge/react-components/package-lock.json +0 -231
  97. package/templates/skills/knowledge/react-components/package.json +0 -16
  98. package/templates/skills/knowledge/react-components/resources/architecture-checklist.md +0 -15
  99. package/templates/skills/knowledge/react-components/resources/component-template.tsx +0 -37
  100. package/templates/skills/knowledge/react-components/resources/stitch-api-reference.md +0 -14
  101. package/templates/skills/knowledge/react-components/resources/style-guide.json +0 -27
  102. package/templates/skills/knowledge/react-components/scripts/fetch-stitch.sh +0 -30
  103. package/templates/skills/knowledge/react-components/scripts/validate.js +0 -68
  104. package/templates/skills/knowledge/self-update/SKILL.md +0 -60
  105. package/templates/skills/knowledge/self-update/scripts/update_kit.py +0 -103
  106. package/templates/skills/knowledge/stitch-loop/README.md +0 -54
  107. package/templates/skills/knowledge/stitch-loop/SKILL.md +0 -235
  108. package/templates/skills/knowledge/stitch-loop/examples/SITE.md +0 -73
  109. package/templates/skills/knowledge/stitch-loop/examples/next-prompt.md +0 -25
  110. package/templates/skills/knowledge/stitch-loop/resources/baton-schema.md +0 -61
  111. package/templates/skills/knowledge/stitch-loop/resources/site-template.md +0 -104
@@ -0,0 +1,124 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Bulk Log Work Utility
4
+
5
+ A reusable script to log work across a date range for a specific ticket.
6
+ Automatically handles weekends and European Commission holidays.
7
+
8
+ Usage:
9
+ python bulk_log_work.py --ticket <key> --start <YYYY-MM-DD> --end <YYYY-MM-DD> --time <duration> --comment <text>
10
+
11
+ Arguments:
12
+ --ticket Ticket key (required)
13
+ --start Start date YYYY-MM-DD (required)
14
+ --end End date YYYY-MM-DD (default: today)
15
+ --time Time per working day (e.g., "3h", "1h 30m")
16
+ --comment Comment for worklogs. If multiple, separate with '|' to rotate.
17
+ --dry-run Show what would be logged without making API calls
18
+
19
+ Example:
20
+ python bulk_log_work.py --ticket KASP-1241 --start 2026-01-01 --time 2h --comment "Infra check|Alert check"
21
+ """
22
+
23
+ import argparse
24
+ import sys
25
+ from datetime import datetime, timedelta
26
+ from pathlib import Path
27
+ from typing import List
28
+
29
+ sys.path.insert(0, str(Path(__file__).parent))
30
+ from jira_client import get_client
31
+
32
+
33
+ def get_ec_holidays(year: int) -> List[str]:
34
+ """Return a list of EC holidays for a given year (approximate)."""
35
+ # Standard EC holidays
36
+ holidays = [
37
+ f"{year}-01-01", f"{year}-01-02", # New Year
38
+ f"{year}-05-01", # Labor Day
39
+ f"{year}-05-09", # Europe Day
40
+ f"{year}-07-21", # National Day
41
+ f"{year}-08-15", # Assumption
42
+ f"{year}-11-01", # All Saints
43
+ f"{year}-11-02", # All Souls
44
+ f"{year}-12-24", f"{year}-12-25", f"{year}-12-26",
45
+ f"{year}-12-27", f"{year}-12-28", f"{year}-12-29",
46
+ f"{year}-12-30", f"{year}-12-31" # Winter break
47
+ ]
48
+
49
+ # Easter-related (approximate for demonstration, usually fixed per year)
50
+ if year == 2026:
51
+ holidays.extend(["2026-04-02", "2026-04-03", "2026-04-06", "2026-05-14", "2026-05-25"])
52
+
53
+ return holidays
54
+
55
+
56
+ def main():
57
+ parser = argparse.ArgumentParser(description="Bulk log work to Jira tickets")
58
+ parser.add_argument("--ticket", required=True, help="Ticket key")
59
+ parser.add_argument("--start", required=True, help="Start date (YYYY-MM-DD)")
60
+ parser.add_argument("--end", help="End date (YYYY-MM-DD, defaults to today)")
61
+ parser.add_argument("--time", required=True, help="Time per working day (e.g., 2h)")
62
+ parser.add_argument("--comment", required=True, help="Comment(s) - use | to rotate multiple")
63
+ parser.add_argument("--dry-run", action="store_true", help="Don't actually log work")
64
+ args = parser.parse_args()
65
+
66
+ client = get_client()
67
+ ticket = args.ticket.upper()
68
+
69
+ # Parse dates
70
+ start_date = datetime.strptime(args.start, "%Y-%m-%d")
71
+ end_date = datetime.strptime(args.end, "%Y-%m-%d") if args.end else datetime.now()
72
+
73
+ # Parse comments
74
+ comments = [c.strip() for c in args.comment.split('|')]
75
+
76
+ print(f"🚀 {'[DRY RUN] ' if args.dry_run else ''}Bulk logging for {ticket}")
77
+ print(f" Period: {start_date.strftime('%Y-%m-%d')} to {end_date.strftime('%Y-%m-%d')}")
78
+ print(f" Work: {args.time} per day")
79
+ print("-" * 50)
80
+
81
+ current_date = start_date
82
+ working_days_count = 0
83
+
84
+ while current_date <= end_date:
85
+ date_str = current_date.strftime("%Y-%m-%d")
86
+ holidays = get_ec_holidays(current_date.year)
87
+
88
+ # Skip weekends and holidays
89
+ if current_date.weekday() >= 5:
90
+ # print(f"🏠 {date_str}: Weekend (Skipping)")
91
+ pass
92
+ elif date_str in holidays:
93
+ print(f"🎉 {date_str}: Holiday (Skipping)")
94
+ else:
95
+ comment = comments[working_days_count % len(comments)]
96
+ # 14:00 (2 PM) as a default work check time
97
+ started = current_date.strftime("%Y-%m-%dT14:00:00.000+0100")
98
+
99
+ print(f"⏱️ {date_str}: Logging {args.time} - {comment}...", end=" ", flush=True)
100
+
101
+ if not args.dry_run:
102
+ success, result = client.add_worklog(
103
+ ticket,
104
+ time_spent=args.time,
105
+ comment=comment,
106
+ started=started
107
+ )
108
+ if success:
109
+ print("✅ Done")
110
+ else:
111
+ print(f"❌ Failed: {result}")
112
+ else:
113
+ print("📝 (Skipped)")
114
+
115
+ working_days_count += 1
116
+
117
+ current_date += timedelta(days=1)
118
+
119
+ print("-" * 50)
120
+ print(f"🏁 Finished! Processed {working_days_count} working day(s).")
121
+
122
+
123
+ if __name__ == "__main__":
124
+ main()
@@ -0,0 +1,162 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Create Jira Ticket
4
+
5
+ Creates a new issue in Jira with specified fields.
6
+
7
+ Usage:
8
+ python create_ticket.py --project <key> --summary <text> [options]
9
+
10
+ Arguments:
11
+ --project Project key (required)
12
+ --summary Issue summary (required)
13
+ --type Issue type: Bug, Task, Story, Epic (default: Task)
14
+ --priority Priority: Highest, High, Medium, Low, Lowest
15
+ --description Description text
16
+ --assignee Assignee username or email
17
+ --labels Comma-separated labels
18
+ --components Comma-separated component names
19
+ --epic Epic link key
20
+ --parent Parent ticket key (for sub-tasks)
21
+ --custom-fields Custom fields as JSON string
22
+ --output Output format: json, key, url (default: json)
23
+
24
+ Exit Codes:
25
+ 0 - Success
26
+ 1 - Invalid arguments
27
+ 2 - API error
28
+ """
29
+
30
+ import argparse
31
+ import json
32
+ import sys
33
+ from pathlib import Path
34
+
35
+ # Add scripts directory to path for imports
36
+ sys.path.insert(0, str(Path(__file__).parent))
37
+ from jira_client import get_client
38
+
39
+
40
+ def parse_labels(labels_str: str) -> list:
41
+ """Parse comma-separated labels."""
42
+ if not labels_str:
43
+ return []
44
+ return [l.strip() for l in labels_str.split(',') if l.strip()]
45
+
46
+
47
+ def build_fields(args, client) -> dict:
48
+ """Build the fields dictionary for issue creation."""
49
+ fields = {
50
+ 'project': {'key': args.project},
51
+ 'summary': args.summary,
52
+ 'issuetype': {'name': args.type}
53
+ }
54
+
55
+ if args.priority:
56
+ fields['priority'] = {'name': args.priority}
57
+
58
+ if args.description:
59
+ fields['description'] = client._format_body(args.description)
60
+
61
+ if args.assignee:
62
+ # Try to resolve assignee
63
+ user_id_key = 'name' if client.is_server else 'accountId'
64
+
65
+ if args.assignee.lower() == 'me':
66
+ success, me = client.get_myself()
67
+ if success:
68
+ fields['assignee'] = {user_id_key: me.get(user_id_key)}
69
+ else:
70
+ # Search for user
71
+ success, users = client.search_users(args.assignee, max_results=1)
72
+ if success and users:
73
+ fields['assignee'] = {user_id_key: users[0].get(user_id_key)}
74
+ else:
75
+ # Fallback to literal name if search fails but we have a candidate
76
+ fields['assignee'] = {user_id_key: args.assignee}
77
+
78
+ if args.parent:
79
+ fields['parent'] = {'key': args.parent}
80
+
81
+ if args.epic:
82
+ # Epic link field varies by project, but 'parent' is often used in newer Jira
83
+ fields['parent'] = {'key': args.epic}
84
+
85
+ if args.labels:
86
+ fields['labels'] = parse_labels(args.labels)
87
+
88
+ if args.components:
89
+ fields['components'] = [{'name': c.strip()} for c in args.components.split(',')]
90
+
91
+ # Custom fields
92
+ if args.custom_fields:
93
+ try:
94
+ custom = json.loads(args.custom_fields)
95
+ fields.update(custom)
96
+ except json.JSONDecodeError as e:
97
+ print(f"⚠️ Warning: Invalid custom-fields JSON: {e}", file=sys.stderr)
98
+
99
+ return fields
100
+
101
+
102
+ def main():
103
+ parser = argparse.ArgumentParser(
104
+ description='Create a Jira ticket',
105
+ formatter_class=argparse.RawDescriptionHelpFormatter,
106
+ epilog=__doc__
107
+ )
108
+ parser.add_argument('--project', required=True, help='Project key')
109
+ parser.add_argument('--summary', required=True, help='Issue summary')
110
+ parser.add_argument('--type', default='Task', help='Issue type')
111
+ parser.add_argument('--priority', help='Priority level')
112
+ parser.add_argument('--description', help='Description text')
113
+ parser.add_argument('--assignee', help='Assignee username/email')
114
+ parser.add_argument('--labels', help='Comma-separated labels')
115
+ parser.add_argument('--components', help='Comma-separated components')
116
+ parser.add_argument('--epic', help='Epic link key')
117
+ parser.add_argument('--parent', help='Parent ticket key (for sub-tasks)')
118
+ parser.add_argument('--custom-fields', help='Custom fields as JSON')
119
+ parser.add_argument('--output', choices=['json', 'key', 'url'], default='json',
120
+ help='Output format')
121
+ args = parser.parse_args()
122
+
123
+ client = get_client()
124
+
125
+ print(f"📝 Creating ticket in project {args.project}...", file=sys.stderr)
126
+
127
+ # Build fields
128
+ fields = build_fields(args, client)
129
+
130
+ # Create issue
131
+ success, result = client.create_issue(fields)
132
+
133
+ if not success:
134
+ print(f"❌ Error creating ticket: {result}", file=sys.stderr)
135
+ sys.exit(2)
136
+
137
+ issue_key = result.get('key', 'Unknown')
138
+ issue_url = f"{client.base_url}/browse/{issue_key}"
139
+
140
+ # Output based on format
141
+ if args.output == 'key':
142
+ print(issue_key)
143
+ elif args.output == 'url':
144
+ print(issue_url)
145
+ else:
146
+ output = {
147
+ 'success': True,
148
+ 'key': issue_key,
149
+ 'id': result.get('id'),
150
+ 'url': issue_url,
151
+ 'self': result.get('self')
152
+ }
153
+ print(json.dumps(output, indent=2))
154
+
155
+ print(f"✅ Created ticket: {issue_key}", file=sys.stderr)
156
+ print(f" URL: {issue_url}", file=sys.stderr)
157
+
158
+ sys.exit(0)
159
+
160
+
161
+ if __name__ == '__main__':
162
+ main()
@@ -0,0 +1,191 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Get Jira Ticket Details
4
+
5
+ Retrieve full details for a specific Jira issue.
6
+
7
+ Usage:
8
+ python get_ticket.py --ticket <key> [options]
9
+
10
+ Arguments:
11
+ --ticket Ticket key (required)
12
+ --include-comments Include comments
13
+ --include-worklog Include work logs
14
+ --include-transitions Show available transitions
15
+ --output Output format: json, summary (default: summary)
16
+
17
+ Exit Codes:
18
+ 0 - Success
19
+ 1 - Invalid arguments
20
+ 2 - Ticket not found
21
+ """
22
+
23
+ import argparse
24
+ import json
25
+ import sys
26
+ from pathlib import Path
27
+
28
+ sys.path.insert(0, str(Path(__file__).parent))
29
+ from jira_client import get_client
30
+
31
+
32
+ def format_summary(issue: dict, transitions: list = None, comments: list = None,
33
+ worklogs: list = None, base_url: str = '') -> str:
34
+ """Format issue as human-readable summary."""
35
+ key = issue.get('key', 'Unknown')
36
+ fields = issue.get('fields', {})
37
+
38
+ lines = [
39
+ f"# {key}: {fields.get('summary', 'No summary')}",
40
+ "",
41
+ f"**URL:** {base_url}/browse/{key}",
42
+ "",
43
+ "## Details",
44
+ "",
45
+ f"| Field | Value |",
46
+ f"|-------|-------|",
47
+ f"| Type | {fields.get('issuetype', {}).get('name', '-')} |",
48
+ f"| Status | {fields.get('status', {}).get('name', '-')} |",
49
+ f"| Priority | {fields.get('priority', {}).get('name', '-') if fields.get('priority') else '-'} |",
50
+ ]
51
+
52
+ assignee = fields.get('assignee')
53
+ lines.append(f"| Assignee | {assignee.get('displayName', '-') if assignee else 'Unassigned'} |")
54
+
55
+ reporter = fields.get('reporter')
56
+ lines.append(f"| Reporter | {reporter.get('displayName', '-') if reporter else '-'} |")
57
+
58
+ lines.append(f"| Created | {fields.get('created', '-')[:10] if fields.get('created') else '-'} |")
59
+ lines.append(f"| Updated | {fields.get('updated', '-')[:10] if fields.get('updated') else '-'} |")
60
+
61
+ if fields.get('labels'):
62
+ lines.append(f"| Labels | {', '.join(fields['labels'])} |")
63
+
64
+ lines.append("")
65
+
66
+ # Description
67
+ description = fields.get('description')
68
+ if description:
69
+ lines.append("## Description")
70
+ lines.append("")
71
+ if isinstance(description, dict):
72
+ # ADF format - extract text
73
+ content = description.get('content', [])
74
+ for block in content:
75
+ if block.get('type') == 'paragraph':
76
+ text_parts = [c.get('text', '') for c in block.get('content', [])]
77
+ lines.append(''.join(text_parts))
78
+ else:
79
+ lines.append(str(description))
80
+ lines.append("")
81
+
82
+ # Transitions
83
+ if transitions:
84
+ lines.append("## Available Transitions")
85
+ lines.append("")
86
+ for t in transitions:
87
+ lines.append(f"- {t.get('name')} → {t.get('to', {}).get('name', '?')}")
88
+ lines.append("")
89
+
90
+ # Comments
91
+ if comments:
92
+ lines.append(f"## Comments ({len(comments)})")
93
+ lines.append("")
94
+ for c in comments[-5:]: # Last 5 comments
95
+ author = c.get('author', {}).get('displayName', 'Unknown')
96
+ created = c.get('created', '')[:10]
97
+ body = c.get('body', '')
98
+ if isinstance(body, dict):
99
+ # ADF format
100
+ text_parts = []
101
+ for block in body.get('content', []):
102
+ if block.get('type') == 'paragraph':
103
+ text_parts.extend([p.get('text', '') for p in block.get('content', [])])
104
+ body = ' '.join(text_parts)
105
+ lines.append(f"**{author}** ({created}):")
106
+ lines.append(f"> {body[:200]}...")
107
+ lines.append("")
108
+
109
+ # Worklogs
110
+ if worklogs:
111
+ lines.append(f"## Work Logs ({len(worklogs)})")
112
+ lines.append("")
113
+ total_seconds = sum(w.get('timeSpentSeconds', 0) for w in worklogs)
114
+ total_hours = total_seconds / 3600
115
+ lines.append(f"**Total Time Logged:** {total_hours:.1f}h")
116
+ lines.append("")
117
+ for w in worklogs[-5:]: # Last 5 entries
118
+ author = w.get('author', {}).get('displayName', 'Unknown')
119
+ time_spent = w.get('timeSpent', '?')
120
+ lines.append(f"- {author}: {time_spent}")
121
+ lines.append("")
122
+
123
+ return '\n'.join(lines)
124
+
125
+
126
+ def main():
127
+ parser = argparse.ArgumentParser(
128
+ description='Get Jira ticket details',
129
+ formatter_class=argparse.RawDescriptionHelpFormatter,
130
+ epilog=__doc__
131
+ )
132
+ parser.add_argument('--ticket', required=True, help='Ticket key')
133
+ parser.add_argument('--include-comments', action='store_true', help='Include comments')
134
+ parser.add_argument('--include-worklog', action='store_true', help='Include work logs')
135
+ parser.add_argument('--include-transitions', action='store_true', help='Show transitions')
136
+ parser.add_argument('--output', choices=['json', 'summary'], default='summary',
137
+ help='Output format')
138
+ args = parser.parse_args()
139
+
140
+ client = get_client()
141
+ ticket = args.ticket.upper()
142
+
143
+ print(f"📋 Getting details for {ticket}...", file=sys.stderr)
144
+
145
+ # Get issue
146
+ expand = []
147
+ if args.include_comments:
148
+ expand.append('renderedFields')
149
+
150
+ success, issue = client.get_issue(ticket, expand=expand if expand else None)
151
+ if not success:
152
+ print(f"❌ Error: Could not find ticket {ticket}: {issue}", file=sys.stderr)
153
+ sys.exit(2)
154
+
155
+ # Get optional data
156
+ transitions = None
157
+ comments = None
158
+ worklogs = None
159
+
160
+ if args.include_transitions:
161
+ success, trans_data = client.get_transitions(ticket)
162
+ if success:
163
+ transitions = trans_data.get('transitions', [])
164
+
165
+ if args.include_comments:
166
+ success, comment_data = client.get_comments(ticket)
167
+ if success:
168
+ comments = comment_data.get('comments', [])
169
+
170
+ if args.include_worklog:
171
+ success, worklog_data = client.get_worklogs(ticket)
172
+ if success:
173
+ worklogs = worklog_data.get('worklogs', [])
174
+
175
+ # Output
176
+ if args.output == 'json':
177
+ output = {
178
+ 'issue': issue,
179
+ 'transitions': transitions,
180
+ 'comments': comments,
181
+ 'worklogs': worklogs
182
+ }
183
+ print(json.dumps(output, indent=2))
184
+ else:
185
+ print(format_summary(issue, transitions, comments, worklogs, client.base_url))
186
+
187
+ sys.exit(0)
188
+
189
+
190
+ if __name__ == '__main__':
191
+ main()