@keystrokehq/cli 0.1.24 → 0.1.26

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 (31) hide show
  1. package/dist/index.mjs +216 -153
  2. package/dist/index.mjs.map +1 -1
  3. package/dist/{maybe-auto-update-q5MthdI8.mjs → maybe-auto-update-BDvSKDZp.mjs} +2 -2
  4. package/dist/{maybe-auto-update-q5MthdI8.mjs.map → maybe-auto-update-BDvSKDZp.mjs.map} +1 -1
  5. package/dist/skills-bundle/_AGENTS.md +1 -1
  6. package/dist/templates/hello-world/README.md +3 -2
  7. package/dist/{version-DcR3O1UD.mjs → version-pY9N8XlL.mjs} +1 -2
  8. package/dist/version-pY9N8XlL.mjs.map +1 -0
  9. package/package.json +1 -1
  10. package/dist/skills-bundle/skills/keystroke-actions/SKILL.md +0 -160
  11. package/dist/skills-bundle/skills/keystroke-actions/references/catalog-and-imports.md +0 -71
  12. package/dist/skills-bundle/skills/keystroke-agents/SKILL.md +0 -115
  13. package/dist/skills-bundle/skills/keystroke-agents/references/models.md +0 -23
  14. package/dist/skills-bundle/skills/keystroke-agents/references/tools-mcp-codemode.md +0 -85
  15. package/dist/skills-bundle/skills/keystroke-agents/references/workflows-and-testing.md +0 -26
  16. package/dist/skills-bundle/skills/keystroke-apps/SKILL.md +0 -151
  17. package/dist/skills-bundle/skills/keystroke-apps/references/cli-and-catalog.md +0 -104
  18. package/dist/skills-bundle/skills/keystroke-channels/SKILL.md +0 -66
  19. package/dist/skills-bundle/skills/keystroke-channels/references/slack-setup.md +0 -41
  20. package/dist/skills-bundle/skills/keystroke-cli/SKILL.md +0 -93
  21. package/dist/skills-bundle/skills/keystroke-deploy/SKILL.md +0 -93
  22. package/dist/skills-bundle/skills/keystroke-deploy/references/build-and-full-deploy.md +0 -30
  23. package/dist/skills-bundle/skills/keystroke-deploy/references/filtered-deploy.md +0 -50
  24. package/dist/skills-bundle/skills/keystroke-deploy/references/wip-ignore.md +0 -35
  25. package/dist/skills-bundle/skills/keystroke-files/SKILL.md +0 -43
  26. package/dist/skills-bundle/skills/keystroke-skills/SKILL.md +0 -42
  27. package/dist/skills-bundle/skills/keystroke-triggers/SKILL.md +0 -143
  28. package/dist/skills-bundle/skills/keystroke-workflows/SKILL.md +0 -78
  29. package/dist/skills-bundle/skills/keystroke-workflows/references/authoring.md +0 -168
  30. package/dist/skills-bundle/skills/keystroke-workflows/references/testing.md +0 -138
  31. package/dist/version-DcR3O1UD.mjs.map +0 -1
package/dist/index.mjs CHANGED
@@ -1,15 +1,14 @@
1
1
  #!/usr/bin/env node
2
2
  import { $ as GetCustomAppResponseSchema, $n as slugifyAppName, $t as SlugAvailabilityResponseSchema, A as CreateCustomAppRequestSchema, An as WorkflowRunHooksResponseSchema, At as PollRunResponseSchema, B as CredentialConsumerListQuerySchema, Bt as ProjectReachabilityResponseSchema, C as ConnectAuthorizeUrlResponseSchema, Cn as UploadProjectSourceResponseSchema, Ct as ListProjectsResponseSchema, D as CreateCredentialInstanceBodySchema, Dn as UserPreferencesPatchSchema, Dt as OrganizationSidebarBrandingSchema, E as CreateApiKeyResponseSchema, En as UserAvatarSchema, Et as OrganizationSidebarBrandingPatchSchema, F as CreateProjectRequestSchema, Fn as WorkspaceTriggerFileSchema, Ft as PresignProjectSourceRequestSchema, G as DOCS_QUERY_TOOL, Gt as PromptResponseSchema, H as CredentialInstanceListResponseSchema, Hn as detectProjectPackageManagerFromSnapshot, Ht as ProjectSettingsResponseSchema, I as CreateProjectResponseSchema, In as WorkspaceTriggerListResponseSchema, It as PresignProjectSourceResponseSchema, J as DownloadActiveProjectArtifactResponseSchema, Jn as parseErrorResponse, Jt as QueuedRunResponseSchema, K as DOCS_SEARCH_TOOL, L as CredentialAssignmentListQuerySchema, Ln as WorkspaceTriggerOverviewSchema, Lt as PresignUserAvatarRequestSchema, M as CreateOrganizationRequestSchema, Mn as WorkflowSummaryDetailResponseSchema, Mt as PresignChatAttachmentResponseSchema, N as CreateOrganizationResponseSchema, Nn as WorkflowSummaryListResponseSchema, Nt as PresignOrgLogoRequestSchema, O as CreateCredentialsRequestSchema, On as UserPreferencesSchema, Ot as PROJECT_PULL_STATE_RELATIVE_PATH, P as CreateProjectArtifactResponseSchema, Pn as WorkspaceTriggerDetailSchema, Pt as PresignOrgLogoResponseSchema, Q as GetCredentialResponseSchema, Qn as resolvePublicPlatformOrigin, Qt as SkillSummaryListResponseSchema, R as CredentialAssignmentListResponseSchema, Rn as WorkspaceTriggerRunListResponseSchema, Rt as PresignUserAvatarResponseSchema, S as CompleteProjectArtifactResponseSchema, Sn as UploadProjectSourceManifestRequestSchema, St as ListProjectMetricsResponseSchema, T as CreateApiKeyRequestSchema, Tn as UserAvatarPatchSchema, Tt as OpenApiDiscoverResponseSchema, U as CredentialInstanceRecordSchema, Un as isAcceptableInstallExit, Ut as ProjectSlugAvailabilityResponseSchema, V as CredentialConsumerListResponseSchema, Vn as deriveCustomAppDisplay, Vt as ProjectResponseSchema, Wn as listenPortFromPublicUrl, Wt as PromptInputSchema, X as ErrorResponseSchema, Xn as resolveConnectAppSlug, Xt as RecentResourceListResponseSchema, Y as DownloadActiveProjectSourceResponseSchema, Z as GatewayAttachmentRecordSchema, Zn as resolveDocsMcpUrl, Zt as SkillSummaryDetailResponseSchema, _ as ChannelAccountListResponseSchema, _n as UpdateOrganizationRequestSchema, _t as ListOrganizationMembersResponseSchema, a as AgentSessionDetailResponseSchema, an as StartOAuthConnectionResultSchema, at as HistoryRunListResponseSchema, b as ChannelDirectoryListResponseSchema, bn as UpdateProjectRequestSchema, bt as ListProjectFilesResponseSchema, c as AgentSummaryListResponseSchema, cn as TriggerDetailResponseSchema, ct as InviteProjectMembersRequestSchema, d as AssignCredentialBodySchema, dn as TriggerRunListResponseSchema, dt as ListAgentMemoryFilesResponseSchema, en as StartKeystrokeConnectionInputSchema, et as GraphqlDiscoverResponseSchema, f as BindChannelBodySchema, fn as UpdateChannelBindingBodySchema, ft as ListAgentWorkspaceFilesResponseSchema, g as CatalogAppsPageResponseSchema, gn as UpdateOrganizationMemberResponseSchema, gt as ListOrganizationInvitationsResponseSchema, h as CatalogAppDetailResponseSchema, hn as UpdateOrganizationMemberRequestSchema, ht as ListCredentialsResponseSchema, i as AgentSessionChatStateResponseSchema, in as StartOAuthConnectionInputSchema, it as HistoryRunListQuerySchema, j as CreateCustomAppResponseSchema, jn as WorkflowRunListResponseSchema, jt as PresignChatAttachmentRequestSchema, k as CreateCredentialsResponseSchema, kn as WorkflowRunDetailResponseSchema, kt as PROJECT_REACHABILITY_REQUEST_TIMEOUT_MS, l as AgentTriggerSummaryListResponseSchema, ln as TriggerListResponseSchema, lt as InviteProjectMembersResponseSchema, m as CatalogActionsPageResponseSchema, mn as UpdateCredentialRequestSchema, mt as ListAppsResponseSchema, n as AcceptOrganizationInvitationResponseSchema, nn as StartMcpOAuthConnectionInputSchema, nt as HistoryRunCancelResponseSchema, o as AgentSessionListResponseSchema, on as SubmitMarketingContactRequestSchema, ot as InviteOrganizationMembersRequestSchema, p as CatalogActionDetailResponseSchema, pn as UpdateCredentialInstanceBodySchema, pt as ListApiKeysResponseSchema, q as DeclineOrganizationInvitationResponseSchema, qn as parseAppSlug, qt as QueuedAgentPromptResponseSchema, r as ActiveOrganizationResponseSchema, rn as StartMcpOAuthConnectionResultSchema, rt as HistoryRunDetailResponseSchema, s as AgentSummaryDetailResponseSchema, sn as SubmitTeamRequestRequestSchema, st as InviteOrganizationMembersResponseSchema, t as ACTIVE_ORG_HEADER, tn as StartKeystrokeConnectionResultSchema, tt as HealthResponseSchema, u as AppSlugAvailabilityResponseSchema, un as TriggerRunDetailResponseSchema, v as ChannelConnectionListResponseSchema, vn as UpdateProjectMemberRequestSchema, vt as ListOrganizationsResponseSchema, w as ConnectProvidersResponseSchema, wn as UpsertGatewayAttachmentBodySchema, wt as McpDiscoverResponseSchema, x as ChannelPlatformSchema, xn as UpdateProjectSettingsRequestSchema, xt as ListProjectMembersResponseSchema, y as ChannelConnectionSchema, yn as UpdateProjectMemberResponseSchema, yt as ListProjectDeploymentsResponseSchema, z as CredentialAssignmentRecordSchema, zn as buildConnectDeeplink, zt as ProjectPullStateSchema } from "./dist-BOhrc_Nv.mjs";
