copilot-session-usage 0.6.0__tar.gz → 0.6.1__tar.gz

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 (113) hide show
  1. {copilot_session_usage-0.6.0 → copilot_session_usage-0.6.1}/.github/skills/gh-release-notes/SKILL.md +16 -0
  2. copilot_session_usage-0.6.1/.github/workflows/refresh-pricing.yml +72 -0
  3. copilot_session_usage-0.6.1/.github/workflows/release-notes.yml +127 -0
  4. copilot_session_usage-0.6.1/.github/workflows/release.yml +195 -0
  5. copilot_session_usage-0.6.1/CONTRIBUTING.md +65 -0
  6. {copilot_session_usage-0.6.0 → copilot_session_usage-0.6.1}/PKG-INFO +1 -1
  7. copilot_session_usage-0.6.1/docs/internal/automated_release_proces.md +497 -0
  8. copilot_session_usage-0.6.0/CONTRIBUTING.md +0 -49
  9. {copilot_session_usage-0.6.0 → copilot_session_usage-0.6.1}/.editorconfig +0 -0
  10. {copilot_session_usage-0.6.0 → copilot_session_usage-0.6.1}/.gitattributes +0 -0
  11. {copilot_session_usage-0.6.0 → copilot_session_usage-0.6.1}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  12. {copilot_session_usage-0.6.0 → copilot_session_usage-0.6.1}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  13. {copilot_session_usage-0.6.0 → copilot_session_usage-0.6.1}/.github/changes/requests/skill-breakdown/01-request.md +0 -0
  14. {copilot_session_usage-0.6.0 → copilot_session_usage-0.6.1}/.github/guidelines/git-commit-message.guideline.md +0 -0
  15. {copilot_session_usage-0.6.0 → copilot_session_usage-0.6.1}/.github/guidelines/knowledge-base.guidelines.md +0 -0
  16. {copilot_session_usage-0.6.0 → copilot_session_usage-0.6.1}/.github/pull_request_template.md +0 -0
  17. {copilot_session_usage-0.6.0 → copilot_session_usage-0.6.1}/.github/skills/consolidate-knowledge-base/SKILL.md +0 -0
  18. {copilot_session_usage-0.6.0 → copilot_session_usage-0.6.1}/.github/skills/record-finding/SKILL.md +0 -0
  19. {copilot_session_usage-0.6.0 → copilot_session_usage-0.6.1}/.github/workflows/ci.yml +0 -0
  20. {copilot_session_usage-0.6.0 → copilot_session_usage-0.6.1}/.github/workflows/publish.yml +0 -0
  21. {copilot_session_usage-0.6.0 → copilot_session_usage-0.6.1}/.gitignore +0 -0
  22. {copilot_session_usage-0.6.0 → copilot_session_usage-0.6.1}/.readthedocs.yaml +0 -0
  23. {copilot_session_usage-0.6.0 → copilot_session_usage-0.6.1}/AGENTS.md +0 -0
  24. {copilot_session_usage-0.6.0 → copilot_session_usage-0.6.1}/CHANGELOG.md +0 -0
  25. {copilot_session_usage-0.6.0 → copilot_session_usage-0.6.1}/LICENSE +0 -0
  26. {copilot_session_usage-0.6.0 → copilot_session_usage-0.6.1}/README.md +0 -0
  27. {copilot_session_usage-0.6.0 → copilot_session_usage-0.6.1}/docs/source/_static/changelog.js +0 -0
  28. {copilot_session_usage-0.6.0 → copilot_session_usage-0.6.1}/docs/source/_static/custom.css +0 -0
  29. {copilot_session_usage-0.6.0 → copilot_session_usage-0.6.1}/docs/source/changelog.md +0 -0
  30. {copilot_session_usage-0.6.0 → copilot_session_usage-0.6.1}/docs/source/conf.py +0 -0
  31. {copilot_session_usage-0.6.0 → copilot_session_usage-0.6.1}/docs/source/explanation/how-cost-estimation-works.md +0 -0
  32. {copilot_session_usage-0.6.0 → copilot_session_usage-0.6.1}/docs/source/explanation/index.md +0 -0
  33. {copilot_session_usage-0.6.0 → copilot_session_usage-0.6.1}/docs/source/how-to/add-commit-trailer.md +0 -0
  34. {copilot_session_usage-0.6.0 → copilot_session_usage-0.6.1}/docs/source/how-to/analyze-specific-session.md +0 -0
  35. {copilot_session_usage-0.6.0 → copilot_session_usage-0.6.1}/docs/source/how-to/batch-and-spending.md +0 -0
  36. {copilot_session_usage-0.6.0 → copilot_session_usage-0.6.1}/docs/source/how-to/export-json.md +0 -0
  37. {copilot_session_usage-0.6.0 → copilot_session_usage-0.6.1}/docs/source/how-to/index.md +0 -0
  38. {copilot_session_usage-0.6.0 → copilot_session_usage-0.6.1}/docs/source/how-to/wsl2.md +0 -0
  39. {copilot_session_usage-0.6.0 → copilot_session_usage-0.6.1}/docs/source/index.md +0 -0
  40. {copilot_session_usage-0.6.0 → copilot_session_usage-0.6.1}/docs/source/installation.md +0 -0
  41. {copilot_session_usage-0.6.0 → copilot_session_usage-0.6.1}/docs/source/reference/api.md +0 -0
  42. {copilot_session_usage-0.6.0 → copilot_session_usage-0.6.1}/docs/source/reference/cli.md +0 -0
  43. {copilot_session_usage-0.6.0 → copilot_session_usage-0.6.1}/docs/source/reference/index.md +0 -0
  44. {copilot_session_usage-0.6.0 → copilot_session_usage-0.6.1}/docs/source/reference/pricing.md +0 -0
  45. {copilot_session_usage-0.6.0 → copilot_session_usage-0.6.1}/docs/source/tutorials/getting-started.md +0 -0
  46. {copilot_session_usage-0.6.0 → copilot_session_usage-0.6.1}/docs/source/tutorials/index.md +0 -0
  47. {copilot_session_usage-0.6.0 → copilot_session_usage-0.6.1}/justfile +0 -0
  48. {copilot_session_usage-0.6.0 → copilot_session_usage-0.6.1}/knowledge/_schema/Base.schema.yaml +0 -0
  49. {copilot_session_usage-0.6.0 → copilot_session_usage-0.6.1}/knowledge/_schema/Concept.schema.yaml +0 -0
  50. {copilot_session_usage-0.6.0 → copilot_session_usage-0.6.1}/knowledge/_schema/Experiment.schema.yaml +0 -0
  51. {copilot_session_usage-0.6.0 → copilot_session_usage-0.6.1}/knowledge/_schema/Finding.schema.yaml +0 -0
  52. {copilot_session_usage-0.6.0 → copilot_session_usage-0.6.1}/knowledge/_schema/Hypothesis.schema.yaml +0 -0
  53. {copilot_session_usage-0.6.0 → copilot_session_usage-0.6.1}/knowledge/_schema/Outcome.schema.yaml +0 -0
  54. {copilot_session_usage-0.6.0 → copilot_session_usage-0.6.1}/knowledge/_schema/Playbook.schema.yaml +0 -0
  55. {copilot_session_usage-0.6.0 → copilot_session_usage-0.6.1}/knowledge/_schema/Principle.schema.yaml +0 -0
  56. {copilot_session_usage-0.6.0 → copilot_session_usage-0.6.1}/knowledge/_schema/Reference.schema.yaml +0 -0
  57. {copilot_session_usage-0.6.0 → copilot_session_usage-0.6.1}/knowledge/_schema/Structure.schema.yaml +0 -0
  58. {copilot_session_usage-0.6.0 → copilot_session_usage-0.6.1}/knowledge/concepts/copilot-cli.md +0 -0
  59. {copilot_session_usage-0.6.0 → copilot_session_usage-0.6.1}/knowledge/concepts/index.md +0 -0
  60. {copilot_session_usage-0.6.0 → copilot_session_usage-0.6.1}/knowledge/concepts/overview.md +0 -0
  61. {copilot_session_usage-0.6.0 → copilot_session_usage-0.6.1}/knowledge/concepts/session-cost-analysis.md +0 -0
  62. {copilot_session_usage-0.6.0 → copilot_session_usage-0.6.1}/knowledge/concepts/threshold-based-pricing.md +0 -0
  63. {copilot_session_usage-0.6.0 → copilot_session_usage-0.6.1}/knowledge/experiments/index.md +0 -0
  64. {copilot_session_usage-0.6.0 → copilot_session_usage-0.6.1}/knowledge/experiments/verify-subagent-cost-attribution.md +0 -0
  65. {copilot_session_usage-0.6.0 → copilot_session_usage-0.6.1}/knowledge/findings/2026.07.02-00.00-subagent-logs-runsubagent-prefix.md +0 -0
  66. {copilot_session_usage-0.6.0 → copilot_session_usage-0.6.1}/knowledge/findings/2026.07.02-22.00-title-generation-not-counted-as-model-turn.md +0 -0
  67. {copilot_session_usage-0.6.0 → copilot_session_usage-0.6.1}/knowledge/findings/2026.07.02-23.00-cache-write-approximation.md +0 -0
  68. {copilot_session_usage-0.6.0 → copilot_session_usage-0.6.1}/knowledge/findings/index.md +0 -0
  69. {copilot_session_usage-0.6.0 → copilot_session_usage-0.6.1}/knowledge/guides/automation-scripts.md +0 -0
  70. {copilot_session_usage-0.6.0 → copilot_session_usage-0.6.1}/knowledge/guides/cost-optimization.md +0 -0
  71. {copilot_session_usage-0.6.0 → copilot_session_usage-0.6.1}/knowledge/guides/index.md +0 -0
  72. {copilot_session_usage-0.6.0 → copilot_session_usage-0.6.1}/knowledge/guides/wsl2-setup.md +0 -0
  73. {copilot_session_usage-0.6.0 → copilot_session_usage-0.6.1}/knowledge/ideas/index.md +0 -0
  74. {copilot_session_usage-0.6.0 → copilot_session_usage-0.6.1}/knowledge/ideas/multi-session-efficiency-analytics.md +0 -0
  75. {copilot_session_usage-0.6.0 → copilot_session_usage-0.6.1}/knowledge/index.md +0 -0
  76. {copilot_session_usage-0.6.0 → copilot_session_usage-0.6.1}/knowledge/log.md +0 -0
  77. {copilot_session_usage-0.6.0 → copilot_session_usage-0.6.1}/knowledge/principles/findings-are-immutable.md +0 -0
  78. {copilot_session_usage-0.6.0 → copilot_session_usage-0.6.1}/knowledge/principles/index.md +0 -0
  79. {copilot_session_usage-0.6.0 → copilot_session_usage-0.6.1}/knowledge/reference/debug-log-format.md +0 -0
  80. {copilot_session_usage-0.6.0 → copilot_session_usage-0.6.1}/knowledge/reference/index.md +0 -0
  81. {copilot_session_usage-0.6.0 → copilot_session_usage-0.6.1}/knowledge/reference/pricing-formats.md +0 -0
  82. {copilot_session_usage-0.6.0 → copilot_session_usage-0.6.1}/knowledge/structures/cache-cost-approximation.md +0 -0
  83. {copilot_session_usage-0.6.0 → copilot_session_usage-0.6.1}/knowledge/structures/index.md +0 -0
  84. {copilot_session_usage-0.6.0 → copilot_session_usage-0.6.1}/knowledge/structures/knowledge-base-information-types.md +0 -0
  85. {copilot_session_usage-0.6.0 → copilot_session_usage-0.6.1}/knowledge/structures/session-discovery-algorithm.md +0 -0
  86. {copilot_session_usage-0.6.0 → copilot_session_usage-0.6.1}/knowledge/structures/subagent-cost-tracking.md +0 -0
  87. {copilot_session_usage-0.6.0 → copilot_session_usage-0.6.1}/knowledge/structures/vscode-copilot-extension.md +0 -0
  88. {copilot_session_usage-0.6.0 → copilot_session_usage-0.6.1}/pyproject.toml +0 -0
  89. {copilot_session_usage-0.6.0 → copilot_session_usage-0.6.1}/scripts/refresh_pricing.py +0 -0
  90. {copilot_session_usage-0.6.0 → copilot_session_usage-0.6.1}/skills/copilot-session-usage/SKILL.md +0 -0
  91. {copilot_session_usage-0.6.0 → copilot_session_usage-0.6.1}/src/copilot_session_usage/__init__.py +0 -0
  92. {copilot_session_usage-0.6.0 → copilot_session_usage-0.6.1}/src/copilot_session_usage/_internal/__init__.py +0 -0
  93. {copilot_session_usage-0.6.0 → copilot_session_usage-0.6.1}/src/copilot_session_usage/_internal/copilot_cli.py +0 -0
  94. {copilot_session_usage-0.6.0 → copilot_session_usage-0.6.1}/src/copilot_session_usage/_internal/core.py +0 -0
  95. {copilot_session_usage-0.6.0 → copilot_session_usage-0.6.1}/src/copilot_session_usage/_internal/git.py +0 -0
  96. {copilot_session_usage-0.6.0 → copilot_session_usage-0.6.1}/src/copilot_session_usage/_internal/vscode.py +0 -0
  97. {copilot_session_usage-0.6.0 → copilot_session_usage-0.6.1}/src/copilot_session_usage/api.py +0 -0
  98. {copilot_session_usage-0.6.0 → copilot_session_usage-0.6.1}/src/copilot_session_usage/cli.py +0 -0
  99. {copilot_session_usage-0.6.0 → copilot_session_usage-0.6.1}/src/copilot_session_usage/data/__init__.py +0 -0
  100. {copilot_session_usage-0.6.0 → copilot_session_usage-0.6.1}/src/copilot_session_usage/data/custom-models-pricing.yml +0 -0
  101. {copilot_session_usage-0.6.0 → copilot_session_usage-0.6.1}/src/copilot_session_usage/data/models-and-pricing.lock +0 -0
  102. {copilot_session_usage-0.6.0 → copilot_session_usage-0.6.1}/src/copilot_session_usage/data/models-and-pricing.yml +0 -0
  103. {copilot_session_usage-0.6.0 → copilot_session_usage-0.6.1}/tests/conftest.py +0 -0
  104. {copilot_session_usage-0.6.0 → copilot_session_usage-0.6.1}/tests/test_api.py +0 -0
  105. {copilot_session_usage-0.6.0 → copilot_session_usage-0.6.1}/tests/test_cli.py +0 -0
  106. {copilot_session_usage-0.6.0 → copilot_session_usage-0.6.1}/tests/test_core.py +0 -0
  107. {copilot_session_usage-0.6.0 → copilot_session_usage-0.6.1}/tests/test_coverage_gaps.py +0 -0
  108. {copilot_session_usage-0.6.0 → copilot_session_usage-0.6.1}/tests/test_git.py +0 -0
  109. {copilot_session_usage-0.6.0 → copilot_session_usage-0.6.1}/tests/test_rendering.py +0 -0
  110. {copilot_session_usage-0.6.0 → copilot_session_usage-0.6.1}/tests/test_vscode.py +0 -0
  111. {copilot_session_usage-0.6.0 → copilot_session_usage-0.6.1}/tests/test_vscode_platform.py +0 -0
  112. {copilot_session_usage-0.6.0 → copilot_session_usage-0.6.1}/uv.lock +0 -0
  113. {copilot_session_usage-0.6.0 → copilot_session_usage-0.6.1}/uv.toml +0 -0
