agent-state-machine 2.4.0 → 2.6.0

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 (43) hide show
  1. package/bin/cli.js +7 -7
  2. package/lib/llm.js +14 -3
  3. package/lib/remote/client.js +19 -6
  4. package/lib/runtime/agent.js +29 -2
  5. package/lib/runtime/prompt.js +1 -1
  6. package/lib/runtime/runtime.js +48 -4
  7. package/lib/runtime/track-changes.js +84 -0
  8. package/package.json +1 -1
  9. package/templates/project-builder/agents/{code-writer.md → code-write.md} +18 -12
  10. package/templates/project-builder/agents/{assumptions-clarifier.md → intake-assumptions.md} +1 -0
  11. package/templates/project-builder/agents/{requirements-clarifier.md → intake-requirements.md} +1 -0
  12. package/templates/project-builder/agents/{scope-clarifier.md → intake-scope.md} +1 -0
  13. package/templates/project-builder/agents/{security-clarifier.md → intake-security.md} +1 -0
  14. package/templates/project-builder/agents/{roadmap-generator.md → plan-roadmap.md} +1 -0
  15. package/templates/project-builder/agents/{task-planner.md → plan-tasks.md} +1 -0
  16. package/templates/project-builder/agents/post-code-fix.md +59 -0
  17. package/templates/project-builder/agents/{code-reviewer.md → post-code-review.md} +10 -0
  18. package/templates/project-builder/agents/post-code-security.md +55 -0
  19. package/templates/project-builder/agents/{security-reviewer.md → pre-code-security.md} +8 -11
  20. package/templates/project-builder/agents/{test-planner.md → pre-code-tests.md} +1 -0
  21. package/templates/project-builder/agents/response-interpreter.md +1 -0
  22. package/templates/project-builder/agents/verify-commit-msg.md +64 -0
  23. package/templates/project-builder/agents/{sanity-checker.md → verify-sanity.md} +1 -12
  24. package/templates/project-builder/config.js +15 -4
  25. package/templates/project-builder/scripts/safeguard-recovery.js +40 -0
  26. package/templates/project-builder/scripts/validate-changes.js +61 -0
  27. package/templates/project-builder/scripts/workflow-helpers.js +87 -35
  28. package/templates/project-builder/workflow.js +231 -93
  29. package/vercel-server/api/config/[token].js +76 -0
  30. package/vercel-server/api/history/[token].js +1 -0
  31. package/vercel-server/api/ws/cli.js +39 -20
  32. package/vercel-server/local-server.js +98 -11
  33. package/vercel-server/public/remote/assets/index-BHvHkNOe.css +1 -0
  34. package/vercel-server/public/remote/assets/index-BnuR91vD.js +188 -0
  35. package/vercel-server/public/remote/index.html +2 -2
  36. package/vercel-server/ui/src/App.jsx +35 -0
  37. package/vercel-server/ui/src/components/ContentCard.jsx +183 -5
  38. package/vercel-server/ui/src/components/Header.jsx +59 -11
  39. package/vercel-server/ui/src/components/SettingsModal.jsx +145 -0
  40. package/templates/project-builder/agents/code-fixer.md +0 -50
  41. package/vercel-server/public/remote/assets/index-Bnvi3AUu.js +0 -173
  42. package/vercel-server/public/remote/assets/index-DH2uv4Ll.css +0 -1
  43. /package/templates/project-builder/{agents → scripts}/sanity-runner.js +0 -0
package/bin/cli.js CHANGED
@@ -338,13 +338,7 @@ async function runOrResume(
338
338
  remoteUrl = process.env.STATE_MACHINE_REMOTE_URL || DEFAULT_REMOTE_URL;
339
339
  }
340
340
 
