@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.
- package/dist/index.mjs +216 -153
- package/dist/index.mjs.map +1 -1
- package/dist/{maybe-auto-update-q5MthdI8.mjs → maybe-auto-update-BDvSKDZp.mjs} +2 -2
- package/dist/{maybe-auto-update-q5MthdI8.mjs.map → maybe-auto-update-BDvSKDZp.mjs.map} +1 -1
- package/dist/skills-bundle/_AGENTS.md +1 -1
- package/dist/templates/hello-world/README.md +3 -2
- package/dist/{version-DcR3O1UD.mjs → version-pY9N8XlL.mjs} +1 -2
- package/dist/version-pY9N8XlL.mjs.map +1 -0
- package/package.json +1 -1
- package/dist/skills-bundle/skills/keystroke-actions/SKILL.md +0 -160
- package/dist/skills-bundle/skills/keystroke-actions/references/catalog-and-imports.md +0 -71
- package/dist/skills-bundle/skills/keystroke-agents/SKILL.md +0 -115
- package/dist/skills-bundle/skills/keystroke-agents/references/models.md +0 -23
- package/dist/skills-bundle/skills/keystroke-agents/references/tools-mcp-codemode.md +0 -85
- package/dist/skills-bundle/skills/keystroke-agents/references/workflows-and-testing.md +0 -26
- package/dist/skills-bundle/skills/keystroke-apps/SKILL.md +0 -151
- package/dist/skills-bundle/skills/keystroke-apps/references/cli-and-catalog.md +0 -104
- package/dist/skills-bundle/skills/keystroke-channels/SKILL.md +0 -66
- package/dist/skills-bundle/skills/keystroke-channels/references/slack-setup.md +0 -41
- package/dist/skills-bundle/skills/keystroke-cli/SKILL.md +0 -93
- package/dist/skills-bundle/skills/keystroke-deploy/SKILL.md +0 -93
- package/dist/skills-bundle/skills/keystroke-deploy/references/build-and-full-deploy.md +0 -30
- package/dist/skills-bundle/skills/keystroke-deploy/references/filtered-deploy.md +0 -50
- package/dist/skills-bundle/skills/keystroke-deploy/references/wip-ignore.md +0 -35
- package/dist/skills-bundle/skills/keystroke-files/SKILL.md +0 -43
- package/dist/skills-bundle/skills/keystroke-skills/SKILL.md +0 -42
- package/dist/skills-bundle/skills/keystroke-triggers/SKILL.md +0 -143
- package/dist/skills-bundle/skills/keystroke-workflows/SKILL.md +0 -78
- package/dist/skills-bundle/skills/keystroke-workflows/references/authoring.md +0 -168
- package/dist/skills-bundle/skills/keystroke-workflows/references/testing.md +0 -138
- 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-
|
|
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,
|
|
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
|
|
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 ??
|
|
4910
|
-
if (!projectRef) throw new Error("No project selected. Pass `--project <slug>` or run `keystroke
|
|
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}
|
|
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.
|
|
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
|
|
6937
|
+
throw new Error("Bundled AGENTS guide not found — run `pnpm --filter @keystrokehq/cli build`");
|
|
6879
6938
|
}
|
|
6880
6939
|
//#endregion
|
|
6881
|
-
//#region src/skills/
|
|
6882
|
-
|
|
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,
|
|
6947
|
+
await symlink(target, linkPath, "file");
|
|
6903
6948
|
}
|
|
6904
|
-
/**
|
|
6905
|
-
async function
|
|
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, ".
|
|
6923
|
-
await
|
|
6924
|
-
|
|
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
|
|
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
|
|
7056
|
-
process.stdout.write(" keystroke deploy
|
|
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 ??
|
|
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 ??
|
|
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 ??
|
|
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 ??
|
|
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 ??
|
|
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
|
|
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() ||
|
|
7833
|
-
if (!ref) throw new Error("Project is required. Pass --project <slug> or
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
8069
|
-
process.stdout.write(`Synced
|
|
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 : "
|
|
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-
|
|
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
|
|
8098
|
-
async function
|
|
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
|
|
8103
|
-
if (!existsSync(
|
|
8104
|
-
if (lstatSync(
|
|
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
|
|
8108
|
-
process.stderr.write(`Synced
|
|
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
|
|
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-
|
|
8210
|
+
const { maybeAutoUpdate } = await import("./maybe-auto-update-BDvSKDZp.mjs");
|
|
8148
8211
|
await maybeAutoUpdate(argv);
|
|
8149
8212
|
createProgram().parse(argv);
|
|
8150
8213
|
}
|