@polka-codes/cli 0.9.49 → 0.9.51

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 +572 -243
  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.51";
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:
@@ -78958,27 +79038,78 @@ Branch names should:
78958
79038
 
78959
79039
  ## Response Format
78960
79040
 
78961
- ${createJsonResponseInstruction({
78962
- plan: "The generated or updated plan.",
78963
- branchName: "feat/new-feature-name",
78964
- question: {
78965
- question: "The clarifying question to ask the user.",
78966
- defaultAnswer: "The default answer to provide if the user does not provide an answer."
78967
- },
78968
- reason: "If no plan is needed, provide a reason here."
78969
- })}
79041
+ Respond with a JSON object in a markdown code block. The JSON object should have a "type" field that determines the structure of the rest of the object.
79042
+
79043
+ ### 1. When you generate a plan (type: 'plan-generated')
79044
+ - **type**: Must be "plan-generated".
79045
+ - **plan**: The generated or updated plan.
79046
+ - **branchName**: A suitable git branch name for the work.
79047
+
79048
+ Example:
79049
+ \`\`\`json
79050
+ {
79051
+ "type": "plan-generated",
79052
+ "plan": "1. Phase 1: Backend API Development\\n - [ ] Design and implement user authentication endpoints...",
79053
+ "branchName": "feat/user-authentication"
79054
+ }
79055
+ \`\`\`
79056
+
79057
+ ### 2. When you need to ask a question (type: 'question')
79058
+ - **type**: Must be "question".
79059
+ - **question**: An object containing the question and an optional default answer.
79060
+
79061
+ Example:
79062
+ \`\`\`json
79063
+ {
79064
+ "type": "question",
79065
+ "question": {
79066
+ "question": "What database are you using?",
79067
+ "defaultAnswer": "PostgreSQL"
79068
+ }
79069
+ }
79070
+ \`\`\`
79071
+
79072
+ ### 3. When no plan is needed or an error occurs (type: 'error')
79073
+ - **type**: Must be "error".
79074
+ - **reason**: A string explaining why no plan is generated (e.g., task already complete, unclear requirements after questioning).
79075
+
79076
+ Example:
79077
+ \`\`\`json
79078
+ {
79079
+ "type": "error",
79080
+ "reason": "The requested feature is already implemented in 'src/features/existing-feature.ts'."
79081
+ }
79082
+ \`\`\`
78970
79083
  `;
78971
79084
  var BRANCH_NAME_PATTERN = /^[a-zA-Z0-9/_-]+$/;
78972
79085
  var EpicPlanSchema = exports_external.object({
79086
+ type: exports_external.enum(["plan-generated", "question", "error"]),
78973
79087
  plan: exports_external.string().nullish(),
78974
79088
  branchName: exports_external.string().refine((name18) => name18.length >= 3, { message: "Branch name is too short (min 3 characters)." }).refine((name18) => name18.length <= 255, { message: "Branch name is too long (max 255 characters)." }).refine((name18) => BRANCH_NAME_PATTERN.test(name18), {
78975
79089
  message: "Invalid branch name format. Branch names should contain only letters, numbers, hyphens, underscores, and forward slashes."
78976
- }),
79090
+ }).nullish(),
78977
79091
  question: exports_external.object({
78978
79092
  question: exports_external.string(),
78979
79093
  defaultAnswer: exports_external.string().nullish()
78980
79094
  }).nullish(),
78981
79095
  reason: exports_external.string().nullish()
79096
+ }).superRefine((data, ctx) => {
79097
+ if (data.type === "plan-generated") {
79098
+ if (!data.plan || data.plan.trim() === "") {
79099
+ ctx.addIssue({
79100
+ code: "custom",
79101
+ message: 'Plan is required when type is "plan-generated".',
79102
+ path: ["plan"]
79103
+ });
79104
+ }
79105
+ if (!data.branchName || data.branchName.trim() === "") {
79106
+ ctx.addIssue({
79107
+ code: "custom",
79108
+ message: 'Branch name is required when type is "plan-generated".',
79109
+ path: ["branchName"]
79110
+ });
79111
+ }
79112
+ }
78982
79113
  });
78983
79114
  function getPlanPrompt(task, planContent) {
78984
79115
  const planSection = planContent ? `
@@ -78996,21 +79127,90 @@ ${task}
78996
79127
  ${planSection}`;
78997
79128
  }
