@polka-codes/cli 0.9.49 → 0.9.50

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 +443 -174
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -35579,7 +35579,7 @@ var {
35579
35579
  Help
35580
35580
  } = import__.default;
35581
35581
  // package.json
35582
- var version = "0.9.49";
35582
+ var version = "0.9.50";
35583
35583
 
35584
35584
  // src/commands/code.ts
35585
35585
  import { readFile as readFile3 } from "node:fs/promises";
@@ -48609,7 +48609,6 @@ var TodoItemSchema = exports_external.object({
48609
48609
  id: exports_external.string(),
48610
48610
  title: exports_external.string(),
48611
48611
  description: exports_external.string(),
48612
- relevantFileList: exports_external.array(exports_external.string()),
48613
48612
  status: TodoStatus
48614
48613
  });
48615
48614
  var UpdateTodoItemInputSchema = exports_external.object({
@@ -48618,7 +48617,6 @@ var UpdateTodoItemInputSchema = exports_external.object({
48618
48617
  parentId: exports_external.string().nullish(),
48619
48618
  title: exports_external.string().nullish(),
48620
48619
  description: exports_external.string().nullish(),
48621
- relevantFileList: exports_external.array(exports_external.string()).nullish(),
48622
48620
  status: TodoStatus.nullish()
48623
48621
  }).superRefine((data, ctx) => {
48624
48622
  if (data.operation === "add") {
@@ -52472,13 +52470,23 @@ async function searchFiles(path, regex, filePattern, cwd, excludeFiles) {
52472
52470
  }
52473
52471
 
52474
52472
  // ../cli-shared/src/provider.ts
52473
+ class InMemoryStore {
52474
+ #data;
52475
+ async read() {
52476
+ return this.#data;
52477
+ }
52478
+ async write(data) {
52479
+ this.#data = data;
52480
+ }
52481
+ }
52475
52482
  var getProvider = (options = {}) => {
52476
52483
  const ig = import_ignore2.default().add(options.excludeFiles ?? []);
52477
- const memoryStore = {};
52478
- const todoItems = [];
52484
+ const memoryStore = options.memoryStore ?? new InMemoryStore;
52485
+ const todoItemStore = options.todoItemStore ?? new InMemoryStore;
52479
52486
  const defaultMemoryTopic = ":default:";
52480
52487
  const provider2 = {
52481
52488
  listTodoItems: async (id, status) => {
52489
+ const todoItems = await todoItemStore.read() ?? [];
52482
52490
  let items;
52483
52491
  if (!id) {
52484
52492
  items = todoItems.filter((i) => !i.id.includes("."));
@@ -52507,6 +52515,7 @@ var getProvider = (options = {}) => {
52507
52515
  return items;
52508
52516
  },
52509
52517
  getTodoItem: async (id) => {
52518
+ const todoItems = await todoItemStore.read() ?? [];
52510
52519
  const item = todoItems.find((i) => i.id === id);
52511
52520
  if (!item) {
52512
52521
  throw new Error(`To-do item with id ${id} not found`);
@@ -52515,8 +52524,9 @@ var getProvider = (options = {}) => {
52515
52524
  return { ...item, subItems };
52516
52525
  },
52517
52526
  updateTodoItem: async (input) => {
52527
+ const todoItems = await todoItemStore.read() ?? [];
52518
52528
  if (input.operation === "add") {
52519
- const { parentId, title, description, relevantFileList } = input;
52529
+ const { parentId, title, description, status } = input;
52520
52530
  if (!title) {
52521
52531
  throw new Error("Title is required for add operation");
52522
52532
  }
@@ -52527,19 +52537,27 @@ var getProvider = (options = {}) => {
52527
52537
  throw new Error(`Parent to-do item with id ${parentId} not found`);
52528
52538
  }
52529
52539
  const childItems = todoItems.filter((i) => i.id.startsWith(`${parentId}.`) && i.id.split(".").length === parentId.split(".").length + 1);
52530
- newId = `${parentId}.${childItems.length + 1}`;
52540
+ const maxId = childItems.reduce((max, item) => {
52541
+ const parts = item.id.split(".");
52542
+ const lastPart = parseInt(parts[parts.length - 1], 10);
52543
+ return Math.max(max, lastPart);
52544
+ }, 0);
52545
+ newId = `${parentId}.${maxId + 1}`;
52531
52546
  } else {
52532
52547
  const rootItems = todoItems.filter((i) => !i.id.includes("."));
52533
- newId = `${rootItems.length + 1}`;
52548
+ const maxId = rootItems.reduce((max, item) => {
52549
+ const idNum = parseInt(item.id, 10);
52550
+ return Math.max(max, idNum);
52551
+ }, 0);
52552
+ newId = `${maxId + 1}`;
52534
52553
  }
52535
52554
  const newItem = {
52536
52555
  id: newId,
52537
52556
  title,
52538
52557
  description: description ?? "",
52539
- relevantFileList: relevantFileList ?? [],
52540
- status: "open"
52558
+ status: status ?? "open"
52541
52559
  };
52542
- todoItems.push(newItem);
52560
+ await todoItemStore.write([...todoItems, newItem]);
52543
52561
  return { id: newId };
52544
52562
  } else {
52545
52563
  const { id } = input;
@@ -52556,40 +52574,43 @@ var getProvider = (options = {}) => {
52556
52574
  if (input.description != null) {
52557
52575
  item.description = input.description ?? "";
52558
52576
  }
52559
- if (input.relevantFileList != null) {
52560
- item.relevantFileList = input.relevantFileList;
52561
- }
52562
52577
  if (input.status != null) {
52563
52578
  item.status = input.status;
52564
52579
  }
52580
+ await todoItemStore.write(todoItems);
52565
52581
  return { id };
52566
52582
  }
52567
52583
  },
52568
52584
  listMemoryTopics: async () => {
52569
- return Object.keys(memoryStore);
52585
+ const memory = await memoryStore.read() ?? {};
52586
+ return Object.keys(memory);
52570
52587
  },
52571
52588
  readMemory: async (topic = defaultMemoryTopic) => {
52572
- return memoryStore[topic];
52589
+ const memory = await memoryStore.read() ?? {};
52590
+ return memory[topic];
52573
52591
  },
52574
52592
  updateMemory: async (operation, topic, content) => {
52575
52593
  const memoryTopic = topic ?? defaultMemoryTopic;
52594
+ const memory = await memoryStore.read() ?? {};
52576
52595
  switch (operation) {
52577
52596
  case "append":
52578
52597
  if (content === undefined) {
52579
52598
  throw new Error("Content is required for append operation.");
52580
52599
  }
52581
- memoryStore[memoryTopic] = (memoryStore[memoryTopic] || "") + content;
52600
+ memory[memoryTopic] = `${memory[memoryTopic] || ""}
52601
+ ${content}`;
52582
52602
  break;
52583
52603
  case "replace":
52584
52604
  if (content === undefined) {
52585
52605
  throw new Error("Content is required for replace operation.");
52586
52606
  }
52587
- memoryStore[memoryTopic] = content;
52607
+ memory[memoryTopic] = content;
52588
52608
  break;
52589
52609
  case "remove":
52590
- delete memoryStore[memoryTopic];
52610
+ delete memory[memoryTopic];
52591
52611
  break;
52592
52612
  }
52613
+ await memoryStore.write(memory);
52593
52614
  },
52594
52615
  readFile: async (path, includeIgnored) => {
52595
52616
  if (!includeIgnored && ig.ignores(path)) {
@@ -65261,9 +65282,10 @@ ${event.systemPrompt}`);
65261
65282
  }
65262
65283
  case "ToolUse" /* ToolUse */: {
65263
65284
  if (verbose > 0) {
65285
+ const params = verbose > 1 ? event.params : simplifyToolParameters(event.tool, event.params);
65264
65286
  customConsole.log(source_default.yellow(`
65265
65287
 
65266
- Tool use:`, event.tool), simplifyToolParameters(event.tool, event.params));
65288
+ Tool use:`, event.tool), params);
65267
65289
  }
65268
65290
  const stats = taskToolCallStats.get(event.tool) ?? { calls: 0, success: 0, errors: 0 };
65269
65291
  stats.calls++;
@@ -65308,7 +65330,9 @@ Tool error:`, event.tool));
65308
65330
  break;
65309
65331
  }
65310
65332
  case "Exit" /* Exit */:
65311
- customConsole.log("Exit Message:", event.exitReason.message);
65333
+ if (verbose > 0) {
65334
+ customConsole.log("Exit Message:", event.exitReason.message);
65335
+ }
65312
65336
  break;
65313
65337
  }
65314
65338
  for (const [tool3, taskStats] of taskToolCallStats.entries()) {
@@ -78501,6 +78525,14 @@ ${defaultContent}
78501
78525
  return contextParts.join(`
78502
78526
  `);
78503
78527
  }
78528
+ async function readMemory(input2, context) {
78529
+ const provider3 = context.toolProvider;
78530
+ return await provider3.readMemory(input2.topic) ?? "";
78531
+ }
78532
+ async function listMemoryTopics(_input, context) {
78533
+ const provider3 = context.toolProvider;
78534
+ return provider3.listMemoryTopics();
78535
+ }
78504
78536
  async function updateMemory(input2, context) {
78505
78537
  const provider3 = context.toolProvider;
78506
78538
  const content = "content" in input2 ? input2.content : undefined;
@@ -78532,6 +78564,8 @@ var localToolHandlers = {
78532
78564
  invokeTool,
78533
78565
  taskEvent,
78534
78566
  getMemoryContext,
78567
+ readMemory,
78568
+ listMemoryTopics,
78535
78569
  updateMemory,
78536
78570
  listTodoItems,
78537
78571
  getTodoItem,
@@ -78564,7 +78598,7 @@ async function runWorkflow(workflow2, workflowInput, options) {
78564
78598
  maxCost: config4.budget
78565
78599
  });
78566
78600
  const onEvent = printEvent(verbose, usage, process.stderr);
78567
- const toolProvider = getProvider({ excludeFiles: config4.excludeFiles });
78601
+ const toolProvider = (options.getProvider ?? getProvider)({ excludeFiles: config4.excludeFiles });
78568
78602
  const commandConfig = providerConfig.getConfigForCommand(commandName);
78569
78603
  if (!commandConfig) {
78570
78604
  throw new Error(`No provider configured for command: ${commandName}`);
@@ -78637,7 +78671,17 @@ ${JSON.stringify(schema, null, 2)}
78637
78671
  \`\`\`
78638
78672
  `;
78639
78673
  }
78640
- var TOOL_USAGE_INSTRUCTION = `When you use a tool, you MUST first provide a short summary of what you are doing and why you are using that tool. This summary will be shown to the user to explain your actions.`;
78674
+ var TOOL_USAGE_INSTRUCTION = `
78675
+ ## Action Line
78676
+
78677
+ Before any tool call, emit a single high-level action line.
78678
+
78679
+ You MUST follow these style constraints for the action line:
78680
+ - NO filler or preambles.
78681
+ - DO NOT use "ok", "okay", "alright".
78682
+ - DO NOT use first person ("I", "I'm", "I will", "I'll", etc.).
78683
+ - NO apologies, hedging, or promises about later work.
78684
+ `;
78641
78685
  var MEMORY_USAGE_SECTION = `## Memory Usage
78642
78686
 
78643
78687
  You have access to a memory feature to store and retrieve information across tool calls.
@@ -78873,15 +78917,21 @@ Effective planning requires understanding before action:
78873
78917
  - File system exploration (\`listFiles\`, \`searchFiles\`) reveals structure and patterns.
78874
78918
  - Reading existing files (\`readFile\`) shows coding style and conventions.
78875
78919
 
78876
- 3. **High-Level, Not Granular**
78877
- - Your plan should focus on the "what" and "why" at a strategic level.
78878
- - Avoid getting bogged down in implementation minutiae. For example, instead of "add a 20px margin to the button", say "Update the component styling to align with the design system".
78879
- - The output should be a roadmap, not a turn-by-turn navigation.
78920
+ 3. **High-Level Strategy with Detailed Tasks**
78921
+ - "High-level" refers to strategic phases and architectural decisions, not individual implementation steps.
78922
+ - While the plan structure should be strategic (phases, components), each task item must contain detailed implementation guidance.
78923
+ - Each task should include specific file paths, function names, implementation patterns, and technical details.
78924
+ - The plan should be detailed enough that task creation does not require additional codebase exploration.
78925
+ - Example of appropriate detail level:
78926
+ * ❌ Too vague: "Add authentication"
78927
+ * ❌ Too granular: "Add a 20px margin to the button on line 45"
78928
+ * ✅ Appropriate: "Implement authentication endpoints in \`src/api/auth.ts\` with \`POST /api/auth/login\` that validates credentials using bcrypt and returns JWT tokens"
78880
78929
 
78881
78930
  4. **Clarity for AI Implementation**
78882
- - The plan must be clear enough for AI agents to implement directly.
78883
- - Each task should be a concrete, implementable piece of work.
78884
- - Focus on what needs to be built and how.
78931
+ - The plan must be clear enough for AI agents to implement directly without further exploration.
78932
+ - Each task should be a concrete, implementable piece of work with all necessary details.
78933
+ - Include specific technical information: file paths, function signatures, dependencies, patterns to follow.
78934
+ - The task creation agent should be able to parse the plan and create todo items without exploring the codebase.
78885
78935
 
78886
78936
  ## Your Approach
78887
78937
 
@@ -78902,33 +78952,61 @@ For epic-scale work, **checkboxes are RECOMMENDED** to help track progress throu
78902
78952
 
78903
78953
  **Recommended Checklist Format**:
78904
78954
  - Use markdown checkboxes (\`- [ ] item\`) for major components and tasks
78955
+ - Create nested task breakdowns with 3-4 levels of detail
78905
78956
  - Each checkbox represents a distinct, trackable piece of work
78906
- - Each checkbox item should be specific and implementable independently
78907
- - Group related checkboxes under numbered sections or phases when appropriate
78957
+ - Each checkbox item must include detailed implementation guidance
78958
+ - Group related checkboxes under numbered sections or phases
78908
78959
  - Items will be implemented one at a time iteratively
78909
78960
  - After each implementation, the completed item will be marked with \`- [x]\` when the plan is updated
78910
78961
 
78911
- **Example checklist format**:
78962
+ **Implementation Details Requirements**:
78963
+
78964
+ Each task item must specify:
78965
+ - **Exact file paths** for new files or modifications (e.g., \`src/api/auth.ts\`)
78966
+ - **Function signatures and class names** to implement (e.g., \`async function authenticateUser(email: string, password: string)\`)
78967
+ - **Specific patterns from the codebase** to follow (e.g., "Follow the middleware pattern used in \`src/middleware/logger.ts\`")
78968
+ - **Required imports and dependencies** (e.g., "Import \`bcrypt\` for password hashing, use \`jsonwebtoken\` for JWT generation")
78969
+ - **Error handling approach** (e.g., "Use \`ApiError\` class from \`src/errors.ts\`")
78970
+ - **Integration points** with existing code (e.g., "Register middleware in \`src/app.ts\` before route handlers")
78971
+ - **Testing requirements** if applicable (e.g., "Add unit tests in \`src/api/__tests__/auth.test.ts\`")
78972
+
78973
+ **Example checklist format with proper detail**:
78912
78974
  \`\`\`
78913
78975
  1. Phase 1: Backend API Development
78914
- - [ ] Design and implement user authentication endpoints
78976
+ - [ ] Design and implement user authentication endpoints in \`src/api/auth.ts\`
78977
+ - [ ] Create \`POST /api/auth/login\` endpoint that accepts email/password in request body, validates using bcrypt, returns JWT token using \`jsonwebtoken\` library with 24h expiration
78978
+ - [ ] Create \`POST /api/auth/register\` endpoint that validates input with zod schema (\`email\`, \`password\` min 8 chars), hashes password with bcrypt (10 rounds), stores in database using Prisma ORM
78979
+ - [ ] Add authentication middleware in \`src/middleware/auth.ts\` that verifies JWT tokens from Authorization header and attaches user object to \`req.user\`
78915
78980
  - [ ] Create database schema and migrations
78916
- - [ ] Implement data validation middleware
78981
+ - [ ] Define User model in \`prisma/schema.prisma\` with fields: id (UUID), email (unique string), passwordHash (string), createdAt (DateTime), updatedAt (DateTime)
78982
+ - [ ] Generate migration with \`npx prisma migrate dev --name add-user-auth\`
78983
+ - [ ] Update \`src/db/client.ts\` to export Prisma client instance
78984
+ - [ ] Implement data validation middleware in \`src/middleware/validation.ts\`
78985
+ - [ ] Create \`validateRequest\` function that accepts zod schema and returns Express middleware
78986
+ - [ ] Add error handling that returns 400 status with validation errors in response body
78987
+ - [ ] Follow error format used in \`src/middleware/errorHandler.ts\`
78917
78988
 
78918
78989
  2. Phase 2: Frontend Integration
78919
- - [ ] Build authentication UI components
78920
- - [ ] Integrate with backend API
78990
+ - [ ] Build authentication UI components in \`src/components/auth/\`
78991
+ - [ ] Create \`LoginForm.tsx\` component with email/password fields using React Hook Form, submit handler calls \`/api/auth/login\`
78992
+ - [ ] Create \`RegisterForm.tsx\` component with email/password/confirmPassword fields, client-side validation matches backend rules
78993
+ - [ ] Add \`AuthContext.tsx\` using React Context API to manage auth state (user object, isAuthenticated boolean, login/logout functions)
78994
+ - [ ] Integrate with backend API using \`src/lib/api.ts\`
78995
+ - [ ] Add \`login(email, password)\` function that calls \`POST /api/auth/login\`, stores JWT in localStorage, returns user object
78996
+ - [ ] Add \`register(email, password)\` function that calls \`POST /api/auth/register\`
78997
+ - [ ] Add \`logout()\` function that removes JWT from localStorage and clears auth state
78921
78998
  - [ ] Add error handling and loading states
78999
+ - [ ] Display API errors in form using \`ErrorMessage\` component from \`src/components/ui/ErrorMessage.tsx\`
79000
+ - [ ] Show loading spinner during API calls using \`LoadingSpinner\` component from \`src/components/ui/LoadingSpinner.tsx\`
79001
+ - [ ] Add toast notifications for success/error using \`react-hot-toast\` library
78922
79002
  \`\`\`
78923
79003
 
78924
- **Alternative Format**:
78925
- You may also use numbered lists if checkboxes don't fit the task structure. However, each item should still be clear, actionable, and implementable independently.
78926
-
78927
79004
  **What to Include**:
78928
- - Actionable implementation steps
78929
- - Technical requirements and specifications
78930
- - Specific files, functions, or components to create/modify
78931
- - Context needed for implementation
79005
+ - Actionable implementation steps with complete technical specifications
79006
+ - Exact file paths, function names, and implementation patterns
79007
+ - All dependencies, imports, and integration points
79008
+ - Specific technical constraints and requirements
79009
+ - Testing approach if applicable
78932
79010
 
78933
79011
  **What NOT to Include**:
78934
79012
  - Future enhancements or scope outside the current task
@@ -78947,9 +79025,11 @@ Branch names should:
78947
79025
 
78948
79026
  1. Analyze the task and the existing plan (if any).
78949
79027
  2. If the requirements are clear and you can generate or update the plan:
78950
- a. Provide the plan in the "plan" field using the checklist format described above
78951
- b. Propose a suitable git branch name in the "branchName" field.
78952
- c. Include relevant file paths in the "files" array if applicable
79028
+ a. **Explore the codebase first** to understand patterns, conventions, and existing implementations
79029
+ b. Provide the plan in the "plan" field using the checklist format with detailed implementation guidance
79030
+ c. Ensure the plan is detailed enough that task creation does not require additional codebase exploration
79031
+ d. Each task item must include specific file paths, function names, implementation patterns, and technical details
79032
+ e. Propose a suitable git branch name in the "branchName" field
78953
79033
  3. If the requirements are not clear:
78954
79034
  a. Ask a clarifying question in the "question" field
78955
79035
  4. If the task is already implemented or no action is needed:
@@ -78996,21 +79076,90 @@ ${task}
78996
79076
  ${planSection}`;
78997
79077
  }
78998
79078
  var EPIC_ADD_TODO_ITEMS_SYSTEM_PROMPT = `Role: Task creation agent
78999
- Goal: Read a high-level plan and create corresponding todo items.
79079
+ Goal: Parse a detailed epic plan and create todo items from the provided task breakdowns.
79000
79080
 
79001
79081
  ${TOOL_USAGE_INSTRUCTION}
79002
79082
 
79003
- You are a task creation agent. Your responsibility is to read a high-level plan for an epic and create a todo item for each task in the plan.
79083
+ You are a task creation agent responsible for parsing a detailed epic plan and creating todo items that can be executed autonomously by an AI coding agent.
79004
79084
 
79005
- ## Your Task
79085
+ ## Your Responsibility
79086
+
79087
+ Your goal is to create todo items that are:
79088
+ - **Specific and actionable**: Each item should be clear enough for an AI agent to implement without human intervention
79089
+ - **Well-documented**: Include detailed implementation guidance extracted from the plan
79090
+ - **Context-rich**: Specify relevant files that will be modified or created (as provided in the plan)
79091
+ - **Self-contained**: Each item should be implementable independently with the context from the plan
79092
+
79093
+ ## Plan Structure Expectations
79094
+
79095
+ The plan you receive contains all necessary implementation details. You should NOT need to explore the codebase because:
79096
+ - Each task in the plan already includes specific file paths
79097
+ - Function and class names are specified in the plan
79098
+ - Implementation patterns and approaches are provided
79099
+ - Dependencies and imports are listed
79100
+ - Technical specifications are included
79101
+ - Integration points are documented
79102
+
79103
+ Your job is to extract these details from the plan and create todo items, not to research or discover them.
79104
+
79105
+ ## Todo Item Requirements
79106
+
79107
+ For each task in the plan, create a todo item using the \`updateTodoItem\` tool with:
79108
+
79109
+ ### 1. **title** (required)
79110
+ A concise, action-oriented task name that clearly states what needs to be done.
79111
+
79112
+ **Examples:**
79113
+ - ✅ "Implement user authentication with JWT tokens"
79114
+ - ✅ "Add validation middleware for API endpoints"
79115
+ - ❌ "Authentication" (too vague)
79116
+
79117
+ ### 2. **description** (required)
79118
+ Detailed implementation instructions extracted from the plan. Include:
79119
+
79120
+ **Must extract from the plan:**
79121
+ - **Specific files to create or modify** with exact paths (as specified in the plan)
79122
+ - **Function/class names** to implement or modify (as specified in the plan)
79123
+ - **Implementation patterns** to follow (as referenced in the plan)
79124
+ - **Technical requirements** and constraints (as documented in the plan)
79125
+ - **Dependencies** and imports needed (as listed in the plan)
79126
+ - **Integration points** with existing code (as described in the plan)
79127
+
79128
+ **Example of extracting from a detailed plan task:**
79006
79129
 
79007
- You will have access to the high-level plan stored in memory under the topic 'epic-plan'.
79008
- Read the plan and for each task item in the plan, use the 'updateTodoItem' tool to create a new todo item.
79130
+ Plan task:
79131
+ \`\`\`
79132
+ - [ ] Add authentication middleware in \`src/middleware/auth.ts\` that verifies JWT tokens from Authorization header and attaches user object to \`req.user\`
79133
+ \`\`\`
79134
+
79135
+ Todo item description:
79136
+ \`\`\`
79137
+ Create a new authentication middleware in \`src/middleware/auth.ts\`:
79138
+
79139
+ 1. Implement \`authenticateJWT\` function that:
79140
+ - Extracts JWT token from Authorization header
79141
+ - Verifies token (implementation details from plan)
79142
+ - Attaches user object to \`req.user\`
79143
+ - Returns 401 error for invalid/missing tokens
79144
+
79145
+ 2. Export the middleware for use in route definitions
79146
+ \`\`\`
79147
+
79148
+ **Note:** All implementation details should come from the plan. Do not add information not present in the plan.
79009
79149
 
79010
79150
  ## Process
79011
79151
 
79012
- 1. Parse the plan to identify individual tasks.
79013
- 2. For each task, call 'updateTodoItem' with the task description as the 'title'.
79152
+ 1. **Read and parse the plan** provided in the user message to identify individual tasks
79153
+ 2. **Parse the plan structure** to understand the task hierarchy and details
79154
+ 3. **For each task in the plan:**
79155
+ a. Extract the specific files mentioned in the task
79156
+ b. Extract implementation patterns specified in the plan
79157
+ c. Create a detailed description using the plan's guidance
79158
+ d. Identify all relevant files from the plan
79159
+ e. Call \`updateTodoItem\` with operation='add', title and description
79160
+
79161
+ **Important:** Do NOT explore the codebase. All necessary information is already in the plan.
79162
+
79014
79163
  `;
79015
79164
  var CODER_SYSTEM_PROMPT = `Role: AI developer.
79016
79165
  Goal: Implement the provided plan by writing and modifying code.
@@ -79690,10 +79839,10 @@ var ImplementOutputSchema = exports_external.object({
79690
79839
  });
79691
79840
  var codeWorkflow = async (input2, context) => {
79692
79841
  const { logger, step, tools: tools2 } = context;
79693
- const { task, files, mode = "interactive" } = input2;
79842
+ const { task, files, mode = "interactive", additionalTools, additionalInstructions } = input2;
79694
79843
  const summaries = [];
79695
79844
  logger.info(`
79696
- \uD83D\uDCCB Phase 1: Creating implementation plan...
79845
+ Phase 1: Creating implementation plan...
79697
79846
  `);
79698
79847
  const planResult = await step("plan", async () => {
79699
79848
  return await planWorkflow({ task, files, mode: mode === "interactive" ? "confirm" : "noninteractive" }, context);
@@ -79704,7 +79853,7 @@ var codeWorkflow = async (input2, context) => {
79704
79853
  }
79705
79854
  const { plan, files: planFiles } = planResult;
79706
79855
  logger.info(`
79707
- ⚙️ Phase 2: Implementing the plan...
79856
+ Phase 2: Implementing the plan...
79708
79857
  `);
79709
79858
  let implementPrompt = getImplementPrompt(plan);
79710
79859
  if (planFiles && planFiles.length > 0) {
@@ -79752,6 +79901,9 @@ ${fileContentString}`;
79752
79901
  if (mode === "interactive") {
79753
79902
  agentTools.push(askFollowupQuestion_default);
79754
79903
  }
79904
+ if (additionalTools) {
79905
+ agentTools.push(...additionalTools);
79906
+ }
79755
79907
  const res = await step("implement", async () => {
79756
79908
  const defaultContext = await getDefaultContext();
79757
79909
  const memoryContext = await tools2.getMemoryContext();
@@ -79768,8 +79920,11 @@ ${memoryContext}`;
79768
79920
  ${memoryContext}`
79769
79921
  });
79770
79922
  }
79923
+ const systemPrompt = additionalInstructions ? `${CODER_SYSTEM_PROMPT}
79924
+
79925
+ ${additionalInstructions}` : CODER_SYSTEM_PROMPT;
79771
79926
  return await agentWorkflow({
79772
- systemPrompt: CODER_SYSTEM_PROMPT,
79927
+ systemPrompt,
79773
79928
  userMessage: [{ role: "user", content: userContent }],
79774
79929
  tools: agentTools,
79775
79930
  outputSchema: ImplementOutputSchema
@@ -79779,13 +79934,13 @@ ${memoryContext}`
79779
79934
  const { summary, bailReason } = res.object;
79780
79935
  if (bailReason) {
79781
79936
  logger.error(`
79782
- Implementation failed: ${bailReason}
79937
+ Implementation failed: ${bailReason}
79783
79938
  `);
79784
79939
  return { success: false, reason: bailReason, summaries };
79785
79940
  }
79786
79941
  if (summary) {
79787
79942
  logger.info(`
79788
- Implementation complete!
79943
+ Implementation complete!
79789
79944
  `);
79790
79945
  summaries.push(summary);
79791
79946
  logger.info(`Summary: ${summary}`);
@@ -79798,20 +79953,20 @@ ${memoryContext}`
79798
79953
  });
79799
79954
  } else {
79800
79955
  logger.info(`
79801
- Implementation complete!
79956
+ Implementation complete!
79802
79957
  `);
79803
79958
  }
79804
79959
  } else if (res.type === "Exit" /* Exit */) {
79805
79960
  logger.info(`
79806
- Implementation complete!
79961
+ Implementation complete!
79807
79962
  `);
79808
79963
  } else {
79809
79964
  logger.warn(`
79810
- ⚠️ Implementation failed. Please check the output for errors.
79965
+ Warning: Implementation failed. Please check the output for errors.
79811
79966
  `, res);
79812
79967
  }
79813
79968
  logger.info(`
79814
- \uD83D\uDD27 Phase 3: Checking for errors...
79969
+ Phase 3: Checking for errors...
79815
79970
  `);
79816
79971
  const fixResult = await step("fix", async () => {
79817
79972
  return await fixWorkflow({ interactive: false, task: input2.task }, context);
@@ -80221,8 +80376,67 @@ ${provider3.toUpperCase()}_API_KEY=${providerConfig.apiKey}`;
80221
80376
  });
80222
80377
  return { configPath };
80223
80378
  };
80379
+ // src/workflows/epic-context.ts
80380
+ import { promises as fs3 } from "node:fs";
80381
+ var EPIC_CONTEXT_FILE = ".epic.yml";
80382
+ var EpicContextSchema = exports_external.object({
80383
+ task: exports_external.string().nullish(),
80384
+ plan: exports_external.string().nullish(),
80385
+ branchName: exports_external.string().nullish(),
80386
+ todos: exports_external.array(TodoItemSchema).nullish(),
80387
+ memory: exports_external.record(exports_external.string(), exports_external.string()).nullish()
80388
+ });
80389
+ var saveEpicContext = async (context) => {
80390
+ const yamlString = $stringify(context);
80391
+ await fs3.writeFile(EPIC_CONTEXT_FILE, yamlString, "utf-8");
80392
+ };
80393
+ var loadEpicContext = async () => {
80394
+ let fileContent;
80395
+ try {
80396
+ fileContent = await fs3.readFile(EPIC_CONTEXT_FILE, "utf-8");
80397
+ } catch {
80398
+ return {};
80399
+ }
80400
+ try {
80401
+ const loaded = $parse(fileContent);
80402
+ return EpicContextSchema.parse(loaded);
80403
+ } catch (error46) {
80404
+ console.error("Error parsing epic context file:", EPIC_CONTEXT_FILE, error46);
80405
+ return {};
80406
+ }
80407
+ };
80408
+
80409
+ class EpicMemoryStore {
80410
+ #context;
80411
+ constructor(context) {
80412
+ this.#context = context;
80413
+ }
80414
+ async read() {
80415
+ return this.#context.memory ?? {};
80416
+ }
80417
+ async write(data) {
80418
+ this.#context.memory = data;
80419
+ await saveEpicContext(this.#context);
80420
+ }
80421
+ }
80422
+
80423
+ class EpicTodoItemStore {
80424
+ #context;
80425
+ constructor(context) {
80426
+ this.#context = context;
80427
+ }
80428
+ async read() {
80429
+ return this.#context.todos ?? [];
80430
+ }
80431
+ async write(data) {
80432
+ this.#context.todos = data;
80433
+ await saveEpicContext(this.#context);
80434
+ }
80435
+ }
80436
+
80224
80437
  // src/workflows/epic.workflow.ts
80225
80438
  var MAX_REVIEW_RETRIES = 5;
80439
+ var TODO_HANDLING_INSTRUCTIONS = `If you discover that a task is larger than you thought, or that a new task is required, you can add a // TODO comment in the code and create a todo item for it. This will allow you to continue with the current task and address the larger issue later.`;
80226
80440
  async function createPlan2(input2, context) {
80227
80441
  const { task, plan, files, feedback } = input2;
80228
80442
  const content = [{ type: "text", text: getPlanPrompt(task, plan) }];
@@ -80274,7 +80488,7 @@ ${feedback}`
80274
80488
  }
80275
80489
  async function createAndApprovePlan(task, context) {
80276
80490
  const { logger, step, tools: tools2 } = context;
80277
- logger.info(`\uD83D\uDCDD Phase 2: Creating high-level plan...
80491
+ logger.info(`Phase 2: Creating high-level plan...
80278
80492
  `);
80279
80493
  let feedback;
80280
80494
  let highLevelPlan;
@@ -80300,10 +80514,10 @@ async function createAndApprovePlan(task, context) {
80300
80514
  }
80301
80515
  return null;
80302
80516
  }
80303
- logger.info(`\uD83D\uDCDD Plan:
80517
+ logger.info(`Plan:
80304
80518
  ${result.plan}`);
80305
80519
  if (result.branchName) {
80306
- logger.info(`\uD83C\uDF3F Suggested branch name: ${result.branchName}`);
80520
+ logger.info(`Suggested branch name: ${result.branchName}`);
80307
80521
  }
80308
80522
  feedback = await tools2.input({ message: "Press Enter to approve the plan, or provide feedback to refine it." });
80309
80523
  if (feedback.trim() === "") {
@@ -80324,48 +80538,46 @@ ${result.plan}`);
80324
80538
  return null;
80325
80539
  }
80326
80540
  if (!branchName) {
80327
- logger.error("Error: No branch name was generated from the plan. Exiting.");
80541
+ logger.error("Error: No branch name was generated from the plan. Exiting.");
80328
80542
  return null;
80329
80543
  }
80330
- logger.info(`✅ High-level plan approved.
80544
+ logger.info(`High-level plan approved.
80331
80545
  `);
80332
80546
  return { plan: highLevelPlan, branchName };
80333
80547
  }
80334
80548
  async function createFeatureBranch(branchName, context) {
80335
80549
  const { logger, step, tools: tools2 } = context;
80336
- logger.info(`\uD83C\uDF3F Phase 3: Creating feature branch...
80550
+ logger.info(`Phase 3: Creating/switching to feature branch...
80337
80551
  `);
80338
- let finalBranchName = branchName;
80339
- const initialCheckResult = await step("checkBranch-initial", async () => await tools2.executeCommand({ command: "git", args: ["rev-parse", "--verify", finalBranchName] }));
80340
- if (initialCheckResult.exitCode === 0) {
80341
- logger.warn(`⚠️ Branch '${finalBranchName}' already exists. Trying to find an available name...`);
80342
- const suffixMatch = branchName.match(/-(\d+)$/);
80343
- let baseName = branchName;
80344
- let counter = 2;
80345
- if (suffixMatch) {
80346
- baseName = branchName.substring(0, suffixMatch.index);
80347
- counter = parseInt(suffixMatch[1], 10) + 1;
80348
- }
80349
- while (true) {
80350
- finalBranchName = `${baseName}-${counter}`;
80351
- const branchCheckResult = await step(`checkBranch-${counter}`, async () => await tools2.executeCommand({ command: "git", args: ["rev-parse", "--verify", finalBranchName] }));
80352
- if (branchCheckResult.exitCode !== 0) {
80353
- break;
80552
+ const branchExistsResult = await step("checkBranchExists", async () => await tools2.executeCommand({ command: "git", args: ["rev-parse", "--verify", branchName] }));
80553
+ if (branchExistsResult.exitCode === 0) {
80554
+ const currentBranchResult = await step("getCurrentBranch", async () => tools2.executeCommand({ command: "git", args: ["rev-parse", "--abbrev-ref", "HEAD"] }));
80555
+ const currentBranch = currentBranchResult.stdout.trim();
80556
+ if (currentBranch !== branchName) {
80557
+ logger.info(`Branch '${branchName}' already exists. Switching to it...`);
80558
+ const checkoutResult = await step("checkoutBranch", async () => await tools2.executeCommand({ command: "git", args: ["checkout", branchName] }));
80559
+ if (checkoutResult.exitCode !== 0) {
80560
+ logger.error(`Error: Failed to switch to branch '${branchName}'. Git command failed.`);
80561
+ return { success: false, branchName: null };
80354
80562
  }
80355
- counter++;
80563
+ } else {
80564
+ logger.info(`Already on branch '${branchName}'.`);
80565
+ }
80566
+ } else {
80567
+ logger.info(`Creating new branch '${branchName}'...`);
80568
+ const createBranchResult = await step("createBranch", async () => await tools2.executeCommand({ command: "git", args: ["checkout", "-b", branchName] }));
80569
+ if (createBranchResult.exitCode !== 0) {
80570
+ logger.error(`Error: Failed to create branch '${branchName}'. Git command failed.`);
80571
+ return { success: false, branchName: null };
80356
80572
  }
80357
80573
  }
80358
- if (finalBranchName !== branchName) {
80359
- logger.info(`Branch name '${branchName}' was taken. Using '${finalBranchName}' instead.`);
80360
- }
80361
- await step("createBranch", async () => await tools2.executeCommand({ command: "git", args: ["checkout", "-b", finalBranchName] }));
80362
- logger.info(`✅ Branch '${finalBranchName}' created.
80574
+ logger.info(`Successfully on branch '${branchName}'.
80363
80575
  `);
80364
- return { success: true, branchName: finalBranchName };
80576
+ return { success: true, branchName };
80365
80577
  }
80366
80578
  async function addTodoItemsFromPlan(plan, context) {
80367
80579
  const { logger, step, tools: tools2 } = context;
80368
- logger.info(`\uD83D\uDCDD Phase 4: Creating todo items from plan...
80580
+ logger.info(`Phase 4: Creating todo items from plan...
80369
80581
  `);
80370
80582
  await step("add-todo-items", async () => {
80371
80583
  await agentWorkflow({
@@ -80377,28 +80589,40 @@ ${plan}</plan>` }],
80377
80589
  }, context);
80378
80590
  });
80379
80591
  const todos = await tools2.listTodoItems({});
80380
- logger.info(`✅ Created ${todos.length} todo items.
80592
+ logger.info(`Created ${todos.length} todo items.
80381
80593
  `);
80382
80594
  }
80383
- async function runPreflightChecks(context) {
80595
+ async function runPreflightChecks(epicContext, context) {
80384
80596
  const { logger, step, tools: tools2 } = context;
80385
- logger.info(`\uD83D\uDCCB Phase 1: Running pre-flight checks...
80597
+ logger.info(`Phase 1: Running pre-flight checks...
80386
80598
  `);
80387
80599
  const gitCheckResult = await step("gitCheck", async () => tools2.executeCommand({ command: "git", args: ["rev-parse", "--git-dir"] }));
80388
80600
  if (gitCheckResult.exitCode !== 0) {
80389
- logger.error("Error: Git is not initialized in this directory. Please run `git init` first.");
80390
- logger.info("\uD83D\uDCA1 Suggestion: Run `git init` to initialize a git repository.\n");
80391
- return { success: false };
80601
+ logger.error("Error: Git is not initialized in this directory. Please run `git init` first.");
80602
+ logger.info("Suggestion: Run `git init` to initialize a git repository.\n");
80603
+ return false;
80604
+ }
80605
+ if (epicContext.plan) {
80606
+ logger.info("Found an existing `.epic.yml` file.");
80607
+ if (epicContext.branchName) {
80608
+ const currentBranchResult = await step("getCurrentBranch", async () => tools2.executeCommand({ command: "git", args: ["rev-parse", "--abbrev-ref", "HEAD"] }));
80609
+ const currentBranch = currentBranchResult.stdout.trim();
80610
+ if (currentBranch !== epicContext.branchName) {
80611
+ throw new Error(`You are on branch '${currentBranch}' but the epic was started on branch '${epicContext.branchName}'. Please switch to the correct branch to resume.`);
80612
+ }
80613
+ }
80614
+ logger.info("Resuming previous epic session.");
80615
+ return true;
80392
80616
  }
80393
80617
  const gitStatusResult = await step("gitStatus", async () => tools2.executeCommand({ command: "git status --porcelain", shell: true }));
80394
80618
  if (gitStatusResult.stdout.trim() !== "") {
80395
- logger.error("Error: Your working directory is not clean. Please stash or commit your changes before running the epic workflow.");
80396
- logger.info("\uD83D\uDCA1 Suggestion: Run `git add .` and `git commit` to clean your working directory, or `git stash` to temporarily save changes.\n");
80397
- return { success: false };
80619
+ logger.error("Error: Your working directory is not clean. Please stash or commit your changes before running the epic workflow.");
80620
+ logger.info("Suggestion: Run `git add .` and `git commit` to clean your working directory, or `git stash` to temporarily save changes.\n");
80621
+ return false;
80398
80622
  }
80399
- logger.info(`✅ Pre-flight checks passed.
80623
+ logger.info(`Pre-flight checks passed.
80400
80624
  `);
80401
- return { success: true };
80625
+ return true;
80402
80626
  }
80403
80627
  async function performReviewAndFixCycle(iterationCount, taskItem, highLevelPlan, context) {
80404
80628
  const { logger, step, tools: tools2 } = context;
@@ -80406,12 +80630,12 @@ async function performReviewAndFixCycle(iterationCount, taskItem, highLevelPlan,
80406
80630
  const diffResult = await tools2.executeCommand({ command: "git", args: ["diff", "--name-status", "HEAD~1", "HEAD"] });
80407
80631
  const changedFiles = parseGitDiffNameStatus(diffResult.stdout);
80408
80632
  if (changedFiles.length === 0) {
80409
- logger.info(`ℹ️ No files were changed. Skipping review.
80633
+ logger.info(`No files were changed. Skipping review.
80410
80634
  `);
80411
80635
  return { passed: true };
80412
80636
  }
80413
80637
  logger.info(`
80414
- \uD83D\uDD0E Review iteration ${i + 1}/${MAX_REVIEW_RETRIES}`);
80638
+ Review iteration ${i + 1}/${MAX_REVIEW_RETRIES}`);
80415
80639
  logger.info(` Changed files: ${changedFiles.length}`);
80416
80640
  const changeInfo = {
80417
80641
  commitRange: "HEAD~1...HEAD",
@@ -80432,16 +80656,16 @@ ${formatReviewToolInput(changeInfo)}`;
80432
80656
  }, context);
80433
80657
  });
80434
80658
  if (reviewAgentResult.type !== "Exit" /* Exit */) {
80435
- logger.error(`\uD83D\uDEAB Review agent failed with status: ${reviewAgentResult.type}.`);
80659
+ logger.error(`Review agent failed with status: ${reviewAgentResult.type}.`);
80436
80660
  break;
80437
80661
  }
80438
80662
  const reviewResult = reviewAgentResult.object;
80439
80663
  if (!reviewResult || !reviewResult.specificReviews || reviewResult.specificReviews.length === 0) {
80440
- logger.info(`✅ Review passed. No issues found.
80664
+ logger.info(`Review passed. No issues found.
80441
80665
  `);
80442
80666
  return { passed: true };
80443
80667
  }
80444
- logger.warn(`⚠️ Review found ${reviewResult.specificReviews.length} issue(s). Attempting to fix...
80668
+ logger.warn(`Warning: Review found ${reviewResult.specificReviews.length} issue(s). Attempting to fix...
80445
80669
  `);
80446
80670
  for (const [idx, review] of reviewResult.specificReviews.entries()) {
80447
80671
  logger.warn(` ${idx + 1}. ${review.file}:${review.lines}`);
@@ -80462,7 +80686,12 @@ After an initial implementation, a review found the following issues. Please fix
80462
80686
 
80463
80687
  ${reviewSummary}`;
80464
80688
  await step(`fix-${iterationCount}-${i}`, async () => {
80465
- await codeWorkflow({ task: fixTask, mode: "noninteractive" }, context);
80689
+ await codeWorkflow({
80690
+ task: fixTask,
80691
+ mode: "noninteractive",
80692
+ additionalInstructions: TODO_HANDLING_INSTRUCTIONS,
80693
+ additionalTools: [getTodoItem_default, listTodoItems_default, updateTodoItem_default]
80694
+ }, context);
80466
80695
  });
80467
80696
  await step(`commit-fix-${iterationCount}-${i}`, async () => {
80468
80697
  await tools2.executeCommand({ command: "git", args: ["add", "."] });
@@ -80470,7 +80699,7 @@ ${reviewSummary}`;
80470
80699
  });
80471
80700
  if (i === MAX_REVIEW_RETRIES - 1) {
80472
80701
  logger.error(`
80473
- \uD83D\uDEAB Max retries (${MAX_REVIEW_RETRIES}) reached. Moving to the next task, but issues might remain.
80702
+ Max retries (${MAX_REVIEW_RETRIES}) reached. Moving to the next task, but issues might remain.
80474
80703
  `);
80475
80704
  return { passed: false };
80476
80705
  }
@@ -80480,7 +80709,7 @@ ${reviewSummary}`;
80480
80709
  async function runImplementationLoop(context, highLevelPlan) {
80481
80710
  const { logger, step, tools: tools2 } = context;
80482
80711
  const commitMessages = [];
80483
- logger.info(`\uD83D\uDE80 Phase 5: Iterative Implementation Loop...
80712
+ logger.info(`Phase 5: Iterative Implementation Loop...
80484
80713
  `);
80485
80714
  logger.info(`${"=".repeat(80)}
80486
80715
  `);
@@ -80502,9 +80731,9 @@ async function runImplementationLoop(context, highLevelPlan) {
80502
80731
  iterationCount++;
80503
80732
  const taskStartTime = Date.now();
80504
80733
  logger.info(`
80505
- ${"".repeat(80)}`);
80506
- logger.info(`\uD83D\uDCCC Iteration ${iterationCount}`);
80507
- logger.info(`${"".repeat(80)}`);
80734
+ ${"-".repeat(80)}`);
80735
+ logger.info(`Iteration ${iterationCount}`);
80736
+ logger.info(`${"-".repeat(80)}`);
80508
80737
  logger.info(`${nextTask}
80509
80738
  `);
80510
80739
  await step(`task-${iterationCount}`, async () => {
@@ -80518,7 +80747,12 @@ Your current task is to implement this specific item:
80518
80747
  ${nextTask}
80519
80748
 
80520
80749
  Focus only on this item, but use the plan for context.`;
80521
- return await codeWorkflow({ task: taskWithContext, mode: "noninteractive" }, context);
80750
+ return await codeWorkflow({
80751
+ task: taskWithContext,
80752
+ mode: "noninteractive",
80753
+ additionalInstructions: TODO_HANDLING_INSTRUCTIONS,
80754
+ additionalTools: [getTodoItem_default, listTodoItems_default, updateTodoItem_default]
80755
+ }, context);
80522
80756
  });
80523
80757
  const commitMessage = `feat: ${nextTask}`;
80524
80758
  await step(`commit-initial-${iterationCount}`, async () => {
@@ -80530,9 +80764,9 @@ Focus only on this item, but use the plan for context.`;
80530
80764
  const taskElapsed = Date.now() - taskStartTime;
80531
80765
  const taskElapsedTime = formatElapsedTime(taskElapsed);
80532
80766
  if (reviewPassed) {
80533
- logger.info(`✅ Iteration ${iterationCount} completed successfully (${taskElapsedTime})`);
80767
+ logger.info(`Iteration ${iterationCount} completed successfully (${taskElapsedTime})`);
80534
80768
  } else {
80535
- logger.warn(`⚠️ Iteration ${iterationCount} completed with potential issues (${taskElapsedTime})`);
80769
+ logger.warn(`Warning: Iteration ${iterationCount} completed with potential issues (${taskElapsedTime})`);
80536
80770
  }
80537
80771
  await step(`update-task-status-${iterationCount}`, async () => {
80538
80772
  if (!nextTaskId) {
@@ -80563,17 +80797,17 @@ Focus only on this item, but use the plan for context.`;
80563
80797
  progressMessage = `Iteration ${iterationCount} completed`;
80564
80798
  }
80565
80799
  logger.info(`
80566
- \uD83D\uDCCA Progress: ${progressMessage}`);
80800
+ Progress: ${progressMessage}`);
80567
80801
  if (isComplete) {
80568
- logger.info(`✅ All tasks complete!
80802
+ logger.info(`All tasks complete!
80569
80803
  `);
80570
80804
  break;
80571
80805
  }
80572
80806
  if (nextTask) {
80573
- logger.info(`\uD83D\uDCCC Next task: ${nextTask}
80807
+ logger.info(`Next task: ${nextTask}
80574
80808
  `);
80575
80809
  }
80576
- logger.info(`${"".repeat(80)}
80810
+ logger.info(`${"-".repeat(80)}
80577
80811
  `);
80578
80812
  }
80579
80813
  return commitMessages;
@@ -80585,7 +80819,7 @@ Phase 6: Final Review and Fixup...
80585
80819
  `);
80586
80820
  const ghCheckResult = await tools2.executeCommand({ command: "gh", args: ["--version"] });
80587
80821
  if (ghCheckResult.exitCode !== 0) {
80588
- logger.warn("⚠️ GitHub CLI (gh) is not installed. Skipping final review step. Please install it from https://cli.github.com/ to enable final reviews.");
80822
+ logger.warn("Warning: GitHub CLI (gh) is not installed. Skipping final review step. Please install it from https://cli.github.com/ to enable final reviews.");
80589
80823
  return { passed: true };
80590
80824
  }
80591
80825
  const defaultBranchResult = await tools2.executeCommand({
@@ -80596,7 +80830,7 @@ Phase 6: Final Review and Fixup...
80596
80830
  const currentBranchResult = await tools2.executeCommand({ command: "git", args: ["rev-parse", "--abbrev-ref", "HEAD"] });
80597
80831
  const currentBranch = currentBranchResult.stdout.trim();
80598
80832
  if (currentBranch === defaultBranch) {
80599
- logger.info(`✅ You are on the default branch ('${defaultBranch}'). No final review needed.`);
80833
+ logger.info(`You are on the default branch ('${defaultBranch}'). No final review needed.`);
80600
80834
  return { passed: true };
80601
80835
  }
80602
80836
  const commitRange = `${defaultBranch}...${currentBranch}`;
@@ -80604,12 +80838,12 @@ Phase 6: Final Review and Fixup...
80604
80838
  const diffResult = await tools2.executeCommand({ command: "git", args: ["diff", "--name-status", commitRange] });
80605
80839
  const changedFiles = parseGitDiffNameStatus(diffResult.stdout);
80606
80840
  if (changedFiles.length === 0) {
80607
- logger.info(`ℹ️ No files have been changed in this branch. Skipping final review.
80841
+ logger.info(`No files have been changed in this branch. Skipping final review.
80608
80842
  `);
80609
80843
  return { passed: true };
80610
80844
  }
80611
80845
  logger.info(`
80612
- \uD83D\uDD0E Final review iteration ${i + 1}/${MAX_REVIEW_RETRIES}`);
80846
+ Final review iteration ${i + 1}/${MAX_REVIEW_RETRIES}`);
80613
80847
  logger.info(` Changed files: ${changedFiles.length}`);
80614
80848
  const changeInfo = {
80615
80849
  commitRange,
@@ -80630,16 +80864,16 @@ ${formatReviewToolInput(changeInfo)}`;
80630
80864
  }, context);
80631
80865
  });
80632
80866
  if (reviewAgentResult.type !== "Exit" /* Exit */) {
80633
- logger.error(`\uD83D\uDEAB Review agent failed with status: ${reviewAgentResult.type}.`);
80867
+ logger.error(`Review agent failed with status: ${reviewAgentResult.type}.`);
80634
80868
  break;
80635
80869
  }
80636
80870
  const reviewResult = reviewAgentResult.object;
80637
80871
  if (!reviewResult || !reviewResult.specificReviews || reviewResult.specificReviews.length === 0) {
80638
- logger.info(`✅ Final review passed. No issues found.
80872
+ logger.info(`Final review passed. No issues found.
80639
80873
  `);
80640
80874
  return { passed: true };
80641
80875
  }
80642
- logger.warn(`⚠️ Final review found ${reviewResult.specificReviews.length} issue(s). Attempting to fix...
80876
+ logger.warn(`Warning: Final review found ${reviewResult.specificReviews.length} issue(s). Attempting to fix...
80643
80877
  `);
80644
80878
  for (const [idx, review] of reviewResult.specificReviews.entries()) {
80645
80879
  logger.warn(` ${idx + 1}. ${review.file}:${review.lines}`);
@@ -80659,7 +80893,12 @@ A final review of all the changes in the branch found the following issues. Plea
80659
80893
 
80660
80894
  ${reviewSummary}`;
80661
80895
  await step(`final-fix-${i}`, async () => {
80662
- await codeWorkflow({ task: fixTask, mode: "noninteractive" }, context);
80896
+ await codeWorkflow({
80897
+ task: fixTask,
80898
+ mode: "noninteractive",
80899
+ additionalInstructions: TODO_HANDLING_INSTRUCTIONS,
80900
+ additionalTools: [getTodoItem_default, listTodoItems_default, updateTodoItem_default]
80901
+ }, context);
80663
80902
  });
80664
80903
  await step(`commit-final-fix-${i}`, async () => {
80665
80904
  await tools2.executeCommand({ command: "git", args: ["add", "."] });
@@ -80667,7 +80906,7 @@ ${reviewSummary}`;
80667
80906
  });
80668
80907
  if (i === MAX_REVIEW_RETRIES - 1) {
80669
80908
  logger.error(`
80670
- \uD83D\uDEAB Max retries (${MAX_REVIEW_RETRIES}) reached for final review. Issues might remain.
80909
+ Max retries (${MAX_REVIEW_RETRIES}) reached for final review. Issues might remain.
80671
80910
  `);
80672
80911
  return { passed: false };
80673
80912
  }
@@ -80675,57 +80914,85 @@ ${reviewSummary}`;
80675
80914
  return { passed: false };
80676
80915
  }
80677
80916
  var epicWorkflow = async (input2, context) => {
80678
- const { logger } = context;
80679
- const { task } = input2;
80917
+ const { logger, tools: tools2 } = context;
80918
+ const { task, epicContext = {} } = input2;
80680
80919
  const workflowStartTime = Date.now();
80681
- let branchName = "";
80682
80920
  if (!task || task.trim() === "") {
80683
- logger.error("Error: Task cannot be empty. Please provide a valid task description.");
80921
+ logger.error("Error: Task cannot be empty. Please provide a valid task description.");
80684
80922
  return;
80685
80923
  }
80924
+ if (!epicContext.task) {
80925
+ epicContext.task = task;
80926
+ }
80686
80927
  try {
80687
- const preflightResult = await runPreflightChecks(context);
80688
- if (!preflightResult.success) {
80928
+ const preflightResult = await runPreflightChecks(epicContext, context);
80929
+ if (!preflightResult) {
80930
+ return;
80931
+ }
80932
+ if (!epicContext.plan) {
80933
+ if (!epicContext.task) {
80934
+ logger.error("Error: Task is missing in epic context. Exiting.");
80935
+ return;
80936
+ }
80937
+ const planResult = await createAndApprovePlan(epicContext.task, context);
80938
+ if (!planResult)
80939
+ return;
80940
+ epicContext.plan = planResult.plan;
80941
+ epicContext.branchName = planResult.branchName;
80942
+ await saveEpicContext(epicContext);
80943
+ }
80944
+ if (!epicContext.plan) {
80945
+ logger.error("Error: Plan is missing after planning phase. Exiting.");
80689
80946
  return;
80690
80947
  }
80691
- const planResult = await createAndApprovePlan(task, context);
80692
- if (!planResult) {
80948
+ if (!epicContext.branchName) {
80949
+ logger.error("Error: Branch name is missing after planning phase. Exiting.");
80693
80950
  return;
80694
80951
  }
80695
- const { plan: highLevelPlan } = planResult;
80696
- branchName = planResult.branchName;
80697
- const branchResult = await createFeatureBranch(branchName, context);
80698
- if (!branchResult.success || !branchResult.branchName) {
80952
+ const branchResult = await createFeatureBranch(epicContext.branchName, context);
80953
+ if (!branchResult.success || !branchResult.branchName)
80699
80954
  return;
80955
+ if (epicContext.branchName !== branchResult.branchName) {
80956
+ epicContext.branchName = branchResult.branchName;
80957
+ await saveEpicContext(epicContext);
80958
+ }
80959
+ const todos = await tools2.listTodoItems({});
80960
+ if (todos.length === 0) {
80961
+ await addTodoItemsFromPlan(epicContext.plan, context);
80962
+ }
80963
+ const commitMessages = await runImplementationLoop(context, epicContext.plan);
80964
+ await performFinalReviewAndFix(context, epicContext.plan);
80965
+ await tools2.executeCommand({ command: "git", args: ["rm", "-f", ".epic.yml"] });
80966
+ const statusResult = await tools2.executeCommand({
80967
+ command: "git",
80968
+ args: ["status", "--porcelain", "--", ".epic.yml"]
80969
+ });
80970
+ if (statusResult.stdout.trim() !== "") {
80971
+ await tools2.executeCommand({ command: "git", args: ["commit", "-m", "chore: remove .epic.yml", "--", ".epic.yml"] });
80972
+ logger.info("Cleaned up .epic.yml file.");
80700
80973
  }
80701
- branchName = branchResult.branchName;
80702
- await addTodoItemsFromPlan(highLevelPlan, context);
80703
- const commitMessages = await runImplementationLoop(context, highLevelPlan);
80704
- await performFinalReviewAndFix(context, highLevelPlan);
80705
80974
  const totalElapsed = Date.now() - workflowStartTime;
80706
80975
  const totalElapsedTime = formatElapsedTime(totalElapsed);
80707
- const iterationCount = commitMessages.length;
80708
80976
  logger.info(`
80709
80977
  ${"=".repeat(80)}`);
80710
- logger.info("\uD83C\uDF89 Epic Workflow Complete!");
80978
+ logger.info("Epic Workflow Complete!");
80711
80979
  logger.info(`${"=".repeat(80)}`);
80712
80980
  logger.info(`
80713
- \uD83D\uDCCA Summary:`);
80714
- logger.info(` Total iterations: ${iterationCount}`);
80715
- logger.info(` Total commits: ${commitMessages.length}`);
80716
- logger.info(` Branch: ${branchName}`);
80981
+ Summary:`);
80982
+ logger.info(` Branch: ${epicContext.branchName}`);
80717
80983
  logger.info(` Total time: ${totalElapsedTime}`);
80718
- logger.info("\uD83D\uDCDD Commits created:");
80984
+ logger.info(` Total commits: ${commitMessages.length}`);
80985
+ logger.info("Commits created:");
80719
80986
  for (const [idx, msg] of commitMessages.entries()) {
80720
80987
  logger.info(` ${idx + 1}. ${msg}`);
80721
80988
  }
80722
80989
  } catch (error46) {
80723
80990
  logger.error(`
80724
- Epic workflow failed: ${error46 instanceof Error ? error46.message : String(error46)}`);
80725
- if (branchName) {
80991
+ Epic workflow failed: ${error46 instanceof Error ? error46.message : String(error46)}`);
80992
+ if (epicContext?.branchName) {
80726
80993
  logger.info(`
80727
- Branch '${branchName}' was created but work is incomplete.`);
80728
- logger.info(`To cleanup: git checkout <previous-branch> && git branch -D ${branchName}
80994
+ Branch '${epicContext.branchName}' was created but work is incomplete.`);
80995
+ logger.info(`To cleanup: git checkout <previous-branch> && git branch -D ${epicContext.branchName}
80729
80996
  `);
80730
80997
  }
80731
80998
  throw error46;
@@ -80740,7 +81007,7 @@ var taskWorkflow = async (input2, context) => {
80740
81007
  const { logger, step } = context;
80741
81008
  const { task } = input2;
80742
81009
  logger.info(`
80743
- \uD83E\uDD16 Running generic agent...
81010
+ Running generic agent...
80744
81011
  `);
80745
81012
  await step("agent", async () => {
80746
81013
  const defaultContext = await getDefaultContext();
@@ -80765,7 +81032,7 @@ ${defaultContext}`;
80765
81032
  }, context);
80766
81033
  });
80767
81034
  logger.info(`
80768
- Agent finished!
81035
+ Agent finished!
80769
81036
  `);
80770
81037
  return { success: true };
80771
81038
  };
@@ -80778,7 +81045,7 @@ var metaWorkflow = async (input2, context) => {
80778
81045
  const { task } = input2;
80779
81046
  const { logger } = context;
80780
81047
  logger.info(`
80781
- \uD83E\uDD14 Deciding which workflow to use for task...
81048
+ Deciding which workflow to use for task...
80782
81049
  `);
80783
81050
  const result = await agentWorkflow({
80784
81051
  systemPrompt: META_SYSTEM_PROMPT,
@@ -80799,7 +81066,7 @@ var metaWorkflow = async (input2, context) => {
80799
81066
  throw new Error("Could not decide which workflow to run.");
80800
81067
  }
80801
81068
  logger.info(`
80802
- Decision: Using '${decision.workflow}' workflow.`);
81069
+ Decision: Using '${decision.workflow}' workflow.`);
80803
81070
  switch (decision.workflow) {
80804
81071
  case "code":
80805
81072
  await codeWorkflow({ task }, context);
@@ -81019,30 +81286,32 @@ var commitCommand = new Command("commit").description("Create a commit with AI-g
81019
81286
  async function runEpic(task2, _options, command) {
81020
81287
  let taskInput = task2;
81021
81288
  if (!taskInput) {
81022
- try {
81023
- taskInput = await esm_default3({
81024
- message: "What epic or large feature do you want to implement?"
81025
- });
81026
- } catch (error46) {
81027
- if (error46 instanceof Error && error46.name === "ExitPromptError") {
81028
- return;
81029
- }
81030
- throw error46;
81289
+ taskInput = await getUserInput("What epic or large feature do you want to implement?");
81290
+ if (!taskInput) {
81291
+ return;
81031
81292
  }
81032
81293
  }
81033
81294
  if (!taskInput) {
81034
81295
  console.error("No task provided. Aborting.");
81035
81296
  return;
81036
81297
  }
81298
+ const ctx = await loadEpicContext();
81037
81299
  const workflowInput = {
81038
- task: taskInput
81300
+ task: taskInput,
81301
+ epicContext: ctx
81039
81302
  };
81040
81303
  const globalOpts = (command.parent ?? command).opts();
81041
81304
  const { verbose, yes } = globalOpts;
81042
81305
  const logger = createLogger({
81043
81306
  verbose
81044
81307
  });
81045
- await runWorkflow(epicWorkflow, workflowInput, { commandName: "epic", command, logger, yes });
81308
+ await runWorkflow(epicWorkflow, workflowInput, {
81309
+ commandName: "epic",
81310
+ command,
81311
+ logger,
81312
+ yes,
81313
+ getProvider: (opt) => getProvider({ ...opt, todoItemStore: new EpicTodoItemStore(ctx), memoryStore: new EpicMemoryStore(ctx) })
81314
+ });
81046
81315
  }
81047
81316
  var epicCommand = new Command("epic").description("Orchestrates a large feature or epic, breaking it down into smaller tasks.").argument("[task]", "The epic to plan and implement.").action(runEpic);
81048
81317
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@polka-codes/cli",
3
- "version": "0.9.49",
3
+ "version": "0.9.50",
4
4
  "license": "AGPL-3.0",
5
5
  "author": "github@polka.codes",
6
6
  "type": "module",