@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.
Files changed (3) hide show
  1. package/README.md +18 -3
  2. package/dist/index.mjs +35 -35
  3. 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.25.2",
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 import_proper_lockfile2 = __toESM(require_proper_lockfile(), 1);
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 import_proper_lockfile2.default.lock(lockTarget, {
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
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@przeprogramowani/10x-cli",
3
- "version": "1.25.2",
3
+ "version": "1.26.0",
4
4
  "description": "Open-source CLI for 10xDevs course content",
5
5
  "repository": {
6
6
  "type": "git",