@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.
- package/dist/index.js +572 -243
- 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.
|
|
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
|
|
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,
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
52540
|
-
status: "open"
|
|
52558
|
+
status: status ?? "open"
|
|
52541
52559
|
};
|
|
52542
|
-
|
|
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
|
-
|
|
52585
|
+
const memory = await memoryStore.read() ?? {};
|
|
52586
|
+
return Object.keys(memory);
|
|
52570
52587
|
},
|
|
52571
52588
|
readMemory: async (topic = defaultMemoryTopic) => {
|
|
52572
|
-
|
|
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
|
-
|
|
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
|
-
|
|
52607
|
+
memory[memoryTopic] = content;
|
|
52588
52608
|
break;
|
|
52589
52609
|
case "remove":
|
|
52590
|
-
delete
|
|
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),
|
|
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
|
-
|
|
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 = `
|
|
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
|
|
78877
|
-
-
|
|
78878
|
-
-
|
|
78879
|
-
-
|
|
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
|
-
-
|
|
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
|
|
78907
|
-
- Group related checkboxes under numbered sections or phases
|
|
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
|
-
**
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
-
|
|
78930
|
-
-
|
|
78931
|
-
-
|
|
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.
|
|
78951
|
-
b.
|
|
78952
|
-
c.
|
|
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
|
-
|
|
78962
|
-
|
|
78963
|
-
|
|
78964
|
-
|
|
78965
|
-
|
|
78966
|
-
|
|
78967
|
-
|
|
78968
|
-
|
|
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:
|
|
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
|
|
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
|
|
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
|
-
|
|
79008
|
-
|
|
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.
|
|
79013
|
-
2.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
80007
|
+
Implementation complete!
|
|
79802
80008
|
`);
|
|
79803
80009
|
}
|
|
79804
80010
|
} else if (res.type === "Exit" /* Exit */) {
|
|
79805
80011
|
logger.info(`
|
|
79806
|
-
|
|
80012
|
+
Implementation complete!
|
|
79807
80013
|
`);
|
|
79808
80014
|
} else {
|
|
79809
80015
|
logger.warn(`
|
|
79810
|
-
|
|
80016
|
+
Warning: Implementation failed. Please check the output for errors.
|
|
79811
80017
|
`, res);
|
|
79812
80018
|
}
|
|
79813
80019
|
logger.info(`
|
|
79814
|
-
|
|
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
|
|
80537
|
+
return planResult.object;
|
|
80272
80538
|
}
|
|
80273
|
-
return {
|
|
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(
|
|
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
|
-
|
|
80288
|
-
|
|
80289
|
-
|
|
80290
|
-
|
|
80291
|
-
|
|
80292
|
-
|
|
80293
|
-
|
|
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
|
-
|
|
80306
|
-
|
|
80307
|
-
|
|
80308
|
-
|
|
80309
|
-
|
|
80310
|
-
|
|
80311
|
-
|
|
80312
|
-
|
|
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(
|
|
80600
|
+
logger.info(`Phase 3: Creating/switching to feature branch...
|
|
80337
80601
|
`);
|
|
80338
|
-
|
|
80339
|
-
|
|
80340
|
-
|
|
80341
|
-
|
|
80342
|
-
|
|
80343
|
-
|
|
80344
|
-
|
|
80345
|
-
|
|
80346
|
-
|
|
80347
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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(
|
|
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(
|
|
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(
|
|
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("
|
|
80390
|
-
logger.info("
|
|
80391
|
-
return
|
|
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("
|
|
80396
|
-
logger.info("
|
|
80397
|
-
return
|
|
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(
|
|
80682
|
+
logger.info(`Pre-flight checks passed.
|
|
80400
80683
|
`);
|
|
80401
|
-
return
|
|
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(
|
|
80692
|
+
logger.info(`No files were changed. Skipping review.
|
|
80410
80693
|
`);
|
|
80411
80694
|
return { passed: true };
|
|
80412
80695
|
}
|
|
80413
80696
|
logger.info(`
|
|
80414
|
-
|
|
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(
|
|
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(
|
|
80723
|
+
logger.info(`Review passed. No issues found.
|
|
80441
80724
|
`);
|
|
80442
80725
|
return { passed: true };
|
|
80443
80726
|
}
|
|
80444
|
-
logger.warn(
|
|
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({
|
|
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
|
-
|
|
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(
|
|
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
|
-
${"
|
|
80506
|
-
logger.info(
|
|
80507
|
-
logger.info(`${"
|
|
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({
|
|
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(
|
|
80826
|
+
logger.info(`Iteration ${iterationCount} completed successfully (${taskElapsedTime})`);
|
|
80534
80827
|
} else {
|
|
80535
|
-
logger.warn(
|
|
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
|
-
|
|
80859
|
+
Progress: ${progressMessage}`);
|
|
80567
80860
|
if (isComplete) {
|
|
80568
|
-
logger.info(
|
|
80861
|
+
logger.info(`All tasks complete!
|
|
80569
80862
|
`);
|
|
80570
80863
|
break;
|
|
80571
80864
|
}
|
|
80572
80865
|
if (nextTask) {
|
|
80573
|
-
logger.info(
|
|
80866
|
+
logger.info(`Next task: ${nextTask}
|
|
80574
80867
|
`);
|
|
80575
80868
|
}
|
|
80576
|
-
logger.info(`${"
|
|
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
|
-
|
|
80587
|
-
|
|
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 ===
|
|
80599
|
-
logger.info(
|
|
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 = `${
|
|
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(
|
|
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
|
-
|
|
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(
|
|
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(
|
|
80925
|
+
logger.info(`Final review passed. No issues found.
|
|
80639
80926
|
`);
|
|
80640
80927
|
return { passed: true };
|
|
80641
80928
|
}
|
|
80642
|
-
logger.warn(
|
|
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({
|
|
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
|
-
|
|
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 (
|
|
80678
|
-
const { logger } = context;
|
|
80679
|
-
const { task } =
|
|
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("
|
|
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
|
|
80981
|
+
const preflightResult = await runPreflightChecks(epicContext, context);
|
|
80982
|
+
if (!preflightResult) {
|
|
80689
80983
|
return;
|
|
80690
80984
|
}
|
|
80691
|
-
|
|
80692
|
-
|
|
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
|
|
80696
|
-
|
|
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("
|
|
81031
|
+
logger.info("Epic Workflow Complete!");
|
|
80711
81032
|
logger.info(`${"=".repeat(80)}`);
|
|
80712
81033
|
logger.info(`
|
|
80713
|
-
|
|
80714
|
-
logger.info(`
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
81022
|
-
|
|
81023
|
-
|
|
81024
|
-
|
|
81025
|
-
|
|
81026
|
-
}
|
|
81027
|
-
|
|
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
|
-
|
|
81034
|
-
|
|
81035
|
-
|
|
81036
|
-
|
|
81037
|
-
|
|
81038
|
-
|
|
81039
|
-
|
|
81040
|
-
|
|
81041
|
-
|
|
81042
|
-
|
|
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
|
|