@keystrokehq/cli 0.1.15 → 0.1.17

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 (46) hide show
  1. package/dist/{dist-BnIugffH.mjs → dist-BRA_tOTT.mjs} +47 -5
  2. package/dist/{dist-BnIugffH.mjs.map → dist-BRA_tOTT.mjs.map} +1 -1
  3. package/dist/dist-BZBvPUyu.mjs +3 -0
  4. package/dist/{dist-PBiyADK0.mjs → dist-C6KqfgGN.mjs} +3 -3
  5. package/dist/{dist-PBiyADK0.mjs.map → dist-C6KqfgGN.mjs.map} +1 -1
  6. package/dist/{dist-B4pkCJsM.mjs → dist-DeRE4uJW.mjs} +2 -2
  7. package/dist/dist-DeRE4uJW.mjs.map +1 -0
  8. package/dist/{dist-BW3AMCud.mjs → dist-E9nHDRnf.mjs} +3 -3
  9. package/dist/{dist-BW3AMCud.mjs.map → dist-E9nHDRnf.mjs.map} +1 -1
  10. package/dist/index.mjs +27 -15
  11. package/dist/index.mjs.map +1 -1
  12. package/dist/{maybe-auto-update-BIarxWf3.mjs → maybe-auto-update-douMZFWJ.mjs} +2 -2
  13. package/dist/{maybe-auto-update-BIarxWf3.mjs.map → maybe-auto-update-douMZFWJ.mjs.map} +1 -1
  14. package/dist/skills-bundle/_AGENTS.mcp.md +10 -3
  15. package/dist/skills-bundle/_AGENTS.md +61 -70
  16. package/dist/skills-bundle/skills/keystroke-actions/SKILL.md +60 -12
  17. package/dist/skills-bundle/skills/keystroke-actions/references/catalog-and-imports.md +32 -3
  18. package/dist/skills-bundle/skills/keystroke-agents/SKILL.md +50 -8
  19. package/dist/skills-bundle/skills/keystroke-agents/references/models.md +11 -13
  20. package/dist/skills-bundle/skills/keystroke-agents/references/tools-mcp-codemode.md +45 -3
  21. package/dist/skills-bundle/skills/keystroke-agents/references/workflows-and-testing.md +1 -1
  22. package/dist/skills-bundle/skills/keystroke-apps/SKILL.md +26 -13
  23. package/dist/skills-bundle/skills/keystroke-apps/references/cli-and-catalog.md +47 -16
  24. package/dist/skills-bundle/skills/keystroke-channels/SKILL.md +66 -0
  25. package/dist/skills-bundle/skills/keystroke-channels/references/slack-setup.md +41 -0
  26. package/dist/skills-bundle/skills/keystroke-cli/SKILL.md +41 -93
  27. package/dist/skills-bundle/skills/keystroke-deploy/SKILL.md +10 -9
  28. package/dist/skills-bundle/skills/keystroke-deploy/references/build-and-full-deploy.md +3 -1
  29. package/dist/skills-bundle/skills/keystroke-deploy/references/filtered-deploy.md +3 -2
  30. package/dist/skills-bundle/skills/keystroke-deploy/references/wip-ignore.md +5 -2
  31. package/dist/skills-bundle/skills/keystroke-files/SKILL.md +12 -4
  32. package/dist/skills-bundle/skills/keystroke-skills/SKILL.md +7 -2
  33. package/dist/skills-bundle/skills/keystroke-triggers/SKILL.md +30 -17
  34. package/dist/skills-bundle/skills/keystroke-workflows/SKILL.md +27 -12
  35. package/dist/skills-bundle/skills/keystroke-workflows/references/authoring.md +116 -4
  36. package/dist/skills-bundle/skills/keystroke-workflows/references/testing.md +17 -9
  37. package/dist/templates/hello-world/README.md +19 -8
  38. package/dist/templates/hello-world/src/workflows/greeting.test.ts +1 -1
  39. package/dist/{version-DScIhncv.mjs → version-CJd1mEoq.mjs} +2 -2
  40. package/dist/{version-DScIhncv.mjs.map → version-CJd1mEoq.mjs.map} +1 -1
  41. package/package.json +2 -2
  42. package/dist/dist-B4pkCJsM.mjs.map +0 -1
  43. package/dist/dist-c4WWC9_F.mjs +0 -3
  44. package/dist/skills-bundle/skills/keystroke-cli/references/api-targets.md +0 -87
  45. package/dist/skills-bundle/skills/keystroke-gateways/SKILL.md +0 -43
  46. package/dist/skills-bundle/skills/keystroke-gateways/references/slack-setup.md +0 -27
@@ -1,132 +1,80 @@
1
1
  ---
2
2
  name: keystroke-cli
3
- description: Keystroke CLI — local dev vs cloud platform targets, config switching, deploy, and when to use each. Use when running keystroke commands against localhost or a deployed project.
3
+ description: Keystroke CLI — log in, manage projects, deploy your src/, and run/inspect what's deployed (workflows, agents, triggers, apps). Use when running keystroke commands for a project.
4
4
  metadata:
