@keystrokehq/skills 0.0.1

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 (44) hide show
  1. package/AGENTS-blurb.md +123 -0
  2. package/LICENSE +21 -0
  3. package/README.md +63 -0
  4. package/keystroke-agent-authoring/SKILL.md +225 -0
  5. package/keystroke-agent-authoring/evals/evals.json +29 -0
  6. package/keystroke-agent-authoring/references/messaging-gateways.md +242 -0
  7. package/keystroke-agent-authoring/references/patterns.md +417 -0
  8. package/keystroke-agent-authoring/references/prebuilt-integrations.md +879 -0
  9. package/keystroke-agent-authoring/references/sandbox-and-mcp.md +214 -0
  10. package/keystroke-agent-authoring/references/source-map.md +182 -0
  11. package/keystroke-agent-authoring/references/testing.md +85 -0
  12. package/keystroke-cli-workspace/SKILL.md +93 -0
  13. package/keystroke-cli-workspace/evals/evals.json +23 -0
  14. package/keystroke-cli-workspace/references/command-map.md +50 -0
  15. package/keystroke-cli-workspace/references/credentials-and-connect.md +79 -0
  16. package/keystroke-cli-workspace/references/project-lifecycle.md +85 -0
  17. package/keystroke-credential-binding/SKILL.md +509 -0
  18. package/keystroke-credential-binding/evals/evals.json +29 -0
  19. package/keystroke-credential-binding/references/cli.md +85 -0
  20. package/keystroke-credential-binding/references/patterns.md +878 -0
  21. package/keystroke-credential-binding/references/source-map.md +69 -0
  22. package/keystroke-data-toolkit/SKILL.md +59 -0
  23. package/keystroke-data-toolkit/evals/evals.json +23 -0
  24. package/keystroke-data-toolkit/references/usage.md +79 -0
  25. package/keystroke-task-authoring/SKILL.md +124 -0
  26. package/keystroke-task-authoring/evals/evals.json +23 -0
  27. package/keystroke-task-authoring/references/patterns.md +132 -0
  28. package/keystroke-task-authoring/references/source-map.md +61 -0
  29. package/keystroke-trigger-authoring/SKILL.md +189 -0
  30. package/keystroke-trigger-authoring/evals/evals.json +29 -0
  31. package/keystroke-trigger-authoring/references/patterns.md +265 -0
  32. package/keystroke-trigger-authoring/references/source-map.md +128 -0
  33. package/keystroke-trigger-authoring/references/testing.md +148 -0
  34. package/keystroke-workflow-as-tool-debugging/SKILL.md +52 -0
  35. package/keystroke-workflow-as-tool-debugging/evals/evals.json +23 -0
  36. package/keystroke-workflow-as-tool-debugging/references/playbook.md +77 -0
  37. package/keystroke-workflow-authoring/SKILL.md +234 -0
  38. package/keystroke-workflow-authoring/evals/evals.json +29 -0
  39. package/keystroke-workflow-authoring/references/patterns.md +265 -0
  40. package/keystroke-workflow-authoring/references/prebuilt-integrations.md +811 -0
  41. package/keystroke-workflow-authoring/references/runtime-helpers.md +264 -0
  42. package/keystroke-workflow-authoring/references/source-map.md +108 -0
  43. package/keystroke-workflow-authoring/references/testing.md +108 -0
  44. package/package.json +26 -0