78998
79129
  var EPIC_ADD_TODO_ITEMS_SYSTEM_PROMPT = `Role: Task creation agent
78999
- Goal: Read a high-level plan and create corresponding todo items.
79130
+ Goal: Parse a detailed epic plan and create todo items from the provided task breakdowns.
79000
79131
 
79001
79132
  ${TOOL_USAGE_INSTRUCTION}
79002
79133
 
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.
79134
+ 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
79135
 
79005
- ## Your Task
79136
+ ## Your Responsibility
79137
+
79138
+ Your goal is to create todo items that are:
79139
+ - **Specific and actionable**: Each item should be clear enough for an AI agent to implement without human intervention
79140
+ - **Well-documented**: Include detailed implementation guidance extracted from the plan
79141
+ - **Context-rich**: Specify relevant files that will be modified or created (as provided in the plan)
79142
+ - **Self-contained**: Each item should be implementable independently with the context from the plan
79143
+
79144
+ ## Plan Structure Expectations
79145
+
79146
+ The plan you receive contains all necessary implementation details. You should NOT need to explore the codebase because:
79147
+ - Each task in the plan already includes specific file paths
79148
+ - Function and class names are specified in the plan
79149
+ - Implementation patterns and approaches are provided
79150
+ - Dependencies and imports are listed
79151
+ - Technical specifications are included
79152
+ - Integration points are documented
79153
+
79154
+ Your job is to extract these details from the plan and create todo items, not to research or discover them.
79155
+
79156
+ ## Todo Item Requirements
79006
79157
 
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.
79158
+ For each task in the plan, create a todo item using the \`updateTodoItem\` tool with:
79159
+
79160
+ ### 1. **title** (required)
79161
+ A concise, action-oriented task name that clearly states what needs to be done.
79162
+
79163
+ **Examples:**
79164
+ - ✅ "Implement user authentication with JWT tokens"
79165
+ - ✅ "Add validation middleware for API endpoints"
79166
+ - ❌ "Authentication" (too vague)
79167
+
79168
+ ### 2. **description** (required)
79169
+ Detailed implementation instructions extracted from the plan. Include:
79170
+
79171
+ **Must extract from the plan:**
79172
+ - **Specific files to create or modify** with exact paths (as specified in the plan)
79173
+ - **Function/class names** to implement or modify (as specified in the plan)
79174
+ - **Implementation patterns** to follow (as referenced in the plan)
79175
+ - **Technical requirements** and constraints (as documented in the plan)
79176
+ - **Dependencies** and imports needed (as listed in the plan)
79177
+ - **Integration points** with existing code (as described in the plan)
79178
+
79179
+ **Example of extracting from a detailed plan task:**
79180
+
79181
+ Plan task:
79182
+ \`\`\`
79183
+ - [ ] Add authentication middleware in \`src/middleware/auth.ts\` that verifies JWT tokens from Authorization header and attaches user object to \`req.user\`
79184
+ \`\`\`
79185
+
79186
+ Todo item description:
79187
+ \`\`\`
79188
+ Create a new authentication middleware in \`src/middleware/auth.ts\`:
79189
+
79190
+ 1. Implement \`authenticateJWT\` function that:
79191
+ - Extracts JWT token from Authorization header
79192
+ - Verifies token (implementation details from plan)
79193
+ - Attaches user object to \`req.user\`
79194
+ - Returns 401 error for invalid/missing tokens
79195
+
79196
+ 2. Export the middleware for use in route definitions
79197
+ \`\`\`
79198
+
79199
+ **Note:** All implementation details should come from the plan. Do not add information not present in the plan.
79009
79200
 
79010
79201
  ## Process
79011
79202
 
79012
- 1. Parse the plan to identify individual tasks.
79013
- 2. For each task, call 'updateTodoItem' with the task description as the 'title'.
79203
+ 1. **Read and parse the plan** provided in the user message to identify individual tasks
79204
+ 2. **Parse the plan structure** to understand the task hierarchy and details
79205
+ 3. **For each task in the plan:**
79206
+ a. Extract the specific files mentioned in the task
79207
+ b. Extract implementation patterns specified in the plan
79208
+ c. Create a detailed description using the plan's guidance
79209
+ d. Identify all relevant files from the plan
79210
+ e. Call \`updateTodoItem\` with operation='add', title and description
79211
+
79212
+ **Important:** Do NOT explore the codebase. All necessary information is already in the plan.
79213
+
79014
79214
  `;
79015
79215
  var CODER_SYSTEM_PROMPT = `Role: AI developer.
79016
79216
  Goal: Implement the provided plan by writing and modifying code.
@@ -79683,17 +79883,17 @@ Files:`);
79683
79883
 
79684
79884
  // src/workflows/code.workflow.ts
79685
79885
  var ImplementOutputSchema = exports_external.object({
79686
- summary: exports_external.string().nullish(),
79687
- bailReason: exports_external.string().nullish()
79886
+ summary: exports_external.string().nullish().describe("Short summary of the changes made"),
79887
+ bailReason: exports_external.string().nullish().describe("Reason for bailing out of the implementation loop")
79688
79888
  }).refine((data) => data.summary != null !== (data.bailReason != null), {
79689
79889
  message: "Either summary or bailReason must be provided, but not both"
79690
79890
  });