5
5
  keystroke-domain: cli
6
6
  ---
7
7
 
8
8
  # CLI
9
9
 
10
- The CLI is the primary interface for keystroke projects. Most commands hit an **API target**: either **local** (your keystroke server) or **cloud** (keystroke platform control planedeployed keystroke server for that project).
10
+ The CLI is the primary interface for keystroke projects. You build in `src/`, deploy to your project on the platform, then run and inspect what's deployed. The loop is **edit deploy run/inspect repeat**; deploy often.
11
11
 
12
- ## Local vs cloud
13
-
14
- | Target | Use for | Default URL |
15
- | --------- | ------------------------------------------------------------------------------------------------------ | ------------------------------------- |
16
- | **Local** | Development and testing — iterate on `src/`, run workflows/agents against your machine, debug sessions | `PUBLIC_PLATFORM_URL` in project `.env` (dev session when `keystroke dev` is running) |
17
- | **Cloud** | Invoking, listing, and operating a **deployed** project — production runs, trigger URLs, remote audit | `{platformUrl}/api/projects/{id}` |
18
-
19
- **Rule of thumb:** while you are building or validating changes, use **local**. After deploy, use **cloud** to invoke and inspect what is live.
20
-
21
- Auth (`keystroke auth login`) is **shared** — one login in the keychain. Switching local ↔ cloud or organizations does not require re-authenticating.
22
-
23
- **Organizations (cloud only)**
12
+ ## Log in
24
13
 
25
14
  ```bash
26
- keystroke auth login # auto-picks org if you have one; prompts if several
27
- keystroke auth login --org <id> # headless org pick
28
- keystroke config org list # list memberships
29
- keystroke config use org # interactive switch
30
- keystroke config use org <id> # switch active org
15
+ keystroke auth login # once; token stored in the OS keychain and reused
16
+ keystroke auth login --org <org-slug> # headless org pick
17
+ keystroke config org # list org memberships
18
+ keystroke config use org <org-slug> # switch active org
31
19
  ```
32
20
 
33
- ## Platform projects
21
+ One login covers every command. `auth login` auto-picks your org if you have one and prompts if there are several.
34
22
 
35
- A **platform project** is a deploy target on the keystroke control plane — not your local repo directory (`keystroke init` scaffolds the latter).
23
+ ## Projects
24
+
25
+ A **project** is your deploy target on the platform. `keystroke init` scaffolds the local repo; the platform project is where it runs.
36
26
 
37
27
  ```bash
38
28
  keystroke project list
39
29
  keystroke project create --name "My app" --description "Optional"
40
- keystroke deploy --project <id> # build + upload; see deploy skill for --filter
41
- keystroke config use project <id> # set default cloud target (works before deploy)
30
+ keystroke deploy --project <id> # build + upload src/; see deploy skill for --filter
42
31
  ```
43
32
 
44
- New projects are **inactive** until deploy. You can set the default project before deploy; runtime commands still require an active project. The CLI prints hints when the list is empty or when status is `inactive`.
45
-
46
- Full deploy, filtered module redeploy, and WIP ignore: [deploy skill](../keystroke-deploy/SKILL.md).
47
-
48
- ## Quick start
33
+ `--project` selects the deploy target. It's a global flag that works before or after the subcommand (`keystroke deploy --project <id>`).
49
34
 
50
- **Local development**
35
+ New projects are **inactive** until the first deploy. After a deploy, `keystroke deploy` remembers the project (saved in `~/.keystroke`), so later commands target it without `--project`. To switch which project later commands use:
51
36
 
52
37
  ```bash
53
- keystroke dev # watch + rebuild; other CLI commands auto-target local while this runs
54
- keystroke start # or one-shot local server
55
- keystroke workflow run greeting --input '{"name":"Ada"}'
56
- keystroke agent prompt hello --message "Hi"
38
+ keystroke config use project <id>
57
39
  ```
58
40
 
59
- **Cloud (deployed project)**
41
+ To target a different project for a single command without changing the default:
60
42
 
61
43
  ```bash
62
- keystroke auth login
63
- keystroke project list # or: project create --name "My app"
64
- keystroke deploy --project <project-id>
65
- keystroke workflow run greeting --input '{"name":"Ada"}' # hits cloud after deploy
66
- keystroke trigger list
67
- keystroke trigger list --endpoint stripe # all webhooks on a shared endpoint
68
- keystroke trigger url stripe # shared route URL (or use a trigger key)
44
+ keystroke workflow runs list greeting --project <id>
69
45
  ```
70
46
 
71
- ## Switch targets
47
+ By default the CLI targets the cloud platform. To run commands against a locally running server (`keystroke start` / `keystroke dev`), use the global `--local` flag for one command, or `keystroke config use local` / `keystroke config use cloud` to switch the default target.
72
48
 