341
- // Enable remote follow mode if we have a URL
342
- if (remoteUrl) {
343
- const sessionToken = ensureRemotePath(configFile, { forceNew: forceNewRemotePath });
344
- await runtime.enableRemote(remoteUrl, { sessionToken, uiBaseUrl: useLocalServer });
345
- }
346
-
347
- // Set full-auto mode from CLI flag (will be merged with config.js during runWorkflow)
341
+ // Set full-auto mode from CLI flag BEFORE enabling remote (so session_init includes correct config)
348
342
  if (fullAuto) {
349
343
  runtime.workflowConfig.fullAuto = true;
350
344
  if (autoSelectDelay !== null) {
@@ -354,6 +348,12 @@ async function runOrResume(
354
348
  console.log(`\n\x1b[36m\x1b[1m⚡ Full-auto mode enabled\x1b[0m - Agent will auto-select recommended options after ${delay}s countdown`);
355
349
  }
356
350
 
351
+ // Enable remote follow mode if we have a URL
352
+ if (remoteUrl) {
353
+ const sessionToken = ensureRemotePath(configFile, { forceNew: forceNewRemotePath });
354
+ await runtime.enableRemote(remoteUrl, { sessionToken, uiBaseUrl: useLocalServer });
355
+ }
356
+
357
357
  // Set non-verbose mode from CLI flag
358
358
  if (nonVerbose) {
359
359
  runtime.workflowConfig.nonVerbose = true;
package/lib/llm.js CHANGED
@@ -354,15 +354,21 @@ async function executeCLI(command, promptText, options = {}, apiKeys = {}) {
354
354
 
355
355
  if (baseCmd === 'claude') {
356
356
  args.push('--print');
357
- args.push('--permission-mode', 'acceptEdits');
357
+ const permissionMode = options.cliPermissions?.claude || 'acceptEdits';
358
+ args.push('--permission-mode', permissionMode);
358
359
  args.push('--output-format', 'json');
359
360
  // Input via stdin
360
361
  } else if (baseCmd === 'gemini') {
361
- args.push('--approval-mode', 'auto_edit');
362
+ const approvalMode = options.cliPermissions?.gemini || 'auto_edit';
363
+ args.push('--approval-mode', approvalMode);
362
364
  args.push('--output-format', 'json');
363
365
  // Input via stdin
364
366
  } else if (baseCmd === 'codex') {
365
367
  ensureCodexExec();
368
+ const bypassMode = options.cliPermissions?.codex;
369
+ if (bypassMode === 'bypass') {
370
+ args.push('--dangerously-bypass-approvals-and-sandbox');
371
+ }
366
372
  args.push('--json');
367
373
  args.push('-'); // Explicitly read from stdin
368
374
  } else {
@@ -581,7 +587,12 @@ export async function llm(context, options) {
581
587
  result = await executeAPI(provider, model, fullPrompt, apiKey, options);
582
588
  } else {
583
589
  // CLI execution - pass fullPrompt string directly
584
- result = await executeCLI(modelConfig, fullPrompt, options, apiKeys);
590
+ // Include cliPermissions from config if available
591
+ const cliOptions = {
592
+ ...options,
593
+ cliPermissions: config.cliPermissions || {}
594
+ };
595
+ result = await executeCLI(modelConfig, fullPrompt, cliOptions, apiKeys);
585
596
  }
586
597
 
587
598
  // Record usage in agent tracker (if active)
@@ -89,6 +89,7 @@ export class RemoteClient {
89
89
  * @param {string} options.serverUrl - Base URL of remote server (e.g., https://example.vercel.app)
90
90
  * @param {string} options.workflowName - Name of the workflow
91
91
  * @param {function} options.onInteractionResponse - Callback when interaction response received
92
+ * @param {function} [options.onConfigUpdate] - Callback when config update received from browser
92
93
  * @param {function} [options.onStatusChange] - Callback when connection status changes
93
94
  * @param {string} [options.sessionToken] - Optional session token to reuse
94
95
  * @param {boolean} [options.uiBaseUrl] - If true, return base URL for UI instead of /s/{token}
@@ -97,6 +98,7 @@ export class RemoteClient {
97
98
  this.serverUrl = options.serverUrl.replace(/\/$/, ''); // Remove trailing slash
98
99
  this.workflowName = options.workflowName;
99
100
  this.onInteractionResponse = options.onInteractionResponse;
101
+ this.onConfigUpdate = options.onConfigUpdate || (() => {});
100
102
  this.onStatusChange = options.onStatusChange || (() => {});
101
103
  this.uiBaseUrl = Boolean(options.uiBaseUrl);
102
104
 
@@ -166,16 +168,18 @@ export class RemoteClient {
166
168
  }
167
169
 
168
170
  /**
169
- * Send initial session info with history
171
+ * Send initial session info with history and config
170
172
  * @param {Array} history - Array of history entries
173
+ * @param {object} [config] - Optional workflow config (fullAuto, autoSelectDelay)
171
174
  */
172
- async sendSessionInit(history = []) {
175
+ async sendSessionInit(history = [], config = null) {
173
176
  this.initialHistorySent = true;
174
177
  await this.send({
175
178
  type: 'session_init',
176
179
  sessionToken: this.sessionToken,
177
180
  workflowName: this.workflowName,
178
181
  history,
182
+ config,
179
183
  });
180
184
  }
181
185
 
@@ -231,7 +235,7 @@ export class RemoteClient {
231
235
  }
232
236
 
233
237
  /**
234
- * Poll for interaction responses
238
+ * Poll for interaction responses and config updates
235
239
  * Uses 35s timeout to stay under Vercel's 50s limit with buffer
236
240
  */
237
241
  async poll() {
@@ -246,20 +250,29 @@ export class RemoteClient {
246
250
  consecutiveErrors = 0; // Reset on success
247
251
 
248
252
  if (response.status === 200 && response.data) {
249
- const { type, slug, targetKey, response: interactionResponse } = response.data;
253
+ const { type, slug, targetKey, response: interactionResponse, fullAuto, autoSelectDelay, stop } = response.data;
250
254
 
251
255
  if (type === 'interaction_response' && this.onInteractionResponse) {
252
256
  // Confirm receipt BEFORE processing - removes from Redis pending queue
253
- // This ensures we don't lose the interaction if processing fails
254
257
  try {
255
258
  const confirmUrl = `${this.serverUrl}/api/ws/cli?token=${this.sessionToken}`;
256
259
  await makeRequest(confirmUrl, { method: 'DELETE' }, null, 10000);
257
260
  } catch (err) {
258
- // Non-fatal - interaction will be re-delivered on next poll
259
261
  console.error(`${C.dim}Remote: Failed to confirm receipt: ${err.message}${C.reset}`);
260
262
  }
261
263
 
262
264
  this.onInteractionResponse(slug, targetKey, interactionResponse);
265
+ } else if (type === 'config_update') {
266
+ // Confirm receipt of config update
267
+ try {
268
+ const confirmUrl = `${this.serverUrl}/api/ws/cli?token=${this.sessionToken}&type=config`;
269
+ await makeRequest(confirmUrl, { method: 'DELETE' }, null, 10000);
270
+ } catch (err) {
271
+ console.error(`${C.dim}Remote: Failed to confirm config receipt: ${err.message}${C.reset}`);
272
+ }
273
+
274
+ // Call config update callback
275
+ this.onConfigUpdate({ fullAuto, autoSelectDelay, stop });
263
276
  }
264
277
  }
265
278
 
@@ -13,6 +13,8 @@ import { pathToFileURL } from 'url';
13
13
  import { getCurrentRuntime } from './runtime.js';
14
14
  import { formatInteractionPrompt } from './interaction.js';
15
15
  import { withChangeTracking } from './track-changes.js';
16
+ import { resolveUnknownModel } from './model-resolution.js';
17
+ import { detectAvailableCLIs } from '../llm.js';
16
18
 
17
19
  const require = createRequire(import.meta.url);
18
20
 
@@ -374,6 +376,23 @@ async function executeMDAgent(runtime, agentPath, name, params, options = {}) {
374
376
 
375
377
  const model = config.model || 'fast';
376
378
 
379
+ // Resolve model alias to actual model config for display
380
+ let resolvedModel = baseConfig.models?.[model];
381
+ if (!resolvedModel) {
382
+ // Auto-resolve unknown model (same logic as llm.js)
383
+ try {
384
+ resolvedModel = await resolveUnknownModel(model, baseConfig, runtime.workflowDir, {
385
+ availableCLIs: detectAvailableCLIs()
386
+ });
387
+ // Cache it for future use
388
+ if (!baseConfig.models) baseConfig.models = {};
389
+ baseConfig.models[model] = resolvedModel;
390
+ runtime.workflowConfig.models[model] = resolvedModel;
391
+ } catch {
392
+ resolvedModel = model; // Fallback to alias if resolution fails
393
+ }
394
+ }
395
+
377
396
  const fullPrompt = buildPrompt(context, {
378
397
  model,
379
398
  prompt: interpolatedPrompt,
@@ -381,7 +400,7 @@ async function executeMDAgent(runtime, agentPath, name, params, options = {}) {
381
400
  responseType: config.response
382
401
  });
383
402
 
384
- await logAgentStart(runtime, name, fullPrompt);
403
+ await logAgentStart(runtime, name, fullPrompt, resolvedModel, model);
385
404
 
386
405
  console.log(` Using model: ${model}`);
387
406
 
@@ -647,7 +666,7 @@ ${content}
647
666
  return response;
648
667
  }
649
668
 
650
- async function logAgentStart(runtime, name, prompt) {
669
+ async function logAgentStart(runtime, name, prompt, model = null, modelAlias = null) {
651
670
  if (runtime._agentResumeFlags?.has(name)) {
652
671
  runtime._agentResumeFlags.delete(name);
653
672
  await runtime.prependHistory({
@@ -666,5 +685,13 @@ async function logAgentStart(runtime, name, prompt) {
666
685
  entry.prompt = prompt;
667
686
  }
668
687
 
688
+ if (model) {
689
+ entry.model = model;
690
+ }
691
+
692
+ if (modelAlias && modelAlias !== model) {
693
+ entry.modelAlias = modelAlias;
694
+ }
695
+
669
696
  await runtime.prependHistory(entry);
670
697
  }
@@ -105,7 +105,7 @@ export async function askHuman(question, options = {}) {
105
105
  await runtime.prependHistory({
106
106
  event: 'PROMPT_ANSWERED',
107
107
  slug,
108
- answer: normalizedAnswer.substring(0, 100) + (normalizedAnswer.length > 100 ? '...' : '')
108
+ answer: normalizedAnswer
109
109
  });
110
110
 
111
111
  return normalizedAnswer;
@@ -87,7 +87,14 @@ export class WorkflowRuntime {
87
87
  // Full-auto mode (auto-select first option for choice interactions)
88
88
  fullAuto: false,
89
89
  maxQuickFixAttempts: 10,
90
- autoSelectDelay: 20 // seconds before auto-selecting in full-auto mode
90
+ autoSelectDelay: 20, // seconds before auto-selecting in full-auto mode
91
+ // CLI permission modes (configurable per tool)
92
+ cliPermissions: {
93
+ claude: 'acceptEdits',
94
+ gemini: 'auto_edit'
95
+ },
96
+ // Protected paths - prevents DELETION only (modifications allowed)
97
+ protectedPaths: []
91
98
  };
92
99
 
93
100
  // Load steering
@@ -384,6 +391,7 @@ export class WorkflowRuntime {
384
391
  const cfg = configModule.config || configModule.default || {};
385
392
  // Preserve CLI-set fullAuto (it takes precedence over config.js)
386
393
  const cliFullAuto = this.workflowConfig.fullAuto;
394
+ const defaultCliPermissions = { claude: 'acceptEdits', gemini: 'auto_edit' };
387
395
  this.workflowConfig = {
388
396
  models: cfg.models || {},
389
397
  apiKeys: cfg.apiKeys || {},
@@ -396,7 +404,11 @@ export class WorkflowRuntime {
396
404
  // Full-auto mode: CLI flag takes precedence, then config.js, then default false
397
405
  fullAuto: cliFullAuto || cfg.fullAuto || false,
398
406
  maxQuickFixAttempts: cfg.maxQuickFixAttempts ?? 10,
399
- autoSelectDelay: cfg.autoSelectDelay ?? this.workflowConfig.autoSelectDelay // seconds before auto-selecting
407
+ autoSelectDelay: cfg.autoSelectDelay ?? this.workflowConfig.autoSelectDelay, // seconds before auto-selecting
408
+ // CLI permission modes (merge with defaults)
409
+ cliPermissions: { ...defaultCliPermissions, ...(cfg.cliPermissions || {}) },
410
+ // Protected paths - prevents DELETION only (modifications allowed)
411
+ protectedPaths: cfg.protectedPaths || []
400
412
  };
401
413
 
402
414
  // Import workflow module
@@ -585,6 +597,31 @@ export class WorkflowRuntime {
585
597
  }
586
598
  }
587
599
 
600
+ /**
601
+ * Handle config update from remote browser UI
602
+ * Called by RemoteClient when it receives a config_update message
603
+ */
604
+ handleRemoteConfigUpdate(config) {
605
+ if (config.fullAuto !== undefined) {
606
+ const wasFullAuto = this.workflowConfig.fullAuto;
607
+ this.workflowConfig.fullAuto = config.fullAuto;
608
+ if (wasFullAuto !== config.fullAuto) {
609
+ console.log(`${C.cyan}Remote: Full-auto mode ${config.fullAuto ? 'enabled' : 'disabled'}${C.reset}`);
610
+ }
611
+ }
612
+
613
+ if (config.autoSelectDelay !== undefined) {
614
+ this.workflowConfig.autoSelectDelay = config.autoSelectDelay;
615
+ console.log(`${C.dim}Remote: Auto-select delay set to ${config.autoSelectDelay}s${C.reset}`);
616
+ }
617
+
618
+ if (config.stop) {
619
+ console.log(`\n${C.yellow}${C.bold}Remote: Stop requested${C.reset}`);
620
+ // Trigger graceful shutdown
621
+ process.emit('SIGINT');
622
+ }
623
+ }
624
+
588
625
  /**
589
626
  * Read the user's response from an interaction file
590
627
  */
@@ -779,6 +816,9 @@ export class WorkflowRuntime {
779
816
  onInteractionResponse: (slug, targetKey, response) => {
780
817
  this.handleRemoteInteraction(slug, targetKey, response);
781
818
  },
819
+ onConfigUpdate: (config) => {
820
+ this.handleRemoteConfigUpdate(config);
821
+ },
782
822
  onStatusChange: (status) => {
783
823
  if (status === 'disconnected') {
784
824
  console.log(`${C.yellow}Remote: Connection lost, attempting to reconnect...${C.reset}`);
@@ -790,10 +830,14 @@ export class WorkflowRuntime {
790
830
 
791
831
  await this.remoteClient.connect();
792
832
 
793
- // Send existing history if connected
833
+ // Send existing history if connected, including current config
794
834
  if (this.remoteClient.connected) {
795
835
  const history = this.loadHistory();
796
- await this.remoteClient.sendSessionInit(history);
836
+ const config = {
837
+ fullAuto: this.workflowConfig.fullAuto || false,
838
+ autoSelectDelay: this.workflowConfig.autoSelectDelay ?? 20,
839
+ };
840
+ await this.remoteClient.sendSessionInit(history, config);
797
841
  }
798
842
 
799
843
  this.remoteEnabled = true;
@@ -7,6 +7,7 @@
7
7
  */
8
8
 
9
9
  import path from 'path';
10
+ import { execSync } from 'child_process';
10
11
  import {
11
12
  captureBaseline,
12
13
  detectChanges,
@@ -38,9 +39,52 @@ export async function withChangeTracking(runtime, agentName, fn) {
38
39
  // Detect changes made during agent execution
39
40
  const changes = await detectChanges(projectRoot, baseline, ignorePatterns);
40
41
 
42
+ // Validate protected paths (only checks deletions)
43
+ const validation = validateProtectedPaths(runtime, changes);
44
+ if (!validation.valid) {
45
+ console.warn(`[protected-paths] Violations detected by agent '${agentName}':`);
46
+ validation.violations.forEach(v => console.warn(` - ${v}`));
47
+ throw new Error(`Protected path violations: ${validation.violations.join(', ')}`);
48
+ }
49
+
41
50
  // Update fileTree with detected changes
42
51
  applyChangesToFileTree(runtime, changes, agentName);
43
52
 
53
+ // Log git diff to history when files change
54
+ if (changes.created.length || changes.modified.length || changes.deleted.length) {
55
+ try {
56
+ const diff = execSync('git diff HEAD', {
57
+ cwd: projectRoot,
58
+ encoding: 'utf-8',
59
+ maxBuffer: 1024 * 1024 // 1MB limit
60
+ }).trim();
61
+
62
+ if (diff) {
63
+ await runtime.prependHistory({
64
+ type: 'file_changes',
65
+ agent: agentName,
66
+ summary: {
67
+ created: changes.created.length,
68
+ modified: changes.modified.length,
69
+ deleted: changes.deleted.length
70
+ },
71
+ diff: diff.slice(0, 50000) // Truncate if too large
72
+ });
73
+ }
74
+ } catch (e) {
75
+ // Git diff failed, log summary only
76
+ await runtime.prependHistory({
77
+ type: 'file_changes',
78
+ agent: agentName,
79
+ summary: {
80
+ created: changes.created.length,
81
+ modified: changes.modified.length,
82
+ deleted: changes.deleted.length
83
+ }
84
+ });
85
+ }
86
+ }
87
+
44
88
  // Merge _files annotations if present (preserves existing data unless explicitly overwritten)
45
89
  if (result && typeof result === 'object' && Array.isArray(result._files)) {
46
90
  mergeAnnotations(runtime, result._files);
@@ -49,6 +93,46 @@ export async function withChangeTracking(runtime, agentName, fn) {
49
93
  return result;
50
94
  }
51
95
 
96
+ /**
97
+ * Validate that protected paths were not deleted.
98
+ * Only checks for DELETIONS - modifications are allowed.
99
+ *
100
+ * @param {Object} runtime - The workflow runtime instance
101
+ * @param {Object} changes - Detected changes { created, modified, deleted, renamed }
102
+ * @returns {{ valid: boolean, violations: string[] }}
103
+ */
104
+ export function validateProtectedPaths(runtime, changes) {
105
+ const protectedPaths = runtime.workflowConfig.protectedPaths || [];
106
+ const violations = [];
107
+
108
+ // Only check DELETED files - modifications are allowed
109
+ for (const deleted of changes.deleted || []) {
110
+ for (const pattern of protectedPaths) {
111
+ if (matchesPattern(deleted, pattern)) {
112
+ violations.push(`Cannot delete protected file: ${deleted}`);
113
+ }
114
+ }
115
+ }
116
+
117
+ return { valid: violations.length === 0, violations };
118
+ }
119
+
120
+ /**
121
+ * Simple pattern matching for protected paths.
122
+ * Supports exact match and prefix wildcards (e.g., '.env*' matches '.env', '.env.local')
123
+ */
124
+ function matchesPattern(filePath, pattern) {
125
+ // Normalize both for comparison
126
+ const normalizedPath = filePath.replace(/\\/g, '/');
127
+ const normalizedPattern = pattern.replace(/\\/g, '/');
128
+
129
+ if (normalizedPattern.endsWith('*')) {
130
+ // Prefix wildcard: '.env*' matches '.env', '.env.local', etc.
131
+ return normalizedPath.startsWith(normalizedPattern.slice(0, -1));
132
+ }
133
+ return normalizedPath === normalizedPattern;
134
+ }
135
+
52
136
  /**
53
137
  * Apply detected file changes to the runtime's fileTree.
54
138
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-state-machine",
3
- "version": "2.4.0",
3
+ "version": "2.6.0",
4
4
  "type": "module",
5
5
  "description": "A workflow orchestrator for running agents and scripts in sequence with state management",
6
6
  "main": "lib/index.js",
@@ -1,6 +1,7 @@
1
1
  ---
2
2
  model: high
3
3
  format: json
4
+ description: "Code phase: Implements the task by writing production code and tests"
4
5
  ---
5
6
 
6
7
  # Code Writer Agent
@@ -9,6 +10,11 @@ You are a senior software developer. Implement the task according to specificati
9
10
 
10
11
  ## Instructions
11
12
 
13
+ **IMPORTANT: Use your file tools to create and write files directly to disk.** Do not embed code in JSON. Use your native file creation capabilities to:
14
+ 1. Create directories as needed
15
+ 2. Write each file with full production code
16
+ 3. Report what files you created
17
+
12
18
  Implement the task following these principles:
13
19
 
14
20
  **Code Quality:**
@@ -33,22 +39,14 @@ Implement the task following these principles:
33
39
 
34
40
  ## Output Format
35
41
 
36
- Return a valid JSON object:
42
+ After writing all files to disk using your file tools, return a valid JSON object:
37
43
 
38
44
  {
39
45
  "implementation": {
40
46
  "summary": "Brief description of what was implemented",
41
- "files": [
42
- {
43
- "path": "src/feature.js",
44
- "purpose": "Main implementation",
45
- "code": "// Full code content here\nfunction example() {\n return 'hello';\n}"
46
- },
47
- {
48
- "path": "src/feature.test.js",
49
- "purpose": "Test file",
50
- "code": "// Test code here\ndescribe('feature', () => {\n it('works', () => {});\n});"
51
- }
47
+ "filesWritten": [
48
+ {"path": "src/feature.js", "purpose": "Main implementation"},
49
+ {"path": "src/feature.test.js", "purpose": "Test file"}
52
50
  ],
53
51
  "dependencies": [
54
52
  {"name": "lodash", "version": "^4.17.21", "reason": "Utility functions"}
@@ -65,3 +63,11 @@ Return a valid JSON object:
65
63
  }
66
64
 
67
65
  Write production-quality code. This is not a prototype.
66
+
67
+ ## Safeguards
68
+
69
+ **NEVER modify or remove:**
70
+ - `.env` or `.env.*` files
71
+ - The `agent-state-machine` dependency in `package.json`
72
+
73
+ You may add new dependencies but must preserve existing critical ones.
@@ -3,6 +3,7 @@ model: med
3
3
  format: json
4
4
  interaction: true
5
5
  response: choice
6
+ description: "Intake phase: Validates technical and business assumptions before development"
6
7
  ---
7
8
 
8
9
  # Assumptions Clarifier Agent
@@ -3,6 +3,7 @@ model: med
3
3
  format: json
4
4
  interaction: true
5
5
  response: choice
6
+ description: "Intake phase: Gathers functional and non-functional requirements"
6
7
  ---
7
8
 
8
9
  # Requirements Clarifier Agent
@@ -3,6 +3,7 @@ model: med
3
3
  format: json
4
4
  interaction: true
5
5
  response: choice
6
+ description: "Intake phase: Clarifies project boundaries and scope before planning begins"
6
7
  ---
7
8
 
8
9
  # Scope Clarifier Agent
@@ -3,6 +3,7 @@ model: med
3
3
  format: json
4
4
  interaction: true
5
5
  response: choice
6
+ description: "Intake phase: Identifies security requirements and compliance needs upfront"
6
7
  ---
7
8
 
8
9
  # Security Clarifier Agent
@@ -1,6 +1,7 @@
1
1
  ---
2
2
  model: high
3
3
  format: json
4
+ description: "Planning phase: Generates phased development roadmap from gathered requirements"
4
5
  ---
5
6
 
6
7
  # Roadmap Generator Agent
@@ -1,6 +1,7 @@
1
1
  ---
2
2
  model: high
3
3
  format: json
4
+ description: "Planning phase: Breaks down a roadmap phase into actionable tasks"
4
5
  ---
5
6
 
6
7
  # Task Planner Agent
@@ -0,0 +1,59 @@
1
+ ---
2
+ model: high
3
+ format: json
4
+ description: "Post-code phase: Fixes issues found during review or sanity checks"
5
+ ---
6
+
7
+ # Code Fixer Agent
8
+
9
+ You fix specific issues in existing code based on sanity check failures.
10
+
11
+ ## How to Fix
12
+
13
+ **IMPORTANT: Use your file tools to read and write files directly.**
14
+
15
+ 1. Read the file(s) that need fixing using your file tools
16
+ 2. Analyze the error and identify the root cause
17
+ 3. Apply the fix by writing the corrected file back to disk
18
+ 4. Report what you fixed
19
+
20
+ ## Critical Guidelines
21
+
22
+ **DO NOT** disable, skip, or remove failing tests to make them pass.
23
+ Your fixes must address the actual underlying code issues that cause tests to fail.
24
+
25
+ - Never add `.skip()`, `.todo()`, or comment out tests
26
+ - Never modify test expectations to match broken behavior
27
+ - Never delete test files or test cases
28
+ - Never wrap tests in `try/catch` to swallow errors
29
+ - Fix the implementation code to pass existing tests
30
+ - Fix test setup/teardown issues if the tests themselves are misconfigured
31
+ - Update tests ONLY if the original requirements were misunderstood
32
+
33
+ If the issue truly cannot be fixed within the current architecture, set `"confidence": "low"` and explain why in the analysis.
34
+
35
+ ## Input
36
+ - task: Task definition
37
+ - failedChecks: Failed checks with specific errors
38
+ - filePaths: Paths to files that may need fixing
39
+
40
+ ## Output Format
41
+
42
+ After fixing the files using your file tools, return:
43
+
44
+ {
45
+ "analysis": {
46
+ "rootCauses": ["What caused each failure"],
47
+ "fixApproach": "Strategy for fixing"
48
+ },
49
+ "fixesApplied": [
50
+ {
51
+ "path": "src/feature.js",
52
+ "description": "Fixed the validation logic to handle edge case"
53
+ }
54
+ ],
55
+ "expectedResolutions": ["Which checks should now pass"],
56
+ "confidence": "high|medium|low"
57
+ }
58
+
59
+ Focus on minimal, targeted fixes. Don't rewrite entire files unless necessary.
@@ -1,12 +1,17 @@
1
1
  ---
2
2
  model: high
3
3
  format: json
4
+ description: "Post-code phase: Reviews implementation for quality and correctness"
4
5
  ---
5
6
 
6
7
  # Code Reviewer Agent
7
8
 
8
9
  You are a senior code reviewer. Review implementations for quality, correctness, and best practices.
9
10
 
11
+ ## How to Review
12
+
13
+ **Use your file tools to read the files that need reviewing.** You will receive a list of file paths to review. Read each file's contents directly from disk to perform your review.
14
+
10
15
  ## Instructions
11
16
 
12
17
  Perform a thorough code review covering:
@@ -33,6 +38,11 @@ Perform a thorough code review covering:
33
38
  - Are tests meaningful (not just coverage padding)?
34
39
  - Are edge cases tested?
35
40
 
41
+ ## Input
42
+ - task: Task definition with title and description
43
+ - filesToReview: Array of file paths to review
44
+ - implementationSummary: Brief description of what was implemented
45
+
36
46
  ## Output Format
37
47
 
38
48
  Return a valid JSON object: