소스 정보
- 저장소
- github/gh-aw
- 최근 소스 활동
- 2026년 5월 12일 22:45
- 감지된 SKILL.md 언어
- 영어
- 스타
- 4,937
- 포크
- 497
설치 방법
기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.
소스 파일 검토
설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.
메뉴
기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.
설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.
SOC 직업 분류 기준
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
직접 명령은 검토 Prompt를 거치지 않습니다. 실행하기 전에 소스를 확인하세요.
npx skills add https://github.com/github/gh-aw --skill javascript-refactoring명령은 한 줄로 유지됩니다. 복사하기 전에 가로로 스크롤해 전체 내용을 확인하세요.
로컬 사본을 원하시나요? SkillsMP에서 현재 제공할 수 있는 파일을 다운로드하세요.
SKILL.md 표시 중
GitHub Agentic Workflows (`gh-aw`) is a GitHub CLI extension for writing Agentic Workflows in markdown and compiling them to GitHub Actions.
Operate safely and efficiently inside a gh-aw workflow with a restricted tools/bash allowlist, and correctly triage tool-denial events before they exhaust the session's denial budget.
Optional Sergo examples for cache formats and reporting templates.
| name | javascript-refactoring |
| description | Split large JavaScript files into maintainable modules safely. |
Use this guide to refactor JavaScript into separate .cjs files in gh-aw.
gh-aw uses CommonJS modules (.cjs) for JavaScript in GitHub Actions workflows. These files are:
//go:embed directivesrequire() callsactions/github-script@v8Top-level .cjs scripts executed directly in workflows follow this pattern:
✅ Correct Pattern - Export main, but don't call it:
async function main() {
// Script logic here
core.info("Running the script");
}
module.exports = { main };
❌ Incorrect Pattern - Don't call main in the file:
async function main() {
// Script logic here
core.info("Running the script");
}
await main(); // ❌ Don't do this!
module.exports = { main };
Why this pattern?
await main() during inline execution in GitHub Actionsmain() with mocksExamples of top-level scripts:
create_issue.cjs - Creates GitHub issuesadd_comment.cjs - Adds comments to issues/PRsadd_labels.cjs - Adds labels to issues/PRsupdate_project.cjs - Updates GitHub ProjectsAll of these files export main but do not call it directly.
Create your new file in /home/runner/work/gh-aw/gh-aw/pkg/workflow/js/ with a descriptive name:
File naming convention:
sanitize_content.cjs, load_agent_output.cjs).cjs extension (CommonJS module)Example file structure:
// @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) {
// Implementation
return input;
}
// Export the function(s)
module.exports = {
myFunction,
};
Key points:
// @ts-check for TypeScript checking/// <reference types="@actions/github-script" /> for GitHub Actions typesmodule.exports = { ... }@actions/core or @actions/github - these are available globally in GitHub ActionsCreate a test file with the same base name plus .test.cjs:
Example: pkg/workflow/js/my_module.test.cjs
import { describe, it, expect, beforeEach, vi } from "vitest";
// Mock the global objects that GitHub Actions provides
const mockCore = {
debug: vi.fn(),
info: vi.fn(),
warning: vi.fn(),
error: vi.fn(),
setFailed: vi.fn(),
setOutput: vi.fn(),
summary: {
addRaw: vi.fn().mockReturnThis(),
write: vi.fn().mockResolvedValue(),
},
};
// Set up global mocks before importing the module
global.core = mockCore;
describe("myFunction", () => {
beforeEach(() => {
// Reset mocks before each test
vi.clearAllMocks();
});
it("should handle basic input", async () => {
// Import the module to test
const { myFunction } = await import("./my_module.cjs");
const result = myFunction("test input");
expect(result).toBe();
});
(, () => {
{ myFunction } = ();
result = ();
(result).();
});
});
Testing guidelines:
core and github globals as neededawait import()) to allow mocking before module loadbeforeEach to ensure test isolationpkg/workflow/js/*.test.cjs filesRun tests:
make test-js
Add an //go:embed directive and variable in the appropriate Go file:
Add to pkg/workflow/js.go:
//go:embed js/my_module.cjs
var myModuleScript string
Then add to the GetJavaScriptSources() function:
func GetJavaScriptSources() map[string]string {
return map[string]string{
"sanitize_content.cjs": sanitizeContentScript,
"sanitize_label_content.cjs": sanitizeLabelContentScript,
"sanitize_workflow_name.cjs": sanitizeWorkflowNameScript,
"load_agent_output.cjs": loadAgentOutputScript,
"staged_preview.cjs": stagedPreviewScript,
"is_truthy.cjs": isTruthyScript,
"my_module.cjs": myModuleScript, // Add this line
}
}
Add to pkg/workflow/scripts.go:
//go:embed js/my_script.cjs
var myScriptSource string
Then create a getter function with bundling:
var (
myScript string
myScriptOnce sync.Once
)
// getMyScript returns the bundled my_script script
// Bundling is performed on first access and cached for subsequent calls
func getMyScript() string {
myScriptOnce.Do(func() {
sources := GetJavaScriptSources()
bundled, err := BundleJavaScriptFromSources(myScriptSource, sources, "")
if err != nil {
scriptsLog.Printf("Bundling failed for my_script, using source as-is: %v", err)
// If bundling fails, use the source as-is
myScript = myScriptSource
} else {
myScript = bundled
}
})
return myScript
}
Important:
js.go are for shared utilities that get bundled into other scriptsscripts.go are for main scripts that use the bundler to inline dependenciessync.Once pattern for lazy bundling in scripts.gorequire() calls at runtimeIf you're creating a shared utility that will be used by other scripts via require(), it's automatically available through the GetJavaScriptSources() map (Step 3).
The bundler will:
require('./my_module.cjs') in any scriptGetJavaScriptSources() maprequire() statementNo additional bundler registration needed - just ensure the file is in the GetJavaScriptSources() map.
To use your new module in other JavaScript files, use CommonJS require():
Example usage in another .cjs file:
// @ts-check
/// <reference types="@actions/github-script" />
const { myFunction } = require("./my_module.cjs");
async function main() {
const result = myFunction("some input");
core.info(`Result: ${result}`);
}
module.exports = { main };
Important: Top-level scripts should export main but NOT call it directly. The bundler injects await main() during inline execution in GitHub Actions.
Require guidelines:
./.cjs extensionMultiple requires example:
const { sanitizeContent } = require("./sanitize_content.cjs");
const { loadAgentOutput } = require("./load_agent_output.cjs");
const { generateStagedPreview } = require("./staged_preview.cjs");
Let's walk through creating a new format_timestamp.cjs utility:
pkg/workflow/js/format_timestamp.cjs// @ts-check
/// <reference types="@actions/github-script" />
/**
* Formats a timestamp to ISO 8601 format
* @param {Date|string|number} timestamp - Timestamp to format
* @returns {string} ISO 8601 formatted timestamp
*/
function formatTimestamp(timestamp) {
const date = timestamp instanceof Date ? timestamp : new Date(timestamp);
return date.toISOString();
}
/**
* Formats a timestamp to a human-readable string
* @param {Date|string|number} timestamp - Timestamp to format
* @returns {string} Human-readable timestamp
*/
function formatTimestampHuman(timestamp) {
const date = timestamp instanceof Date ? timestamp : new Date(timestamp);
return date.toLocaleString('en-US', {
dateStyle: 'medium',
timeStyle: 'short'
});
}
module.exports = {
formatTimestamp,
formatTimestampHuman,
};
pkg/workflow/js/format_timestamp.test.cjsimport { describe, it, expect } from "vitest";
describe("formatTimestamp", () => {
it("should format Date object to ISO 8601", async () => {
const { formatTimestamp } = await import("./format_timestamp.cjs");
const date = new Date('2024-01-15T12:30:00Z');
const result = formatTimestamp(date);
expect(result).toBe('2024-01-15T12:30:00.000Z');
});
it("should format timestamp number to ISO 8601", async () => {
const { formatTimestamp } = await import("./format_timestamp.cjs");
const timestamp = 1705323000000; // Jan 15, 2024 12:30:00 UTC
const result = formatTimestamp(timestamp);
expect(result).toBe('2024-01-15T12:30:00.000Z');
});
});
describe("formatTimestampHuman", () => {
it("should format Date object to human-readable string", async () => {
const { formatTimestampHuman } = await import();
date = ();
result = (date);
(result).();
(result).();
(result).();
});
});
pkg/workflow/js.go://go:embed js/format_timestamp.cjs
var formatTimestampScript string
func GetJavaScriptSources() map[string]string {
return map[string]string{
// ... existing entries ...
"format_timestamp.cjs": formatTimestampScript,
}
}
// @ts-check
/// <reference types="@actions/github-script" />
const { formatTimestamp } = require("./format_timestamp.cjs");
async function main() {
const now = new Date();
core.info(`Current time: ${formatTimestamp(now)}`);
}
module.exports = { main };
Note: The script exports main but does not call it. The bundler will inject await main() when the script is executed inline in GitHub Actions.
# Format the code
make fmt-cjs
# Run JavaScript tests
make test-js
# Run Go tests (includes bundler tests)
make test-unit
# Build the binary (embeds JavaScript files)
make build
Before committing your refactored code:
.cjs file created in pkg/workflow/js/.test.cjs filemake test-jspkg/workflow/js.go or pkg/workflow/scripts.goGetJavaScriptSources() mapsync.Oncerequire() statements work correctly in other filesmake fmt-cjsmake lint-cjsmake test-unitmake buildFiles like sanitize_content.cjs, load_agent_output.cjs that provide reusable functions:
js.go with //go:embedGetJavaScriptSources() maprequire() in other scriptsFiles like create_issue.cjs, add_labels.cjs that are top-level scripts:
scripts.go with //go:embed as xxxSource variablesync.Once patternrequire() utilities from GetJavaScriptSources()main function but NOT call it - the bundler injects await main() during executionFiles like parse_claude_log.cjs that parse AI engine logs:
js.go with //go:embedGetLogParserScript() functionCause: File not added to GetJavaScriptSources() map
Solution: Add the file to the map in pkg/workflow/js.go
Cause: Missing global mocks
Solution: Add proper mocks before importing the module:
global.core = mockCore;
global.github = mockGithub;
Cause: File A requires File B which requires File A
Solution: Restructure to break the circular dependency, or combine the modules
Cause: Go build cache not recognizing embedded file changes
Solution:
make clean
make build
pkg/workflow/bundler.gopkg/workflow/js.gopkg/workflow/scripts.gopkg/workflow/js/*.test.cjs