73
- Config lives in `~/.keystroke`. Two settings work together:
74
-
75
- | Setting | Purpose |
76
- | ----------------- | ----------------------------------------------------------------------------- |
77
- | `activeProjectId` | Which cloud project you use (set by deploy; **not** cleared when going local) |
78
- | `apiTarget` | `local` or `platform` — default routing for commands |
49
+ Full deploy, filtered module redeploy, and WIP ignore: [deploy skill](../keystroke-deploy/SKILL.md).
79
50
 
80
- ```bash
81
- keystroke config show
82
- keystroke config use local # local dev; cloud project id preserved
83
- keystroke config use cloud # back to stored activeProjectId
84
- keystroke config use project <id> # swap active cloud project
85
- ```
51
+ ## Run & inspect
86
52
 
87
- **One-off overrides** (do not change config):
53
+ After deploy, runtime commands operate on your project:
88
54
 
89
55
  ```bash
90
- keystroke --local workflow runs list greeting
91
- keystroke --project other-id trigger list
56
+ keystroke workflow run greeting --input '{"name":"Ada"}'
57
+ keystroke workflow runs list greeting
58
+ keystroke workflow runs get greeting <run-id> --include steps,trace
59
+ keystroke agent prompt hello --message "Hi"
60
+ keystroke agent sessions get support <session-id> --include messages,trace
61
+ keystroke app list
62
+ keystroke trigger list
63
+ keystroke trigger list --endpoint stripe # all webhooks on a shared endpoint
64
+ keystroke trigger url stripe # shared route URL (or a trigger key)
92
65
  ```
93
66
 
94
- While `keystroke dev` is running, other commands auto-target **local** unless you pass `--project`.
95
-
96
- ## Resolution order
97
-
98
- 1. `--local` → local
99
- 2. `keystroke dev` session active → local
100
- 3. `--project <id>` → cloud (that project only)
101
- 4. `apiTarget=local` → local
102
- 5. `apiTarget=platform` + `activeProjectId` → cloud
103
- 6. default → local
104
-
105
- Full detail: [api-targets.md](references/api-targets.md).
67
+ Always use the CLI to inspect runs and sessions do not hand-craft HTTP requests.
106
68
 
107
- ## Commands by target
69
+ ## Commands at a glance
108
70
 
109
- | Command | Typical target |
110
- | ------------------------------------------------------------------ | ---------------------------------------------------------- |
111
- | `dev`, `start`, `build` | Local project only |
112
- | `project list`, `project create` | Platform control plane (org-scoped) |
113
- | `deploy` | Platform (sets `activeProjectId` + `apiTarget=platform`) |
114
- | `workflow`, `agent`, `trigger`, `app`, `connect`, `health` | Follows resolved target |
115
- | `auth login` | Web dashboard (`webUrl`); token reused for cloud API calls |
116
-
117
- ## Audit & debug
118
-
119
- Always use the CLI — do not hand-craft HTTP against the wrong host.
120
-
121
- ```bash
122
- # Local: default while developing
123
- keystroke workflow runs list signup-pipeline
124
- keystroke agent sessions get support <session-id> --include messages,trace
125
-
126
- # Cloud: after deploy or keystroke config use cloud
127
- keystroke trigger list
128
- keystroke trigger list --endpoint shared-hub
129
- keystroke trigger url incoming-message
130
- ```
71
+ | Command | What it does |
72
+ | -------------------------------------------------- | -------------------------------------------------------- |
73
+ | `auth login` | Authenticate; token reused for all commands |
74
+ | `project list`, `project create` | Manage deploy targets in the active org |
75
+ | `deploy` | Build `src/` and ship it; sets the active project |
76
+ | `workflow`, `agent`, `trigger`, `app` | Run and inspect resources on your project |
77
+ | `connect <slug>` | Connect an integration (see apps skill) |
78
+ | `config use project <id>`, `config show` | Set/inspect the active project |
131
79
 
132
- Related: [deploy](.agents/skills/keystroke-deploy/SKILL.md), [apps](.agents/skills/keystroke-apps/SKILL.md), [workflows](.agents/skills/keystroke-workflows/SKILL.md), [agents](.agents/skills/keystroke-agents/SKILL.md), [triggers](.agents/skills/keystroke-triggers/SKILL.md).
80
+ Related: [deploy](../keystroke-deploy/SKILL.md), [apps](../keystroke-apps/SKILL.md), [workflows](../keystroke-workflows/SKILL.md), [agents](../keystroke-agents/SKILL.md), [triggers](../keystroke-triggers/SKILL.md).
@@ -7,7 +7,7 @@ metadata:
7
7
 
8
8
  # Deploy
9
9
 
10
- Deploy uploads a `dist/` tarball to the keystroke **platform** and promotes a new project-server runtime (blue/green). Local `keystroke start` / `keystroke dev` never deploy — use this skill when shipping to cloud.
10
+ Deploy uploads a `dist/` tarball to the keystroke **platform** and promotes a new project-server runtime (blue/green). Use this skill when shipping your project to the platform.
11
11
 
12
12
  ## Prerequisites
13
13
 
