@keystrokehq/cli 0.0.25 → 0.0.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.
@@ -45,7 +45,7 @@ function createInitCommand() {
45
45
  description: "Initialize a Keystroke project (creates keystroke.config.ts)",
46
46
  schema: InitOptionsSchema,
47
47
  optionsConfig: INIT_OPTIONS_CONFIG,
48
- loadHandler: async () => (await import("./init.handler-Cw-Wg5Fp.mjs")).handleInit
48
+ loadHandler: async () => (await import("./init.handler-C60qFTIV.mjs")).handleInit
49
49
  });
50
50
  }
51
51
  //#endregion
@@ -4,7 +4,7 @@ import { D as CliExitError, a as ui, i as fetchLatestNpmPackageVersion } from ".
4
4
  import { i as projects } from "./dist-D_KgdxW5.mjs";
5
5
  import { a as writeProjectConfig, i as readProjectConfig, r as getProjectConfigPath } from "./project-config-DudGRFPO.mjs";
6
6
  import { i as requireClient } from "./context-DHOTSgPb.mjs";
7
- import { i as UnknownSkillAgentError, n as resolveSkillInstallChoices, r as installKeystrokeAgentSkills, t as summarizeSkillInstall } from "./skill-installer-C-1et32n.mjs";
7
+ import { i as UnknownSkillAgentError, n as resolveSkillInstallChoices, r as installKeystrokeAgentSkills, t as summarizeSkillInstall } from "./skill-installer-AX0X-u1J.mjs";
8
8
  import { createRequire } from "node:module";
9
9
  import path from "node:path";
10
10
  import { access, mkdir, readFile, writeFile } from "node:fs/promises";
@@ -13,7 +13,8 @@ import { cancel, isCancel, text } from "@clack/prompts";
13
13
  //#region src/lib/keystroke-scaffold-version-ranges.ts
14
14
  const WORKFLOW_CORE_PACKAGE_NAME = "@keystrokehq/core";
15
15
  const CONFIG_PACKAGE_NAME = "@keystrokehq/config";
16
- const FALLBACK_WORKFLOW_CORE_VERSION_RANGE = "latest";
16
+ const RUNTIME_PACKAGE_NAME = "@keystrokehq/runtime";
17
+ const FALLBACK_SCAFFOLD_VERSION_RANGE = "latest";
17
18
  const DEFAULT_TIMEOUT_MS = 750;
