Thank you for your interest in contributing! This document provides guidelines and instructions for contributing to this project.
By participating in this project, you agree to maintain a respectful and inclusive environment for everyone.
# Fork the repository on GitHub, then clone your fork
git clone https://github.com/<your-username>/stream-deck-github-utilities.git
cd stream-deck-github-utilities- Node.js >= 20: Download
- Stream Deck CLI:
npm install -g @elgato/cli - Stream Deck Software >= 6.9: Download
npm install# Start the development watcher
npm run watch
# In a separate terminal, run tests in watch mode
npm run test:watchUse descriptive branch names:
feature/action-name— for new features/actionsfix/issue-description— for bug fixesdocs/description— for documentation changestest/description— for test additions or changesrefactor/description— for code refactoring
Follow Conventional Commits:
type(scope): description
feat(actions): add repository star count action
fix(utils): handle null token in validation
test(utils): add edge cases for formatCount
docs(readme): update installation instructions
Types: feat, fix, docs, test, refactor, chore, style, perf, ci
- TypeScript is mandatory — no plain JavaScript in
src/. - Use strict mode — the tsconfig enforces it.
- Follow existing code patterns and naming conventions.
- Use tabs for indentation (matching the Stream Deck SDK convention).
- Always type function parameters and return values explicitly.
All code changes must include tests. This is non-negotiable.
- Place test files alongside the code they test or in the
tests/directory mirroring thesrc/structure. - Test file naming:
*.test.tsor*.spec.ts - Write tests for:
- Happy path — expected normal usage
- Edge cases — boundary values, empty inputs, null/undefined
- Error cases — invalid inputs, API failures, network errors
- Coverage thresholds (80% for branches, functions, lines, statements) are enforced.
- All tests must pass before a PR can be merged.
Cross-layer tests live in tests/integration/. These mock only globalThis.fetch and let real coordinator, extractors, renderers, and strategies run. Add integration tests when:
- Adding new data fragments (verify extraction → cache → result flow)
- Changing API response handling (verify schema → transformation → rendering)
SVG renderer output is captured as golden master snapshots in tests/renderers/. After changing any render function, run npx vitest run tests/renderers/ --update to regenerate snapshots, then review the diff.
Run tests:
npm test # Run all tests once
npm run test:watch # Watch mode
npm run test:coverage # With coverage reportFollow these steps and refer to the existing Repo Stats and Workflow Status actions as references.
Note: All actions now use the GraphQL Query Coordinator for data fetching. New actions should integrate with the coordinator rather than making direct REST API calls. The coordinator batches queries across actions sharing the same repository, reducing API calls and improving cache efficiency.
- Define settings in
src/types.ts— must include[key: string]: JsonValueindex signature - Create action class in
src/actions/your-action.ts:- Use
@action({ UUID: "com.pedrofuentes.github-utilities.your-action" })decorator - Extend
BaseGitHubAction<YourSettings>(from./base-github-action.js) — provides polling, URL debouncing, error handling, PI data requests, and cleanup - Implement
onWillAppear,onKeyDown,onDidReceiveSettings - Override
onWillDisappear— callawait super.onWillDisappear(ev)then clean up action-specific state - For double-click: use
this.urlOpener.handlePress()+this.urlOpener.scheduleOpen()(inherited from base)
- Use
- Integrate with GraphQL Coordinator (for actions that query GitHub data):
- Import
coordinatorfromsrc/utils/graphql-query-coordinator - Subscribe in
onWillAppearwith the data fragments your action needs - Unsubscribe is handled by
BaseGitHubAction.onWillDisappear() - Fetch data via
coordinator.fetchData()instead of direct REST API calls - For force-refresh (double-click), use
coordinator.invalidateAndFetch() - Add a new
FragmentStrategyinsrc/utils/fragment-strategies.tsif your action needs new data - Register the strategy in the
fragmentRegistryMap - See
src/actions/discussions-monitor.tsorsrc/actions/projects-board.tsfor examples
- Import
- Register in
src/plugin.ts - Add manifest entry in
plugin/manifest.json - Create Property Inspector in
plugin/ui/usingsdpi-components.js - Create icons in
plugin/imgs/actions/your-action/(icon.svg 20x20, key.svg 144x144) - Use button renderer from
src/utils/button-renderer.tsfor button SVGs - Write tests in
tests/actions/your-action.test.ts— mock SDK withvi.hoisted()+vi.mock() - Update README features section and roadmap
- Add the function to the appropriate file in
src/utils/ - Export it from
src/utils/index.ts - Write tests covering all code paths and edge cases
- Document the function with JSDoc comments
- For new API functions that call GitHub endpoints, add a Zod schema in
src/utils/github-api/schemas.tsand use.parse()instead ofastype assertions on API responses.
The plugin uses a GraphQL Query Coordinator layer between actions and the GitHub API. Instead of each action making independent REST/GraphQL calls, all 14 actions subscribe to the coordinator with the data fragments they need. The coordinator:
- Batches queries — combines fragments from multiple actions into a single GraphQL request per repository
- Caches per-repo —
repo-data-cache.tstracks field-level staleness so only stale fields are re-fetched - Builds queries dynamically —
graphql-query-builder.tscomposes the minimal GraphQL query for requested fields - Dispatches via strategies —
fragment-strategies.tsencapsulates each fragment's extraction, REST fallback, and result assignment - Retries transient failures —
fetchWithRetry()ingithub-api/core.tsretries 429/502/503/504 with exponential backoff
Key files:
src/utils/graphql-query-coordinator.ts— singleton coordinator (subscribe, fetch, invalidate)src/utils/fragment-strategies.ts— strategy pattern with 12 fragment strategiessrc/utils/graphql-query-builder.ts— dynamic query compositionsrc/utils/repo-data-cache.ts— per-repo cache with field-level TTLsrc/utils/data-fragments.ts— GraphQL→REST interface extractorssrc/utils/github-api/— domain-split API modules (core, repos, PRs, issues, workflows, security, datasources)
- Run the full test suite:
npm test - Run linting:
npm run lint - Build successfully:
npm run build - Validate the plugin:
npm run validate - Update documentation if your changes affect the public API or user experience.
- Tests added/updated for all changes
- All tests pass (
npm test) - Code lints cleanly (
npm run lint) - Build succeeds (
npm run build) - Plugin validates (
npm run validate) - Documentation updated (README, JSDoc, etc.)
- Commit messages follow conventional commit format
- Branch is up-to-date with
main
- Submit your PR against the
mainbranch. - Provide a clear description of what your PR does and why.
- Link any related issues.
- A maintainer will review your PR and may request changes.
- Once approved, a maintainer will merge your PR.
Release packages are created using the Stream Deck CLI:
npm run packThis command will:
- Build the TypeScript source
- Run the full test suite (all tests must pass)
- Validate the plugin manifest
- Create a
.streamDeckPluginfile in therelease/directory
Only maintainers create release packages. Contributors should focus on code changes and tests.
- Use GitHub Issues to report bugs or request features.
- Include steps to reproduce for bugs.
- Include your Stream Deck software version and OS version.
Open a discussion or reach out via the Marketplace Makers Discord.