Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Split large JavaScript files into maintainable modules safely.
.claude/skills/github-javascript-refactoring/SKILL.md| Model | Eval pass | Runs |
|---|---|---|
| gemini-3.6-flash | 100% | 26 |
| gemini-3.1-pro-preview | 100% | 1 |
| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-01 | ✗→✓ | ▲ Improved | 44% | 0% |
| case-02 | ✗→✓ | ▲ Improved | 37% | 0% |
| case-03 | ✗→✓ | ▲ Improved | 30% | 0% |
| case-08 | ✗→✓ | ▲ Improved | 34% | 0% |
| case-09 | ✗→✓ | ▲ Improved | 30% | 0% |
Use this guide to split JavaScript into maintainable CommonJS modules in gh-aw without drifting into dead embedding patterns.
The current gh-aw architecture is action-centric:
pkg/workflow/js/*.cjs and actions/setup/js/*.cjsactions/<action-name>/src/actions/<action-name>/index.jsmake actions-build, gh aw actions-build) and dependency maps such as pkg/cli/actions_build_command.gopkg/workflow/js.go is a stub; it no longer owns the runtime JavaScript shipping path for the main workflowsIf you are refactoring a workflow utility, prefer the current action/module architecture over any older //go:embed pattern.
Top-level .cjs scripts executed directly in workflows follow this pattern:
✅ Correct Pattern - Export main, but don't call it:
javascriptasync function main() { // Script logic here core.info("Running the script"); } module.exports = { main };
❌ Incorrect Pattern - Don't call main in the file:
javascriptasync function main() { // Script logic here core.info("Running the script"); } await main(); // ❌ Don't do this! module.exports = { main };
Why this pattern?
await main() at execution timeChoose the correct location for the module before writing code:
pkg/workflow/js/actions/<action-name>/src/ or actions/setup/js/actions/<action-name>/index.jsFile naming convention:
sanitize_content.cjs, load_agent_output.cjs).cjs for CommonJS modulesExample file structure:
javascript// @ts-check /// <reference types="@actions/github-script" /> /** * Brief description of what this module does */ /** * Function documentation * @param {string} input - Description of parameter * @returns {string} Description of return value */ function myFunction(input) { return input; } module.exports = { myFunction, };
Key points:
// @ts-check for TypeScript checking/// <reference types="@actions/github-script" /> when the module is used with GitHub Actions scriptsmodule.exports = { ... }@actions/core or @actions/github directly unless the module is running in an action context that explicitly expects itCreate a matching test beside the module using the same base name plus .test.cjs:
Example: pkg/workflow/js/my_module.test.cjs
javascriptimport { describe, it, expect, beforeEach, vi } from "vitest"; const mockCore = { debug: vi.fn(), info: vi.fn(), warning: vi.fn(), error: vi.fn(), setFailed: vi.fn(), setOutput: vi.fn(), }; global.core = mockCore; describe("myFunction", () => { beforeEach(() => { vi.clearAllMocks(); }); it("handles a normal input", async () => { const { myFunction } = await import("./my_module.cjs"); expect(myFunction("test input")).toBe("expected output"); }); it("handles empty input", async () => { const { myFunction } = await import("./my_module.cjs"); expect(myFunction("")).toBe(""); }); });
Testing guidelines:
core and github globals as neededawait import()) to allow module setup at test timebeforeEachRun tests:
bashmake test-js
Do not add a new //go:embed mapping just to ship a new runtime script. The current repo ships JavaScript through the action-generation/build pipeline.
Use this checklist:
pkg/cli/actions_build_command.goactions/<action-name>/src/make actions-buildpkg/workflow/js/ and update the action or workflow definition that consumes itExample design:
javascriptconst { myFunction } = require("./my_module.cjs"); async function main() { const result = myFunction("some input"); core.info(`Result: ${result}`); } module.exports = { main };
Run the relevant checks for the area you changed:
bashmake fmt-cjs make lint-cjs make test-js make test-unit make actions-build
Before committing your refactor:
.cjs file created in the correct source directory.test.cjs file createdmake test-js or the targeted Vitest suiterequire() statements work correctly in other JS filesmake fmt-cjsmake lint-cjs or make test-unitFiles like sanitize_content.cjs or load_agent_output.cjs are best kept under pkg/workflow/js/ or actions/setup/js/ and consumed by other JS modules via require().
When the JavaScript belongs to a single action, keep it under actions/<action-name>/src/ and regenerate the output bundle with make actions-build.
If the script is executed directly in a workflow, export main and omit the direct await main() call. The host build/runtime step handles execution.
Cause: Action bundle was not rebuilt after editing the source file
Solution:
bashmake actions-build
core is not definedCause: Missing global mocks
Solution:
javascriptglobal.core = mockCore;
Cause: It was added to the wrong layer
Solution: Move it to the action-specific source tree instead of creating a broad workflow-level registry entry.
actions/README.md - current action-generation/build workflowpkg/cli/actions_build_command.go - action dependency mappingpkg/workflow/js/*.cjs - existing shared module patternsactions/setup/js/*.cjs - action runtime/source examples| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | fail→pass | 22,681 | 20,253 | -11% | 1 | 1 | 0% | 3,590 | 5,161 | +44% | 0 | 0 | — |
case-02 | fail→pass | 21,337 | 16,520 | -23% | 1 | 1 | 0% | 2,972 | 4,081 | +37% | 0 | 0 | — |
case-03 | fail→pass | 23,985 | 18,122 | -24% | 1 | 1 | 0% | 3,524 | 4,589 | +30% | 0 | 0 | — |
case-04 | pass→pass | 11,575 | 10,427 | -10% | 1 | 1 | 0% | 1,251 | 2,831 | +126% | 0 | 0 | — |
case-05 | pass→pass | 14,317 | 15,106 | +6% | 1 | 1 | 0% | 1,526 | 3,702 | +143% | 0 | 0 | — |
case-06 | fail→fail | 11,792 | 9,988 | -15% | 1 | 1 | 0% | 1,137 | 2,676 | +135% | 0 | 0 | — |
case-07 | pass→pass | 15,899 | 8,663 | -46% | 1 | 1 | 0% | 1,932 | 2,410 | +25% | 0 | 0 | — |
case-08 | fail→pass | 15,236 | 7,479 | -51% | 1 | 1 | 0% | 1,683 | 2,256 | +34% | 0 | 0 | — |
case-09 | fail→pass | 16,529 | 8,018 | -51% | 1 | 1 | 0% | 1,872 | 2,435 | +30% | 0 | 0 | — |
case-10 | pass→pass | 13,656 | 8,055 | -41% | 1 | 1 | 0% | 1,566 | 2,435 | +55% | 0 | 0 | — |
case-11 | fail→pass | 19,232 | 10,269 | -47% | 1 | 1 | 0% | 2,318 | 2,840 | +23% | 0 | 0 | — |
case-12 | fail→pass | 17,277 | 14,432 | -16% | 1 | 1 | 0% | 2,335 | 3,752 | +61% | 0 | 0 | — |
case-13 | pass→pass | 17,861 | 11,898 | -33% | 1 | 1 | 0% | 2,071 | 2,990 | +44% | 0 | 0 | — |
case-14 | pass→pass | 14,676 | 7,006 | -52% | 1 | 1 | 0% | 2,370 | 2,158 | -9% | 0 | 0 | — |
case-15 | fail→pass | 14,634 | 10,821 | -26% | 1 | 1 | 0% | 1,716 | 2,968 | +73% | 0 | 0 | — |
case-16 | fail→pass | 14,789 | 7,220 | -51% | 1 | 1 | 0% | 1,504 | 2,276 | +51% | 0 | 0 | — |
case-17 | fail→pass | 15,620 | 8,611 | -45% | 1 | 1 | 0% | 1,989 | 2,543 | +28% | 0 | 0 | — |
case-18 | fail→pass | 11,280 | 7,798 | -31% | 1 | 1 | 0% | 1,100 | 2,252 | +105% | 0 | 0 | — |
case-19 | fail→pass | 9,491 | 7,324 | -23% | 1 | 1 | 0% | 776 | 2,212 | +185% | 0 | 0 | — |
case-20 | pass→pass | 17,031 | 6,831 | -60% | 1 | 1 | 0% | 1,864 | 2,053 | +10% | 0 | 0 | — |
case-21 | pass→pass | 17,195 | 11,288 | -34% | 1 | 1 | 0% | 2,006 | 2,848 | +42% | 0 | 0 | — |
case-22 | pass→pass | 14,935 | 10,037 | -33% | 1 | 1 | 0% | 1,906 | 2,684 | +41% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted. The headline lift of +55 percentage points is the difference between those two pass rates over the 22 comparable cases.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
| Model | Method | Date | Lift |
|---|---|---|---|
| gemini-3.6-flash | verified | 8/9/2026 | +68% |
Other measured skills in the registry, with their headline benchmark lift.