18
19
  /**
19
20
  * Semver range for scaffold `package.json` — prefers the latest published
@@ -33,6 +34,13 @@ async function configScaffoldVersionRange(options = {}) {
33
34
  readLocalVersion: options.readLocalConfigVersion ?? readLocalConfigVersion
34
35
  });
35
36
  }
37
+ async function runtimeScaffoldVersionRange(options = {}) {
38
+ return packageScaffoldVersionRange({
39
+ packageName: RUNTIME_PACKAGE_NAME,
40
+ timeoutMs: options.timeoutMs,
41
+ readLocalVersion: options.readLocalRuntimeVersion ?? readLocalRuntimeVersion
42
+ });
43
+ }
36
44
  async function packageScaffoldVersionRange(options) {
37
45
  const latestVersion = await fetchLatestNpmPackageVersion({
38
46
  packageName: options.packageName,
@@ -41,7 +49,7 @@ async function packageScaffoldVersionRange(options) {
41
49
  if (latestVersion) return `^${latestVersion}`;
42
50
  const localVersion = options.readLocalVersion();
43
51
  if (localVersion) return `^${localVersion}`;
44
- return FALLBACK_WORKFLOW_CORE_VERSION_RANGE;
52
+ return FALLBACK_SCAFFOLD_VERSION_RANGE;
45
53
  }
46
54
  function readLocalWorkflowCoreVersion() {
47
55
  try {
@@ -59,6 +67,14 @@ function readLocalConfigVersion() {
59
67
  } catch {}
60
68
  return null;
61
69
  }
70
+ function readLocalRuntimeVersion() {
71
+ try {
72
+ const raw = readFileSync(createRequire(import.meta.url).resolve(`${RUNTIME_PACKAGE_NAME}/package.json`), "utf-8");
73
+ const version = JSON.parse(raw).version?.trim();
74
+ if (version) return version;
75
+ } catch {}
76
+ return null;
77
+ }
62
78
  //#endregion
63
79
  //#region src/commands/init/templates/biome-config.ts
64
80
  /**
@@ -210,6 +226,7 @@ function createPackageJsonContent(projectName, options) {
210
226
  },
211
227
  devDependencies: {
212
228
  "@biomejs/biome": "2.4.13",
229
+ "@keystrokehq/runtime": options.runtimeVersionRange,
213
230
  "@keystrokehq/testing": options.workflowCoreVersionRange,
214
231
  vitest: "^4.0.18",
215
232
  typescript: "^5.9.3"
@@ -312,7 +329,7 @@ async function promptProjectDescription(options) {
312
329
  const t = typeof answer === "string" ? answer.trim() : "";
313
330
  return t.length > 0 ? t : void 0;
314
331
  }
315
- async function ensureScaffoldPackageJson(targetDir, projectName, workflowCoreRange, configRange) {
332
+ async function ensureScaffoldPackageJson(targetDir, projectName, workflowCoreRange, configRange, runtimeRange) {
316
333
  const pkgPath = path.join(targetDir, "package.json");
317
334
  try {
318
335
  const raw = await readFile(pkgPath, "utf-8");
@@ -322,6 +339,7 @@ async function ensureScaffoldPackageJson(targetDir, projectName, workflowCoreRan
322
339
  pkg.dependencies["@keystrokehq/core"] = workflowCoreRange;
323
340
  pkg.dependencies.zod = pkg.dependencies.zod ?? "^4.3.6";
324
341
  pkg.devDependencies = pkg.devDependencies ?? {};
342
+ pkg.devDependencies["@keystrokehq/runtime"] = runtimeRange;
325
343
  pkg.devDependencies["@keystrokehq/testing"] = workflowCoreRange;
326
344
  pkg.devDependencies.vitest = pkg.devDependencies.vitest ?? "^4.0.18";
327
345
  pkg.devDependencies.typescript = pkg.devDependencies.typescript ?? "^5.9.3";
@@ -331,6 +349,7 @@ async function ensureScaffoldPackageJson(targetDir, projectName, workflowCoreRan
331
349
  } catch {
332
350
  await writeFile(pkgPath, `${createPackageJsonContent(projectName, {
333
351
  configVersionRange: configRange,
352
+ runtimeVersionRange: runtimeRange,
334
353
  workflowCoreVersionRange: workflowCoreRange
335
354
  })}\n`, "utf-8");
336
355
  return "created";
@@ -408,8 +427,12 @@ async function handleInit(options, ctx) {
408
427
  await projects.track(targetDir, { name: projectName });
409
428
  await installAgentSkillsForInit(targetDir, options);
410
429
  if (options.scaffold) {
411
- const [workflowCoreVersionRange, configVersionRange] = await Promise.all([workflowCoreScaffoldVersionRange(), configScaffoldVersionRange()]);
412
- await scaffoldProject(targetDir, projectName, workflowCoreVersionRange, configVersionRange);
430
+ const [workflowCoreVersionRange, configVersionRange, runtimeVersionRange] = await Promise.all([
431
+ workflowCoreScaffoldVersionRange(),
432
+ configScaffoldVersionRange(),
433
+ runtimeScaffoldVersionRange()
434
+ ]);
435
+ await scaffoldProject(targetDir, projectName, workflowCoreVersionRange, configVersionRange, runtimeVersionRange);
413
436
  }
414
437
  }
415
438
  /** Write a file only if it does not already exist. Returns true if written. */
@@ -422,9 +445,9 @@ async function writeIfMissing(filePath, content) {
422
445
  return true;
423
446
  }
424
447
  }