79691
79891
  var codeWorkflow = async (input2, context) => {
79692
79892
  const { logger, step, tools: tools2 } = context;
79693
- const { task, files, mode = "interactive" } = input2;
79893
+ const { task, files, mode = "interactive", additionalTools, additionalInstructions } = input2;
79694
79894
  const summaries = [];
79695
79895
  logger.info(`
79696
- \uD83D\uDCCB Phase 1: Creating implementation plan...
79896
+ Phase 1: Creating implementation plan...
79697
79897
  `);
79698
79898
  const planResult = await step("plan", async () => {
79699
79899
  return await planWorkflow({ task, files, mode: mode === "interactive" ? "confirm" : "noninteractive" }, context);
@@ -79704,7 +79904,7 @@ var codeWorkflow = async (input2, context) => {
79704
79904
  }
79705
79905
  const { plan, files: planFiles } = planResult;
79706
79906
  logger.info(`
79707
- ⚙️ Phase 2: Implementing the plan...
79907
+ Phase 2: Implementing the plan...
79708
79908
  `);
79709
79909
  let implementPrompt = getImplementPrompt(plan);
79710
79910
  if (planFiles && planFiles.length > 0) {
@@ -79752,6 +79952,9 @@ ${fileContentString}`;
79752
79952
  if (mode === "interactive") {
79753
79953
  agentTools.push(askFollowupQuestion_default);
79754
79954
  }
79955
+ if (additionalTools) {
79956
+ agentTools.push(...additionalTools);
79957
+ }
79755
79958
  const res = await step("implement", async () => {
79756
79959
  const defaultContext = await getDefaultContext();
79757
79960
  const memoryContext = await tools2.getMemoryContext();
@@ -79768,8 +79971,11 @@ ${memoryContext}`;
79768
79971
  ${memoryContext}`
79769
79972
  });
79770
79973
  }
79974
+ const systemPrompt = additionalInstructions ? `${CODER_SYSTEM_PROMPT}
79975
+
79976
+ ${additionalInstructions}` : CODER_SYSTEM_PROMPT;
79771
79977
  return await agentWorkflow({
79772
- systemPrompt: CODER_SYSTEM_PROMPT,
79978
+ systemPrompt,
79773
79979
  userMessage: [{ role: "user", content: userContent }],
79774
79980
  tools: agentTools,
79775
79981
  outputSchema: ImplementOutputSchema
@@ -79779,13 +79985,13 @@ ${memoryContext}`
79779
79985
  const { summary, bailReason } = res.object;
79780
79986
  if (bailReason) {
79781
79987
  logger.error(`
79782
- Implementation failed: ${bailReason}
79988
+ Implementation failed: ${bailReason}
79783
79989
  `);
79784
79990
  return { success: false, reason: bailReason, summaries };
79785
79991
  }
79786
79992
  if (summary) {
79787
79993
  logger.info(`
79788
- Implementation complete!
79994
+ Implementation complete!
79789
79995
  `);
79790
79996
  summaries.push(summary);
79791
79997
  logger.info(`Summary: ${summary}`);
@@ -79798,20 +80004,20 @@ ${memoryContext}`
79798
80004
  });
79799
80005
  } else {
79800
80006
  logger.info(`
79801
- Implementation complete!
80007
+ Implementation complete!
79802
80008
  `);
79803
80009
  }
79804
80010
  } else if (res.type === "Exit" /* Exit */) {
79805
80011
  logger.info(`
79806
- Implementation complete!
80012
+ Implementation complete!
79807
80013
  `);
79808
80014
  } else {
79809
80015
  logger.warn(`
79810
- ⚠️ Implementation failed. Please check the output for errors.
80016
+ Warning: Implementation failed. Please check the output for errors.
79811
80017
  `, res);
79812
80018
  }
79813
80019
  logger.info(`
79814
- \uD83D\uDD27 Phase 3: Checking for errors...
80020
+ Phase 3: Checking for errors...
79815
80021
  `);
79816
80022
  const fixResult = await step("fix", async () => {
79817
80023
  return await fixWorkflow({ interactive: false, task: input2.task }, context);
@@ -80221,8 +80427,68 @@ ${provider3.toUpperCase()}_API_KEY=${providerConfig.apiKey}`;
80221
80427
  });
80222
80428
  return { configPath };
80223
80429
  };
80430
+ // src/workflows/epic-context.ts
80431
+ import { promises as fs3 } from "node:fs";
80432
+ var EPIC_CONTEXT_FILE = ".epic.yml";
80433
+ var EpicContextSchema = exports_external.object({
80434
+ task: exports_external.string().nullish(),
80435
+ plan: exports_external.string().nullish(),
80436
+ branchName: exports_external.string().nullish(),
80437
+ baseBranch: exports_external.string().nullish(),
80438
+ todos: exports_external.array(TodoItemSchema).nullish(),
80439
+ memory: exports_external.record(exports_external.string(), exports_external.string()).nullish()
80440
+ });
80441
+ var saveEpicContext = async (context) => {
80442
+ const yamlString = $stringify(context);
80443
+ await fs3.writeFile(EPIC_CONTEXT_FILE, yamlString, "utf-8");
80444
+ };
80445
+ var loadEpicContext = async () => {
80446
+ let fileContent;
80447
+ try {
80448
+ fileContent = await fs3.readFile(EPIC_CONTEXT_FILE, "utf-8");
80449
+ } catch {
80450
+ return {};
80451
+ }
80452
+ try {
80453
+ const loaded = $parse(fileContent);
80454
+ return EpicContextSchema.parse(loaded);
80455
+ } catch (error46) {
80456
+ console.error("Error parsing epic context file:", EPIC_CONTEXT_FILE, error46);
80457
+ return {};
80458
+ }
80459
+ };
80460
+
80461
+ class EpicMemoryStore {
80462
+ #context;
80463
+ constructor(context) {
80464
+ this.#context = context;
80465
+ }
80466
+ async read() {
80467
+ return this.#context.memory ?? {};
80468
+ }
80469
+ async write(data) {
80470
+ this.#context.memory = data;
80471
+ await saveEpicContext(this.#context);
80472
+ }
80473
+ }
80474
+
80475
+ class EpicTodoItemStore {
80476
+ #context;
80477
+ constructor(context) {
80478
+ this.#context = context;
80479
+ }
80480
+ async read() {
80481
+ return this.#context.todos ?? [];
80482
+ }
80483
+ async write(data) {
80484
+ this.#context.todos = data;
80485
+ await saveEpicContext(this.#context);
80486
+ }
80487
+ }
80488
+
80224
80489
  // src/workflows/epic.workflow.ts
80225
80490
  var MAX_REVIEW_RETRIES = 5;
80491
+ 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
80492
  async function createPlan2(input2, context) {
80227
80493
  const { task, plan, files, feedback } = input2;
80228
80494
  const content = [{ type: "text", text: getPlanPrompt(task, plan) }];
@@ -80268,48 +80534,57 @@ ${feedback}`
80268
80534
  outputSchema: EpicPlanSchema
80269
80535
  }, context);
80270
80536
  if (planResult.type === "Exit" /* Exit */) {
80271
- return { ...planResult.object, type: planResult.type };
80537
+ return planResult.object;
80272
80538
  }
80273
- return { plan: "", reason: "Usage limit exceeded.", type: "Exit" /* Exit */, branchName: "" };
80539
+ return { type: "error", reason: "Usage limit exceeded." };
80274
80540
  }
80275
80541
  async function createAndApprovePlan(task, context) {
80276
80542
  const { logger, step, tools: tools2 } = context;
80277
- logger.info(`\uD83D\uDCDD Phase 2: Creating high-level plan...
80543
+ logger.info(`Phase 2: Creating high-level plan...
80278
80544
  `);
80279
80545
  let feedback;
80280
- let highLevelPlan;
80281
- let branchName;
80282
80546
  let planAttempt = 1;
80283
80547
  try {
80284
80548
  while (true) {
80285
80549
  const result = await step(`plan-${planAttempt}`, () => createPlan2({ task, feedback }, context));
80286
80550
  planAttempt++;
80287
- if (result.question) {
80288
- const answer = await tools2.input({
80289
- message: result.question.question,
80290
- default: result.question.defaultAnswer || undefined
80291
- });
80292
- feedback = `The user answered the question "${result.question.question}" with: "${answer}"`;
80293
- continue;
80294
- }
80295
- if (!result.plan) {
80296
- if (result.reason) {
80297
- logger.info(`No plan created. Reason: ${result.reason}`);
80298
- } else {
80299
- logger.info("No plan created.");
80300
- }
80301
- return null;
80302
- }
80303
- logger.info(`\uD83D\uDCDD Plan:
80551
+ switch (result.type) {
80552
+ case "plan-generated": {
80553
+ if (!result.plan || !result.branchName) {
80554
+ logger.error("Invalid plan-generated response. Missing plan or branchName.");
80555
+ return null;
80556
+ }
80557
+ logger.info(`Plan:
80304
80558
  ${result.plan}`);
80305
- if (result.branchName) {
80306
- logger.info(`\uD83C\uDF3F Suggested branch name: ${result.branchName}`);
80307
- }
80308
- feedback = await tools2.input({ message: "Press Enter to approve the plan, or provide feedback to refine it." });
80309
- if (feedback.trim() === "") {
80310
- highLevelPlan = result.plan;
80311
- branchName = result.branchName;
80312
- break;
80559
+ logger.info(`Suggested branch name: ${result.branchName}`);
80560
+ feedback = await tools2.input({ message: "Press Enter to approve the plan, or provide feedback to refine it." });
80561
+ if (feedback.trim() === "") {
80562
+ logger.info(`High-level plan approved.
80563
+ `);
80564
+ return { plan: result.plan, branchName: result.branchName };
80565
+ }
80566
+ break;
80567
+ }
80568
+ case "question": {
80569
+ if (!result.question) {
80570
+ logger.error("Invalid question response. Missing question object.");
80571
+ return null;
80572
+ }
80573
+ const answer = await tools2.input({
80574
+ message: result.question.question,
80575
+ default: result.question.defaultAnswer || undefined
80576
+ });
80577
+ feedback = `The user answered the question "${result.question.question}" with: "${answer}"`;
80578
+ break;
80579
+ }
80580
+ case "error": {
80581
+ logger.info(`Plan creation failed. Reason: ${result.reason || "Unknown error"}`);
80582
+ return null;
80583
+ }
80584
+ default: {
80585
+ logger.error(`Unknown response type from planner: ${result.type}`);
80586
+ return null;
80587
+ }
80313
80588
  }
80314
80589
  }
80315
80590
  } catch (e) {
@@ -80319,53 +80594,43 @@ ${result.plan}`);
80319
80594
  }
80320
80595
  throw e;
80321
80596
  }
80322
- if (!highLevelPlan) {
80323
- logger.info("Plan not approved. Exiting.");
80324
- return null;
80325
- }
80326
- if (!branchName) {
80327
- logger.error("❌ Error: No branch name was generated from the plan. Exiting.");
80328
- return null;
80329
- }
80330
- logger.info(`✅ High-level plan approved.
80331
- `);
80332
- return { plan: highLevelPlan, branchName };
80333
80597
  }
80334
- async function createFeatureBranch(branchName, context) {
80598
+ async function createFeatureBranch(branchName, baseBranch, context) {
80335
80599
  const { logger, step, tools: tools2 } = context;
80336
- logger.info(`\uD83C\uDF3F Phase 3: Creating feature branch...
80600
+ logger.info(`Phase 3: Creating/switching to feature branch...
80337
80601
  `);
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;
80602
+ const branchExistsResult = await step("checkBranchExists", async () => await tools2.executeCommand({ command: "git", args: ["rev-parse", "--verify", branchName] }));
80603
+ if (branchExistsResult.exitCode === 0) {
80604
+ const currentBranchResult = await step("getCurrentBranch", async () => tools2.executeCommand({ command: "git", args: ["rev-parse", "--abbrev-ref", "HEAD"] }));
80605
+ const currentBranch = currentBranchResult.stdout.trim();
80606
+ if (currentBranch !== branchName) {
80607
+ logger.info(`Branch '${branchName}' already exists. Switching to it...`);
80608
+ const checkoutResult = await step("checkoutBranch", async () => await tools2.executeCommand({ command: "git", args: ["checkout", branchName] }));
80609
+ if (checkoutResult.exitCode !== 0) {
80610
+ logger.error(`Error: Failed to switch to branch '${branchName}'. Git command failed.`);
80611
+ return { success: false, branchName: null };
80354
80612
  }
80355
- counter++;
80613
+ } else {
80614
+ logger.info(`Already on branch '${branchName}'.`);
80615
+ }
80616
+ } else {
80617
+ logger.info(`Creating new branch '${branchName}'...`);
80618
+ const createBranchResult = await step("createBranch", async () => await tools2.executeCommand({
80619
+ command: "git",
80620
+ args: baseBranch ? ["checkout", "-b", branchName, baseBranch] : ["checkout", "-b", branchName]
80621
+ }));
80622
+ if (createBranchResult.exitCode !== 0) {
80623
+ logger.error(`Error: Failed to create branch '${branchName}'. Git command failed.`);
80624
+ return { success: false, branchName: null };
80356
80625
  }
80357
80626
  }
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.
80627
+ logger.info(`Successfully on branch '${branchName}'.
80363
80628
  `);
80364
- return { success: true, branchName: finalBranchName };
80629
+ return { success: true, branchName };
80365
80630
  }
80366
80631
  async function addTodoItemsFromPlan(plan, context) {
80367
80632
  const { logger, step, tools: tools2 } = context;
80368
- logger.info(`\uD83D\uDCDD Phase 4: Creating todo items from plan...
80633
+ logger.info(`Phase 4: Creating todo items from plan...
80369
80634
  `);
80370
80635
  await step("add-todo-items", async () => {
80371
80636
  await agentWorkflow({
@@ -80377,28 +80642,46 @@ ${plan}</plan>` }],
80377
80642
  }, context);
80378
80643
  });
80379
80644
  const todos = await tools2.listTodoItems({});
80380
- logger.info(`✅ Created ${todos.length} todo items.
80645
+ logger.info(`Created ${todos.length} todo items.
80381
80646
  `);
80382
80647
  }
80383
- async function runPreflightChecks(context) {
80648
+ async function runPreflightChecks(epicContext, context) {
80384
80649
  const { logger, step, tools: tools2 } = context;
80385
- logger.info(`\uD83D\uDCCB Phase 1: Running pre-flight checks...
80650
+ logger.info(`Phase 1: Running pre-flight checks...
80386
80651
  `);
80387
80652
  const gitCheckResult = await step("gitCheck", async () => tools2.executeCommand({ command: "git", args: ["rev-parse", "--git-dir"] }));
80388
80653
  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 };
80654
+ logger.error("Error: Git is not initialized in this directory. Please run `git init` first.");
80655
+ logger.info("Suggestion: Run `git init` to initialize a git repository.\n");
80656
+ return false;
80657
+ }
80658
+ if (epicContext.plan) {
80659
+ logger.info("Found an existing `.epic.yml` file.");
80660
+ if (!epicContext.branchName) {
80661
+ throw new Error("Invalid epic context loaded from .epic.yml: branchName is required.");
80662
+ }
80663
+ const currentBranchResult = await step("getCurrentBranch", async () => tools2.executeCommand({ command: "git", args: ["rev-parse", "--abbrev-ref", "HEAD"] }));
80664
+ const currentBranch = currentBranchResult.stdout.trim();
80665
+ if (currentBranch !== epicContext.branchName) {
80666
+ 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.`);
80667
+ }
80668
+ logger.info("Resuming previous epic session.");
80669
+ return true;
80392
80670
  }
80393
80671
  const gitStatusResult = await step("gitStatus", async () => tools2.executeCommand({ command: "git status --porcelain", shell: true }));
80394
80672
  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 };
80673
+ logger.error("Error: Your working directory is not clean. Please stash or commit your changes before running the epic workflow.");
80674
+ logger.info("Suggestion: Run `git add .` and `git commit` to clean your working directory, or `git stash` to temporarily save changes.\n");
80675
+ return false;
80676
+ }
80677
+ const baseBranchResult = await step("getBaseBranch", async () => tools2.executeCommand({ command: "git", args: ["rev-parse", "--abbrev-ref", "HEAD"] }));
80678
+ if (baseBranchResult.exitCode === 0 && baseBranchResult.stdout.trim()) {
80679
+ epicContext.baseBranch = baseBranchResult.stdout.trim();
80680
+ logger.info(`Using current branch '${epicContext.baseBranch}' as the base for this epic.`);
80398
80681
  }
80399
- logger.info(`✅ Pre-flight checks passed.
80682
+ logger.info(`Pre-flight checks passed.
80400
80683
  `);
80401
- return { success: true };
80684
+ return true;
80402
80685
  }
80403
80686
  async function performReviewAndFixCycle(iterationCount, taskItem, highLevelPlan, context) {
80404
80687
  const { logger, step, tools: tools2 } = context;
@@ -80406,12 +80689,12 @@ async function performReviewAndFixCycle(iterationCount, taskItem, highLevelPlan,
80406
80689
  const diffResult = await tools2.executeCommand({ command: "git", args: ["diff", "--name-status", "HEAD~1", "HEAD"] });
80407
80690
  const changedFiles = parseGitDiffNameStatus(diffResult.stdout);
80408
80691
  if (changedFiles.length === 0) {
80409
- logger.info(`ℹ️ No files were changed. Skipping review.
80692
+ logger.info(`No files were changed. Skipping review.
80410
80693
  `);
80411
80694
  return { passed: true };
80412
80695
  }
80413
80696
  logger.info(`
80414
- \uD83D\uDD0E Review iteration ${i + 1}/${MAX_REVIEW_RETRIES}`);
80697
+ Review iteration ${i + 1}/${MAX_REVIEW_RETRIES}`);
80415
80698
  logger.info(` Changed files: ${changedFiles.length}`);
80416
80699
  const changeInfo = {
80417
80700
  commitRange: "HEAD~1...HEAD",
@@ -80432,16 +80715,16 @@ ${formatReviewToolInput(changeInfo)}`;
80432
80715
  }, context);
80433
80716
  });
80434
80717
  if (reviewAgentResult.type !== "Exit" /* Exit */) {
80435
- logger.error(`\uD83D\uDEAB Review agent failed with status: ${reviewAgentResult.type}.`);
80718
+ logger.error(`Review agent failed with status: ${reviewAgentResult.type}.`);
80436
80719
  break;
80437
80720
  }
80438
80721
  const reviewResult = reviewAgentResult.object;
80439
80722
  if (!reviewResult || !reviewResult.specificReviews || reviewResult.specificReviews.length === 0) {
80440
- logger.info(`✅ Review passed. No issues found.
80723
+ logger.info(`Review passed. No issues found.
80441
80724
  `);
80442
80725
  return { passed: true };
80443
80726
  }
80444
- logger.warn(`⚠️ Review found ${reviewResult.specificReviews.length} issue(s). Attempting to fix...
80727
+ logger.warn(`Warning: Review found ${reviewResult.specificReviews.length} issue(s). Attempting to fix...
80445
80728
  `);
80446
80729
  for (const [idx, review] of reviewResult.specificReviews.entries()) {
80447
80730
  logger.warn(` ${idx + 1}. ${review.file}:${review.lines}`);
@@ -80462,7 +80745,12 @@ After an initial implementation, a review found the following issues. Please fix
80462
80745
 
80463
80746
  ${reviewSummary}`;
80464
80747
  await step(`fix-${iterationCount}-${i}`, async () => {
80465
- await codeWorkflow({ task: fixTask, mode: "noninteractive" }, context);
80748
+ await codeWorkflow({
80749
+ task: fixTask,
80750
+ mode: "noninteractive",
80751
+ additionalInstructions: TODO_HANDLING_INSTRUCTIONS,
80752
+ additionalTools: [getTodoItem_default, listTodoItems_default, updateTodoItem_default]
80753
+ }, context);
80466
80754
  });
80467
80755
  await step(`commit-fix-${iterationCount}-${i}`, async () => {
80468
80756
  await tools2.executeCommand({ command: "git", args: ["add", "."] });
@@ -80470,7 +80758,7 @@ ${reviewSummary}`;
80470
80758
  });
80471
80759
  if (i === MAX_REVIEW_RETRIES - 1) {
80472
80760
  logger.error(`
80473
- \uD83D\uDEAB Max retries (${MAX_REVIEW_RETRIES}) reached. Moving to the next task, but issues might remain.
80761
+ Max retries (${MAX_REVIEW_RETRIES}) reached. Moving to the next task, but issues might remain.
80474
80762
  `);
80475
80763
  return { passed: false };
80476
80764
  }
@@ -80480,7 +80768,7 @@ ${reviewSummary}`;
80480
80768
  async function runImplementationLoop(context, highLevelPlan) {
80481
80769
  const { logger, step, tools: tools2 } = context;
80482
80770
  const commitMessages = [];
80483
- logger.info(`\uD83D\uDE80 Phase 5: Iterative Implementation Loop...
80771
+ logger.info(`Phase 5: Iterative Implementation Loop...
80484
80772
  `);
80485
80773
  logger.info(`${"=".repeat(80)}
80486
80774
  `);
@@ -80502,9 +80790,9 @@ async function runImplementationLoop(context, highLevelPlan) {
80502
80790
  iterationCount++;
80503
80791
  const taskStartTime = Date.now();
80504
80792
  logger.info(`
80505
- ${"".repeat(80)}`);
80506
- logger.info(`\uD83D\uDCCC Iteration ${iterationCount}`);
80507
- logger.info(`${"".repeat(80)}`);
80793
+ ${"-".repeat(80)}`);
80794
+ logger.info(`Iteration ${iterationCount}`);
80795
+ logger.info(`${"-".repeat(80)}`);
80508
80796
  logger.info(`${nextTask}
80509
80797
  `);
80510
80798
  await step(`task-${iterationCount}`, async () => {
@@ -80518,7 +80806,12 @@ Your current task is to implement this specific item:
80518
80806
  ${nextTask}
80519
80807
 
80520
80808
  Focus only on this item, but use the plan for context.`;
80521
- return await codeWorkflow({ task: taskWithContext, mode: "noninteractive" }, context);
80809
+ return await codeWorkflow({
80810
+ task: taskWithContext,
80811
+ mode: "noninteractive",
80812
+ additionalInstructions: TODO_HANDLING_INSTRUCTIONS,
80813
+ additionalTools: [getTodoItem_default, listTodoItems_default, updateTodoItem_default]
80814
+ }, context);
80522
80815
  });
80523
80816
  const commitMessage = `feat: ${nextTask}`;
80524
80817
  await step(`commit-initial-${iterationCount}`, async () => {
@@ -80530,9 +80823,9 @@ Focus only on this item, but use the plan for context.`;
80530
80823
  const taskElapsed = Date.now() - taskStartTime;
80531
80824
  const taskElapsedTime = formatElapsedTime(taskElapsed);
80532
80825
  if (reviewPassed) {
80533
- logger.info(`✅ Iteration ${iterationCount} completed successfully (${taskElapsedTime})`);
80826
+ logger.info(`Iteration ${iterationCount} completed successfully (${taskElapsedTime})`);
80534
80827
  } else {
80535
- logger.warn(`⚠️ Iteration ${iterationCount} completed with potential issues (${taskElapsedTime})`);
80828
+ logger.warn(`Warning: Iteration ${iterationCount} completed with potential issues (${taskElapsedTime})`);
80536
80829
  }
80537
80830
  await step(`update-task-status-${iterationCount}`, async () => {
80538
80831
  if (!nextTaskId) {
@@ -80563,53 +80856,47 @@ Focus only on this item, but use the plan for context.`;
80563
80856
  progressMessage = `Iteration ${iterationCount} completed`;
80564
80857
  }
80565
80858
  logger.info(`
80566
- \uD83D\uDCCA Progress: ${progressMessage}`);
80859
+ Progress: ${progressMessage}`);
80567
80860
  if (isComplete) {
80568
- logger.info(`✅ All tasks complete!
80861
+ logger.info(`All tasks complete!
80569
80862
  `);
80570
80863
  break;
80571
80864
  }
80572
80865
  if (nextTask) {
80573
- logger.info(`\uD83D\uDCCC Next task: ${nextTask}
80866
+ logger.info(`Next task: ${nextTask}
80574
80867
  `);
80575
80868
  }
80576
- logger.info(`${"".repeat(80)}
80869
+ logger.info(`${"-".repeat(80)}
80577
80870
  `);
80578
80871
  }
80579
80872
  return commitMessages;
80580
80873
  }
80581
- async function performFinalReviewAndFix(context, highLevelPlan) {
80874
+ async function performFinalReviewAndFix(context, highLevelPlan, baseBranch) {
80582
80875
  const { logger, step, tools: tools2 } = context;
80583
80876
  logger.info(`
80584
80877
  Phase 6: Final Review and Fixup...
80585
80878
  `);
80586
- const ghCheckResult = await tools2.executeCommand({ command: "gh", args: ["--version"] });
80587
- 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.");
80879
+ if (!baseBranch) {
80880
+ logger.warn("Warning: Base branch is not defined. Skipping final review step.");
80589
80881
  return { passed: true };
80590
80882
  }
80591
- const defaultBranchResult = await tools2.executeCommand({
80592
- command: "gh",
80593
- args: ["repo", "view", "--json", "defaultBranchRef", "--jq", ".defaultBranchRef.name"]
80594
- });
80595
- const defaultBranch = defaultBranchResult.stdout.trim();
80596
80883
  const currentBranchResult = await tools2.executeCommand({ command: "git", args: ["rev-parse", "--abbrev-ref", "HEAD"] });
80597
80884
  const currentBranch = currentBranchResult.stdout.trim();
80598
- if (currentBranch === defaultBranch) {
80599
- logger.info(`✅ You are on the default branch ('${defaultBranch}'). No final review needed.`);
80885
+ if (currentBranch === baseBranch) {
80886
+ logger.info(`You are on the base branch ('${baseBranch}'). No final review needed.`);
80600
80887
  return { passed: true };
80601
80888
  }
80602
- const commitRange = `${defaultBranch}...${currentBranch}`;
80889
+ const commitRange = `${baseBranch}...${currentBranch}`;
80603
80890
  for (let i = 0;i < MAX_REVIEW_RETRIES; i++) {
80604
80891
  const diffResult = await tools2.executeCommand({ command: "git", args: ["diff", "--name-status", commitRange] });
80605
80892
  const changedFiles = parseGitDiffNameStatus(diffResult.stdout);
80606
80893
  if (changedFiles.length === 0) {
80607
- logger.info(`ℹ️ No files have been changed in this branch. Skipping final review.
80894
+ logger.info(`No files have been changed in this branch. Skipping final review.
80608
80895
  `);
80609
80896
  return { passed: true };
80610
80897
  }
80611
80898
  logger.info(`
80612
- \uD83D\uDD0E Final review iteration ${i + 1}/${MAX_REVIEW_RETRIES}`);
80899
+ Final review iteration ${i + 1}/${MAX_REVIEW_RETRIES}`);
80613
80900
  logger.info(` Changed files: ${changedFiles.length}`);
80614
80901
  const changeInfo = {
80615
80902
  commitRange,
@@ -80630,16 +80917,16 @@ ${formatReviewToolInput(changeInfo)}`;
80630
80917
  }, context);
80631
80918
  });
80632
80919
  if (reviewAgentResult.type !== "Exit" /* Exit */) {
80633
- logger.error(`\uD83D\uDEAB Review agent failed with status: ${reviewAgentResult.type}.`);
80920
+ logger.error(`Review agent failed with status: ${reviewAgentResult.type}.`);
80634
80921
  break;
80635
80922
  }
80636
80923
  const reviewResult = reviewAgentResult.object;
80637
80924
  if (!reviewResult || !reviewResult.specificReviews || reviewResult.specificReviews.length === 0) {
80638
- logger.info(`✅ Final review passed. No issues found.
80925
+ logger.info(`Final review passed. No issues found.
80639
80926
  `);
80640
80927
  return { passed: true };
80641
80928
  }
80642
- logger.warn(`⚠️ Final review found ${reviewResult.specificReviews.length} issue(s). Attempting to fix...
80929
+ logger.warn(`Warning: Final review found ${reviewResult.specificReviews.length} issue(s). Attempting to fix...
80643
80930
  `);
80644
80931
  for (const [idx, review] of reviewResult.specificReviews.entries()) {
80645
80932
  logger.warn(` ${idx + 1}. ${review.file}:${review.lines}`);
@@ -80659,7 +80946,12 @@ A final review of all the changes in the branch found the following issues. Plea
80659
80946
 
80660
80947
  ${reviewSummary}`;
80661
80948
  await step(`final-fix-${i}`, async () => {
80662
- await codeWorkflow({ task: fixTask, mode: "noninteractive" }, context);
80949
+ await codeWorkflow({
80950
+ task: fixTask,
80951
+ mode: "noninteractive",
80952
+ additionalInstructions: TODO_HANDLING_INSTRUCTIONS,
80953
+ additionalTools: [getTodoItem_default, listTodoItems_default, updateTodoItem_default]
80954
+ }, context);
80663
80955
  });
80664
80956
  await step(`commit-final-fix-${i}`, async () => {
80665
80957
  await tools2.executeCommand({ command: "git", args: ["add", "."] });
@@ -80667,65 +80959,93 @@ ${reviewSummary}`;
80667
80959
  });
80668
80960
  if (i === MAX_REVIEW_RETRIES - 1) {
80669
80961
  logger.error(`
80670
- \uD83D\uDEAB Max retries (${MAX_REVIEW_RETRIES}) reached for final review. Issues might remain.
80962
+ Max retries (${MAX_REVIEW_RETRIES}) reached for final review. Issues might remain.
80671
80963
  `);
80672
80964
  return { passed: false };
80673
80965
  }
80674
80966
  }
80675
80967
  return { passed: false };
80676
80968
  }
80677
- var epicWorkflow = async (input2, context) => {
80678
- const { logger } = context;
80679
- const { task } = input2;
80969
+ var epicWorkflow = async (epicContext, context) => {
80970
+ const { logger, tools: tools2 } = context;
80971
+ const { task } = epicContext;
80680
80972
  const workflowStartTime = Date.now();
80681
- let branchName = "";
80682
80973
  if (!task || task.trim() === "") {
80683
- logger.error("Error: Task cannot be empty. Please provide a valid task description.");
80974
+ logger.error("Error: Task cannot be empty. Please provide a valid task description.");
80684
80975
  return;
80685
80976
  }
80977
+ if (!epicContext.task) {
80978
+ epicContext.task = task;
80979
+ }
80686
80980
  try {
80687
- const preflightResult = await runPreflightChecks(context);
80688
- if (!preflightResult.success) {
80981
+ const preflightResult = await runPreflightChecks(epicContext, context);
80982
+ if (!preflightResult) {
80689
80983
  return;
80690
80984
  }
80691
- const planResult = await createAndApprovePlan(task, context);
80692
- if (!planResult) {
80985
+ if (!epicContext.plan) {
80986
+ if (!epicContext.task) {
80987
+ logger.error("Error: Task is missing in epic context. Exiting.");
80988
+ return;
80989
+ }
80990
+ const planResult = await createAndApprovePlan(epicContext.task, context);
80991
+ if (!planResult)
80992
+ return;
80993
+ epicContext.plan = planResult.plan;
80994
+ epicContext.branchName = planResult.branchName;
80995
+ await saveEpicContext(epicContext);
80996
+ }
80997
+ if (!epicContext.plan) {
80998
+ logger.error("Error: Plan is missing after planning phase. Exiting.");
80999
+ return;
81000
+ }
81001
+ if (!epicContext.branchName) {
81002
+ logger.error("Error: Branch name is missing after planning phase. Exiting.");
80693
81003
  return;
80694
81004
  }
80695
- const { plan: highLevelPlan } = planResult;
80696
- branchName = planResult.branchName;
80697
- const branchResult = await createFeatureBranch(branchName, context);
80698
- if (!branchResult.success || !branchResult.branchName) {
81005
+ const branchResult = await createFeatureBranch(epicContext.branchName, epicContext.baseBranch ?? undefined, context);
81006
+ if (!branchResult.success || !branchResult.branchName)
80699
81007
  return;
81008
+ if (epicContext.branchName !== branchResult.branchName) {
81009
+ epicContext.branchName = branchResult.branchName;
81010
+ await saveEpicContext(epicContext);
81011
+ }
81012
+ const todos = await tools2.listTodoItems({});
81013
+ if (todos.length === 0) {
81014
+ await addTodoItemsFromPlan(epicContext.plan, context);
81015
+ }
81016
+ const commitMessages = await runImplementationLoop(context, epicContext.plan);
81017
+ await performFinalReviewAndFix(context, epicContext.plan, epicContext.baseBranch ?? undefined);
81018
+ await tools2.executeCommand({ command: "git", args: ["rm", "-f", ".epic.yml"] });
81019
+ const statusResult = await tools2.executeCommand({
81020
+ command: "git",
81021
+ args: ["status", "--porcelain", "--", ".epic.yml"]
81022
+ });
81023
+ if (statusResult.stdout.trim() !== "") {
81024
+ await tools2.executeCommand({ command: "git", args: ["commit", "-m", "chore: remove .epic.yml", "--", ".epic.yml"] });
81025
+ logger.info("Cleaned up .epic.yml file.");
80700
81026
  }
80701
- branchName = branchResult.branchName;
80702
- await addTodoItemsFromPlan(highLevelPlan, context);
80703
- const commitMessages = await runImplementationLoop(context, highLevelPlan);
80704
- await performFinalReviewAndFix(context, highLevelPlan);
80705
81027
  const totalElapsed = Date.now() - workflowStartTime;
80706
81028
  const totalElapsedTime = formatElapsedTime(totalElapsed);
80707
- const iterationCount = commitMessages.length;
80708
81029
  logger.info(`
80709
81030
  ${"=".repeat(80)}`);
80710
- logger.info("\uD83C\uDF89 Epic Workflow Complete!");
81031
+ logger.info("Epic Workflow Complete!");
80711
81032
  logger.info(`${"=".repeat(80)}`);
80712
81033
  logger.info(`
80713
- \uD83D\uDCCA Summary:`);
80714
- logger.info(` Total iterations: ${iterationCount}`);
80715
- logger.info(` Total commits: ${commitMessages.length}`);
80716
- logger.info(` Branch: ${branchName}`);
81034
+ Summary:`);
81035
+ logger.info(` Branch: ${epicContext.branchName}`);
80717
81036
  logger.info(` Total time: ${totalElapsedTime}`);
80718
- logger.info("\uD83D\uDCDD Commits created:");
81037
+ logger.info(` Total commits: ${commitMessages.length}`);
81038
+ logger.info("Commits created:");
80719
81039
  for (const [idx, msg] of commitMessages.entries()) {
80720
81040
  logger.info(` ${idx + 1}. ${msg}`);
80721
81041
  }
80722
81042
  } catch (error46) {
80723
81043
  logger.error(`
80724
- Epic workflow failed: ${error46 instanceof Error ? error46.message : String(error46)}`);
80725
- if (branchName) {
81044
+ Epic workflow failed: ${error46 instanceof Error ? error46.message : String(error46)}`);
81045
+ if (epicContext?.branchName) {
80726
81046
  logger.info(`
80727
- Branch '${branchName}' was created but work is incomplete.`);
80728
- logger.info(`To cleanup: git checkout <previous-branch> && git branch -D ${branchName}
81047
+ Branch '${epicContext.branchName}' was created but work is incomplete.`);
81048
+ logger.info(`To cleanup: git checkout <previous-branch> && git branch -D ${epicContext.branchName}
80729
81049
  `);
80730
81050
  }
80731
81051
  throw error46;
@@ -80740,7 +81060,7 @@ var taskWorkflow = async (input2, context) => {
80740
81060
  const { logger, step } = context;
80741
81061
  const { task } = input2;
80742
81062
  logger.info(`
80743
- \uD83E\uDD16 Running generic agent...
81063
+ Running generic agent...
80744
81064
  `);
80745
81065
  await step("agent", async () => {
80746
81066
  const defaultContext = await getDefaultContext();
@@ -80765,7 +81085,7 @@ ${defaultContext}`;
80765
81085
  }, context);
80766
81086
  });
80767
81087
  logger.info(`
80768
- Agent finished!
81088
+ Agent finished!
80769
81089
  `);
80770
81090
  return { success: true };
80771
81091
  };
@@ -80778,7 +81098,7 @@ var metaWorkflow = async (input2, context) => {
80778
81098
  const { task } = input2;
80779
81099
  const { logger } = context;
80780
81100
  logger.info(`
80781
- \uD83E\uDD14 Deciding which workflow to use for task...
81101
+ Deciding which workflow to use for task...
80782
81102
  `);
80783
81103
  const result = await agentWorkflow({
80784
81104
  systemPrompt: META_SYSTEM_PROMPT,
@@ -80799,7 +81119,7 @@ var metaWorkflow = async (input2, context) => {
80799
81119
  throw new Error("Could not decide which workflow to run.");
80800
81120
  }
80801
81121
  logger.info(`
80802
- Decision: Using '${decision.workflow}' workflow.`);
81122
+ Decision: Using '${decision.workflow}' workflow.`);
80803
81123
  switch (decision.workflow) {
80804
81124
  case "code":
80805
81125
  await codeWorkflow({ task }, context);
@@ -81017,32 +81337,41 @@ var commitCommand = new Command("commit").description("Create a commit with AI-g
81017
81337
 
81018
81338
  // src/commands/epic.ts
81019
81339
  async function runEpic(task2, _options, command) {
81340
+ const globalOpts = (command.parent ?? command).opts();
81341
+ const { verbose, yes } = globalOpts;
81342
+ const logger = createLogger({
81343
+ verbose
81344
+ });
81020
81345
  let taskInput = task2;
81021
- 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") {
81346
+ const epicContext = await loadEpicContext();
81347
+ if (epicContext.task) {
81348
+ if (taskInput) {
81349
+ logger.error("Error: Existing epic context found, but task was provided via CLI args. Exiting.");
81350
+ return;
81351
+ }
81352
+ logger.info("Resuming existing epic session. Task:");
81353
+ logger.info(` ${epicContext.task}`);
81354
+ } else {
81355
+ if (!taskInput) {
81356
+ taskInput = await getUserInput("What feature do you want to implement?");
81357
+ if (!taskInput) {
81358
+ logger.info("No task provided. Exiting.");
81028
81359
  return;
81029
81360
  }
81030
- throw error46;
81031
81361
  }
81362
+ epicContext.task = taskInput;
81032
81363
  }
81033
- if (!taskInput) {
81034
- console.error("No task provided. Aborting.");
81035
- return;
81036
- }
81037
- const workflowInput = {
81038
- task: taskInput
81039
- };
81040
- const globalOpts = (command.parent ?? command).opts();
81041
- const { verbose, yes } = globalOpts;
81042
- const logger = createLogger({
81043
- verbose
81364
+ await runWorkflow(epicWorkflow, epicContext, {
81365
+ commandName: "epic",
81366
+ command,
81367
+ logger,
81368
+ yes,
81369
+ getProvider: (opt) => getProvider({
81370
+ ...opt,
81371
+ todoItemStore: new EpicTodoItemStore(epicContext),
81372
+ memoryStore: new EpicMemoryStore(epicContext)
81373
+ })
81044
81374
  });
81045
- await runWorkflow(epicWorkflow, workflowInput, { commandName: "epic", command, logger, yes });
81046
81375
  }
81047
81376
  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
81377