Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/instructions/terminal.instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ Placement policy for wrappers:
|--------|---------|
| `Git-Search.ps1` | git show/diff/log/grep/blame |
| `Read-FileContent.ps1` | File reading with filtering |
| `Run-TestCoverage.ps1` | Collect local coverage (cobertura/xml/html summaries) |

**Build/test**: Run `.\build.ps1` or `.\test.ps1` directly—they're auto-approvable.
## Beads CLI (auto-approvable patterns)
Expand Down
18 changes: 18 additions & 0 deletions .github/instructions/testing.instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,24 @@ Use `.\test.ps1` for all managed (C#) tests.
.\test.ps1 -NoBuild -TestProject "FwUtilsTests"
```

## Local Coverage Reports (Managed)

Use the repo wrapper to collect local, machine-readable coverage that agents can parse.

```powershell
# Default DetailControls coverage (cobertura+xml+html summaries)
.\Build\Agent\Run-TestCoverage.ps1 -Configuration Debug -TestFilter "FullyQualifiedName~DetailControls"

# Custom test slice (example)
.\Build\Agent\Run-TestCoverage.ps1 -Configuration Debug -TestFilter "FullyQualifiedName~DataTreeTests"
```

Artifacts are written under `Output/<Configuration>/Coverage/`:
- `coverage.cobertura.xml` (machine-readable)
- `coverage-summary.json` (agent-friendly aggregate + lowest-coverage classes)
- `coverage-summary.md` (human summary)
- `html/index.html` (navigable report)

## Running Tests (Native C++)

Use `.\test.ps1 -Native` for native (C++) tests. This wraps `Build/scripts/Invoke-CppTest.ps1`.
Expand Down
14 changes: 7 additions & 7 deletions .github/skills/atlassian-readonly-skills/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,17 +29,17 @@ Read-only Python utilities for Jira, Confluence, and Bitbucket integration, supp
- Use `fetch_webpage` or similar tools on JIRA URLs
- Use GitHub issue tools for LT-* tickets

**ALWAYS** use these Python modules. The scripts are Python modules (not CLI tools), so use them via inline Python or import:
**ALWAYS** use these Python scripts via CLI entry points:

```powershell
# Get a single issue (inline Python one-liner)
python -c "import sys; sys.path.insert(0, '.github/skills/atlassian-readonly-skills/scripts'); from jira_issues import jira_get_issue; print(jira_get_issue('LT-22382'))"
# Get a single issue
python .github/skills/atlassian-readonly-skills/scripts/jira_cli.py get-issue --issue-key LT-22382

# Search for issues (JQL query)
python -c "import sys; sys.path.insert(0, '.github/skills/atlassian-readonly-skills/scripts'); from jira_search import jira_search; print(jira_search('project = LT AND status = Open'))"
python .github/skills/atlassian-readonly-skills/scripts/jira_cli.py search --jql "project = LT AND status = Open"

# Get issue workflow transitions
python -c "import sys; sys.path.insert(0, '.github/skills/atlassian-readonly-skills/scripts'); from jira_workflow import jira_get_transitions; print(jira_get_transitions('LT-22382'))"
python .github/skills/atlassian-readonly-skills/scripts/jira_cli.py get-transitions --issue-key LT-22382
```

Alternatively, use the CLI helper in `jira-to-beads` skill:
Expand Down Expand Up @@ -114,12 +114,12 @@ credentials = AtlassianCredentials(
jira_url="https://your-company.atlassian.net",
jira_username="your.email@company.com",
jira_api_token="your_api_token",

# Confluence configuration (optional)
confluence_url="https://your-company.atlassian.net/wiki",
confluence_username="your.email@company.com",
confluence_api_token="your_api_token",

# Bitbucket configuration (optional)
# bitbucket_url="https://bitbucket.your-company.com",
# bitbucket_pat_token="your_pat_token"
Expand Down
39 changes: 39 additions & 0 deletions .github/skills/atlassian-readonly-skills/scripts/jira_cli.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
"""CLI wrapper for atlassian-readonly-skills Jira operations."""

import argparse

from jira_issues import jira_get_issue
from jira_search import jira_search
from jira_workflow import jira_get_transitions


def main():
parser = argparse.ArgumentParser(description="Readonly Jira CLI")
subparsers = parser.add_subparsers(dest="action", required=True)

get_issue = subparsers.add_parser("get-issue", help="Get a Jira issue")
get_issue.add_argument("--issue-key", required=True)
get_issue.add_argument("--fields", default=None)
get_issue.add_argument("--expand", default=None)

search = subparsers.add_parser("search", help="Search Jira issues")
search.add_argument("--jql", required=True)
search.add_argument("--fields", default=None)
search.add_argument("--limit", type=int, default=10)
search.add_argument("--start-at", type=int, default=0)

transitions = subparsers.add_parser("get-transitions", help="Get issue transitions")
transitions.add_argument("--issue-key", required=True)

args = parser.parse_args()

if args.action == "get-issue":
print(jira_get_issue(args.issue_key, fields=args.fields, expand=args.expand))
elif args.action == "search":
print(jira_search(args.jql, fields=args.fields, limit=args.limit, start_at=args.start_at))
elif args.action == "get-transitions":
print(jira_get_transitions(args.issue_key))


if __name__ == "__main__":
main()
29 changes: 15 additions & 14 deletions .github/skills/atlassian-readonly-skills/scripts/jira_workflow.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@
from pathlib import Path
sys.path.insert(0, str(Path(__file__).parent))

from typing import Any, Dict
from typing import Any, Dict, Optional

from _common import (
AtlassianCredentials,
get_jira_client,
format_json_response,
format_error_response,
Expand All @@ -29,15 +30,15 @@ def _simplify_transition(transition_data: Dict[str, Any]) -> Dict[str, Any]:
'id': transition_data.get('id', ''),
'name': transition_data.get('name', ''),
}

to_status = transition_data.get('to', {})
if to_status and isinstance(to_status, dict):
simplified['to_status'] = to_status.get('name', '')
simplified['to_status_id'] = to_status.get('id', '')

has_screen = transition_data.get('hasScreen', False)
simplified['has_screen'] = has_screen

fields = transition_data.get('fields', {})
if fields:
required_fields = []
Expand All @@ -53,47 +54,47 @@ def _simplify_transition(transition_data: Dict[str, Any]) -> Dict[str, Any]:
required_fields.append(field_data)
else:
optional_fields.append(field_data)

if required_fields:
simplified['required_fields'] = required_fields
if optional_fields:
simplified['optional_fields'] = optional_fields

return simplified


def jira_get_transitions(issue_key: str,
credentials: Optional[AtlassianCredentials] = None) -> str:
"""Get available status transitions for a Jira issue.

Args:
issue_key: Issue key (e.g., 'PROJ-123')

Returns:
JSON string with list of available transitions or error information
"""
try:
client = get_jira_client(credentials)

if not issue_key:
raise ValidationError('issue_key is required')

params = {'expand': 'transitions.fields'}
response = client.get(
client.api_path(f'issue/{issue_key}/transitions'), params=params
)

transitions = response.get('transitions', [])
simplified_transitions = [_simplify_transition(t) for t in transitions]

result = {
'issue_key': issue_key,
'transitions': simplified_transitions,
'count': len(simplified_transitions)
}

return format_json_response(result)

except ConfigurationError as e:
return format_error_response('ConfigurationError', str(e))
except AuthenticationError as e:
Expand Down
22 changes: 11 additions & 11 deletions .github/skills/atlassian-skills/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,20 +29,20 @@ Python utilities for Jira, Confluence, and Bitbucket integration, supporting bot
- Use `fetch_webpage` or similar tools on JIRA URLs
- Use GitHub issue tools for LT-* tickets

**ALWAYS** use these Python modules. The scripts are Python modules (not CLI tools), so use them via inline Python or import:
**ALWAYS** use these Python scripts via CLI entry points:

```powershell
# Create a new issue
python -c "import sys; sys.path.insert(0, '.github/skills/atlassian-skills/scripts'); from jira_issues import jira_create_issue; print(jira_create_issue('LT', 'Issue title', 'Bug'))"
python .github/skills/atlassian-skills/scripts/jira_cli.py create-issue --project-key LT --summary "Issue title" --issue-type Bug

# Update an existing issue
python -c "import sys; sys.path.insert(0, '.github/skills/atlassian-skills/scripts'); from jira_issues import jira_update_issue; print(jira_update_issue('LT-22382', summary='Updated title'))"
python .github/skills/atlassian-skills/scripts/jira_cli.py update-issue --issue-key LT-22382 --summary "Updated title"

# Add a comment
python -c "import sys; sys.path.insert(0, '.github/skills/atlassian-skills/scripts'); from jira_issues import jira_add_comment; print(jira_add_comment('LT-22382', 'Comment text'))"
python .github/skills/atlassian-skills/scripts/jira_cli.py add-comment --issue-key LT-22382 --comment "Comment text"

# Transition issue status
python -c "import sys; sys.path.insert(0, '.github/skills/atlassian-skills/scripts'); from jira_workflow import jira_transition_issue; print(jira_transition_issue('LT-22382', 'In Progress'))"
python .github/skills/atlassian-skills/scripts/jira_cli.py transition --issue-key LT-22382 --transition-id 31
```

For read-only operations (get issue, search, get comments), use `atlassian-readonly-skills` instead.
Expand Down Expand Up @@ -112,12 +112,12 @@ credentials = AtlassianCredentials(
jira_url="https://your-company.atlassian.net",
jira_username="your.email@company.com",
jira_api_token="your_api_token",

# Confluence configuration (optional)
confluence_url="https://your-company.atlassian.net/wiki",
confluence_username="your.email@company.com",
confluence_api_token="your_api_token",

# Bitbucket configuration (optional)
# bitbucket_url="https://bitbucket.your-company.com",
# bitbucket_pat_token="your_pat_token"
Expand Down Expand Up @@ -167,15 +167,15 @@ AtlassianCredentials(
jira_pat_token: Optional[str] = None,
jira_api_version: Optional[str] = None, # '2' or '3', auto-detected if not set
jira_ssl_verify: bool = False,

# Confluence
confluence_url: Optional[str] = None,
confluence_username: Optional[str] = None,
confluence_api_token: Optional[str] = None,
confluence_pat_token: Optional[str] = None,
confluence_api_version: Optional[str] = None,
confluence_ssl_verify: bool = False,

# Bitbucket
bitbucket_url: Optional[str] = None,
bitbucket_username: Optional[str] = None,
Expand Down Expand Up @@ -614,14 +614,14 @@ def skill_function(
credentials: Optional[AtlassianCredentials] = None # Always last parameter
) -> str:
"""Function description.

Args:
required_param1: Description
required_param2: Description
optional_param: Description (optional)
credentials: Optional AtlassianCredentials for Agent environments.
If not provided, uses environment variables.

Returns:
JSON string with result or error information
"""
Expand Down
88 changes: 88 additions & 0 deletions .github/skills/atlassian-skills/scripts/jira_cli.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
"""CLI wrapper for atlassian-skills Jira operations."""

import argparse

from jira_issues import jira_add_comment, jira_create_issue, jira_update_issue
from jira_workflow import jira_get_transitions, jira_transition_issue


def _parse_labels(labels_value):
if labels_value is None or labels_value == "":
return None
return [label.strip() for label in labels_value.split(",") if label.strip()]


def main():
parser = argparse.ArgumentParser(description="Jira CLI")
subparsers = parser.add_subparsers(dest="action", required=True)

create_issue = subparsers.add_parser("create-issue", help="Create a Jira issue")
create_issue.add_argument("--project-key", required=True)
create_issue.add_argument("--summary", required=True)
create_issue.add_argument("--issue-type", required=True)
create_issue.add_argument("--description", default=None)
create_issue.add_argument("--assignee", default=None)
create_issue.add_argument("--priority", default=None)
create_issue.add_argument("--labels", default=None)

update_issue = subparsers.add_parser("update-issue", help="Update a Jira issue")
update_issue.add_argument("--issue-key", required=True)
update_issue.add_argument("--summary", default=None)
update_issue.add_argument("--description", default=None)
update_issue.add_argument("--assignee", default=None)
update_issue.add_argument("--priority", default=None)
update_issue.add_argument("--labels", default=None)

add_comment = subparsers.add_parser("add-comment", help="Add issue comment")
add_comment.add_argument("--issue-key", required=True)
add_comment.add_argument("--comment", required=True)

transitions = subparsers.add_parser("get-transitions", help="Get issue transitions")
transitions.add_argument("--issue-key", required=True)

transition_issue = subparsers.add_parser("transition", help="Transition issue status")
transition_issue.add_argument("--issue-key", required=True)
transition_issue.add_argument("--transition-id", required=True)
transition_issue.add_argument("--comment", default=None)

args = parser.parse_args()

if args.action == "create-issue":
print(
jira_create_issue(
project_key=args.project_key,
summary=args.summary,
issue_type=args.issue_type,
description=args.description,
assignee=args.assignee,
priority=args.priority,
labels=_parse_labels(args.labels),
)
)
elif args.action == "update-issue":
print(
jira_update_issue(
issue_key=args.issue_key,
summary=args.summary,
description=args.description,
assignee=args.assignee,
priority=args.priority,
labels=_parse_labels(args.labels),
)
)
elif args.action == "add-comment":
print(jira_add_comment(args.issue_key, args.comment))
elif args.action == "get-transitions":
print(jira_get_transitions(args.issue_key))
elif args.action == "transition":
print(
jira_transition_issue(
issue_key=args.issue_key,
transition_id=args.transition_id,
comment=args.comment,
)
)


if __name__ == "__main__":
main()
2 changes: 1 addition & 1 deletion .github/skills/beads/CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ wc -w claude-plugin/skills/beads/SKILL.md # Target: 400-600 words
# (Manual check: ensure all resource links in SKILL.md exist)

# Verify bd prime still works
bd prime | head -20
bd prime
```

## Attribution
Expand Down
Loading
Loading