@@ -1,3 +1,10 @@
1
+ ---
2
+ name: gh-release-notes
3
+ description: Generate end-user-friendly GitHub release notes from the actual diff between releases, including user impact, examples, breaking changes, and public documentation links.
4
+ argument-hint: "from_tag=... to_tag=... repo_path=..."
5
+ user-invocable: true
6
+ ---
7
+
1
8
  # Release Notes Generator (Git Diff Based)
2
9
 
3
10
  Generate **end-user friendly** release notes by analyzing actual code changes between releases.
@@ -186,6 +193,15 @@ Organize changes into buckets. **Only include sections that have content.** Omit
186
193
 
187
194
  ---
188
195
 
196
+ ## Non-interactive automation mode
197
+
198
+ When this skill is invoked by a CI job with an explicit request for machine-readable output:
199
+
200
+ - Honor the requested tag range and repository path exactly.
201
+ - Do not modify, commit, or push repository files unless the caller explicitly requests it.
202
+ - Return only the final release-note Markdown, without an explanation or a code fence.
203
+ - Preserve the user-impact categories, examples, breaking-change detection, and public documentation links described above.
204
+
189
205
  ## How to Use This Skill in a Session
190
206
 
191
207
  **User Query:**
@@ -0,0 +1,72 @@
1
+ name: Refresh GitHub model pricing
2
+
3
+ on:
4
+ schedule:
5
+ # Run once a day, shortly after GitHub's documentation refresh window.
6
+ - cron: "17 2 * * *"
7
+ workflow_dispatch:
8
+
9
+ concurrency:
10
+ group: refresh-pricing
11
+ cancel-in-progress: false
12
+
13
+ permissions:
14
+ contents: write
15
+ pull-requests: write
16
+
17
+ jobs:
18
+ refresh-pricing:
19
+ runs-on: ubuntu-latest
20
+
21
+ steps:
22
+ - name: Check out repository
23
+ uses: actions/checkout@v4
24
+
25
+ - name: Install uv
26
+ uses: astral-sh/setup-uv@v5
27
+ with:
28
+ enable-cache: true
29
+ python-version: "3.12"
30
+
31
+ - name: Install just
32
+ uses: extractions/setup-just@v2
33
+
34
+ - name: Install dependencies
35
+ run: uv sync --frozen --all-groups
36
+
37
+ - name: Refresh pricing
38
+ run: just refresh-pricing
39
+
40
+ - name: Check for upstream pricing changes
41
+ id: changes
42
+ shell: bash
43
+ run: |
44
+ pricing_file="src/copilot_session_usage/data/models-and-pricing.yml"
45
+ if git diff --quiet -- "$pricing_file"; then
46
+ echo "changed=false" >> "$GITHUB_OUTPUT"
47
+ echo "No pricing YAML changes detected."
48
+ else
49
+ echo "changed=true" >> "$GITHUB_OUTPUT"
50
+ echo "Pricing YAML changed:"
51
+ git diff --stat -- "$pricing_file"
52
+ fi
53
+
54
+ - name: Create or update pull request
55
+ if: steps.changes.outputs.changed == 'true'
56
+ uses: peter-evans/create-pull-request@v7
57
+ with:
58
+ token: ${{ secrets.GITHUB_TOKEN }}
59
+ base: main
60
+ branch: automation/refresh-pricing
61
+ delete-branch: true
62
+ commit-message: "chore(pricing): refresh GitHub model pricing"
63
+ title: "chore(pricing): refresh GitHub model pricing"
64
+ body: |
65
+ This automated pull request updates the bundled GitHub Copilot model pricing data.
66
+
67
+ Source: https://raw.githubusercontent.com/github/docs/main/data/tables/copilot/models-and-pricing.yml
68
+
69
+ The update was generated by running `just refresh-pricing`.
70
+ add-paths: |
71
+ src/copilot_session_usage/data/models-and-pricing.yml
72
+ src/copilot_session_usage/data/models-and-pricing.lock
@@ -0,0 +1,127 @@
1
+ name: Generate release notes (manual)
2
+
3
+ on:
4
+ workflow_dispatch:
5
+ inputs:
6
+ tag:
7
+ description: "Existing version tag to generate a draft release for"
8
+ required: true
9
+ type: string
10
+
11
+ concurrency:
12
+ group: release-notes-${{ github.ref }}
13
+ cancel-in-progress: false
14
+
15
+ permissions:
16
+ contents: write
17
+
18
+ jobs:
19
+ generate-release-notes:
20
+ runs-on: ubuntu-latest
21
+
22
+ steps:
23
+ - name: Check out tagged source
24
+ uses: actions/checkout@v4
25
+ with:
26
+ fetch-depth: 0
27
+ ref: ${{ inputs.tag || github.ref }}
28
+
29
+ - name: Resolve release range
30
+ id: range
31
+ shell: bash
32
+ env:
33
+ TARGET_TAG: ${{ inputs.tag || github.ref_name }}
34
+ run: |
35
+ set -euo pipefail
36
+
37
+ if [[ ! "$TARGET_TAG" =~ ^v[0-9]+\.[0-9]+\.[0-9]+([.-].*)?$ ]]; then
38
+ echo "::error::Expected a semantic-version tag such as v0.7.0, got '$TARGET_TAG'."
39
+ exit 1
40
+ fi
41
+
42
+ previous_tag="$(git tag --sort=-v:refname --merged "$TARGET_TAG" \
43
+ | awk -v current="$TARGET_TAG" '$0 != current { print; exit }')"
44
+
45
+ if [[ -z "$previous_tag" ]]; then
46
+ echo "::error::Could not find a previous release tag for $TARGET_TAG."
47
+ exit 1
48
+ fi
49
+
50
+ echo "target_tag=$TARGET_TAG" >> "$GITHUB_OUTPUT"
51
+ echo "previous_tag=$previous_tag" >> "$GITHUB_OUTPUT"
52
+ echo "Generating notes from $previous_tag to $TARGET_TAG."
53
+
54
+ - name: Verify release-note skill
55
+ env:
56
+ COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
57
+ run: |
58
+ set -euo pipefail
59
+
60
+ if [[ -z "${COPILOT_GITHUB_TOKEN:-}" ]]; then
61
+ echo "::error::The COPILOT_GITHUB_TOKEN repository secret is not configured."
62
+ echo "Create a fine-grained PAT with the Copilot Requests permission and add it as a repository secret."
63
+ exit 1
64
+ fi
65
+
66
+ if ! gh copilot -- skill list | grep -Fq 'gh-release-notes'; then
67
+ echo "::error::The gh-release-notes skill was not discovered by GitHub CLI."
68
+ exit 1
69
+ fi
70
+
71
+ - name: Generate release notes with the skill
72
+ env:
73
+ COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
74
+ PREVIOUS_TAG: ${{ steps.range.outputs.previous_tag }}
75
+ TARGET_TAG: ${{ steps.range.outputs.target_tag }}
76
+ run: |
77
+ set -euo pipefail
78
+
79
+ prompt="Use the /gh-release-notes skill.
80
+
81
+ Generate the final release notes for ${TARGET_TAG}, covering the actual changes from ${PREVIOUS_TAG} to ${TARGET_TAG} in ${GITHUB_WORKSPACE}.
82
+
83
+ Follow the skill's diff-based analysis process. Use the project's public documentation URL when documentation links are appropriate. Return only the final release-note Markdown, with no introduction, explanation, or code fence. Do not modify any repository files."
84
+
85
+ gh copilot -- \
86
+ --prompt "$prompt" \
87
+ --silent \
88
+ --no-ask-user \
89
+ --no-auto-update \
90
+ --no-color \
91
+ --disable-builtin-mcps \
92
+ --available-tools='read,shell' \
93
+ --allow-tool='read' \
94
+ --allow-tool='shell(git:*)' \
95
+ --allow-url='https://github.com' \
96
+ --allow-url='https://copilot-session-usage.readthedocs.io' \
97
+ > release-notes.md
98
+
99
+ if [[ ! -s release-notes.md ]]; then
100
+ echo "::error::The release-note skill returned an empty document."
101
+ exit 1
102
+ fi
103
+
104
+ - name: Upload generated notes for review
105
+ uses: actions/upload-artifact@v4
106
+ with:
107
+ name: release-notes-${{ steps.range.outputs.target_tag }}
108
+ path: release-notes.md
109
+
110
+ - name: Create draft GitHub release
111
+ env:
112
+ GH_TOKEN: ${{ github.token }}
113
+ TARGET_TAG: ${{ steps.range.outputs.target_tag }}
114
+ run: |
115
+ set -euo pipefail
116
+
117
+ if gh release view "$TARGET_TAG" --repo "$GITHUB_REPOSITORY" >/dev/null 2>&1; then
118
+ echo "::error::A GitHub Release already exists for $TARGET_TAG."
119
+ exit 1
120
+ fi
121
+
122
+ gh release create "$TARGET_TAG" \
123
+ --repo "$GITHUB_REPOSITORY" \
124
+ --verify-tag \
125
+ --draft \
126
+ --title "$TARGET_TAG" \
127
+ --notes-file release-notes.md
@@ -0,0 +1,195 @@
1
+ name: Release
2
+
3
+ on:
4
+ workflow_dispatch:
5
+ inputs:
6
+ increment:
7
+ description: "Version bump to apply"
8
+ required: true
9
+ type: choice
10
+ default: auto
11
+ options:
12
+ - auto
13
+ - major
14
+ - minor
15
+ - patch
16
+ draft:
17
+ description: "Create a draft release instead of publishing it"
18
+ required: true
19
+ type: boolean
20
+ default: false
21
+ force:
22
+ description: "Force a patch release when auto finds no eligible commit"
23
+ required: true
24
+ type: boolean
25
+ default: false
26
+
27
+ concurrency:
28
+ group: release
29
+ cancel-in-progress: false
30
+
31
+ permissions:
32
+ contents: write
33
+
34
+ jobs:
35
+ release:
36
+ runs-on: ubuntu-latest
37
+ env:
38
+ GH_TOKEN: ${{ github.token }}
39
+ RELEASE_BRANCH: ${{ github.event.repository.default_branch }}
40
+ INCREMENT: ${{ inputs.increment }}
41
+ DRAFT_RELEASE: ${{ inputs.draft }}
42
+ FORCE_RELEASE: ${{ inputs.force }}
43
+
44
+ steps:
45
+ - name: Check out the default branch
46
+ uses: actions/checkout@v4
47
+ with:
48
+ fetch-depth: 0
49
+ ref: ${{ github.event.repository.default_branch }}
50
+
51
+ - name: Install uv
52
+ uses: astral-sh/setup-uv@v5
53
+
54
+ - name: Install release tooling
55
+ run: uv sync --locked --all-groups
56
+
57
+ - name: Calculate version and create tag
58
+ id: version
59
+ shell: bash
60
+ run: |
61
+ set -euo pipefail
62
+
63
+ if [[ -n "$(git status --porcelain)" ]]; then
64
+ echo "::error::The release branch is not clean. Refusing to create a release."
65
+ git status --short
66
+ exit 1
67
+ fi
68
+
69
+ git switch --force-create "$RELEASE_BRANCH" "origin/$RELEASE_BRANCH"
70
+
71
+ previous_tag="$(git describe --tags --abbrev=0 HEAD 2>/dev/null || true)"
72
+ if [[ -z "$previous_tag" ]]; then
73
+ echo "::error::Could not find a previous release tag."
74
+ exit 1
75
+ fi
76
+
77
+ if [[ "$INCREMENT" == "auto" ]]; then
78
+ bump_args=(--get-next --yes)
79
+ if [[ "$FORCE_RELEASE" == "true" ]]; then
80
+ bump_args+=(--allow-no-commit)
81
+ fi
82
+ if ! next_version_output="$(uv run --no-sync cz bump "${bump_args[@]}" 2>&1)"; then
83
+ echo "::error::No eligible conventional commits were found for an automatic release."
84
+ printf '%s\n' "$next_version_output"
85
+ exit 1
86
+ fi
87
+ else
88
+ echo "Using explicit $INCREMENT bump."
89
+ next_version_output="$(uv run --no-sync cz bump --get-next \
90
+ --increment "${INCREMENT^^}" --yes --allow-no-commit)"
91
+ fi
92
+
93
+ next_version="$(printf '%s\n' "$next_version_output" | tail -n 1)"
94
+ if [[ ! "$next_version" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
95
+ echo "::error::Commitizen returned an invalid next version: '$next_version'."
96
+ printf '%s\n' "$next_version_output"
97
+ exit 1
98
+ fi
99
+
100
+ target_tag="v$next_version"
101
+ if [[ ! "$target_tag" =~ ^v[0-9]+\.[0-9]+\.[0-9]+([.-].*)?$ ]]; then
102
+ echo "::error::The calculated version did not create a semantic-version tag."
103
+ exit 1
104
+ fi
105
+ if git rev-parse "$target_tag" >/dev/null 2>&1; then
106
+ echo "::error::The tag $target_tag already exists."
107
+ exit 1
108
+ fi
109
+
110
+ git tag "$target_tag"
111
+ git push origin "$target_tag"
112
+
113
+ echo "previous_tag=$previous_tag" >> "$GITHUB_OUTPUT"
114
+ echo "target_tag=$target_tag" >> "$GITHUB_OUTPUT"
115
+ echo "Generated $target_tag from $previous_tag without modifying the default branch."
116
+
117
+ - name: Verify release-note skill
118
+ env:
119
+ COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
120
+ run: |
121
+ set -euo pipefail
122
+
123
+ if [[ -z "${COPILOT_GITHUB_TOKEN:-}" ]]; then
124
+ echo "::error::The COPILOT_GITHUB_TOKEN repository secret is not configured."
125
+ echo "Create a fine-grained PAT with the Copilot Requests permission and add it as a repository secret."
126
+ exit 1
127
+ fi
128
+
129
+ if ! gh copilot -- skill list | grep -Fq 'gh-release-notes'; then
130
+ echo "::error::The gh-release-notes skill was not discovered by GitHub CLI."
131
+ exit 1
132
+ fi
133
+
134
+ - name: Generate release notes with gh-release-notes
135
+ env:
136
+ COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
137
+ PREVIOUS_TAG: ${{ steps.version.outputs.previous_tag }}
138
+ TARGET_TAG: ${{ steps.version.outputs.target_tag }}
139
+ run: |
140
+ set -euo pipefail
141
+
142
+ prompt="Use the /gh-release-notes skill.
143
+
144
+ Generate the final release notes for ${TARGET_TAG}, covering the actual changes from ${PREVIOUS_TAG} to ${TARGET_TAG} in ${GITHUB_WORKSPACE}.
145
+
146
+ Follow the skill's diff-based analysis process. Use the project's public documentation URL when documentation links are appropriate. Return only the final release-note Markdown, with no introduction, explanation, or code fence. Do not modify, commit, or push any repository files."
147
+
148
+ gh copilot -- \
149
+ --prompt "$prompt" \
150
+ --silent \
151
+ --no-ask-user \
152
+ --no-auto-update \
153
+ --no-color \
154
+ --disable-builtin-mcps \
155
+ --available-tools='read,shell' \
156
+ --allow-tool='read' \
157
+ --allow-tool='shell(git:*)' \
158
+ --allow-url='https://github.com' \
159
+ --allow-url='https://copilot-session-usage.readthedocs.io' \
160
+ > release-notes.md
161
+
162
+ if [[ ! -s release-notes.md ]]; then
163
+ echo "::error::The release-note skill returned an empty document."
164
+ exit 1
165
+ fi
166
+
167
+ - name: Upload generated notes
168
+ uses: actions/upload-artifact@v4
169
+ with:
170
+ name: release-notes-${{ steps.version.outputs.target_tag }}
171
+ path: release-notes.md
172
+
173
+ - name: Create GitHub release
174
+ env:
175
+ TARGET_TAG: ${{ steps.version.outputs.target_tag }}
176
+ shell: bash
177
+ run: |
178
+ set -euo pipefail
179
+
180
+ if gh release view "$TARGET_TAG" --repo "$GITHUB_REPOSITORY" >/dev/null 2>&1; then
181
+ echo "::error::A GitHub Release already exists for $TARGET_TAG."
182
+ exit 1
183
+ fi
184
+
185
+ release_args=(
186
+ --repo "$GITHUB_REPOSITORY"
187
+ --verify-tag
188
+ --title "$TARGET_TAG"
189
+ --notes-file release-notes.md
190
+ )
191
+ if [[ "$DRAFT_RELEASE" == "true" ]]; then
192
+ release_args+=(--draft)
193
+ fi
194
+
195
+ gh release create "$TARGET_TAG" "${release_args[@]}"
@@ -0,0 +1,65 @@
1
+ # Contributing to copilot-session-usage
2
+
3
+ ## Setup
4
+
5
+ ```bash
6
+ # Clone the repository
7
+ git clone https://github.com/gsemet/copilot-session-usage
8
+ cd copilot-session-usage
9
+
10
+ # Install dependencies (uses uv)
11
+ just dev
12
+ ```
13
+
14
+ ## Development workflow
15
+
16
+ ```bash
17
+ # Run tests
18
+ just test
19
+
20
+ # Run full validation
21
+ just preflight
22
+
23
+ # Build documentation
24
+ just docs
25
+
26
+ # Serve docs with auto-reload
27
+ just docs-serve
28
+ ```
29
+
30
+ ## Code style
31
+
32
+ - **Formatter**: ruff (line length 100)
33
+ - **Linter**: ruff, ty, mypy
34
+ - **Type checker**: mypy (strict)
35
+ - **Test runner**: pytest with pytest-cov
36
+
37
+ ## Adding features
38
+
39
+ 1. Add or update code in `src/copilot_session_usage/`
40
+ 2. Add tests in `tests/`
41
+ 3. Update documentation in `docs/source/`
42
+ 4. Run `just preflight` to validate
43
+ 5. Submit a pull request
44
+
45
+ ## Release process
46
+
47
+ 1. Open the **Release** workflow under the repository's Actions tab.
48
+ 2. Run it from the default branch, choosing `auto` to derive the major, minor, or
49
+ patch bump from conventional commits, or choose an explicit bump. Enable
50
+ `force` to create a patch release when `auto` finds no eligible commit.
51
+ 3. The workflow uses Commitizen to calculate the next version, creates and pushes
52
+ the `vX.Y.Z` tag on the existing default-branch commit, and invokes the
53
+ `gh-release-notes` skill through `gh copilot`. It does not create a
54
+ version/changelog commit.
55
+ 4. The generated notes are uploaded as an artifact and used to create the GitHub
56
+ Release. Enable the `draft` option if the release needs review before publishing.
57
+ 5. Publishing the GitHub Release triggers CI and publishes the package to PyPI via
58
+ OIDC.
59
+
60
+ The workflow requires a repository secret named `COPILOT_GITHUB_TOKEN`. It must be
61
+ a fine-grained token with the **Copilot Requests** permission. The normal Actions
62
+ `GITHUB_TOKEN` is used for repository writes and GitHub Release creation.
63
+
64
+ The **Generate release notes (manual)** workflow remains available when notes need
65
+ to be regenerated for an existing tag without creating a new release.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: copilot-session-usage
3
- Version: 0.6.0
3
+ Version: 0.6.1
4
4
  Summary: Usage and cost analytics for GitHub Copilot and Copilot-CLI session logs
5
5
  Project-URL: Homepage, https://github.com/gsemet/copilot-session-usage
6
6
  Project-URL: Documentation, https://copilot-session-usage.readthedocs.io/en/stable/