@kradle/cli 0.2.1 → 0.2.3
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/README.md +43 -13
- package/dist/commands/ai-docs/api.d.ts +6 -0
- package/dist/commands/ai-docs/api.js +13 -0
- package/dist/commands/ai-docs/cli.d.ts +6 -0
- package/dist/commands/ai-docs/cli.js +13 -0
- package/dist/commands/challenge/run.d.ts +0 -1
- package/dist/commands/challenge/run.js +9 -6
- package/dist/commands/init.js +4 -2
- package/dist/lib/arguments.d.ts +6 -2
- package/dist/lib/arguments.js +17 -5
- package/oclif.manifest.json +52 -12
- package/package.json +4 -1
- package/static/ai_docs/LLM_API_REFERENCE.md +1468 -0
- package/static/ai_docs/LLM_CLI_REFERENCE.md +674 -0
- package/static/project_template/AGENTS.md +15 -0
- package/static/project_template/CLAUDE.md +1 -0
package/README.md
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
<!-- This file is for human consumption. If you are an LLM or any AI Agent, make sure to read static/ai_docs/LLM_CLI_REFERENCE.md for an exhaustive explanation of this package. -->
|
|
2
|
+
|
|
1
3
|
# Kradle CLI
|
|
2
4
|
|
|
3
5
|
Kradle's CLI for managing Minecraft challenges, experiments, agents, and more!
|
|
@@ -8,6 +10,7 @@ Kradle's CLI for managing Minecraft challenges, experiments, agents, and more!
|
|
|
8
10
|
* [Challenge](#challenge-commands)
|
|
9
11
|
* [Experiments](#experiment-commands)
|
|
10
12
|
* [Agents](#agent-commands)
|
|
13
|
+
* [AI Docs](#ai-docs-commands)
|
|
11
14
|
* [Publishing a New Version](#publishing-a-new-version)
|
|
12
15
|
* [Development](#development)
|
|
13
16
|
* [Architecture](#architecture)
|
|
@@ -125,18 +128,9 @@ Run a challenge in production or studio environment:
|
|
|
125
128
|
```bash
|
|
126
129
|
kradle challenge run <challenge-name>
|
|
127
130
|
kradle challenge run <challenge-name> --studio # Run in local studio environment
|
|
131
|
+
kradle challenge run <team-name>:<challenge-name> # Run a public challenge from another team
|
|
128
132
|
```
|
|
129
133
|
|
|
130
|
-
### Multi-Upload
|
|
131
|
-
|
|
132
|
-
Interactively select and upload multiple challenges:
|
|
133
|
-
|
|
134
|
-
```bash
|
|
135
|
-
kradle challenge multi-upload
|
|
136
|
-
```
|
|
137
|
-
|
|
138
|
-
Provides an interactive UI to select multiple challenges and uploads them in parallel.
|
|
139
|
-
|
|
140
134
|
## Experiment Commands
|
|
141
135
|
|
|
142
136
|
Experiments allow you to run batches of challenge runs with different agents and configurations, then analyze the results. This is useful for benchmarking agents, testing challenge difficulty, or gathering statistics across many runs.
|
|
@@ -172,9 +166,10 @@ This creates `experiments/<name>/config.ts` with a template that you can customi
|
|
|
172
166
|
Execute or resume an experiment:
|
|
173
167
|
|
|
174
168
|
```bash
|
|
175
|
-
kradle experiment run <name>
|
|
176
|
-
kradle experiment run <name> --new-version
|
|
177
|
-
kradle experiment run <name> --max-concurrent 10
|
|
169
|
+
kradle experiment run <name> # Resume current version or create first one
|
|
170
|
+
kradle experiment run <name> --new-version # Start a new version
|
|
171
|
+
kradle experiment run <name> --max-concurrent 10 # Control parallelism (default: 5)
|
|
172
|
+
kradle experiment run <name> --download-recordings # Auto-download recordings as runs complete
|
|
178
173
|
```
|
|
179
174
|
|
|
180
175
|
The run command:
|
|
@@ -184,6 +179,20 @@ The run command:
|
|
|
184
179
|
4. Saves progress periodically (allows resuming if interrupted)
|
|
185
180
|
5. Opens Metabase dashboard with results when complete
|
|
186
181
|
|
|
182
|
+
### Download Recordings
|
|
183
|
+
|
|
184
|
+
Download gameplay recordings from completed experiment runs:
|
|
185
|
+
|
|
186
|
+
```bash
|
|
187
|
+
kradle experiment recordings <name> # Interactive selection of run and participant
|
|
188
|
+
kradle experiment recordings <name> <run-id> # Download specific run
|
|
189
|
+
kradle experiment recordings <name> --all # Download all runs and participants
|
|
190
|
+
kradle experiment recordings <name> --version 2 # Download from specific version
|
|
191
|
+
kradle experiment recordings <name> <run-id> --all # Download all participants for a run
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
Recordings are saved to `experiments/<name>/versions/<version>/recordings/<run-id>/`.
|
|
195
|
+
|
|
187
196
|
### List Experiments
|
|
188
197
|
|
|
189
198
|
List all local experiments:
|
|
@@ -202,6 +211,26 @@ List all agents registered in the system:
|
|
|
202
211
|
kradle agent list
|
|
203
212
|
```
|
|
204
213
|
|
|
214
|
+
## AI Docs Commands
|
|
215
|
+
|
|
216
|
+
Output LLM-focused documentation to stdout. These commands are designed to provide AI agents with comprehensive reference material about the Kradle CLI and API.
|
|
217
|
+
|
|
218
|
+
### CLI Reference
|
|
219
|
+
|
|
220
|
+
Output the CLI reference documentation:
|
|
221
|
+
|
|
222
|
+
```bash
|
|
223
|
+
kradle ai-docs cli
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
### API Reference
|
|
227
|
+
|
|
228
|
+
Output the API reference documentation for the `@kradle/challenges` package:
|
|
229
|
+
|
|
230
|
+
```bash
|
|
231
|
+
kradle ai-docs api
|
|
232
|
+
```
|
|
233
|
+
|
|
205
234
|
## Publishing a New Version
|
|
206
235
|
|
|
207
236
|
The CLI uses GitHub Actions for automated releases. To publish a new version:
|
|
@@ -303,6 +332,7 @@ kradle-cli/
|
|
|
303
332
|
├── src/
|
|
304
333
|
│ ├── commands/ # CLI commands
|
|
305
334
|
│ │ ├── agent/ # Agent commands
|
|
335
|
+
│ │ ├── ai-docs/ # AI documentation commands
|
|
306
336
|
│ │ ├── challenge/ # Challenge management commands
|
|
307
337
|
│ │ └── experiment/ # Experiment commands
|
|
308
338
|
│ └── lib/ # Core libraries
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import { Command } from "@oclif/core";
|
|
3
|
+
import { getStaticResourcePath } from "../../lib/utils.js";
|
|
4
|
+
export default class Api extends Command {
|
|
5
|
+
static description = "Output the Kradle API reference documentation for LLMs";
|
|
6
|
+
static examples = ["<%= config.bin %> <%= command.id %>"];
|
|
7
|
+
async run() {
|
|
8
|
+
await this.parse(Api);
|
|
9
|
+
const docPath = getStaticResourcePath("ai_docs/LLM_API_REFERENCE.md");
|
|
10
|
+
const content = await fs.readFile(docPath, "utf-8");
|
|
11
|
+
this.log(content);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import { Command } from "@oclif/core";
|
|
3
|
+
import { getStaticResourcePath } from "../../lib/utils.js";
|
|
4
|
+
export default class Cli extends Command {
|
|
5
|
+
static description = "Output the Kradle CLI reference documentation for LLMs";
|
|
6
|
+
static examples = ["<%= config.bin %> <%= command.id %>"];
|
|
7
|
+
async run() {
|
|
8
|
+
await this.parse(Cli);
|
|
9
|
+
const docPath = getStaticResourcePath("ai_docs/LLM_CLI_REFERENCE.md");
|
|
10
|
+
const content = await fs.readFile(docPath, "utf-8");
|
|
11
|
+
this.log(content);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -11,7 +11,6 @@ export default class Run extends Command {
|
|
|
11
11
|
"web-url": import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
12
12
|
"studio-api-url": import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
13
13
|
"studio-url": import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
14
|
-
"challenges-path": import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
15
14
|
studio: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
16
15
|
open: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
17
16
|
};
|
|
@@ -2,7 +2,6 @@ import { Command, Flags } from "@oclif/core";
|
|
|
2
2
|
import pc from "picocolors";
|
|
3
3
|
import { ApiClient } from "../../lib/api-client.js";
|
|
4
4
|
import { getChallengeSlugArgument } from "../../lib/arguments.js";
|
|
5
|
-
import { Challenge } from "../../lib/challenge.js";
|
|
6
5
|
import { getConfigFlags } from "../../lib/flags.js";
|
|
7
6
|
import { loadTemplateRun, openInBrowser } from "../../lib/utils.js";
|
|
8
7
|
export default class Run extends Command {
|
|
@@ -10,27 +9,31 @@ export default class Run extends Command {
|
|
|
10
9
|
static examples = [
|
|
11
10
|
"<%= config.bin %> <%= command.id %> my-challenge",
|
|
12
11
|
"<%= config.bin %> <%= command.id %> my-challenge --studio",
|
|
12
|
+
"<%= config.bin %> <%= command.id %> team-name:my-challenge",
|
|
13
13
|
];
|
|
14
14
|
static args = {
|
|
15
|
-
challengeSlug: getChallengeSlugArgument({
|
|
15
|
+
challengeSlug: getChallengeSlugArgument({
|
|
16
|
+
description: "Challenge slug to run (e.g., 'my-challenge' or 'team-name:my-challenge')",
|
|
17
|
+
allowTeam: true,
|
|
18
|
+
}),
|
|
16
19
|
};
|
|
17
20
|
static flags = {
|
|
18
21
|
studio: Flags.boolean({ char: "s", description: "Run in studio environment", default: false }),
|
|
19
22
|
open: Flags.boolean({ char: "o", description: "Open the run URL in the browser", default: false }),
|
|
20
|
-
...getConfigFlags("api-key", "api-url", "
|
|
23
|
+
...getConfigFlags("api-key", "api-url", "web-url", "studio-url", "studio-api-url"),
|
|
21
24
|
};
|
|
22
25
|
async run() {
|
|
23
26
|
const { args, flags } = await this.parse(Run);
|
|
24
27
|
const apiUrl = flags.studio ? flags["studio-api-url"] : flags["api-url"];
|
|
25
28
|
const studioApi = new ApiClient(apiUrl, flags["api-key"], flags.studio);
|
|
26
|
-
const
|
|
29
|
+
const challengeSlug = args.challengeSlug;
|
|
27
30
|
try {
|
|
28
31
|
const { participants } = (await loadTemplateRun());
|
|
29
32
|
const template = {
|
|
30
|
-
challenge:
|
|
33
|
+
challenge: challengeSlug,
|
|
31
34
|
participants,
|
|
32
35
|
};
|
|
33
|
-
this.log(pc.blue(`>> Running challenge: ${
|
|
36
|
+
this.log(pc.blue(`>> Running challenge: ${challengeSlug}${flags.studio ? " (studio)" : ""}...`));
|
|
34
37
|
const response = await studioApi.runChallenge(template);
|
|
35
38
|
if (response.runIds && response.runIds.length > 0) {
|
|
36
39
|
const baseUrl = flags.studio ? flags["studio-url"] : flags["web-url"];
|
package/dist/commands/init.js
CHANGED
|
@@ -104,13 +104,15 @@ export default class Init extends Command {
|
|
|
104
104
|
const templateDir = getStaticResourcePath("project_template");
|
|
105
105
|
const templateFiles = await readDirSorted(templateDir);
|
|
106
106
|
for (const file of templateFiles) {
|
|
107
|
-
|
|
108
|
-
|
|
107
|
+
// We ignore .env as it will be created later
|
|
108
|
+
if (file.name.endsWith(".env") || file.isDirectory()) {
|
|
109
109
|
continue;
|
|
110
110
|
}
|
|
111
111
|
const srcPath = path.join(file.parentPath, file.name);
|
|
112
112
|
const srcRelativePath = path.relative(templateDir, srcPath);
|
|
113
113
|
const destPath = path.join(targetDir, srcRelativePath);
|
|
114
|
+
// Ensure the destination directory exists before copying the file
|
|
115
|
+
await fs.mkdir(path.dirname(destPath), { recursive: true });
|
|
114
116
|
await fs.copyFile(srcPath, destPath);
|
|
115
117
|
}
|
|
116
118
|
// Create .env file based on the environment
|
package/dist/lib/arguments.d.ts
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
import type { Arg } from "@oclif/core/interfaces";
|
|
2
2
|
/**
|
|
3
|
-
* Returns a "challenge slug" argument,
|
|
3
|
+
* Returns a "challenge slug" argument, validating it to be a valid challenge slug.
|
|
4
|
+
* @param description - Description for the argument
|
|
5
|
+
* @param required - Whether the argument is required (default: true)
|
|
6
|
+
* @param allowTeam - Whether to allow namespaced slugs like "team-name:my-challenge" (default: false)
|
|
4
7
|
*/
|
|
5
|
-
export declare function getChallengeSlugArgument<R extends boolean = true>({ description, required, }: {
|
|
8
|
+
export declare function getChallengeSlugArgument<R extends boolean = true>({ description, required, allowTeam, }: {
|
|
6
9
|
description: string;
|
|
7
10
|
required?: R;
|
|
11
|
+
allowTeam?: boolean;
|
|
8
12
|
}): Arg<R extends true ? string : string | undefined>;
|
package/dist/lib/arguments.js
CHANGED
|
@@ -1,15 +1,27 @@
|
|
|
1
1
|
import { Args } from "@oclif/core";
|
|
2
|
-
|
|
2
|
+
// Base pattern for a slug segment (lowercase alphanumeric with hyphens, no leading/trailing hyphens)
|
|
3
|
+
const SLUG_SEGMENT = "[a-z0-9]+(?:-[a-z0-9]+)*";
|
|
4
|
+
// Local challenge slug pattern: just the challenge name (no namespace)
|
|
5
|
+
const LOCAL_SLUG_REGEX = new RegExp(`^${SLUG_SEGMENT}$`);
|
|
6
|
+
// Full challenge slug pattern: optional namespace prefix (e.g., "team-name:") followed by the challenge slug
|
|
7
|
+
const NAMESPACED_SLUG_REGEX = new RegExp(`^(?:${SLUG_SEGMENT}:)?${SLUG_SEGMENT}$`);
|
|
3
8
|
/**
|
|
4
|
-
* Returns a "challenge slug" argument,
|
|
9
|
+
* Returns a "challenge slug" argument, validating it to be a valid challenge slug.
|
|
10
|
+
* @param description - Description for the argument
|
|
11
|
+
* @param required - Whether the argument is required (default: true)
|
|
12
|
+
* @param allowTeam - Whether to allow namespaced slugs like "team-name:my-challenge" (default: false)
|
|
5
13
|
*/
|
|
6
|
-
export function getChallengeSlugArgument({ description, required, }) {
|
|
14
|
+
export function getChallengeSlugArgument({ description, required, allowTeam = false, }) {
|
|
15
|
+
const regex = allowTeam ? NAMESPACED_SLUG_REGEX : LOCAL_SLUG_REGEX;
|
|
16
|
+
const errorMessage = allowTeam
|
|
17
|
+
? "Challenge slugs must be lowercase alphanumeric characters and hyphens, and must not start or end with a hyphen. Optionally, a team prefix can be provided (e.g., 'team-name:my-challenge')."
|
|
18
|
+
: "Challenge slugs must be lowercase alphanumeric characters and hyphens, and must not start or end with a hyphen.";
|
|
7
19
|
const arg = Args.string({
|
|
8
20
|
description,
|
|
9
21
|
required: required ?? true,
|
|
10
22
|
parse: async (input) => {
|
|
11
|
-
if (!
|
|
12
|
-
throw new Error(`Invalid challenge slug: ${input}.
|
|
23
|
+
if (!regex.test(input)) {
|
|
24
|
+
throw new Error(`Invalid challenge slug: ${input}. ${errorMessage}`);
|
|
13
25
|
}
|
|
14
26
|
return input;
|
|
15
27
|
},
|
package/oclif.manifest.json
CHANGED
|
@@ -361,7 +361,7 @@
|
|
|
361
361
|
"aliases": [],
|
|
362
362
|
"args": {
|
|
363
363
|
"challengeSlug": {
|
|
364
|
-
"description": "Challenge slug to run",
|
|
364
|
+
"description": "Challenge slug to run (e.g., 'my-challenge' or 'team-name:my-challenge')",
|
|
365
365
|
"name": "challengeSlug",
|
|
366
366
|
"required": true
|
|
367
367
|
}
|
|
@@ -369,7 +369,8 @@
|
|
|
369
369
|
"description": "Run a challenge",
|
|
370
370
|
"examples": [
|
|
371
371
|
"<%= config.bin %> <%= command.id %> my-challenge",
|
|
372
|
-
"<%= config.bin %> <%= command.id %> my-challenge --studio"
|
|
372
|
+
"<%= config.bin %> <%= command.id %> my-challenge --studio",
|
|
373
|
+
"<%= config.bin %> <%= command.id %> team-name:my-challenge"
|
|
373
374
|
],
|
|
374
375
|
"flags": {
|
|
375
376
|
"studio": {
|
|
@@ -405,15 +406,6 @@
|
|
|
405
406
|
"multiple": false,
|
|
406
407
|
"type": "option"
|
|
407
408
|
},
|
|
408
|
-
"challenges-path": {
|
|
409
|
-
"description": "Absolute path to the challenges directory",
|
|
410
|
-
"env": "KRADLE_CHALLENGES_PATH",
|
|
411
|
-
"name": "challenges-path",
|
|
412
|
-
"default": "~/Documents/kradle-studio/challenges",
|
|
413
|
-
"hasDynamicHelp": false,
|
|
414
|
-
"multiple": false,
|
|
415
|
-
"type": "option"
|
|
416
|
-
},
|
|
417
409
|
"web-url": {
|
|
418
410
|
"description": "Kradle Web URL, used to display the run URL",
|
|
419
411
|
"env": "KRADLE_WEB_URL",
|
|
@@ -526,6 +518,54 @@
|
|
|
526
518
|
"watch.js"
|
|
527
519
|
]
|
|
528
520
|
},
|
|
521
|
+
"ai-docs:api": {
|
|
522
|
+
"aliases": [],
|
|
523
|
+
"args": {},
|
|
524
|
+
"description": "Output the Kradle API reference documentation for LLMs",
|
|
525
|
+
"examples": [
|
|
526
|
+
"<%= config.bin %> <%= command.id %>"
|
|
527
|
+
],
|
|
528
|
+
"flags": {},
|
|
529
|
+
"hasDynamicHelp": false,
|
|
530
|
+
"hiddenAliases": [],
|
|
531
|
+
"id": "ai-docs:api",
|
|
532
|
+
"pluginAlias": "@kradle/cli",
|
|
533
|
+
"pluginName": "@kradle/cli",
|
|
534
|
+
"pluginType": "core",
|
|
535
|
+
"strict": true,
|
|
536
|
+
"enableJsonFlag": false,
|
|
537
|
+
"isESM": true,
|
|
538
|
+
"relativePath": [
|
|
539
|
+
"dist",
|
|
540
|
+
"commands",
|
|
541
|
+
"ai-docs",
|
|
542
|
+
"api.js"
|
|
543
|
+
]
|
|
544
|
+
},
|
|
545
|
+
"ai-docs:cli": {
|
|
546
|
+
"aliases": [],
|
|
547
|
+
"args": {},
|
|
548
|
+
"description": "Output the Kradle CLI reference documentation for LLMs",
|
|
549
|
+
"examples": [
|
|
550
|
+
"<%= config.bin %> <%= command.id %>"
|
|
551
|
+
],
|
|
552
|
+
"flags": {},
|
|
553
|
+
"hasDynamicHelp": false,
|
|
554
|
+
"hiddenAliases": [],
|
|
555
|
+
"id": "ai-docs:cli",
|
|
556
|
+
"pluginAlias": "@kradle/cli",
|
|
557
|
+
"pluginName": "@kradle/cli",
|
|
558
|
+
"pluginType": "core",
|
|
559
|
+
"strict": true,
|
|
560
|
+
"enableJsonFlag": false,
|
|
561
|
+
"isESM": true,
|
|
562
|
+
"relativePath": [
|
|
563
|
+
"dist",
|
|
564
|
+
"commands",
|
|
565
|
+
"ai-docs",
|
|
566
|
+
"cli.js"
|
|
567
|
+
]
|
|
568
|
+
},
|
|
529
569
|
"experiment:create": {
|
|
530
570
|
"aliases": [],
|
|
531
571
|
"args": {
|
|
@@ -760,5 +800,5 @@
|
|
|
760
800
|
]
|
|
761
801
|
}
|
|
762
802
|
},
|
|
763
|
-
"version": "0.2.
|
|
803
|
+
"version": "0.2.3"
|
|
764
804
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kradle/cli",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.3",
|
|
4
4
|
"description": "Kradle's CLI. Manage challenges, experiments, agents and more!",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cli"
|
|
@@ -81,6 +81,9 @@
|
|
|
81
81
|
},
|
|
82
82
|
"experiment": {
|
|
83
83
|
"description": "Manage and run experiments"
|
|
84
|
+
},
|
|
85
|
+
"ai-docs": {
|
|
86
|
+
"description": "Output LLM reference documentation"
|
|
84
87
|
}
|
|
85
88
|
}
|
|
86
89
|
}
|