@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.
- package/dist/index.js +590 -113
- 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.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
64355
|
-
|
|
64356
|
-
|
|
64357
|
-
|
|
64358
|
-
|
|
64359
|
-
|
|
64360
|
-
|
|
64361
|
-
|
|
64362
|
-
|
|
64363
|
-
|
|
64364
|
-
|
|
64365
|
-
|
|
64366
|
-
|
|
64367
|
-
|
|
64368
|
-
|
|
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
|
|
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 (
|
|
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,
|
|
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
|
|
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
|
-
|
|
64534
|
-
async
|
|
64535
|
-
|
|
64536
|
-
}
|
|
64537
|
-
|
|
64538
|
-
|
|
64539
|
-
|
|
64540
|
-
|
|
64541
|
-
|
|
64542
|
-
|
|
64543
|
-
|
|
64544
|
-
|
|
64545
|
-
|
|
64546
|
-
|
|
64547
|
-
|
|
64548
|
-
|
|
64549
|
-
|
|
64550
|
-
|
|
64551
|
-
|
|
64552
|
-
|
|
64553
|
-
Example
|
|
64554
|
-
\`\`\`
|
|
64555
|
-
|
|
64556
|
-
|
|
64557
|
-
|
|
64558
|
-
|
|
64559
|
-
|
|
64560
|
-
|
|
64561
|
-
|
|
64562
|
-
|
|
64563
|
-
|
|
64564
|
-
|
|
64565
|
-
|
|
64566
|
-
|
|
64567
|
-
|
|
64568
|
-
|
|
64569
|
-
|
|
64570
|
-
|
|
64571
|
-
|
|
64572
|
-
|
|
64573
|
-
|
|
64574
|
-
|
|
64575
|
-
|
|
64576
|
-
|
|
64577
|
-
|
|
64578
|
-
|
|
64579
|
-
|
|
64580
|
-
|
|
64581
|
-
}
|
|
64582
|
-
|
|
64583
|
-
|
|
64584
|
-
|
|
64585
|
-
|
|
64586
|
-
|
|
64587
|
-
|
|
64588
|
-
|
|
64589
|
-
|
|
64590
|
-
|
|
64591
|
-
|
|
64592
|
-
|
|
64593
|
-
|
|
64594
|
-
|
|
64595
|
-
|
|
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
|
|
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
|
|
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
|
|
84877
|
-
stats[
|
|
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 = (
|
|
84883
|
-
if (
|
|
85335
|
+
var unquotePath = (path2) => {
|
|
85336
|
+
if (path2.startsWith('"') && path2.endsWith('"')) {
|
|
84884
85337
|
try {
|
|
84885
|
-
return JSON.parse(
|
|
85338
|
+
return JSON.parse(path2);
|
|
84886
85339
|
} catch {
|
|
84887
|
-
return
|
|
85340
|
+
return path2;
|
|
84888
85341
|
}
|
|
84889
85342
|
}
|
|
84890
|
-
return
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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 }) =>
|
|
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 }) =>
|
|
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
|
|
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
|
|
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
|
|
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(
|
|
88796
|
-
await mkdir3(dirname3(
|
|
88797
|
-
await writeFile2(
|
|
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);
|