diff --git a/.github/workflows/run_tests.yml b/.github/workflows/run_tests.yml new file mode 100644 index 0000000..eeece84 --- /dev/null +++ b/.github/workflows/run_tests.yml @@ -0,0 +1,32 @@ +name: Run Tests + +on: + push: + branches: [main] + pull_request: + branches: [main] + +jobs: + test: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: 20 + + - name: Install dependencies + run: yarn install + + - name: Run tests with coverage + run: yarn vitest run --coverage + + # - name: Upload coverage to Codecov + # uses: codecov/codecov-action@v5 + # with: + # files: coverage/coverage-final.json + # token: ${{ secrets.CODECOV_TOKEN }} # optional if public repo diff --git a/.github/workflows/todo.yml b/.github/workflows/todo.yml index 15f0804..3061d3d 100644 --- a/.github/workflows/todo.yml +++ b/.github/workflows/todo.yml @@ -1,4 +1,4 @@ -name: Smart TODO Check +name: Smart TODO Tracker on: push: @@ -7,6 +7,10 @@ on: jobs: smart-todo: runs-on: ubuntu-latest + permissions: + contents: write + issues: write + steps: - uses: actions/checkout@v3 @@ -22,3 +26,20 @@ jobs: uses: ./ with: repo-token: ${{ secrets.PERSONAL_ACCESS_TOKEN }} + issue-title-template: src/templates/issueTitle.txt + issue-body-template: src/templates/issueBody.md + report: true + + - name: Upload TODO report + uses: actions/upload-artifact@v3 + with: + name: todo-report + path: TODO_REPORT.md + + - name: Commit TODO report + run: | + git config --global user.name "github-actions[bot]" + git config --global user.email "github-actions[bot]@users.noreply.github.com" + git add TODO_REPORT.md + git commit -m "chore(report): update TODO report" || echo "No changes" + git push diff --git a/.github/workflows/todo/issueBody.md b/.github/workflows/todo/issueBody.md new file mode 100644 index 0000000..db52414 --- /dev/null +++ b/.github/workflows/todo/issueBody.md @@ -0,0 +1,3 @@ +π **File:** `{{file}}:{{line}}` + +π **Content:** \ No newline at end of file diff --git a/.github/workflows/todo/issueTitle.txt b/.github/workflows/todo/issueTitle.txt new file mode 100644 index 0000000..9dcd6c3 --- /dev/null +++ b/.github/workflows/todo/issueTitle.txt @@ -0,0 +1 @@ +[{{tag}}] {{text}} diff --git a/.gitignore b/.gitignore index f94630c..dcd9a1e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ node_modules/ dist/ -.env \ No newline at end of file +.env + + +TODO_REPORT.md \ No newline at end of file diff --git a/README.md b/README.md index cd815a6..330b01e 100644 --- a/README.md +++ b/README.md @@ -1 +1,138 @@ -# smart-todo-action \ No newline at end of file +# π§ smart-todo-action + +A GitHub Action that scans your codebase for inline TODOs, FIXMEs, and BUG comments, and automatically creates GitHub Issues β with support for labels, metadata parsing, and semantic enrichment. + +--- + +## π Features + +- β Detects `TODO`, `FIXME`, `BUG`, and `HACK` comments +- β Supports multiple languages: `.ts`, `.js`, `.py`, `.go`, `.html`, etc. +- β Extracts metadata like `priority`, `due`, etc. +- β Automatically labels issues based on type and metadata +- β Creates labels on the fly if they don't exist + +--- + +## βοΈ Usage + +### 1. Add the Action to your workflow + +```yaml +name: Smart TODO Tracker + +on: + push: + branches: [main] + +jobs: + smart-todo: + runs-on: ubuntu-latest + permissions: + issues: write + + steps: + - uses: actions/checkout@v3 + + - name: Setup Node.js + uses: actions/setup-node@v3 + with: + node-version: 20 + + - run: yarn install + - run: yarn prepare + + - name: Run Smart TODO Action + uses: ./ + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} +``` + +## π Example TODOs + +```ts +// TODO(priority=high, due=2025-06-01): Refactor this method for performance +// FIXME: Handle null input properly +// BUG: This causes a crash when file is empty +``` + +## π·οΈ Automatic Labels + +Based on your TODO comment, the following labels will be applied: + +| Tag | Label(s) | +|--------|-----------------------------------------------| +| TODO | `enhancement`, `priority:high`, `due:2025-06-01` | +| FIXME | `bug` | +| BUG | `bug` | +| HACK | `technical-debt` | + +If a label like `priority:high` or `due:2025-06-01` doesn't exist, it will be automatically created. + +--- + +## π Notes + +- Max **5 issues** are created per run to avoid rate limiting +- **Duplicate detection** is not yet implemented _(coming soon)_ +- All labels are **auto-created with default colors** if missing + +--- + +## π€ Coming Soon + +- β Issue deduplication +- β Custom templates for issue bodies +- β CLI usage outside GitHub +- β LLM-powered summarization and classification +- β Support for more languages and comment styles +- β Customizable label creation and management +- β Integration with project management tools (e.g., Jira, Trello) +- β Support for multiple repositories in a single run +- β Rate limiting and error handling improvements +- β Customizable issue creation frequency (e.g., daily, weekly) +- β Support for user-defined metadata tags +- β Customizable issue assignment (e.g., to specific users or teams) +- β Support for issue templates and custom fields +- β Integration with CI/CD pipelines for automated issue tracking +- β Support for issue comments and discussions +- β Customizable notification settings (e.g., email, Slack) +- β Support for issue closing and resolution tracking +- β Customizable issue lifecycle management (e.g., open, in progress, closed) + + +```plaintext +smart-todo-action/ +βββ .github/ +β βββ workflows/ +β βββ todo.yml +β +βββ dist/ +β βββ index.js +β +βββ src/ +β βββ core/ +β β βββ issueManager.ts +β β βββ labelManager.ts # π Label logic (static + metadata + creation) +β β βββ report.ts +β β βββ todoUtils.ts +β β βββ __tests__/ # (opcional) unit tests +β +β βββ parser/ +β β βββ extractTodosFromDir.ts +β β βββ extractTodosFromFile.ts +β β βββ types.ts +β +β βββ templates/ +β β βββ issueTitle.txt +β β βββ issueBody.md +β β βββ utils.ts +β +β βββ ActionMain.ts +β +βββ .gitignore +βββ action.yml +βββ package.json +βββ tsconfig.json +βββ README.md +``` \ No newline at end of file diff --git a/ROADMAP.md b/ROADMAP.md index 42989c6..78909d5 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -44,8 +44,9 @@ A smart GitHub Action that detects, classifies, and transforms inline TODOs in y > Provide visibility into the evolution and structure of tracked TODOs. -- [ ] Markdown/HTML dashboard with summary statistics - _Total TODOs, grouped by folder, priority, author (`git blame`)_ +- [X] Markdown/HTML dashboard with summary statistics + _Total TODOs, grouped by folder, priority, author (`git blame`) β **now sorted by `priority` and `due` date**_ + - [ ] TODO history tracking (added/removed/modified) - [ ] Due date notifications or PR comments @@ -68,101 +69,3 @@ A smart GitHub Action that detects, classifies, and transforms inline TODOs in y - Clean architecture and modularity are core principles from day one. - LLM functionality will be optional and clearly separated from core logic. - Built with automation, extensibility, and developer workflows in mind. - ---- - -# π Roadmap for TODO Issue Tracker 2.0 - -This project aims to build an intelligent GitHub Action that scans your codebase for TODOs, classifies them, and transforms them into contextualized GitHub Issues β with semantic analysis and multi-platform integration. - ---- - -## π§± Phase 1: Foundations and Parity with the Original Project - -π― Goal: Recreate the original functionality with clean, modular, and testable code. - -- [ ] Create the base project structure - - `src/` folder for source code - - Subfolders: `core/`, `parser/`, `tasks/`, `templates/`, etc. - -- [ ] Implement TODO parser - - Detect `TODO`, `FIXME`, `BUG`, etc. comments - - Support for multiple languages (`.js`, `.ts`, `.py`, `.go`, etc.) - -- [ ] Initial task system: GitHub Issues - - Create, update, and remove issues based on detected TODOs - -- [ ] Templating system for issue creation - - Customizable titles and descriptions via templates - -- [ ] Functional GitHub Action workflow - - `action.yml` definition file - - Example usage in `.github/workflows/todo.yml` - -- [ ] Unit testing with Jest or Vitest - ---- - -## π§ Phase 2: Intelligence and Semantics - -π― Goal: Make the system smarter by leveraging LLMs and contextual awareness. - -- [ ] Automatic TODO classification - - Use LLMs or heuristics to classify as `bug`, `enhancement`, `refactor`, etc. - -- [ ] Auto-generate issue titles and descriptions using LLMs - _Example: `Review sorting algorithm` β `Optimize Sorting Algorithm for Edge Cases`_ - -- [ ] Extract `due date` and `priority` via inline metadata parsing - _Example: `TODO(priority=high, due=2025-06-01): improve this logic`_ - ---- - -## π Phase 3: Extended Support - -π― Goal: Make the project adaptable to diverse environments and workflows. - -- [ ] Support for multiple task management platforms - _GitHub, Jira, Notion, Trello, Linear (via APIs)_ - -- [ ] Internationalization (i18n) - _Detect TODOs written in different languages_ - -- [ ] Support for additional file types - _`.ipynb`, `.yaml`, `.md`, `.json`, `.xml`, and more_ - ---- - -## π Phase 4: Analysis and Reporting - -π― Goal: Provide visibility into the state and evolution of TODOs. - -- [ ] Markdown/HTML dashboard with metrics - _Total TODOs, grouped by folder, priority, author (`git blame`)_ - -- [ ] TODO history tracking - _Track when TODOs are added, removed, or changed over time_ - -- [ ] Notifications and reminders - _Comment on PRs or issues when due dates are approaching_ - ---- - -## π Phase 5: Optimizations and Contributions - -π― Goal: Ensure quality, performance, and ease of collaboration. - -- [ ] Plugin-based modular architecture -- [ ] CLI support (standalone usage outside GitHub Actions) -- [ ] Test coverage >90% -- [ ] Full documentation with usage examples -- [ ] Publish to GitHub Marketplace as an official Action - ---- - -## π Notes - -- Modularity, testability, and code clarity are priorities from day one. -- LLM integration will be optional and cleanly decoupled from core logic. -- Designed with automation, extensibility, and developer experience in mind. - diff --git a/action.yml b/action.yml index a66d0d0..9eaf551 100644 --- a/action.yml +++ b/action.yml @@ -4,8 +4,21 @@ author: 'Diogo Ribeiro' inputs: repo-token: - description: 'GitHub token com permissΓ£o para criar issues' required: true + description: GitHub token to create issues + + report: + required: false + description: Whether to generate a TODO markdown report + default: 'false' + + issue-title-template: + required: false + description: Optional path to custom issue title template + + issue-body-template: + required: false + description: Optional path to custom issue body template runs: using: 'node20' @@ -14,3 +27,4 @@ runs: branding: icon: 'check-circle' color: 'blue' + diff --git a/coverage/base.css b/coverage/base.css new file mode 100644 index 0000000..f418035 --- /dev/null +++ b/coverage/base.css @@ -0,0 +1,224 @@ +body, html { + margin:0; padding: 0; + height: 100%; +} +body { + font-family: Helvetica Neue, Helvetica, Arial; + font-size: 14px; + color:#333; +} +.small { font-size: 12px; } +*, *:after, *:before { + -webkit-box-sizing:border-box; + -moz-box-sizing:border-box; + box-sizing:border-box; + } +h1 { font-size: 20px; margin: 0;} +h2 { font-size: 14px; } +pre { + font: 12px/1.4 Consolas, "Liberation Mono", Menlo, Courier, monospace; + margin: 0; + padding: 0; + -moz-tab-size: 2; + -o-tab-size: 2; + tab-size: 2; +} +a { color:#0074D9; text-decoration:none; } +a:hover { text-decoration:underline; } +.strong { font-weight: bold; } +.space-top1 { padding: 10px 0 0 0; } +.pad2y { padding: 20px 0; } +.pad1y { padding: 10px 0; } +.pad2x { padding: 0 20px; } +.pad2 { padding: 20px; } +.pad1 { padding: 10px; } +.space-left2 { padding-left:55px; } +.space-right2 { padding-right:20px; } +.center { text-align:center; } +.clearfix { display:block; } +.clearfix:after { + content:''; + display:block; + height:0; + clear:both; + visibility:hidden; + } +.fl { float: left; } +@media only screen and (max-width:640px) { + .col3 { width:100%; max-width:100%; } + .hide-mobile { display:none!important; } +} + +.quiet { + color: #7f7f7f; + color: rgba(0,0,0,0.5); +} +.quiet a { opacity: 0.7; } + +.fraction { + font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; + font-size: 10px; + color: #555; + background: #E8E8E8; + padding: 4px 5px; + border-radius: 3px; + vertical-align: middle; +} + +div.path a:link, div.path a:visited { color: #333; } +table.coverage { + border-collapse: collapse; + margin: 10px 0 0 0; + padding: 0; +} + +table.coverage td { + margin: 0; + padding: 0; + vertical-align: top; +} +table.coverage td.line-count { + text-align: right; + padding: 0 5px 0 20px; +} +table.coverage td.line-coverage { + text-align: right; + padding-right: 10px; + min-width:20px; +} + +table.coverage td span.cline-any { + display: inline-block; + padding: 0 5px; + width: 100%; +} +.missing-if-branch { + display: inline-block; + margin-right: 5px; + border-radius: 3px; + position: relative; + padding: 0 4px; + background: #333; + color: yellow; +} + +.skip-if-branch { + display: none; + margin-right: 10px; + position: relative; + padding: 0 4px; + background: #ccc; + color: white; +} +.missing-if-branch .typ, .skip-if-branch .typ { + color: inherit !important; +} +.coverage-summary { + border-collapse: collapse; + width: 100%; +} +.coverage-summary tr { border-bottom: 1px solid #bbb; } +.keyline-all { border: 1px solid #ddd; } +.coverage-summary td, .coverage-summary th { padding: 10px; } +.coverage-summary tbody { border: 1px solid #bbb; } +.coverage-summary td { border-right: 1px solid #bbb; } +.coverage-summary td:last-child { border-right: none; } +.coverage-summary th { + text-align: left; + font-weight: normal; + white-space: nowrap; +} +.coverage-summary th.file { border-right: none !important; } +.coverage-summary th.pct { } +.coverage-summary th.pic, +.coverage-summary th.abs, +.coverage-summary td.pct, +.coverage-summary td.abs { text-align: right; } +.coverage-summary td.file { white-space: nowrap; } +.coverage-summary td.pic { min-width: 120px !important; } +.coverage-summary tfoot td { } + +.coverage-summary .sorter { + height: 10px; + width: 7px; + display: inline-block; + margin-left: 0.5em; + background: url(sort-arrow-sprite.png) no-repeat scroll 0 0 transparent; +} +.coverage-summary .sorted .sorter { + background-position: 0 -20px; +} +.coverage-summary .sorted-desc .sorter { + background-position: 0 -10px; +} +.status-line { height: 10px; } +/* yellow */ +.cbranch-no { background: yellow !important; color: #111; } +/* dark red */ +.red.solid, .status-line.low, .low .cover-fill { background:#C21F39 } +.low .chart { border:1px solid #C21F39 } +.highlighted, +.highlighted .cstat-no, .highlighted .fstat-no, .highlighted .cbranch-no{ + background: #C21F39 !important; +} +/* medium red */ +.cstat-no, .fstat-no, .cbranch-no, .cbranch-no { background:#F6C6CE } +/* light red */ +.low, .cline-no { background:#FCE1E5 } +/* light green */ +.high, .cline-yes { background:rgb(230,245,208) } +/* medium green */ +.cstat-yes { background:rgb(161,215,106) } +/* dark green */ +.status-line.high, .high .cover-fill { background:rgb(77,146,33) } +.high .chart { border:1px solid rgb(77,146,33) } +/* dark yellow (gold) */ +.status-line.medium, .medium .cover-fill { background: #f9cd0b; } +.medium .chart { border:1px solid #f9cd0b; } +/* light yellow */ +.medium { background: #fff4c2; } + +.cstat-skip { background: #ddd; color: #111; } +.fstat-skip { background: #ddd; color: #111 !important; } +.cbranch-skip { background: #ddd !important; color: #111; } + +span.cline-neutral { background: #eaeaea; } + +.coverage-summary td.empty { + opacity: .5; + padding-top: 4px; + padding-bottom: 4px; + line-height: 1; + color: #888; +} + +.cover-fill, .cover-empty { + display:inline-block; + height: 12px; +} +.chart { + line-height: 0; +} +.cover-empty { + background: white; +} +.cover-full { + border-right: none !important; +} +pre.prettyprint { + border: none !important; + padding: 0 !important; + margin: 0 !important; +} +.com { color: #999 !important; } +.ignore-none { color: #999; font-weight: normal; } + +.wrapper { + min-height: 100%; + height: auto !important; + height: 100%; + margin: 0 auto -48px; +} +.footer, .push { + height: 48px; +} diff --git a/coverage/block-navigation.js b/coverage/block-navigation.js new file mode 100644 index 0000000..cc12130 --- /dev/null +++ b/coverage/block-navigation.js @@ -0,0 +1,87 @@ +/* eslint-disable */ +var jumpToCode = (function init() { + // Classes of code we would like to highlight in the file view + var missingCoverageClasses = ['.cbranch-no', '.cstat-no', '.fstat-no']; + + // Elements to highlight in the file listing view + var fileListingElements = ['td.pct.low']; + + // We don't want to select elements that are direct descendants of another match + var notSelector = ':not(' + missingCoverageClasses.join('):not(') + ') > '; // becomes `:not(a):not(b) > ` + + // Selecter that finds elements on the page to which we can jump + var selector = + fileListingElements.join(', ') + + ', ' + + notSelector + + missingCoverageClasses.join(', ' + notSelector); // becomes `:not(a):not(b) > a, :not(a):not(b) > b` + + // The NodeList of matching elements + var missingCoverageElements = document.querySelectorAll(selector); + + var currentIndex; + + function toggleClass(index) { + missingCoverageElements + .item(currentIndex) + .classList.remove('highlighted'); + missingCoverageElements.item(index).classList.add('highlighted'); + } + + function makeCurrent(index) { + toggleClass(index); + currentIndex = index; + missingCoverageElements.item(index).scrollIntoView({ + behavior: 'smooth', + block: 'center', + inline: 'center' + }); + } + + function goToPrevious() { + var nextIndex = 0; + if (typeof currentIndex !== 'number' || currentIndex === 0) { + nextIndex = missingCoverageElements.length - 1; + } else if (missingCoverageElements.length > 1) { + nextIndex = currentIndex - 1; + } + + makeCurrent(nextIndex); + } + + function goToNext() { + var nextIndex = 0; + + if ( + typeof currentIndex === 'number' && + currentIndex < missingCoverageElements.length - 1 + ) { + nextIndex = currentIndex + 1; + } + + makeCurrent(nextIndex); + } + + return function jump(event) { + if ( + document.getElementById('fileSearch') === document.activeElement && + document.activeElement != null + ) { + // if we're currently focused on the search input, we don't want to navigate + return; + } + + switch (event.which) { + case 78: // n + case 74: // j + goToNext(); + break; + case 66: // b + case 75: // k + case 80: // p + goToPrevious(); + break; + } + }; +})(); +window.addEventListener('keydown', jumpToCode); diff --git a/coverage/favicon.png b/coverage/favicon.png new file mode 100644 index 0000000..c1525b8 Binary files /dev/null and b/coverage/favicon.png differ diff --git a/coverage/index.html b/coverage/index.html new file mode 100644 index 0000000..8a937a4 --- /dev/null +++ b/coverage/index.html @@ -0,0 +1,161 @@ + + + + +
++ Press n or j to go to the next uncovered block, b, p or k for the previous block. +
+ +| File | ++ | Statements | ++ | Branches | ++ | Functions | ++ | Lines | ++ |
|---|---|---|---|---|---|---|---|---|---|
| src | +
+
+ |
+ 0% | +0/50 | +66.66% | +2/3 | +66.66% | +2/3 | +0% | +0/50 | +
| src/core | +
+
+ |
+ 60.43% | +110/182 | +68.75% | +11/16 | +83.33% | +5/6 | +60.43% | +110/182 | +
| src/parser | +
+
+ |
+ 100% | +113/113 | +96.42% | +27/28 | +100% | +7/7 | +100% | +113/113 | +
| src/templates | +
+
+ |
+ 86.36% | +19/22 | +66.66% | +4/6 | +100% | +2/2 | +86.36% | +19/22 | +