@@ -17,6 +17,8 @@ keystroke project list # or: project create --name "My app"
17
17
  keystroke deploy --project <slug> # first deploy must be full (no --filter)
18
18
  ```
19
19
 
20
+ `--project` selects the deploy target; as a global flag it works before or after `deploy`. After a successful deploy the project becomes the active target (saved in `~/.keystroke`), so later commands (including subsequent deploys) can omit it.
21
+
20
22
  Deploy always **builds before upload**. Full deploy wipes `dist/` (`clean: true`), rebuilds all modules, regenerates `route-manifest.json`, then packs and uploads.
21
23
 
22
24
  ## Full deploy
@@ -24,10 +26,10 @@ Deploy always **builds before upload**. Full deploy wipes `dist/` (`clean: true`
24
26
  ```bash
25
27
  keystroke build # optional preview; deploy rebuilds anyway
26
28
  keystroke deploy --project <slug>
27
- keystroke deploy --dir ./my-app --project <slug>
29
+ keystroke deploy --project <slug> --dir ./my-app
28
30
  ```
29
31
 
30
- After success: `activeProjectId` and `apiTarget=platform` are set. Verify with `keystroke config use cloud` and `keystroke workflow run <key> --input '{}'`.
32
+ After success the project becomes the active target for later commands. Verify with `keystroke workflow run <key> --input '{}'`.
31
33
 
32
34
  ## Filtered deploy (one module)
33
35
 
@@ -58,7 +60,7 @@ Keep draft agents/workflows/triggers in `src/` but out of the runtime:
58
60
  - `ignore` — broken or not-ready; stays out of `dist/` everywhere.
59
61
  - `ignore:deploy` — runs locally; excluded from deploy only.
60
62
 
61
- A shipped module that **imports** an ignored file fails the build (import guard). Triggers attached to an ignored workflow need the same directive.
63
+ Import guard: a shipped module that **imports** an `@keystroke ignore` file fails the build in **every** phase; importing an `@keystroke ignore:deploy` file builds fine locally and fails only at **deploy** time. Triggers attached to an ignored workflow need the same directive.
62
64
 
63
65
  Detail: [wip-ignore.md](references/wip-ignore.md).
64
66
 
@@ -66,10 +68,10 @@ Detail: [wip-ignore.md](references/wip-ignore.md).
66
68
 
67
69
  | Command | Phase | Output |
68
70
  | ------------------ | ------- | ------------------------------------------- |
69
- | `keystroke build` | `build` | `dist/` for local inspection |
71
+ | `keystroke build` | `build` | `dist/` for inspection |
70
72
  | `keystroke deploy` | `deploy`| `dist/` + upload; honors `ignore:deploy` |
71
73
 
72
- `keystroke dev` / `keystroke start` use incremental builds (`clean: false`). Deploy always runs a fresh build first.
74
+ Deploy always runs a fresh, clean build before upload.
73
75
 
74
76
  Detail: [build-and-full-deploy.md](references/build-and-full-deploy.md).
75
77
 
@@ -78,13 +80,12 @@ Detail: [build-and-full-deploy.md](references/build-and-full-deploy.md).
78
80
  - **First deploy with `--filter`** — fails; run full deploy once.
79
81
  - **Wrong `--dir`** — builds a different tree than you edited.
80
82
  - **Expecting full refresh with `--filter`** — only matched modules rebuild; rest comes from active prod.
81
- - **Connected apps aren't in `.env`** — deploy never uploads `.env`; connect apps on the cloud target. See [apps skill](../keystroke-apps/SKILL.md).
83
+ - **Connected apps aren't in `.env`** — deploy never uploads `.env`; connect apps for your project. See [apps skill](../keystroke-apps/SKILL.md).
82
84
 
83
85
  ## Audit deployed runtime
84
86
 
85
87
  ```bash
86
- keystroke config use cloud
87
- keystroke --project <slug> project deployments list
88
+ keystroke project deployments list --project <slug>
88
89
  keystroke workflow run morning-check --input '{}'
89
90
  keystroke trigger list
90
91
  ```
@@ -7,7 +7,9 @@
7
7
  3. `packProjectArtifact` — gzip tarball of `dist/`.
8
8
  4. Platform `artifacts.create` → PUT tarball → `complete` → blue/green promote.
9
9
 
10
- Source files upload in parallel (content-addressed); failure there warns but does not block deploy.
10
+ Source files upload in parallel (content-addressed); failure there warns but does not block deploy. The dashboard source snapshot is capped (per-file 256KB, total 8MB, 2000 files) and skips binaries, `.env`, and `.log` files — so `.env` is never uploaded.
11
+
12
+ After upload, the CLI polls the project status (every ~2s, up to a 120s timeout) until it reports `active` or `failed`; on failure it surfaces the platform's `lastError`. A hang usually means the platform promotion is still in progress.
11
13
 
12
14
  ## Entry keys
13
15
 
@@ -9,6 +9,8 @@ keystroke deploy --project <slug> --filter workflows/morning-check
9
9
  keystroke deploy --project <slug> --filter agents/support --filter workflows/signup-pipeline
10
10
  ```