@@ -0,0 +1,264 @@
1
+ # Workflow Runtime Helpers
2
+
3
+ Read this file when the user asks what they can access from `Workflow.run(...)` or `Step.run(...)`.
4
+
5
+ `Operation`, `Step`, and `Tool` are aliases for the same class. In this workflow reference, the operation runtime context is described with the workflow-oriented `Step` naming.
6
+
7
+ ## Workflow runtime boundary
8
+
9
+ Treat authored workflows as TypeScript orchestration code, not as a shell runtime.
10
+
11
+ Teach these rules explicitly:
12
+ - a workflow cannot run bash commands as part of the workflow authoring model
13
+ - a step is not the place to shell out to `python`, `pnpm`, or arbitrary binaries by default
14
+ - a workflow is for control flow, typed inputs and outputs, waits, hooks, and calls to steps, child workflows, or agents
15
+ - if the task needs shell access, persistent filesystem state, or tool installation, move that work into an agent
16
+ - if the agent needs Python or another binary, install it in the sandbox through sandbox setup or runtime preparation
17
+
18
+ ## Workflow `ctx.wait(duration)`
19
+
20
+ ```ts
21
+ run: async (_input, ctx) => {
22
+ await ctx.wait('10m');
23
+ return { resumed: true };
24
+ };
25
+ ```
26
+
27
+ Use this when the workflow should pause durably and resume later.
28
+
29
+ In authored workflow code, treat `ctx.wait(...)` as taking duration strings.
30
+
31
+ Common public examples are:
32
+
33
+ - `await ctx.wait('30s')`
34
+ - `await ctx.wait('10m')`
35
+ - `await ctx.wait('2h')`
36
+ - `await ctx.wait('3d')`
37
+ - `await ctx.wait('1w')`
38
+ - `await ctx.wait('1y')`
39
+
40
+ The executor normalizes waits into milliseconds internally, but the public workflow authoring surface to teach by default is the unit-based string form.
41
+
42
+ When a workflow may be used as an agent tool, prefer adding intent metadata so yield receipts are understandable:
43
+
44
+ ```ts
45
+ run: async (_input, ctx) => {
46
+ await ctx.wait('30m', {
47
+ intent: 'data-settlement',
48
+ reason: 'The provider needs time to settle before final reconciliation.',
49
+ });
50
+
51
+ return { resumed: true };
52
+ };
53
+ ```
54
+
55
+ ## Workflow `ctx.createHook(name)`
56
+
57
+ ```ts
58
+ run: async (_input, ctx) => {
59
+ const approval = ctx.createHook('approval');
60
+ await approval;
61
+
62
+ return { approved: true };
63
+ };
64
+ ```
65
+
66
+ Use this when the workflow needs an external approval or resume point.
67
+
68
+ The hook returned by `ctx.createHook(name)` is promise-like, so you can `await` it directly.
69
+
70
+ Think of a hook as a set of workflow suspension endpoints:
71
+
72
+ - `resumeUrl`: endpoint that resumes the workflow
73
+ - `cancelUrl`: endpoint that cancels the hook
74
+ - `approvalPageUrl`: URL for a built-in approval page or human-facing approval flow
75
+ - `token`: stable identifier for the hook instance
76
+
77
+ It also exposes these properties:
78
+
79
+ - `token`
80
+ - `resumeUrl`
81
+ - `cancelUrl`
82
+ - `approvalPageUrl`
83
+
84
+ When the hook may be mediated through an agent tool, include intent metadata:
85
+
86
+ ```ts
87
+ run: async (_input, ctx) => {
88
+ const approval = ctx.createHook('approval', {
89
+ intent: 'human-decision',
90
+ prompt: 'Approve the refund?',
91
+ reason: 'Refunds above the policy threshold require approval.',
92
+ });
93
+
94
+ const result = await approval;
95
+ return { result };
96
+ };
97
+ ```
98
+
99
+ Example:
100
+
101
+ ```ts
102
+ run: async (_input, ctx) => {
103
+ const approval = ctx.createHook('approval');
104
+
105
+ const token = await approval.token;
106
+ const resumeUrl = await approval.resumeUrl;
107
+
108
+ return {
109
+ token,
110
+ resumeUrl,
111
+ };
112
+ };
113
+ ```
114
+
115
+ You can also use the resumed payload in the workflow.
116
+
117
+ If the external caller resumes the hook with structured data, `await approval` resolves to that payload:
118
+
119
+ ```ts
120
+ run: async (_input, ctx) => {
121
+ const approval = ctx.createHook('approval');
122
+ const approvalPageUrl = await approval.approvalPageUrl;
123
+
124
+ // Send approvalPageUrl to a human or external system here.
125
+
126
+ const result = (await approval) as {
127
+ approved?: boolean;
128
+ reviewer?: string;
129
+ note?: string;
130
+ };
131
+
132
+ return {
133
+ approved: result.approved === true,
134
+ reviewer: result.reviewer ?? 'unknown',
135
+ note: result.note ?? '',
136
+ approvalPageUrl,
137
+ };
138
+ };
139
+ ```
140
+
141
+ You can also create hooks in a loop when the workflow should wait for repeated review until a condition is met:
142
+
143
+ ```ts
144
+ run: async (_input, ctx) => {
145
+ let round = 1;
146
+ let latestNote = '';
147
+
148
+ while (true) {
149
+ const review = ctx.createHook(`approval-round-${round}`);
150
+ const result = (await review) as {
151
+ approved?: boolean;
152
+ note?: string;
153
+ };
154
+
155
+ latestNote = result.note ?? '';
156
+
157
+ if (result.approved === true) {
158
+ return {
159
+ approved: true,
160
+ rounds: round,
161
+ latestNote,
162
+ };
163
+ }
164
+
165
+ round += 1;
166
+ }
167
+ };
168
+ ```
169
+
170
+ ## Workflow `ctx.workflowGlobals`
171
+
172
+ ```ts
173
+ workflowGlobals: z.object({
174
+ tenantId: z.string(),
175
+ }),
176
+ run: async (_input, ctx) => {
177
+ return { tenantId: ctx.workflowGlobals.tenantId };
178
+ };
179
+ ```
180
+
181
+ Use this for typed workflow-wide runtime values.
182
+
183
+ ## Workflow `ctx.workflowId`
184
+
185
+ ```ts
186
+ run: async (_input, ctx) => {
187
+ return { workflowId: ctx.workflowId };
188
+ };
189
+ ```
190
+
191
+ Use this when the workflow output or logs need the current run id.
192
+
193
+ ## Workflow `ctx.hasCredentialSet(id)`
194
+
195
+ ```ts
196
+ run: async (input, ctx) => {
197
+ if (ctx.hasCredentialSet('slack')) {
198
+ await sendSlackMessage.run({
199
+ channel: input.channel,
200
+ text: input.summary,
201
+ });
202
+ }
203
+
204
+ return { ok: true };
205
+ };
206
+ ```
207
+
208
+ Use this for graceful degradation when a credential-backed branch is genuinely optional. Build analysis recognizes literal guards such as `ctx.hasCredentialSet('slack')` and treats matching credentials as conditional instead of required. Dynamic ids are not recognized.
209
+
210
+ ## Operation / Step `ctx.credentials`
211
+
212
+ ```ts
213
+ run: async (_input, ctx) => {
214
+ return { apiKeyUsed: ctx.credentials.crmApi.apiKey };
215
+ };
216
+ ```
217
+
218
+ ## Operation / Step `ctx.workflowGlobals`
219
+
220
+ ```ts
221
+ run: async (_input, ctx) => {
222
+ return { tenantId: ctx.workflowGlobals.tenantId };
223
+ };
224
+ ```
225
+
226
+ ## Operation / Step `ctx.attempt`, `ctx.maxAttempts`, and `ctx.stepId`
227
+
228
+ ```ts
229
+ run: async (_input, ctx) => {
230
+ return {
231
+ attempt: ctx.attempt ?? 1,
232
+ maxAttempts: ctx.maxAttempts ?? 1,
233
+ stepId: ctx.stepId ?? 'unknown',
234
+ };
235
+ };
236
+ ```
237
+
238
+ Use these when operation behavior depends on retry state or when you want the step id in output or logs.
239
+
240
+ ## Public testing helpers
241
+
242
+ Import these from `@keystrokehq/core/vitest`:
243
+
244
+ - `keystrokeTestPlugin`
245
+ - `createTestRuntime`
246
+ - `createTestStepContext`
247
+ - `createMockHook`
248
+
249
+ ### What they are used for
250
+
251
+ - `keystrokeTestPlugin`: Vitest plugin that wires core test setup into the test process
252
+ - `createTestRuntime`: build a full workflow test runtime with workflow context plus step execution metadata
253
+ - `createTestStepContext`: build only the operation / step context for isolated tests
254
+ - `createMockHook`: create an immediately resolving hook for tests that would otherwise block on `ctx.createHook(...)`
255
+
256
+ Example:
257
+
258
+ ```ts
259
+ import { createMockHook } from '@keystrokehq/core/vitest';
260
+
261
+ const hook = createMockHook();
262
+ await hook;
263
+ const resumeUrl = await hook.resumeUrl;
264
+ ```
@@ -0,0 +1,108 @@
1
+ # Workflow Feature Map
2
+
3
+ Use only the public imports a user repo can rely on:
4
+
5
+ ```ts
6
+ import { Operation, Step, Workflow } from '@keystrokehq/core';
7
+ import {
8
+ createMockHook,
9
+ createTestRuntime,
10
+ createTestStepContext,
11
+ keystrokeTestPlugin,
12
+ } from '@keystrokehq/core/vitest';
13
+ ```
14
+
15
+ `Operation`, `Step`, and `Tool` are aliases for the same class. In this workflow skill, prefer `Step` for examples and explanations, but `Operation` is equally valid for shared or integration-oriented code.
16
+
17
+ ## `Workflow` config fields
18
+
19
+ - `id`
20
+ - `name`
21
+ - `description`
22
+ - `author`
23
+ - `version`
24
+ - `tags`
25
+ - `input`
26
+ - `output`
27
+ - `triggers`
28
+ - `timeout`
29
+ - `retries`
30
+ - `workflowGlobals`
31
+ - `run`
32
+
33
+ ## `Workflow` instance methods
34
+
35
+ - `run(input, runtimeOrContextOrOptions?)`
36
+ - `withTimeout(duration)`
37
+ - `retry(policy)`
38
+ - `describe()`
39
+ - `toManifest()`
40
+
41
+ ### What they are used for
42
+
43
+ - `run(...)`: execute the workflow directly
44
+ - `withTimeout(...)`: create a new workflow instance with a different timeout
45
+ - `retry(...)`: create a new workflow instance with a different retry policy
46
+ - `describe()`: return a human-readable summary string
47
+ - `toManifest()`: return the core manifest description
48
+
49
+ Teach this distinction when it matters:
50
+ - `workflow.toManifest()` returns the primitive-level core manifest
51
+ - the build pipeline enriches that into the full built workflow manifest
52
+
53
+ ## `Workflow.run` context
54
+
55
+ - `ctx.wait(duration)`
56
+ - `ctx.createHook(name)`
57
+ - `ctx.workflowGlobals`
58
+ - `ctx.workflowId`
59
+
60
+ ## `Operation` / `Step` config fields
61
+
62
+ - `name`
63
+ - `description`
64
+ - `input`
65
+ - `output`
66
+ - `timeout`
67
+ - `tags`
68
+ - `retries`
69
+ - `workflowGlobals`
70
+ - `credentialSets`
71
+ - `needsApproval`
72
+ - `run`
73
+
74
+ ## `Operation` / `Step` instance methods
75
+
76
+ - `run(input, ctx?)`
77
+ - `withTimeout(duration)`
78
+ - `retry(policy)`
79
+ - `configure(overrides)`
80
+ - `mapInput(schema, fn)`
81
+ - `mapOutput(schema, fn)`
82
+ - `describe()`
83
+ - `toManifest()`
84
+
85
+ ### What they are used for
86
+
87
+ - `run(...)`: execute the operation directly
88
+ - `withTimeout(...)`: create a new operation instance with a different timeout
89
+ - `retry(...)`: create a new operation instance with a different retry policy
90
+ - `configure(...)`: create a new operation instance with updated metadata such as description, timeout, retries, tags, or approval metadata
91
+ - `mapInput(...)`: create a wrapper operation with a new input schema and input transformation
92
+ - `mapOutput(...)`: create a wrapper operation with a new output schema and output transformation
93
+ - `describe()`: return a human-readable summary string
94
+ - `toManifest()`: return the operation manifest description
95
+
96
+ ## `Operation.run` / `Step.run` context
97
+
98
+ - `ctx.credentials`
99
+ - `ctx.workflowGlobals`
100
+ - `ctx.attempt`
101
+ - `ctx.maxAttempts`
102
+ - `ctx.stepId`
103
+
104
+ ## Where to read next
105
+
106
+ - `patterns.md` shows workflow and operation examples
107
+ - `runtime-helpers.md` shows public context examples
108
+ - `testing.md` shows public testing examples
@@ -0,0 +1,108 @@
1
+ # Workflow Testing
2
+
3
+ Read this file when the user asks how to test a Keystroke workflow or workflow-facing operation.
4
+
5
+ Assume `helloWorkflow`, `accountSyncWorkflow`, `loadCustomer`, `paymentWebhook`, and `paymentWorkflow` are the workflow, operation, and trigger instances shown elsewhere in this skill.
6
+
7
+ ## Vitest setup
8
+
9
+ `keystrokeTestPlugin()` adds the core test setup file to Vitest. Use it when you want normal `workflow.run(...)` and `step.run(...)` calls to work in tests without building custom harness code first.
10
+
11
+ ```ts
12
+ import { defineConfig } from 'vitest/config';
13
+ import { keystrokeTestPlugin } from '@keystrokehq/core/vitest';
14
+
15
+ export default defineConfig({
16
+ plugins: [keystrokeTestPlugin()],
17
+ });
18
+ ```
19
+
20
+ ## Test a workflow directly
21
+
22
+ ```ts
23
+ import { expect, test } from 'vitest';
24
+
25
+ test('returns the greeting', async () => {
26
+ await expect(helloWorkflow.run({ name: 'Ada' })).resolves.toEqual({
27
+ message: 'Hello, Ada!',
28
+ });
29
+ });
30
+ ```
31
+
32
+ ## Test a workflow with globals or hooks
33
+
34
+ `createTestRuntime()` builds both sides of the workflow test runtime:
35
+
36
+ - `runtime.context` for the workflow `ctx`
37
+ - `runtime.stepContext` for operation execution metadata such as credentials, retry info, and workflow globals
38
+
39
+ Use it when a workflow test needs more than plain input data.
40
+
41
+ ```ts
42
+ import { createMockHook, createTestRuntime } from '@keystrokehq/core/vitest';
43
+
44
+ const runtime = createTestRuntime({
45
+ workflowGlobals: {
46
+ tenantId: 'tenant_123',
47
+ },
48
+ createHook: () => createMockHook(),
49
+ });
50
+
51
+ await accountSyncWorkflow.run(
52
+ { accountId: 'acct_123', waitForApproval: true },
53
+ runtime.context
54
+ );
55
+ ```
56
+
57
+ ## Test a step directly
58
+
59
+ `createTestStepContext()` is the smaller helper for operation unit tests. Use it when you want to call `step.run(...)` or `operation.run(...)` directly and provide only the runtime context values that operation reads.
60
+
61
+ ```ts
62
+ import { createTestStepContext } from '@keystrokehq/core/vitest';
63
+
64
+ const stepContext = createTestStepContext({
65
+ credentials: {
66
+ crmApi: {
67
+ apiKey: 'test-key',
68
+ },
69
+ },
70
+ workflowGlobals: {
71
+ tenantId: 'tenant_123',
72
+ },
73
+ attempt: 2,
74
+ maxAttempts: 4,
75
+ stepId: 'load-customer',
76
+ });
77
+
78
+ await loadCustomer.run({ customerId: 'cus_123' }, stepContext);
79
+ ```
80
+
81
+ ## Test a trigger transform
82
+
83
+ Create a bound trigger by calling the trigger with `{ transform }`, then call `bound.transform?.(payload)` to verify the mapping from trigger payload to workflow input.
84
+
85
+ ```ts
86
+ const bound = paymentWebhook({
87
+ transform: (payload) => ({
88
+ eventId: payload.id,
89
+ amount: payload.amount,
90
+ }),
91
+ });
92
+
93
+ const input = await bound.transform?.(
94
+ { id: 'evt_123', type: 'payment.completed', amount: 5000 }
95
+ );
96
+
97
+ expect(input).toEqual({ eventId: 'evt_123', amount: 5000 });
98
+ await paymentWorkflow.run(input);
99
+ ```
100
+
101
+ ## What to test
102
+
103
+ - workflow happy path
104
+ - operation / step happy path
105
+ - waits and hooks when they affect behavior
106
+ - workflow globals validation
107
+ - credential-backed operation behavior
108
+ - bound trigger transforms when triggers are involved
package/package.json ADDED
@@ -0,0 +1,26 @@
1
+ {
2
+ "name": "@keystrokehq/skills",
3
+ "version": "0.0.1",
4
+ "private": false,
5
+ "type": "module",
6
+ "publishConfig": {
7
+ "access": "public",
8
+ "registry": "https://registry.npmjs.org/"
9
+ },
10
+ "files": [
11
+ "README.md",
12
+ "AGENTS-blurb.md",
13
+ "keystroke-workflow-authoring",
14
+ "keystroke-agent-authoring",
15
+ "keystroke-data-toolkit",
16
+ "keystroke-workflow-as-tool-debugging",
17
+ "keystroke-credential-binding",
18
+ "keystroke-trigger-authoring",
19
+ "keystroke-task-authoring",
20
+ "keystroke-cli-workspace"
21
+ ],
22
+ "scripts": {
23
+ "build": "node -e \"process.exit(0)\"",
24
+ "lint": "biome check ."
25
+ }
26
+ }