Complete Technical Reference for Polyman CLI Tool
A TypeScript-based CLI tool for Codeforces problem setters that automates problem preparation workflows including test generation, validation, solution verification, and checker integration.
Polyman follows a layered architecture:
┌─────────────────────────────────────┐
│ CLI Layer (cli.ts) │
│ Commander.js command parsing │
└─────────────────────────────────────┘
↓
┌─────────────────────────────────────┐
│ Action Layer (actions.ts) │
│ High-level workflow orchestration│
└─────────────────────────────────────┘
↓
┌─────────────────────────────────────┐
│ Helper Layer (helpers/) │
│ Domain-specific logic modules │
└─────────────────────────────────────┘
↓
┌─────────────────────────────────────┐
│ Execution Engine (executor.ts) │
│ Process management & I/O │
└─────────────────────────────────────┘
↓
┌─────────────────────────────────────┐
│ Formatter (formatter.ts) │
│ Terminal output styling │
└─────────────────────────────────────┘
Polyman's core architecture consists of a CLI layer that delegates to action functions, which coordinate helper modules for specific tasks.
src/cli.tsThe CLI interface uses Commander.js to define all user-facing commands:
Commands:
new <directory> → createTemplatedownload-testlib → downloadTestliblist checkers → listAvailableCheckersgenerate [options] → generateTestsvalidate [options] → validateTestsrun <solution-name> [options] → solveTeststest <what> → testWhatverify → fullVerificationlist testsets → Lists all testsets from configurationExample:
program
.command('verify')
.description('Run full verification of the problem')
.action(fullVerification);
src/types.d.tsThis section documents all TypeScript type definitions used throughout Polyman.
type SolutionTag =
| 'MA' // Main correct solution (required, exactly one)
| 'OK' // Additional correct solution
| 'WA' // Wrong Answer
| 'TL' // Time Limit Exceeded
| 'ML' // Memory Limit Exceeded
| 'PE' // Presentation Error
| 'RE'; // Runtime Error
Polyman supports multiple programming languages and compiler versions for solutions, while generators, validators, and checkers must use C++ with testlib.h.
type CppSourceType =
| 'cpp.g++11'
| 'cpp.g++14'
| 'cpp.g++17'
| 'cpp.g++20'
| 'cpp.ms2017'
| 'cpp.ms2019'
| 'cpp.clang++17'
| 'cpp.clang++20';
type JavaSourceType = 'java.8' | 'java.11' | 'java.17' | 'java.21';
type PythonSourceType =
| 'python.2'
| 'python.3'
| 'python.pypy2'
| 'python.pypy3';
type SolutionSourceType = CppSourceType | JavaSourceType | PythonSourceType;
type TestlibSourceType = CppSourceType;
These interfaces define the structure of local configuration objects that reference files in your problem directory.
interface LocalSolution {
name: string; // Solution name/identifier
source: string; // Path to solution source file
tag: SolutionTag; // Expected behavior tag
sourceType?: SolutionSourceType; // Language/compiler (C++, Java, or Python)
}
interface LocalGenerator {
name: string; // Generator name/identifier
source: string; // Path to generator source file (must be C++)
sourceType?: TestlibSourceType; // C++ compiler version (generators must use testlib.h)
}
interface LocalChecker {
name: string; // Checker name
source: string; // Path to checker source or standard checker name
isStandard?: boolean; // True if using standard testlib checker
testsFilePath?: string; // Path to checker tests JSON file
sourceType?: TestlibSourceType; // C++ compiler version (checkers must use testlib.h)
}
interface LocalValidator {
name: string; // Validator name
source: string; // Path to validator source file (must be C++)
sourceType?: TestlibSourceType; // C++ compiler version (validators must use testlib.h)
testsFilePath?: string; // Path to validator tests JSON file
}
interface GeneratorScriptCommand {
type: 'manual' | 'generator';
generator?: string;
number?: number;
index?: number;
manualFile?: string;
group?: string;
points?: number;
range?: [number, number];
}
interface GeneratorScript {
commands?: GeneratorScriptCommand[];
script?: string;
scriptFile?: string;
}
interface LocalTestGroup {
name: string;
pointsPolicy?: 'COMPLETE_GROUP' | 'EACH_TEST';
feedbackPolicy?: 'NONE' | 'POINTS' | 'ICPC' | 'COMPLETE';
dependencies?: string[];
}
interface LocalTestset {
name: string;
generatorScript?: GeneratorScript;
groupsEnabled?: boolean;
pointsEnabled?: boolean;
groups?: LocalTestGroup[];
}
The main configuration file that defines all aspects of a competitive programming problem.
interface ConfigFile {
// Polygon metadata
problemId?: number;
name: string;
owner?: string;
revision?: number;
// Problem info
timeLimit: number; // Milliseconds
memoryLimit: number; // Megabytes
inputFile: string; // 'stdin' or filename
outputFile: string; // 'stdout' or filename
interactive: boolean;
// Tags and descriptions
tags?: string[];
description?: string;
tutorial?: string;
// Statements
statements: {
[language: string]: Statement;
};
// Solutions
solutions: LocalSolution[];
// Generators
generators?: LocalGenerator[];
// Checker
checker: LocalChecker;
// Validator
validator: LocalValidator;
// Testsets
testsets?: LocalTestset[];
}
interface Statement {
encoding: string;
name: string;
legend: string;
input: string;
output: string;
scoring?: string;
interaction?: string;
notes?: string;
tutorial?: string;
}
src/cli.tsThe CLI layer is minimal and delegates to action functions:
// Creates new problem template
program
.command('new <directory>')
.description('Create a new problem template')
.action(createTemplate);
// Downloads testlib.h from GitHub
program
.command('download-testlib')
.description('Download testlib.h header file')
.action(downloadTestlib);
// Lists available standard checkers
program
.command('list checkers')
.description('List all available standard checkers')
.action(listAvailableCheckers);
// Runs generators to create test files
program
.command('generate')
.description('Generate tests for testsets')
.option('-a, --all', 'Generate all testsets')
.option('-t, --testset <name>', 'Generate specific testset')
.option('-g, --group <name>', 'Generate specific group within testset')
.option('-i, --index <number>', 'Generate specific test by index')
.action(generateTests);
// Validates test inputs
program
.command('validate')
.description('Validate tests using validator')
.option('-a, --all', 'Validate all testsets')
.option('-t, --testset <name>', 'Validate specific testset')
.option('-g, --group <name>', 'Validate specific group within testset')
.option('-i, --index <number>', 'Validate specific test by index')
.action(validateTests);
// Executes solutions on tests
program
.command('run <solution-name>')
.description('Run solution on tests')
.option('-a, --all', 'Run on all testsets')
.option('-t, --testset <name>', 'Run on specific testset')
.option('-g, --group <name>', 'Run on specific group within testset')
.option('-i, --index <number>', 'Run on specific test by index')
.action(solveTests);
// Tests validators/checkers/solutions
program
.command('test <what>')
.description('Test validator, checker, or solution')
.action(testWhat);
// Complete verification workflow
program
.command('verify')
.description('Run full problem verification')
.action(fullVerification);
All Actions are separated into steps, each step calls relevant helper functions to perform the task, each step is supposed to be completely independent from others to allow better maintainability and testability.
src/actions.tsHigh-level workflow orchestration functions that coordinate helper modules.
createTemplate(directory: string)Creates new problem directory structure from template.
Workflow:
template/Called by: polyman new <dir>
downloadTestlib()Downloads testlib.h from official repository.
Workflow:
https://raw.githubusercontent.com/MikeMirzayanov/testlib/master/testlib.hCalled by: polyman download-testlib
Implementation:
const testlibUrl =
'https://raw.githubusercontent.com/MikeMirzayanov/testlib/master/testlib.h';
const testlibContent = await downloadFile(testlibUrl);
fs.writeFileSync('testlib.h', testlibContent, 'utf-8');
listAvailableCheckers()Lists all standard checkers from assets/checkers/.
Workflow:
// Description:)fmtCalled by: polyman list checkers
Output Format:
1. ncmp.cpp → Compares signed int64 numbers
2. wcmp.cpp → Compares sequences of tokens
...
generateTests(generatorName: string)Runs generators to create test input files.
Workflow:
Config.jsonensureGeneratorsExistrunMatchingGeneratorsCalled by: polyman generate [options]
Options:
--all: Generate all testsets--testset <name>: Testset name--group <name>: (Optional) Group name within testset--index <number>: (Optional) Test number within testsetvalidateTests(target: string, modifier?: string)Validates test input files using validator.
Workflow:
ensureValidatorExistsvalidateSingleTest or validateAllTestsCalled by: polyman validate [options]
Options:
--all: Validate all testsets--testset <name>: Testset name--group <name>: (Optional) Group name within testset--index <number>: (Optional) Test number within testsetsolveTests(solutionName: string, testNumber: string)Executes solutions on test inputs.
Workflow:
validateSolutionsExistrunSingleSolutionOnTests or runMatchingSolutionsOnTestsCalled by: polyman run <name> [options]
Options:
--all: Run on all testsets--testset <name>: Testset name--group <name>: (Optional) Group name within testset--index <number>: (Optional) Test number within testsettestWhat(what: string)Tests validators, checkers, or solutions against expected behavior.
Workflow:
For 'validator':
testValidatorItselfvalidator_tests.jsonFor 'checker':
testCheckerItselfchecker_tests.jsonFor solution name:
testSolutionAgainstMainCorrectCalled by: polyman test <what>
fullVerification()Complete problem verification workflow.
Workflow:
testValidatorItselftestCheckerItselfCalled by: polyman verify
Success Criteria:
Polyman provides comprehensive Polygon integration for remote problem management. All remote commands are namespaced under polyman remote.
registerApiKeyAndSecretAction(apiKey: string, secret: string)Registers Polygon API credentials locally for future use.
Workflow:
~/.polyman/credentials.json)Called by: polyman remote register <api-key> <secret>
Storage Location:
~/.polyman/credentials.json%USERPROFILE%\.polyman\credentials.jsonremoteListProblemsAction(owner?: string)Lists all problems from Polygon accessible to the user.
Workflow:
stepReadCredentialsstepInitializeSDKstepListProblemsstepDisplayProblemsCalled by:
polyman remote listpolyman remote list --owner <username>Output Format:
╔════════════════════════════════════════════════════════════════╗
║ YOUR PROBLEMS ON POLYGON ║
╚════════════════════════════════════════════════════════════════╝
ID | Name | Owner | Access | Modified
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
123456 | A Plus B | tourist | WRITE | Yes
789012 | Graph Problem | you | OWNER | No
remotePullProblemAction(problemId: string, directory: string, options: PullOptions)Downloads a problem from Polygon to local directory.
Workflow:
Config.json with complete configurationCalled by: polyman remote pull <problem-id> <directory> [options]
Options:
interface PullOptions {
all?: boolean; // Pull all components (default)
solutions?: boolean; // Pull solutions only
checker?: boolean; // Pull checker only
validator?: boolean; // Pull validator only
generators?: boolean; // Pull generators only
statements?: boolean; // Pull statements only
tests?: string; // Pull tests (optionally specify testsets)
metadata?: boolean; // Pull description and tags
info?: boolean; // Pull problem info (limits)
}
Examples:
# Pull everything (default)
polyman remote pull 123456 ./my-problem
# Pull only solutions and checker
polyman remote pull 123456 ./my-problem -s -c
# Pull specific testsets
polyman remote pull 123456 ./my-problem -t samples,tests
Line Ending Normalization:
All text files are automatically converted from Windows (CRLF) to Unix (LF) line endings during pull.
remotePushProblemAction(directory: string, options: PushOptions)Uploads local problem changes to Polygon.
Workflow:
Config.json to get problem IDCalled by: polyman remote push <directory> [options]
Options:
interface PushOptions {
all?: boolean; // Push all components (default)
solutions?: boolean; // Push solutions
checker?: boolean; // Push checker
validator?: boolean; // Push validator
generators?: boolean; // Push generators
statements?: boolean; // Push statements
tests?: boolean; // Push testsets and tests
metadata?: boolean; // Push description and tags
info?: boolean; // Update problem info (limits)
}
Examples:
# Push everything (default)
polyman remote push . ./my-problem
# Push only solutions and checker
polyman remote push . ./my-problem -s -c
# Push only tests
polyman remote push . ./my-problem -t
Important Notes:
polyman remote commit)Config.json that has a valid problem ID, it will be used.remoteViewProblemAction(problemIdOrPath: string)Displays comprehensive information about a problem on Polygon.
Workflow:
Called by:
polyman remote view <problem-id>polyman remote view ./my-problemOutput Format:
╔════════════════════════════════════════════════════════════════╗
║ PROBLEM DETAILS ║
╚════════════════════════════════════════════════════════════════╝
Basic Information:
ID: 123456
Name: A Plus B
Owner: tourist
Access: WRITE
Modified: Yes
Revision: 42
Limits:
Time Limit: 1000 ms
Memory Limit: 256 MB
Input: stdin
Output: stdout
Interactive: No
Components:
Solutions: 5 files
Checker: custom_checker.cpp
Validator: validator.cpp
Generators: 3 files
Statements: 2 languages (english, russian)
Packages:
Latest Package: Revision 40 (Available)
Total Packages: 12
remoteCommitProblemAction(problemIdOrPath: string, commitMessage: string)Commits pending changes to Polygon problem.
Workflow:
Called by:
polyman remote commit <problem-id> <message>polyman remote commit ./my-problem <message>Examples:
# Commit with problem ID
polyman remote commit 123456 "Updated test cases"
# Commit using directory path
polyman remote commit ./my-problem "Fixed validator"
API Call:
await sdk.commitChanges(problemId, {
message: commitMessage,
});
remotePackageProblemAction(problemIdOrPath: string, packageType: string)Builds a problem package on Polygon and waits for completion.
Workflow:
Called by:
polyman remote package <problem-id> <type>polyman remote package ./my-problem <type>Package Types:
standard - Windows executables, no generated testslinux - Generated tests, no binarieswindows - Generated tests, Windows binariesfull - All three types aboveExamples:
# Build standard package
polyman remote package 123456 standard
# Build full package
polyman remote package ./my-problem full
Polling Logic:
while (attempts < maxAttempts) {
await new Promise(resolve => setTimeout(resolve, 60000)); // Wait 1 minute
const currentPackages = await sdk.listPackages(problemId);
if (currentPackages.length > initialCount) {
const latestPackage = /* get latest package */;
if (latestPackage.state === 'READY' || latestPackage.state === 'FAILED') {
break;
}
}
}
Helper modules contain domain-specific logic for generators, validators, checkers, solutions, and utilities.
Polyman includes specialized helper modules for Polygon integration located in src/helpers/remote/.
src/helpers/remote/pulling.tsHandles downloading problem components from Polygon.
Key Functions:
downloadSolutions(sdk: PolygonSDK, problemId: number, problemDir: string)
Downloads all solutions from Polygon.
Workflow:
solutions/ directoryReturns:
{
data: Array<{
name: string;
source: string;
tag: SolutionTag;
}>;
count: number;
}
downloadChecker(sdk: PolygonSDK, problemId: number, problemDir: string)
Downloads checker from Polygon.
Workflow:
std::)Returns:
{
data: {
name: string;
source: string;
isStandard: boolean;
testsFilePath?: string;
};
count: number;
}
downloadValidator(sdk: PolygonSDK, problemId: number, problemDir: string)
Downloads validator from Polygon.
Workflow:
downloadGenerators(sdk: PolygonSDK, problemId: number, problemDir: string, validatorName: string)
Downloads all generator source files.
Workflow:
downloadStatements(sdk: PolygonSDK, problemId: number, problemDir: string)
Downloads problem statements in all languages.
Workflow:
fetchProblemMetadata(sdk: PolygonSDK, problemId: number)
Fetches problem description and tags.
Returns:
{
description: string;
tags: string[];
}
downloadTestsetAndBuildGenerationScripts(sdk: PolygonSDK, problemId: number, problemDir: string, testsetName: string, generators: LocalGenerator[])
Downloads tests and builds testset configuration.
Workflow:
manual/<testset>/ directoryPerformance Optimization:
Manual test inputs are fetched in parallel using Promise.all():
const manualTestPromises = manualTests.map(async test => {
const input = await sdk.getTestInput(problemId, testsetName, test.index);
return { test, input };
});
const results = await Promise.all(manualTestPromises);
Returns:
{
testset: LocalTestset;
manualCount: number;
}
src/helpers/remote/pushing.tsHandles uploading problem components to Polygon.
Key Functions:
uploadSolutions(sdk: PolygonSDK, problemId: number, problemDir: string, config: ConfigFile)
Uploads all solutions to Polygon.
Workflow:
uploadChecker(sdk: PolygonSDK, problemId: number, problemDir: string, config: ConfigFile)
Uploads checker to Polygon.
Workflow:
uploadValidator(sdk: PolygonSDK, problemId: number, problemDir: string, config: ConfigFile)
Uploads validator to Polygon.
Workflow:
uploadGenerators(sdk: PolygonSDK, problemId: number, problemDir: string, config: ConfigFile)
Uploads all generators to Polygon.
Workflow:
uploadStatements(sdk: PolygonSDK, problemId: number, problemDir: string, config: ConfigFile)
Uploads problem statements to Polygon.
Workflow:
uploadMetadata(sdk: PolygonSDK, problemId: number, config: ConfigFile)
Uploads problem description and tags.
uploadTestsets(sdk: PolygonSDK, problemId: number, problemDir: string, config: ConfigFile)
Uploads testsets and tests to Polygon.
Workflow:
Performance Optimization:
Manual tests are uploaded in parallel:
const manualTestsPromises = createManualTestsPromises(
sdk,
problemId,
problemDir,
testset,
indices
);
await Promise.all(manualTestsPromises);
Test Upload:
Each manual test:
sdk.saveTest() with options (group, points, useInStatements)updateProblemInfo(sdk: PolygonSDK, problemId: number, config: ConfigFile)
Updates problem information (limits, I/O files).
API Call:
await sdk.updateProblemInfo(problemId, {
inputFile: config.inputFile,
outputFile: config.outputFile,
interactive: config.interactive,
timeLimit: config.timeLimit,
memoryLimit: config.memoryLimit,
});
src/helpers/remote/utils.tsUtility functions for remote operations.
Key Functions:
normalizeLineEndingsFromWinToUnix(content: string): string
Converts Windows line endings (CRLF) to Unix (LF).
return content.replace(/\r\n/g, '\n');
Used during: Pull operations
normalizeLineEndingsFromUnixToWin(content: string): string
Converts Unix line endings (LF) to Windows (CRLF).
return content.replace(/\n/g, '\r\n');
Used during: Push operations
readCredentialsFromHomeDirectory(): { apiKey: string; secret: string }
Reads stored API credentials.
Location:
~/.polyman/credentials.json%USERPROFILE%\.polyman\credentials.jsonsaveCredentialsToHomeDirectory(apiKey: string, secret: string)
Stores API credentials securely.
getProblemIdFromPath(path: string): number
Extracts problem ID from Config.json in directory.
Usage:
// User provides directory path instead of problem ID
const problemId = getProblemIdFromPath('./my-problem');
src/helpers/utils.tsCore utility functions for compilation, configuration, and file operations.
compileCPP(sourcePath: string): Promise<string>
Compiles C++ source using g++.
// Compilation command:
g++ -o <output> <source>
Parameters:
sourcePath: Path to .cpp fileReturns: Path to compiled executable
Throws: Error if not .cpp or compilation fails
compileJava(sourcePath: string): Promise<string>
Compiles Java source using javac.
Returns: Class name (e.g., 'Solution')
readConfigFile(): ConfigFile
Reads and parses Config.json.
Returns: Parsed configuration object
Throws: Error if file doesn't exist or has invalid JSON
isNumeric(value: string): boolean
Checks if string represents a number.
ensureDirectoryExists(dirName: string)
Creates directory if it doesn't exist (recursive).
ensureDirectoryExists('tests');
ensureDirectoryExists('nested/path/to/dir');
removeDirectory(dirName: string)
Removes directory and all contents.
readFirstLine(filePath: string): string
Reads first line from file (used for verdict detection).
logError(error: unknown)
Logs error with formatted output.
logErrorAndExit(error: unknown)
Logs error and exits with code 1.
logErrorAndThrow(error: unknown, message?: string)
Logs error and re-throws.
throwError(error: unknown, message?: string): never
Throws error, ensuring it's an Error instance.
src/helpers/generator.tsHandles test case generation.
ensureGeneratorsExist(generators: LocalGenerator[] | undefined)Type assertion that throws if no generators defined.
runMatchingGenerators(generators: LocalGenerator[], generatorName: string)Runs generators matching name or 'all'.
Workflow:
compileCPPgenerateTestFilesSpecial Cases:
name: 'samples' → Uses existing manual test filesname: 'manual' → Uses existing manual test filesPrivate Functions:
compileGenerator(generator: LocalGenerator): Promise<string>
Compiles generator C++ source.
Returns: Path to compiled executable
generateTestFiles(compiledPath: string, generator: LocalGenerator, testsDir: string)
Generates test files by running generator for each test number in range.
Command:
./generator <testNum> > tests/test<testNum>.txt
ensureTestsDirectory(): string
Creates tests/ directory if needed.
src/helpers/validator.tsInput validation system.
ensureValidatorExists(validator: LocalValidator | undefined)Type assertion for validator existence.
validateSingleTest(testNumber: number)Validates a single test file.
Workflow:
compileValidatorCommand:
./validator < tests/test<N>.txt
validateAllTests()Validates all test files in tests/ directory.
Workflow:
validateSingleTesttestValidatorItself()Validator self-testing using validator_tests.json.
Workflow:
parseValidatorTestsmakeValidatorTestsTest File Format:
{
"tests": [
{
"stdin": "5\n",
"expectedVerdict": "VALID"
},
{
"stdin": "101\n",
"expectedVerdict": "INVALID"
}
]
}
Private Functions:
compileValidator(): Promise<void>
Compiles validator C++ source.
parseValidatorTests(): Promise<ValidatorTest[]>
Reads and parses validator/validator_tests.json.
makeValidatorTests()
Creates test files in validator_tests/ directory.
runValidator(compiledPath: string, testFilePath: string): Promise<ValidatorVerdict>
Executes validator and returns verdict based on exit code.
getValidatorVerdict(exitCode: number): ValidatorVerdict
Maps exit code to verdict (0 → VALID, 3 → INVALID).
src/helpers/checker.tsOutput verification system.
ensureCheckerExists(checker: LocalChecker | undefined)Type assertion for checker existence.
compileChecker(checker: LocalChecker): Promise<void>Compiles checker.
For custom checkers:
g++ -o checker checker/chk.cpp
For standard checkers:
Copies from assets/checkers/ and compiles.
runChecker(compiledPath: string, inputFile: string, outputFile: string, answerFile: string): Promise<CheckerVerdict>Runs checker to compare output with answer.
Command:
./checker <input> <output> <answer>
Returns: Verdict from checker output (OK/WA/PE)
testCheckerItself()Checker self-testing using checker_tests.json.
Workflow:
parseCheckerTestsTest File Format:
{
"tests": [
{
"stdin": "3",
"stdout": "YES",
"answer": "YES",
"verdict": "OK"
},
{
"stdin": "3",
"stdout": "NO",
"answer": "YES",
"verdict": "WA"
}
]
}
getExpectedCheckerVerdict(solutionTag: SolutionTag): CheckerVerdictMaps solution tag to expected checker verdict.
Mapping:
'MA', 'OK', 'TL', 'ML' → 'OK' or 'WRONG_ANSWER' (depends on checker)'WA' → 'WRONG_ANSWER''PE' → 'PRESENTATION_ERROR''RE' → Depends on runtime behaviorPrivate Functions:
parseCheckerTests(): Promise<CheckerTest[]>
Reads checker/checker_tests.json.
makeCheckerTests()
Creates test files in checker_tests/ directory.
getCheckerVerdict(output: string): CheckerVerdict
Parses checker output for verdict.
src/helpers/solution.tsSolution execution and verification.
validateSolutionsExist(solutions: LocalSolution[] | undefined)Type assertion for solutions existence.
ensureMainSolutionExists(solutions: LocalSolution[] | undefined)Type assertion ensuring main-correct solution exists.
Throws: If no main-correct solution found.
ensureSolutionExists(solutions: LocalSolution[] | undefined, solutionName: string)Type assertion for specific solution existence.
getMainSolution(solutions: LocalSolution[]): LocalSolutionReturns the main-correct solution.
runSingleSolutionOnTests(config: ConfigFile, solutionName: string, testNumber: string)Runs a solution on test(s).
Workflow:
compileSolutionrunSolutionOnTestsrunMatchingSolutionsOnTests(config: ConfigFile, solutionName: string, testNumber: string)Runs solution(s) matching name on test(s).
Parameters:
solutionName: Solution name or 'all'testNumber: Test number or 'all'testSolutionAgainstMainCorrect(solutionName: string)Tests solution against main-correct using checker.
Workflow:
Verification:
startTheComparisonProcess(solutions: LocalSolution[], checker: LocalChecker)Compares all solutions against main-correct.
Workflow:
Private Functions:
compileSolution(sourcePath: string): Promise<void>
Compiles solution based on language.
Supported Languages:
compileCPPcompileJavarunSolutionOnTests(solution: LocalSolution, compiledPath: string, testFiles: string[])
Executes solution on test files.
For each test:
runSolutionOnTest(compiledPath: string, testFilePath: string, outputPath: string, timeLimit: number, memoryLimit: number, solutionTag: SolutionTag): Promise<void>
Runs solution on single test.
Verdict Detection:
ensureOutputDirectory(solutionName: string): string
Creates solutions-outputs/<solution-name>/ directory.
compareSolutionWithMainCorrect(solution: LocalSolution, mainSolution: LocalSolution, checker: LocalChecker, compiledChecker: string, testFiles: string[]): Promise<VerdictTracker>
Compares solution outputs with main-correct.
Returns: Verdict tracker with counts per verdict type.
validateSolutionVerdicts(solution: LocalSolution, verdictTracker: VerdictTracker)
Validates verdicts match expected solution type.
Rules:
isTLE(firstLine: string): boolean
Checks if first line indicates TLE.
isMLE(firstLine: string): boolean
Checks if first line indicates MLE.
isRTE(firstLine: string): boolean
Checks if first line indicates RTE.
isTLEValue(solutionTag: SolutionTag): boolean
Checks if solution tag allows TLE verdict.
isMLEValue(solutionTag: SolutionTag): boolean
Checks if solution tag allows MLE verdict.
src/helpers/create-template.tsTemplate creation utilities.
copyTemplate(sourceDir: string, targetDir: string)Recursively copies template directory.
logTemplateCreationSuccess(problemName: string)Logs success message with next steps.
src/helpers/testlib-download.tsdownloadFile(url: string): Promise<string>Downloads file from URL.
Uses: Node.js https module
src/executor.tsLow-level process execution with timeout and memory limit support.
executor.executeWithTimeout(command: string, args: string[], timeoutMs: number, memoryLimitMB: number): Promise<ExecutionResult>Executes command with resource limits.
Parameters:
command: Executable path or commandargs: Command-line argumentstimeoutMs: Maximum execution timememoryLimitMB: Maximum memory usageReturns:
interface ExecutionResult {
stdout: string;
stderr: string;
exitCode: number;
timedOut: boolean;
memoryExceeded: boolean;
}
Platform Support:
ulimit for memory limitingImplementation:
// Linux memory limiting
if (process.platform === 'linux' && memoryLimitMB > 0) {
const memoryLimitKB = memoryLimitMB * 1024;
command = `ulimit -v ${memoryLimitKB} && ${command}`;
}
// Spawn process with timeout
const child = spawn(command, args, { shell: true });
const timeout = setTimeout(() => {
child.kill();
timedOut = true;
}, timeoutMs);
src/formatter.tsTerminal output styling with Codeforces theme.
Colors:
#1E88E5 (blue)#FF6B6B (red)#4CAF50 (green)#FFC107 (yellow)section(title: string)
Prints section header with box.
╭─────────────────────────────────╮
│ 📁 SECTION TITLE │
╰─────────────────────────────────╯
step(stepNumber: number, title: string)
Prints numbered step header.
╭─ STEP 1: Creating Directory
stepComplete(message: string)
Prints step completion.
╰─ ✓ Done
success(message: string)
Prints success message (green).
error(message: string)
Prints error message (red).
warning(message: string)
Prints warning message (yellow).
info(message: string)
Prints info message (blue).
log(message: string)
Prints plain message.
successBox(title: string)
Prints success box with checkmark.
errorBox(title: string)
Prints error box with X mark.
Utility Methods:
primary(text: string): Blue colorhighlight(text: string): Cyan colordim(text: string): Dimmed textsuccessIcon(): ✓ iconerrorIcon(): ✗ iconwarningIcon(): ⚠ iconinfoIcon(): ℹ iconThe Config.json file is the heart of every Polyman problem, defining all metadata, constraints, and components.
Location: Config.json in problem root
Full Schema:
{
"name": "problem-name",
"description": "Problem description",
"timeLimit": 2000,
"memoryLimit": 256,
"inputFile": "stdin",
"outputFile": "stdout",
"interactive": false,
"tags": ["tag1", "tag2"],
"statements": {
"english": {
"encoding": "UTF-8",
"name": "Problem Title",
"legend": "./statements/english/legend.tex",
"input": "./statements/english/input-format.tex",
"output": "./statements/english/output-format.tex",
"notes": "./statements/english/notes.tex"
}
},
"solutions": [
{
"name": "main",
"source": "./solutions/Solution.cpp",
"tag": "MA",
"sourceType": "cpp.g++17"
}
],
"generators": [
{
"name": "gen-all",
"source": "./generators/Generator.cpp"
}
],
"checker": {
"name": "ncmp.cpp",
"isStandard": true
},
"validator": {
"name": "validator",
"source": "./validator/Validator.cpp",
"testsFilePath": "./validator/validator_tests.json"
},
"testsets": [
{
"name": "tests",
"generatorScript": {
"commands": [
{
"type": "generator",
"generator": "gen-all",
"range": [1, 20],
"group": "main"
}
]
},
"groupsEnabled": true,
"groups": [
{
"name": "main"
}
]
}
]
}
Required Fields:
nametimeLimitmemoryLimitinputFileoutputFilesolutions (must include exactly one with tag MA)checkervalidatortestsetsOptional Fields:
descriptiontagsinteractivestatementsC++:
g++ -o output source.cpp
Java:
javac source.java
java ClassName < input.txt > output.txt
Python:
python3 source.py < input.txt > output.txt
.cpp → compileCPP.java → compileJava.py → Return interpreter command0 → VALID3 → INVALIDValidator tests from validator/validator_tests.json:
Test Structure:
{
"tests": [
{
"stdin": "test input content",
"expectedVerdict": "VALID" | "INVALID"
}
]
}
Verification:
validator_tests/Compilation:
compileSolutionExecution:
Comparison:
Verification:
From Output File First Line:
"Time Limit Exceeded" → TLE"Memory Limit Exceeded" → MLE"Runtime Error" → RTEVerdict Tracking:
type VerdictTracker = {
didWA: boolean;
didTLE: boolean;
didMLE: boolean;
didRTE: boolean;
};
Located in assets/checkers/.
Available:
ncmp.cpp - Sequence of signed 64-bit integersicmp.cpp - Single signed 32-bit integerwcmp.cpp - Sequence of tokens (words)fcmp.cpp - Line-by-line exact comparisonlcmp.cpp - Line-by-line comparison ignoring extra whitespacedcmp.cpp - Double with absolute/relative error 1E-6rcmp.cpp - Double with absolute error 1.5E-6rcmp4.cpp - Double with 4 decimal places precisionrcmp6.cpp - Double with 6 decimal places precisionrcmp9.cpp - Double with 9 decimal places precisionrncmp.cpp - Sequence of doubles with absolute error 1.5E-5acmp.cpp - Double with maximal absolute erroryesno.cpp - Single YES/NO answer (case insensitive)nyesno.cpp - Multiple YES/NO answers (case insensitive)hcmp.cpp - Arbitrary-precision (huge) integersuncmp.cpp - Unordered sequence of signed 64-bit integerscaseicmp.cpp - Case-sensitive single integer comparisoncasencmp.cpp - Case-sensitive sequence of integerscasewcmp.cpp - Case-sensitive sequence of tokensLocation: checker/chk.cpp
Interface:
int main(int argc, char* argv[]) {
registerTestlibCmd(argc, argv);
// Read input
// Read output (ouf)
// Read answer (ans)
// Compare and return verdict:
// quitf(_ok, "Correct");
// quitf(_wa, "Wrong Answer");
// quitf(_pe, "Presentation Error");
}
Command:
./checker <input_file> <output_file> <answer_file>
Output Parsing:
ok/OK → OKwrong/WA → WApresentation/PE → PEInput: Test number as command-line argument
Output: Test content to stdout
Example:
int main(int argc, char* argv[]) {
registerGen(argc, argv, 1);
int testNum = atoi(argv[1]);
int n = rnd.next(1, testNum * 100);
cout << n << endl;
return 0;
}
./generator <testNum> > tests/test<testNum>.txt
Samples (name: 'samples'):
tests/test1.txt, tests/test2.txt, etc.Manual (name: 'manual'):
Polyman provides comprehensive error handling with formatted output and proper exit codes.
logErrorlogErrorAndExitlogErrorAndThrowConfiguration Errors:
Compilation Errors:
Execution Errors:
Validation Errors:
Checker Errors:
Polyman provides comprehensive integration with the Codeforces Polygon system through a type-safe TypeScript SDK and remote operations.
Location: src/polygon.ts
The Polygon SDK is a complete TypeScript implementation of the Polygon API v1, providing type-safe methods for all Polygon operations.
class PolygonSDK {
private apiKey: string;
private apiSecret: string;
private baseUrl: string;
constructor(config: PolygonConfig) {
this.apiKey = config.apiKey;
this.apiSecret = config.apiSecret;
this.baseUrl = config.baseUrl || 'https://polygon.codeforces.com/api';
}
// 54+ API methods for complete Polygon integration
}
Problem Management:
listProblems(options?) - List all accessible problemsgetProblemInfo(problemId) - Get problem detailsupdateProblemInfo(problemId, info) - Update limits and settingsupdateWorkingCopy(problemId) - Update working copy from repositorydiscardWorkingCopy(problemId) - Discard uncommitted changescommitChanges(problemId, options) - Commit changes with messageStatements:
getStatements(problemId) - Get statements in all languagessaveStatement(problemId, lang, statement) - Upload/update statementgetStatementResources(problemId) - List statement resourcessaveStatementResource(problemId, name, file) - Upload resource fileSolutions:
getSolutions(problemId) - List all solutionsviewSolution(problemId, name) - Get solution source codesaveSolution(problemId, name, file, tag) - Upload solution with tageditSolutionExtraTags(problemId, name, tags) - Update solution tagsChecker & Validator:
getChecker(problemId) - Get current checker namesetChecker(problemId, checker) - Set problem checkergetValidator(problemId) - Get current validator namesetValidator(problemId, validator) - Set problem validatorgetCheckerTests(problemId) - Get checker self-testssaveCheckerTest(problemId, test) - Add/edit checker testgetValidatorTests(problemId) - Get validator self-testssaveValidatorTest(problemId, test) - Add/edit validator testGenerators:
getFiles(problemId) - List all problem filesviewFile(problemId, type, name) - Get file contentsaveFile(problemId, type, name, file) - Upload filegetScript(problemId, testset) - Get generation scriptsaveScript(problemId, testset, script) - Update generation scriptTests:
getTests(problemId, testset, noInputs?) - List testsgetTestInput(problemId, testset, index) - Get test inputgetTestAnswer(problemId, testset, index) - Get test answersaveTest(problemId, testset, index, input, options) - Add/edit testsetTestGroup(problemId, testset, group, indices) - Assign tests to groupenableGroups(problemId, testset, enable) - Enable/disable test groupsenablePoints(problemId, testset, enable) - Enable/disable pointsviewTestGroup(problemId, testset, group) - Get group infosaveTestGroup(problemId, testset, group, options) - Create/edit groupMetadata:
viewTags(problemId) - Get problem tagssaveTags(problemId, tags) - Update tagsviewGeneralDescription(problemId) - Get descriptionsaveGeneralDescription(problemId, description) - Update descriptionviewGeneralTutorial(problemId) - Get tutorialsaveGeneralTutorial(problemId, tutorial) - Update tutorialPackages:
listPackages(problemId) - List all built packagesdownloadPackage(problemId, packageId, type) - Download package zipbuildPackage(problemId, full, verify) - Build new packageContests:
getContestProblems(contestId) - List problems in contestPolygon API uses SHA-512 signature-based authentication.
API Credentials:
{
apiKey: 'your-api-key',
apiSecret: 'your-api-secret'
}
Request Signing:
// Generate 6-character random prefix
const rand = generateRandomString(6);
// Sort parameters alphabetically
const sortedParams = Object.keys(params).sort();
// Build signature string
const sigString = `${rand}/${methodName}?${paramString}#${apiSecret}`;
// Compute SHA-512 hash
const hash = crypto.createHash('sha512').update(sigString).digest('hex');
// Final signature
const apiSig = rand + hash;
Request Parameters:
apiKey - Your API keytime - Current Unix timestampapiSig - Computed signatureAPI Call:
POST https://polygon.codeforces.com/api/{method}
Content-Type: application/x-www-form-urlencoded
apiKey=xxx&time=xxx&apiSig=xxx&problemId=xxx&...
~/.polyman/credentials.jsonUser Command
↓
polyman remote pull 123456 ./my-problem
↓
remotePullProblemAction
↓
┌────────────────────────────────────┐
│ Step 1: Read Credentials │
│ - Load from ~/.polyman/ │
└────────────────────────────────────┘
↓
┌────────────────────────────────────┐
│ Step 2: Initialize SDK │
│ - Create PolygonSDK instance │
│ - Configure authentication │
└────────────────────────────────────┘
↓
┌────────────────────────────────────┐
│ Step 3: Fetch Problem Info │
│ - Get limits, I/O files │
│ - Get problem metadata │
└────────────────────────────────────┘
↓
┌────────────────────────────────────┐
│ Step 4: Create Directory │
│ - Create problem directory │
│ - Create subdirectories │
└────────────────────────────────────┘
↓
┌────────────────────────────────────┐
│ Step 5: Download Components │
│ ┌──────────────────────────────┐ │
│ │ Solutions (parallel) │ │
│ │ Checker + tests │ │
│ │ Validator + tests │ │
│ │ Generators │ │
│ │ Statements (all languages) │ │
│ │ Tests (parallel fetch) │ │
│ │ Metadata │ │
│ └──────────────────────────────┘ │
│ - Normalize line endings (→ Unix) │
└────────────────────────────────────┘
↓
┌────────────────────────────────────┐
│ Step 6: Generate Config.json │
│ - Build complete configuration │
│ - Include all metadata │
└────────────────────────────────────┘
↓
Success! Problem ready for local work
User Command
↓
polyman remote push . ./my-problem
↓
remotePushProblemAction
↓
┌────────────────────────────────────┐
│ Step 1: Read Credentials │
└────────────────────────────────────┘
↓
┌────────────────────────────────────┐
│ Step 2: Initialize SDK │
└────────────────────────────────────┘
↓
┌────────────────────────────────────┐
│ Step 3: Read Config.json │
│ - Get problem ID │
│ - Get all configurations │
└────────────────────────────────────┘
↓
┌────────────────────────────────────┐
│ Step 4: Update Problem Info │
│ - Upload time/memory limits │
│ - Upload I/O file settings │
└────────────────────────────────────┘
↓
┌────────────────────────────────────┐
│ Step 5-11: Upload Components │
│ ┌──────────────────────────────┐ │
│ │ Solutions with tags │ │
│ │ Checker + set active │ │
│ │ Validator + tests + set │ │
│ │ Generators │ │
│ │ Statements (all languages) │ │
│ │ Metadata │ │
│ │ Testsets: │ │
│ │ - Clear existing │ │
│ │ - Enable groups │ │
│ │ - Upload tests (parallel) │ │
│ │ - Upload script │ │
│ └──────────────────────────────┘ │
│ - Normalize line endings (→ Win) │
└────────────────────────────────────┘
↓
Success! Changes uploaded to Polygon
(Don't forget to commit!)
Parallel Test Operations:
Both pull and push operations fetch/upload manual tests in parallel for significant performance improvements:
Pull (Parallel Fetch):
// Fetch test metadata without inputs (fast)
const tests = await sdk.getTests(problemId, testsetName, true);
const manualTests = tests.filter(t => t.manual);
// Fetch all inputs in parallel
const promises = manualTests.map(test =>
sdk.getTestInput(problemId, testsetName, test.index)
);
const results = await Promise.all(promises);
Push (Parallel Upload):
// Create upload promises for all manual tests
const promises = manualTests.map(test =>
sdk.saveTest(problemId, testsetName, index, input, options)
);
// Upload all in parallel
await Promise.all(promises);
Performance Impact:
template/
├── Config.json # Configuration template
├── GUIDE.md # User guide
├── solutions/
│ └── acc.cpp # Main solution template
├── generators/
│ └── gen.cpp # Generator template
├── validator/
│ ├── val.cpp # Validator template
│ └── validator_tests.json # Validator tests template
├── checker/
│ ├── chk.cpp # Custom checker template
│ └── checker_tests.json # Checker tests template
└── statements/
└── english/
├── legend.tex
└── ...
my-problem/
├── Config.json
├── testlib.h # Downloaded via polyman download-testlib
├── solutions/
│ ├── Solution.cpp
│ └── WA.cpp
├── generators/
│ └── Generator.cpp
├── validator/
│ ├── Validator.cpp
│ └── validator_tests.json
├── checker/
│ ├── chk.cpp
│ └── checker_tests.json
├── tests/ # Generated by 'polyman generate'
│ ├── test1.txt
│ └── ...
└── solutions-outputs/ # Generated by 'polyman run'
├── main/
│ ├── output_test1.txt
│ └── ...
└── wa-solution/
└── ...
git clone https://github.com/HamzaHassanain/polyman.git
cd polyman
npm install
npm run build
npm link
Entry Points:
src/cli.ts - CLI commandssrc/actions.ts - Action functionsCore Logic:
src/helpers/ - Domain modulessrc/executor.ts - Process executionsrc/formatter.ts - Output formattingTypes:
src/types.d.ts - TypeScript type definitionsAssets:
assets/checkers/ - Standard checkerstemplate/ - Problem templateNew Command:
src/actions.tssrc/cli.tsNew Helper Module:
src/helpers/src/actions.tsNew Solution Type:
src/types.d.tsgetExpectedCheckerVerdictsrc/helpers/solution.ts# Lint code
npm run lint
# Build
npm run build
# Manual testing
polyman new test-problem
cd test-problem
# Test commands...
See eslint.config.js for linting rules.
Key Rules:
_)any (warning)For detailed API documentation of all functions, classes, and types, see the auto-generated TypeDoc documentation.
From src/actions.ts:
createTemplatedownloadTestliblistAvailableCheckersgenerateTestsvalidateTestssolveTeststestWhatfullVerificationFrom src/helpers/utils.ts:
compileCPPcompileJavareadConfigFileensureDirectoryExistsremoveDirectorylogErrorlogErrorAndExitFrom src/helpers/generator.ts:
ensureGeneratorsExistrunMatchingGeneratorsFrom src/helpers/validator.ts:
ensureValidatorExistsvalidateSingleTestvalidateAllTeststestValidatorItselfFrom src/helpers/checker.ts:
ensureCheckerExistscompileCheckerrunCheckertestCheckerItselfgetExpectedCheckerVerdictFrom src/helpers/solution.ts:
validateSolutionsExistensureMainSolutionExistsensureSolutionExistsgetMainSolutionrunSingleSolutionOnTestsrunMatchingSolutionsOnTeststestSolutionAgainstMainCorrectstartTheComparisonProcessFrom src/formatter.ts:
fmt (Formatter instance)From src/executor.ts:
executorFrom src/polygon.ts:
PolygonSDK (Polygon API SDK class)PolygonConfigPolygonProblemPolygonSolutionPolygonTestPolygonTestGroupPolygonFilePolygonStatementPolygonPackagePackageState ('NOT_STARTED' | 'WAITING' | 'RUNNING' | 'READY' | 'FAILED')Remote Operations (from src/actions.ts):
registerApiKeyAndSecretAction(apiKey, apiSecret) - Register Polygon credentialsremoteListProblemsAction(options?) - List accessible problemsremotePullProblemAction(problemId, targetPath, options?) - Download problemremotePushProblemAction(problemPath, options?) - Upload changes to PolygonremoteViewProblemAction(problemId) - Display problem detailsremoteCommitProblemAction(problemPath, message) - Commit changesremotePackageProblemAction(problemPath, options?) - Build packageRemote Helpers (from src/helpers/remote/):
pulling.ts:
downloadSolutions(sdk, problemId, targetPath)downloadChecker(sdk, problemId, targetPath)downloadValidator(sdk, problemId, targetPath)downloadGenerators(sdk, problemId, targetPath)downloadStatements(sdk, problemId, targetPath)fetchProblemMetadata(sdk, problemId)downloadTestsetAndBuildGenerationScripts(sdk, problemId, targetPath)pushing.ts:
uploadSolutions(sdk, problemId, solutions)uploadChecker(sdk, problemId, checker, problemPath)uploadValidator(sdk, problemId, validator, problemPath)uploadGenerators(sdk, problemId, generators, problemPath)uploadStatements(sdk, problemId, statements, problemPath)uploadMetadata(sdk, problemId, config)uploadTestsets(sdk, problemId, testsets, problemPath)updateProblemInfo(sdk, problemId, config)utils.ts (remote utilities):
normalizeLineEndingsFromWinToUnix(text) - Convert CRLF to LFnormalizeLineEndingsFromUnixToWin(text) - Convert LF to CRLFreadCredentialsFromHomeDirectory() - Load API credentialssaveCredentialsToHomeDirectory(apiKey, apiSecret) - Store credentialsgetProblemIdFromPath(problemPath) - Extract problem ID from Config.jsonviewer.ts:
displayProblemInfo(problem, sdk?) - Format and display problem detailsFrom src/types.d.ts:
ConfigFileLocalSolutionLocalGeneratorLocalCheckerLocalValidatorLocalTestsetLocalTestGroupGeneratorScriptGeneratorScriptCommandSolutionTagSolutionSourceTypeTestlibSourceTypeCppSourceTypeJavaSourceTypePythonSourceTypeStatementCheckerVerdictValidatorVerdictVerdictTrackerPullOptions (remote pull configuration)PushOptions (remote push configuration)Important considerations for development, deployment, and platform-specific behavior.
ulimitLinux:
macOS:
Windows:
spawn with shell: true; sanitize user input if accepting external configsREADME.mdGUIDE.mdMIT License - See LICENCE file for details.
This technical documentation is auto-generated alongside TypeDoc API documentation. For the latest version, visit the online documentation.