11
11
 
12
+ (`--project` can go before or after `deploy`, and can be omitted after the first deploy.)
13
+
12
14
  - Exact entry keys only — no globs, no negation, no slug aliases.
13
15
  - First deploy for a project must be **full** (no `--filter`).
14
16
  - Without an active artifact, deploy errors: run a full deploy first.
@@ -41,8 +43,7 @@ Change `keystroke.config.ts` or project-wide assets → run a **full** deploy.
41
43
  ## Verify
42
44
 
43
45
  ```bash
44
- keystroke --project <slug> project deployments list # new version active
45
- keystroke config use cloud
46
+ keystroke project deployments list --project <slug> # new version active
46
47
  keystroke workflow run <workflow-slug> --input '{}'
47
48
  ```
48
49
 
@@ -1,12 +1,15 @@
1
1
  # WIP modules and `@keystroke ignore`
2
2
 
3
- Place the directive at the **top** of a module file under `src/agents/`, `src/workflows/`, or `src/triggers/`.
3
+ Place the directive in the **leading comment block** of a module file under `src/agents/`, `src/workflows/`, or `src/triggers/` (the scan stops at the first non-comment line). Line (`//`) and block (`/* … */`, `*`) comments both work.
4
4
 
5
5
  ```ts
6
6
  // @keystroke ignore
7
7
  // @keystroke ignore:deploy
8
+ /** @keystroke ignore */
8
9
  ```
9
10
 
11
+ Only `ignore` and `ignore:deploy` are valid. Any other scope (e.g. `// @keystroke ignore:build`) throws `Unknown @keystroke ignore target "…"` at build time.
12
+
10
13
  ## Directives
11
14
 
12
15
  | Directive | Local build (`build`, `dev`, `start`) | Deploy (`phase: deploy`) |
@@ -21,7 +24,7 @@ Place the directive at the **top** of a module file under `src/agents/`, `src/wo
21
24
 
22
25
  ## Import guard
23
26
 
24
- If a **shipped** module imports a file marked `@keystroke ignore` (either form), the build **fails**. Compose in workflows instead of importing ignored actions from production modules.
27
+ If a **shipped** module imports a file marked `@keystroke ignore`, the build **fails in every phase**. If it imports an `@keystroke ignore:deploy` file, the local build/dev succeeds and only the **deploy** build fails (because `ignore:deploy` files are excluded only in the deploy phase). Compose in workflows instead of importing ignored actions from production modules.
25
28
 
26
29
  ## Triggers
27
30
 
@@ -15,11 +15,15 @@ Files under `src/files/` mount into the agent sandbox at `/workspace/agent/`.
15
15
  src/files/support/
16
16
  product-guide.md
17
17
  support-instructions.md
18
+ runbooks/
19
+ refunds.md
18
20
  ```
19
21
 
22
+ Nested directories are preserved — `src/files/support/runbooks/refunds.md` mounts at `/workspace/agent/runbooks/refunds.md`.
23
+
20
24
  ```ts
21
- import { defineAgent } from "@keystrokehq/agent";
22
- import { defineSandbox } from "@keystrokehq/sandbox";
25
+ import { defineAgent } from "@keystrokehq/keystroke/agent";
26
+ import { defineSandbox } from "@keystrokehq/keystroke/sandbox";
23
27
 
24
28
  export default defineAgent({
25
29
  slug: "support",
@@ -28,8 +32,12 @@ export default defineAgent({
28
32
  });
29
33
  ```
30
34
 
31
- Use `defineSandbox({ files: "shared" })` to mount `src/files/shared/` instead.
35
+ Use `defineSandbox({ files: "shared" })` to mount `src/files/shared/` instead — the string value names the folder under `src/files/`.
36
+
37
+ If the named folder doesn't exist, the build fails with a clear error — create the folder (it must contain at least one file) before deploying.
38
+
39
+ Files are seeded **write-once** at sandbox startup: the agent can edit them at runtime, but those edits aren't persisted back to `src/files/` and reset on the next run. Treat `src/files/` as read-only seed content.
32
40
 
33
- Point the agent at paths in `systemPrompt`. After `keystroke start`, verify behavior with `keystroke agent prompt support --message "…"`.
41
+ Point the agent at paths in `systemPrompt`. After deploy, verify behavior with `keystroke agent prompt support --message "…"`.
34
42
 
35
43
  Related: [agents](.agents/skills/keystroke-agents/SKILL.md), [skills](.agents/skills/keystroke-skills/SKILL.md).
@@ -28,10 +28,15 @@ At runtime → `/workspace/agent/skills/support/`.
28
28
 
29
29
  ## SKILL.md
30
30
 