425
- async function scaffoldProject(targetDir, projectName, workflowCoreVersionRange, configVersionRange) {
426
- const pkgAction = await ensureScaffoldPackageJson(targetDir, projectName, workflowCoreVersionRange, configVersionRange);
427
- ui.success(pkgAction === "created" ? `Created package.json (@keystrokehq/core ${workflowCoreVersionRange}, @keystrokehq/config ${configVersionRange})` : `Updated package.json (Keystroke deps -> core ${workflowCoreVersionRange}, config ${configVersionRange})`);
448
+ async function scaffoldProject(targetDir, projectName, workflowCoreVersionRange, configVersionRange, runtimeVersionRange) {
449
+ const pkgAction = await ensureScaffoldPackageJson(targetDir, projectName, workflowCoreVersionRange, configVersionRange, runtimeVersionRange);
450
+ ui.success(pkgAction === "created" ? `Created package.json (@keystrokehq/core ${workflowCoreVersionRange}, @keystrokehq/config ${configVersionRange}, @keystrokehq/runtime ${runtimeVersionRange})` : `Updated package.json (Keystroke deps -> core ${workflowCoreVersionRange}, config ${configVersionRange}, runtime ${runtimeVersionRange})`);
428
451
  const files = [
429
452
  {
430
453
  rel: "vitest.config.ts",
@@ -521,7 +521,7 @@ const logger = {
521
521
  };
522
522
  //#endregion
523
523
  //#region package.json
524
- var version = "0.0.25";
524
+ var version = "0.0.26";
525
525
  //#endregion
526
526
  //#region src/command-registry.ts
527
527
  const ROOT_OPTIONS_WITH_VALUES$1 = new Set([
@@ -566,7 +566,7 @@ const lazyCommandDefinitions = [
566
566
  },
567
567
  {
568
568
  name: "init",
569
- loadCommand: async () => (await import("./init-9PiVyCVi.mjs")).createInitCommand()
569
+ loadCommand: async () => (await import("./init-DonAqdBg.mjs")).createInitCommand()
570
570
  },
571
571
  {
572
572
  name: "integrations",
@@ -594,7 +594,7 @@ const lazyCommandDefinitions = [
594
594
  },
595
595
  {
596
596
  name: "skills",
597
- loadCommand: async () => (await import("./skills.command-DFpATWrm.mjs")).createSkillsCommand()
597
+ loadCommand: async () => (await import("./skills.command-B6jly3ew.mjs")).createSkillsCommand()
598
598
  },
599
599
  {
600
600
  name: "sync",
@@ -4,7 +4,6 @@ import { D as CliExitError, a as ui } from "./keystroke.mjs";
4
4
  import { createRequire } from "node:module";
5
5
  import path from "node:path";
6
6
  import { access, cp, mkdir, readFile, readdir, rm, symlink, writeFile } from "node:fs/promises";
7
- import { fileURLToPath } from "node:url";
8
7
  import { autocompleteMultiselect, cancel, isCancel, select } from "@clack/prompts";
9
8
  //#region src/lib/skill-installer/package.ts
10
9
  const SKILLS_PACKAGE_CONTENT_DIR = "src";
@@ -39,23 +38,14 @@ function normalizeLineEndings$1(value) {
39
38
  function normalizeGuidanceBlurb(value) {
40
39
  return `${normalizeLineEndings$1(value).trim()}\n`;
41
40
  }
42
- function resolveMonorepoSkillsBlurbPath() {
43
- return fileURLToPath(new URL("../../../../../packages/skills/src/_AGENTS.md", import.meta.url));
44
- }
45
- function resolveCliPackageSkillsBlurbPath() {
46
- const relativePath = import.meta.url.includes("/dist/") ? "../AGENTS-blurb.md" : "../../../AGENTS-blurb.md";
47
- return fileURLToPath(new URL(relativePath, import.meta.url));
48
- }
49
41
  function resolveBundledKeystrokeAgentsBlurbPath() {
50
42
  const require = createRequire(import.meta.url);
51
43
  try {
52
44
  const skillsPackageJsonPath = require.resolve("@keystrokehq/skills/package.json");
53
45
  return path.join(path.dirname(skillsPackageJsonPath), AGENTS_PACKAGE_PATH);
54
- } catch {}
55
- try {
56
- return resolveCliPackageSkillsBlurbPath();
57
- } catch {}
58
- return resolveMonorepoSkillsBlurbPath();
46
+ } catch {
47
+ throw new CliExitError("Could not resolve @keystrokehq/skills. Reinstall the CLI with its bundled skills package.", { exitCode: 1 });
48
+ }
59
49
  }
60
50
  async function loadKeystrokeAgentsBlurb() {
61
51
  try {
@@ -2,7 +2,7 @@
2
2
 
3
3
  import { D as CliExitError, a as ui } from "./keystroke.mjs";
4
4
  import { i as writeJson, r as isJsonMode } from "./output-BWcVRt-T.mjs";
5
- import { i as UnknownSkillAgentError, n as resolveSkillInstallChoices, r as installKeystrokeAgentSkills, t as summarizeSkillInstall } from "./skill-installer-C-1et32n.mjs";
5
+ import { i as UnknownSkillAgentError, n as resolveSkillInstallChoices, r as installKeystrokeAgentSkills, t as summarizeSkillInstall } from "./skill-installer-AX0X-u1J.mjs";
6
6
  import path from "node:path";
7
7
  //#region src/commands/skills/skills-sync.handler.ts
8
8
  async function handleSkillsSync(options, _ctx) {
@@ -38,7 +38,7 @@ function createSkillsCommand() {
38
38
  description: "Install bundled Keystroke skills into selected agent skill directories",
39
39
  schema: SkillsCommandOptionsSchema,
40
40
  optionsConfig: SKILLS_OPTIONS_CONFIG,
41
- loadHandler: async () => (await import("./skills-sync.handler-B_5dgdvs.mjs")).handleSkillsSync
41
+ loadHandler: async () => (await import("./skills-sync.handler-DsJP_-XZ.mjs")).handleSkillsSync
42
42
  })]
43
43
  });
44
44
  cmd.enablePositionalOptions();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@keystrokehq/cli",
3
- "version": "0.0.25",
3
+ "version": "0.0.26",
4
4
  "private": false,
5
5
  "description": "Command-line interface for creating, managing, and deploying Keystroke automations.",
6
6
  "type": "module",
@@ -17,7 +17,6 @@
17
17
  "keystroke": "./bin/keystroke.mjs"
18
18
  },
19
19
  "files": [
20
- "AGENTS-blurb.md",
21
20
  "bin",
22
21
  "dist",
23
22
  "README.md"
@@ -33,7 +32,7 @@
33
32
  "rolldown": "1.0.0",
34
33
  "tsx": "^4.21.0",
35
34
  "zod": "4.4.3",
36
- "@keystrokehq/skills": "0.0.6"
35
+ "@keystrokehq/skills": "0.0.7"
37
36
  },
38
37
  "devDependencies": {
39
38
  "tsdown": "0.21.10",
@@ -43,15 +42,16 @@
43
42
  "@keystroke/local-memory": "0.0.1",
44
43
  "@keystroke/shared-types": "0.0.5",
45
44
  "@keystrokehq/config": "0.0.2",
45
+ "@keystroke/test-utils": "0.0.3",
46
46
  "@keystroke/typescript-config": "0.0.0",
47
- "@keystrokehq/core": "0.0.7",
48
47
  "@keystroke/utils": "0.0.0",
48
+ "@keystrokehq/core": "0.0.7",
49
+ "@keystrokehq/runtime": "0.0.4",
49
50
  "@keystrokehq/workflow-build-contracts": "0.0.3",
50
- "@keystroke/workflow-builder": "0.0.7",
51
- "@keystroke/workflow-deploy": "0.0.6",
52
51
  "@keystrokehq/testing": "0.2.2",
52
+ "@keystroke/workflow-deploy": "0.0.6",
53
53
  "@keystroke/workflow-sdk": "0.0.4",
54
- "@keystroke/test-utils": "0.0.3"
54
+ "@keystroke/workflow-builder": "0.0.7"
55
55
  },
56
56
  "keywords": [
57
57
  "automation",
package/AGENTS-blurb.md DELETED
@@ -1,123 +0,0 @@
1
- # Keystroke Project Context
2
-
3
- You are working inside a Keystroke project. Keystroke is a code-first workflow and agent automation platform. Authors define workflows, agents, operations, tasks, triggers, messaging gateways, MCP servers, sandboxes, and credential bindings in TypeScript, then build and deploy them with the Keystroke CLI.
4
-
5
- When reasoning about authored code, use this top-level split:
6
-
7
- - A `Workflow` is deterministic orchestration. Its `run(...)` method coordinates steps, child workflows, waits, hooks, and agents.
8
- - An `Agent` is model-driven execution. It runs with agent tools, can use sandboxes, MCP servers, and messaging gateways, and is the right place for llm driven work.
9
- - A `Task` is the trigger-driven agent path. It combines triggers, a prompt, and an agent run.
10
- - A workflow can also be registered as an agent tool. Sync workflow tools return inline results; suspending workflow tools yield and resume later; large outputs can return refs inspected with bounded data toolkit tools.
11
-
12
- Triggers, tasks, and agent conversations are different entry models:
13
-
14
- - a workflow is started by a trigger or direct invocation
15
- - a trigger is a code-based primitive you author in TypeScript and attach to a workflow
16
- - a trigger attaches to a workflow and resolves external payload into validated workflow input
17
- - a trigger is not a chat session; it is an ingress boundary that transforms payload and starts a workflow
18
- - a task is not attached with `trigger.attach(...)`; it lists triggers inline and resolves a prompt for an agent run
19
- - an agent conversation is a chat session, not a code-authored primitive like `CronTrigger`, `WebhookTrigger`, or `PollingTrigger`
20
- - conversations can be started from the UI, from messaging adapters such as Slack, Linear, or GitHub, or from a workflow when the workflow runs an agent
21
- - messaging gateways configure conversational entry on agents; they are not workflow triggers
22
- - an agent keeps full context of the conversation session while the workflow side stays replay-safe and stateless between execution boundaries
23
-
24
- Keystroke also has one shared unit-of-work primitive: `Operation`.
25
-
26
- - `Operation`, `Step`, and `Tool` are aliases for the same class from `@keystrokehq/core`.
27
- - Use the `Step` name when teaching workflow-side usage.
28
- - Use the `Tool` name when teaching agent-side usage.
29
- - Use the `Operation` name when describing shared infrastructure, integrations, or a reusable unit that can be used in both places.
30
- - The runtime behavior comes from context, not from which alias name was used in the constructor.
31
-
32
- Runtime boundary:
33
-
34
- - workflows and steps are authored as TypeScript control-flow and unit-of-work code
35
- - workflows do not run bash commands as part of the workflow authoring model
36
- - agents are the correct place for bash, filesystem work, and sandbox-managed dependencies such as Python
37
-
38
- Keystroke workflow execution is replay-based and stateless at the workflow layer:
39
-
40
- - Keystroke does not persist live in-memory workflow state.
41
- - Instead, it persists execution events and terminal results for execution boundaries, then replays the workflow code from the top with that saved state.
42
- - The workflow body itself is re-executed during replay. Local variables and control flow are recomputed, not resumed from memory.
43
- - Because of that, workflow code must be replay-safe and deterministic.
44
-
45
- Treat these calls inside `Workflow.run(...)` as execution boundaries:
46
-
47
- - `await step.run(...)`
48
- - `await operation.run(...)`
49
- - `await childWorkflow.run(...)`
50
- - `await agent.run(...)`
51
-
52
- What gets persisted:
53
-
54
- - For steps, Keystroke persists created/completed/failed state and reuses the saved result during workflow replay.
55
- - For child workflows, Keystroke persists the child run and its terminal result, then resumes the parent workflow with that saved outcome.
56
- - For agents, Keystroke persists agent execution state and terminal output, then resumes the workflow with that saved result.
57
- - The workflow's own in-memory logic is not persisted. The platform saves boundary state, not the live workflow stack.
58
-
59
- Where code runs:
60
-
61
- - Operations used as workflow steps are low-level units of work and run in separate worker executions.
62
- - Child workflows run as separate workflow executions in their own workers, using the same replay model as parent workflows.
63
- - Agents run outside the workflow replay worker. They run in persisted sandboxes with a persistent filesystem, where they can use files, shell commands, installed skills, MCP servers, and other runtime tools. The filesystem persists over all agent runs for a deployed agent.
64
- - Operations used as agent tools are not top-level orchestration units. A tool runs inside the agent runtime when the agent chooses to call it, inside that persisted sandbox context.
65
-
66
- Workflow triggers versus agent conversations:
67
-
68
- - use code-authored triggers when external schedules, webhook requests, or polling results should become workflow input
69
- - use agent conversations when a user or system is chatting with an agent over time
70
- - author triggers in code; do not think of conversations as authored primitives in the same way
71
- - messaging adapters normalize inbound events into thread-based conversations, and the agent responds inside that conversation context
72
- - a workflow can still start an agent-backed conversation by running an agent, but that is different from a workflow trigger boundary
73
-
74
- Authoring implications:
75
-
76
- - Put orchestration, branching, loops, waits, and composition in workflows.
77
- - Put deterministic side effects and integration calls in operations used as steps.
78
- - Put LLM-driven reasoning and tool selection in agents.
79
- - Put concrete callable actions in operations used as tools.
80
- - Use `largeResultMode: 'ref'` plus `describe_ref`, `read_ref`, and `slice_ref` for large workflow-tool outputs. Reducers and DuckDB-backed data tools are deferred.
81
- - Use `midSessionSnapshot: true` only for measured workflow-tool cases that need Phase D replay. Current snapshot behavior is conversation-log replay, not native Pi process restore.
82
- - Do not depend on workflow-local mutable state, random values, direct network I/O, or filesystem mutations in the workflow body itself unless they happen behind a Keystroke execution boundary.
83
- - Never assume workflow-local memory or filesystem state survives between replays. If state must survive, return it from an operation, agent, or child workflow, or persist it externally.
84
-
85
- ## Workflow Builder File Structure
86
-
87
- The workflow builder now relies on explicit file structure. Teach and author Keystroke code with one exported primitive per typed file:
88
-
89
- - `*.workflow.ts` for one `Workflow`
90
- - `*.step.ts`, `*.tool.ts`, or `*.operation.ts` for one exported `Operation`
91
- - `*.agent.ts` for one `Agent`
92
- - `*.gateway.ts` for one `MessagingGateway`
93
- - `*.trigger.ts` for one trigger
94
- - `*.credential-set.ts` for one `CredentialSet`
95
- - `*.mcp-server.ts` for one `McpServer`
96
-
97
- Builder note:
98
-
99
- - `*.step.ts`, `*.tool.ts`, and `*.operation.ts` all validate as the same operation convention
100
- - choose the suffix that communicates intent to the reader
101
-
102
- Required structure:
103
-
104
- - exported primitives should be top-level and statically visible
105
- - helper files such as `schemas.ts`, `utils.ts`, or `prompts.ts` should not export primitives
106
- - a `*.trigger.ts` file may also export that trigger's `TriggerAttachment` values
107
- - tests are exempt, but authored project code should follow the typed-file convention everywhere
108
-
109
- Example layout:
110
-
111
- ```text
112
- customer-support/
113
- crm-api.credential-set.ts
114
- lookup-customer.tool.ts
115
- support.agent.ts
116
- support.gateway.ts
117
- triage.step.ts
118
- support.workflow.ts
119
- support.trigger.ts
120
- coding.sandbox.ts
121
- docs.mcp-server.ts
122
- schemas.ts
123
- ```