@jutge.org/toolkit 4.0.5 → 4.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/assets/prompts/ask/ask.md +11 -0
- package/dist/index.js +431 -435
- package/docs/getting-started-guide.md +515 -0
- package/docs/jutge-ai.md +82 -0
- package/docs/windows.md +114 -0
- package/package.json +3 -1
- package/toolkit/about.ts +40 -0
- package/toolkit/ai.ts +56 -0
- package/toolkit/ask.ts +45 -0
- package/toolkit/clean.ts +25 -0
- package/toolkit/clone.ts +12 -0
- package/toolkit/compilers.ts +29 -0
- package/toolkit/config.ts +113 -0
- package/toolkit/create.ts +36 -0
- package/toolkit/doctor.ts +22 -0
- package/toolkit/generate.ts +213 -0
- package/toolkit/index.ts +63 -0
- package/toolkit/make.ts +92 -0
- package/toolkit/quiz.ts +44 -0
- package/toolkit/upgrade.ts +9 -0
- package/toolkit/upload.ts +20 -0
- package/toolkit/verify.ts +15 -0
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jutge.org/toolkit",
|
|
3
3
|
"description": "Toolkit to prepare problems for Jutge.org",
|
|
4
|
-
"version": "4.0
|
|
4
|
+
"version": "4.2.0",
|
|
5
5
|
"homepage": "https://jutge.org",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Jutge.org",
|
|
@@ -40,6 +40,8 @@
|
|
|
40
40
|
},
|
|
41
41
|
"files": [
|
|
42
42
|
"dist",
|
|
43
|
+
"toolkit",
|
|
44
|
+
"docs",
|
|
43
45
|
"assets"
|
|
44
46
|
],
|
|
45
47
|
"dependencies": {
|
package/toolkit/about.ts
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { Command } from '@commander-js/extra-typings'
|
|
2
|
+
import chalk from 'chalk'
|
|
3
|
+
import tui from '../lib/tui.ts'
|
|
4
|
+
import { nothing } from '../lib/utils.ts'
|
|
5
|
+
import { packageJson } from '../lib/versions.ts'
|
|
6
|
+
|
|
7
|
+
export const aboutCmd = new Command('about')
|
|
8
|
+
.description('Get information about jutge-toolkit')
|
|
9
|
+
|
|
10
|
+
.action(async () => {
|
|
11
|
+
await nothing()
|
|
12
|
+
tui.print(chalk.bold(`jutge-toolkit ${packageJson.version}`))
|
|
13
|
+
tui.print(packageJson.description)
|
|
14
|
+
tui.print('')
|
|
15
|
+
tui.url(packageJson.homepage!)
|
|
16
|
+
tui.print('')
|
|
17
|
+
tui.print('Author:')
|
|
18
|
+
showPerson(packageJson.author!)
|
|
19
|
+
tui.print('')
|
|
20
|
+
tui.print('Contributors:')
|
|
21
|
+
for (const contributor of packageJson.contributors!) {
|
|
22
|
+
showPerson(contributor)
|
|
23
|
+
}
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
function showPerson(person: string | { name: string; email?: string; url?: string }) {
|
|
27
|
+
let line = ' - '
|
|
28
|
+
if (typeof person === 'string') {
|
|
29
|
+
line += person
|
|
30
|
+
} else {
|
|
31
|
+
line += person.name
|
|
32
|
+
if (person.email) {
|
|
33
|
+
line += ` <${tui.link('mailto://' + person.email, person.email)}>`
|
|
34
|
+
}
|
|
35
|
+
if (person.url) {
|
|
36
|
+
line += ` (${tui.link(person.url)})`
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
tui.print(line)
|
|
40
|
+
}
|
package/toolkit/ai.ts
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { Command } from '@commander-js/extra-typings'
|
|
2
|
+
import sharp from 'sharp'
|
|
3
|
+
import z from 'zod'
|
|
4
|
+
import { complete, generateImage, listModels } from '../lib/ai.ts'
|
|
5
|
+
import { settings } from '../lib/settings.ts'
|
|
6
|
+
import tui from '../lib/tui.ts'
|
|
7
|
+
import { convertStringToItsType } from '../lib/utils.ts'
|
|
8
|
+
|
|
9
|
+
export const aiCmd = new Command('ai')
|
|
10
|
+
.description('Query AI models')
|
|
11
|
+
|
|
12
|
+
.action(() => {
|
|
13
|
+
aiCmd.help()
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
aiCmd
|
|
17
|
+
.command('models')
|
|
18
|
+
.description('Show available AI models')
|
|
19
|
+
|
|
20
|
+
.action(async () => {
|
|
21
|
+
const models = await listModels()
|
|
22
|
+
tui.yaml(models)
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
aiCmd
|
|
26
|
+
.command('complete')
|
|
27
|
+
.description('Complete a prompt using an AI model')
|
|
28
|
+
|
|
29
|
+
.argument('<prompt>', 'the user prompt to complete')
|
|
30
|
+
.option('-s, --system-prompt <system>', 'the system prompt to use', 'You are a helpful assistant.')
|
|
31
|
+
.option('-m, --model <model>', 'the AI model to use', settings.defaultModel)
|
|
32
|
+
|
|
33
|
+
.action(async (prompt, { model, systemPrompt }) => {
|
|
34
|
+
prompt = prompt.trim()
|
|
35
|
+
systemPrompt = systemPrompt.trim()
|
|
36
|
+
const answer = await complete(model, systemPrompt, prompt)
|
|
37
|
+
tui.print(answer)
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
// TODO: generate with different aspect ratios
|
|
41
|
+
aiCmd
|
|
42
|
+
.command('image')
|
|
43
|
+
.description('Generate a square image using an AI model')
|
|
44
|
+
|
|
45
|
+
.argument('<prompt>', 'description of the image to generate')
|
|
46
|
+
.option('-m, --model <model>', 'the graphic AI model to use', 'openai/dall-e-3')
|
|
47
|
+
.option('-s, --size <size>', 'the size of the image (in pixels)', '1024')
|
|
48
|
+
.option('-o, --output <path>', 'the output image path', 'image.png')
|
|
49
|
+
|
|
50
|
+
.action(async (prompt, { model, size, output }) => {
|
|
51
|
+
const sizeInt = z.int().min(16).max(2048).parse(convertStringToItsType(size))
|
|
52
|
+
const image = await generateImage(model, prompt)
|
|
53
|
+
await sharp(image).resize(sizeInt, sizeInt).toFile(output)
|
|
54
|
+
tui.success(`Generated image saved to ${output}`)
|
|
55
|
+
await tui.image(output, 20, 10)
|
|
56
|
+
})
|
package/toolkit/ask.ts
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { Command } from '@commander-js/extra-typings'
|
|
2
|
+
import { glob } from 'fs/promises'
|
|
3
|
+
import { complete } from '../lib/ai'
|
|
4
|
+
import { settings } from '../lib/settings'
|
|
5
|
+
import tui from '../lib/tui'
|
|
6
|
+
import { projectDir, readTextInDir } from '../lib/utils'
|
|
7
|
+
import { join } from 'path'
|
|
8
|
+
import { all } from 'radash'
|
|
9
|
+
|
|
10
|
+
export const askCmd = new Command('ask')
|
|
11
|
+
.description('Ask questions about jutge-toolkit to JutgeAI')
|
|
12
|
+
|
|
13
|
+
.argument('[question]', 'your question about the toolkit')
|
|
14
|
+
.option('-m, --model <model>', 'AI model to use', settings.defaultModel)
|
|
15
|
+
|
|
16
|
+
.action(async (question, { model }) => {
|
|
17
|
+
if (!question || question.trim().length === 0) {
|
|
18
|
+
askCmd.help()
|
|
19
|
+
return
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const systemPrompt = await readTextInDir(join(projectDir(), 'assets', 'prompts', 'ask'), 'ask.md')
|
|
23
|
+
|
|
24
|
+
const docs = await loadDocumentation() // Load your markdown files
|
|
25
|
+
const fullPrompt = `${docs}\n\nUser question: ${question}`
|
|
26
|
+
|
|
27
|
+
const answer = await complete(model, systemPrompt, fullPrompt)
|
|
28
|
+
await tui.markdown(answer)
|
|
29
|
+
tui.warning(`This answer was generated by JutgeAI using model (${model}).`)
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
export async function loadDocumentation(): Promise<string> {
|
|
33
|
+
const documentsDir = join(projectDir(), 'docs')
|
|
34
|
+
const toolkitDir = join(projectDir(), 'toolkit')
|
|
35
|
+
|
|
36
|
+
const documentPaths = await Array.fromAsync(glob('*.md', { cwd: documentsDir }))
|
|
37
|
+
const sourcePaths = await Array.fromAsync(glob('*.ts', { cwd: toolkitDir }))
|
|
38
|
+
|
|
39
|
+
const documents = await all(documentPaths.map((file) => readTextInDir(documentsDir, file)))
|
|
40
|
+
const sources = await all(sourcePaths.map((file) => readTextInDir(toolkitDir, file)))
|
|
41
|
+
|
|
42
|
+
const texts = [...documents, ...sources]
|
|
43
|
+
|
|
44
|
+
return texts.join('\n\n')
|
|
45
|
+
}
|
package/toolkit/clean.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { Command, Option } from '@commander-js/extra-typings'
|
|
2
|
+
import { cleanDirectory } from '../lib/cleaner'
|
|
3
|
+
import tui from '../lib/tui'
|
|
4
|
+
import { findRealDirectories } from '../lib/helpers'
|
|
5
|
+
|
|
6
|
+
export const cleanCmd = new Command('clean')
|
|
7
|
+
.description('Clean disposable files')
|
|
8
|
+
|
|
9
|
+
.option('-d, --directories <directories...>', 'problem directories', ['.'])
|
|
10
|
+
.option('-a, --all', 'clean all disposable files (including generated statement and correct files', false)
|
|
11
|
+
.addOption(new Option('-f, --force', 'force removal').conflicts('dryRun'))
|
|
12
|
+
.addOption(new Option('-n, --dry-run', 'show but do not remove files').conflicts('force'))
|
|
13
|
+
|
|
14
|
+
.action(async ({ directories, all, force, dryRun }) => {
|
|
15
|
+
const isForce = force || false // default to dry-run if neither option is specified
|
|
16
|
+
|
|
17
|
+
await tui.section(`Cleaning generated files`, async () => {
|
|
18
|
+
const realDirectories = await findRealDirectories(directories)
|
|
19
|
+
for (const directory of realDirectories) {
|
|
20
|
+
await tui.section(`Cleaning directory ${tui.hyperlink(directory)}`, async () => {
|
|
21
|
+
await cleanDirectory(isForce, all, directory)
|
|
22
|
+
})
|
|
23
|
+
}
|
|
24
|
+
})
|
|
25
|
+
})
|
package/toolkit/clone.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Command } from '@commander-js/extra-typings'
|
|
2
|
+
import { createProblemWithTemplate } from '../lib/create-with-template'
|
|
3
|
+
|
|
4
|
+
export const cloneCmd = new Command('clone')
|
|
5
|
+
.description('Clone a template into a new problem')
|
|
6
|
+
|
|
7
|
+
.argument('[template]', 'template to use (empty to interactive selection)')
|
|
8
|
+
.option('-d, --directory <path>', 'output directory', 'new-problem.pbm')
|
|
9
|
+
|
|
10
|
+
.action(async (template, { directory }) => {
|
|
11
|
+
await createProblemWithTemplate(directory, template)
|
|
12
|
+
})
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { getAvailableCompilers, getCompilersInfo, getDefinedCompilerIds } from '../lib/compilers'
|
|
2
|
+
import { Command } from '@commander-js/extra-typings'
|
|
3
|
+
|
|
4
|
+
export const compilersCmd = new Command('compilers')
|
|
5
|
+
.description('Query compiler information')
|
|
6
|
+
// default action is to list all compilers because of older compatibility
|
|
7
|
+
|
|
8
|
+
.action(async () => {
|
|
9
|
+
const info = await getCompilersInfo()
|
|
10
|
+
console.dir(info)
|
|
11
|
+
})
|
|
12
|
+
|
|
13
|
+
compilersCmd
|
|
14
|
+
.command('list-defined')
|
|
15
|
+
.description('List all defined compiler names')
|
|
16
|
+
|
|
17
|
+
.action(async () => {
|
|
18
|
+
const items = await getDefinedCompilerIds()
|
|
19
|
+
console.dir(items)
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
compilersCmd
|
|
23
|
+
.command('list-available')
|
|
24
|
+
.description('List all available compiler names')
|
|
25
|
+
|
|
26
|
+
.action(async () => {
|
|
27
|
+
const items = await getAvailableCompilers()
|
|
28
|
+
console.dir(items)
|
|
29
|
+
})
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { Command } from '@commander-js/extra-typings'
|
|
2
|
+
import { confirm } from '@inquirer/prompts'
|
|
3
|
+
import { editor } from '@inquirer/prompts'
|
|
4
|
+
import YAML from 'yaml'
|
|
5
|
+
import { ZodError } from 'zod'
|
|
6
|
+
import { fromError } from 'zod-validation-error'
|
|
7
|
+
import { configPath, loadSettings, saveSettings, settings } from '../lib/settings'
|
|
8
|
+
import { Settings } from '../lib/types.ts'
|
|
9
|
+
import tui from '../lib/tui.ts'
|
|
10
|
+
import { convertStringToItsType } from '../lib/utils.ts'
|
|
11
|
+
|
|
12
|
+
export const configCmd = new Command('config')
|
|
13
|
+
.summary('Manage configuration')
|
|
14
|
+
.description(
|
|
15
|
+
`Manage configuration
|
|
16
|
+
|
|
17
|
+
The actual configuration file is stored at ${configPath()}`,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
.action(() => {
|
|
21
|
+
configCmd.help()
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
configCmd
|
|
25
|
+
.command('show')
|
|
26
|
+
.alias('list')
|
|
27
|
+
.description('Show configuration options')
|
|
28
|
+
|
|
29
|
+
.action(async () => {
|
|
30
|
+
const settings = await loadSettings()
|
|
31
|
+
tui.yaml(settings)
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
configCmd
|
|
35
|
+
.command('get <key>')
|
|
36
|
+
.description('Get the value of a configuration option')
|
|
37
|
+
|
|
38
|
+
.action((key: string) => {
|
|
39
|
+
if (!(key in settings)) {
|
|
40
|
+
throw new Error(`Configuration key ${key} does not exist`)
|
|
41
|
+
}
|
|
42
|
+
console.log((settings as any)[key])
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
configCmd
|
|
46
|
+
.command('set <key> <value>')
|
|
47
|
+
.description('Set the value of a configuration option')
|
|
48
|
+
|
|
49
|
+
.action(async (key: string, value: string) => {
|
|
50
|
+
if (!(key in settings)) {
|
|
51
|
+
throw new Error(`Configuration key ${key} does not exist`)
|
|
52
|
+
}
|
|
53
|
+
const convertedValue = convertStringToItsType(value)
|
|
54
|
+
const newSettings = Settings.parse({ ...settings, [key]: convertedValue })
|
|
55
|
+
await saveSettings(newSettings)
|
|
56
|
+
tui.success(`Configuration key ${key} updated successfully`)
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
configCmd
|
|
60
|
+
.command('edit')
|
|
61
|
+
.description('Open an editor (uses $EDITOR or $VISUAL) to modify the configuration options')
|
|
62
|
+
|
|
63
|
+
.action(async () => {
|
|
64
|
+
let data = YAML.stringify(settings, null, 4)
|
|
65
|
+
while (true) {
|
|
66
|
+
const newData = await editor({
|
|
67
|
+
message: 'Edit configuration',
|
|
68
|
+
default: data,
|
|
69
|
+
postfix: '.yml',
|
|
70
|
+
waitForUserInput: false,
|
|
71
|
+
})
|
|
72
|
+
try {
|
|
73
|
+
const newSettings = Settings.parse(YAML.parse(newData))
|
|
74
|
+
await saveSettings(newSettings)
|
|
75
|
+
tui.success('Configuration options updated successfully')
|
|
76
|
+
return
|
|
77
|
+
} catch (error) {
|
|
78
|
+
if (error instanceof ZodError) {
|
|
79
|
+
console.error(fromError(error).toString())
|
|
80
|
+
} else {
|
|
81
|
+
console.error(error)
|
|
82
|
+
}
|
|
83
|
+
const again = await confirm({ message: 'Edit again?', default: true })
|
|
84
|
+
if (!again) {
|
|
85
|
+
tui.warning('No changes made to the configuration options')
|
|
86
|
+
return
|
|
87
|
+
}
|
|
88
|
+
data = newData
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
configCmd
|
|
94
|
+
.command('reset')
|
|
95
|
+
.description('Reset configuration to default values')
|
|
96
|
+
|
|
97
|
+
.option('-f, --force', 'force reset without confirmation', false)
|
|
98
|
+
|
|
99
|
+
.action(async ({ force }) => {
|
|
100
|
+
if (!force) {
|
|
101
|
+
const confirmReset = await confirm({
|
|
102
|
+
message: 'Are you sure you want to reset the configuration to default values?',
|
|
103
|
+
default: false,
|
|
104
|
+
})
|
|
105
|
+
if (!confirmReset) {
|
|
106
|
+
tui.warning('Reset cancelled')
|
|
107
|
+
return
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
const defaultSettings = Settings.parse({})
|
|
111
|
+
await saveSettings(defaultSettings)
|
|
112
|
+
tui.success('Configuration reset to default values successfully')
|
|
113
|
+
})
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { Command } from '@commander-js/extra-typings'
|
|
2
|
+
import { createProblemWithJutgeAI } from '../lib/create-with-jutgeai'
|
|
3
|
+
import { createProblemWithTemplate } from '../lib/create-with-template'
|
|
4
|
+
import { settings } from '../lib/settings'
|
|
5
|
+
|
|
6
|
+
export const createCmd = new Command('create')
|
|
7
|
+
.description('Create a new problem')
|
|
8
|
+
|
|
9
|
+
.action(() => {
|
|
10
|
+
createCmd.help()
|
|
11
|
+
})
|
|
12
|
+
|
|
13
|
+
createCmd
|
|
14
|
+
.command('with-template')
|
|
15
|
+
.description('Create a problem with a template')
|
|
16
|
+
|
|
17
|
+
.argument('[template]', 'template to use (empty to interactive selection)')
|
|
18
|
+
.option('-d, --directory <path>', 'output directory', 'new-problem.pbm')
|
|
19
|
+
|
|
20
|
+
.action(async (template, { directory }) => {
|
|
21
|
+
await createProblemWithTemplate(directory, template)
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
createCmd
|
|
25
|
+
.command('with-ai')
|
|
26
|
+
.description('Create a problem with JutgeAI')
|
|
27
|
+
|
|
28
|
+
.option('-d, --directory <path>', 'output directory', 'new-problem.pbm')
|
|
29
|
+
.option('-i, --input <path>', 'input specification file')
|
|
30
|
+
.option('-o, --output <path>', 'output specification file')
|
|
31
|
+
.option('-n, --do-not-ask', 'do not ask interactively if --input given', false)
|
|
32
|
+
.option('-m, --model <model>', 'AI model to use', settings.defaultModel)
|
|
33
|
+
|
|
34
|
+
.action(async ({ input, output, directory, model, doNotAsk }) => {
|
|
35
|
+
await createProblemWithJutgeAI(model, directory, input, output, doNotAsk)
|
|
36
|
+
})
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import * as doc from '../lib/doctor'
|
|
2
|
+
import { Command } from '@commander-js/extra-typings'
|
|
3
|
+
import tui from '../lib/tui'
|
|
4
|
+
|
|
5
|
+
export const doctorCmd = new Command('doctor')
|
|
6
|
+
.description('Diagnose status of the environment')
|
|
7
|
+
|
|
8
|
+
.action(async () => {
|
|
9
|
+
await tui.section('Perform checks', async () => {
|
|
10
|
+
await tui.section('Checking Python3 installation', doc.checkPython3)
|
|
11
|
+
await tui.section('Checking C/C++ installation', doc.checkGCC)
|
|
12
|
+
await tui.section('Checking Haskell installation', doc.checkHaskell)
|
|
13
|
+
await tui.section('Checking Clojure installation', doc.checkClojure)
|
|
14
|
+
await tui.section('Checking Java installation', doc.checkJava)
|
|
15
|
+
await tui.section('Checking Rust installation', doc.checkRust)
|
|
16
|
+
await tui.section('Checking XeLaTeX installation', doc.checkXeLaTeX)
|
|
17
|
+
await tui.section('Checking Pandoc installation', doc.checkPandoc)
|
|
18
|
+
await tui.section('Checking ImageMagick installation', doc.checkImageMagick)
|
|
19
|
+
await tui.section('Checking AI models', doc.checkAIEnvVars)
|
|
20
|
+
await tui.section('Checking terminal', doc.checkTerminal)
|
|
21
|
+
})
|
|
22
|
+
})
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
import { Argument, Command } from '@commander-js/extra-typings'
|
|
2
|
+
import { join } from 'path'
|
|
3
|
+
import sharp from 'sharp'
|
|
4
|
+
import { complete, generateImage } from '../lib/ai'
|
|
5
|
+
import { languageKeys, languageNames, proglangKeys } from '../lib/data'
|
|
6
|
+
import {
|
|
7
|
+
addAlternativeSolution,
|
|
8
|
+
addMainFile,
|
|
9
|
+
addStatementTranslation,
|
|
10
|
+
generateTestCasesGenerator,
|
|
11
|
+
} from '../lib/generate'
|
|
12
|
+
import { newProblem } from '../lib/problem'
|
|
13
|
+
import { settings } from '../lib/settings'
|
|
14
|
+
import tui from '../lib/tui'
|
|
15
|
+
import { writeText } from '../lib/utils'
|
|
16
|
+
import { createProblemWithJutgeAI } from '../lib/create-with-jutgeai'
|
|
17
|
+
|
|
18
|
+
export const generateCmd = new Command('generate')
|
|
19
|
+
.description('Generate problem elements using JutgeAI')
|
|
20
|
+
|
|
21
|
+
.action(() => {
|
|
22
|
+
generateCmd.help()
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
generateCmd
|
|
26
|
+
.command('problem')
|
|
27
|
+
.description('Generate a problem with JutgeAI')
|
|
28
|
+
|
|
29
|
+
.option('-d, --directory <path>', 'output directory', 'new-problem.pbm')
|
|
30
|
+
.option('-i, --input <path>', 'input specification file')
|
|
31
|
+
.option('-o, --output <path>', 'output specification file')
|
|
32
|
+
.option('-n, --do-not-ask', 'do not ask interactively if --input given', false)
|
|
33
|
+
.option('-m, --model <model>', 'AI model to use', settings.defaultModel)
|
|
34
|
+
|
|
35
|
+
.action(async ({ input, output, directory, model, doNotAsk }) => {
|
|
36
|
+
await createProblemWithJutgeAI(model, directory, input, output, doNotAsk)
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
generateCmd
|
|
40
|
+
.command('translations')
|
|
41
|
+
.summary('Generate statement translations using JutgeAI')
|
|
42
|
+
.description(
|
|
43
|
+
`Generate statement translations using JutgeAI
|
|
44
|
+
|
|
45
|
+
Use this command to add translations of the problem statement into different languages.
|
|
46
|
+
The original statement will be used as the source text for translation.
|
|
47
|
+
|
|
48
|
+
Provide one or more target language from the following list:
|
|
49
|
+
${Object.entries(languageNames)
|
|
50
|
+
.map(([key, name]) => ` - ${key}: ${name}`)
|
|
51
|
+
.join('\n')}
|
|
52
|
+
|
|
53
|
+
The added translations will be saved in the problem directory overwrite possible existing files.`,
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
.addArgument(new Argument('<languages...>', 'languages to add').choices(languageKeys))
|
|
57
|
+
.option('-d, --directory <path>', 'problem directory', '.')
|
|
58
|
+
.option('-m, --model <model>', 'AI model to use', settings.defaultModel)
|
|
59
|
+
|
|
60
|
+
.action(async (languages, { directory, model }) => {
|
|
61
|
+
const problem = await newProblem(directory)
|
|
62
|
+
await tui.section('Generating statement translations', async () => {
|
|
63
|
+
for (const language of languages) {
|
|
64
|
+
await addStatementTranslation(model, problem, language)
|
|
65
|
+
}
|
|
66
|
+
})
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
generateCmd
|
|
70
|
+
.command('solutions')
|
|
71
|
+
.summary('Generate alternative solutions using JutgeAI')
|
|
72
|
+
.description(
|
|
73
|
+
`Generate alternative solutions using JutgeAI
|
|
74
|
+
|
|
75
|
+
Use this command to add alternative solutions for the problem in different programming languages.
|
|
76
|
+
The golden solution will be used as a reference for generating the alternatives.
|
|
77
|
+
|
|
78
|
+
Provide one or more target programming languages from the following list:
|
|
79
|
+
${Object.entries(languageNames)
|
|
80
|
+
.map(([key, name]) => ` - ${key}: ${name}`)
|
|
81
|
+
.join('\n')}
|
|
82
|
+
|
|
83
|
+
The added solutions will be saved in the problem directory overwrite possible existing files.`,
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
.addArgument(new Argument('<proglangs...>', 'proglangs to add').choices(proglangKeys))
|
|
87
|
+
.option('-d, --directory <path>', 'problem directory', '.')
|
|
88
|
+
.option('-m, --model <model>', 'AI model to use', settings.defaultModel)
|
|
89
|
+
|
|
90
|
+
.action(async (proglangs, { directory, model }) => {
|
|
91
|
+
const problem = await newProblem(directory)
|
|
92
|
+
for (const proglang of proglangs) {
|
|
93
|
+
await addAlternativeSolution(model, problem, proglang)
|
|
94
|
+
}
|
|
95
|
+
})
|
|
96
|
+
|
|
97
|
+
generateCmd
|
|
98
|
+
.command('mains')
|
|
99
|
+
.summary('Generate main files using JutgeAI')
|
|
100
|
+
.description(
|
|
101
|
+
`Generate main files using JutgeAI
|
|
102
|
+
|
|
103
|
+
Main files are the entry point for problems that ask users to implement specific functions or classes.
|
|
104
|
+
|
|
105
|
+
Use this command to add main files for the problem in different programming languages.
|
|
106
|
+
The main file for the golden solution will be used as a reference for generating the main files.
|
|
107
|
+
|
|
108
|
+
Provide one or more target programming languages from the following list:
|
|
109
|
+
${Object.entries(languageNames)
|
|
110
|
+
.map(([key, name]) => ` - ${key}: ${name}`)
|
|
111
|
+
.join('\n')}
|
|
112
|
+
|
|
113
|
+
The added main files will be saved in the problem directory overwrite possible existing files.`,
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
.addArgument(new Argument('<proglangs...>', 'proglangs to add').choices(proglangKeys))
|
|
117
|
+
.option('-d, --directory <path>', 'problem directory', '.')
|
|
118
|
+
.option('-m, --model <model>', 'AI model to use', settings.defaultModel)
|
|
119
|
+
|
|
120
|
+
.action(async (proglangs, { directory, model }) => {
|
|
121
|
+
const problem = await newProblem(directory)
|
|
122
|
+
for (const proglang of proglangs) {
|
|
123
|
+
await addMainFile(model, problem, proglang)
|
|
124
|
+
}
|
|
125
|
+
})
|
|
126
|
+
|
|
127
|
+
generateCmd
|
|
128
|
+
.command('generators')
|
|
129
|
+
.summary('Generate test cases generators using JutgeAI')
|
|
130
|
+
|
|
131
|
+
.option('--random', 'generate a generator for random test cases')
|
|
132
|
+
.option('--hard', 'generate a generator for hard test cases')
|
|
133
|
+
.option('--efficiency', 'generate a generator for efficiency test cases')
|
|
134
|
+
.option('--all', 'generate all three test case generators')
|
|
135
|
+
|
|
136
|
+
.option('-d, --directory <path>', 'problem directory', '.')
|
|
137
|
+
.option('-o, --output <path>', 'output file', 'generator-{{type}}.py')
|
|
138
|
+
.option('-m, --model <model>', 'AI model to use', settings.defaultModel)
|
|
139
|
+
|
|
140
|
+
.action(async ({ efficiency, hard, random, all, directory, model, output }) => {
|
|
141
|
+
const problem = await newProblem(directory)
|
|
142
|
+
await tui.section('Generating test cases generators', async () => {
|
|
143
|
+
if (all || random) await generateTestCasesGenerator(model, problem, output, 'random')
|
|
144
|
+
if (all || hard) await generateTestCasesGenerator(model, problem, output, 'hard')
|
|
145
|
+
if (all || efficiency) await generateTestCasesGenerator(model, problem, output, 'efficiency')
|
|
146
|
+
})
|
|
147
|
+
})
|
|
148
|
+
|
|
149
|
+
generateCmd
|
|
150
|
+
.command('award.png')
|
|
151
|
+
.summary('Generate award.png using JutgeAI')
|
|
152
|
+
.description(
|
|
153
|
+
`Generate award.png using AI
|
|
154
|
+
|
|
155
|
+
Use this command to add an award image for the problem.
|
|
156
|
+
Awards are shown to users when they solve the problem.
|
|
157
|
+
They help to motivate users and make the platform more engaging.
|
|
158
|
+
|
|
159
|
+
Provide an interesting prompt to customize the image content. For example:
|
|
160
|
+
- "A golden trophy with a blue ribbon on a wooden base."
|
|
161
|
+
- "A star made of sparkling diamonds on a black background."
|
|
162
|
+
- "A minimalist image with a white background using Van Gogh style."
|
|
163
|
+
|
|
164
|
+
The new image will be saved as award.png in the problem directory, overriding any existing file.`,
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
.option('-d, --directory <path>', 'problem directory', '.')
|
|
168
|
+
.option('-m, --model <model>', 'graphic AI model to use', 'openai/dall-e-3')
|
|
169
|
+
.argument('[prompt]', 'prompt to generate the image', 'A colorful image on a white background. ')
|
|
170
|
+
|
|
171
|
+
.action(async (prompt, { directory, model }) => {
|
|
172
|
+
const output = join(directory, 'award.png')
|
|
173
|
+
const problem = await newProblem(directory)
|
|
174
|
+
const image = await generateImage(model, prompt)
|
|
175
|
+
await sharp(image).resize(512, 512).toFile(output)
|
|
176
|
+
await tui.image(output, 20, 10)
|
|
177
|
+
tui.success(`Added ${output}`)
|
|
178
|
+
})
|
|
179
|
+
|
|
180
|
+
generateCmd
|
|
181
|
+
.command('award.html')
|
|
182
|
+
.summary('Generate award.html using JutgeAI')
|
|
183
|
+
.description(
|
|
184
|
+
`Generate award.html using JutgeAI
|
|
185
|
+
|
|
186
|
+
Use this command to add an award message for the problem.
|
|
187
|
+
Awards are shown to users when they solve the problem.
|
|
188
|
+
They help to motivate users and make the platform more engaging.
|
|
189
|
+
|
|
190
|
+
Provide an interesting prompt to customize the message content. For example:
|
|
191
|
+
- "A short encouraging message after having solved a challenge or a problem."
|
|
192
|
+
- "A congratulatory message for completing a difficult task."
|
|
193
|
+
- "A motivational quote to inspire further learning."
|
|
194
|
+
|
|
195
|
+
The new message will be saved as award.html in the problem directory, overriding any existing file.`,
|
|
196
|
+
)
|
|
197
|
+
|
|
198
|
+
.option('-d, --directory <path>', 'problem directory', '.')
|
|
199
|
+
.argument(
|
|
200
|
+
'[prompt]',
|
|
201
|
+
'prompt to generate the award message',
|
|
202
|
+
'Only provide a short encouraging message after having solved a challenge or a problem. Nothing else!',
|
|
203
|
+
)
|
|
204
|
+
.option('-m, --model <model>', 'AI model to use', settings.defaultModel)
|
|
205
|
+
|
|
206
|
+
.action(async (prompt, { directory, model }) => {
|
|
207
|
+
const output = join(directory, 'award.html')
|
|
208
|
+
const problem = await newProblem(directory)
|
|
209
|
+
const message = await complete(model, '', prompt)
|
|
210
|
+
tui.print(message)
|
|
211
|
+
await writeText(output, message)
|
|
212
|
+
tui.success(`Added ${output}`)
|
|
213
|
+
})
|
package/toolkit/index.ts
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
|
|
3
|
+
import { program } from '@commander-js/extra-typings'
|
|
4
|
+
import { ZodError } from 'zod'
|
|
5
|
+
import { fromError } from 'zod-validation-error'
|
|
6
|
+
import { settings } from '../lib/settings'
|
|
7
|
+
import { packageJson } from '../lib/versions'
|
|
8
|
+
import { aboutCmd } from './about'
|
|
9
|
+
import { aiCmd } from './ai'
|
|
10
|
+
import { verifyCmd } from './verify'
|
|
11
|
+
import { cleanCmd } from './clean'
|
|
12
|
+
import { compilersCmd } from './compilers'
|
|
13
|
+
import { configCmd } from './config'
|
|
14
|
+
import { cloneCmd } from './clone'
|
|
15
|
+
import { doctorCmd } from './doctor'
|
|
16
|
+
import { generateCmd } from './generate'
|
|
17
|
+
import { makeCmd } from './make'
|
|
18
|
+
import { quizCmd } from './quiz'
|
|
19
|
+
import { upgradeCmd } from './upgrade'
|
|
20
|
+
import { uploadCmd } from './upload'
|
|
21
|
+
import { askCmd } from './ask'
|
|
22
|
+
|
|
23
|
+
program.name(Object.keys(packageJson.bin as Record<string, string>)[0] as string)
|
|
24
|
+
program.alias(Object.keys(packageJson.bin as Record<string, string>)[1] as string)
|
|
25
|
+
program.version(packageJson.version)
|
|
26
|
+
program.description(packageJson.description!)
|
|
27
|
+
program.helpCommand('help [command]', 'Display help for command') // To get the message with uppercase :-)
|
|
28
|
+
|
|
29
|
+
program.addCommand(configCmd)
|
|
30
|
+
program.addCommand(cloneCmd)
|
|
31
|
+
program.addCommand(generateCmd)
|
|
32
|
+
program.addCommand(makeCmd)
|
|
33
|
+
program.addCommand(uploadCmd)
|
|
34
|
+
program.addCommand(cleanCmd)
|
|
35
|
+
program.addCommand(verifyCmd)
|
|
36
|
+
program.addCommand(doctorCmd)
|
|
37
|
+
if (settings.developer) {
|
|
38
|
+
program.addCommand(quizCmd)
|
|
39
|
+
program.addCommand(compilersCmd)
|
|
40
|
+
program.addCommand(aiCmd)
|
|
41
|
+
}
|
|
42
|
+
program.addCommand(upgradeCmd)
|
|
43
|
+
program.addCommand(aboutCmd)
|
|
44
|
+
program.addCommand(askCmd)
|
|
45
|
+
|
|
46
|
+
try {
|
|
47
|
+
await program.parseAsync()
|
|
48
|
+
} catch (error) {
|
|
49
|
+
console.log()
|
|
50
|
+
console.error('An error occurred:')
|
|
51
|
+
if (error instanceof Error) {
|
|
52
|
+
if (error.name === 'ExitPromptError') {
|
|
53
|
+
console.error('Operation cancelled by the user')
|
|
54
|
+
} else if (error instanceof ZodError) {
|
|
55
|
+
console.error(fromError(error).toString())
|
|
56
|
+
} else {
|
|
57
|
+
console.error(error.message)
|
|
58
|
+
if (settings.developer) console.error(error)
|
|
59
|
+
}
|
|
60
|
+
} else {
|
|
61
|
+
console.error(error)
|
|
62
|
+
}
|
|
63
|
+
}
|