From b6a348d72e73fcf7e5b2508633793ad0372dd14f Mon Sep 17 00:00:00 2001 From: jonathanpopham Date: Wed, 7 Jan 2026 14:10:52 -0500 Subject: [PATCH 1/9] test: Add intentional dead code to test self-analysis --- src/example-dead-code.ts | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 src/example-dead-code.ts diff --git a/src/example-dead-code.ts b/src/example-dead-code.ts new file mode 100644 index 0000000..3f16db9 --- /dev/null +++ b/src/example-dead-code.ts @@ -0,0 +1,24 @@ +/** + * This file contains intentional dead code for testing the dead-code-hunter action. + * These functions should be detected as unused. + */ + +// This function is never called anywhere +function unusedHelperFunction(x: number): number { + return x * 2; +} + +// Another unused function +function formatUnusedData(data: string[]): string { + return data.join(', '); +} + +// Unused async function +async function fetchUnusedData(): Promise { + console.log('This is never called'); +} + +// Unused class method style function +const unusedProcessor = { + process: (input: string) => input.toUpperCase(), +}; From 36ec94800b54c923fb8a818f1496f33e91483468 Mon Sep 17 00:00:00 2001 From: jonathanpopham Date: Wed, 7 Jan 2026 14:12:56 -0500 Subject: [PATCH 2/9] fix: Add detailed API error logging for debugging --- dist/example-dead-code.d.ts | 10 ++++++++++ dist/example-dead-code.d.ts.map | 1 + dist/index.js | 16 ++++++++++++++++ src/index.ts | 20 +++++++++++++++++++- 4 files changed, 46 insertions(+), 1 deletion(-) create mode 100644 dist/example-dead-code.d.ts create mode 100644 dist/example-dead-code.d.ts.map diff --git a/dist/example-dead-code.d.ts b/dist/example-dead-code.d.ts new file mode 100644 index 0000000..233fe32 --- /dev/null +++ b/dist/example-dead-code.d.ts @@ -0,0 +1,10 @@ +/** + * This file contains intentional dead code for testing the dead-code-hunter action. + * These functions should be detected as unused. + */ +declare function unusedHelperFunction(x: number): number; +declare function formatUnusedData(data: string[]): string; +declare function fetchUnusedData(): Promise; +declare const unusedProcessor: { + process: (input: string) => string; +}; diff --git a/dist/example-dead-code.d.ts.map b/dist/example-dead-code.d.ts.map new file mode 100644 index 0000000..90983b7 --- /dev/null +++ b/dist/example-dead-code.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"","sourceRoot":"","sources":["file:///Users/jag/dead-code-hunter/src/example-dead-code.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,iBAAS,oBAAoB,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAE/C;AAGD,iBAAS,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,MAAM,CAEhD;AAGD,iBAAe,eAAe,IAAI,OAAO,CAAC,IAAI,CAAC,CAE9C;AAGD,QAAA,MAAM,eAAe;qBACF,MAAM;CACxB,CAAC"} \ No newline at end of file diff --git a/dist/index.js b/dist/index.js index 348965c..d7817cc 100644 --- a/dist/index.js +++ b/dist/index.js @@ -32492,6 +32492,11 @@ async function generateIdempotencyKey(workspacePath) { async function run() { try { const apiKey = core.getInput('supermodel-api-key', { required: true }); + // Validate API key format + if (!apiKey.startsWith('smsk_')) { + core.warning('API key does not start with expected prefix "smsk_"'); + } + core.info(`API key configured (${apiKey.length} chars, starts with: ${apiKey.substring(0, 10)}...)`); const commentOnPr = core.getBooleanInput('comment-on-pr'); const failOnDeadCode = core.getBooleanInput('fail-on-dead-code'); const ignorePatterns = JSON.parse(core.getInput('ignore-patterns') || '[]'); @@ -32551,6 +32556,17 @@ async function run() { } } catch (error) { + // Log detailed error info for debugging + if (error.response) { + try { + const body = await error.response.text(); + core.error(`API Error - Status: ${error.response.status}`); + core.error(`API Error - Body: ${body}`); + } + catch { + core.error(`API Error - Status: ${error.response.status}`); + } + } if (error instanceof Error) { core.setFailed(error.message); } diff --git a/src/index.ts b/src/index.ts index d82644c..7c3a0c7 100644 --- a/src/index.ts +++ b/src/index.ts @@ -41,6 +41,13 @@ async function generateIdempotencyKey(workspacePath: string): Promise { async function run(): Promise { try { const apiKey = core.getInput('supermodel-api-key', { required: true }); + + // Validate API key format + if (!apiKey.startsWith('smsk_')) { + core.warning('API key does not start with expected prefix "smsk_"'); + } + core.info(`API key configured (${apiKey.length} chars, starts with: ${apiKey.substring(0, 10)}...)`); + const commentOnPr = core.getBooleanInput('comment-on-pr'); const failOnDeadCode = core.getBooleanInput('fail-on-dead-code'); const ignorePatterns = JSON.parse(core.getInput('ignore-patterns') || '[]'); @@ -117,7 +124,18 @@ async function run(): Promise { core.setFailed(`Found ${deadCode.length} dead code functions`); } - } catch (error) { + } catch (error: any) { + // Log detailed error info for debugging + if (error.response) { + try { + const body = await error.response.text(); + core.error(`API Error - Status: ${error.response.status}`); + core.error(`API Error - Body: ${body}`); + } catch { + core.error(`API Error - Status: ${error.response.status}`); + } + } + if (error instanceof Error) { core.setFailed(error.message); } else { From d3362445f5aab94c36818f4824bead515970dd01 Mon Sep 17 00:00:00 2001 From: jonathanpopham Date: Wed, 7 Jan 2026 14:18:06 -0500 Subject: [PATCH 3/9] fix: Trim API key and show more debug info --- dist/index.js | 5 +++-- src/index.ts | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/dist/index.js b/dist/index.js index d7817cc..43ccbad 100644 --- a/dist/index.js +++ b/dist/index.js @@ -32491,12 +32491,13 @@ async function generateIdempotencyKey(workspacePath) { } async function run() { try { - const apiKey = core.getInput('supermodel-api-key', { required: true }); + const apiKey = core.getInput('supermodel-api-key', { required: true }).trim(); // Validate API key format if (!apiKey.startsWith('smsk_')) { core.warning('API key does not start with expected prefix "smsk_"'); } - core.info(`API key configured (${apiKey.length} chars, starts with: ${apiKey.substring(0, 10)}...)`); + // Log key details for debugging (safe - only shows prefix/suffix) + core.info(`API key configured (${apiKey.length} chars, starts: ${apiKey.substring(0, 12)}..., ends: ...${apiKey.substring(apiKey.length - 4)})`); const commentOnPr = core.getBooleanInput('comment-on-pr'); const failOnDeadCode = core.getBooleanInput('fail-on-dead-code'); const ignorePatterns = JSON.parse(core.getInput('ignore-patterns') || '[]'); diff --git a/src/index.ts b/src/index.ts index 7c3a0c7..e03d9e9 100644 --- a/src/index.ts +++ b/src/index.ts @@ -40,13 +40,14 @@ async function generateIdempotencyKey(workspacePath: string): Promise { async function run(): Promise { try { - const apiKey = core.getInput('supermodel-api-key', { required: true }); + const apiKey = core.getInput('supermodel-api-key', { required: true }).trim(); // Validate API key format if (!apiKey.startsWith('smsk_')) { core.warning('API key does not start with expected prefix "smsk_"'); } - core.info(`API key configured (${apiKey.length} chars, starts with: ${apiKey.substring(0, 10)}...)`); + // Log key details for debugging (safe - only shows prefix/suffix) + core.info(`API key configured (${apiKey.length} chars, starts: ${apiKey.substring(0, 12)}..., ends: ...${apiKey.substring(apiKey.length - 4)})`); const commentOnPr = core.getBooleanInput('comment-on-pr'); const failOnDeadCode = core.getBooleanInput('fail-on-dead-code'); From 226a1e3c815201df4a885a8af385b140cbafbd4e Mon Sep 17 00:00:00 2001 From: jonathanpopham Date: Wed, 7 Jan 2026 14:20:15 -0500 Subject: [PATCH 4/9] fix: Add pull-requests write permission for PR comments --- .github/workflows/ci.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f55b0d6..fba3e1d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -31,6 +31,9 @@ jobs: self-test: runs-on: ubuntu-latest if: github.event_name == 'pull_request' + permissions: + contents: read + pull-requests: write steps: - uses: actions/checkout@v4 From c1d3ec7c93468e894374533a946a2410c6a73668 Mon Sep 17 00:00:00 2001 From: jonathanpopham Date: Wed, 7 Jan 2026 14:21:37 -0500 Subject: [PATCH 5/9] debug: Log node structure to fix property extraction --- dist/index.js | 9 +++++++++ src/index.ts | 10 ++++++++++ 2 files changed, 19 insertions(+) diff --git a/dist/index.js b/dist/index.js index 43ccbad..d377a91 100644 --- a/dist/index.js +++ b/dist/index.js @@ -32526,8 +32526,17 @@ async function run() { // Step 4: Analyze for dead code const nodes = response.graph?.nodes || []; const relationships = response.graph?.relationships || []; + // Debug: Log sample node structure + const functionNodes = nodes.filter(n => n.labels?.includes('Function')); + if (functionNodes.length > 0) { + core.info(`Sample function node: ${JSON.stringify(functionNodes[0], null, 2)}`); + } const deadCode = (0, dead_code_1.findDeadCode)(nodes, relationships, ignorePatterns); core.info(`Found ${deadCode.length} potentially dead functions`); + // Debug: Log dead code results + for (const dc of deadCode.slice(0, 5)) { + core.info(`Dead: ${dc.name} @ ${dc.filePath}:${dc.startLine}`); + } // Step 5: Set outputs core.setOutput('dead-code-count', deadCode.length); core.setOutput('dead-code-json', JSON.stringify(deadCode)); diff --git a/src/index.ts b/src/index.ts index e03d9e9..e6db7a4 100644 --- a/src/index.ts +++ b/src/index.ts @@ -89,9 +89,19 @@ async function run(): Promise { const nodes = response.graph?.nodes || []; const relationships = response.graph?.relationships || []; + // Debug: Log sample node structure + const functionNodes = nodes.filter(n => n.labels?.includes('Function')); + if (functionNodes.length > 0) { + core.info(`Sample function node: ${JSON.stringify(functionNodes[0], null, 2)}`); + } + const deadCode = findDeadCode(nodes, relationships, ignorePatterns); core.info(`Found ${deadCode.length} potentially dead functions`); + // Debug: Log dead code results + for (const dc of deadCode.slice(0, 5)) { + core.info(`Dead: ${dc.name} @ ${dc.filePath}:${dc.startLine}`); + } // Step 5: Set outputs core.setOutput('dead-code-count', deadCode.length); From f38d9c3159e34576e4c9fcae29436012b1bbf172 Mon Sep 17 00:00:00 2001 From: jonathanpopham Date: Wed, 7 Jan 2026 14:24:27 -0500 Subject: [PATCH 6/9] fix: Use supermodel endpoint instead of call graph for full metadata --- dist/index.js | 4 ++-- src/index.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/dist/index.js b/dist/index.js index d377a91..f57d728 100644 --- a/dist/index.js +++ b/dist/index.js @@ -32518,11 +32518,11 @@ async function run() { const api = new sdk_1.DefaultApi(config); const zipBuffer = await fs.readFile(zipPath); const zipBlob = new Blob([zipBuffer], { type: 'application/zip' }); - const response = await api.generateCallGraph({ + const response = await api.generateSupermodelGraph({ idempotencyKey, file: zipBlob, }); - core.info(`API response received. Stats: ${JSON.stringify(response.stats)}`); + core.info(`API response received. Summary: ${JSON.stringify(response.summary)}`); // Step 4: Analyze for dead code const nodes = response.graph?.nodes || []; const relationships = response.graph?.relationships || []; diff --git a/src/index.ts b/src/index.ts index e6db7a4..7f7d9e2 100644 --- a/src/index.ts +++ b/src/index.ts @@ -78,12 +78,12 @@ async function run(): Promise { const zipBuffer = await fs.readFile(zipPath); const zipBlob = new Blob([zipBuffer], { type: 'application/zip' }); - const response = await api.generateCallGraph({ + const response = await api.generateSupermodelGraph({ idempotencyKey, file: zipBlob, }); - core.info(`API response received. Stats: ${JSON.stringify(response.stats)}`); + core.info(`API response received. Summary: ${JSON.stringify(response.summary)}`); // Step 4: Analyze for dead code const nodes = response.graph?.nodes || []; From b8f3c9fd2f4573321650819a050597fd9357e312 Mon Sep 17 00:00:00 2001 From: jonathanpopham Date: Wed, 7 Jan 2026 14:27:21 -0500 Subject: [PATCH 7/9] docs: Update README to reflect supermodel endpoint and add permissions --- README.md | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 365aa7e..c76267c 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,12 @@ # Dead Code Hunter -A GitHub Action that uses [Supermodel](https://supermodeltools.com) call graphs to find unreachable functions in your codebase. +A GitHub Action that uses [Supermodel](https://supermodeltools.com) to find unreachable functions in your codebase. ## What it does 1. Creates a zip archive of your repository using `git archive` -2. Sends it to Supermodel's call graph API -3. Analyzes the graph to find functions with no callers +2. Sends it to Supermodel's graph API for analysis +3. Identifies functions with no callers (dead code) 4. Filters out false positives (entry points, exports, tests) 5. Posts findings as a PR comment @@ -16,11 +16,13 @@ A GitHub Action that uses [Supermodel](https://supermodeltools.com) call graphs name: Dead Code Hunter on: pull_request: - workflow_dispatch: jobs: hunt: runs-on: ubuntu-latest + permissions: + contents: read + pull-requests: write steps: - uses: actions/checkout@v4 - uses: supermodeltools/dead-code-hunter@v1 @@ -57,13 +59,13 @@ When dead code is found, the action posts a comment like: > ## Dead Code Hunter > -> Found **7** potentially unused functions: +> Found **3** potentially unused functions: > > | Function | File | Line | > |----------|------|------| -> | `unusedHelper` | src/utils.ts#L42 | L42 | -> | `oldValidator` | src/validation.ts#L15 | L15 | -> | ... | ... | ... | +> | `unusedHelperFunction` | src/example-dead-code.ts#L7 | L7 | +> | `formatUnusedData` | src/example-dead-code.ts#L12 | L12 | +> | `fetchUnusedData` | src/example-dead-code.ts#L17 | L17 | > > --- > _Powered by [Supermodel](https://supermodeltools.com) call graph analysis_ @@ -89,7 +91,7 @@ You can add custom ignore patterns: ## Supported Languages -Supermodel supports call graph analysis for: +Supermodel supports analysis for: - TypeScript / JavaScript - Python @@ -102,10 +104,10 @@ Supermodel supports call graph analysis for: ``` ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ -│ git archive │────▶│ Supermodel API │────▶│ Call Graph │ -│ (create zip) │ │ /v1/graphs/call│ │ Analysis │ -└─────────────────┘ └─────────────────┘ └─────────────────┘ - │ +│ git archive │────▶│ Supermodel API │────▶│ Graph │ +│ (create zip) │ │ /v1/graphs/ │ │ Analysis │ +└─────────────────┘ │ supermodel │ └─────────────────┘ + └─────────────────┘ │ ▼ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │ PR Comment │◀────│ Filter False │◀────│ Find Uncalled │ From d9dc9a7b5c20164305b78c0da7d7e64afe00af38 Mon Sep 17 00:00:00 2001 From: jonathanpopham Date: Wed, 7 Jan 2026 14:34:08 -0500 Subject: [PATCH 8/9] fix: Change 'call graph analysis' to 'graph analysis' --- README.md | 2 +- dist/index.js | 2 +- src/dead-code.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index c76267c..d97778b 100644 --- a/README.md +++ b/README.md @@ -68,7 +68,7 @@ When dead code is found, the action posts a comment like: > | `fetchUnusedData` | src/example-dead-code.ts#L17 | L17 | > > --- -> _Powered by [Supermodel](https://supermodeltools.com) call graph analysis_ +> _Powered by [Supermodel](https://supermodeltools.com) graph analysis_ ## False Positive Filtering diff --git a/dist/index.js b/dist/index.js index f57d728..f0ef8a9 100644 --- a/dist/index.js +++ b/dist/index.js @@ -32412,7 +32412,7 @@ ${rows}`; if (deadCode.length > 50) { comment += `\n\n_...and ${deadCode.length - 50} more. See action output for full list._`; } - comment += `\n\n---\n_Powered by [Supermodel](https://supermodeltools.com) call graph analysis_`; + comment += `\n\n---\n_Powered by [Supermodel](https://supermodeltools.com) graph analysis_`; return comment; } diff --git a/src/dead-code.ts b/src/dead-code.ts index aad61f9..77d41e3 100644 --- a/src/dead-code.ts +++ b/src/dead-code.ts @@ -156,7 +156,7 @@ ${rows}`; comment += `\n\n_...and ${deadCode.length - 50} more. See action output for full list._`; } - comment += `\n\n---\n_Powered by [Supermodel](https://supermodeltools.com) call graph analysis_`; + comment += `\n\n---\n_Powered by [Supermodel](https://supermodeltools.com) graph analysis_`; return comment; } From 96a5abfc9baf219a7dbca63d56b64e1b22149fd3 Mon Sep 17 00:00:00 2001 From: jonathanpopham Date: Wed, 7 Jan 2026 14:35:50 -0500 Subject: [PATCH 9/9] chore: Clean up for open source release - Remove debug logging and test dead code file - Clean up action output messages - Point users to dashboard.supermodeltools.com for API keys - Improve error messages for invalid API keys --- README.md | 4 +-- dist/example-dead-code.d.ts | 10 ------- dist/example-dead-code.d.ts.map | 1 - dist/index.js | 44 ++++++++++--------------------- src/example-dead-code.ts | 24 ----------------- src/index.ts | 46 ++++++++++----------------------- 6 files changed, 30 insertions(+), 99 deletions(-) delete mode 100644 dist/example-dead-code.d.ts delete mode 100644 dist/example-dead-code.d.ts.map delete mode 100644 src/example-dead-code.ts diff --git a/README.md b/README.md index d97778b..6640e99 100644 --- a/README.md +++ b/README.md @@ -32,8 +32,8 @@ jobs: ## Getting a Supermodel API Key -1. Sign up at [supermodeltools.com](https://supermodeltools.com) -2. Create an API key in the dashboard +1. Sign up at [dashboard.supermodeltools.com](https://dashboard.supermodeltools.com) +2. Create an API key 3. Add it as a repository secret named `SUPERMODEL_API_KEY` ## Configuration diff --git a/dist/example-dead-code.d.ts b/dist/example-dead-code.d.ts deleted file mode 100644 index 233fe32..0000000 --- a/dist/example-dead-code.d.ts +++ /dev/null @@ -1,10 +0,0 @@ -/** - * This file contains intentional dead code for testing the dead-code-hunter action. - * These functions should be detected as unused. - */ -declare function unusedHelperFunction(x: number): number; -declare function formatUnusedData(data: string[]): string; -declare function fetchUnusedData(): Promise; -declare const unusedProcessor: { - process: (input: string) => string; -}; diff --git a/dist/example-dead-code.d.ts.map b/dist/example-dead-code.d.ts.map deleted file mode 100644 index 90983b7..0000000 --- a/dist/example-dead-code.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"","sourceRoot":"","sources":["file:///Users/jag/dead-code-hunter/src/example-dead-code.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,iBAAS,oBAAoB,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAE/C;AAGD,iBAAS,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,MAAM,CAEhD;AAGD,iBAAe,eAAe,IAAI,OAAO,CAAC,IAAI,CAAC,CAE9C;AAGD,QAAA,MAAM,eAAe;qBACF,MAAM;CACxB,CAAC"} \ No newline at end of file diff --git a/dist/index.js b/dist/index.js index f0ef8a9..c0f9242 100644 --- a/dist/index.js +++ b/dist/index.js @@ -32467,12 +32467,12 @@ const sdk_1 = __nccwpck_require__(6381); const dead_code_1 = __nccwpck_require__(1655); async function createZipArchive(workspacePath) { const zipPath = path.join(workspacePath, '.dead-code-hunter-repo.zip'); - core.info('Creating zip archive using git archive...'); + core.info('Creating zip archive...'); await exec.exec('git', ['archive', '-o', zipPath, 'HEAD'], { cwd: workspacePath, }); const stats = await fs.stat(zipPath); - core.info(`Created zip archive: ${(stats.size / 1024 / 1024).toFixed(2)} MB`); + core.info(`Archive size: ${(stats.size / 1024 / 1024).toFixed(2)} MB`); return zipPath; } async function generateIdempotencyKey(workspacePath) { @@ -32484,33 +32484,29 @@ async function generateIdempotencyKey(workspacePath) { output += data.toString(); }, }, + silent: true, }); const commitHash = output.trim(); const repoName = path.basename(workspacePath); - return `${repoName}:call:${commitHash}`; + return `${repoName}:supermodel:${commitHash}`; } async function run() { try { const apiKey = core.getInput('supermodel-api-key', { required: true }).trim(); - // Validate API key format if (!apiKey.startsWith('smsk_')) { - core.warning('API key does not start with expected prefix "smsk_"'); + core.warning('API key format looks incorrect. Get your key at https://dashboard.supermodeltools.com'); } - // Log key details for debugging (safe - only shows prefix/suffix) - core.info(`API key configured (${apiKey.length} chars, starts: ${apiKey.substring(0, 12)}..., ends: ...${apiKey.substring(apiKey.length - 4)})`); const commentOnPr = core.getBooleanInput('comment-on-pr'); const failOnDeadCode = core.getBooleanInput('fail-on-dead-code'); const ignorePatterns = JSON.parse(core.getInput('ignore-patterns') || '[]'); const workspacePath = process.env.GITHUB_WORKSPACE || process.cwd(); core.info('Dead Code Hunter starting...'); - core.info(`Workspace: ${workspacePath}`); // Step 1: Create zip archive const zipPath = await createZipArchive(workspacePath); // Step 2: Generate idempotency key const idempotencyKey = await generateIdempotencyKey(workspacePath); - core.info(`Idempotency key: ${idempotencyKey}`); // Step 3: Call Supermodel API - core.info('Calling Supermodel API for call graph...'); + core.info('Analyzing codebase with Supermodel...'); const config = new sdk_1.Configuration({ basePath: process.env.SUPERMODEL_BASE_URL || 'https://api.supermodeltools.com', apiKey: apiKey, @@ -32522,21 +32518,11 @@ async function run() { idempotencyKey, file: zipBlob, }); - core.info(`API response received. Summary: ${JSON.stringify(response.summary)}`); // Step 4: Analyze for dead code const nodes = response.graph?.nodes || []; const relationships = response.graph?.relationships || []; - // Debug: Log sample node structure - const functionNodes = nodes.filter(n => n.labels?.includes('Function')); - if (functionNodes.length > 0) { - core.info(`Sample function node: ${JSON.stringify(functionNodes[0], null, 2)}`); - } const deadCode = (0, dead_code_1.findDeadCode)(nodes, relationships, ignorePatterns); - core.info(`Found ${deadCode.length} potentially dead functions`); - // Debug: Log dead code results - for (const dc of deadCode.slice(0, 5)) { - core.info(`Dead: ${dc.name} @ ${dc.filePath}:${dc.startLine}`); - } + core.info(`Found ${deadCode.length} potentially unused functions`); // Step 5: Set outputs core.setOutput('dead-code-count', deadCode.length); core.setOutput('dead-code-json', JSON.stringify(deadCode)); @@ -32552,7 +32538,7 @@ async function run() { issue_number: github.context.payload.pull_request.number, body: comment, }); - core.info('Posted PR comment'); + core.info('Posted findings to PR'); } else { core.warning('GITHUB_TOKEN not available, skipping PR comment'); @@ -32562,19 +32548,17 @@ async function run() { await fs.unlink(zipPath); // Step 8: Fail if configured and dead code found if (deadCode.length > 0 && failOnDeadCode) { - core.setFailed(`Found ${deadCode.length} dead code functions`); + core.setFailed(`Found ${deadCode.length} potentially unused functions`); } } catch (error) { - // Log detailed error info for debugging if (error.response) { - try { - const body = await error.response.text(); - core.error(`API Error - Status: ${error.response.status}`); - core.error(`API Error - Body: ${body}`); + const status = error.response.status; + if (status === 401) { + core.error('Invalid API key. Get your key at https://dashboard.supermodeltools.com'); } - catch { - core.error(`API Error - Status: ${error.response.status}`); + else { + core.error(`API error (${status})`); } } if (error instanceof Error) { diff --git a/src/example-dead-code.ts b/src/example-dead-code.ts deleted file mode 100644 index 3f16db9..0000000 --- a/src/example-dead-code.ts +++ /dev/null @@ -1,24 +0,0 @@ -/** - * This file contains intentional dead code for testing the dead-code-hunter action. - * These functions should be detected as unused. - */ - -// This function is never called anywhere -function unusedHelperFunction(x: number): number { - return x * 2; -} - -// Another unused function -function formatUnusedData(data: string[]): string { - return data.join(', '); -} - -// Unused async function -async function fetchUnusedData(): Promise { - console.log('This is never called'); -} - -// Unused class method style function -const unusedProcessor = { - process: (input: string) => input.toUpperCase(), -}; diff --git a/src/index.ts b/src/index.ts index 7f7d9e2..d9ce640 100644 --- a/src/index.ts +++ b/src/index.ts @@ -9,14 +9,14 @@ import { findDeadCode, formatPrComment } from './dead-code'; async function createZipArchive(workspacePath: string): Promise { const zipPath = path.join(workspacePath, '.dead-code-hunter-repo.zip'); - core.info('Creating zip archive using git archive...'); + core.info('Creating zip archive...'); await exec.exec('git', ['archive', '-o', zipPath, 'HEAD'], { cwd: workspacePath, }); const stats = await fs.stat(zipPath); - core.info(`Created zip archive: ${(stats.size / 1024 / 1024).toFixed(2)} MB`); + core.info(`Archive size: ${(stats.size / 1024 / 1024).toFixed(2)} MB`); return zipPath; } @@ -30,24 +30,22 @@ async function generateIdempotencyKey(workspacePath: string): Promise { output += data.toString(); }, }, + silent: true, }); const commitHash = output.trim(); const repoName = path.basename(workspacePath); - return `${repoName}:call:${commitHash}`; + return `${repoName}:supermodel:${commitHash}`; } async function run(): Promise { try { const apiKey = core.getInput('supermodel-api-key', { required: true }).trim(); - // Validate API key format if (!apiKey.startsWith('smsk_')) { - core.warning('API key does not start with expected prefix "smsk_"'); + core.warning('API key format looks incorrect. Get your key at https://dashboard.supermodeltools.com'); } - // Log key details for debugging (safe - only shows prefix/suffix) - core.info(`API key configured (${apiKey.length} chars, starts: ${apiKey.substring(0, 12)}..., ends: ...${apiKey.substring(apiKey.length - 4)})`); const commentOnPr = core.getBooleanInput('comment-on-pr'); const failOnDeadCode = core.getBooleanInput('fail-on-dead-code'); @@ -56,17 +54,15 @@ async function run(): Promise { const workspacePath = process.env.GITHUB_WORKSPACE || process.cwd(); core.info('Dead Code Hunter starting...'); - core.info(`Workspace: ${workspacePath}`); // Step 1: Create zip archive const zipPath = await createZipArchive(workspacePath); // Step 2: Generate idempotency key const idempotencyKey = await generateIdempotencyKey(workspacePath); - core.info(`Idempotency key: ${idempotencyKey}`); // Step 3: Call Supermodel API - core.info('Calling Supermodel API for call graph...'); + core.info('Analyzing codebase with Supermodel...'); const config = new Configuration({ basePath: process.env.SUPERMODEL_BASE_URL || 'https://api.supermodeltools.com', @@ -83,25 +79,13 @@ async function run(): Promise { file: zipBlob, }); - core.info(`API response received. Summary: ${JSON.stringify(response.summary)}`); - // Step 4: Analyze for dead code const nodes = response.graph?.nodes || []; const relationships = response.graph?.relationships || []; - // Debug: Log sample node structure - const functionNodes = nodes.filter(n => n.labels?.includes('Function')); - if (functionNodes.length > 0) { - core.info(`Sample function node: ${JSON.stringify(functionNodes[0], null, 2)}`); - } - const deadCode = findDeadCode(nodes, relationships, ignorePatterns); - core.info(`Found ${deadCode.length} potentially dead functions`); - // Debug: Log dead code results - for (const dc of deadCode.slice(0, 5)) { - core.info(`Dead: ${dc.name} @ ${dc.filePath}:${dc.startLine}`); - } + core.info(`Found ${deadCode.length} potentially unused functions`); // Step 5: Set outputs core.setOutput('dead-code-count', deadCode.length); @@ -121,7 +105,7 @@ async function run(): Promise { body: comment, }); - core.info('Posted PR comment'); + core.info('Posted findings to PR'); } else { core.warning('GITHUB_TOKEN not available, skipping PR comment'); } @@ -132,18 +116,16 @@ async function run(): Promise { // Step 8: Fail if configured and dead code found if (deadCode.length > 0 && failOnDeadCode) { - core.setFailed(`Found ${deadCode.length} dead code functions`); + core.setFailed(`Found ${deadCode.length} potentially unused functions`); } } catch (error: any) { - // Log detailed error info for debugging if (error.response) { - try { - const body = await error.response.text(); - core.error(`API Error - Status: ${error.response.status}`); - core.error(`API Error - Body: ${body}`); - } catch { - core.error(`API Error - Status: ${error.response.status}`); + const status = error.response.status; + if (status === 401) { + core.error('Invalid API key. Get your key at https://dashboard.supermodeltools.com'); + } else { + core.error(`API error (${status})`); } }