@przeprogramowani/10x-cli 1.19.1 → 1.20.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/README.md +6 -3
- package/dist/index.mjs +95 -33
- package/package.json +1 -1
- package/skills/10x-cli-guide/SKILL.md +1 -0
package/README.md
CHANGED
|
@@ -68,7 +68,7 @@ Once installed, just tell your agent to **set up 10x-cli** and it will pick up t
|
|
|
68
68
|
|
|
69
69
|
| Flag | Description |
|
|
70
70
|
|------|-------------|
|
|
71
|
-
| `--tool <tool>` | AI coding tool: `claude-code`, `cursor`, `copilot`, `codex`, `
|
|
71
|
+
| `--tool <tool>` | AI coding tool: `claude-code`, `cursor`, `copilot`, `codex`, `devin-desktop`, `gemini`, `generic` |
|
|
72
72
|
| `--print` | Output artifact content to stdout instead of writing files |
|
|
73
73
|
| `--type <type>` | Filter by artifact type: `skills`, `prompts`, `rules`, `configs` |
|
|
74
74
|
| `--name <name>` | Filter by artifact name (requires `--type`) |
|
|
@@ -197,7 +197,7 @@ scoring live in the template and the instance.
|
|
|
197
197
|
| Flag | Description |
|
|
198
198
|
|------|-------------|
|
|
199
199
|
| `--template-version <tag>` | Template tag to install (default: latest) |
|
|
200
|
-
| `--tool <id>` | Agent tool for skill placement (`claude-code`, `cursor`, `copilot`, `codex`, `
|
|
200
|
+
| `--tool <id>` | Agent tool for skill placement (`claude-code`, `cursor`, `copilot`, `codex`, `devin-desktop`, `gemini`, `generic`) |
|
|
201
201
|
| `--yes` | Run non-interactively, accepting defaults |
|
|
202
202
|
|
|
203
203
|
```bash
|
|
@@ -239,11 +239,14 @@ On first run, the CLI prompts you to choose your AI coding tool. Artifacts are w
|
|
|
239
239
|
| Cursor | `.cursor/` | `.cursor/rules/10x-course.mdc` |
|
|
240
240
|
| GitHub Copilot | `.github/` | `.github/copilot-instructions.md` |
|
|
241
241
|
| Codex CLI | `.agents/` | `AGENTS.md` |
|
|
242
|
-
|
|
|
242
|
+
| Devin Desktop | `.devin/` | `AGENTS.md` |
|
|
243
243
|
| Gemini CLI | `.gemini/` | `GEMINI.md` |
|
|
244
244
|
| Generic | `.ai/` | `AGENTS.md` |
|
|
245
245
|
|
|
246
246
|
Override anytime with `--tool <name>`. Your choice is saved in `~/.config/10x-cli/config.json`.
|
|
247
|
+
The former `windsurf` ID remains accepted as an alias and is upgraded to
|
|
248
|
+
`devin-desktop`; existing `.windsurf/` artifacts can be migrated by the normal
|
|
249
|
+
tool-switch prompt.
|
|
247
250
|
|
|
248
251
|
## Development
|
|
249
252
|
|
package/dist/index.mjs
CHANGED
|
@@ -3183,7 +3183,7 @@ var cac = (name = "") => new CAC(name);
|
|
|
3183
3183
|
// package.json
|
|
3184
3184
|
var package_default = {
|
|
3185
3185
|
name: "@przeprogramowani/10x-cli",
|
|
3186
|
-
version: "1.
|
|
3186
|
+
version: "1.20.0",
|
|
3187
3187
|
description: "Open-source CLI for 10xDevs course content",
|
|
3188
3188
|
repository: {
|
|
3189
3189
|
type: "git",
|
|
@@ -4296,15 +4296,16 @@ var PROFILES = {
|
|
|
4296
4296
|
sentinelBegin: SENTINEL_BEGIN,
|
|
4297
4297
|
sentinelEnd: SENTINEL_END
|
|
4298
4298
|
},
|
|
4299
|
-
|
|
4300
|
-
toolId: "
|
|
4301
|
-
|
|
4302
|
-
|
|
4303
|
-
|
|
4304
|
-
|
|
4305
|
-
|
|
4306
|
-
|
|
4307
|
-
|
|
4299
|
+
"devin-desktop": {
|
|
4300
|
+
toolId: "devin-desktop",
|
|
4301
|
+
contentToolId: "windsurf",
|
|
4302
|
+
displayName: "Devin Desktop",
|
|
4303
|
+
skillPath: (n) => `.devin/skills/${n}/SKILL.md`,
|
|
4304
|
+
skillDir: (n) => `.devin/skills/${n}`,
|
|
4305
|
+
promptPath: (n) => `.devin/prompts/${n}.md`,
|
|
4306
|
+
configPath: (n) => `.devin/config-templates/${n}`,
|
|
4307
|
+
rulesFile: "AGENTS.md",
|
|
4308
|
+
manifestDir: ".devin",
|
|
4308
4309
|
sentinelBegin: SENTINEL_BEGIN,
|
|
4309
4310
|
sentinelEnd: SENTINEL_END
|
|
4310
4311
|
},
|
|
@@ -4333,6 +4334,32 @@ var PROFILES = {
|
|
|
4333
4334
|
sentinelEnd: SENTINEL_END
|
|
4334
4335
|
}
|
|
4335
4336
|
};
|
|
4337
|
+
var TOOL_ALIASES = {
|
|
4338
|
+
windsurf: "devin-desktop"
|
|
4339
|
+
};
|
|
4340
|
+
var LEGACY_PROFILES = {
|
|
4341
|
+
windsurf: {
|
|
4342
|
+
toolId: "windsurf",
|
|
4343
|
+
displayName: "Windsurf",
|
|
4344
|
+
skillPath: (n) => `.windsurf/skills/${n}/SKILL.md`,
|
|
4345
|
+
skillDir: (n) => `.windsurf/skills/${n}`,
|
|
4346
|
+
promptPath: (n) => `.windsurf/prompts/${n}.md`,
|
|
4347
|
+
configPath: (n) => `.windsurf/config-templates/${n}`,
|
|
4348
|
+
rulesFile: ".windsurfrules",
|
|
4349
|
+
manifestDir: ".windsurf",
|
|
4350
|
+
sentinelBegin: SENTINEL_BEGIN,
|
|
4351
|
+
sentinelEnd: SENTINEL_END
|
|
4352
|
+
}
|
|
4353
|
+
};
|
|
4354
|
+
function canonicalToolId(toolId) {
|
|
4355
|
+
return TOOL_ALIASES[toolId] ?? toolId;
|
|
4356
|
+
}
|
|
4357
|
+
function getToolProfile(toolId) {
|
|
4358
|
+
return PROFILES[canonicalToolId(toolId)];
|
|
4359
|
+
}
|
|
4360
|
+
function contentToolId(profile) {
|
|
4361
|
+
return profile.contentToolId ?? profile.toolId;
|
|
4362
|
+
}
|
|
4336
4363
|
var DEFAULT_TOOL = "claude-code";
|
|
4337
4364
|
|
|
4338
4365
|
// src/lib/tool-detect.ts
|
|
@@ -4391,16 +4418,34 @@ function detectTools(projectRoot) {
|
|
|
4391
4418
|
} else if (hit("AGENTS.md") && !hit(".ai")) {
|
|
4392
4419
|
signals.push({ profileId: "codex", confidence: "medium", reason: "AGENTS.md" });
|
|
4393
4420
|
}
|
|
4394
|
-
if (hit(".
|
|
4421
|
+
if (hit(".devin/" + MANIFEST_FILENAME)) {
|
|
4422
|
+
signals.push({
|
|
4423
|
+
profileId: "devin-desktop",
|
|
4424
|
+
confidence: "strong",
|
|
4425
|
+
reason: ".devin/.10x-cli-manifest.json"
|
|
4426
|
+
});
|
|
4427
|
+
} else if (hit(".devin/rules")) {
|
|
4428
|
+
signals.push({ profileId: "devin-desktop", confidence: "strong", reason: ".devin/rules/" });
|
|
4429
|
+
} else if (hit(".devin")) {
|
|
4430
|
+
signals.push({ profileId: "devin-desktop", confidence: "medium", reason: ".devin/ directory" });
|
|
4431
|
+
} else if (hit(".windsurf/" + MANIFEST_FILENAME)) {
|
|
4395
4432
|
signals.push({
|
|
4396
|
-
profileId: "
|
|
4433
|
+
profileId: "devin-desktop",
|
|
4397
4434
|
confidence: "strong",
|
|
4398
|
-
reason: ".windsurf/.10x-cli-manifest.json"
|
|
4435
|
+
reason: ".windsurf/.10x-cli-manifest.json (legacy)"
|
|
4399
4436
|
});
|
|
4400
4437
|
} else if (hit(".windsurfrules")) {
|
|
4401
|
-
signals.push({
|
|
4438
|
+
signals.push({
|
|
4439
|
+
profileId: "devin-desktop",
|
|
4440
|
+
confidence: "strong",
|
|
4441
|
+
reason: ".windsurfrules (legacy)"
|
|
4442
|
+
});
|
|
4402
4443
|
} else if (hit(".windsurf")) {
|
|
4403
|
-
signals.push({
|
|
4444
|
+
signals.push({
|
|
4445
|
+
profileId: "devin-desktop",
|
|
4446
|
+
confidence: "medium",
|
|
4447
|
+
reason: ".windsurf/ directory (legacy)"
|
|
4448
|
+
});
|
|
4404
4449
|
}
|
|
4405
4450
|
if (hit(".gemini/" + MANIFEST_FILENAME)) {
|
|
4406
4451
|
signals.push({
|
|
@@ -4427,7 +4472,15 @@ function detectTools(projectRoot) {
|
|
|
4427
4472
|
return rankSignals(signals);
|
|
4428
4473
|
}
|
|
4429
4474
|
var CONFIDENCE_ORDER = { strong: 3, medium: 2, weak: 1 };
|
|
4430
|
-
var PROFILE_ORDER = [
|
|
4475
|
+
var PROFILE_ORDER = [
|
|
4476
|
+
"claude-code",
|
|
4477
|
+
"cursor",
|
|
4478
|
+
"copilot",
|
|
4479
|
+
"codex",
|
|
4480
|
+
"devin-desktop",
|
|
4481
|
+
"gemini",
|
|
4482
|
+
"generic"
|
|
4483
|
+
];
|
|
4431
4484
|
function rankSignals(signals) {
|
|
4432
4485
|
return [...signals].sort((a2, b2) => {
|
|
4433
4486
|
const c2 = CONFIDENCE_ORDER[b2.confidence] - CONFIDENCE_ORDER[a2.confidence];
|
|
@@ -4541,7 +4594,7 @@ async function runBenchKitInit(ctx, dirArg, options, deps = defaultDeps) {
|
|
|
4541
4594
|
...res.runnerDeps === "failed" ? ["Runner dependencies did not install — run 'npm ci --prefix .bench-kit/runner' yourself."] : res.runnerDeps === "installed" ? ["Runner dependencies installed (.bench-kit/runner/node_modules)."] : [],
|
|
4542
4595
|
res.committed === true ? "Initialized a fresh git repository with an initial commit." : "Initialized a fresh git repository (initial commit skipped — commit the files yourself).",
|
|
4543
4596
|
...(res.warnings ?? []).map((warning) => `Warning: ${warning}`),
|
|
4544
|
-
|
|
4597
|
+
`Next: go to ${targetDir}, launch you preferred agent and apply skill *bench-wiring*`
|
|
4545
4598
|
];
|
|
4546
4599
|
output(ctx, humanLines.join(`
|
|
4547
4600
|
`), {
|
|
@@ -4648,10 +4701,11 @@ function describeSync(counts) {
|
|
|
4648
4701
|
}
|
|
4649
4702
|
async function resolveInstanceTool(ctx, options, deps, existingInstance) {
|
|
4650
4703
|
if (options.tool !== undefined) {
|
|
4651
|
-
|
|
4704
|
+
const canonicalId = canonicalToolId(options.tool);
|
|
4705
|
+
if (!getToolProfile(options.tool)) {
|
|
4652
4706
|
outputError(ctx, "unknown_tool", `'${options.tool}' is not a supported agent tool.`, ExitCodes.USAGE, `Supported: ${Object.keys(PROFILES).join(", ")}.`);
|
|
4653
4707
|
}
|
|
4654
|
-
return { id:
|
|
4708
|
+
return { id: canonicalId, explicit: true };
|
|
4655
4709
|
}
|
|
4656
4710
|
const signals = deps.detectToolSignals(process.cwd());
|
|
4657
4711
|
const top = signals[0];
|
|
@@ -4666,7 +4720,7 @@ async function resolveInstanceTool(ctx, options, deps, existingInstance) {
|
|
|
4666
4720
|
return { id: choice !== null && PROFILES[choice] ? choice : initial, explicit: choice !== null };
|
|
4667
4721
|
}
|
|
4668
4722
|
function skillRootFor(toolId) {
|
|
4669
|
-
const profile =
|
|
4723
|
+
const profile = getToolProfile(toolId) ?? PROFILES[DEFAULT_TOOL];
|
|
4670
4724
|
return `${profile.manifestDir}/skills`;
|
|
4671
4725
|
}
|
|
4672
4726
|
function allToolProfiles() {
|
|
@@ -5304,7 +5358,7 @@ async function checkCliVersion() {
|
|
|
5304
5358
|
function checkToolDirectory() {
|
|
5305
5359
|
const cwd = process.cwd();
|
|
5306
5360
|
const toolId = readToolConfig()?.tool ?? DEFAULT_TOOL;
|
|
5307
|
-
const profile =
|
|
5361
|
+
const profile = getToolProfile(toolId) ?? PROFILES[DEFAULT_TOOL];
|
|
5308
5362
|
const dirName = profile.manifestDir;
|
|
5309
5363
|
const toolDir = join5(cwd, dirName);
|
|
5310
5364
|
if (!existsSync5(toolDir)) {
|
|
@@ -6009,7 +6063,7 @@ function removeEmptyParentDirs(filePath, skillDirAbs) {
|
|
|
6009
6063
|
}
|
|
6010
6064
|
function findOrphanedManifests(projectRoot, currentProfile) {
|
|
6011
6065
|
const out = [];
|
|
6012
|
-
for (const profile of Object.values(PROFILES)) {
|
|
6066
|
+
for (const profile of [...Object.values(PROFILES), ...Object.values(LEGACY_PROFILES)]) {
|
|
6013
6067
|
if (profile.toolId === currentProfile.toolId)
|
|
6014
6068
|
continue;
|
|
6015
6069
|
const manifestPath = join6(projectRoot, profile.manifestDir, MANIFEST_FILENAME);
|
|
@@ -6282,13 +6336,14 @@ async function resolveToolProfile(flagOverride, projectRoot = process.cwd()) {
|
|
|
6282
6336
|
}
|
|
6283
6337
|
async function resolveProfileOnly(flagOverride, projectRoot) {
|
|
6284
6338
|
if (flagOverride) {
|
|
6285
|
-
const
|
|
6339
|
+
const canonicalId = canonicalToolId(flagOverride);
|
|
6340
|
+
const profile = getToolProfile(flagOverride);
|
|
6286
6341
|
if (!profile) {
|
|
6287
6342
|
throw new Error(`Unknown tool '${flagOverride}'. Supported: ${Object.keys(PROFILES).join(", ")}`);
|
|
6288
6343
|
}
|
|
6289
6344
|
const existing = readToolConfig();
|
|
6290
|
-
if (existing?.tool !==
|
|
6291
|
-
saveToolConfig({ ...existing ?? {}, tool:
|
|
6345
|
+
if (existing?.tool !== canonicalId) {
|
|
6346
|
+
saveToolConfig({ ...existing ?? {}, tool: canonicalId });
|
|
6292
6347
|
if (process.stdout.isTTY) {
|
|
6293
6348
|
process.stderr.write(`Default tool set to ${profile.displayName}.
|
|
6294
6349
|
`);
|
|
@@ -6297,8 +6352,15 @@ async function resolveProfileOnly(flagOverride, projectRoot) {
|
|
|
6297
6352
|
return profile;
|
|
6298
6353
|
}
|
|
6299
6354
|
const config = readToolConfig();
|
|
6300
|
-
if (config?.tool
|
|
6301
|
-
|
|
6355
|
+
if (config?.tool) {
|
|
6356
|
+
const profile = getToolProfile(config.tool);
|
|
6357
|
+
if (profile) {
|
|
6358
|
+
const canonicalId = canonicalToolId(config.tool);
|
|
6359
|
+
if (canonicalId !== config.tool) {
|
|
6360
|
+
saveToolConfig({ ...config, tool: canonicalId });
|
|
6361
|
+
}
|
|
6362
|
+
return profile;
|
|
6363
|
+
}
|
|
6302
6364
|
}
|
|
6303
6365
|
if (process.stdout.isTTY) {
|
|
6304
6366
|
const signals = detectTools(projectRoot);
|
|
@@ -6413,7 +6475,7 @@ function resolveCourseRulesFlag(argv) {
|
|
|
6413
6475
|
}
|
|
6414
6476
|
var DEFAULT_COURSE2 = "10xdevs3";
|
|
6415
6477
|
function registerGetCommand(cli) {
|
|
6416
|
-
cli.command("get <ref>", "Fetch and apply a lesson pack").option("--dry-run", "Show what would be written without touching the filesystem").option("--course <course>", "Override the course slug (default: 10xdevs3)").option("--tool <tool>", "AI coding tool (claude-code, cursor, copilot, codex,
|
|
6478
|
+
cli.command("get <ref>", "Fetch and apply a lesson pack").option("--dry-run", "Show what would be written without touching the filesystem").option("--course <course>", "Override the course slug (default: 10xdevs3)").option("--tool <tool>", "AI coding tool (claude-code, cursor, copilot, codex, devin-desktop, gemini, generic)").option("--print", "Print artifact content to stdout instead of writing to files").option("--type <type>", "Artifact type filter: skills, prompts, rules, configs").option("--name <name>", "Artifact name filter (requires --type)").option("--lang <lang>", "Content language: en (default) or pl").option("--no-course-rules", "Skip applying the course rules block to your rules file (CLAUDE.md/AGENTS.md)").action(async (ref, options) => {
|
|
6417
6479
|
const ctx = resolveContext(options);
|
|
6418
6480
|
await runGet(ctx, ref, options);
|
|
6419
6481
|
});
|
|
@@ -6456,7 +6518,7 @@ async function runGet(ctx, ref, options) {
|
|
|
6456
6518
|
verbose(ctx, `fetching lesson ${course}/${parsed.lessonId}`);
|
|
6457
6519
|
const result = await fetchLesson(course, parsed.lessonId, auth.access_token, {
|
|
6458
6520
|
lang,
|
|
6459
|
-
tool: profile
|
|
6521
|
+
tool: contentToolId(profile)
|
|
6460
6522
|
});
|
|
6461
6523
|
if (!result.ok) {
|
|
6462
6524
|
handleLessonError(ctx, result.status, result.code, result.error, result.payload);
|
|
@@ -6528,7 +6590,7 @@ async function runPrintMode(ctx, lessonId, course, profile, token, lang, options
|
|
|
6528
6590
|
}
|
|
6529
6591
|
if (options.name) {
|
|
6530
6592
|
verbose(ctx, `fetching artifact ${course}/${lessonId}/${options.type}/${options.name}`);
|
|
6531
|
-
const result = await fetchArtifact(course, lessonId, options.type, options.name, profile
|
|
6593
|
+
const result = await fetchArtifact(course, lessonId, options.type, options.name, contentToolId(profile), token, { lang });
|
|
6532
6594
|
if (!result.ok) {
|
|
6533
6595
|
handleLessonError(ctx, result.status, result.code, result.error, result.payload);
|
|
6534
6596
|
}
|
|
@@ -6546,7 +6608,7 @@ async function runPrintMode(ctx, lessonId, course, profile, token, lang, options
|
|
|
6546
6608
|
}
|
|
6547
6609
|
} else {
|
|
6548
6610
|
verbose(ctx, `fetching lesson ${course}/${lessonId} (filtering by ${options.type})`);
|
|
6549
|
-
const result = await fetchLesson(course, lessonId, token, { lang, tool: profile
|
|
6611
|
+
const result = await fetchLesson(course, lessonId, token, { lang, tool: contentToolId(profile) });
|
|
6550
6612
|
if (!result.ok) {
|
|
6551
6613
|
handleLessonError(ctx, result.status, result.code, result.error, result.payload);
|
|
6552
6614
|
}
|
|
@@ -6848,7 +6910,7 @@ import { join as join9 } from "node:path";
|
|
|
6848
6910
|
var DEFAULT_COURSE4 = "10xdevs3";
|
|
6849
6911
|
var SUPPORTED_LANGS = ["en", "pl"];
|
|
6850
6912
|
function registerSyncCommand(cli) {
|
|
6851
|
-
cli.command("sync", "Bulk-download and update lessons, reporting what changed upstream").option("--all", "Sync every unlocked lesson (default: only lessons you've already downloaded)").option("--dry-run", "Show what would change without writing").option("--force", "Ignore the cheap-skip digest and overwrite local edits with upstream").option("--module <module>", "Limit to one module (e.g. 'm2' or '2')").option("--course <course>", "Override the course slug (default: 10xdevs3)").option("--tool <tool>", "AI coding tool (claude-code, cursor, copilot, codex,
|
|
6913
|
+
cli.command("sync", "Bulk-download and update lessons, reporting what changed upstream").option("--all", "Sync every unlocked lesson (default: only lessons you've already downloaded)").option("--dry-run", "Show what would change without writing").option("--force", "Ignore the cheap-skip digest and overwrite local edits with upstream").option("--module <module>", "Limit to one module (e.g. 'm2' or '2')").option("--course <course>", "Override the course slug (default: 10xdevs3)").option("--tool <tool>", "AI coding tool (claude-code, cursor, copilot, codex, devin-desktop, gemini, generic)").option("--lang <lang>", "Content language: en (default) or pl").option("--no-course-rules", "Skip applying the course rules block to your rules file (CLAUDE.md/AGENTS.md)").action(async (options) => {
|
|
6852
6914
|
const ctx = resolveContext(options);
|
|
6853
6915
|
await runSync(ctx, options);
|
|
6854
6916
|
});
|
|
@@ -6949,7 +7011,7 @@ async function syncLesson(ctx, lesson, opts) {
|
|
|
6949
7011
|
verbose(ctx, `${lesson.lessonId}: fetching`);
|
|
6950
7012
|
const result = await fetchLesson(opts.course, lesson.lessonId, opts.token, {
|
|
6951
7013
|
lang: opts.lang,
|
|
6952
|
-
tool: opts.profile
|
|
7014
|
+
tool: contentToolId(opts.profile),
|
|
6953
7015
|
signal: opts.signal
|
|
6954
7016
|
});
|
|
6955
7017
|
if (!result.ok) {
|
package/package.json
CHANGED
|
@@ -81,6 +81,7 @@ The primary daily command. Fetches a lesson bundle from the API and writes skill
|
|
|
81
81
|
| Cursor | `.cursor/skills/<name>/SKILL.md` | `.cursor/prompts/<name>.md` | `.cursor/rules/10x-course.mdc` | `.cursor/config-templates/<name>` |
|
|
82
82
|
| GitHub Copilot | `.github/skills/<name>/SKILL.md` | `.github/prompts/<name>.md` | `.github/copilot-instructions.md` | `.github/config-templates/<name>` |
|
|
83
83
|
| Codex CLI | `.agents/skills/<name>/SKILL.md` | `.agents/prompts/<name>.md` | `AGENTS.md` | `.agents/config-templates/<name>` |
|
|
84
|
+
| Devin Desktop | `.devin/skills/<name>/SKILL.md` | `.devin/prompts/<name>.md` | `AGENTS.md` | `.devin/config-templates/<name>` |
|
|
84
85
|
| Generic | `.ai/skills/<name>/SKILL.md` | `.ai/prompts/<name>.md` | `AGENTS.md` | `.ai/config-templates/<name>` |
|
|
85
86
|
|
|
86
87
|
**Re-applying a lesson** overwrites skills and prompts if content changed, updates the rules sentinel block, but never overwrites config templates (they may contain user edits).
|