3
3
  import { i as packProjectArtifact, n as withMcpReadClient, r as mergeFilteredArtifact, t as mapInParallelBatches } from "./dist-Re6HHSqz.mjs";
4
- import { a as installPlaygroundDependencies, c as createCliConfig, d as getEffectiveApiTarget, f as getPlatformUrl, g as resolvePlatformUrlForWebUrl, i as installDependencies$1, l as getCliConfigDir, m as DEFAULT_PLATFORM_URL, n as buildPlaygroundWorkspace, o as resolvePackageManager, p as getWebUrl, s as resolveCliRoot, t as readCliVersion, u as getConfigDir } from "./version-DcR3O1UD.mjs";
4
+ import { a as installPlaygroundDependencies, c as createCliConfig, d as getEffectiveApiTarget, f as getPlatformUrl, g as resolvePlatformUrlForWebUrl, i as installDependencies$1, l as getCliConfigDir, m as DEFAULT_PLATFORM_URL, n as buildPlaygroundWorkspace, o as resolvePackageManager, p as getWebUrl, s as resolveCliRoot, t as readCliVersion, u as getConfigDir } from "./version-pY9N8XlL.mjs";
5
5
  import { createRequire } from "node:module";
6
6
  import { Command } from "commander";
7
- import { platform } from "node:os";
8
7
  import { basename, dirname, isAbsolute, join, relative, resolve } from "node:path";
9
8
  import { Entry } from "@napi-rs/keyring";
10
9
  import { confirm, input, select } from "@inquirer/prompts";