31
- Follow [Agent Skills](https://agentskills.io/specification): YAML frontmatter with `name` (matches folder) and `description`. Keep the body short; put detail in `references/`.
31
+ Follow [Agent Skills](https://agentskills.io/specification): YAML frontmatter with `name` (matches the folder) and `description` are the required fields; everything else (e.g. `metadata`) is optional. Keep the body short; put detail in `references/`.
32
+
33
+ ## Two kinds of skills (don't confuse them)
34
+
35
+ - **`src/skills/`** — *project* Agent Skills attached to your agents via `skills: [...]`. They deploy with your project; inspect deployed ones with `keystroke skill list`.
36
+ - **`.agents/skills/`** — the *bundled coding-agent* guides (like this one) that `keystroke init` scaffolds. Refresh them to the current CLI version with `keystroke skills sync` (note the plural `skills`).
32
37
 
33
38
  ## External registries
34
39
 
35
- Browse [skills.sh](https://skills.sh) — copy into `src/skills/{key}/` and fix `name` to match the folder.
40
+ Browse [skills.sh](https://skills.sh) — copy into `src/skills/{name}/` and fix `name` to match the folder.
36
41
 
37
42
  Related: [agents](.agents/skills/keystroke-agents/SKILL.md), [files](.agents/skills/keystroke-files/SKILL.md).
@@ -7,18 +7,18 @@ metadata:
7
7
 
8
8
  # Triggers
9
9
 
10
- Triggers **attach** a source to a workflow. No business logic here — only schedule, endpoint, validation, and filters.
10
+ Triggers **attach** a source to a target (a workflow or an agent). No business logic here — only schedule, endpoint, validation, and filters.
11
11
 
12
- Attachment id: `{triggerKey}:{workflowKey}` (e.g. `signup:signup-pipeline`).
12
+ Attachment id: `{sourceSlug}:{targetSlug}` (e.g. `signup:signup-pipeline`), where the suffix is the workflow's (or agent's) `slug`.
13
13
 
14
14
  ## Cron
15
15
 
16
16
  ```ts
17
- import { defineCronSource } from "@keystrokehq/trigger";
17
+ import { defineCronSource } from "@keystrokehq/keystroke/trigger";
18
18
  import workflow from "../workflows/morning-check";
19
19
 
20
20
  export default defineCronSource({
21
- key: "morning-check",
21
+ slug: "morning-check",
22
22
  schedule: "0 9 * * *",
23
23
  }).attach({ workflow });
24
24
  ```
@@ -26,12 +26,12 @@ export default defineCronSource({
26
26
  ## Webhook
27
27
 
28
28
  ```ts
29
- import { defineWebhookSource } from "@keystrokehq/trigger";
29
+ import { defineWebhookSource } from "@keystrokehq/keystroke/trigger";
30
30
  import { z } from "zod";
31
31
  import workflow from "../workflows/signup-pipeline";
32
32
 
33
33
  export default defineWebhookSource({
34
- key: "signup",
34
+ slug: "signup",
35
35
  endpoint: "signup",
36
36
  request: z.object({
37
37
  name: z.string().trim().min(1),
@@ -47,20 +47,22 @@ Use optional Zod `filter` for extra constraints beyond `request`:
47
47
  filter: z.object({ type: z.literal("invoice.paid") }),
48
48
  ```
49
49
 
50
+ **Webhooks ack asynchronously.** The POST returns immediately — `202 { runId }` when a binding matches, or `{ ok: true, skipped: true }` when nothing does. The workflow runs in the background; its output is **not** returned in the HTTP response (there's no "respond to webhook"). To return data to the caller, make an outbound call from the workflow, or have the caller poll the run via the runs API / `keystroke workflow runs get`.
51
+
50
52
  ### Shared endpoint (e.g. Stripe)
51
53
 
52
- Multiple trigger files can use the same `endpoint` — one URL `POST /triggers/{endpoint}`, each with its own `key`, `request`, `filter`, and `transform`. Unmatched payloads return `{ ok: true, skipped: true }`.
54
+ Multiple trigger files can use the same `endpoint` — one URL `POST /triggers/{endpoint}`, each with its own `slug`, `request`, `filter`, and `transform`. Unmatched payloads return `{ ok: true, skipped: true }`.
53
55
 
54
56
  ```ts
55
57
  // src/triggers/stripe-invoice-paid.ts
56
58
  export default defineWebhookSource({
57
- key: "stripe-invoice-paid",
59
+ slug: "stripe-invoice-paid",
58
60
  endpoint: "stripe",
59
61
  request: z.object({ type: z.string(), data: z.object({ id: z.string() }) }),
60
62
  filter: z.object({ type: z.literal("invoice.paid") }),
61
63
  }).attach({ workflow: invoicePaidWorkflow, transform: (p) => ({ invoiceId: p.data.id }) });
62
64
 
63
- // src/triggers/stripe-subscription-deleted.ts — same endpoint, different key/schema/filter
65
+ // src/triggers/stripe-subscription-deleted.ts — same endpoint, different slug/schema/filter
64
66
  ```
65
67
 
66
68
  List or inspect all triggers on an endpoint:
@@ -71,21 +73,23 @@ keystroke trigger get stripe # same rows as list --endpoint (shared rou
71
73
  keystroke trigger url stripe # one webhook URL for the route
72
74
  ```
73
75
 
74
- Use each trigger's `key` for `trigger get` / run history (`keystroke trigger runs list stripe-invoice-paid:…`).
76
+ Use each trigger's `slug` for `trigger get` / run history (`keystroke trigger runs list stripe-invoice-paid:…`).
75
77
 
76
78
  ## Poll
77
79
 
78
80
  ```ts
79
- import { definePollSource } from "@keystrokehq/trigger";
81
+ import { definePollSource } from "@keystrokehq/keystroke/trigger";
80
82
  import workflow from "../workflows/new-inbox";
81
83
 
82
84
  export default definePollSource({
83
- key: "new-inbox",
85
+ slug: "new-inbox",
84
86
  schedule: "*/5 * * * *",
85
87
  run: () => ({ emails: [] }),
86
88
  }).attach({ workflow });
87
89
  ```
88
90
 
91
+ Poll filtering uses a **function predicate** (not a Zod schema like webhooks) — either `.filter((result) => …)` chained on the source, or a `filter:` option. Returning falsy skips the tick. Group polls that should run together with `definePollSource({ id: "...", … })`.
92
+
89
93
  ### Ephemeral poll (agents)
90
94
 
91
95
  Agents can register a scheduled codemode script with `set_trigger`:
@@ -93,7 +97,7 @@ Agents can register a scheduled codemode script with `set_trigger`:
93
97
  ```ts
94
98
  set_trigger({
95
99
  kind: "poll",
96
- key: "inbox",
100
+ slug: "inbox",
97
101
  schedule: "*/5 * * * *",
98
102
  code: [
99
103
  'const emails = await tools["list-emails"]({ query: "is:unread" });',
@@ -101,14 +105,22 @@ set_trigger({
101
105
  "console.log(JSON.stringify({ count: emails.items.length, items: emails.items }));",
102
106
  ].join("\n"),
103
107
  prompt: "You have {{payload.count}} unread emails.",
108
+ lifecycle: { maxExecutions: 10 }, // optional: cap runs (also `until`)
104
109
  });
105
110
  ```
106
111
 
107
112
  - Write the script the same way you would for codemode (`bash` + `js-exec`).
108
113
  - `console.log(JSON.stringify(result))` when there is work to do.
109
- - Log nothing (or `null`) to skip — skipped ticks do not count toward `maxExecutions`.
114
+ - Log nothing (or `null`) to skip — skipped ticks do not count toward `lifecycle.maxExecutions`.
110
115
  - Prompt interpolation matches webhooks (`{{payload.path}}`).
111
116
 
117
+ ## Attachment patterns
118
+
119
+ - **Attach to an agent** instead of a workflow: `.attach({ agent, prompt })` — the source's payload drives the agent prompt (interpolated like webhooks).
120
+ - **Fan-out to multiple targets**: chain `.attach(...).attach(...)` (or export an array of attachments) to bind one source to several workflows/agents.
121
+ - **Shared source definitions** can live under `src/triggers/sources/` — that subfolder is excluded from attachment discovery, so it's a safe place for source defs you import elsewhere.
122
+ - Sources also accept optional `name` / `description` metadata.
123
+
112
124
  ## Develop & audit
113
125
 
114
126
  While building, invoke the workflow directly:
@@ -120,11 +132,12 @@ keystroke workflow run signup-pipeline --input '{"name":"Ada","email":"a@acme.co
120
132
  Inspect trigger-driven runs:
121
133
 
122
134
  ```bash
123
- keystroke trigger runs list signup:signup-pipeline
135
+ keystroke trigger runs list signup:signup-pipeline # --limit / --cursor / --trigger-type
124
136
  keystroke trigger runs get signup:signup-pipeline <run-id> --include workflows,trace
125
- keystroke trigger poll <poll-attachment-id> # on-demand poll
137
+ keystroke trigger poll <poll-attachment-id> # on-demand poll (--group to run a poll group)
138
+ keystroke trigger attachment disable <trigger-slug> <attachment-id> # pause; `enable` to resume
126
139
  ```
127
140
 
128
141
  Check `src/triggers/` in your project for existing patterns before adding new ones.
129
142
 
130
- Related: [workflows](.agents/skills/keystroke-workflows/SKILL.md), [gateways](.agents/skills/keystroke-gateways/SKILL.md).
143
+ Related: [workflows](.agents/skills/keystroke-workflows/SKILL.md), [channels](.agents/skills/keystroke-channels/SKILL.md).
@@ -12,8 +12,8 @@ Workflows are **deterministic orchestration**: Zod input/output, a `run` functio
12
12
  ## Example: action chain
13
13
 
14
14
  ```ts
15
- import { defineWorkflow } from "@keystrokehq/workflow";
16
- import { postMessage } from "@keystrokehq/slack/actions";
15
+ import { defineWorkflow } from "@keystrokehq/keystroke/workflow";
16
+ import { slackSendMessage } from "@keystrokehq/slack/actions";
17
17
  import { z } from "zod";
18
18
  import { researchSignup } from "../actions/research-signup";
19
19
  import { signupBriefMessage } from "../lib/signup";
@@ -24,12 +24,16 @@ export default defineWorkflow({
24
24
  output: z.object({ brief: z.string(), channel: z.string(), ts: z.string() }),
25
25
  async run(input) {
26
26
  const { brief } = await researchSignup.run(input);
27
- return postMessage.run({ channel: "#pipeline", text: signupBriefMessage({ ...input, brief }) });
27
+ const sent = await slackSendMessage.run({
28
+ channel: "#pipeline",
29
+ markdown_text: signupBriefMessage({ ...input, brief }),
30
+ });
31
+ return { brief, ...sent }; // sent provides { channel, ts }; merge in brief for the output schema
28
32
  },
29
33
  });
30
34
  ```
31
35
 
32
- `research-signup` calls an agent; `postMessage` is an integration action used **directly as a step** here — never wrapped in a custom action. Keep orchestration in the workflow; an action is a single leaf step and never calls another action.
36
+ `research-signup` calls an agent; `slackSendMessage` is an integration action used **directly as a step** here — never wrapped in a custom action. Note the `run` return is spread to satisfy the `output` schema (the Slack action only returns `channel`/`ts`). Keep orchestration in the workflow; an action is a single leaf step and never calls another action.
33
37
 
34
38
  ## Run & audit
35
39
 
@@ -41,18 +45,29 @@ keystroke workflow runs get signup-pipeline <run-id> --include steps,trace
41
45
 
42
46
  ## How workflows get invoked
43
47
 
44
- | From | How |
45
- | ---------------- | --------------------------------------------------------------------- |
46
- | CLI | `keystroke workflow run {key} --input '{...}'` |
47
- | Trigger | cron / webhook / poll attachment in `src/triggers/` |
48
- | Agent tool | `defineWorkflowTool(workflow)` from `@keystrokehq/runtime` on an agent |
49
- | Another workflow | call from `run` |
48
+ | From | How |
49
+ | ---------- | --------------------------------------------------------------------- |
50
+ | CLI | `keystroke workflow run {slug} --input '{...}'` |
51
+ | HTTP | `POST /workflows/{slug}` |
52
+ | Trigger | cron / webhook / poll attachment in `src/triggers/` |
53
+ | Agent tool | `defineWorkflowTool(workflow)` from `@keystrokehq/runtime` on an agent |
50
54
 
51
- `key` must be unique across agents, workflows, triggers, and actions.
55
+ There is no first-class "call another workflow" step. To share logic between workflows, extract it into `src/lib/` (or an action, which adds typed IO and its own durable checkpoint). To run a workflow as its own tracked run, expose it as an agent tool (`defineWorkflowTool`) or invoke it over HTTP. Calling another workflow's `.run()` directly skips that workflow's input/output validation (the durable step context is preserved inside a run, but the IO schemas are not re-applied).
56
+
57
+ The workflow `slug` is its identifier and route key, and must be unique across agents, workflows, triggers, and actions.
58
+
59
+ ## Durability (the model you must design for)
60
+
61
+ Workflows are **durable**: each `await` of an action, agent `.prompt()`, or `promptLlm` is recorded as a `step_completed` event. If a later step fails, the run **replays** the log — completed steps return their recorded result instead of running again — and resumes at the first unfinished step. Two rules follow:
62
+
63
+ - **Side effects go inside steps.** Code in the `run` body that isn't a step (network calls, writes, `Date.now()`, random) re-executes on every replay. Wrap it in an action/agent/`promptLlm` call so it's recorded once.
64
+ - **Keep control flow deterministic, steps idempotent.** Branch on input and recorded step results, not on values that change between attempts. A step can be retried after a transient failure, so design actions to tolerate being called twice with the same input. Retries are automatic — you don't write retry loops; the runtime emits `step_retrying` / `step_failed`.
65
+
66
+ Step ids are correlation ids: `step:<slug>#<occurrence>` (`#0`, `#1`, …), or `step:<id>` when pinned with `.stepId()`. If an action's position can shift, pin a stable `.stepId()` so replays line up. Durable waits (`ctx.sleep`, `ctx.hook`) suspend the run without holding a process open. See [authoring.md](references/authoring.md).
52
67
 
53
68
  ## Testing
54
69
 
55
- Unit-test through `executeWorkflow` from `@keystrokehq/workflow` — never call `workflow.run(...)` directly (skips validation + action context). Stub costly actions by seeding `step_completed` events in a `MemoryEventLog`. See [testing.md](references/testing.md).
70
+ Unit-test through `executeWorkflow` from `@keystrokehq/keystroke/workflow` — never call `workflow.run(...)` directly (skips validation + action context). Stub costly actions by seeding `step_completed` events in a `MemoryEventLog`. See [testing.md](references/testing.md).
56
71
 
57
72
  ## Next references
58
73