@keystrokehq/cli 0.0.115 → 0.0.116

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/dist/index.mjs CHANGED
@@ -8,11 +8,11 @@ import Conf from "conf";
8
8
  import { homedir, platform, tmpdir } from "node:os";
9
9
  import { basename, dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
10
10
  import { confirm, input, select } from "@inquirer/prompts";
11
- import { existsSync, mkdirSync, mkdtempSync, readFileSync, readdirSync, rmSync, statSync, unlinkSync, writeFileSync } from "node:fs";
11
+ import { existsSync, lstatSync, mkdirSync, mkdtempSync, readFileSync, readdirSync, rmSync, statSync, unlinkSync, writeFileSync } from "node:fs";
12
12
  import { spawn, spawnSync } from "node:child_process";
13
13
  import { createHash } from "node:crypto";
14
14
  import { pathToFileURL } from "node:url";
15
- import { access, copyFile, cp, mkdir, readFile, readdir, stat, symlink, unlink, writeFile } from "node:fs/promises";
15
+ import { access, copyFile, cp, lstat, mkdir, readFile, readdir, rm, stat, symlink, unlink, writeFile } from "node:fs/promises";
16
16
  //#region ../../node_modules/.pnpm/ky@2.0.2/node_modules/ky/distribution/errors/KyError.js
17
17
  /**
18
18
  Base class for all Ky-specific errors. `HTTPError`, `NetworkError`, `TimeoutError`, and `ForceRetryError` extend this class.
@@ -9916,7 +9916,7 @@ async function applyPlaygroundManifest(targetDir, projectName, monorepoRoot) {
9916
9916
  await writeFile(tsconfigPath, `${JSON.stringify(tsconfig, null, 2)}\n`);
9917
9917
  }
9918
9918
  //#endregion
9919
- //#region src/init/resolve-skills-bundle-dir.ts
9919
+ //#region src/skills/resolve-skills-bundle-dir.ts
9920
9920
  const SKILLS_BUNDLE = "skills-bundle";
9921
9921
  function resolveSkillsBundleDir(fromModuleUrl = import.meta.url) {
9922
9922
  const cliRoot = resolveCliRoot(fromModuleUrl);
@@ -9930,23 +9930,50 @@ function resolveSkillsBundleDir(fromModuleUrl = import.meta.url) {
9930
9930
  throw new Error("Bundled keystroke skills not found — run `pnpm --filter @keystrokehq/cli build`");
9931
9931
  }
9932
9932
  //#endregion
9933
- //#region src/init/scaffold-bundled-skills.ts
9934
- async function linkPath(target, linkPath, kind) {
9933
+ //#region src/skills/sync-bundled-skills.ts
9934
+ const BUNDLE_VERSION_FILENAME = ".bundle-version";
9935
+ function agentsSkillsDir(projectRoot) {
9936
+ return join(projectRoot, ".agents", "skills");
9937
+ }
9938
+ function bundleVersionPath(projectRoot) {
9939
+ return join(agentsSkillsDir(projectRoot), BUNDLE_VERSION_FILENAME);
9940
+ }
9941
+ async function readBundleVersion(projectRoot) {
9942
+ try {
9943
+ return (await readFile(bundleVersionPath(projectRoot), "utf8")).trim();
9944
+ } catch {
9945
+ return null;
9946
+ }
9947
+ }
9948
+ async function ensureSymlink(target, linkPath, kind) {
9935
9949
  await mkdir(dirname(linkPath), { recursive: true });
9950
+ await rm(linkPath, {
9951
+ recursive: true,
9952
+ force: true
9953
+ });
9936
9954
  await symlink(target, linkPath, platform() === "win32" && kind === "dir" ? "junction" : kind);
9937
9955
  }
9938
- /** Copy bundled skills into `.agents/skills/` and scaffold root agent docs. */
9939
- async function scaffoldBundledSkills(targetDir, fromModuleUrl = import.meta.url) {
9956
+ /** Overwrites `.agents/skills/`, AGENTS.md, and recreates `.claude` symlinks from the CLI bundle. */
9957
+ async function syncBundledSkills(projectRoot, fromModuleUrl = import.meta.url) {
9940
9958
  const bundleDir = resolveSkillsBundleDir(fromModuleUrl);
9941
- const agentsSkillsDir = join(targetDir, ".agents", "skills");
9942
- const agentsMdPath = join(targetDir, "AGENTS.md");
9943
- await mkdir(agentsSkillsDir, { recursive: true });
9944
- await cp(join(bundleDir, "skills"), agentsSkillsDir, { recursive: true });
9959
+ const skillsDir = agentsSkillsDir(projectRoot);
9960
+ const agentsMdPath = join(projectRoot, "AGENTS.md");
9961
+ await mkdir(join(projectRoot, ".agents"), { recursive: true });
9962
+ if ((await lstat(skillsDir).catch(() => null))?.isSymbolicLink()) {
9963
+ await rm(skillsDir, { force: true });
9964
+ await cp(join(bundleDir, "skills"), skillsDir, { recursive: true });
9965
+ } else {
9966
+ await mkdir(skillsDir, { recursive: true });
9967
+ await cp(join(bundleDir, "skills"), skillsDir, {
9968
+ recursive: true,
9969
+ force: true
9970
+ });
9971
+ }
9972
+ if ((await lstat(agentsMdPath).catch(() => null))?.isSymbolicLink()) await rm(agentsMdPath, { force: true });
9945
9973
  await writeFile(agentsMdPath, await readFile(join(bundleDir, "_AGENTS.md"), "utf8"));
9946
- const claudeSkillsLink = join(targetDir, ".claude", "skills");
9947
- await linkPath(relative(dirname(claudeSkillsLink), agentsSkillsDir), claudeSkillsLink, "dir");
9948
- const claudeMdLink = join(targetDir, "CLAUDE.md");
9949
- await linkPath(relative(dirname(claudeMdLink), agentsMdPath), claudeMdLink, "file");
9974
+ await ensureSymlink(relative(dirname(join(projectRoot, ".claude", "skills")), skillsDir), join(projectRoot, ".claude", "skills"), "dir");
9975
+ await ensureSymlink(relative(dirname(join(projectRoot, "CLAUDE.md")), agentsMdPath), join(projectRoot, "CLAUDE.md"), "file");
9976
+ await writeFile(bundleVersionPath(projectRoot), `${readCliVersion()}\n`);
9950
9977
  }
9951
9978
  //#endregion
9952
9979
  //#region src/init/scaffold-empty-src-dirs.ts
@@ -10014,7 +10041,7 @@ async function runInit(options) {
10014
10041
  await scaffoldEmptySrcDirs(targetDir);
10015
10042
  if (playgroundRoot) await applyPlaygroundManifest(targetDir, projectName, playgroundRoot);
10016
10043
  else await scaffoldProjectDotfiles(targetDir);
10017
- await scaffoldBundledSkills(targetDir);
10044
+ await syncBundledSkills(targetDir);
10018
10045
  await copyFile(join(targetDir, ".env.example"), join(targetDir, ".env"));
10019
10046
  if (!options.skipInstall) if (playgroundRoot) {
10020
10047
  installPlaygroundDependencies(targetDir);
@@ -10339,14 +10366,76 @@ function registerConfigCommand(program) {
10339
10366
  });
10340
10367
  }
10341
10368
  //#endregion
10369
+ //#region src/commands/skills.ts
10370
+ function registerSkillsCommand(program) {
10371
+ program.command("skills").description("Manage bundled agent skills").command("sync").description("Overwrite bundled skills and AGENTS.md with the CLI version").option("--dir <path>", "Project directory", process.cwd()).action(async (options) => {
10372
+ try {
10373
+ const root = resolveProjectRoot(options.dir);
10374
+ await syncBundledSkills(root);
10375
+ process.stdout.write(`Synced bundled skills at ${root}\n`);
10376
+ } catch (error) {
10377
+ const message = error instanceof Error ? error.message : "Skills sync failed";
10378
+ process.stderr.write(`${message}\n`);
10379
+ process.exitCode = 1;
10380
+ }
10381
+ });
10382
+ }
10383
+ //#endregion
10384
+ //#region src/project/resolve-keystroke-config-root.ts
10385
+ const KEYSTROKE_CONFIG = "keystroke.config.ts";
10386
+ /** Walk up from `fromDir` and return the directory containing `keystroke.config.ts`, if any. */
10387
+ function resolveKeystrokeConfigRoot(fromDir = process.cwd()) {
10388
+ let dir = resolve(fromDir);
10389
+ while (dir !== dirname(dir)) {
10390
+ if (existsSync(join(dir, KEYSTROKE_CONFIG))) return dir;
10391
+ dir = dirname(dir);
10392
+ }
10393
+ if (existsSync(join(dir, KEYSTROKE_CONFIG))) return dir;
10394
+ return null;
10395
+ }
10396
+ //#endregion
10397
+ //#region src/skills/sync-skills.ts
10398
+ function commandPath(command) {
10399
+ const names = [];
10400
+ for (let current = command; current; current = current.parent) {
10401
+ const name = current.name();
10402
+ if (name && name !== "keystroke") names.unshift(name);
10403
+ }
10404
+ return names;
10405
+ }
10406
+ function shouldSkipAutoSync(command) {
10407
+ if (process.env.CI === "true" || process.env.CI === "1") return true;
10408
+ const path = commandPath(command);
10409
+ if (path[0] === "init") return true;
10410
+ if (path[0] === "skills") return true;
10411
+ return false;
10412
+ }
10413
+ function resolveAutoSyncRoot(command) {
10414
+ return resolveKeystrokeConfigRoot(command.opts().dir ?? process.cwd());
10415
+ }
10416
+ /** Sync bundled skills when stale; no-op when not in a project or already up to date. */
10417
+ async function syncSkills(command) {
10418
+ if (shouldSkipAutoSync(command)) return;
10419
+ const projectRoot = resolveAutoSyncRoot(command);
10420
+ if (!projectRoot) return;
10421
+ const skillsDir = agentsSkillsDir(projectRoot);
10422
+ if (!existsSync(skillsDir)) return;
10423
+ if (lstatSync(skillsDir).isSymbolicLink()) return;
10424
+ const currentVersion = readCliVersion();
10425
+ if (await readBundleVersion(projectRoot) === currentVersion) return;
10426
+ await syncBundledSkills(projectRoot);
10427
+ process.stderr.write(`Synced bundled skills to ${currentVersion}\n`);
10428
+ }
10429
+ //#endregion
10342
10430
  //#region src/program.ts
10343
10431
  function createProgram() {
10344
- const program = new Command().name("keystroke").description("Build, run, and manage keystroke projects").version(readCliVersion()).option("--project <id>", "Platform project id (deploy target or one-off API target)").option("--local", "Force local keystroke server URL").hook("preAction", (thisCommand) => {
10432
+ const program = new Command().name("keystroke").description("Build, run, and manage keystroke projects").version(readCliVersion()).option("--project <id>", "Platform project id (deploy target or one-off API target)").option("--local", "Force local keystroke server URL").hook("preAction", async (thisCommand, actionCommand) => {
10345
10433
  const opts = thisCommand.opts();
10346
10434
  setCliTargetOptions({
10347
10435
  projectId: opts.project,
10348
10436
  local: opts.local
10349
10437
  });
10438
+ await syncSkills(actionCommand);
10350
10439
  });
10351
10440
  registerAuthCommand(program);
10352
10441
  registerConnectCommand(program);
@@ -10355,6 +10444,7 @@ function createProgram() {
10355
10444
  registerCredentialsCommand(program);
10356
10445
  registerHealthCommand(program);
10357
10446
  registerInitCommand(program);
10447
+ registerSkillsCommand(program);
10358
10448
  registerBuildCommand(program);
10359
10449
  registerDeployCommand(program);
10360
10450
  registerDevCommand(program);