@przeprogramowani/10x-cli 1.25.2 → 1.26.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 +18 -3
- package/dist/index.mjs +35 -35
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -42,7 +42,7 @@ Once your release includes it, run from the intended project directory:
|
|
|
42
42
|
|
|
43
43
|
This installs **both** `10x-cli-setup` and `10x-cli-guide`, each with its complete
|
|
44
44
|
`SKILL.md` and `references/compatibility.md`, into `.github/skills/`. Use
|
|
45
|
-
`--tool claude-code`, `cursor`, `codex`, `devin-desktop`, `gemini`, `kiro`, or `generic`
|
|
45
|
+
`--tool claude-code`, `cursor`, `codex`, `devin-desktop`, `factory`, `gemini`, `kiro`, or `generic`
|
|
46
46
|
when that is your intended tool. The target must be explicit; installation is
|
|
47
47
|
project-only. There is no `--global`, automatic agent detection, `skills`/npx
|
|
48
48
|
subprocess, authentication, or network access. The helper bytes come from the
|
|
@@ -181,7 +181,7 @@ remain visible. Full lesson downloads and other commands remain available below.
|
|
|
181
181
|
|
|
182
182
|
| Flag | Description |
|
|
183
183
|
|------|-------------|
|
|
184
|
-
| `--tool <tool>` | AI coding tool: `claude-code`, `cursor`, `copilot`, `codex`, `devin-desktop`, `gemini`, `kiro`, `generic` |
|
|
184
|
+
| `--tool <tool>` | AI coding tool: `claude-code`, `cursor`, `copilot`, `codex`, `devin-desktop`, `gemini`, `kiro`, `factory`, `generic` |
|
|
185
185
|
| `--print` | Output artifact content to stdout instead of writing files |
|
|
186
186
|
| `--type <type>` | Filter by artifact type: `skills`, `prompts`, `rules`, `configs` |
|
|
187
187
|
| `--name <name>` | Filter by artifact name (requires `--type`) |
|
|
@@ -309,7 +309,7 @@ scoring live in the template and the instance.
|
|
|
309
309
|
| Flag | Description |
|
|
310
310
|
|------|-------------|
|
|
311
311
|
| `--template-version <tag>` | Template tag to install (default: latest) |
|
|
312
|
-
| `--tool <id>` | Agent tool for skill placement (`claude-code`, `cursor`, `copilot`, `codex`, `devin-desktop`, `gemini`, `kiro`, `generic`) |
|
|
312
|
+
| `--tool <id>` | Agent tool for skill placement (`claude-code`, `cursor`, `copilot`, `codex`, `devin-desktop`, `gemini`, `kiro`, `factory`, `generic`) |
|
|
313
313
|
| `--yes` | Run non-interactively, accepting defaults |
|
|
314
314
|
| `--deep` | Clone the detected base repo with full history (default: shallow, HEAD only) |
|
|
315
315
|
|
|
@@ -363,6 +363,7 @@ On first run, the CLI prompts you to choose your AI coding tool. Artifacts are w
|
|
|
363
363
|
| Devin Desktop | `.devin/` | `AGENTS.md` |
|
|
364
364
|
| Gemini CLI | `.gemini/` | `GEMINI.md` |
|
|
365
365
|
| Kiro | `.kiro/` | `AGENTS.md` |
|
|
366
|
+
| Factory AI | `.factory/` | `AGENTS.md` |
|
|
366
367
|
| Generic | `.ai/` | `AGENTS.md` |
|
|
367
368
|
|
|
368
369
|
Override with `--tool <name>`. Validated writing commands save your choice in `~/.config/10x-cli/config.json`. Previews leave it unchanged.
|
|
@@ -370,6 +371,20 @@ The former `windsurf` ID remains accepted as an alias and is upgraded to
|
|
|
370
371
|
`devin-desktop`; existing `.windsurf/` artifacts can be migrated by the normal
|
|
371
372
|
tool-switch prompt.
|
|
372
373
|
|
|
374
|
+
Factory AI stores skills in `.factory/skills/`, prompts in
|
|
375
|
+
`.factory/commands/<name>.md` as custom slash commands, and config templates in
|
|
376
|
+
`.factory/config-templates/`. Prompt names and bytes are preserved. Config
|
|
377
|
+
templates are only a staging location; the CLI does not activate or merge them.
|
|
378
|
+
Factory requests the generic content variant from the delivery API while its
|
|
379
|
+
manifest records `factory`. It is auto-detected from `.factory/` (including its
|
|
380
|
+
manifest, `skills/`, or `commands/`); `AGENTS.md` alone does not identify
|
|
381
|
+
Factory.
|
|
382
|
+
|
|
383
|
+
Factory shares root `AGENTS.md` with Codex CLI, Devin Desktop, Kiro, and Generic.
|
|
384
|
+
The managed course-rules block can be shared only when its bytes are identical.
|
|
385
|
+
Factory and Generic use the same generic content, but incompatible Codex or
|
|
386
|
+
Devin rules stay protected even when an overwrite is requested.
|
|
387
|
+
|
|
373
388
|
## Development
|
|
374
389
|
|
|
375
390
|
```bash
|
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.26.0",
|
|
3187
3187
|
description: "Open-source CLI for 10xDevs course content",
|
|
3188
3188
|
repository: {
|
|
3189
3189
|
type: "git",
|
|
@@ -3359,7 +3359,6 @@ function updateToolConfig(patch) {
|
|
|
3359
3359
|
import { hostname as osHostname, release as osRelease } from "node:os";
|
|
3360
3360
|
|
|
3361
3361
|
// src/lib/api-client.ts
|
|
3362
|
-
var import_proper_lockfile = __toESM(require_proper_lockfile(), 1);
|
|
3363
3362
|
var DEFAULT_API_BASE = "https://10x-toolkit-api.przeprogramowani.workers.dev";
|
|
3364
3363
|
var PROD_HOSTNAME = "10x-toolkit-api.przeprogramowani.workers.dev";
|
|
3365
3364
|
var DEV_HOSTNAMES = new Set(["localhost", "127.0.0.1"]);
|
|
@@ -3402,7 +3401,6 @@ var ERROR_CODE_MESSAGES = {
|
|
|
3402
3401
|
module_locked: "This module is not available yet.",
|
|
3403
3402
|
signing_failed: "Could not sign the download URL. Try again in a moment.",
|
|
3404
3403
|
invalid_json: "The server received a malformed request.",
|
|
3405
|
-
email_changed: "Your email changed. Run `10x auth` to sign in again.",
|
|
3406
3404
|
unauthorized: "You are not signed in. Run `10x auth` first.",
|
|
3407
3405
|
no_membership: "No active course membership was found for this email.",
|
|
3408
3406
|
invalid_refresh_token: "Your session has expired. Run `10x auth` again.",
|
|
@@ -3474,21 +3472,6 @@ async function request(method, path, body, options = {}) {
|
|
|
3474
3472
|
}
|
|
3475
3473
|
if (!response.ok) {
|
|
3476
3474
|
const payload = parsed && typeof parsed === "object" ? parsed : undefined;
|
|
3477
|
-
if ((payload?.code ?? payload?.error) === "email_changed" && options.token && readAuth()?.access_token === options.token) {
|
|
3478
|
-
try {
|
|
3479
|
-
const release = await import_proper_lockfile.default.lock(authFilePath(), {
|
|
3480
|
-
realpath: false,
|
|
3481
|
-
stale: 1e4,
|
|
3482
|
-
retries: { retries: 5, factor: 2, minTimeout: 100, maxTimeout: 1000 }
|
|
3483
|
-
});
|
|
3484
|
-
try {
|
|
3485
|
-
if (readAuth()?.access_token === options.token)
|
|
3486
|
-
deleteAuth();
|
|
3487
|
-
} finally {
|
|
3488
|
-
await release();
|
|
3489
|
-
}
|
|
3490
|
-
} catch {}
|
|
3491
|
-
}
|
|
3492
3475
|
const mapped = messageForApiError(payload);
|
|
3493
3476
|
let errorMessage;
|
|
3494
3477
|
if (mapped) {
|
|
@@ -3508,13 +3491,7 @@ async function request(method, path, body, options = {}) {
|
|
|
3508
3491
|
payload
|
|
3509
3492
|
};
|
|
3510
3493
|
}
|
|
3511
|
-
return {
|
|
3512
|
-
ok: true,
|
|
3513
|
-
status: response.status,
|
|
3514
|
-
data: parsed,
|
|
3515
|
-
responseHeaders: response.headers,
|
|
3516
|
-
rawBody: text
|
|
3517
|
-
};
|
|
3494
|
+
return { ok: true, status: response.status, data: parsed, responseHeaders: response.headers, rawBody: text };
|
|
3518
3495
|
}
|
|
3519
3496
|
function apiGet(path, options = {}) {
|
|
3520
3497
|
return request("GET", path, undefined, options);
|
|
@@ -3676,7 +3653,7 @@ async function pollCircleLogin(deviceCode, options = {}) {
|
|
|
3676
3653
|
}
|
|
3677
3654
|
|
|
3678
3655
|
// src/lib/auth-guard.ts
|
|
3679
|
-
var
|
|
3656
|
+
var import_proper_lockfile = __toESM(require_proper_lockfile(), 1);
|
|
3680
3657
|
import { mkdirSync as mkdirSync2 } from "node:fs";
|
|
3681
3658
|
import { dirname } from "node:path";
|
|
3682
3659
|
|
|
@@ -3751,7 +3728,7 @@ function isNearExpiry(auth, windowMs = DEFAULT_REFRESH_WINDOW_MS, now = new Date
|
|
|
3751
3728
|
async function acquireRefreshLock(ctx, lockTarget) {
|
|
3752
3729
|
try {
|
|
3753
3730
|
mkdirSync2(dirname(lockTarget), { recursive: true });
|
|
3754
|
-
return await
|
|
3731
|
+
return await import_proper_lockfile.default.lock(lockTarget, {
|
|
3755
3732
|
realpath: false,
|
|
3756
3733
|
stale: LOCK_STALE_MS,
|
|
3757
3734
|
retries: {
|
|
@@ -3809,12 +3786,6 @@ async function requireAuth(ctx, options = {}) {
|
|
|
3809
3786
|
persist(next);
|
|
3810
3787
|
return next;
|
|
3811
3788
|
}
|
|
3812
|
-
if (refreshed.code === "email_changed") {
|
|
3813
|
-
try {
|
|
3814
|
-
(options.remove ?? deleteAuth)();
|
|
3815
|
-
} catch {}
|
|
3816
|
-
outputError(ctx, "email_changed", "Your email changed. Sign in again.", ExitCodes.AUTH_REQUIRED, "Run '10x auth' to log in.");
|
|
3817
|
-
}
|
|
3818
3789
|
if (forced) {
|
|
3819
3790
|
outputError(ctx, refreshed.code, refreshed.error, refreshed.status === 403 ? ExitCodes.FORBIDDEN : refreshed.status === 401 ? ExitCodes.AUTH_REQUIRED : ExitCodes.ERROR);
|
|
3820
3791
|
}
|
|
@@ -4754,6 +4725,19 @@ var PROFILES = {
|
|
|
4754
4725
|
sentinelBegin: SENTINEL_BEGIN,
|
|
4755
4726
|
sentinelEnd: SENTINEL_END
|
|
4756
4727
|
},
|
|
4728
|
+
factory: {
|
|
4729
|
+
toolId: "factory",
|
|
4730
|
+
contentToolId: "generic",
|
|
4731
|
+
displayName: "Factory AI",
|
|
4732
|
+
skillPath: (n) => `.factory/skills/${n}/SKILL.md`,
|
|
4733
|
+
skillDir: (n) => `.factory/skills/${n}`,
|
|
4734
|
+
promptPath: (n) => `.factory/commands/${n}.md`,
|
|
4735
|
+
configPath: (n) => `.factory/config-templates/${n}`,
|
|
4736
|
+
rulesFile: "AGENTS.md",
|
|
4737
|
+
manifestDir: ".factory",
|
|
4738
|
+
sentinelBegin: SENTINEL_BEGIN,
|
|
4739
|
+
sentinelEnd: SENTINEL_END
|
|
4740
|
+
},
|
|
4757
4741
|
gemini: {
|
|
4758
4742
|
toolId: "gemini",
|
|
4759
4743
|
displayName: "Gemini CLI",
|
|
@@ -5213,6 +5197,21 @@ function detectTools(projectRoot) {
|
|
|
5213
5197
|
reason: ".windsurf/ directory (legacy)"
|
|
5214
5198
|
});
|
|
5215
5199
|
}
|
|
5200
|
+
if (hit(".factory/" + MANIFEST_FILENAME)) {
|
|
5201
|
+
signals.push({
|
|
5202
|
+
profileId: "factory",
|
|
5203
|
+
confidence: "strong",
|
|
5204
|
+
reason: ".factory/.10x-cli-manifest.json"
|
|
5205
|
+
});
|
|
5206
|
+
} else if (hit(".factory/commands")) {
|
|
5207
|
+
signals.push({ profileId: "factory", confidence: "strong", reason: ".factory/commands/" });
|
|
5208
|
+
} else if (hit(".factory/droids")) {
|
|
5209
|
+
signals.push({ profileId: "factory", confidence: "strong", reason: ".factory/droids/" });
|
|
5210
|
+
} else if (hit(".factory/skills")) {
|
|
5211
|
+
signals.push({ profileId: "factory", confidence: "strong", reason: ".factory/skills/" });
|
|
5212
|
+
} else if (hit(".factory")) {
|
|
5213
|
+
signals.push({ profileId: "factory", confidence: "strong", reason: ".factory/ directory" });
|
|
5214
|
+
}
|
|
5216
5215
|
if (hit(".gemini/" + MANIFEST_FILENAME)) {
|
|
5217
5216
|
signals.push({
|
|
5218
5217
|
profileId: "gemini",
|
|
@@ -5265,6 +5264,7 @@ var PROFILE_ORDER = [
|
|
|
5265
5264
|
"copilot",
|
|
5266
5265
|
"codex",
|
|
5267
5266
|
"devin-desktop",
|
|
5267
|
+
"factory",
|
|
5268
5268
|
"gemini",
|
|
5269
5269
|
"kiro",
|
|
5270
5270
|
"generic"
|
|
@@ -7216,7 +7216,7 @@ function resolveCourseRulesFlag(argv) {
|
|
|
7216
7216
|
return;
|
|
7217
7217
|
}
|
|
7218
7218
|
function registerGetCommand(cli) {
|
|
7219
|
-
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>", "Select course ID or slug (default: project edition or API recommendation)").option("--tool <tool>", "AI coding tool (claude-code, cursor, copilot, codex, devin-desktop, gemini, kiro, 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) => {
|
|
7219
|
+
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>", "Select course ID or slug (default: project edition or API recommendation)").option("--tool <tool>", "AI coding tool (claude-code, cursor, copilot, codex, devin-desktop, factory, gemini, kiro, 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) => {
|
|
7220
7220
|
const ctx = resolveContext(options);
|
|
7221
7221
|
await runGet(ctx, ref, options);
|
|
7222
7222
|
});
|
|
@@ -8886,7 +8886,7 @@ function isLessonFresh(root, manifest, id, profile, lang, courseRules) {
|
|
|
8886
8886
|
// src/commands/sync.ts
|
|
8887
8887
|
var SUPPORTED_LANGS = ["en", "pl"];
|
|
8888
8888
|
function registerSyncCommand(cli) {
|
|
8889
|
-
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>", "Select course ID or slug (default: project edition or API recommendation)").option("--tool <tool>", "AI coding tool (claude-code, cursor, copilot, codex, devin-desktop, gemini, kiro, 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) => {
|
|
8889
|
+
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>", "Select course ID or slug (default: project edition or API recommendation)").option("--tool <tool>", "AI coding tool (claude-code, cursor, copilot, codex, devin-desktop, factory, gemini, kiro, 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) => {
|
|
8890
8890
|
const ctx = resolveContext(options);
|
|
8891
8891
|
await runSync(ctx, options);
|
|
8892
8892
|
});
|