@polka-codes/cli 0.9.79 → 0.9.80

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 (2) hide show
  1. package/dist/index.js +590 -113
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -37660,7 +37660,7 @@ var {
37660
37660
  Help
37661
37661
  } = import__.default;
37662
37662
  // package.json
37663
- var version = "0.9.79";
37663
+ var version = "0.9.80";
37664
37664
 
37665
37665
  // src/commands/code.ts
37666
37666
  import { readFile as readFile4 } from "node:fs/promises";
@@ -64205,7 +64205,8 @@ var WorkflowStepDefinitionSchema = exports_external.object({
64205
64205
  output: exports_external.string().nullish(),
64206
64206
  expected_outcome: exports_external.string().nullish(),
64207
64207
  code: exports_external.string().nullish(),
64208
- outputSchema: exports_external.any().nullish()
64208
+ outputSchema: exports_external.any().nullish(),
64209
+ timeout: exports_external.number().positive().nullish()
64209
64210
  });
64210
64211
  var WorkflowDefinitionSchema = exports_external.object({
64211
64212
  task: exports_external.string(),
@@ -64231,6 +64232,29 @@ function parseDynamicWorkflowDefinition(source) {
64231
64232
  }
64232
64233
  }
64233
64234
  var AsyncFunction = Object.getPrototypeOf(async () => {}).constructor;
64235
+ function validateAndApplyDefaults(workflowId, workflow, input) {
64236
+ if (!workflow.inputs || workflow.inputs.length === 0) {
64237
+ return input;
64238
+ }
64239
+ const validatedInput = {};
64240
+ const errors4 = [];
64241
+ for (const inputDef of workflow.inputs) {
64242
+ const providedValue = input[inputDef.id];
64243
+ if (providedValue !== undefined && providedValue !== null) {
64244
+ validatedInput[inputDef.id] = providedValue;
64245
+ } else if (inputDef.default !== undefined && inputDef.default !== null) {
64246
+ validatedInput[inputDef.id] = inputDef.default;
64247
+ } else {
64248
+ errors4.push(`Missing required input '${inputDef.id}'${inputDef.description ? `: ${inputDef.description}` : ""}`);
64249
+ }
64250
+ }
64251
+ if (errors4.length > 0) {
64252
+ throw new Error(`Workflow '${workflowId}' input validation failed:
64253
+ ${errors4.map((e) => ` - ${e}`).join(`
64254
+ `)}`);
64255
+ }
64256
+ return validatedInput;
64257
+ }
64234
64258
  function createRunWorkflowFn(args) {
64235
64259
  return async (subWorkflowId, subInput) => {
64236
64260
  const mergedInput = { ...args.input, ...args.state, ...subInput ?? {} };
@@ -64251,7 +64275,14 @@ function compileStep(stepDef, workflowId, compiledSteps) {
64251
64275
  compiledSteps.set(key, fn);
64252
64276
  return fn;
64253
64277
  } catch (error46) {
64254
- throw new Error(`Failed to compile code for step '${stepDef.id}' in workflow '${workflowId}': ${error46 instanceof Error ? error46.message : String(error46)}`);
64278
+ const errorMsg = error46 instanceof Error ? error46.message : String(error46);
64279
+ const codePreview = stepDef.code.length > 200 ? `${stepDef.code.substring(0, 200)}...` : stepDef.code;
64280
+ throw new Error(`Failed to compile code for step '${stepDef.id}' in workflow '${workflowId}':
64281
+ ` + ` Error: ${errorMsg}
64282
+ ` + ` Code:
64283
+ ${codePreview.split(`
64284
+ `).map((line) => ` ${line}`).join(`
64285
+ `)}`);
64255
64286
  }
64256
64287
  }
64257
64288
  async function executeStepWithAgent(stepDef, workflowId, input, state, context, options, runInternal) {
@@ -64278,6 +64309,7 @@ async function executeStepWithAgent(stepDef, workflowId, input, state, context,
64278
64309
  });
64279
64310
  }
64280
64311
  const allowedToolNameSet = new Set(toolsForAgent.map((t) => t.name));
64312
+ context.logger.debug(`[Agent] Available tools for step '${stepDef.id}': ${toolsForAgent.map((t) => t.name).join(", ")}`);
64281
64313
  const systemPrompt = options.stepSystemPrompt?.({ workflowId, step: stepDef, input, state }) ?? [
64282
64314
  `You are an AI assistant executing a workflow step.`,
64283
64315
  "",
@@ -64346,28 +64378,82 @@ async function executeStepWithAgent(stepDef, workflowId, input, state, context,
64346
64378
  if (parsed.success) {
64347
64379
  return parsed.data;
64348
64380
  }
64381
+ if (options.wrapAgentResultInObject) {
64382
+ context.logger.warn(`[Agent] Step '${stepDef.id}' returned plain text instead of JSON. Wrapping in {result: ...}`);
64383
+ return { result: result.message };
64384
+ }
64349
64385
  return result.message;
64350
64386
  }
64351
- throw new Error(`Agent execution for step '${stepDef.id}' in workflow '${workflowId}' did not exit cleanly.`);
64387
+ if (result.type === "Error") {
64388
+ throw new Error(`Agent step '${stepDef.id}' in workflow '${workflowId}' failed: ${result.error?.message || "Unknown error"}`);
64389
+ }
64390
+ if (result.type === "UsageExceeded") {
64391
+ throw new Error(`Agent step '${stepDef.id}' in workflow '${workflowId}' exceeded usage limits (tokens or rounds)`);
64392
+ }
64393
+ throw new Error(`Agent step '${stepDef.id}' in workflow '${workflowId}' exited unexpectedly with type: ${result.type}`);
64394
+ }
64395
+ async function executeStepWithTimeout(stepDef, workflowId, input, state, context, options, compiledSteps, runInternal) {
64396
+ const executeStepLogic = async () => {
64397
+ if (stepDef.code && options.allowUnsafeCodeExecution) {
64398
+ context.logger.debug(`[Step] Executing step '${stepDef.id}' with compiled code`);
64399
+ const fn = compileStep(stepDef, workflowId, compiledSteps);
64400
+ const runWorkflow = createRunWorkflowFn({ input, state, context, runInternal });
64401
+ const runtimeCtx = {
64402
+ workflowId,
64403
+ stepId: stepDef.id,
64404
+ input,
64405
+ state,
64406
+ tools: context.tools,
64407
+ logger: context.logger,
64408
+ step: context.step,
64409
+ runWorkflow,
64410
+ toolInfo: options.toolInfo
64411
+ };
64412
+ const result2 = await fn(runtimeCtx);
64413
+ context.logger.debug(`[Step] Compiled code execution completed for step '${stepDef.id}'`);
64414
+ return result2;
64415
+ }
64416
+ context.logger.debug(`[Step] Executing step '${stepDef.id}' with agent`);
64417
+ const result = await executeStepWithAgent(stepDef, workflowId, input, state, context, options, runInternal);
64418
+ context.logger.debug(`[Step] Agent execution completed for step '${stepDef.id}'`);
64419
+ return result;
64420
+ };
64421
+ if (stepDef.timeout && stepDef.timeout > 0) {
64422
+ context.logger.debug(`[Step] Step '${stepDef.id}' has timeout of ${stepDef.timeout}ms`);
64423
+ let timeoutId;
64424
+ const timeoutPromise = new Promise((_, reject) => {
64425
+ timeoutId = setTimeout(() => reject(new Error(`Step '${stepDef.id}' in workflow '${workflowId}' timed out after ${stepDef.timeout}ms`)), stepDef.timeout);
64426
+ });
64427
+ try {
64428
+ return await Promise.race([executeStepLogic(), timeoutPromise]);
64429
+ } finally {
64430
+ if (timeoutId)
64431
+ clearTimeout(timeoutId);
64432
+ }
64433
+ }
64434
+ return await executeStepLogic();
64352
64435
  }
64353
64436
  async function executeStep(stepDef, workflowId, input, state, context, options, compiledSteps, runInternal) {
64354
- if (stepDef.code && options.allowUnsafeCodeExecution) {
64355
- const fn = compileStep(stepDef, workflowId, compiledSteps);
64356
- const runWorkflow = createRunWorkflowFn({ input, state, context, runInternal });
64357
- const runtimeCtx = {
64358
- workflowId,
64359
- stepId: stepDef.id,
64360
- input,
64361
- state,
64362
- tools: context.tools,
64363
- logger: context.logger,
64364
- step: context.step,
64365
- runWorkflow,
64366
- toolInfo: options.toolInfo
64367
- };
64368
- return await fn(runtimeCtx);
64437
+ const result = await executeStepWithTimeout(stepDef, workflowId, input, state, context, options, compiledSteps, runInternal);
64438
+ if (stepDef.outputSchema) {
64439
+ try {
64440
+ const _schema = exports_external.any();
64441
+ if (typeof stepDef.outputSchema === "object") {
64442
+ context.logger.debug(`[Step] Validating output for step '${stepDef.id}' against schema`);
64443
+ if (stepDef.outputSchema.type === "object") {
64444
+ if (typeof result !== "object" || result === null || Array.isArray(result)) {
64445
+ throw new Error(`Expected object output, got ${Array.isArray(result) ? "array" : result === null ? "null" : typeof result}`);
64446
+ }
64447
+ }
64448
+ if (stepDef.outputSchema.type === "array" && !Array.isArray(result)) {
64449
+ throw new Error(`Expected array output, got ${typeof result}`);
64450
+ }
64451
+ }
64452
+ } catch (error46) {
64453
+ throw new Error(`Step '${stepDef.id}' in workflow '${workflowId}' output validation failed: ${error46 instanceof Error ? error46.message : String(error46)}`);
64454
+ }
64369
64455
  }
64370
- return await executeStepWithAgent(stepDef, workflowId, input, state, context, options, runInternal);
64456
+ return result;
64371
64457
  }
64372
64458
  function createDynamicWorkflow(definition, options = {}) {
64373
64459
  if (typeof definition === "string") {
@@ -64383,19 +64469,35 @@ function createDynamicWorkflow(definition, options = {}) {
64383
64469
  if (!workflow) {
64384
64470
  throw new Error(`Workflow '${workflowId}' not found`);
64385
64471
  }
64472
+ const validatedInput = validateAndApplyDefaults(workflowId, workflow, input);
64473
+ context.logger.info(`[Workflow] Starting workflow '${workflowId}'`);
64474
+ context.logger.debug(`[Workflow] Input: ${JSON.stringify(validatedInput)}`);
64475
+ context.logger.debug(`[Workflow] Inherited state: ${JSON.stringify(inheritedState)}`);
64476
+ context.logger.debug(`[Workflow] Steps: ${workflow.steps.map((s) => s.id).join(", ")}`);
64386
64477
  const state = { ...inheritedState };
64387
64478
  let lastOutput;
64388
- for (const stepDef of workflow.steps) {
64479
+ for (let i = 0;i < workflow.steps.length; i++) {
64480
+ const stepDef = workflow.steps[i];
64389
64481
  const stepName = `${workflowId}.${stepDef.id}`;
64482
+ context.logger.info(`[Workflow] Step ${i + 1}/${workflow.steps.length}: ${stepDef.id}`);
64483
+ context.logger.debug(`[Workflow] Step task: ${stepDef.task}`);
64484
+ if (stepDef.expected_outcome) {
64485
+ context.logger.debug(`[Workflow] Expected outcome: ${stepDef.expected_outcome}`);
64486
+ }
64487
+ context.logger.debug(`[Workflow] Current state keys: ${Object.keys(state).join(", ")}`);
64390
64488
  lastOutput = await context.step(stepName, async () => {
64391
- return await executeStep(stepDef, workflowId, input, state, context, options, compiledSteps, runInternal);
64489
+ return await executeStep(stepDef, workflowId, validatedInput, state, context, options, compiledSteps, runInternal);
64392
64490
  });
64393
64491
  const outputKey = stepDef.output ?? stepDef.id;
64394
64492
  state[outputKey] = lastOutput;
64493
+ context.logger.debug(`[Workflow] Step output stored as '${outputKey}': ${typeof lastOutput === "object" ? JSON.stringify(lastOutput).substring(0, 200) : lastOutput}`);
64395
64494
  }
64495
+ context.logger.info(`[Workflow] Completed workflow '${workflowId}'`);
64396
64496
  if (workflow.output) {
64497
+ context.logger.debug(`[Workflow] Returning output field: ${workflow.output}`);
64397
64498
  return state[workflow.output];
64398
64499
  }
64500
+ context.logger.debug(`[Workflow] Returning full state with keys: ${Object.keys(state).join(", ")}`);
64399
64501
  return state;
64400
64502
  };
64401
64503
  return async (workflowId, input, context) => {
@@ -64428,8 +64530,11 @@ The workflow definition must follow this structure:
64428
64530
  {
64429
64531
  "id": "stepId",
64430
64532
  "task": "Description of the step",
64431
- "tools": ["toolName1", "toolName2"], // Optional list of tools needed
64432
- "output": "outputVariableName", // Optional
64533
+ "tools": ["toolName1", "toolName2"], // Optional: restrict which tools can be used
64534
+ "output": "outputVariableName", // Optional: defaults to step id
64535
+ "timeout": 30000, // Optional: timeout in milliseconds
64536
+ "expected_outcome": "What this step produces", // Optional: documentation
64537
+ "outputSchema": { "type": "object" } // Optional: validation schema
64433
64538
  }
64434
64539
  ],
64435
64540
  "output": "outputVariableName" // Optional
@@ -64443,6 +64548,15 @@ Constraints:
64443
64548
  - Break down complex tasks into logical steps.
64444
64549
  - Define clear inputs and outputs.
64445
64550
 
64551
+ Quality Guidelines:
64552
+ - Add "timeout" field (in milliseconds) for steps that might take long (file I/O, API calls, searches)
64553
+ - Use "expected_outcome" field to document what each step should produce
64554
+ - Use descriptive step IDs (e.g., "validateInput", "fetchUserData", not "step1", "step2")
64555
+ - Design steps to be focused - one responsibility per step
64556
+ - For steps that process multiple items, consider creating a sub-workflow
64557
+ - Add "outputSchema" with type information for validation-critical steps
64558
+ - Order steps logically with clear data flow
64559
+
64446
64560
  Example 1:
64447
64561
  User: "Research a topic and summarize it."
64448
64562
  Output:
@@ -64530,70 +64644,407 @@ Your task is to implement the TypeScript code for the steps in the provided work
64530
64644
  You will receive a JSON workflow definition where the "code" field is null.
64531
64645
  You must fill in the "code" field for each step with valid TypeScript code.
64532
64646
 
64533
- The code will be executed in an async function with the following signature:
64534
- async (ctx) => {
64535
- // Your code here
64536
- }
64537
-
64538
- The \`ctx\` object provides access to:
64539
- - \`ctx.input\`: The workflow inputs.
64540
- - \`ctx.state\`: A shared state object for passing data between steps.
64541
- - \`ctx.tools\`: An object containing available tools.
64542
- - \`ctx.runWorkflow\`: (workflowId: string, input?: any) => Promise<any>. Use this to run other workflows.
64543
-
64544
- Guidelines:
64545
- - Use \`await\` for asynchronous operations.
64546
- - Return the output value of the step.
64547
- - Access inputs via \`ctx.input.inputName\`.
64548
- - Access previous step outputs via \`ctx.state.stepOutputName\`.
64549
- - Use \`ctx.tools.invokeTool({ toolName: 'name', input: { ... } })\` to call tools.
64550
- - Use \`ctx.tools.generateText({ messages: [...] })\` for LLM calls.
64551
- - Use \`ctx.tools.invokeTool({ toolName: 'runAgent', input: { prompt: '...' } })\` for complex sub-tasks that require multiple steps or tools. Prefer this over \`generateText\` for advanced tasks.
64552
-
64553
- Example Code for a step:
64554
- \`\`\`typescript
64555
- const searchResults = await ctx.tools.invokeTool({
64556
- toolName: 'search',
64557
- input: { query: ctx.input.topic }
64558
- });
64559
- return searchResults;
64560
- \`\`\`
64561
-
64562
- Example Code for LLM step:
64563
- \`\`\`typescript
64564
- const summary = await ctx.tools.generateText({
64565
- messages: [
64566
- { role: 'system', content: 'Summarize the following text.' },
64567
- { role: 'user', content: ctx.state.searchResults }
64568
- ]
64569
- });
64570
- return summary;
64571
- \`\`\`
64572
-
64573
- Example Code for runAgent:
64574
- \`\`\`typescript
64575
- const result = await ctx.tools.invokeTool({
64576
- toolName: 'runAgent',
64577
- input: {
64578
- prompt: 'Research the history of the internet and write a summary.',
64579
- tools: ['search', 'generateText']
64580
- }
64581
- });
64582
- return result;
64583
- \`\`\`
64584
-
64585
- Example Code for invoking a sub-workflow:
64586
- \`\`\`typescript
64587
- const results = [];
64588
- for (const pr of ctx.state.prs) {
64589
- const review = await ctx.runWorkflow('reviewPR', { prId: pr.id });
64590
- results.push(review);
64591
- }
64592
- return results;
64593
- \`\`\`
64594
-
64595
- Return the complete workflow JSON with the "code" fields populated.
64596
- `;
64647
+ CRITICAL: Each step "code" field must contain ONLY the function body statements (the code inside the curly braces).
64648
+ DO NOT include function declaration, arrow function syntax, async keyword, parameter list, or outer curly braces.
64649
+
64650
+ The code will be wrapped automatically in: \`async (ctx) => { YOUR_CODE_HERE }\`
64651
+
64652
+ Example of CORRECT code field:
64653
+ \`\`\`ts
64654
+ const result = await ctx.tools.readFile({ path: 'README.md' })
64655
+ if (!result) throw new Error('File not found')
64656
+ return result
64657
+ \`\`\`
64658
+
64659
+ Example of INCORRECT code field (DO NOT DO THIS):
64660
+ \`\`\`ts
64661
+ async (ctx) => {
64662
+ const result = await ctx.tools.readFile({ path: 'README.md' })
64663
+ return result
64664
+ }
64665
+ \`\`\`
64666
+
64667
+ Example of INCORRECT code field (DO NOT DO THIS):
64668
+ \`\`\`ts
64669
+ (ctx) => {
64670
+ return 'hello'
64671
+ }
64672
+ \`\`\`
64673
+
64674
+ ## Runtime context (ctx)
64675
+ \`\`\`ts
64676
+ // Runtime types (for reference)
64677
+ type Logger = {
64678
+ debug: (...args: any[]) => void
64679
+ info: (...args: any[]) => void
64680
+ warn: (...args: any[]) => void
64681
+ error: (...args: any[]) => void
64682
+ }
64683
+
64684
+ type StepFn = {
64685
+ <T>(name: string, fn: () => Promise<T>): Promise<T>
64686
+ <T>(name: string, options: { retry?: number }, fn: () => Promise<T>): Promise<T>
64687
+ }
64688
+
64689
+ type JsonModelMessage = { role: 'system' | 'user' | 'assistant' | 'tool'; content: any }
64690
+ type JsonResponseMessage = { role: 'assistant' | 'tool'; content: any }
64691
+ type ToolSet = Record<string, any>
64692
+
64693
+ type ToolResponseResult =
64694
+ | { type: 'text'; value: string }
64695
+ | { type: 'json'; value: any }
64696
+ | { type: 'error-text'; value: string }
64697
+ | { type: 'error-json'; value: any }
64698
+ | { type: 'content'; value: any[] }
64699
+
64700
+ type AgentToolResponse =
64701
+ | { type: 'Reply'; message: ToolResponseResult }
64702
+ | { type: 'Exit'; message: string; object?: any }
64703
+ | { type: 'Error'; message: ToolResponseResult }
64704
+
64705
+ type ExitReason =
64706
+ | { type: 'UsageExceeded' }
64707
+ | { type: 'Exit'; message: string; object?: any }
64708
+ | { type: 'Error'; error: { message: string; stack?: string } }
64709
+
64710
+ type FullAgentToolInfo = { name: string; description: string; parameters: any; handler: any }
64711
+
64712
+ // Tools available on ctx.tools in dynamic steps
64713
+ type DynamicWorkflowTools = {
64714
+ // LLM + agent helpers
64715
+ generateText: (input: { messages: JsonModelMessage[]; tools: ToolSet }) => Promise<JsonResponseMessage[]>
64716
+ runAgent: (input: {
64717
+ tools: Readonly<FullAgentToolInfo[]>
64718
+ maxToolRoundTrips?: number
64719
+ userMessage: readonly JsonModelMessage[]
64720
+ } & ({ messages: JsonModelMessage[] } | { systemPrompt: string })) => Promise<ExitReason>
64721
+
64722
+ // Generic bridge to "agent tools" by name
64723
+ invokeTool: (input: { toolName: string; input: any }) => Promise<AgentToolResponse>
64724
+
64725
+ // File + command helpers (direct)
64726
+ readFile: (input: { path: string }) => Promise<string | null>
64727
+ writeToFile: (input: { path: string; content: string }) => Promise<void>
64728
+ executeCommand: (input: { command: string; pipe?: boolean } & ({ args: string[]; shell?: false } | { shell: true })) => Promise<{
64729
+ exitCode: number
64730
+ stdout: string
64731
+ stderr: string
64732
+ }>
64733
+
64734
+ // CLI UX helpers
64735
+ confirm: (input: { message: string }) => Promise<boolean>
64736
+ input: (input: { message: string; default?: string }) => Promise<string>
64737
+ select: (input: { message: string; choices: { name: string; value: string }[] }) => Promise<string>
64738
+ }
64739
+
64740
+ type DynamicStepRuntimeContext = {
64741
+ workflowId: string
64742
+ stepId: string
64743
+ input: Record<string, any>
64744
+ state: Record<string, any>
64745
+ tools: DynamicWorkflowTools
64746
+ logger: Logger
64747
+ step: StepFn
64748
+ runWorkflow: (workflowId: string, input?: Record<string, any>) => Promise<any>
64749
+ toolInfo?: ReadonlyArray<FullAgentToolInfo>
64750
+ }
64751
+ \`\`\`
64752
+
64753
+ - \`ctx.input\`: workflow inputs (read-only).
64754
+ - \`ctx.state\`: shared state between steps (previous step outputs are stored here).
64755
+ - \`ctx.tools\`: async tool functions. Call tools as \`await ctx.tools.someTool({ ... })\`.
64756
+ - \`ctx.runWorkflow\`: run a sub-workflow by id.
64757
+
64758
+ ## Guidelines
64759
+ - Use \`await\` for all async operations.
64760
+ - Return the output value for the step (this becomes the step output).
64761
+ - Access inputs via \`ctx.input.<inputId>\`.
64762
+ - Access previous step outputs via \`ctx.state.<stepOutputKey>\` (defaults to the step \`output\` or \`id\`).
64763
+
64764
+ ## Quality Guidelines for Code Implementation
64765
+
64766
+ ### Error Handling
64767
+ - ALWAYS validate inputs at the start of steps
64768
+ - Use try-catch for operations that might fail (file I/O, parsing, API calls)
64769
+ - Preserve stack traces: re-throw original errors rather than creating new ones
64770
+ - Use error type guards: \`const err = error instanceof Error ? error : new Error(String(error))\`
64771
+ - Check for null/undefined before using values
64772
+ - Handle edge cases (empty arrays, missing files, invalid data)
64773
+
64774
+ ### Logging
64775
+ - Use \`ctx.logger.info()\` for important progress updates
64776
+ - Use \`ctx.logger.debug()\` for detailed information
64777
+ - Use \`ctx.logger.warn()\` for recoverable issues
64778
+ - Use \`ctx.logger.error()\` before throwing errors
64779
+ - Log when starting and completing significant operations
64780
+ - Use template literals for readability: \`ctx.logger.info(\\\`Processing \${items.length} items...\\\`)\`
64781
+
64782
+ ### User Experience
64783
+ - Provide progress feedback for long operations
64784
+ - Return structured data (objects/arrays), not strings when possible
64785
+ - Include helpful metadata in results (counts, timestamps, status)
64786
+ - For batch operations, report progress: \`Processed 5/10 items\`
64787
+
64788
+ ### Data Validation
64789
+ - Validate required fields exist before accessing
64790
+ - Check data types match expectations
64791
+ - Validate array lengths before iteration
64792
+ - Example: \`if (!data?.users || !Array.isArray(data.users)) throw new Error('Invalid data format')\`
64793
+
64794
+ ### Best Practices
64795
+ - Use meaningful variable names
64796
+ - Avoid nested callbacks - use async/await
64797
+ - Clean up resources (close files, clear timeouts)
64798
+ - Return consistent data structures across similar steps
64799
+ - For iteration, consider batching or rate limiting
64800
+
64801
+ ### When to Simplify
64802
+ - Simple transformation steps (e.g., formatting strings) need only basic error handling
64803
+ - Internal sub-workflow steps with validated inputs from parent can skip redundant validation
64804
+ - Minimal logging is fine for fast steps (<100ms) that don't perform I/O or external calls
64805
+ - Use judgment: match error handling complexity to the step's failure risk and impact
64806
+
64807
+ ## Tool calling examples (every tool)
64808
+
64809
+ ### Direct ctx.tools methods
64810
+ \`\`\`ts
64811
+ // readFile
64812
+ const readme = await ctx.tools.readFile({ path: 'README.md' })
64813
+ if (readme == null) throw new Error('README.md not found')
64814
+
64815
+ // writeToFile
64816
+ await ctx.tools.writeToFile({ path: 'notes.txt', content: 'hello\\n' })
64817
+
64818
+ // executeCommand (args form)
64819
+ const rg = await ctx.tools.executeCommand({ command: 'rg', args: ['-n', 'TODO', '.'] })
64820
+ if (rg.exitCode !== 0) throw new Error(rg.stderr)
64821
+
64822
+ // executeCommand (shell form)
64823
+ await ctx.tools.executeCommand({ command: 'ls -la', shell: true, pipe: true })
64824
+
64825
+ // generateText (LLM call; pass tools: {})
64826
+ const msgs = await ctx.tools.generateText({
64827
+ messages: [
64828
+ { role: 'system', content: 'Summarize the following text.' },
64829
+ { role: 'user', content: readme },
64830
+ ],
64831
+ tools: {},
64832
+ })
64833
+ const last = msgs[msgs.length - 1]
64834
+ const lastText = typeof last?.content === 'string' ? last.content : JSON.stringify(last?.content)
64835
+
64836
+ // runAgent (nested agent; use ctx.toolInfo as the tool list)
64837
+ const agentRes = await ctx.tools.runAgent({
64838
+ systemPrompt: 'You are a helpful assistant.',
64839
+ userMessage: [{ role: 'user', content: 'Summarize README.md in 3 bullets.' }],
64840
+ tools: (ctx.toolInfo ?? []) as any,
64841
+ })
64842
+ if (agentRes.type !== 'Exit') throw new Error('runAgent failed')
64843
+
64844
+ // confirm / input / select (interactive)
64845
+ const ok = await ctx.tools.confirm({ message: 'Proceed?' })
64846
+ const name = await ctx.tools.input({ message: 'Name?', default: 'main' })
64847
+ const flavor = await ctx.tools.select({
64848
+ message: 'Pick one',
64849
+ choices: [
64850
+ { name: 'A', value: 'a' },
64851
+ { name: 'B', value: 'b' },
64852
+ ],
64853
+ })
64854
+
64855
+ \`\`\`
64856
+
64857
+ ### Agent tools via ctx.tools.invokeTool (toolName examples)
64858
+ \`\`\`ts
64859
+ // Helper to unwrap a successful tool reply
64860
+ function unwrapToolValue(resp: any) {
64861
+ if (!resp || resp.type !== 'Reply') {
64862
+ const msg = resp?.message?.value
64863
+ throw new Error(typeof msg === 'string' ? msg : JSON.stringify(resp))
64864
+ }
64865
+ return resp.message.value
64866
+ }
64867
+
64868
+ // askFollowupQuestion
64869
+ const answersText = unwrapToolValue(
64870
+ await ctx.tools.invokeTool({
64871
+ toolName: 'askFollowupQuestion',
64872
+ input: { questions: [{ prompt: 'Which directory?', options: ['src', 'packages'] }] },
64873
+ }),
64874
+ )
64875
+
64876
+ // listFiles
64877
+ const filesText = unwrapToolValue(
64878
+ await ctx.tools.invokeTool({
64879
+ toolName: 'listFiles',
64880
+ input: { path: 'src', recursive: true, maxCount: 2000, includeIgnored: false },
64881
+ }),
64882
+ )
64883
+
64884
+ // searchFiles
64885
+ const hitsText = unwrapToolValue(
64886
+ await ctx.tools.invokeTool({
64887
+ toolName: 'searchFiles',
64888
+ input: { path: '.', regex: 'generateWorkflowCodeWorkflow', filePattern: '*.ts' },
64889
+ }),
64890
+ )
64891
+
64892
+ // fetchUrl
64893
+ const pageText = unwrapToolValue(await ctx.tools.invokeTool({ toolName: 'fetchUrl', input: { url: 'https://example.com' } }))
64894
+
64895
+ // search (web search)
64896
+ const webResults = unwrapToolValue(
64897
+ await ctx.tools.invokeTool({ toolName: 'search', input: { query: 'TypeScript zod schema examples' } }),
64898
+ )
64899
+
64900
+ // executeCommand (provider-backed; may require approval in some environments)
64901
+ const cmdText = unwrapToolValue(
64902
+ await ctx.tools.invokeTool({ toolName: 'executeCommand', input: { command: 'bun test', requiresApproval: false } }),
64903
+ )
64904
+
64905
+ // readFile / writeToFile (provider-backed)
64906
+ const fileText = unwrapToolValue(
64907
+ await ctx.tools.invokeTool({ toolName: 'readFile', input: { path: 'README.md', includeIgnored: false } }),
64908
+ )
64909
+ const writeText = unwrapToolValue(await ctx.tools.invokeTool({ toolName: 'writeToFile', input: { path: 'out.txt', content: 'hi' } }))
64910
+
64911
+ // replaceInFile
64912
+ const diff = ['<<<<<<< SEARCH', 'old', '=======', 'new', '>>>>>>> REPLACE'].join('\\n')
64913
+ const replaceText = unwrapToolValue(await ctx.tools.invokeTool({ toolName: 'replaceInFile', input: { path: 'out.txt', diff } }))
64914
+
64915
+ // removeFile / renameFile
64916
+ const rmText = unwrapToolValue(await ctx.tools.invokeTool({ toolName: 'removeFile', input: { path: 'out.txt' } }))
64917
+ const mvText = unwrapToolValue(
64918
+ await ctx.tools.invokeTool({ toolName: 'renameFile', input: { source_path: 'a.txt', target_path: 'b.txt' } }),
64919
+ )
64920
+
64921
+ // readBinaryFile (returns { type: 'content', value: [...] } in resp.message)
64922
+ const binResp = await ctx.tools.invokeTool({ toolName: 'readBinaryFile', input: { url: 'file://path/to/image.png' } })
64923
+ \`\`\`
64924
+
64925
+ ### Sub-workflow example (ctx.runWorkflow)
64926
+ \`\`\`ts
64927
+ const results: any[] = []
64928
+ for (const pr of ctx.state.prs ?? []) {
64929
+ results.push(await ctx.runWorkflow('reviewPR', { prId: pr.id }))
64930
+ }
64931
+ return results
64932
+ \`\`\`
64933
+
64934
+ ## Complete Example: High-Quality Step Implementation
64935
+
64936
+ This example demonstrates all quality guidelines in a single step:
64937
+
64938
+ \`\`\`ts
64939
+ // Step: processUserData
64940
+ // Task: Read, validate, and process user data from a file
64941
+
64942
+ // Input validation
64943
+ if (!ctx.input.dataFile) {
64944
+ throw new Error('Missing required input: dataFile')
64945
+ }
64946
+
64947
+ ctx.logger.info(\`Starting user data processing for: \${ctx.input.dataFile}\`)
64948
+
64949
+ // Read file with error handling
64950
+ let rawData
64951
+ try {
64952
+ ctx.logger.debug(\`Reading file: \${ctx.input.dataFile}\`)
64953
+ rawData = await ctx.tools.readFile({ path: ctx.input.dataFile })
64954
+
64955
+ if (!rawData) {
64956
+ throw new Error(\`File not found or empty: \${ctx.input.dataFile}\`)
64957
+ }
64958
+ } catch (error) {
64959
+ const err = error instanceof Error ? error : new Error(String(error))
64960
+ ctx.logger.error(\`Failed to read file: \${err.message}\`)
64961
+ throw err // Preserve original stack trace
64962
+ }
64963
+
64964
+ // Parse and validate data
64965
+ let users
64966
+ try {
64967
+ ctx.logger.debug('Parsing JSON data')
64968
+ const parsed = JSON.parse(rawData)
64969
+
64970
+ if (!parsed?.users || !Array.isArray(parsed.users)) {
64971
+ throw new Error('Invalid data format: expected {users: [...]}')
64972
+ }
64973
+
64974
+ users = parsed.users
64975
+ ctx.logger.info(\`Found \${users.length} users to process\`)
64976
+ } catch (error) {
64977
+ const err = error instanceof Error ? error : new Error(String(error))
64978
+ ctx.logger.error(\`Data parsing failed: \${err.message}\`)
64979
+ throw err // Preserve original stack trace
64980
+ }
64981
+
64982
+ // Process each user with progress reporting
64983
+ const results = []
64984
+ for (let i = 0; i < users.length; i++) {
64985
+ const user = users[i]
64986
+
64987
+ // Validate each user object
64988
+ if (!user?.id || !user?.email) {
64989
+ ctx.logger.warn(\`Skipping invalid user at index \${i}: missing id or email\`)
64990
+ continue
64991
+ }
64992
+
64993
+ // Process user
64994
+ const processed = {
64995
+ id: user.id,
64996
+ email: user.email.toLowerCase().trim(),
64997
+ name: user.name?.trim() || 'Unknown',
64998
+ processedAt: new Date().toISOString(),
64999
+ status: 'active'
65000
+ }
65001
+
65002
+ results.push(processed)
65003
+
65004
+ // Progress feedback every 10 items
65005
+ if ((i + 1) % 10 === 0) {
65006
+ ctx.logger.info(\`Processed \${i + 1}/\${users.length} users\`)
65007
+ }
65008
+ }
65009
+
65010
+ ctx.logger.info(\`Successfully processed \${results.length}/\${users.length} users\`)
65011
+
65012
+ // Return structured result with metadata
65013
+ return {
65014
+ users: results,
65015
+ metadata: {
65016
+ totalInput: users.length,
65017
+ totalProcessed: results.length,
65018
+ skipped: users.length - results.length,
65019
+ processedAt: new Date().toISOString()
65020
+ }
65021
+ }
65022
+ \`\`\`
65023
+
65024
+ Key features demonstrated:
65025
+ - Input validation at start
65026
+ - Comprehensive error handling with try-catch that preserves stack traces
65027
+ - Logging at info, debug, warn, and error levels
65028
+ - Progress reporting for long operations (every 10 items)
65029
+ - Data validation throughout (null checks, type checks, array validation)
65030
+ - Structured return value with metadata for observability
65031
+ - Descriptive error messages with context
65032
+ - Meaningful variable names (rawData, users, processed)
65033
+ - Clean async/await usage
65034
+ - Template literals for readable string interpolation
65035
+ - Proper error type guards (error instanceof Error)
65036
+
65037
+ ## Final Instructions
65038
+
65039
+ REMEMBER: The "code" field must be ONLY the function body statements.
65040
+ - DO NOT wrap code in arrow functions: \`(ctx) => { ... }\`
65041
+ - DO NOT wrap code in async functions: \`async (ctx) => { ... }\`
65042
+ - DO NOT include outer curly braces
65043
+ - DO include a return statement if the step should produce output
65044
+ - Each "code" field should be a string containing multiple statements separated by newlines
65045
+
65046
+ Return the complete workflow JSON with the "code" fields populated.
65047
+ `;
64597
65048
  var generateWorkflowDefinitionWorkflow = async (input, ctx) => {
64598
65049
  let systemPrompt = WORKFLOW_DEFINITION_SYSTEM_PROMPT;
64599
65050
  if (input.availableTools && input.availableTools.length > 0) {
@@ -84584,7 +85035,7 @@ function getProviderOptions(options) {
84584
85035
 
84585
85036
  // src/tool-implementations.ts
84586
85037
  import { spawn as spawn3, spawnSync as spawnSync2 } from "node:child_process";
84587
- import fs3, { mkdir as mkdir2 } from "node:fs/promises";
85038
+ import fs4, { mkdir as mkdir2 } from "node:fs/promises";
84588
85039
  import { dirname as dirname2 } from "node:path";
84589
85040
 
84590
85041
  // src/tools/utils/diffLineNumbers.ts
@@ -84817,12 +85268,14 @@ async function getUserInput(message, options = {}) {
84817
85268
 
84818
85269
  // src/workflows/workflow.utils.ts
84819
85270
  import { execSync } from "node:child_process";
85271
+ import { promises as fs3 } from "node:fs";
85272
+ import path from "node:path";
84820
85273
  function parseGitDiffNameStatus(diffOutput) {
84821
85274
  const lines = diffOutput.split(`
84822
85275
  `).filter((line) => line.trim());
84823
85276
  return lines.map((line) => {
84824
85277
  const [status, ...pathParts] = line.split("\t");
84825
- const path = pathParts.join("\t");
85278
+ const path2 = pathParts.join("\t");
84826
85279
  let statusDescription;
84827
85280
  switch (status[0]) {
84828
85281
  case "A":
@@ -84846,7 +85299,7 @@ function parseGitDiffNameStatus(diffOutput) {
84846
85299
  default:
84847
85300
  statusDescription = "Unknown";
84848
85301
  }
84849
- return { path, status: statusDescription };
85302
+ return { path: path2, status: statusDescription };
84850
85303
  });
84851
85304
  }
84852
85305
  function printChangedFiles(logger, changedFiles) {
@@ -84873,21 +85326,21 @@ function parseGitDiffNumStat(output) {
84873
85326
  if (parts.length >= 3) {
84874
85327
  const insertions = parts[0] === "-" ? 0 : Number.parseInt(parts[0], 10);
84875
85328
  const deletions = parts[1] === "-" ? 0 : Number.parseInt(parts[1], 10);
84876
- const path = unquotePath(parts.slice(2).join("\t"));
84877
- stats[path] = { insertions, deletions };
85329
+ const path2 = unquotePath(parts.slice(2).join("\t"));
85330
+ stats[path2] = { insertions, deletions };
84878
85331
  }
84879
85332
  }
84880
85333
  return stats;
84881
85334
  }
84882
- var unquotePath = (path) => {
84883
- if (path.startsWith('"') && path.endsWith('"')) {
85335
+ var unquotePath = (path2) => {
85336
+ if (path2.startsWith('"') && path2.endsWith('"')) {
84884
85337
  try {
84885
- return JSON.parse(path);
85338
+ return JSON.parse(path2);
84886
85339
  } catch {
84887
- return path;
85340
+ return path2;
84888
85341
  }
84889
85342
  }
84890
- return path;
85343
+ return path2;
84891
85344
  };
84892
85345
  function parseGitStatus(statusOutput) {
84893
85346
  const statusLines = statusOutput.split(`
@@ -84896,7 +85349,7 @@ function parseGitStatus(statusOutput) {
84896
85349
  for (const line of statusLines) {
84897
85350
  const indexStatus = line[0];
84898
85351
  const workingTreeStatus = line[1];
84899
- const path = line.length > 3 ? unquotePath(line.slice(3)) : line;
85352
+ const path2 = line.length > 3 ? unquotePath(line.slice(3)) : line;
84900
85353
  const statuses = [];
84901
85354
  if (indexStatus !== " " && indexStatus !== "?") {
84902
85355
  switch (indexStatus) {
@@ -84935,7 +85388,7 @@ function parseGitStatus(statusOutput) {
84935
85388
  }
84936
85389
  }
84937
85390
  if (statuses.length > 0) {
84938
- files.push({ path, status: statuses.join(", ") });
85391
+ files.push({ path: path2, status: statuses.join(", ") });
84939
85392
  }
84940
85393
  }
84941
85394
  return files;
@@ -85081,6 +85534,12 @@ ${fileList}
85081
85534
  </file_list>`,
85082
85535
  `<now_date>${new Date().toISOString()}</now_date>`
85083
85536
  ];
85537
+ try {
85538
+ const agentsMdContent = await fs3.readFile(path.join(cwd, "AGENTS.md"), "utf-8");
85539
+ contextParts.push(`<agents_instructions>
85540
+ ${agentsMdContent}
85541
+ </agents_instructions>`);
85542
+ } catch {}
85084
85543
  if (config4?.rules) {
85085
85544
  contextParts.push(`<rules>
85086
85545
  ${config4.rules}
@@ -85205,12 +85664,12 @@ async function select(input2, context) {
85205
85664
  }
85206
85665
  async function writeToFile(input2) {
85207
85666
  await mkdir2(dirname2(input2.path), { recursive: true });
85208
- await fs3.writeFile(input2.path, input2.content);
85667
+ await fs4.writeFile(input2.path, input2.content);
85209
85668
  return {};
85210
85669
  }
85211
85670
  async function readFile3(input2) {
85212
85671
  try {
85213
- const content = await fs3.readFile(input2.path, "utf8");
85672
+ const content = await fs4.readFile(input2.path, "utf8");
85214
85673
  return content;
85215
85674
  } catch {}
85216
85675
  return null;
@@ -85620,6 +86079,9 @@ Memory is organized using topics, which are like named containers for different
85620
86079
  - Use the default topic for simple, single-context scenarios
85621
86080
  - Memory persists across all tool calls within the current workflow
85622
86081
  `;
86082
+ var AGENTS_INSTRUCTION = `## AGENTS.md Instructions
86083
+
86084
+ If you are working in a subdirectory, check if there is an AGENTS.md file in that directory or parent directories for specific instructions. These files contain project-specific guidelines and conventions that you must follow.`;
85623
86085
 
85624
86086
  // src/workflows/prompts/coder.ts
85625
86087
  var CODER_SYSTEM_PROMPT = `Role: AI developer.
@@ -85631,6 +86093,8 @@ ${MEMORY_USAGE_SECTION}
85631
86093
 
85632
86094
  ${TOOL_USAGE_INSTRUCTION}
85633
86095
 
86096
+ ${AGENTS_INSTRUCTION}
86097
+
85634
86098
  ## Implementation Guidelines
85635
86099
 
85636
86100
  ### 1. Plan Analysis
@@ -86187,6 +86651,8 @@ ${MEMORY_USAGE_SECTION}
86187
86651
 
86188
86652
  ${TOOL_USAGE_INSTRUCTION}
86189
86653
 
86654
+ ${AGENTS_INSTRUCTION}
86655
+
86190
86656
  ## Your Role
86191
86657
 
86192
86658
  As a planner, your expertise lies in:
@@ -86709,10 +87175,10 @@ ${defaultContext}`;
86709
87175
  }
86710
87176
  const outputFiles = [];
86711
87177
  if (filePaths) {
86712
- for (const path of filePaths) {
86713
- const content = await tools2.readFile({ path });
87178
+ for (const path2 of filePaths) {
87179
+ const content = await tools2.readFile({ path: path2 });
86714
87180
  if (content) {
86715
- outputFiles.push({ path, content });
87181
+ outputFiles.push({ path: path2, content });
86716
87182
  }
86717
87183
  }
86718
87184
  }
@@ -87473,7 +87939,7 @@ async function performReviewAndFixCycle(iterationCount, taskItem, highLevelPlan,
87473
87939
  const commitMessages = [];
87474
87940
  for (let i2 = 0;i2 < MAX_REVIEW_RETRIES; i2++) {
87475
87941
  const diffResult = await tools2.executeCommand({ command: "git", args: ["diff", "--name-status", "HEAD~1", "HEAD"] });
87476
- const changedFiles = parseGitDiffNameStatus(diffResult.stdout).filter(({ path }) => path !== ".epic.yml");
87942
+ const changedFiles = parseGitDiffNameStatus(diffResult.stdout).filter(({ path: path2 }) => path2 !== ".epic.yml");
87477
87943
  if (changedFiles.length === 0) {
87478
87944
  logger.info(`No files were changed. Skipping review.
87479
87945
  `);
@@ -87682,7 +88148,7 @@ Phase 6: Final Review and Fixup...
87682
88148
  const commitRange = `${baseBranch}...${currentBranch}`;
87683
88149
  for (let i2 = 0;i2 < MAX_REVIEW_RETRIES; i2++) {
87684
88150
  const diffResult = await tools2.executeCommand({ command: "git", args: ["diff", "--name-status", commitRange] });
87685
- const changedFiles = parseGitDiffNameStatus(diffResult.stdout).filter(({ path }) => path !== ".epic.yml");
88151
+ const changedFiles = parseGitDiffNameStatus(diffResult.stdout).filter(({ path: path2 }) => path2 !== ".epic.yml");
87686
88152
  if (changedFiles.length === 0) {
87687
88153
  logger.info(`No files have been changed in this branch. Skipping final review.
87688
88154
  `);
@@ -88228,7 +88694,7 @@ var commitCommand = new Command("commit").description("Create a commit with AI-g
88228
88694
  });
88229
88695
 
88230
88696
  // src/workflows/epic-context.ts
88231
- import { promises as fs4 } from "node:fs";
88697
+ import { promises as fs5 } from "node:fs";
88232
88698
  var EPIC_CONTEXT_FILE = ".epic.yml";
88233
88699
  var EpicUsageSchema = exports_external.object({
88234
88700
  timestamp: exports_external.number(),
@@ -88257,12 +88723,12 @@ var saveEpicContext = async (context) => {
88257
88723
  memory: context.memory,
88258
88724
  usages: context.usages
88259
88725
  });
88260
- await fs4.writeFile(EPIC_CONTEXT_FILE, yamlString, "utf-8");
88726
+ await fs5.writeFile(EPIC_CONTEXT_FILE, yamlString, "utf-8");
88261
88727
  };
88262
88728
  var loadEpicContext = async () => {
88263
88729
  let fileContent;
88264
88730
  try {
88265
- fileContent = await fs4.readFile(EPIC_CONTEXT_FILE, "utf-8");
88731
+ fileContent = await fs5.readFile(EPIC_CONTEXT_FILE, "utf-8");
88266
88732
  } catch {
88267
88733
  return {};
88268
88734
  }
@@ -88792,9 +89258,9 @@ ${formattedReview}`;
88792
89258
  // src/commands/workflow.ts
88793
89259
  import { mkdir as mkdir3, readFile as readFile5, writeFile as writeFile2 } from "node:fs/promises";
88794
89260
  import { dirname as dirname3 } from "node:path";
88795
- async function saveWorkflowFile(path, workflow3) {
88796
- await mkdir3(dirname3(path), { recursive: true });
88797
- await writeFile2(path, $stringify(workflow3));
89261
+ async function saveWorkflowFile(path2, workflow3) {
89262
+ await mkdir3(dirname3(path2), { recursive: true });
89263
+ await writeFile2(path2, $stringify(workflow3));
88798
89264
  }
88799
89265
  function clearWorkflowCode(workflowDef) {
88800
89266
  for (const wf of Object.values(workflowDef.workflows)) {
@@ -88906,12 +89372,15 @@ Return the updated workflow definition.`;
88906
89372
  }
88907
89373
  const workflowDef = parsedResult.definition;
88908
89374
  const workflowNames = Object.keys(workflowDef.workflows);
89375
+ logger.info(`Available workflows: ${workflowNames.join(", ")}`);
88909
89376
  let workflowId = workflowName;
88910
89377
  if (!workflowId) {
88911
89378
  if (workflowNames.includes("main")) {
88912
89379
  workflowId = "main";
89380
+ logger.info(`Using 'main' workflow`);
88913
89381
  } else if (workflowNames.length === 1) {
88914
89382
  workflowId = workflowNames[0];
89383
+ logger.info(`Using workflow '${workflowId}'`);
88915
89384
  } else if (workflowNames.length > 1) {
88916
89385
  logger.error(`Multiple workflows found in file and no 'main' workflow. Please specify one using --workflow <name>. Available workflows: ${workflowNames.join(", ")}`);
88917
89386
  return;
@@ -88924,6 +89393,7 @@ Return the updated workflow definition.`;
88924
89393
  logger.error(`Workflow '${workflowId}' not found in file. Available workflows: ${workflowNames.join(", ")}`);
88925
89394
  return;
88926
89395
  }
89396
+ logger.info(`Using workflow '${workflowId}'`);
88927
89397
  }
88928
89398
  let dynamicRunner;
88929
89399
  try {
@@ -88943,7 +89413,14 @@ Return the updated workflow definition.`;
88943
89413
  if (selectedWorkflow.inputs && selectedWorkflow.inputs.length > 0 && task2) {
88944
89414
  const firstInput = selectedWorkflow.inputs[0];
88945
89415
  workflowInput[firstInput.id] = task2;
89416
+ logger.info(`Workflow input '${firstInput.id}': ${task2}`);
89417
+ } else if (selectedWorkflow.inputs && selectedWorkflow.inputs.length > 0) {
89418
+ logger.info(`Workflow expects inputs: ${selectedWorkflow.inputs.map((i2) => i2.id).join(", ")}`);
89419
+ } else {
89420
+ logger.info("Workflow has no inputs");
88946
89421
  }
89422
+ logger.info(`Workflow has ${selectedWorkflow.steps.length} step(s)`);
89423
+ logger.debug(`Steps: ${selectedWorkflow.steps.map((s2) => `${s2.id} (${s2.task})`).join(", ")}`);
88947
89424
  await runWorkflow(workflowFn, workflowInput, { commandName: "workflow", command, logger, yes });
88948
89425
  }
88949
89426
  var workflowCommand = new Command("workflow").description("Generate, manage, and run custom workflows.").argument("[task]", "The task description for generating the workflow.").option("-f, --file <path>", "Path to the workflow file").option("-w, --workflow <name>", "The name of the workflow to run").option("--create", "Create a new workflow").option("--regenerate", "Regenerate the code for the workflow").action(runWorkflowCommand);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@polka-codes/cli",
3
- "version": "0.9.79",
3
+ "version": "0.9.80",
4
4
  "license": "AGPL-3.0",
5
5
  "author": "github@polka.codes",
6
6
  "type": "module",