agent-state-machine 2.0.13 → 2.0.15

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 (36) hide show
  1. package/README.md +19 -6
  2. package/bin/cli.js +23 -5
  3. package/lib/setup.js +82 -388
  4. package/package.json +2 -1
  5. package/templates/project-builder/README.md +119 -0
  6. package/templates/project-builder/agents/assumptions-clarifier.md +66 -0
  7. package/templates/project-builder/agents/code-reviewer.md +82 -0
  8. package/templates/project-builder/agents/code-writer.md +75 -0
  9. package/templates/project-builder/agents/requirements-clarifier.md +56 -0
  10. package/templates/project-builder/agents/roadmap-generator.md +74 -0
  11. package/templates/project-builder/agents/scope-clarifier.md +45 -0
  12. package/templates/project-builder/agents/security-clarifier.md +72 -0
  13. package/templates/project-builder/agents/security-reviewer.md +72 -0
  14. package/templates/project-builder/agents/task-planner.md +63 -0
  15. package/templates/project-builder/agents/test-planner.md +77 -0
  16. package/templates/project-builder/config.js +13 -0
  17. package/templates/project-builder/scripts/mac-notification.js +24 -0
  18. package/templates/project-builder/scripts/text-human.js +92 -0
  19. package/templates/project-builder/scripts/workflow-helpers.js +167 -0
  20. package/templates/project-builder/state/current.json +9 -0
  21. package/templates/project-builder/state/history.jsonl +0 -0
  22. package/templates/project-builder/steering/config.json +5 -0
  23. package/templates/project-builder/steering/global.md +19 -0
  24. package/templates/project-builder/workflow.js +394 -0
  25. package/templates/starter/README.md +118 -0
  26. package/templates/starter/agents/example.js +36 -0
  27. package/templates/starter/agents/yoda-greeter.md +12 -0
  28. package/templates/starter/agents/yoda-name-collector.md +12 -0
  29. package/templates/starter/config.js +12 -0
  30. package/templates/starter/interactions/.gitkeep +0 -0
  31. package/templates/starter/scripts/mac-notification.js +24 -0
  32. package/templates/starter/state/current.json +9 -0
  33. package/templates/starter/state/history.jsonl +0 -0
  34. package/templates/starter/steering/config.json +5 -0
  35. package/templates/starter/steering/global.md +19 -0
  36. package/templates/starter/workflow.js +52 -0
@@ -0,0 +1,394 @@
1
+ /**
2
+ * Project Builder Workflow
3
+ *
4
+ * A comprehensive workflow that guides users through:
5
+ * 1. Project intake and clarification
6
+ * 2. Phased roadmap generation and approval
7
+ * 3. Sequential phase execution with task lists
8
+ * 4. Task lifecycle with optimal agent sequencing
9
+ */
10
+
11
+ import { memory, askHuman } from 'agent-state-machine';
12
+ import path from 'path';
13
+ import { fileURLToPath } from 'url';
14
+ import {
15
+ writeMarkdownFile,
16
+ isApproval,
17
+ renderRoadmapMarkdown,
18
+ renderTasksMarkdown,
19
+ safeAgent,
20
+ TASK_STAGES,
21
+ getTaskStage,
22
+ setTaskStage,
23
+ getTaskData,
24
+ setTaskData
25
+ } from './scripts/workflow-helpers.js';
26
+
27
+ // Derive workflow directory dynamically
28
+ const __filename = fileURLToPath(import.meta.url);
29
+ const __dirname = path.dirname(__filename);
30
+ const WORKFLOW_DIR = __dirname;
31
+ const STATE_DIR = path.join(WORKFLOW_DIR, 'state');
32
+
33
+ // ============================================
34
+ // MAIN WORKFLOW
35
+ // ============================================
36
+
37
+ export default async function () {
38
+ console.log('Starting Project Builder Workflow...\n');
39
+
40
+ // ============================================
41
+ // PHASE 1: PROJECT INTAKE
42
+ // ============================================
43
+ console.log('=== PHASE 1: PROJECT INTAKE ===\n');
44
+
45
+ if (!memory.projectDescription) {
46
+ const description = await askHuman(
47
+ 'Describe the project you want to build. Include any initial requirements, goals, or constraints you have in mind.',
48
+ { slug: 'project-description' }
49
+ );
50
+ memory.projectDescription = description;
51
+ }
52
+
53
+ console.log('Project description captured. Starting clarification process...\n');
54
+
55
+ // ============================================
56
+ // CLARIFICATION AGENTS (Optimal Sequence)
57
+ // Order: Scope -> Requirements -> Assumptions -> Security
58
+ // Runtime handles interaction blocking automatically
59
+ // ============================================
60
+
61
+ // 1. Scope Clarification
62
+ if (!memory.scopeClarified) {
63
+ console.log('--- Scope Clarification ---');
64
+ const scopeResult = await safeAgent('scope-clarifier', {
65
+ projectDescription: memory.projectDescription
66
+ });
67
+ memory.scope = scopeResult;
68
+ memory.scopeClarified = true;
69
+ }
70
+
71
+ // 2. Requirements Clarification
72
+ if (!memory.requirementsClarified) {
73
+ console.log('--- Requirements Clarification ---');
74
+ const reqResult = await safeAgent('requirements-clarifier', {
75
+ projectDescription: memory.projectDescription,
76
+ scope: memory.scope
77
+ });
78
+ memory.requirements = reqResult;
79
+ memory.requirementsClarified = true;
80
+ }
81
+
82
+ // 3. Assumptions Clarification
83
+ if (!memory.assumptionsClarified) {
84
+ console.log('--- Assumptions Clarification ---');
85
+ const assumeResult = await safeAgent('assumptions-clarifier', {
86
+ projectDescription: memory.projectDescription,
87
+ scope: memory.scope,
88
+ requirements: memory.requirements
89
+ });
90
+ memory.assumptions = assumeResult;
91
+ memory.assumptionsClarified = true;
92
+ }
93
+
94
+ // 4. Security Clarification
95
+ if (!memory.securityClarified) {
96
+ console.log('--- Security Clarification ---');
97
+ const secResult = await safeAgent('security-clarifier', {
98
+ projectDescription: memory.projectDescription,
99
+ scope: memory.scope,
100
+ requirements: memory.requirements,
101
+ assumptions: memory.assumptions
102
+ });
103
+ memory.security = secResult;
104
+ memory.securityClarified = true;
105
+ }
106
+
107
+ console.log('\nClarification complete. Generating roadmap...\n');
108
+
109
+ // ============================================
110
+ // PHASE 2: PHASED ROADMAP
111
+ // ============================================
112
+ console.log('=== PHASE 2: PHASED ROADMAP ===\n');
113
+
114
+ if (!memory.roadmapApproved) {
115
+ // Generate roadmap as JSON
116
+ if (!memory.roadmap) {
117
+ const roadmapResult = await safeAgent('roadmap-generator', {
118
+ projectDescription: memory.projectDescription,
119
+ scope: memory.scope,
120
+ requirements: memory.requirements,
121
+ assumptions: memory.assumptions,
122
+ security: memory.security
123
+ });
124
+ memory.roadmap = roadmapResult;
125
+ }
126
+
127
+ writeMarkdownFile(STATE_DIR, 'roadmap.md', renderRoadmapMarkdown(memory.roadmap));
128
+
129
+ // Roadmap approval loop
130
+ let approved = false;
131
+ while (!approved) {
132
+ const reviewResponse = await askHuman(
133
+ `Please review the roadmap in state/roadmap.md\n\nOptions:\n- A: Approve roadmap as-is\n- B: Request changes (describe what to change)\n\nYour choice:`,
134
+ { slug: 'roadmap-review' }
135
+ );
136
+
137
+ if (isApproval(reviewResponse)) {
138
+ approved = true;
139
+ memory.roadmapApproved = true;
140
+ console.log('Roadmap approved!\n');
141
+ } else {
142
+ // Regenerate roadmap with feedback
143
+ const updatedRoadmap = await safeAgent('roadmap-generator', {
144
+ projectDescription: memory.projectDescription,
145
+ scope: memory.scope,
146
+ requirements: memory.requirements,
147
+ assumptions: memory.assumptions,
148
+ security: memory.security,
149
+ feedback: reviewResponse
150
+ });
151
+ memory.roadmap = updatedRoadmap;
152
+ writeMarkdownFile(STATE_DIR, 'roadmap.md', renderRoadmapMarkdown(memory.roadmap));
153
+ }
154
+ }
155
+ }
156
+
157
+ // ============================================
158
+ // PHASE 3: PHASE EXECUTION
159
+ // ============================================
160
+ console.log('=== PHASE 3: PHASE EXECUTION ===\n');
161
+
162
+ // Initialize phase tracking with proper undefined check
163
+ if (memory.currentPhaseIndex === undefined) {
164
+ memory.currentPhaseIndex = 0;
165
+ }
166
+
167
+ const phases = memory.roadmap?.phases || [];
168
+
169
+ // Sequential phase processing
170
+ for (let i = memory.currentPhaseIndex; i < phases.length; i++) {
171
+ memory.currentPhaseIndex = i;
172
+ const phase = phases[i];
173
+ console.log(`\n--- Processing Phase ${i + 1}: ${phase.title} ---\n`);
174
+
175
+ const tasksKey = `phase_${i}_tasks`;
176
+ const tasksApprovedKey = `phase_${i}_tasks_approved`;
177
+
178
+ // Generate task list for this phase (as JSON)
179
+ if (!memory[tasksApprovedKey]) {
180
+ if (!memory[tasksKey]) {
181
+ const taskResult = await safeAgent('task-planner', {
182
+ projectDescription: memory.projectDescription,
183
+ scope: memory.scope,
184
+ requirements: memory.requirements,
185
+ phase: phase,
186
+ phaseIndex: i + 1
187
+ });
188
+ memory[tasksKey] = taskResult;
189
+ }
190
+
191
+ writeMarkdownFile(STATE_DIR, `phase-${i + 1}-tasks.md`, renderTasksMarkdown(i + 1, phase.title, memory[tasksKey]?.tasks || memory[tasksKey]));
192
+
193
+ // Task list approval loop
194
+ let tasksApproved = false;
195
+ while (!tasksApproved) {
196
+ const taskReview = await askHuman(
197
+ `Please review the task list for Phase ${i + 1} in state/phase-${i + 1}-tasks.md\n\nOptions:\n- A: Approve task list\n- B: Request changes (describe what to change)\n\nYour choice:`,
198
+ { slug: `phase-${i + 1}-task-review` }
199
+ );
200
+
201
+ if (isApproval(taskReview)) {
202
+ tasksApproved = true;
203
+ memory[tasksApprovedKey] = true;
204
+ console.log(`Phase ${i + 1} task list approved!\n`);
205
+ } else {
206
+ const updatedTasks = await safeAgent('task-planner', {
207
+ projectDescription: memory.projectDescription,
208
+ scope: memory.scope,
209
+ requirements: memory.requirements,
210
+ phase: phase,
211
+ phaseIndex: i + 1,
212
+ feedback: taskReview
213
+ });
214
+ memory[tasksKey] = updatedTasks;
215
+ writeMarkdownFile(STATE_DIR, `phase-${i + 1}-tasks.md`, renderTasksMarkdown(i + 1, phase.title, memory[tasksKey]?.tasks || memory[tasksKey]));
216
+ }
217
+ }
218
+ }
219
+
220
+ // ============================================
221
+ // TASK LIFECYCLE WITH IDEMPOTENCY
222
+ // ============================================
223
+ console.log(`\n=== TASK LIFECYCLE: Phase ${i + 1} ===\n`);
224
+
225
+ const tasks = memory[tasksKey]?.tasks || memory[tasksKey] || [];
226
+ const taskIndexKey = `phase_${i}_task_index`;
227
+
228
+ // Fix: use undefined check instead of falsy check
229
+ if (memory[taskIndexKey] === undefined) {
230
+ memory[taskIndexKey] = 0;
231
+ }
232
+
233
+ // Process each task with optimal agent ordering and idempotency
234
+ for (let t = memory[taskIndexKey]; t < tasks.length; t++) {
235
+ memory[taskIndexKey] = t;
236
+ const task = tasks[t];
237
+ const taskId = task.id || t;
238
+
239
+ console.log(`\n Task ${t + 1}/${tasks.length}: ${task.title}\n`);
240
+
241
+ // Get current stage for this task
242
+ let stage = getTaskStage(i, taskId);
243
+
244
+ // Store any feedback for this task
245
+ const feedback = getTaskData(i, taskId, 'feedback');
246
+
247
+ try {
248
+ // 1. Security Review (pre-implementation)
249
+ if (stage === TASK_STAGES.PENDING || stage === TASK_STAGES.SECURITY_PRE) {
250
+ if (!getTaskData(i, taskId, 'security_pre')) {
251
+ console.log(' > Security pre-review...');
252
+ const securityPreReview = await safeAgent('security-reviewer', {
253
+ task: task,
254
+ phase: phase,
255
+ scope: memory.scope,
256
+ stage: 'pre-implementation',
257
+ feedback: feedback
258
+ });
259
+ setTaskData(i, taskId, 'security_pre', securityPreReview);
260
+ }
261
+ setTaskStage(i, taskId, TASK_STAGES.TEST_PLANNING);
262
+ stage = TASK_STAGES.TEST_PLANNING;
263
+ }
264
+
265
+ // 2. Test Planning
266
+ if (stage === TASK_STAGES.TEST_PLANNING) {
267
+ if (!getTaskData(i, taskId, 'tests')) {
268
+ console.log(' > Test planning...');
269
+ const testPlan = await safeAgent('test-planner', {
270
+ task: task,
271
+ phase: phase,
272
+ requirements: memory.requirements,
273
+ securityConsiderations: getTaskData(i, taskId, 'security_pre'),
274
+ feedback: feedback
275
+ });
276
+ setTaskData(i, taskId, 'tests', testPlan);
277
+ }
278
+ setTaskStage(i, taskId, TASK_STAGES.IMPLEMENTING);
279
+ stage = TASK_STAGES.IMPLEMENTING;
280
+ }
281
+
282
+ // 3. Code Writing
283
+ if (stage === TASK_STAGES.IMPLEMENTING) {
284
+ if (!getTaskData(i, taskId, 'code')) {
285
+ console.log(' > Code implementation...');
286
+ const implementation = await safeAgent('code-writer', {
287
+ task: task,
288
+ phase: phase,
289
+ requirements: memory.requirements,
290
+ testPlan: getTaskData(i, taskId, 'tests'),
291
+ securityConsiderations: getTaskData(i, taskId, 'security_pre'),
292
+ feedback: feedback
293
+ });
294
+ setTaskData(i, taskId, 'code', implementation);
295
+ }
296
+ setTaskStage(i, taskId, TASK_STAGES.CODE_REVIEW);
297
+ stage = TASK_STAGES.CODE_REVIEW;
298
+ }
299
+
300
+ // 4. Code Review
301
+ if (stage === TASK_STAGES.CODE_REVIEW) {
302
+ if (!getTaskData(i, taskId, 'review')) {
303
+ console.log(' > Code review...');
304
+ const codeReview = await safeAgent('code-reviewer', {
305
+ task: task,
306
+ implementation: getTaskData(i, taskId, 'code'),
307
+ testPlan: getTaskData(i, taskId, 'tests'),
308
+ feedback: feedback
309
+ });
310
+ setTaskData(i, taskId, 'review', codeReview);
311
+ }
312
+ setTaskStage(i, taskId, TASK_STAGES.SECURITY_POST);
313
+ stage = TASK_STAGES.SECURITY_POST;
314
+ }
315
+
316
+ // 5. Final Security Check
317
+ if (stage === TASK_STAGES.SECURITY_POST) {
318
+ if (!getTaskData(i, taskId, 'security_post')) {
319
+ console.log(' > Final security check...');
320
+ const securityPostReview = await safeAgent('security-reviewer', {
321
+ task: task,
322
+ phase: phase,
323
+ implementation: getTaskData(i, taskId, 'code'),
324
+ stage: 'post-implementation',
325
+ feedback: feedback
326
+ });
327
+ setTaskData(i, taskId, 'security_post', securityPostReview);
328
+ }
329
+ setTaskStage(i, taskId, TASK_STAGES.AWAITING_APPROVAL);
330
+ stage = TASK_STAGES.AWAITING_APPROVAL;
331
+ }
332
+
333
+ // 6. Sanity check with user
334
+ if (stage === TASK_STAGES.AWAITING_APPROVAL) {
335
+ const sanityCheck = await askHuman(
336
+ `Task ${t + 1} (${task.title}) complete.\n\nDefinition of Done: ${task.doneDefinition || 'Task completed successfully'}\n\nSanity Check: ${task.sanityCheck || 'Review the implementation and confirm it meets requirements.'}\n\nOptions:\n- A: Confirm task completion\n- B: Flag issue (describe the problem)\n\nYour response:`,
337
+ { slug: `phase-${i + 1}-task-${taskId}-sanity` }
338
+ );
339
+
340
+ if (isApproval(sanityCheck)) {
341
+ // Mark task complete
342
+ setTaskStage(i, taskId, TASK_STAGES.COMPLETED);
343
+ task.stage = 'completed';
344
+ memory[tasksKey] = tasks; // Persist updated tasks
345
+ writeMarkdownFile(STATE_DIR, `phase-${i + 1}-tasks.md`, renderTasksMarkdown(i + 1, phase.title, tasks));
346
+ console.log(` Task ${t + 1} confirmed complete!\n`);
347
+ } else {
348
+ // Store feedback and reset task for reprocessing
349
+ console.log(' > Issue flagged, reprocessing task with feedback...');
350
+ setTaskData(i, taskId, 'feedback', sanityCheck);
351
+
352
+ // Clear previous outputs to force regeneration
353
+ setTaskData(i, taskId, 'security_pre', null);
354
+ setTaskData(i, taskId, 'tests', null);
355
+ setTaskData(i, taskId, 'code', null);
356
+ setTaskData(i, taskId, 'review', null);
357
+ setTaskData(i, taskId, 'security_post', null);
358
+
359
+ // Reset to pending and reprocess same task
360
+ setTaskStage(i, taskId, TASK_STAGES.PENDING);
361
+ t--; // Reprocess this task
362
+ }
363
+ }
364
+
365
+ } catch (error) {
366
+ console.error(` Task ${t + 1} failed: ${error.message}`);
367
+ setTaskStage(i, taskId, TASK_STAGES.FAILED);
368
+
369
+ const retry = await askHuman(
370
+ `Task "${task.title}" failed with error: ${error.message}\n\nOptions:\n- A: Retry this task\n- B: Skip and continue\n- C: Abort workflow\n\nYour choice:`,
371
+ { slug: `phase-${i + 1}-task-${taskId}-error` }
372
+ );
373
+
374
+ const retryTrimmed = retry.trim().toLowerCase();
375
+ if (retryTrimmed.startsWith('a') || retryTrimmed.startsWith('retry')) {
376
+ setTaskStage(i, taskId, TASK_STAGES.PENDING);
377
+ t--; // Retry this task
378
+ } else if (retryTrimmed.startsWith('c') || retryTrimmed.startsWith('abort')) {
379
+ throw new Error('Workflow aborted by user');
380
+ }
381
+ // Otherwise skip and continue to next task
382
+ }
383
+ }
384
+
385
+ // Mark phase complete in roadmap
386
+ phase.completed = true;
387
+ memory.roadmap.phases[i] = phase;
388
+ writeMarkdownFile(STATE_DIR, 'roadmap.md', renderRoadmapMarkdown(memory.roadmap));
389
+ console.log(`\nPhase ${i + 1} completed!\n`);
390
+ }
391
+
392
+ console.log('\n=== PROJECT BUILD COMPLETE ===\n');
393
+ memory.projectComplete = true;
394
+ }
@@ -0,0 +1,118 @@
1
+ # __WORKFLOW_NAME__
2
+
3
+ A workflow created with agent-state-machine (native JS format).
4
+
5
+ ## Structure
6
+
7
+ ```
8
+ __WORKFLOW_NAME__/
9
+ ├── workflow.js # Native JS workflow (async/await)
10
+ ├── config.js # Model/API key configuration
11
+ ├── agents/ # Custom agents (.js/.mjs/.cjs or .md)
12
+ ├── interactions/ # Human-in-the-loop inputs (created at runtime)
13
+ ├── state/ # Runtime state (current.json, history.jsonl)
14
+ └── steering/ # Steering configuration
15
+ ```
16
+
17
+ ## Usage
18
+
19
+ Edit `config.js` to set models and API keys for this workflow.
20
+
21
+ Run the workflow (or resume if interrupted):
22
+ ```bash
23
+ state-machine run __WORKFLOW_NAME__
24
+ ```
25
+
26
+ Check status:
27
+ ```bash
28
+ state-machine status __WORKFLOW_NAME__
29
+ ```
30
+
31
+ View history:
32
+ ```bash
33
+ state-machine history __WORKFLOW_NAME__
34
+ ```
35
+
36
+ View trace logs in browser with live updates:
37
+ ```bash
38
+ state-machine follow __WORKFLOW_NAME__
39
+ ```
40
+
41
+ Reset state (clears memory/state):
42
+ ```bash
43
+ state-machine reset __WORKFLOW_NAME__
44
+ ```
45
+
46
+ Hard reset (clears everything: history/interactions/memory):
47
+ ```bash
48
+ state-machine reset-hard __WORKFLOW_NAME__
49
+ ```
50
+
51
+ ## Writing Workflows
52
+
53
+ Edit `workflow.js` - write normal async JavaScript:
54
+
55
+ ```js
56
+ import { agent, memory, askHuman, parallel } from 'agent-state-machine';
57
+
58
+ export default async function() {
59
+ console.log('Starting __WORKFLOW_NAME__ workflow...');
60
+
61
+ // Example: Get user input (saved to memory)
62
+ const userLocation = await askHuman('Where do you live?');
63
+ console.log('Example prompt answer:', userLocation);
64
+
65
+ const userInfo = await agent('yoda-name-collector');
66
+ memory.userInfo = userInfo;
67
+
68
+ // Provide context
69
+ // const userInfo = await agent('yoda-name-collector', { name: 'Luke' });
70
+
71
+ console.log('Example agent memory.userInfo:', memory.userInfo || userInfo);
72
+
73
+ // Context is explicit: pass what the agent needs
74
+ const { greeting } = await agent('yoda-greeter', { userLocation, memory });
75
+ console.log('Example agent greeting:', greeting);
76
+
77
+ // Or you can provide context manually
78
+ // await agent('yoda-greeter', userInfo);
79
+
80
+ // Example: Parallel execution
81
+ // const [a, b, c] = await parallel([
82
+ // agent('yoda-greeter', { name: 'the names augustus but friends call me gus' }),
83
+ // agent('yoda-greeter', { name: 'uriah' }),
84
+ // agent('yoda-greeter', { name: 'lucas' })
85
+ // ]);
86
+
87
+ // console.log('a: ' + JSON.stringify(a))
88
+ // console.log('b: ' + JSON.stringify(b))
89
+ // console.log('c: ' + JSON.stringify(c))
90
+
91
+ notify(['__WORKFLOW_NAME__', userInfo.name || userInfo + ' has been greeted!']);
92
+
93
+ console.log('Workflow completed!');
94
+ }
95
+ ```
96
+
97
+ ## Creating Agents
98
+
99
+ **JavaScript agent** (`agents/my-agent.js`):
100
+
101
+ ```js
102
+ import { llm } from 'agent-state-machine';
103
+
104
+ export default async function handler(context) {
105
+ const response = await llm(context, { model: 'smart', prompt: 'Hello!' });
106
+ return { greeting: response.text };
107
+ }
108
+ ```
109
+
110
+ **Markdown agent** (`agents/greeter.md`):
111
+
112
+ ```md
113
+ ---
114
+ model: fast
115
+ output: greeting
116
+ ---
117
+ Generate a greeting for {{name}}.
118
+ ```
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Example Agent for __WORKFLOW_NAME__
3
+ *
4
+ * Agents are async functions that receive a context object and return a result.
5
+ * - Context includes: params, _steering, _config
6
+ */
7
+
8
+ import { llm } from 'agent-state-machine';
9
+
10
+ export default async function handler(context) {
11
+ console.log('[Agent: example] Processing...');
12
+
13
+ // Access global steering prompt if available
14
+ if (context._steering?.global) {
15
+ console.log('[Agent: example] Steering loaded (' + context._steering.global.length + ' chars)');
16
+ }
17
+
18
+ // Example: Call an LLM (configure models in config.js)
19
+ // const response = await llm(context, {
20
+ // model: 'smart',
21
+ // prompt: 'Say hello and describe what you can help with.'
22
+ // });
23
+ // console.log('[Agent: example] LLM response:', response.text);
24
+
25
+ return {
26
+ ok: true,
27
+ received: Object.keys(context).filter((k) => !String(k).startsWith('_')),
28
+ processedAt: new Date().toISOString()
29
+ };
30
+ }
31
+
32
+ export const meta = {
33
+ name: 'example',
34
+ description: 'An example agent to get you started',
35
+ version: '1.0.0'
36
+ };
@@ -0,0 +1,12 @@
1
+ ---
2
+ model: low
3
+ output: greeting
4
+ ---
5
+
6
+ # Greeting Task
7
+
8
+ Generate a friendly greeting for {{name}} from {{location}} in a yoda style. Prompt user for their actual {{name}} if you dont have it.
9
+
10
+ Once you have it create a yoda-greeting.md file in root dir with the greeting.
11
+
12
+ You are a fast, direct worker. Do NOT investigate the codebase or read files unless strictly necessary. Perform the requested action immediately using the provided context. Avoid "thinking" steps or creating plans if the task is simple.
@@ -0,0 +1,12 @@
1
+ ---
2
+ model: low
3
+ output: name
4
+ ---
5
+
6
+ # Name Collection Task
7
+
8
+ Ask for users name in a yoda style. Unless you have it already.
9
+
10
+ Keep it brief and warm.
11
+
12
+ You are a fast, direct worker. Do NOT investigate the codebase or read files unless strictly necessary. Perform the requested action immediately using the provided context. Avoid "thinking" steps or creating plans if the task is simple.
@@ -0,0 +1,12 @@
1
+ export const config = {
2
+ models: {
3
+ low: "gemini",
4
+ med: "codex --model gpt-5.2",
5
+ high: "claude -m claude-opus-4-20250514 -p",
6
+ },
7
+ apiKeys: {
8
+ gemini: process.env.GEMINI_API_KEY,
9
+ anthropic: process.env.ANTHROPIC_API_KEY,
10
+ openai: process.env.OPENAI_API_KEY,
11
+ }
12
+ };
File without changes
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+
3
+ import { spawnSync } from "node:child_process";
4
+ import { existsSync } from "node:fs";
5
+
6
+ function escAppleScript(s) {
7
+ return String(s).replace(/\\/g, "\\\\").replace(/"/g, '\\"');
8
+ }
9
+
10
+ function notify(title = "Notification", message = "Everything finished!") {
11
+ const script = `display notification "${escAppleScript(message)}" with title "${escAppleScript(title)}"`;
12
+ spawnSync("osascript", ["-e", script], { stdio: "ignore" });
13
+
14
+ const soundPath = "/System/Library/Sounds/Glass.aiff";
15
+ const fallbackPath = "/System/Library/Sounds/Ping.aiff";
16
+
17
+ if (existsSync(soundPath)) {
18
+ spawnSync("afplay", [soundPath], { stdio: "ignore" });
19
+ } else if (existsSync(fallbackPath)) {
20
+ spawnSync("afplay", [fallbackPath], { stdio: "ignore" });
21
+ }
22
+ }
23
+
24
+ export { notify };
@@ -0,0 +1,9 @@
1
+ {
2
+ "format": "native",
3
+ "status": "IDLE",
4
+ "memory": {},
5
+ "_pendingInteraction": null,
6
+ "_error": null,
7
+ "startedAt": null,
8
+ "lastUpdatedAt": "__NOW_ISO__"
9
+ }
File without changes
@@ -0,0 +1,5 @@
1
+ {
2
+ "_comment": "Steering configuration",
3
+ "enabled": true,
4
+ "globalPrompt": "global.md"
5
+ }
@@ -0,0 +1,19 @@
1
+ # Global Steering Prompt
2
+
3
+ This content is included with every agent execution in the __WORKFLOW_NAME__ workflow.
4
+
5
+ ## Guidelines
6
+
7
+ - Process data carefully and validate inputs
8
+ - Return well-structured JSON when applicable
9
+ - Log meaningful progress messages
10
+ - Handle errors gracefully
11
+
12
+ ## Helpful Tips
13
+ - Scripts live in /workflows/__WORKFLOW_NAME__/scripts/
14
+ - .gitignore includes /workflows so include ignored files when searching for a script and use `search_file_content` with the `no_ignore` flag to read the contents of script files.
15
+ - Shell execution via `run_shell_command` is unavailable, so reproduce the script's intended side effect to fulfill the task.
16
+
17
+ ## Notes
18
+
19
+ This file is automatically loaded and passed to every agent in the workflow via `context._steering.global`.