11
10
  import { existsSync, lstatSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from "node:fs";
12
- import { access, copyFile, cp, lstat, mkdir, readFile, readdir, rm, stat, symlink, unlink, writeFile } from "node:fs/promises";
11
+ import { access, copyFile, lstat, mkdir, readFile, readdir, rm, stat, symlink, unlink, writeFile } from "node:fs/promises";
13
12
  import { spawn, spawnSync } from "node:child_process";
14
13
  import { pathToFileURL } from "node:url";
15
14
  //#region ../../node_modules/.pnpm/ky@2.0.2/node_modules/ky/distribution/errors/KyError.js
@@ -4685,7 +4684,7 @@ async function resolveProjectRef(platform, ref) {
4685
4684
  if (looksLikeUuid(ref)) try {
4686
4685
  return await platform.projects.get(ref);
4687
4686
  } catch (error) {
4688
- if (error instanceof PlatformError && error.status === 404) throw new Error(`Project ${ref} not found in the active organization. Run \`keystroke project list\`, then \`keystroke config use project <slug>\`.`);
4687
+ if (error instanceof PlatformError && error.status === 404) throw new Error(`Project ${ref} not found in the active organization. Run \`keystroke project list\`, then \`keystroke project link --project <slug>\`.`);
4689
4688
  throw error;
4690
4689
  }
4691
4690
  const match = (await platform.projects.list()).find((project) => project.slug === ref);
@@ -4847,6 +4846,78 @@ function readDevSession(configDir = getCliConfigDir()) {
4847
4846
  }
4848
4847
  }
4849
4848
  //#endregion
4849
+ //#region src/project/resolve-keystroke-config-root.ts
4850
+ const KEYSTROKE_CONFIG$2 = "keystroke.config.ts";
4851
+ /** Walk up from `fromDir` and return the directory containing `keystroke.config.ts`, if any. */
4852
+ function resolveKeystrokeConfigRoot(fromDir = process.cwd()) {
4853
+ let dir = resolve(fromDir);
4854
+ while (dir !== dirname(dir)) {
4855
+ if (existsSync(join(dir, KEYSTROKE_CONFIG$2))) return dir;
4856
+ dir = dirname(dir);
4857
+ }
4858
+ if (existsSync(join(dir, KEYSTROKE_CONFIG$2))) return dir;
4859
+ return null;
4860
+ }
4861
+ //#endregion
4862
+ //#region src/project/read-project-config.ts
4863
+ const KEYSTROKE_CONFIG$1 = "keystroke.config.ts";
4864
+ /** Remove line and block comments before scanning config literals. */
4865
+ function stripConfigComments(source) {
4866
+ return source.replace(/\/\/[^\n]*/g, "").replace(/\/\*[\s\S]*?\*\//g, "");
4867
+ }
4868
+ /** Mask comments while preserving string length so match indices stay aligned. */
4869
+ function stripConfigCommentsPreservingLength(source) {
4870
+ return source.replace(/\/\/[^\n]*/g, (match) => " ".repeat(match.length)).replace(/\/\*[\s\S]*?\*\//g, (match) => " ".repeat(match.length));
4871
+ }
4872
+ function readStringLiteral(source, key) {
4873
+ const stripped = stripConfigComments(source);
4874
+ const pattern = new RegExp(`\\b${key}\\s*:\\s*(['"])([^'"]*)\\1`);
4875
+ return stripped.match(pattern)?.[2]?.trim() || void 0;
4876
+ }
4877
+ function parseLinkedProjectConfig(source) {
4878
+ const project = readStringLiteral(source, "project");
4879
+ const organization = readStringLiteral(source, "organization");
4880
+ return {
4881
+ ...project ? { project } : {},
4882
+ ...organization ? { organization } : {}
4883
+ };
4884
+ }
4885
+ /** Read optional project/organization slugs from keystroke.config.ts without executing it. */
4886
+ function readLinkedProjectConfig(fromDir = process.cwd()) {
4887
+ const root = resolveKeystrokeConfigRoot(fromDir);
4888
+ if (!root) return {};
4889
+ return parseLinkedProjectConfig(readFileSync(join(root, KEYSTROKE_CONFIG$1), "utf8"));
4890
+ }
4891
+ //#endregion
4892
+ //#region src/target-options.ts
4893
+ let targetOptions = {};
4894
+ function setCliTargetOptions(options) {
4895
+ targetOptions = options;
4896
+ }
4897
+ function getCliTargetOptions() {
4898
+ return targetOptions;
4899
+ }
4900
+ //#endregion
4901
+ //#region src/resolve-project-target.ts
4902
+ function resolveLinkedProjectRef(fromDir = process.cwd()) {
4903
+ return readLinkedProjectConfig(fromDir).project;
4904
+ }
4905
+ /** Activate the organization slug from keystroke.config.ts when present. */
4906
+ async function ensureLinkedOrganization(config, fromDir = process.cwd()) {
4907
+ const linked = readLinkedProjectConfig(fromDir);
4908
+ if (!linked.organization) return;
4909
+ await activateOrganization(config, resolveOrganizationRef(await listOrganizations(config), linked.organization).organization.id);
4910
+ }
4911
+ async function resolveActiveProject(config, platform, fromDir = process.cwd()) {
4912
+ const ref = getCliTargetOptions().projectId ?? readLinkedProjectConfig(fromDir).project;
4913
+ if (!ref) return;
4914
+ await ensureLinkedOrganization(config, fromDir);
4915
+ return resolveProjectRef(platform ?? createCliPlatformClient(config), ref);
4916
+ }
4917
+ async function resolveActiveProjectId(config, fromDir = process.cwd()) {
4918
+ return (await resolveActiveProject(config, void 0, fromDir))?.id;
4919
+ }
4920
+ //#endregion
4850
4921
  //#region src/resolve-api-target.ts
4851
4922
  /** Standalone local server default project id — keep in sync with @keystrokehq/database. */
4852
4923
  const DEFAULT_LOCAL_PROJECT_ID = "default";
@@ -4879,6 +4950,7 @@ function resolveOrgPlatformTarget(config) {
4879
4950
  };
4880
4951
  }
4881
4952
  async function resolvePlatformTarget(config, projectRef) {
4953
+ await ensureLinkedOrganization(config);
4882
4954
  const platform = createCliPlatformClient(config);
4883
4955
  let project;
4884
4956
  try {
@@ -4906,20 +4978,11 @@ async function resolveApiTarget(config, options = {}) {
4906
4978
  if (options.projectId) return resolvePlatformTarget(config, options.projectId);
4907
4979
  if (getEffectiveApiTarget(config) === "local") return resolveLocalTarget(options);
4908
4980
  if (!options.projectScoped) return resolveOrgPlatformTarget(config);
4909
- const projectRef = options.projectId ?? config.get("activeProjectId");
4910
- if (!projectRef) throw new Error("No project selected. Pass `--project <slug>` or run `keystroke config use project <slug>`.");
4981
+ const projectRef = options.projectId ?? readLinkedProjectConfig().project;
4982
+ if (!projectRef) throw new Error("No project selected. Pass `--project <slug>` or run `keystroke project link --project <slug>`.");
4911
4983
  return resolvePlatformTarget(config, projectRef);
4912
4984
  }
4913
4985
  //#endregion
4914
- //#region src/target-options.ts
4915
- let targetOptions = {};
4916
- function setCliTargetOptions(options) {
4917
- targetOptions = options;
4918
- }
4919
- function getCliTargetOptions() {
4920
- return targetOptions;
4921
- }
4922
- //#endregion
4923
4986
  //#region src/errors/example-input.ts
4924
4987
  function exampleValueFromValidationMessage(message) {
4925
4988
  if (/expected number/i.test(message)) return 0;
@@ -5089,18 +5152,6 @@ async function requireAdminRole(config) {
5089
5152
  if (!active || active.role !== "owner" && active.role !== "admin") throw new Error("--admin requires org owner or admin");
5090
5153
  }
5091
5154
  //#endregion
5092
- //#region src/resolve-project-target.ts
5093
- async function resolveActiveProject(config, platform) {
5094
- const ref = getCliTargetOptions().projectId ?? config.get("activeProjectId");
5095
- if (!ref) return;
5096
- const client = platform ?? createCliPlatformClient(config);
5097
- if (!getCliTargetOptions().projectId && looksLikeUuid(ref)) return resolveProjectRef(client, ref);
5098
- return resolveProjectRef(client, ref);
5099
- }
5100
- async function resolveActiveProjectId(config) {
5101
- return (await resolveActiveProject(config))?.id;
5102
- }
5103
- //#endregion
5104
5155
  //#region src/commands/agent/list.ts
5105
5156
  function registerAgentListCommand(agent) {
5106
5157
  agent.command("list").description("List agents in the active organization").option("--admin", "List all org resources (org owner or admin only)").action((options) => runCliCommand("List agents failed", async () => {
@@ -5129,7 +5180,8 @@ function registerAgentPromptCommand(agent) {
5129
5180
  }
5130
5181
  //#endregion
5131
5182
  //#region src/commands/project/run-project.ts
5132
- async function withActivePlatformClient(config, fn) {
5183
+ async function withActivePlatformClient(config, fn, fromDir = process.cwd(), applyLinkedOrganization = true) {
5184
+ if (applyLinkedOrganization) await ensureLinkedOrganization(config, fromDir);
5133
5185
  const platform = createCliPlatformClient(config);
5134
5186
  let active = await platform.organizations.getActive();
5135
5187
  if (!active) active = await selectActiveOrganization(config);
@@ -5140,7 +5192,7 @@ function writeInactiveDeployHints(projects) {
5140
5192
  const bin = cliBinaryName();
5141
5193
  for (const project of projects) {
5142
5194
  if (project.status !== "inactive") continue;
5143
- process.stderr.write(`Project ${formatProjectLabel(project)} is inactive. Deploy with: ${bin} deploy --project ${formatProjectLabel(project)}\n`);
5195
+ process.stderr.write(`Project ${formatProjectLabel(project)} is inactive. Deploy with: ${bin} project link --project ${formatProjectLabel(project)}, then ${bin} deploy\n`);
5144
5196
  }
5145
5197
  }
5146
5198
  async function runProjectList(config, options = {}) {
@@ -5544,19 +5596,6 @@ function registerAppShowCommand(app) {
5544
5596
  }, void 0, { orgScoped: true }));
5545
5597
  }
5546
5598
  //#endregion
5547
- //#region src/project/resolve-keystroke-config-root.ts
5548
- const KEYSTROKE_CONFIG = "keystroke.config.ts";
5549
- /** Walk up from `fromDir` and return the directory containing `keystroke.config.ts`, if any. */
5550
- function resolveKeystrokeConfigRoot(fromDir = process.cwd()) {
5551
- let dir = resolve(fromDir);
5552
- while (dir !== dirname(dir)) {
5553
- if (existsSync(join(dir, KEYSTROKE_CONFIG))) return dir;
5554
- dir = dirname(dir);
5555
- }
5556
- if (existsSync(join(dir, KEYSTROKE_CONFIG))) return dir;
5557
- return null;
5558
- }
5559
- //#endregion
5560
5599
  //#region src/commands/app/generate-app-stub.ts
5561
5600
  function slugPartToExportName(part) {
5562
5601
  return part.split(/[-_]+/).filter(Boolean).map((segment, index) => index === 0 ? segment.charAt(0).toLowerCase() + segment.slice(1) : segment.charAt(0).toUpperCase() + segment.slice(1)).join("");
@@ -6315,6 +6354,7 @@ async function runDeploy(options) {
6315
6354
  const root = resolveProjectRoot(options.dir);
6316
6355
  const config = createCliConfig();
6317
6356
  const client = createCliPlatformClient(config);
6357
+ await ensureLinkedOrganization(config, root);
6318
6358
  let active = await client.organizations.getActive();
6319
6359
  if (!active) active = await selectActiveOrganization(config);
6320
6360
  client.setActiveOrganizationId(active.organization.id);
@@ -6339,7 +6379,6 @@ async function runDeploy(options) {
6339
6379
  while (Date.now() < deadline) {
6340
6380
  const current = await client.projects.get(project.id);
6341
6381
  if (current.status === "active") {
6342
- config.set("activeProjectId", project.id);
6343
6382
  config.set("apiTarget", "platform");
6344
6383
  process.stdout.write(`Project active${current.baseUrl ? `: ${current.baseUrl}` : ""}\n`);
6345
6384
  return;
@@ -6352,8 +6391,8 @@ async function runDeploy(options) {
6352
6391
  function registerDeployCommand(program) {
6353
6392
  program.command("deploy").description("Build, upload, and deploy the project to the platform").option("--dir <path>", "Project directory", process.cwd()).option("--filter <entry>", "Build and deploy only matching module entry keys (repeatable)", (value, previous) => [...previous, value], []).action(async (options) => {
6354
6393
  try {
6355
- const projectId = await resolveActiveProjectId(createCliConfig());
6356
- if (!projectId) throw new Error("Usage: keystroke deploy --project <slug>");
6394
+ const projectId = await resolveActiveProjectId(createCliConfig(), resolveProjectRoot(options.dir));
6395
+ if (!projectId) throw new Error("Usage: keystroke deploy --project <slug> (or run `keystroke project link --project <slug>`)");
6357
6396
  await runDeploy({
6358
6397
  dir: options.dir,
6359
6398
  projectId,
@@ -6774,7 +6813,7 @@ const INIT_CATALOG_VERSIONS = {
6774
6813
  vitest: "^4.1.7",
6775
6814
  "@types/node": "^25.9.1"
6776
6815
  };
6777
- const INIT_KEYSTROKE_VERSION = "^0.1.22";
6816
+ const INIT_KEYSTROKE_VERSION = "^0.1.23";
6778
6817
  //#endregion
6779
6818
  //#region src/init/copy-template.ts
6780
6819
  function renderTemplate(content, variables) {
@@ -6864,6 +6903,26 @@ async function applyPlaygroundManifest(targetDir, projectName, monorepoRoot) {
6864
6903
  await writeFile(tsconfigPath, `${JSON.stringify(tsconfig, null, 2)}\n`);
6865
6904
  }
6866
6905
  //#endregion
6906
+ //#region src/skills/agents-guide-version.ts
6907
+ const BUNDLE_VERSION_FILENAME = ".bundle-version";
6908
+ const LEGACY_BUNDLE_VERSION_RELATIVE_PATH = join("skills", BUNDLE_VERSION_FILENAME);
6909
+ function agentsGuideDir(projectRoot) {
6910
+ return join(projectRoot, ".agents");
6911
+ }
6912
+ function bundleVersionPath(projectRoot) {
6913
+ return join(agentsGuideDir(projectRoot), BUNDLE_VERSION_FILENAME);
6914
+ }
6915
+ async function readBundleVersion(projectRoot) {
6916
+ for (const path of [bundleVersionPath(projectRoot), join(agentsGuideDir(projectRoot), LEGACY_BUNDLE_VERSION_RELATIVE_PATH)]) try {
6917
+ return (await readFile(path, "utf8")).trim();
6918
+ } catch {}
6919
+ return null;
6920
+ }
6921
+ async function writeBundleVersion(projectRoot) {
6922
+ await mkdir(agentsGuideDir(projectRoot), { recursive: true });
6923
+ await writeFile(bundleVersionPath(projectRoot), `${readCliVersion()}\n`);
6924
+ }
6925
+ //#endregion
6867
6926
  //#region src/skills/resolve-skills-bundle-dir.ts
6868
6927
  const SKILLS_BUNDLE = "skills-bundle";
6869
6928
  function resolveSkillsBundleDir(fromModuleUrl = import.meta.url) {
@@ -6875,53 +6934,59 @@ function resolveSkillsBundleDir(fromModuleUrl = import.meta.url) {
6875
6934
  join(cliRoot, SKILLS_BUNDLE)
6876
6935
  ];
6877
6936
  for (const candidate of candidates) if (existsSync(join(candidate, "_AGENTS.md"))) return candidate;
6878
- throw new Error("Bundled keystroke skills not found — run `pnpm --filter @keystrokehq/cli build`");
6937
+ throw new Error("Bundled AGENTS guide not found — run `pnpm --filter @keystrokehq/cli build`");
6879
6938
  }
6880
6939
  //#endregion
6881
- //#region src/skills/sync-bundled-skills.ts
6882
- const BUNDLE_VERSION_FILENAME = ".bundle-version";
6883
- function agentsSkillsDir(projectRoot) {
6884
- return join(projectRoot, ".agents", "skills");
6885
- }
6886
- function bundleVersionPath(projectRoot) {
6887
- return join(agentsSkillsDir(projectRoot), BUNDLE_VERSION_FILENAME);
6888
- }
6889
- async function readBundleVersion(projectRoot) {
6890
- try {
6891
- return (await readFile(bundleVersionPath(projectRoot), "utf8")).trim();
6892
- } catch {
6893
- return null;
6894
- }
6895
- }
6896
- async function ensureSymlink(target, linkPath, kind) {
6940
+ //#region src/skills/scaffold-agents-guide.ts
6941
+ async function ensureSymlink(target, linkPath) {
6897
6942
  await mkdir(dirname(linkPath), { recursive: true });
6898
6943
  await rm(linkPath, {
6899
6944
  recursive: true,
6900
6945
  force: true
6901
6946
  });
6902
- await symlink(target, linkPath, platform() === "win32" && kind === "dir" ? "junction" : kind);
6947
+ await symlink(target, linkPath, "file");
6903
6948
  }
6904
- /** Overwrites `.agents/skills/`, AGENTS.md, and recreates `.claude` symlinks from the CLI bundle. */
6905
- async function syncBundledSkills(projectRoot, fromModuleUrl = import.meta.url) {
6949
+ /** Writes AGENTS.md from the CLI bundle and recreates the CLAUDE.md symlink. */
6950
+ async function scaffoldAgentsGuide(projectRoot, fromModuleUrl = import.meta.url) {
6906
6951
  const bundleDir = resolveSkillsBundleDir(fromModuleUrl);
6907
- const skillsDir = agentsSkillsDir(projectRoot);
6908
6952
  const agentsMdPath = join(projectRoot, "AGENTS.md");
6909
- await mkdir(join(projectRoot, ".agents"), { recursive: true });
6910
- if ((await lstat(skillsDir).catch(() => null))?.isSymbolicLink()) {
6911
- await rm(skillsDir, { force: true });
6912
- await cp(join(bundleDir, "skills"), skillsDir, { recursive: true });
6913
- } else {
6914
- await mkdir(skillsDir, { recursive: true });
6915
- await cp(join(bundleDir, "skills"), skillsDir, {
6916
- recursive: true,
6917
- force: true
6918
- });
6919
- }
6920
6953
  if ((await lstat(agentsMdPath).catch(() => null))?.isSymbolicLink()) await rm(agentsMdPath, { force: true });
6921
6954
  await writeFile(agentsMdPath, await readFile(join(bundleDir, "_AGENTS.md"), "utf8"));
6922
- await ensureSymlink(relative(dirname(join(projectRoot, ".claude", "skills")), skillsDir), join(projectRoot, ".claude", "skills"), "dir");
6923
- await ensureSymlink(relative(dirname(join(projectRoot, "CLAUDE.md")), agentsMdPath), join(projectRoot, "CLAUDE.md"), "file");
6924
- await writeFile(bundleVersionPath(projectRoot), `${readCliVersion()}\n`);
6955
+ await ensureSymlink(relative(dirname(join(projectRoot, "CLAUDE.md")), agentsMdPath), join(projectRoot, "CLAUDE.md"));
6956
+ await writeBundleVersion(projectRoot);
6957
+ }
6958
+ //#endregion
6959
+ //#region src/project/write-project-config.ts
6960
+ const KEYSTROKE_CONFIG = "keystroke.config.ts";
6961
+ function upsertStringLiteral(source, key, value) {
6962
+ const stripped = stripConfigCommentsPreservingLength(source);
6963
+ const entryPattern = new RegExp(`\\b${key}\\s*:\\s*(['"])[^'"]*\\1,?\\s*\\n?`);
6964
+ const entry = value === void 0 ? void 0 : ` ${key}: "${value}",\n`;
6965
+ const match = stripped.match(entryPattern);
6966
+ if (match?.index !== void 0) {
6967
+ const start = match.index;
6968
+ const end = start + match[0].length;
6969
+ if (value === void 0) return source.slice(0, start) + source.slice(end);
6970
+ return source.slice(0, start) + entry + source.slice(end);
6971
+ }
6972
+ if (value === void 0) return source;
6973
+ const defineConfigPattern = /defineConfig\(\{\s*\n?/;
6974
+ if (defineConfigPattern.test(source)) return source.replace(defineConfigPattern, `defineConfig({\n${entry}`);
6975
+ throw new Error(`Could not find defineConfig({ ... }) in ${KEYSTROKE_CONFIG}`);
6976
+ }
6977
+ function upsertLinkedProjectConfig(source, input) {
6978
+ let next = source;
6979
+ if (input.project !== void 0) next = upsertStringLiteral(next, "project", input.project);
6980
+ if (input.organization !== void 0) next = upsertStringLiteral(next, "organization", input.organization);
6981
+ return next;
6982
+ }
6983
+ /** Write project/organization slugs into keystroke.config.ts. */
6984
+ function writeLinkedProjectConfig(input, fromDir = process.cwd()) {
6985
+ const root = resolveKeystrokeConfigRoot(fromDir);
6986
+ if (!root) throw new Error(`No ${KEYSTROKE_CONFIG} found — run from a keystroke project directory`);
6987
+ const configPath = join(root, KEYSTROKE_CONFIG);
6988
+ writeFileSync(configPath, upsertLinkedProjectConfig(readFileSync(configPath, "utf8"), input));
6989
+ return configPath;
6925
6990
  }
6926
6991
  //#endregion
6927
6992
  //#region src/init/scaffold-empty-src-dirs.ts
@@ -7022,8 +7087,12 @@ async function runInit(options) {
7022
7087
  }
7023
7088
  if (playgroundRoot) await applyPlaygroundManifest(targetDir, projectName, playgroundRoot);
7024
7089
  else await scaffoldProjectDotfiles(targetDir);
7025
- await syncBundledSkills(targetDir);
7090
+ await scaffoldAgentsGuide(targetDir);
7026
7091
  await copyFile(join(targetDir, ".env.example"), join(targetDir, ".env"));
7092
+ if (options.project || options.organization) writeLinkedProjectConfig({
7093
+ ...options.project ? { project: options.project } : {},
7094
+ ...options.organization ? { organization: options.organization } : {}
7095
+ }, targetDir);
7027
7096
  if (!options.skipInstall) if (playgroundRoot) {
7028
7097
  installPlaygroundDependencies(targetDir);
7029
7098
  buildPlaygroundWorkspace(playgroundRoot);
@@ -7036,7 +7105,7 @@ async function runInit(options) {
7036
7105
  //#endregion
7037
7106
  //#region src/commands/init.ts
7038
7107
  function registerInitCommand(program) {
7039
- program.command("init").description("Create a new keystroke project from a template").argument("[directory]", "Directory to create the project in").option("--name <name>", "Project name (kebab-case)").option("--template <name>", "Template to use", "hello-world").option("-y, --yes", "Skip prompts (headless mode for scripts and AI agents)").option("--skip-install", "Skip dependency installation").option("--pm <manager>", "Package manager: npm, pnpm, yarn, or bun").option("--no-example", "Scaffold without the example action, agent, and workflow").action(async (directory, options) => {
7108
+ program.command("init").description("Create a new keystroke project from a template").argument("[directory]", "Directory to create the project in").option("--name <name>", "Project name (kebab-case)").option("--template <name>", "Template to use", "hello-world").option("-y, --yes", "Skip prompts (headless mode for scripts and AI agents)").option("--skip-install", "Skip dependency installation").option("--pm <manager>", "Package manager: npm, pnpm, yarn, or bun").option("--no-example", "Scaffold without the example action, agent, and workflow").option("--project <slug>", "Platform project slug to write into keystroke.config.ts").option("--organization <slug>", "Platform organization slug to write into keystroke.config.ts").action(async (directory, options) => {
7040
7109
  try {
7041
7110
  const result = await runInit({
7042
7111
  directory,
@@ -7046,14 +7115,16 @@ function registerInitCommand(program) {
7046
7115
  skipInstall: options.skipInstall,
7047
7116
  packageManager: options.pm,
7048
7117
  noExample: options.example === false,
7118
+ project: options.project,
7119
+ organization: options.organization,
7049
7120
  version: readCliVersion()
7050
7121
  });
7051
7122
  process.stdout.write(`Created keystroke project "${result.projectName}" at ${result.targetDir}\n`);
7052
7123
  process.stdout.write("\nNext steps:\n");
7053
7124
  process.stdout.write(` cd ${result.targetDir}\n`);
7054
7125
  process.stdout.write(" keystroke auth login # once\n");
7055
- process.stdout.write(" keystroke project list # pick a project (or: project create)\n");
7056
- process.stdout.write(" keystroke deploy --project <id> # build + ship src/ to the platform\n");
7126
+ if (!options.project) process.stdout.write(" keystroke project link --project <slug> # link this directory to a platform project\n");
7127
+ process.stdout.write(" keystroke deploy # build + ship src/ to the platform\n");
7057
7128
  } catch (error) {
7058
7129
  const message = error instanceof Error ? error.message : "Init failed";
7059
7130
  process.stderr.write(`${message}\n`);
@@ -7062,15 +7133,6 @@ function registerInitCommand(program) {
7062
7133
  });
7063
7134
  }
7064
7135
  //#endregion
7065
- //#region src/active-project.ts
7066
- function clearActiveProjectIfMatches(config, project) {
7067
- const activeProjectId = config.get("activeProjectId");
7068
- if (activeProjectId === project.id || activeProjectId === project.slug) config.delete("activeProjectId");
7069
- }
7070
- function clearActiveProject(config) {
7071
- if (config.get("activeProjectId") !== void 0) config.delete("activeProjectId");
7072
- }
7073
- //#endregion
7074
7136
  //#region src/commands/organization/run-organization.ts
7075
7137
  async function runOrganizationUpdate(config, input) {
7076
7138
  await withActivePlatformClient(config, async (platform) => {
@@ -7112,7 +7174,6 @@ async function runOrganizationLeave(config) {
7112
7174
  await platform.members.removeOrganizationMember(user.id);
7113
7175
  platform.setActiveOrganizationId(null);
7114
7176
  config.delete("activeOrganizationId");
7115
- clearActiveProject(config);
7116
7177
  process.stdout.write(`${JSON.stringify({ left: user.id }, null, 2)}\n`);
7117
7178
  });
7118
7179
  }
@@ -7185,9 +7246,23 @@ function registerOrganizationCommand(program) {
7185
7246
  });
7186
7247
  }
7187
7248
  //#endregion
7249
+ //#region src/commands/project/run-project-link.ts
7250
+ async function runProjectLink(config, projectRef, fromDir = process.cwd()) {
7251
+ await withActivePlatformClient(config, async (platform) => {
7252
+ const project = await resolveProjectRef(platform, projectRef);
7253
+ const membership = (await listOrganizations(config)).find((entry) => entry.organization.id === project.organizationId);
7254
+ if (!membership) throw new Error(`Organization for project ${formatProjectLabel(project)} was not found or you do not have access`);
7255
+ const configPath = writeLinkedProjectConfig({
7256
+ project: project.slug,
7257
+ organization: membership.organization.slug
7258
+ }, fromDir);
7259
+ process.stdout.write(`Linked ${configPath} to ${membership.organization.slug}/${formatProjectLabel(project)}\nDeploy with: ${cliBinaryName()} deploy\n`);
7260
+ }, fromDir, false);
7261
+ }
7262
+ //#endregion
7188
7263
  //#region src/commands/project/run-project-members.ts
7189
7264
  async function resolveMembersProject(platform, config, projectRef) {
7190
- const ref = projectRef ?? getCliTargetOptions().projectId ?? config.get("activeProjectId");
7265
+ const ref = projectRef ?? getCliTargetOptions().projectId ?? readLinkedProjectConfig().project;
7191
7266
  if (!ref) throw new Error("Usage: keystroke --project <slug> project members <command>");
7192
7267
  return resolveProjectRef(platform, ref);
7193
7268
  }
@@ -7227,7 +7302,6 @@ async function runProjectMembersLeave(config, options) {
7227
7302
  await withActivePlatformClient(config, async (platform) => {
7228
7303
  const project = await resolveMembersProject(platform, config, options.projectRef);
7229
7304
  await platform.members.leaveProject(project.id);
7230
- clearActiveProjectIfMatches(config, project);
7231
7305
  process.stdout.write(`${JSON.stringify({ left: project.id }, null, 2)}\n`);
7232
7306
  });
7233
7307
  }
@@ -7241,7 +7315,7 @@ function parseProjectRole$1(role) {
7241
7315
  throw new Error("Role must be admin or builder");
7242
7316
  }
7243
7317
  function resolveProjectRefOption$1(config, optionsProject) {
7244
- return optionsProject ?? getCliTargetOptions().projectId ?? config.get("activeProjectId");
7318
+ return optionsProject ?? getCliTargetOptions().projectId ?? readLinkedProjectConfig().project;
7245
7319
  }
7246
7320
  function projectRefInput$1(config, optionsProject) {
7247
7321
  const projectRef = resolveProjectRefOption$1(config, optionsProject);
@@ -7307,7 +7381,7 @@ function registerProjectMembersCommand(project) {
7307
7381
  //#endregion
7308
7382
  //#region src/commands/project/run-project-settings.ts
7309
7383
  async function resolveSettingsProject(platform, config, projectRef) {
7310
- const ref = projectRef ?? getCliTargetOptions().projectId ?? config.get("activeProjectId");
7384
+ const ref = projectRef ?? getCliTargetOptions().projectId ?? readLinkedProjectConfig().project;
7311
7385
  if (!ref) throw new Error("Usage: keystroke --project <slug> project settings <command>");
7312
7386
  return resolveProjectRef(platform, ref);
7313
7387
  }
@@ -7329,7 +7403,7 @@ async function runProjectSettingsUpdate(config, input) {
7329
7403
  //#endregion
7330
7404
  //#region src/commands/project/settings.ts
7331
7405
  function resolveProjectRefOption(config, optionsProject) {
7332
- return optionsProject ?? getCliTargetOptions().projectId ?? config.get("activeProjectId");
7406
+ return optionsProject ?? getCliTargetOptions().projectId ?? readLinkedProjectConfig().project;
7333
7407
  }
7334
7408
  function projectRefInput(config, optionsProject) {
7335
7409
  const projectRef = resolveProjectRefOption(config, optionsProject);
@@ -7410,8 +7484,8 @@ function registerProjectCommand(program) {
7410
7484
  project.command("deployments").description("View deployment history for platform projects").command("list").description("List deployment history for a project").option("--project <slug>", "Project slug").action(async (options) => {
7411
7485
  const config = createCliConfig();
7412
7486
  try {
7413
- const projectRef = options.project ?? getCliTargetOptions().projectId ?? config.get("activeProjectId");
7414
- if (!projectRef) throw new Error("Usage: keystroke project deployments list --project <slug>");
7487
+ const projectRef = options.project ?? getCliTargetOptions().projectId ?? resolveLinkedProjectRef();
7488
+ if (!projectRef) throw new Error("Usage: keystroke project deployments list --project <slug> (or run `keystroke project link`)");
7415
7489
  await runProjectDeploymentsList(config, { projectRef });
7416
7490
  } catch (error) {
7417
7491
  process.stderr.write(`${formatCliError(error, "List deployments failed", {
@@ -7421,6 +7495,18 @@ function registerProjectCommand(program) {
7421
7495
  process.exitCode = 1;
7422
7496
  }
7423
7497
  });
7498
+ project.command("link").description("Link this directory to a platform project in keystroke.config.ts").requiredOption("--project <slug>", "Platform project slug").option("--dir <path>", "Project directory", process.cwd()).action(async (options) => {
7499
+ const config = createCliConfig();
7500
+ try {
7501
+ await runProjectLink(config, options.project, options.dir);
7502
+ } catch (error) {
7503
+ process.stderr.write(`${formatCliError(error, "Link project failed", {
7504
+ serverUrl: getPlatformUrl(config),
7505
+ webUrl: getWebUrl(config)
7506
+ })}\n`);
7507
+ process.exitCode = 1;
7508
+ }
7509
+ });
7424
7510
  project.command("create").description("Create a platform project in the active organization").requiredOption("--name <name>", "Project name").option("--description <description>", "Project description").action(async (options) => {
7425
7511
  const config = createCliConfig();
7426
7512
  try {
@@ -7523,7 +7609,7 @@ async function runPull(options) {
7523
7609
  }
7524
7610
  await writeSourceTree(targetDir, source.files);
7525
7611
  installDependencies(targetDir, detectProjectPackageManagerFromSnapshot(source.files));
7526
- await syncBundledSkills(targetDir);
7612
+ await scaffoldAgentsGuide(targetDir);
7527
7613
  await writePullState({
7528
7614
  targetDir,
7529
7615
  projectId: options.projectId,
@@ -7536,7 +7622,7 @@ async function runPull(options) {
7536
7622
  function registerPullCommand(program) {
7537
7623
  program.command("pull").description("Download the active deploy source snapshot into a local directory").option("--dir <path>", "Target directory", process.cwd()).option("--force", "Overwrite an existing local project tree").action(async (options) => runCliCommand("Pull failed", async () => {
7538
7624
  const config = createCliConfig();
7539
- const projectId = await resolveActiveProjectId(config);
7625
+ const projectId = await resolveActiveProjectId(config, resolveProjectRoot(options.dir));
7540
7626
  if (!projectId) throw new Error("Usage: keystroke pull --project <slug>");
7541
7627
  await runPull({
7542
7628
  client: createCliPlatformClient(config),
@@ -7828,9 +7914,10 @@ function registerChannelPlatformsCommand(channel) {
7828
7914
  //#region src/commands/channel/resolve-channel-project.ts
7829
7915
  async function resolveChannelProjectId(config, projectRef) {
7830
7916
  const platform = createCliPlatformClient(config);
7917
+ await ensureLinkedOrganization(config);
7831
7918
  await ensureActiveOrganization(config);
7832
- const ref = projectRef?.trim() || getCliTargetOptions().projectId?.trim() || config.get("activeProjectId")?.trim();
7833
- if (!ref) throw new Error("Project is required. Pass --project <slug> or set an active project.");
7919
+ const ref = projectRef?.trim() || getCliTargetOptions().projectId?.trim() || readLinkedProjectConfig().project?.trim();
7920
+ if (!ref) throw new Error("Project is required. Pass --project <slug> or run `keystroke project link --project <slug>`.");
7834
7921
  return (await resolveProjectRef(platform, ref)).id;
7835
7922
  }
7836
7923
  //#endregion
@@ -7928,27 +8015,6 @@ function registerChannelCommand(program) {
7928
8015
  registerChannelUpdateBindingCommand(channel);
7929
8016
  }
7930
8017
  //#endregion
7931
- //#region src/commands/config/use-project.ts
7932
- async function fetchProjectInOrg(config, projectRef) {
7933
- return resolveProjectRef(createCliPlatformClient(config), projectRef);
7934
- }
7935
- function assertProjectReadyForConfig(project) {
7936
- const label = formatProjectLabel(project);
7937
- if (project.status === "failed") throw new Error(project.lastError ? `Project ${label} deploy failed: ${project.lastError}` : `Project ${label} deploy failed`);
7938
- if (project.status === "starting") throw new Error(`Project ${label} is still starting. Wait for deploy to finish, then run \`keystroke config use project ${label}\`.`);
7939
- }
7940
- async function runConfigUseProject(config, projectRef) {
7941
- const project = await fetchProjectInOrg(config, projectRef);
7942
- assertProjectReadyForConfig(project);
7943
- config.set("activeProjectId", project.id);
7944
- config.set("apiTarget", "platform");
7945
- const label = formatProjectLabel(project);
7946
- return {
7947
- stdout: project.status === "active" ? `Using platform project ${label}\n` : `Using platform project ${label} (${project.status})\n`,
7948
- stderr: project.status === "inactive" ? `Project is not active yet. Deploy with: ${cliBinaryName()} deploy --project ${label}\n` : void 0
7949
- };
7950
- }
7951
- //#endregion
7952
8018
  //#region src/commands/config/preferences-patch.ts
7953
8019
  function parsePreferenceValue(rawValue) {
7954
8020
  try {
@@ -8002,22 +8068,23 @@ function registerConfigCommand(program) {
8002
8068
  config.command("show").description("Show CLI configuration").action(() => {
8003
8069
  const cliConfig = createCliConfig();
8004
8070
  const configDir = getConfigDir(cliConfig);
8071
+ const linked = readLinkedProjectConfig();
8005
8072
  process.stdout.write(`${JSON.stringify({
8006
8073
  webUrl: cliConfig.get("webUrl"),
8007
8074
  platformUrl: getPlatformUrl(cliConfig),
8008
8075
  localApiUrl: resolveLocalApiOrigin(process.cwd()),
8009
8076
  activeOrganizationId: cliConfig.get("activeOrganizationId"),
8010
- activeProjectId: cliConfig.get("activeProjectId"),
8077
+ linkedProject: linked.project ?? null,
8078
+ linkedOrganization: linked.organization ?? null,
8011
8079
  apiTarget: getEffectiveApiTarget(cliConfig),
8012
8080
  devSession: readDevSession(configDir) ?? null
8013
8081
  }, null, 2)}\n`);
8014
8082
  });
8015
- config.command("use").description("Switch API target (local/cloud/project) or active organization").argument("[mode]", "local, cloud, project, or org").argument("[id]", "Project or organization id when switching cloud target or org").action(async (mode, id) => {
8083
+ config.command("use").description("Switch API target (local/cloud) or active organization").argument("[mode]", "local, cloud, or org").argument("[id]", "Organization slug when switching org").action(async (mode, id) => {
8016
8084
  const cliConfig = createCliConfig();
8017
8085
  if (!mode || mode === "local") {
8018
8086
  cliConfig.set("apiTarget", "local");
8019
- const activeProjectId = cliConfig.get("activeProjectId");
8020
- process.stdout.write(activeProjectId ? `Using local keystroke server (cloud project ${activeProjectId} unchanged)\n` : "Using local keystroke server\n");
8087
+ process.stdout.write("Using local keystroke server\n");
8021
8088
  return;
8022
8089
  }
8023
8090
  if (mode === "org") {
@@ -8027,16 +8094,10 @@ function registerConfigCommand(program) {
8027
8094
  }
8028
8095
  if (mode === "cloud") {
8029
8096
  cliConfig.set("apiTarget", "platform");
8030
- const activeProjectId = cliConfig.get("activeProjectId");
8031
- process.stdout.write(activeProjectId ? `Using platform API (default project ${activeProjectId} for project-scoped commands)\n` : "Using platform API\n");
8097
+ process.stdout.write("Using platform API\n");
8032
8098
  return;
8033
8099
  }
8034
- if (mode !== "project") throw new Error("Usage: keystroke config use [local|cloud|project [slug]|org [slug]]");
8035
- const targetProjectId = id ?? cliConfig.get("activeProjectId");
8036
- if (!targetProjectId) throw new Error("Usage: keystroke config use project <slug>");
8037
- const result = await runConfigUseProject(cliConfig, targetProjectId);
8038
- process.stdout.write(result.stdout);
8039
- if (result.stderr) process.stderr.write(result.stderr);
8100
+ throw new Error("Usage: keystroke config use [local|cloud|org [slug]]");
8040
8101
  });
8041
8102
  config.command("org").description("List organizations you belong to").action(async () => {
8042
8103
  const organizations = await listOrganizations(createCliConfig());
@@ -8062,20 +8123,20 @@ function registerSkillCommand(program) {
8062
8123
  //#endregion
8063
8124
  //#region src/commands/skills.ts
8064
8125
  function registerSkillsCommand(program) {
8065
- 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) => {
8126
+ program.command("skills").description("Manage the scaffolded AGENTS guide").command("sync").description("Overwrite AGENTS.md with the CLI version").option("--dir <path>", "Project directory", process.cwd()).action(async (options) => {
8066
8127
  try {
8067
8128
  const root = resolveProjectRoot(options.dir);
8068
- await syncBundledSkills(root);
8069
- process.stdout.write(`Synced bundled skills at ${root}\n`);
8129
+ await scaffoldAgentsGuide(root);
8130
+ process.stdout.write(`Synced AGENTS.md at ${root}\n`);
8070
8131
  } catch (error) {
8071
- const message = error instanceof Error ? error.message : "Skills sync failed";
8132
+ const message = error instanceof Error ? error.message : "AGENTS guide sync failed";
8072
8133
  process.stderr.write(`${message}\n`);
8073
8134
  process.exitCode = 1;
8074
8135
  }
8075
8136
  });
8076
8137
  }
8077
8138
  //#endregion
8078
- //#region src/skills/sync-skills.ts
8139
+ //#region src/skills/sync-agents-guide.ts
8079
8140
  function commandPath(command) {
8080
8141
  const names = [];
8081
8142
  for (let current = command; current; current = current.parent) {
@@ -8094,18 +8155,20 @@ function shouldSkipAutoSync(command) {
8094
8155
  function resolveAutoSyncRoot(command) {
8095
8156
  return resolveKeystrokeConfigRoot(command.opts().dir ?? process.cwd());
8096
8157
  }
8097
- /** Sync bundled skills when stale; no-op when not in a project or already up to date. */
8098
- async function syncSkills(command) {
8158
+ /** Sync AGENTS.md when stale; no-op when not in a project or already up to date. */
8159
+ async function syncAgentsGuide(command) {
8099
8160
  if (shouldSkipAutoSync(command)) return;
8100
8161
  const projectRoot = resolveAutoSyncRoot(command);
8101
8162
  if (!projectRoot) return;
8102
- const skillsDir = agentsSkillsDir(projectRoot);
8103
- if (!existsSync(skillsDir)) return;
8104
- if (lstatSync(skillsDir).isSymbolicLink()) return;
8163
+ const agentsMdPath = join(projectRoot, "AGENTS.md");
8164
+ if (!existsSync(agentsMdPath)) return;
8165
+ if (lstatSync(agentsMdPath).isSymbolicLink()) return;
8166
+ const legacySkillsDir = join(agentsGuideDir(projectRoot), "skills");
8167
+ if (existsSync(legacySkillsDir) && lstatSync(legacySkillsDir).isSymbolicLink()) return;
8105
8168
  const currentVersion = readCliVersion();
8106
8169
  if (await readBundleVersion(projectRoot) === currentVersion) return;
8107
- await syncBundledSkills(projectRoot);
8108
- process.stderr.write(`Synced bundled skills to ${currentVersion}\n`);
8170
+ await scaffoldAgentsGuide(projectRoot);
8171
+ process.stderr.write(`Synced AGENTS.md to ${currentVersion}\n`);
8109
8172
  }
8110
8173
  //#endregion
8111
8174
  //#region src/program.ts
@@ -8116,7 +8179,7 @@ function createProgram() {
8116
8179
  projectId: opts.project,
8117
8180
  local: opts.local
8118
8181
  });
8119
- await syncSkills(actionCommand);
8182
+ await syncAgentsGuide(actionCommand);
8120
8183
  });
8121
8184
  registerAuthCommand(program);
8122
8185
  registerConnectCommand(program);
@@ -8144,7 +8207,7 @@ function createProgram() {
8144
8207
  return program;
8145
8208
  }
8146
8209
  async function runCli(argv) {
8147
- const { maybeAutoUpdate } = await import("./maybe-auto-update-q5MthdI8.mjs");
8210
+ const { maybeAutoUpdate } = await import("./maybe-auto-update-BDvSKDZp.mjs");
8148
8211
  await maybeAutoUpdate(argv);
8149
8212
  createProgram().parse(argv);
8150
8213
  }