@polka-codes/cli 0.9.49 → 0.9.50
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +443 -174
- 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.50";
|
|
35583
35583
|
|
|
35584
35584
|
// src/commands/code.ts
|
|
35585
35585
|
import { readFile as readFile3 } from "node:fs/promises";
|
|
@@ -48609,7 +48609,6 @@ var TodoItemSchema = exports_external.object({
|
|
|
48609
48609
|
id: exports_external.string(),
|
|
48610
48610
|
title: exports_external.string(),
|
|
48611
48611
|
description: exports_external.string(),
|
|
48612
|
-
relevantFileList: exports_external.array(exports_external.string()),
|
|
48613
48612
|
status: TodoStatus
|
|
48614
48613
|
});
|
|
48615
48614
|
var UpdateTodoItemInputSchema = exports_external.object({
|
|
@@ -48618,7 +48617,6 @@ var UpdateTodoItemInputSchema = exports_external.object({
|
|
|
48618
48617
|
parentId: exports_external.string().nullish(),
|
|
48619
48618
|
title: exports_external.string().nullish(),
|
|
48620
48619
|
description: exports_external.string().nullish(),
|
|
48621
|
-
relevantFileList: exports_external.array(exports_external.string()).nullish(),
|
|
48622
48620
|
status: TodoStatus.nullish()
|
|
48623
48621
|
}).superRefine((data, ctx) => {
|
|
48624
48622
|
if (data.operation === "add") {
|
|
@@ -52472,13 +52470,23 @@ async function searchFiles(path, regex, filePattern, cwd, excludeFiles) {
|
|
|
52472
52470
|
}
|
|
52473
52471
|
|
|
52474
52472
|
// ../cli-shared/src/provider.ts
|
|
52473
|
+
class InMemoryStore {
|
|
52474
|
+
#data;
|
|
52475
|
+
async read() {
|
|
52476
|
+
return this.#data;
|
|
52477
|
+
}
|
|
52478
|
+
async write(data) {
|
|
52479
|
+
this.#data = data;
|
|
52480
|
+
}
|
|
52481
|
+
}
|
|
52475
52482
|
var getProvider = (options = {}) => {
|
|
52476
52483
|
const ig = import_ignore2.default().add(options.excludeFiles ?? []);
|
|
52477
|
-
const memoryStore =
|
|
52478
|
-
const
|
|
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:
|
|
@@ -78996,21 +79076,90 @@ ${task}
|
|
|
78996
79076
|
${planSection}`;
|
|
78997
79077
|
}
|
|
78998
79078
|
var EPIC_ADD_TODO_ITEMS_SYSTEM_PROMPT = `Role: Task creation agent
|
|
78999
|
-
Goal:
|
|
79079
|
+
Goal: Parse a detailed epic plan and create todo items from the provided task breakdowns.
|
|
79000
79080
|
|
|
79001
79081
|
${TOOL_USAGE_INSTRUCTION}
|
|
79002
79082
|
|
|
79003
|
-
You are a task creation agent
|
|
79083
|
+
You are a task creation agent responsible for parsing a detailed epic plan and creating todo items that can be executed autonomously by an AI coding agent.
|
|
79004
79084
|
|
|
79005
|
-
## Your
|
|
79085
|
+
## Your Responsibility
|
|
79086
|
+
|
|
79087
|
+
Your goal is to create todo items that are:
|
|
79088
|
+
- **Specific and actionable**: Each item should be clear enough for an AI agent to implement without human intervention
|
|
79089
|
+
- **Well-documented**: Include detailed implementation guidance extracted from the plan
|
|
79090
|
+
- **Context-rich**: Specify relevant files that will be modified or created (as provided in the plan)
|
|
79091
|
+
- **Self-contained**: Each item should be implementable independently with the context from the plan
|
|
79092
|
+
|
|
79093
|
+
## Plan Structure Expectations
|
|
79094
|
+
|
|
79095
|
+
The plan you receive contains all necessary implementation details. You should NOT need to explore the codebase because:
|
|
79096
|
+
- Each task in the plan already includes specific file paths
|
|
79097
|
+
- Function and class names are specified in the plan
|
|
79098
|
+
- Implementation patterns and approaches are provided
|
|
79099
|
+
- Dependencies and imports are listed
|
|
79100
|
+
- Technical specifications are included
|
|
79101
|
+
- Integration points are documented
|
|
79102
|
+
|
|
79103
|
+
Your job is to extract these details from the plan and create todo items, not to research or discover them.
|
|
79104
|
+
|
|
79105
|
+
## Todo Item Requirements
|
|
79106
|
+
|
|
79107
|
+
For each task in the plan, create a todo item using the \`updateTodoItem\` tool with:
|
|
79108
|
+
|
|
79109
|
+
### 1. **title** (required)
|
|
79110
|
+
A concise, action-oriented task name that clearly states what needs to be done.
|
|
79111
|
+
|
|
79112
|
+
**Examples:**
|
|
79113
|
+
- ✅ "Implement user authentication with JWT tokens"
|
|
79114
|
+
- ✅ "Add validation middleware for API endpoints"
|
|
79115
|
+
- ❌ "Authentication" (too vague)
|
|
79116
|
+
|
|
79117
|
+
### 2. **description** (required)
|
|
79118
|
+
Detailed implementation instructions extracted from the plan. Include:
|
|
79119
|
+
|
|
79120
|
+
**Must extract from the plan:**
|
|
79121
|
+
- **Specific files to create or modify** with exact paths (as specified in the plan)
|
|
79122
|
+
- **Function/class names** to implement or modify (as specified in the plan)
|
|
79123
|
+
- **Implementation patterns** to follow (as referenced in the plan)
|
|
79124
|
+
- **Technical requirements** and constraints (as documented in the plan)
|
|
79125
|
+
- **Dependencies** and imports needed (as listed in the plan)
|
|
79126
|
+
- **Integration points** with existing code (as described in the plan)
|
|
79127
|
+
|
|
79128
|
+
**Example of extracting from a detailed plan task:**
|
|
79006
79129
|
|
|
79007
|
-
|
|
79008
|
-
|
|
79130
|
+
Plan task:
|
|
79131
|
+
\`\`\`
|
|
79132
|
+
- [ ] Add authentication middleware in \`src/middleware/auth.ts\` that verifies JWT tokens from Authorization header and attaches user object to \`req.user\`
|
|
79133
|
+
\`\`\`
|
|
79134
|
+
|
|
79135
|
+
Todo item description:
|
|
79136
|
+
\`\`\`
|
|
79137
|
+
Create a new authentication middleware in \`src/middleware/auth.ts\`:
|
|
79138
|
+
|
|
79139
|
+
1. Implement \`authenticateJWT\` function that:
|
|
79140
|
+
- Extracts JWT token from Authorization header
|
|
79141
|
+
- Verifies token (implementation details from plan)
|
|
79142
|
+
- Attaches user object to \`req.user\`
|
|
79143
|
+
- Returns 401 error for invalid/missing tokens
|
|
79144
|
+
|
|
79145
|
+
2. Export the middleware for use in route definitions
|
|
79146
|
+
\`\`\`
|
|
79147
|
+
|
|
79148
|
+
**Note:** All implementation details should come from the plan. Do not add information not present in the plan.
|
|
79009
79149
|
|
|
79010
79150
|
## Process
|
|
79011
79151
|
|
|
79012
|
-
1.
|
|
79013
|
-
2.
|
|
79152
|
+
1. **Read and parse the plan** provided in the user message to identify individual tasks
|
|
79153
|
+
2. **Parse the plan structure** to understand the task hierarchy and details
|
|
79154
|
+
3. **For each task in the plan:**
|
|
79155
|
+
a. Extract the specific files mentioned in the task
|
|
79156
|
+
b. Extract implementation patterns specified in the plan
|
|
79157
|
+
c. Create a detailed description using the plan's guidance
|
|
79158
|
+
d. Identify all relevant files from the plan
|
|
79159
|
+
e. Call \`updateTodoItem\` with operation='add', title and description
|
|
79160
|
+
|
|
79161
|
+
**Important:** Do NOT explore the codebase. All necessary information is already in the plan.
|
|
79162
|
+
|
|
79014
79163
|
`;
|
|
79015
79164
|
var CODER_SYSTEM_PROMPT = `Role: AI developer.
|
|
79016
79165
|
Goal: Implement the provided plan by writing and modifying code.
|
|
@@ -79690,10 +79839,10 @@ var ImplementOutputSchema = exports_external.object({
|
|
|
79690
79839
|
});
|
|
79691
79840
|
var codeWorkflow = async (input2, context) => {
|
|
79692
79841
|
const { logger, step, tools: tools2 } = context;
|
|
79693
|
-
const { task, files, mode = "interactive" } = input2;
|
|
79842
|
+
const { task, files, mode = "interactive", additionalTools, additionalInstructions } = input2;
|
|
79694
79843
|
const summaries = [];
|
|
79695
79844
|
logger.info(`
|
|
79696
|
-
|
|
79845
|
+
Phase 1: Creating implementation plan...
|
|
79697
79846
|
`);
|
|
79698
79847
|
const planResult = await step("plan", async () => {
|
|
79699
79848
|
return await planWorkflow({ task, files, mode: mode === "interactive" ? "confirm" : "noninteractive" }, context);
|
|
@@ -79704,7 +79853,7 @@ var codeWorkflow = async (input2, context) => {
|
|
|
79704
79853
|
}
|
|
79705
79854
|
const { plan, files: planFiles } = planResult;
|
|
79706
79855
|
logger.info(`
|
|
79707
|
-
|
|
79856
|
+
Phase 2: Implementing the plan...
|
|
79708
79857
|
`);
|
|
79709
79858
|
let implementPrompt = getImplementPrompt(plan);
|
|
79710
79859
|
if (planFiles && planFiles.length > 0) {
|
|
@@ -79752,6 +79901,9 @@ ${fileContentString}`;
|
|
|
79752
79901
|
if (mode === "interactive") {
|
|
79753
79902
|
agentTools.push(askFollowupQuestion_default);
|
|
79754
79903
|
}
|
|
79904
|
+
if (additionalTools) {
|
|
79905
|
+
agentTools.push(...additionalTools);
|
|
79906
|
+
}
|
|
79755
79907
|
const res = await step("implement", async () => {
|
|
79756
79908
|
const defaultContext = await getDefaultContext();
|
|
79757
79909
|
const memoryContext = await tools2.getMemoryContext();
|
|
@@ -79768,8 +79920,11 @@ ${memoryContext}`;
|
|
|
79768
79920
|
${memoryContext}`
|
|
79769
79921
|
});
|
|
79770
79922
|
}
|
|
79923
|
+
const systemPrompt = additionalInstructions ? `${CODER_SYSTEM_PROMPT}
|
|
79924
|
+
|
|
79925
|
+
${additionalInstructions}` : CODER_SYSTEM_PROMPT;
|
|
79771
79926
|
return await agentWorkflow({
|
|
79772
|
-
systemPrompt
|
|
79927
|
+
systemPrompt,
|
|
79773
79928
|
userMessage: [{ role: "user", content: userContent }],
|
|
79774
79929
|
tools: agentTools,
|
|
79775
79930
|
outputSchema: ImplementOutputSchema
|
|
@@ -79779,13 +79934,13 @@ ${memoryContext}`
|
|
|
79779
79934
|
const { summary, bailReason } = res.object;
|
|
79780
79935
|
if (bailReason) {
|
|
79781
79936
|
logger.error(`
|
|
79782
|
-
|
|
79937
|
+
Implementation failed: ${bailReason}
|
|
79783
79938
|
`);
|
|
79784
79939
|
return { success: false, reason: bailReason, summaries };
|
|
79785
79940
|
}
|
|
79786
79941
|
if (summary) {
|
|
79787
79942
|
logger.info(`
|
|
79788
|
-
|
|
79943
|
+
Implementation complete!
|
|
79789
79944
|
`);
|
|
79790
79945
|
summaries.push(summary);
|
|
79791
79946
|
logger.info(`Summary: ${summary}`);
|
|
@@ -79798,20 +79953,20 @@ ${memoryContext}`
|
|
|
79798
79953
|
});
|
|
79799
79954
|
} else {
|
|
79800
79955
|
logger.info(`
|
|
79801
|
-
|
|
79956
|
+
Implementation complete!
|
|
79802
79957
|
`);
|
|
79803
79958
|
}
|
|
79804
79959
|
} else if (res.type === "Exit" /* Exit */) {
|
|
79805
79960
|
logger.info(`
|
|
79806
|
-
|
|
79961
|
+
Implementation complete!
|
|
79807
79962
|
`);
|
|
79808
79963
|
} else {
|
|
79809
79964
|
logger.warn(`
|
|
79810
|
-
|
|
79965
|
+
Warning: Implementation failed. Please check the output for errors.
|
|
79811
79966
|
`, res);
|
|
79812
79967
|
}
|
|
79813
79968
|
logger.info(`
|
|
79814
|
-
|
|
79969
|
+
Phase 3: Checking for errors...
|
|
79815
79970
|
`);
|
|
79816
79971
|
const fixResult = await step("fix", async () => {
|
|
79817
79972
|
return await fixWorkflow({ interactive: false, task: input2.task }, context);
|
|
@@ -80221,8 +80376,67 @@ ${provider3.toUpperCase()}_API_KEY=${providerConfig.apiKey}`;
|
|
|
80221
80376
|
});
|
|
80222
80377
|
return { configPath };
|
|
80223
80378
|
};
|
|
80379
|
+
// src/workflows/epic-context.ts
|
|
80380
|
+
import { promises as fs3 } from "node:fs";
|
|
80381
|
+
var EPIC_CONTEXT_FILE = ".epic.yml";
|
|
80382
|
+
var EpicContextSchema = exports_external.object({
|
|
80383
|
+
task: exports_external.string().nullish(),
|
|
80384
|
+
plan: exports_external.string().nullish(),
|
|
80385
|
+
branchName: exports_external.string().nullish(),
|
|
80386
|
+
todos: exports_external.array(TodoItemSchema).nullish(),
|
|
80387
|
+
memory: exports_external.record(exports_external.string(), exports_external.string()).nullish()
|
|
80388
|
+
});
|
|
80389
|
+
var saveEpicContext = async (context) => {
|
|
80390
|
+
const yamlString = $stringify(context);
|
|
80391
|
+
await fs3.writeFile(EPIC_CONTEXT_FILE, yamlString, "utf-8");
|
|
80392
|
+
};
|
|
80393
|
+
var loadEpicContext = async () => {
|
|
80394
|
+
let fileContent;
|
|
80395
|
+
try {
|
|
80396
|
+
fileContent = await fs3.readFile(EPIC_CONTEXT_FILE, "utf-8");
|
|
80397
|
+
} catch {
|
|
80398
|
+
return {};
|
|
80399
|
+
}
|
|
80400
|
+
try {
|
|
80401
|
+
const loaded = $parse(fileContent);
|
|
80402
|
+
return EpicContextSchema.parse(loaded);
|
|
80403
|
+
} catch (error46) {
|
|
80404
|
+
console.error("Error parsing epic context file:", EPIC_CONTEXT_FILE, error46);
|
|
80405
|
+
return {};
|
|
80406
|
+
}
|
|
80407
|
+
};
|
|
80408
|
+
|
|
80409
|
+
class EpicMemoryStore {
|
|
80410
|
+
#context;
|
|
80411
|
+
constructor(context) {
|
|
80412
|
+
this.#context = context;
|
|
80413
|
+
}
|
|
80414
|
+
async read() {
|
|
80415
|
+
return this.#context.memory ?? {};
|
|
80416
|
+
}
|
|
80417
|
+
async write(data) {
|
|
80418
|
+
this.#context.memory = data;
|
|
80419
|
+
await saveEpicContext(this.#context);
|
|
80420
|
+
}
|
|
80421
|
+
}
|
|
80422
|
+
|
|
80423
|
+
class EpicTodoItemStore {
|
|
80424
|
+
#context;
|
|
80425
|
+
constructor(context) {
|
|
80426
|
+
this.#context = context;
|
|
80427
|
+
}
|
|
80428
|
+
async read() {
|
|
80429
|
+
return this.#context.todos ?? [];
|
|
80430
|
+
}
|
|
80431
|
+
async write(data) {
|
|
80432
|
+
this.#context.todos = data;
|
|
80433
|
+
await saveEpicContext(this.#context);
|
|
80434
|
+
}
|
|
80435
|
+
}
|
|
80436
|
+
|
|
80224
80437
|
// src/workflows/epic.workflow.ts
|
|
80225
80438
|
var MAX_REVIEW_RETRIES = 5;
|
|
80439
|
+
var TODO_HANDLING_INSTRUCTIONS = `If you discover that a task is larger than you thought, or that a new task is required, you can add a // TODO comment in the code and create a todo item for it. This will allow you to continue with the current task and address the larger issue later.`;
|
|
80226
80440
|
async function createPlan2(input2, context) {
|
|
80227
80441
|
const { task, plan, files, feedback } = input2;
|
|
80228
80442
|
const content = [{ type: "text", text: getPlanPrompt(task, plan) }];
|
|
@@ -80274,7 +80488,7 @@ ${feedback}`
|
|
|
80274
80488
|
}
|
|
80275
80489
|
async function createAndApprovePlan(task, context) {
|
|
80276
80490
|
const { logger, step, tools: tools2 } = context;
|
|
80277
|
-
logger.info(
|
|
80491
|
+
logger.info(`Phase 2: Creating high-level plan...
|
|
80278
80492
|
`);
|
|
80279
80493
|
let feedback;
|
|
80280
80494
|
let highLevelPlan;
|
|
@@ -80300,10 +80514,10 @@ async function createAndApprovePlan(task, context) {
|
|
|
80300
80514
|
}
|
|
80301
80515
|
return null;
|
|
80302
80516
|
}
|
|
80303
|
-
logger.info(
|
|
80517
|
+
logger.info(`Plan:
|
|
80304
80518
|
${result.plan}`);
|
|
80305
80519
|
if (result.branchName) {
|
|
80306
|
-
logger.info(
|
|
80520
|
+
logger.info(`Suggested branch name: ${result.branchName}`);
|
|
80307
80521
|
}
|
|
80308
80522
|
feedback = await tools2.input({ message: "Press Enter to approve the plan, or provide feedback to refine it." });
|
|
80309
80523
|
if (feedback.trim() === "") {
|
|
@@ -80324,48 +80538,46 @@ ${result.plan}`);
|
|
|
80324
80538
|
return null;
|
|
80325
80539
|
}
|
|
80326
80540
|
if (!branchName) {
|
|
80327
|
-
logger.error("
|
|
80541
|
+
logger.error("Error: No branch name was generated from the plan. Exiting.");
|
|
80328
80542
|
return null;
|
|
80329
80543
|
}
|
|
80330
|
-
logger.info(
|
|
80544
|
+
logger.info(`High-level plan approved.
|
|
80331
80545
|
`);
|
|
80332
80546
|
return { plan: highLevelPlan, branchName };
|
|
80333
80547
|
}
|
|
80334
80548
|
async function createFeatureBranch(branchName, context) {
|
|
80335
80549
|
const { logger, step, tools: tools2 } = context;
|
|
80336
|
-
logger.info(
|
|
80550
|
+
logger.info(`Phase 3: Creating/switching to feature branch...
|
|
80337
80551
|
`);
|
|
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;
|
|
80552
|
+
const branchExistsResult = await step("checkBranchExists", async () => await tools2.executeCommand({ command: "git", args: ["rev-parse", "--verify", branchName] }));
|
|
80553
|
+
if (branchExistsResult.exitCode === 0) {
|
|
80554
|
+
const currentBranchResult = await step("getCurrentBranch", async () => tools2.executeCommand({ command: "git", args: ["rev-parse", "--abbrev-ref", "HEAD"] }));
|
|
80555
|
+
const currentBranch = currentBranchResult.stdout.trim();
|
|
80556
|
+
if (currentBranch !== branchName) {
|
|
80557
|
+
logger.info(`Branch '${branchName}' already exists. Switching to it...`);
|
|
80558
|
+
const checkoutResult = await step("checkoutBranch", async () => await tools2.executeCommand({ command: "git", args: ["checkout", branchName] }));
|
|
80559
|
+
if (checkoutResult.exitCode !== 0) {
|
|
80560
|
+
logger.error(`Error: Failed to switch to branch '${branchName}'. Git command failed.`);
|
|
80561
|
+
return { success: false, branchName: null };
|
|
80354
80562
|
}
|
|
80355
|
-
|
|
80563
|
+
} else {
|
|
80564
|
+
logger.info(`Already on branch '${branchName}'.`);
|
|
80565
|
+
}
|
|
80566
|
+
} else {
|
|
80567
|
+
logger.info(`Creating new branch '${branchName}'...`);
|
|
80568
|
+
const createBranchResult = await step("createBranch", async () => await tools2.executeCommand({ command: "git", args: ["checkout", "-b", branchName] }));
|
|
80569
|
+
if (createBranchResult.exitCode !== 0) {
|
|
80570
|
+
logger.error(`Error: Failed to create branch '${branchName}'. Git command failed.`);
|
|
80571
|
+
return { success: false, branchName: null };
|
|
80356
80572
|
}
|
|
80357
80573
|
}
|
|
80358
|
-
|
|
80359
|
-
logger.info(`Branch name '${branchName}' was taken. Using '${finalBranchName}' instead.`);
|
|
80360
|
-
}
|
|
80361
|
-
await step("createBranch", async () => await tools2.executeCommand({ command: "git", args: ["checkout", "-b", finalBranchName] }));
|
|
80362
|
-
logger.info(`✅ Branch '${finalBranchName}' created.
|
|
80574
|
+
logger.info(`Successfully on branch '${branchName}'.
|
|
80363
80575
|
`);
|
|
80364
|
-
return { success: true, branchName
|
|
80576
|
+
return { success: true, branchName };
|
|
80365
80577
|
}
|
|
80366
80578
|
async function addTodoItemsFromPlan(plan, context) {
|
|
80367
80579
|
const { logger, step, tools: tools2 } = context;
|
|
80368
|
-
logger.info(
|
|
80580
|
+
logger.info(`Phase 4: Creating todo items from plan...
|
|
80369
80581
|
`);
|
|
80370
80582
|
await step("add-todo-items", async () => {
|
|
80371
80583
|
await agentWorkflow({
|
|
@@ -80377,28 +80589,40 @@ ${plan}</plan>` }],
|
|
|
80377
80589
|
}, context);
|
|
80378
80590
|
});
|
|
80379
80591
|
const todos = await tools2.listTodoItems({});
|
|
80380
|
-
logger.info(
|
|
80592
|
+
logger.info(`Created ${todos.length} todo items.
|
|
80381
80593
|
`);
|
|
80382
80594
|
}
|
|
80383
|
-
async function runPreflightChecks(context) {
|
|
80595
|
+
async function runPreflightChecks(epicContext, context) {
|
|
80384
80596
|
const { logger, step, tools: tools2 } = context;
|
|
80385
|
-
logger.info(
|
|
80597
|
+
logger.info(`Phase 1: Running pre-flight checks...
|
|
80386
80598
|
`);
|
|
80387
80599
|
const gitCheckResult = await step("gitCheck", async () => tools2.executeCommand({ command: "git", args: ["rev-parse", "--git-dir"] }));
|
|
80388
80600
|
if (gitCheckResult.exitCode !== 0) {
|
|
80389
|
-
logger.error("
|
|
80390
|
-
logger.info("
|
|
80391
|
-
return
|
|
80601
|
+
logger.error("Error: Git is not initialized in this directory. Please run `git init` first.");
|
|
80602
|
+
logger.info("Suggestion: Run `git init` to initialize a git repository.\n");
|
|
80603
|
+
return false;
|
|
80604
|
+
}
|
|
80605
|
+
if (epicContext.plan) {
|
|
80606
|
+
logger.info("Found an existing `.epic.yml` file.");
|
|
80607
|
+
if (epicContext.branchName) {
|
|
80608
|
+
const currentBranchResult = await step("getCurrentBranch", async () => tools2.executeCommand({ command: "git", args: ["rev-parse", "--abbrev-ref", "HEAD"] }));
|
|
80609
|
+
const currentBranch = currentBranchResult.stdout.trim();
|
|
80610
|
+
if (currentBranch !== epicContext.branchName) {
|
|
80611
|
+
throw new Error(`You are on branch '${currentBranch}' but the epic was started on branch '${epicContext.branchName}'. Please switch to the correct branch to resume.`);
|
|
80612
|
+
}
|
|
80613
|
+
}
|
|
80614
|
+
logger.info("Resuming previous epic session.");
|
|
80615
|
+
return true;
|
|
80392
80616
|
}
|
|
80393
80617
|
const gitStatusResult = await step("gitStatus", async () => tools2.executeCommand({ command: "git status --porcelain", shell: true }));
|
|
80394
80618
|
if (gitStatusResult.stdout.trim() !== "") {
|
|
80395
|
-
logger.error("
|
|
80396
|
-
logger.info("
|
|
80397
|
-
return
|
|
80619
|
+
logger.error("Error: Your working directory is not clean. Please stash or commit your changes before running the epic workflow.");
|
|
80620
|
+
logger.info("Suggestion: Run `git add .` and `git commit` to clean your working directory, or `git stash` to temporarily save changes.\n");
|
|
80621
|
+
return false;
|
|
80398
80622
|
}
|
|
80399
|
-
logger.info(
|
|
80623
|
+
logger.info(`Pre-flight checks passed.
|
|
80400
80624
|
`);
|
|
80401
|
-
return
|
|
80625
|
+
return true;
|
|
80402
80626
|
}
|
|
80403
80627
|
async function performReviewAndFixCycle(iterationCount, taskItem, highLevelPlan, context) {
|
|
80404
80628
|
const { logger, step, tools: tools2 } = context;
|
|
@@ -80406,12 +80630,12 @@ async function performReviewAndFixCycle(iterationCount, taskItem, highLevelPlan,
|
|
|
80406
80630
|
const diffResult = await tools2.executeCommand({ command: "git", args: ["diff", "--name-status", "HEAD~1", "HEAD"] });
|
|
80407
80631
|
const changedFiles = parseGitDiffNameStatus(diffResult.stdout);
|
|
80408
80632
|
if (changedFiles.length === 0) {
|
|
80409
|
-
logger.info(
|
|
80633
|
+
logger.info(`No files were changed. Skipping review.
|
|
80410
80634
|
`);
|
|
80411
80635
|
return { passed: true };
|
|
80412
80636
|
}
|
|
80413
80637
|
logger.info(`
|
|
80414
|
-
|
|
80638
|
+
Review iteration ${i + 1}/${MAX_REVIEW_RETRIES}`);
|
|
80415
80639
|
logger.info(` Changed files: ${changedFiles.length}`);
|
|
80416
80640
|
const changeInfo = {
|
|
80417
80641
|
commitRange: "HEAD~1...HEAD",
|
|
@@ -80432,16 +80656,16 @@ ${formatReviewToolInput(changeInfo)}`;
|
|
|
80432
80656
|
}, context);
|
|
80433
80657
|
});
|
|
80434
80658
|
if (reviewAgentResult.type !== "Exit" /* Exit */) {
|
|
80435
|
-
logger.error(
|
|
80659
|
+
logger.error(`Review agent failed with status: ${reviewAgentResult.type}.`);
|
|
80436
80660
|
break;
|
|
80437
80661
|
}
|
|
80438
80662
|
const reviewResult = reviewAgentResult.object;
|
|
80439
80663
|
if (!reviewResult || !reviewResult.specificReviews || reviewResult.specificReviews.length === 0) {
|
|
80440
|
-
logger.info(
|
|
80664
|
+
logger.info(`Review passed. No issues found.
|
|
80441
80665
|
`);
|
|
80442
80666
|
return { passed: true };
|
|
80443
80667
|
}
|
|
80444
|
-
logger.warn(
|
|
80668
|
+
logger.warn(`Warning: Review found ${reviewResult.specificReviews.length} issue(s). Attempting to fix...
|
|
80445
80669
|
`);
|
|
80446
80670
|
for (const [idx, review] of reviewResult.specificReviews.entries()) {
|
|
80447
80671
|
logger.warn(` ${idx + 1}. ${review.file}:${review.lines}`);
|
|
@@ -80462,7 +80686,12 @@ After an initial implementation, a review found the following issues. Please fix
|
|
|
80462
80686
|
|
|
80463
80687
|
${reviewSummary}`;
|
|
80464
80688
|
await step(`fix-${iterationCount}-${i}`, async () => {
|
|
80465
|
-
await codeWorkflow({
|
|
80689
|
+
await codeWorkflow({
|
|
80690
|
+
task: fixTask,
|
|
80691
|
+
mode: "noninteractive",
|
|
80692
|
+
additionalInstructions: TODO_HANDLING_INSTRUCTIONS,
|
|
80693
|
+
additionalTools: [getTodoItem_default, listTodoItems_default, updateTodoItem_default]
|
|
80694
|
+
}, context);
|
|
80466
80695
|
});
|
|
80467
80696
|
await step(`commit-fix-${iterationCount}-${i}`, async () => {
|
|
80468
80697
|
await tools2.executeCommand({ command: "git", args: ["add", "."] });
|
|
@@ -80470,7 +80699,7 @@ ${reviewSummary}`;
|
|
|
80470
80699
|
});
|
|
80471
80700
|
if (i === MAX_REVIEW_RETRIES - 1) {
|
|
80472
80701
|
logger.error(`
|
|
80473
|
-
|
|
80702
|
+
Max retries (${MAX_REVIEW_RETRIES}) reached. Moving to the next task, but issues might remain.
|
|
80474
80703
|
`);
|
|
80475
80704
|
return { passed: false };
|
|
80476
80705
|
}
|
|
@@ -80480,7 +80709,7 @@ ${reviewSummary}`;
|
|
|
80480
80709
|
async function runImplementationLoop(context, highLevelPlan) {
|
|
80481
80710
|
const { logger, step, tools: tools2 } = context;
|
|
80482
80711
|
const commitMessages = [];
|
|
80483
|
-
logger.info(
|
|
80712
|
+
logger.info(`Phase 5: Iterative Implementation Loop...
|
|
80484
80713
|
`);
|
|
80485
80714
|
logger.info(`${"=".repeat(80)}
|
|
80486
80715
|
`);
|
|
@@ -80502,9 +80731,9 @@ async function runImplementationLoop(context, highLevelPlan) {
|
|
|
80502
80731
|
iterationCount++;
|
|
80503
80732
|
const taskStartTime = Date.now();
|
|
80504
80733
|
logger.info(`
|
|
80505
|
-
${"
|
|
80506
|
-
logger.info(
|
|
80507
|
-
logger.info(`${"
|
|
80734
|
+
${"-".repeat(80)}`);
|
|
80735
|
+
logger.info(`Iteration ${iterationCount}`);
|
|
80736
|
+
logger.info(`${"-".repeat(80)}`);
|
|
80508
80737
|
logger.info(`${nextTask}
|
|
80509
80738
|
`);
|
|
80510
80739
|
await step(`task-${iterationCount}`, async () => {
|
|
@@ -80518,7 +80747,12 @@ Your current task is to implement this specific item:
|
|
|
80518
80747
|
${nextTask}
|
|
80519
80748
|
|
|
80520
80749
|
Focus only on this item, but use the plan for context.`;
|
|
80521
|
-
return await codeWorkflow({
|
|
80750
|
+
return await codeWorkflow({
|
|
80751
|
+
task: taskWithContext,
|
|
80752
|
+
mode: "noninteractive",
|
|
80753
|
+
additionalInstructions: TODO_HANDLING_INSTRUCTIONS,
|
|
80754
|
+
additionalTools: [getTodoItem_default, listTodoItems_default, updateTodoItem_default]
|
|
80755
|
+
}, context);
|
|
80522
80756
|
});
|
|
80523
80757
|
const commitMessage = `feat: ${nextTask}`;
|
|
80524
80758
|
await step(`commit-initial-${iterationCount}`, async () => {
|
|
@@ -80530,9 +80764,9 @@ Focus only on this item, but use the plan for context.`;
|
|
|
80530
80764
|
const taskElapsed = Date.now() - taskStartTime;
|
|
80531
80765
|
const taskElapsedTime = formatElapsedTime(taskElapsed);
|
|
80532
80766
|
if (reviewPassed) {
|
|
80533
|
-
logger.info(
|
|
80767
|
+
logger.info(`Iteration ${iterationCount} completed successfully (${taskElapsedTime})`);
|
|
80534
80768
|
} else {
|
|
80535
|
-
logger.warn(
|
|
80769
|
+
logger.warn(`Warning: Iteration ${iterationCount} completed with potential issues (${taskElapsedTime})`);
|
|
80536
80770
|
}
|
|
80537
80771
|
await step(`update-task-status-${iterationCount}`, async () => {
|
|
80538
80772
|
if (!nextTaskId) {
|
|
@@ -80563,17 +80797,17 @@ Focus only on this item, but use the plan for context.`;
|
|
|
80563
80797
|
progressMessage = `Iteration ${iterationCount} completed`;
|
|
80564
80798
|
}
|
|
80565
80799
|
logger.info(`
|
|
80566
|
-
|
|
80800
|
+
Progress: ${progressMessage}`);
|
|
80567
80801
|
if (isComplete) {
|
|
80568
|
-
logger.info(
|
|
80802
|
+
logger.info(`All tasks complete!
|
|
80569
80803
|
`);
|
|
80570
80804
|
break;
|
|
80571
80805
|
}
|
|
80572
80806
|
if (nextTask) {
|
|
80573
|
-
logger.info(
|
|
80807
|
+
logger.info(`Next task: ${nextTask}
|
|
80574
80808
|
`);
|
|
80575
80809
|
}
|
|
80576
|
-
logger.info(`${"
|
|
80810
|
+
logger.info(`${"-".repeat(80)}
|
|
80577
80811
|
`);
|
|
80578
80812
|
}
|
|
80579
80813
|
return commitMessages;
|
|
@@ -80585,7 +80819,7 @@ Phase 6: Final Review and Fixup...
|
|
|
80585
80819
|
`);
|
|
80586
80820
|
const ghCheckResult = await tools2.executeCommand({ command: "gh", args: ["--version"] });
|
|
80587
80821
|
if (ghCheckResult.exitCode !== 0) {
|
|
80588
|
-
logger.warn("
|
|
80822
|
+
logger.warn("Warning: GitHub CLI (gh) is not installed. Skipping final review step. Please install it from https://cli.github.com/ to enable final reviews.");
|
|
80589
80823
|
return { passed: true };
|
|
80590
80824
|
}
|
|
80591
80825
|
const defaultBranchResult = await tools2.executeCommand({
|
|
@@ -80596,7 +80830,7 @@ Phase 6: Final Review and Fixup...
|
|
|
80596
80830
|
const currentBranchResult = await tools2.executeCommand({ command: "git", args: ["rev-parse", "--abbrev-ref", "HEAD"] });
|
|
80597
80831
|
const currentBranch = currentBranchResult.stdout.trim();
|
|
80598
80832
|
if (currentBranch === defaultBranch) {
|
|
80599
|
-
logger.info(
|
|
80833
|
+
logger.info(`You are on the default branch ('${defaultBranch}'). No final review needed.`);
|
|
80600
80834
|
return { passed: true };
|
|
80601
80835
|
}
|
|
80602
80836
|
const commitRange = `${defaultBranch}...${currentBranch}`;
|
|
@@ -80604,12 +80838,12 @@ Phase 6: Final Review and Fixup...
|
|
|
80604
80838
|
const diffResult = await tools2.executeCommand({ command: "git", args: ["diff", "--name-status", commitRange] });
|
|
80605
80839
|
const changedFiles = parseGitDiffNameStatus(diffResult.stdout);
|
|
80606
80840
|
if (changedFiles.length === 0) {
|
|
80607
|
-
logger.info(
|
|
80841
|
+
logger.info(`No files have been changed in this branch. Skipping final review.
|
|
80608
80842
|
`);
|
|
80609
80843
|
return { passed: true };
|
|
80610
80844
|
}
|
|
80611
80845
|
logger.info(`
|
|
80612
|
-
|
|
80846
|
+
Final review iteration ${i + 1}/${MAX_REVIEW_RETRIES}`);
|
|
80613
80847
|
logger.info(` Changed files: ${changedFiles.length}`);
|
|
80614
80848
|
const changeInfo = {
|
|
80615
80849
|
commitRange,
|
|
@@ -80630,16 +80864,16 @@ ${formatReviewToolInput(changeInfo)}`;
|
|
|
80630
80864
|
}, context);
|
|
80631
80865
|
});
|
|
80632
80866
|
if (reviewAgentResult.type !== "Exit" /* Exit */) {
|
|
80633
|
-
logger.error(
|
|
80867
|
+
logger.error(`Review agent failed with status: ${reviewAgentResult.type}.`);
|
|
80634
80868
|
break;
|
|
80635
80869
|
}
|
|
80636
80870
|
const reviewResult = reviewAgentResult.object;
|
|
80637
80871
|
if (!reviewResult || !reviewResult.specificReviews || reviewResult.specificReviews.length === 0) {
|
|
80638
|
-
logger.info(
|
|
80872
|
+
logger.info(`Final review passed. No issues found.
|
|
80639
80873
|
`);
|
|
80640
80874
|
return { passed: true };
|
|
80641
80875
|
}
|
|
80642
|
-
logger.warn(
|
|
80876
|
+
logger.warn(`Warning: Final review found ${reviewResult.specificReviews.length} issue(s). Attempting to fix...
|
|
80643
80877
|
`);
|
|
80644
80878
|
for (const [idx, review] of reviewResult.specificReviews.entries()) {
|
|
80645
80879
|
logger.warn(` ${idx + 1}. ${review.file}:${review.lines}`);
|
|
@@ -80659,7 +80893,12 @@ A final review of all the changes in the branch found the following issues. Plea
|
|
|
80659
80893
|
|
|
80660
80894
|
${reviewSummary}`;
|
|
80661
80895
|
await step(`final-fix-${i}`, async () => {
|
|
80662
|
-
await codeWorkflow({
|
|
80896
|
+
await codeWorkflow({
|
|
80897
|
+
task: fixTask,
|
|
80898
|
+
mode: "noninteractive",
|
|
80899
|
+
additionalInstructions: TODO_HANDLING_INSTRUCTIONS,
|
|
80900
|
+
additionalTools: [getTodoItem_default, listTodoItems_default, updateTodoItem_default]
|
|
80901
|
+
}, context);
|
|
80663
80902
|
});
|
|
80664
80903
|
await step(`commit-final-fix-${i}`, async () => {
|
|
80665
80904
|
await tools2.executeCommand({ command: "git", args: ["add", "."] });
|
|
@@ -80667,7 +80906,7 @@ ${reviewSummary}`;
|
|
|
80667
80906
|
});
|
|
80668
80907
|
if (i === MAX_REVIEW_RETRIES - 1) {
|
|
80669
80908
|
logger.error(`
|
|
80670
|
-
|
|
80909
|
+
Max retries (${MAX_REVIEW_RETRIES}) reached for final review. Issues might remain.
|
|
80671
80910
|
`);
|
|
80672
80911
|
return { passed: false };
|
|
80673
80912
|
}
|
|
@@ -80675,57 +80914,85 @@ ${reviewSummary}`;
|
|
|
80675
80914
|
return { passed: false };
|
|
80676
80915
|
}
|
|
80677
80916
|
var epicWorkflow = async (input2, context) => {
|
|
80678
|
-
const { logger } = context;
|
|
80679
|
-
const { task } = input2;
|
|
80917
|
+
const { logger, tools: tools2 } = context;
|
|
80918
|
+
const { task, epicContext = {} } = input2;
|
|
80680
80919
|
const workflowStartTime = Date.now();
|
|
80681
|
-
let branchName = "";
|
|
80682
80920
|
if (!task || task.trim() === "") {
|
|
80683
|
-
logger.error("
|
|
80921
|
+
logger.error("Error: Task cannot be empty. Please provide a valid task description.");
|
|
80684
80922
|
return;
|
|
80685
80923
|
}
|
|
80924
|
+
if (!epicContext.task) {
|
|
80925
|
+
epicContext.task = task;
|
|
80926
|
+
}
|
|
80686
80927
|
try {
|
|
80687
|
-
const preflightResult = await runPreflightChecks(context);
|
|
80688
|
-
if (!preflightResult
|
|
80928
|
+
const preflightResult = await runPreflightChecks(epicContext, context);
|
|
80929
|
+
if (!preflightResult) {
|
|
80930
|
+
return;
|
|
80931
|
+
}
|
|
80932
|
+
if (!epicContext.plan) {
|
|
80933
|
+
if (!epicContext.task) {
|
|
80934
|
+
logger.error("Error: Task is missing in epic context. Exiting.");
|
|
80935
|
+
return;
|
|
80936
|
+
}
|
|
80937
|
+
const planResult = await createAndApprovePlan(epicContext.task, context);
|
|
80938
|
+
if (!planResult)
|
|
80939
|
+
return;
|
|
80940
|
+
epicContext.plan = planResult.plan;
|
|
80941
|
+
epicContext.branchName = planResult.branchName;
|
|
80942
|
+
await saveEpicContext(epicContext);
|
|
80943
|
+
}
|
|
80944
|
+
if (!epicContext.plan) {
|
|
80945
|
+
logger.error("Error: Plan is missing after planning phase. Exiting.");
|
|
80689
80946
|
return;
|
|
80690
80947
|
}
|
|
80691
|
-
|
|
80692
|
-
|
|
80948
|
+
if (!epicContext.branchName) {
|
|
80949
|
+
logger.error("Error: Branch name is missing after planning phase. Exiting.");
|
|
80693
80950
|
return;
|
|
80694
80951
|
}
|
|
80695
|
-
const
|
|
80696
|
-
|
|
80697
|
-
const branchResult = await createFeatureBranch(branchName, context);
|
|
80698
|
-
if (!branchResult.success || !branchResult.branchName) {
|
|
80952
|
+
const branchResult = await createFeatureBranch(epicContext.branchName, context);
|
|
80953
|
+
if (!branchResult.success || !branchResult.branchName)
|
|
80699
80954
|
return;
|
|
80955
|
+
if (epicContext.branchName !== branchResult.branchName) {
|
|
80956
|
+
epicContext.branchName = branchResult.branchName;
|
|
80957
|
+
await saveEpicContext(epicContext);
|
|
80958
|
+
}
|
|
80959
|
+
const todos = await tools2.listTodoItems({});
|
|
80960
|
+
if (todos.length === 0) {
|
|
80961
|
+
await addTodoItemsFromPlan(epicContext.plan, context);
|
|
80962
|
+
}
|
|
80963
|
+
const commitMessages = await runImplementationLoop(context, epicContext.plan);
|
|
80964
|
+
await performFinalReviewAndFix(context, epicContext.plan);
|
|
80965
|
+
await tools2.executeCommand({ command: "git", args: ["rm", "-f", ".epic.yml"] });
|
|
80966
|
+
const statusResult = await tools2.executeCommand({
|
|
80967
|
+
command: "git",
|
|
80968
|
+
args: ["status", "--porcelain", "--", ".epic.yml"]
|
|
80969
|
+
});
|
|
80970
|
+
if (statusResult.stdout.trim() !== "") {
|
|
80971
|
+
await tools2.executeCommand({ command: "git", args: ["commit", "-m", "chore: remove .epic.yml", "--", ".epic.yml"] });
|
|
80972
|
+
logger.info("Cleaned up .epic.yml file.");
|
|
80700
80973
|
}
|
|
80701
|
-
branchName = branchResult.branchName;
|
|
80702
|
-
await addTodoItemsFromPlan(highLevelPlan, context);
|
|
80703
|
-
const commitMessages = await runImplementationLoop(context, highLevelPlan);
|
|
80704
|
-
await performFinalReviewAndFix(context, highLevelPlan);
|
|
80705
80974
|
const totalElapsed = Date.now() - workflowStartTime;
|
|
80706
80975
|
const totalElapsedTime = formatElapsedTime(totalElapsed);
|
|
80707
|
-
const iterationCount = commitMessages.length;
|
|
80708
80976
|
logger.info(`
|
|
80709
80977
|
${"=".repeat(80)}`);
|
|
80710
|
-
logger.info("
|
|
80978
|
+
logger.info("Epic Workflow Complete!");
|
|
80711
80979
|
logger.info(`${"=".repeat(80)}`);
|
|
80712
80980
|
logger.info(`
|
|
80713
|
-
|
|
80714
|
-
logger.info(`
|
|
80715
|
-
logger.info(` Total commits: ${commitMessages.length}`);
|
|
80716
|
-
logger.info(` Branch: ${branchName}`);
|
|
80981
|
+
Summary:`);
|
|
80982
|
+
logger.info(` Branch: ${epicContext.branchName}`);
|
|
80717
80983
|
logger.info(` Total time: ${totalElapsedTime}`);
|
|
80718
|
-
logger.info(
|
|
80984
|
+
logger.info(` Total commits: ${commitMessages.length}`);
|
|
80985
|
+
logger.info("Commits created:");
|
|
80719
80986
|
for (const [idx, msg] of commitMessages.entries()) {
|
|
80720
80987
|
logger.info(` ${idx + 1}. ${msg}`);
|
|
80721
80988
|
}
|
|
80722
80989
|
} catch (error46) {
|
|
80723
80990
|
logger.error(`
|
|
80724
|
-
|
|
80725
|
-
if (branchName) {
|
|
80991
|
+
Epic workflow failed: ${error46 instanceof Error ? error46.message : String(error46)}`);
|
|
80992
|
+
if (epicContext?.branchName) {
|
|
80726
80993
|
logger.info(`
|
|
80727
|
-
Branch '${branchName}' was created but work is incomplete.`);
|
|
80728
|
-
logger.info(`To cleanup: git checkout <previous-branch> && git branch -D ${branchName}
|
|
80994
|
+
Branch '${epicContext.branchName}' was created but work is incomplete.`);
|
|
80995
|
+
logger.info(`To cleanup: git checkout <previous-branch> && git branch -D ${epicContext.branchName}
|
|
80729
80996
|
`);
|
|
80730
80997
|
}
|
|
80731
80998
|
throw error46;
|
|
@@ -80740,7 +81007,7 @@ var taskWorkflow = async (input2, context) => {
|
|
|
80740
81007
|
const { logger, step } = context;
|
|
80741
81008
|
const { task } = input2;
|
|
80742
81009
|
logger.info(`
|
|
80743
|
-
|
|
81010
|
+
Running generic agent...
|
|
80744
81011
|
`);
|
|
80745
81012
|
await step("agent", async () => {
|
|
80746
81013
|
const defaultContext = await getDefaultContext();
|
|
@@ -80765,7 +81032,7 @@ ${defaultContext}`;
|
|
|
80765
81032
|
}, context);
|
|
80766
81033
|
});
|
|
80767
81034
|
logger.info(`
|
|
80768
|
-
|
|
81035
|
+
Agent finished!
|
|
80769
81036
|
`);
|
|
80770
81037
|
return { success: true };
|
|
80771
81038
|
};
|
|
@@ -80778,7 +81045,7 @@ var metaWorkflow = async (input2, context) => {
|
|
|
80778
81045
|
const { task } = input2;
|
|
80779
81046
|
const { logger } = context;
|
|
80780
81047
|
logger.info(`
|
|
80781
|
-
|
|
81048
|
+
Deciding which workflow to use for task...
|
|
80782
81049
|
`);
|
|
80783
81050
|
const result = await agentWorkflow({
|
|
80784
81051
|
systemPrompt: META_SYSTEM_PROMPT,
|
|
@@ -80799,7 +81066,7 @@ var metaWorkflow = async (input2, context) => {
|
|
|
80799
81066
|
throw new Error("Could not decide which workflow to run.");
|
|
80800
81067
|
}
|
|
80801
81068
|
logger.info(`
|
|
80802
|
-
|
|
81069
|
+
Decision: Using '${decision.workflow}' workflow.`);
|
|
80803
81070
|
switch (decision.workflow) {
|
|
80804
81071
|
case "code":
|
|
80805
81072
|
await codeWorkflow({ task }, context);
|
|
@@ -81019,30 +81286,32 @@ var commitCommand = new Command("commit").description("Create a commit with AI-g
|
|
|
81019
81286
|
async function runEpic(task2, _options, command) {
|
|
81020
81287
|
let taskInput = task2;
|
|
81021
81288
|
if (!taskInput) {
|
|
81022
|
-
|
|
81023
|
-
|
|
81024
|
-
|
|
81025
|
-
});
|
|
81026
|
-
} catch (error46) {
|
|
81027
|
-
if (error46 instanceof Error && error46.name === "ExitPromptError") {
|
|
81028
|
-
return;
|
|
81029
|
-
}
|
|
81030
|
-
throw error46;
|
|
81289
|
+
taskInput = await getUserInput("What epic or large feature do you want to implement?");
|
|
81290
|
+
if (!taskInput) {
|
|
81291
|
+
return;
|
|
81031
81292
|
}
|
|
81032
81293
|
}
|
|
81033
81294
|
if (!taskInput) {
|
|
81034
81295
|
console.error("No task provided. Aborting.");
|
|
81035
81296
|
return;
|
|
81036
81297
|
}
|
|
81298
|
+
const ctx = await loadEpicContext();
|
|
81037
81299
|
const workflowInput = {
|
|
81038
|
-
task: taskInput
|
|
81300
|
+
task: taskInput,
|
|
81301
|
+
epicContext: ctx
|
|
81039
81302
|
};
|
|
81040
81303
|
const globalOpts = (command.parent ?? command).opts();
|
|
81041
81304
|
const { verbose, yes } = globalOpts;
|
|
81042
81305
|
const logger = createLogger({
|
|
81043
81306
|
verbose
|
|
81044
81307
|
});
|
|
81045
|
-
await runWorkflow(epicWorkflow, workflowInput, {
|
|
81308
|
+
await runWorkflow(epicWorkflow, workflowInput, {
|
|
81309
|
+
commandName: "epic",
|
|
81310
|
+
command,
|
|
81311
|
+
logger,
|
|
81312
|
+
yes,
|
|
81313
|
+
getProvider: (opt) => getProvider({ ...opt, todoItemStore: new EpicTodoItemStore(ctx), memoryStore: new EpicMemoryStore(ctx) })
|
|
81314
|
+
});
|
|
81046
81315
|
}
|
|
81047
81316
|
var epicCommand = new Command("epic").description("Orchestrates a large feature or epic, breaking it down into smaller tasks.").argument("[task]", "The epic to plan and implement.").action(runEpic);
|
|
81048
81317
|
|