@locusai/sdk 0.9.18 → 0.10.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/dist/agent/git-workflow.d.ts +44 -0
  2. package/dist/agent/git-workflow.d.ts.map +1 -0
  3. package/dist/agent/index.d.ts +2 -0
  4. package/dist/agent/index.d.ts.map +1 -1
  5. package/dist/agent/reviewer-worker.d.ts.map +1 -1
  6. package/dist/agent/worker-cli.d.ts +6 -0
  7. package/dist/agent/worker-cli.d.ts.map +1 -0
  8. package/dist/agent/worker-types.d.ts +44 -0
  9. package/dist/agent/worker-types.d.ts.map +1 -0
  10. package/dist/agent/worker.d.ts +12 -48
  11. package/dist/agent/worker.d.ts.map +1 -1
  12. package/dist/agent/worker.js +1026 -847
  13. package/dist/ai/claude-runner.d.ts.map +1 -1
  14. package/dist/index-node.d.ts +1 -1
  15. package/dist/index-node.d.ts.map +1 -1
  16. package/dist/index-node.js +1660 -1133
  17. package/dist/index.js +363 -316
  18. package/dist/orchestrator/agent-pool.d.ts +59 -0
  19. package/dist/orchestrator/agent-pool.d.ts.map +1 -0
  20. package/dist/orchestrator/execution.d.ts +55 -0
  21. package/dist/orchestrator/execution.d.ts.map +1 -0
  22. package/dist/orchestrator/index.d.ts +91 -0
  23. package/dist/orchestrator/index.d.ts.map +1 -0
  24. package/dist/orchestrator/tier-merge.d.ts +50 -0
  25. package/dist/orchestrator/tier-merge.d.ts.map +1 -0
  26. package/dist/orchestrator/types.d.ts +45 -0
  27. package/dist/orchestrator/types.d.ts.map +1 -0
  28. package/dist/planning/agents/cross-task-reviewer.d.ts.map +1 -1
  29. package/dist/planning/agents/sprint-organizer.d.ts.map +1 -1
  30. package/dist/planning/plan-manager.d.ts.map +1 -1
  31. package/dist/planning/sprint-plan.d.ts +2 -0
  32. package/dist/planning/sprint-plan.d.ts.map +1 -1
  33. package/dist/project/knowledge-base.d.ts +4 -5
  34. package/dist/project/knowledge-base.d.ts.map +1 -1
  35. package/dist/utils/resolve-bin.d.ts +3 -0
  36. package/dist/utils/resolve-bin.d.ts.map +1 -1
  37. package/dist/worktree/worktree-manager.d.ts.map +1 -1
  38. package/package.json +2 -2
  39. package/dist/orchestrator.d.ts +0 -124
  40. package/dist/orchestrator.d.ts.map +0 -1
@@ -38,42 +38,26 @@ var __export = (target, all) => {
38
38
  set: (newValue) => all[name] = () => newValue
39
39
  });
40
40
  };
41
-
42
- // src/index.ts
43
- var exports_src = {};
44
- __export(exports_src, {
45
- WorkspacesModule: () => WorkspacesModule,
46
- TasksModule: () => TasksModule,
47
- SprintsModule: () => SprintsModule,
48
- OrganizationsModule: () => OrganizationsModule,
49
- LocusEvent: () => LocusEvent,
50
- LocusEmitter: () => LocusEmitter,
51
- LocusClient: () => LocusClient,
52
- InvitationsModule: () => InvitationsModule,
53
- DocsModule: () => DocsModule,
54
- CiModule: () => CiModule,
55
- AuthModule: () => AuthModule
56
- });
57
- module.exports = __toCommonJS(exports_src);
58
- var import_axios = __toESM(require("axios"));
41
+ var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
59
42
 
60
43
  // src/events.ts
61
- var import_events = require("events");
62
- var LocusEvent;
63
- ((LocusEvent2) => {
64
- LocusEvent2["TOKEN_EXPIRED"] = "TOKEN_EXPIRED";
65
- LocusEvent2["AUTH_ERROR"] = "AUTH_ERROR";
66
- LocusEvent2["REQUEST_ERROR"] = "REQUEST_ERROR";
67
- })(LocusEvent ||= {});
68
-
69
- class LocusEmitter extends import_events.EventEmitter {
70
- on(event, listener) {
71
- return super.on(event, listener);
72
- }
73
- emit(event, ...args) {
74
- return super.emit(event, ...args);
75
- }
76
- }
44
+ var import_events, LocusEvent, LocusEmitter;
45
+ var init_events = __esm(() => {
46
+ import_events = require("events");
47
+ ((LocusEvent2) => {
48
+ LocusEvent2["TOKEN_EXPIRED"] = "TOKEN_EXPIRED";
49
+ LocusEvent2["AUTH_ERROR"] = "AUTH_ERROR";
50
+ LocusEvent2["REQUEST_ERROR"] = "REQUEST_ERROR";
51
+ })(LocusEvent ||= {});
52
+ LocusEmitter = class LocusEmitter extends import_events.EventEmitter {
53
+ on(event, listener) {
54
+ return super.on(event, listener);
55
+ }
56
+ emit(event, ...args) {
57
+ return super.emit(event, ...args);
58
+ }
59
+ };
60
+ });
77
61
 
78
62
  // src/modules/base.ts
79
63
  class BaseModule {
@@ -86,304 +70,344 @@ class BaseModule {
86
70
  }
87
71
 
88
72
  // src/modules/auth.ts
89
- class AuthModule extends BaseModule {
90
- async getProfile() {
91
- const { data } = await this.api.get("/auth/me");
92
- return data;
93
- }
94
- async getApiKeyInfo() {
95
- const { data } = await this.api.get("/auth/api-key");
96
- return data;
97
- }
98
- async requestRegisterOtp(email) {
99
- const { data } = await this.api.post("/auth/register-otp", { email });
100
- return data;
101
- }
102
- async requestLoginOtp(email) {
103
- const { data } = await this.api.post("/auth/login-otp", { email });
104
- return data;
105
- }
106
- async verifyLogin(body) {
107
- const { data } = await this.api.post("/auth/verify-login", body);
108
- return data;
109
- }
110
- async completeRegistration(body) {
111
- const { data } = await this.api.post("/auth/complete-registration", body);
112
- return data;
113
- }
114
- async deleteAccount() {
115
- const { data } = await this.api.delete("/auth/account");
116
- return data;
117
- }
118
- }
73
+ var AuthModule;
74
+ var init_auth = __esm(() => {
75
+ AuthModule = class AuthModule extends BaseModule {
76
+ async getProfile() {
77
+ const { data } = await this.api.get("/auth/me");
78
+ return data;
79
+ }
80
+ async getApiKeyInfo() {
81
+ const { data } = await this.api.get("/auth/api-key");
82
+ return data;
83
+ }
84
+ async requestRegisterOtp(email) {
85
+ const { data } = await this.api.post("/auth/register-otp", { email });
86
+ return data;
87
+ }
88
+ async requestLoginOtp(email) {
89
+ const { data } = await this.api.post("/auth/login-otp", { email });
90
+ return data;
91
+ }
92
+ async verifyLogin(body) {
93
+ const { data } = await this.api.post("/auth/verify-login", body);
94
+ return data;
95
+ }
96
+ async completeRegistration(body) {
97
+ const { data } = await this.api.post("/auth/complete-registration", body);
98
+ return data;
99
+ }
100
+ async deleteAccount() {
101
+ const { data } = await this.api.delete("/auth/account");
102
+ return data;
103
+ }
104
+ };
105
+ });
119
106
 
120
107
  // src/modules/ci.ts
121
- class CiModule extends BaseModule {
122
- async report(body) {
123
- const { data } = await this.api.post("/ci/report", body);
124
- return data;
125
- }
126
- }
108
+ var CiModule;
109
+ var init_ci = __esm(() => {
110
+ CiModule = class CiModule extends BaseModule {
111
+ async report(body) {
112
+ const { data } = await this.api.post("/ci/report", body);
113
+ return data;
114
+ }
115
+ };
116
+ });
127
117
 
128
118
  // src/modules/docs.ts
129
- class DocsModule extends BaseModule {
130
- async create(workspaceId, body) {
131
- const { data } = await this.api.post(`/workspaces/${workspaceId}/docs`, body);
132
- return data.doc;
133
- }
134
- async list(workspaceId) {
135
- const { data } = await this.api.get(`/workspaces/${workspaceId}/docs`);
136
- return data.docs;
137
- }
138
- async getById(id, workspaceId) {
139
- const { data } = await this.api.get(`/workspaces/${workspaceId}/docs/${id}`);
140
- return data.doc;
141
- }
142
- async update(id, workspaceId, body) {
143
- const { data } = await this.api.put(`/workspaces/${workspaceId}/docs/${id}`, body);
144
- return data.doc;
145
- }
146
- async delete(id, workspaceId) {
147
- await this.api.delete(`/workspaces/${workspaceId}/docs/${id}`);
148
- }
149
- async listGroups(workspaceId) {
150
- const { data } = await this.api.get(`/workspaces/${workspaceId}/doc-groups`);
151
- return data.groups;
152
- }
153
- async createGroup(workspaceId, body) {
154
- const { data } = await this.api.post(`/workspaces/${workspaceId}/doc-groups`, body);
155
- return data.group;
156
- }
157
- async updateGroup(id, workspaceId, body) {
158
- const { data } = await this.api.patch(`/workspaces/${workspaceId}/doc-groups/${id}`, body);
159
- return data.group;
160
- }
161
- async deleteGroup(id, workspaceId) {
162
- await this.api.delete(`/workspaces/${workspaceId}/doc-groups/${id}`);
163
- }
164
- }
119
+ var DocsModule;
120
+ var init_docs = __esm(() => {
121
+ DocsModule = class DocsModule extends BaseModule {
122
+ async create(workspaceId, body) {
123
+ const { data } = await this.api.post(`/workspaces/${workspaceId}/docs`, body);
124
+ return data.doc;
125
+ }
126
+ async list(workspaceId) {
127
+ const { data } = await this.api.get(`/workspaces/${workspaceId}/docs`);
128
+ return data.docs;
129
+ }
130
+ async getById(id, workspaceId) {
131
+ const { data } = await this.api.get(`/workspaces/${workspaceId}/docs/${id}`);
132
+ return data.doc;
133
+ }
134
+ async update(id, workspaceId, body) {
135
+ const { data } = await this.api.put(`/workspaces/${workspaceId}/docs/${id}`, body);
136
+ return data.doc;
137
+ }
138
+ async delete(id, workspaceId) {
139
+ await this.api.delete(`/workspaces/${workspaceId}/docs/${id}`);
140
+ }
141
+ async listGroups(workspaceId) {
142
+ const { data } = await this.api.get(`/workspaces/${workspaceId}/doc-groups`);
143
+ return data.groups;
144
+ }
145
+ async createGroup(workspaceId, body) {
146
+ const { data } = await this.api.post(`/workspaces/${workspaceId}/doc-groups`, body);
147
+ return data.group;
148
+ }
149
+ async updateGroup(id, workspaceId, body) {
150
+ const { data } = await this.api.patch(`/workspaces/${workspaceId}/doc-groups/${id}`, body);
151
+ return data.group;
152
+ }
153
+ async deleteGroup(id, workspaceId) {
154
+ await this.api.delete(`/workspaces/${workspaceId}/doc-groups/${id}`);
155
+ }
156
+ };
157
+ });
165
158
 
166
159
  // src/modules/invitations.ts
167
- class InvitationsModule extends BaseModule {
168
- async create(orgId, body) {
169
- const { data } = await this.api.post(`/org/${orgId}/invitations`, body);
170
- return data.invitation;
171
- }
172
- async list(orgId) {
173
- const { data } = await this.api.get(`/org/${orgId}/invitations`);
174
- return data.invitations;
175
- }
176
- async verify(token) {
177
- const { data } = await this.api.get(`/invitations/verify/${token}`);
178
- return data;
179
- }
180
- async accept(body) {
181
- const { data } = await this.api.post("/invitations/accept", body);
182
- return data;
183
- }
184
- async revoke(orgId, id) {
185
- await this.api.delete(`/org/${orgId}/invitations/${id}`);
186
- }
187
- }
160
+ var InvitationsModule;
161
+ var init_invitations = __esm(() => {
162
+ InvitationsModule = class InvitationsModule extends BaseModule {
163
+ async create(orgId, body) {
164
+ const { data } = await this.api.post(`/org/${orgId}/invitations`, body);
165
+ return data.invitation;
166
+ }
167
+ async list(orgId) {
168
+ const { data } = await this.api.get(`/org/${orgId}/invitations`);
169
+ return data.invitations;
170
+ }
171
+ async verify(token) {
172
+ const { data } = await this.api.get(`/invitations/verify/${token}`);
173
+ return data;
174
+ }
175
+ async accept(body) {
176
+ const { data } = await this.api.post("/invitations/accept", body);
177
+ return data;
178
+ }
179
+ async revoke(orgId, id) {
180
+ await this.api.delete(`/org/${orgId}/invitations/${id}`);
181
+ }
182
+ };
183
+ });
188
184
 
189
185
  // src/modules/organizations.ts
190
- class OrganizationsModule extends BaseModule {
191
- async list() {
192
- const { data } = await this.api.get("/organizations");
193
- return data.organizations;
194
- }
195
- async getById(id) {
196
- const { data } = await this.api.get(`/organizations/${id}`);
197
- return data.organization;
198
- }
199
- async listMembers(id) {
200
- const { data } = await this.api.get(`/organizations/${id}/members`);
201
- return data.members;
202
- }
203
- async addMember(id, body) {
204
- const { data } = await this.api.post(`/organizations/${id}/members`, body);
205
- return data.membership;
206
- }
207
- async removeMember(orgId, userId) {
208
- await this.api.delete(`/organizations/${orgId}/members/${userId}`);
209
- }
210
- async delete(orgId) {
211
- await this.api.delete(`/organizations/${orgId}`);
212
- }
213
- async listApiKeys(orgId) {
214
- const { data } = await this.api.get(`/organizations/${orgId}/api-keys`);
215
- return data.apiKeys;
216
- }
217
- async createApiKey(orgId, name) {
218
- const { data } = await this.api.post(`/organizations/${orgId}/api-keys`, { name });
219
- return data.apiKey;
220
- }
221
- async deleteApiKey(orgId, keyId) {
222
- await this.api.delete(`/organizations/${orgId}/api-keys/${keyId}`);
223
- }
224
- }
186
+ var OrganizationsModule;
187
+ var init_organizations = __esm(() => {
188
+ OrganizationsModule = class OrganizationsModule extends BaseModule {
189
+ async list() {
190
+ const { data } = await this.api.get("/organizations");
191
+ return data.organizations;
192
+ }
193
+ async getById(id) {
194
+ const { data } = await this.api.get(`/organizations/${id}`);
195
+ return data.organization;
196
+ }
197
+ async listMembers(id) {
198
+ const { data } = await this.api.get(`/organizations/${id}/members`);
199
+ return data.members;
200
+ }
201
+ async addMember(id, body) {
202
+ const { data } = await this.api.post(`/organizations/${id}/members`, body);
203
+ return data.membership;
204
+ }
205
+ async removeMember(orgId, userId) {
206
+ await this.api.delete(`/organizations/${orgId}/members/${userId}`);
207
+ }
208
+ async delete(orgId) {
209
+ await this.api.delete(`/organizations/${orgId}`);
210
+ }
211
+ async listApiKeys(orgId) {
212
+ const { data } = await this.api.get(`/organizations/${orgId}/api-keys`);
213
+ return data.apiKeys;
214
+ }
215
+ async createApiKey(orgId, name) {
216
+ const { data } = await this.api.post(`/organizations/${orgId}/api-keys`, { name });
217
+ return data.apiKey;
218
+ }
219
+ async deleteApiKey(orgId, keyId) {
220
+ await this.api.delete(`/organizations/${orgId}/api-keys/${keyId}`);
221
+ }
222
+ };
223
+ });
225
224
 
226
225
  // src/modules/sprints.ts
227
- class SprintsModule extends BaseModule {
228
- async list(workspaceId) {
229
- const { data } = await this.api.get(`/workspaces/${workspaceId}/sprints`);
230
- return data.sprints;
231
- }
232
- async getActive(workspaceId) {
233
- const { data } = await this.api.get(`/workspaces/${workspaceId}/sprints/active`);
234
- return data.sprint;
235
- }
236
- async getById(id, workspaceId) {
237
- const { data } = await this.api.get(`/workspaces/${workspaceId}/sprints/${id}`);
238
- return data.sprint;
239
- }
240
- async create(workspaceId, body) {
241
- const { data } = await this.api.post(`/workspaces/${workspaceId}/sprints`, body);
242
- return data.sprint;
243
- }
244
- async update(id, workspaceId, body) {
245
- const { data } = await this.api.patch(`/workspaces/${workspaceId}/sprints/${id}`, body);
246
- return data.sprint;
247
- }
248
- async delete(id, workspaceId) {
249
- await this.api.delete(`/workspaces/${workspaceId}/sprints/${id}`);
250
- }
251
- async start(id, workspaceId) {
252
- const { data } = await this.api.post(`/workspaces/${workspaceId}/sprints/${id}/start`);
253
- return data.sprint;
254
- }
255
- async complete(id, workspaceId) {
256
- const { data } = await this.api.post(`/workspaces/${workspaceId}/sprints/${id}/complete`);
257
- return data.sprint;
258
- }
259
- async triggerAIPlanning(id, workspaceId) {
260
- const { data } = await this.api.post(`/workspaces/${workspaceId}/sprints/${id}/trigger-ai-planning`);
261
- return data.sprint;
262
- }
263
- }
226
+ var SprintsModule;
227
+ var init_sprints = __esm(() => {
228
+ SprintsModule = class SprintsModule extends BaseModule {
229
+ async list(workspaceId) {
230
+ const { data } = await this.api.get(`/workspaces/${workspaceId}/sprints`);
231
+ return data.sprints;
232
+ }
233
+ async getActive(workspaceId) {
234
+ const { data } = await this.api.get(`/workspaces/${workspaceId}/sprints/active`);
235
+ return data.sprint;
236
+ }
237
+ async getById(id, workspaceId) {
238
+ const { data } = await this.api.get(`/workspaces/${workspaceId}/sprints/${id}`);
239
+ return data.sprint;
240
+ }
241
+ async create(workspaceId, body) {
242
+ const { data } = await this.api.post(`/workspaces/${workspaceId}/sprints`, body);
243
+ return data.sprint;
244
+ }
245
+ async update(id, workspaceId, body) {
246
+ const { data } = await this.api.patch(`/workspaces/${workspaceId}/sprints/${id}`, body);
247
+ return data.sprint;
248
+ }
249
+ async delete(id, workspaceId) {
250
+ await this.api.delete(`/workspaces/${workspaceId}/sprints/${id}`);
251
+ }
252
+ async start(id, workspaceId) {
253
+ const { data } = await this.api.post(`/workspaces/${workspaceId}/sprints/${id}/start`);
254
+ return data.sprint;
255
+ }
256
+ async complete(id, workspaceId) {
257
+ const { data } = await this.api.post(`/workspaces/${workspaceId}/sprints/${id}/complete`);
258
+ return data.sprint;
259
+ }
260
+ async triggerAIPlanning(id, workspaceId) {
261
+ const { data } = await this.api.post(`/workspaces/${workspaceId}/sprints/${id}/trigger-ai-planning`);
262
+ return data.sprint;
263
+ }
264
+ };
265
+ });
264
266
 
265
267
  // src/modules/tasks.ts
266
- var import_shared = require("@locusai/shared");
267
- class TasksModule extends BaseModule {
268
- async list(workspaceId, options) {
269
- const { data } = await this.api.get(`/workspaces/${workspaceId}/tasks`);
270
- let tasks = data.tasks;
271
- if (options?.sprintId) {
272
- tasks = tasks.filter((t) => t.sprintId === options.sprintId);
273
- }
274
- if (options?.status) {
275
- const statuses = Array.isArray(options.status) ? options.status : [options.status];
276
- tasks = tasks.filter((t) => statuses.includes(t.status));
277
- }
278
- return tasks;
279
- }
280
- async getAvailable(workspaceId, sprintId) {
281
- const tasks = await this.list(workspaceId, {
282
- sprintId
283
- });
284
- return tasks.filter((t) => t.status === import_shared.TaskStatus.BACKLOG || t.status === import_shared.TaskStatus.IN_PROGRESS && !t.assignedTo);
285
- }
286
- async getById(id, workspaceId) {
287
- const { data } = await this.api.get(`/workspaces/${workspaceId}/tasks/${id}`);
288
- return data.task;
289
- }
290
- async create(workspaceId, body) {
291
- const { data } = await this.api.post(`/workspaces/${workspaceId}/tasks`, body);
292
- return data.task;
293
- }
294
- async update(id, workspaceId, body) {
295
- const { data } = await this.api.patch(`/workspaces/${workspaceId}/tasks/${id}`, body);
296
- return data.task;
297
- }
298
- async delete(id, workspaceId) {
299
- await this.api.delete(`/workspaces/${workspaceId}/tasks/${id}`);
300
- }
301
- async getBacklog(workspaceId) {
302
- const { data } = await this.api.get(`/workspaces/${workspaceId}/tasks/backlog`);
303
- return data.tasks;
304
- }
305
- async addComment(id, workspaceId, body) {
306
- const { data } = await this.api.post(`/workspaces/${workspaceId}/tasks/${id}/comment`, body);
307
- return data.comment;
308
- }
309
- async batchUpdate(ids, workspaceId, updates) {
310
- await this.api.patch(`/workspaces/${workspaceId}/tasks/batch`, {
311
- ids,
312
- updates
313
- });
314
- }
315
- }
268
+ var import_shared, TasksModule;
269
+ var init_tasks = __esm(() => {
270
+ import_shared = require("@locusai/shared");
271
+ TasksModule = class TasksModule extends BaseModule {
272
+ async list(workspaceId, options) {
273
+ const { data } = await this.api.get(`/workspaces/${workspaceId}/tasks`);
274
+ let tasks = data.tasks;
275
+ if (options?.sprintId) {
276
+ tasks = tasks.filter((t) => t.sprintId === options.sprintId);
277
+ }
278
+ if (options?.status) {
279
+ const statuses = Array.isArray(options.status) ? options.status : [options.status];
280
+ tasks = tasks.filter((t) => statuses.includes(t.status));
281
+ }
282
+ return tasks;
283
+ }
284
+ async getAvailable(workspaceId, sprintId) {
285
+ const tasks = await this.list(workspaceId, {
286
+ sprintId
287
+ });
288
+ return tasks.filter((t) => t.status === import_shared.TaskStatus.BACKLOG || t.status === import_shared.TaskStatus.IN_PROGRESS && !t.assignedTo);
289
+ }
290
+ async getById(id, workspaceId) {
291
+ const { data } = await this.api.get(`/workspaces/${workspaceId}/tasks/${id}`);
292
+ return data.task;
293
+ }
294
+ async create(workspaceId, body) {
295
+ const { data } = await this.api.post(`/workspaces/${workspaceId}/tasks`, body);
296
+ return data.task;
297
+ }
298
+ async update(id, workspaceId, body) {
299
+ const { data } = await this.api.patch(`/workspaces/${workspaceId}/tasks/${id}`, body);
300
+ return data.task;
301
+ }
302
+ async delete(id, workspaceId) {
303
+ await this.api.delete(`/workspaces/${workspaceId}/tasks/${id}`);
304
+ }
305
+ async getBacklog(workspaceId) {
306
+ const { data } = await this.api.get(`/workspaces/${workspaceId}/tasks/backlog`);
307
+ return data.tasks;
308
+ }
309
+ async addComment(id, workspaceId, body) {
310
+ const { data } = await this.api.post(`/workspaces/${workspaceId}/tasks/${id}/comment`, body);
311
+ return data.comment;
312
+ }
313
+ async batchUpdate(ids, workspaceId, updates) {
314
+ await this.api.patch(`/workspaces/${workspaceId}/tasks/batch`, {
315
+ ids,
316
+ updates
317
+ });
318
+ }
319
+ };
320
+ });
316
321
 
317
322
  // src/modules/workspaces.ts
318
- class WorkspacesModule extends BaseModule {
319
- async listAll() {
320
- const { data } = await this.api.get("/workspaces");
321
- return data.workspaces;
322
- }
323
- async listByOrg(orgId) {
324
- const { data } = await this.api.get(`/workspaces/org/${orgId}`);
325
- return data.workspaces;
326
- }
327
- async create(body) {
328
- const { orgId, ...bodyWithoutOrgId } = body;
329
- const { data } = await this.api.post(`/workspaces/org/${orgId}`, bodyWithoutOrgId);
330
- return data.workspace;
331
- }
332
- async createWithAutoOrg(body) {
333
- const { data } = await this.api.post("/workspaces", body);
334
- return data.workspace;
335
- }
336
- async getById(id) {
337
- const { data } = await this.api.get(`/workspaces/${id}`);
338
- return data.workspace;
339
- }
340
- async update(id, body) {
341
- const { data } = await this.api.put(`/workspaces/${id}`, body);
342
- return data.workspace;
343
- }
344
- async delete(id) {
345
- await this.api.delete(`/workspaces/${id}`);
346
- }
347
- async getStats(id) {
348
- const { data } = await this.api.get(`/workspaces/${id}/stats`);
349
- return data;
350
- }
351
- async getActivity(id, limit) {
352
- const { data } = await this.api.get(`/workspaces/${id}/activity`, {
353
- params: { limit }
354
- });
355
- return data.activity;
356
- }
357
- async dispatch(id, workerId, sprintId) {
358
- const { data } = await this.api.post(`/workspaces/${id}/dispatch`, { workerId, sprintId });
359
- return data.task;
360
- }
361
- async heartbeat(workspaceId, agentId, currentTaskId, status) {
362
- const { data } = await this.api.post(`/workspaces/${workspaceId}/agents/heartbeat`, {
363
- agentId,
364
- currentTaskId: currentTaskId ?? null,
365
- status: status ?? "WORKING"
366
- });
367
- return data.agent;
368
- }
369
- async getAgents(workspaceId) {
370
- const { data } = await this.api.get(`/workspaces/${workspaceId}/agents`);
371
- return data.agents;
372
- }
373
- async listApiKeys(workspaceId) {
374
- const { data } = await this.api.get(`/workspaces/${workspaceId}/api-keys`);
375
- return data.apiKeys;
376
- }
377
- async createApiKey(workspaceId, name) {
378
- const { data } = await this.api.post(`/workspaces/${workspaceId}/api-keys`, { name });
379
- return data.apiKey;
380
- }
381
- async deleteApiKey(workspaceId, keyId) {
382
- await this.api.delete(`/workspaces/${workspaceId}/api-keys/${keyId}`);
383
- }
384
- }
323
+ var WorkspacesModule;
324
+ var init_workspaces = __esm(() => {
325
+ WorkspacesModule = class WorkspacesModule extends BaseModule {
326
+ async listAll() {
327
+ const { data } = await this.api.get("/workspaces");
328
+ return data.workspaces;
329
+ }
330
+ async listByOrg(orgId) {
331
+ const { data } = await this.api.get(`/workspaces/org/${orgId}`);
332
+ return data.workspaces;
333
+ }
334
+ async create(body) {
335
+ const { orgId, ...bodyWithoutOrgId } = body;
336
+ const { data } = await this.api.post(`/workspaces/org/${orgId}`, bodyWithoutOrgId);
337
+ return data.workspace;
338
+ }
339
+ async createWithAutoOrg(body) {
340
+ const { data } = await this.api.post("/workspaces", body);
341
+ return data.workspace;
342
+ }
343
+ async getById(id) {
344
+ const { data } = await this.api.get(`/workspaces/${id}`);
345
+ return data.workspace;
346
+ }
347
+ async update(id, body) {
348
+ const { data } = await this.api.put(`/workspaces/${id}`, body);
349
+ return data.workspace;
350
+ }
351
+ async delete(id) {
352
+ await this.api.delete(`/workspaces/${id}`);
353
+ }
354
+ async getStats(id) {
355
+ const { data } = await this.api.get(`/workspaces/${id}/stats`);
356
+ return data;
357
+ }
358
+ async getActivity(id, limit) {
359
+ const { data } = await this.api.get(`/workspaces/${id}/activity`, {
360
+ params: { limit }
361
+ });
362
+ return data.activity;
363
+ }
364
+ async dispatch(id, workerId, sprintId) {
365
+ const { data } = await this.api.post(`/workspaces/${id}/dispatch`, { workerId, sprintId });
366
+ return data.task;
367
+ }
368
+ async heartbeat(workspaceId, agentId, currentTaskId, status) {
369
+ const { data } = await this.api.post(`/workspaces/${workspaceId}/agents/heartbeat`, {
370
+ agentId,
371
+ currentTaskId: currentTaskId ?? null,
372
+ status: status ?? "WORKING"
373
+ });
374
+ return data.agent;
375
+ }
376
+ async getAgents(workspaceId) {
377
+ const { data } = await this.api.get(`/workspaces/${workspaceId}/agents`);
378
+ return data.agents;
379
+ }
380
+ async listApiKeys(workspaceId) {
381
+ const { data } = await this.api.get(`/workspaces/${workspaceId}/api-keys`);
382
+ return data.apiKeys;
383
+ }
384
+ async createApiKey(workspaceId, name) {
385
+ const { data } = await this.api.post(`/workspaces/${workspaceId}/api-keys`, { name });
386
+ return data.apiKey;
387
+ }
388
+ async deleteApiKey(workspaceId, keyId) {
389
+ await this.api.delete(`/workspaces/${workspaceId}/api-keys/${keyId}`);
390
+ }
391
+ };
392
+ });
385
393
 
386
394
  // src/index.ts
395
+ var exports_src = {};
396
+ __export(exports_src, {
397
+ WorkspacesModule: () => WorkspacesModule,
398
+ TasksModule: () => TasksModule,
399
+ SprintsModule: () => SprintsModule,
400
+ OrganizationsModule: () => OrganizationsModule,
401
+ LocusEvent: () => LocusEvent,
402
+ LocusEmitter: () => LocusEmitter,
403
+ LocusClient: () => LocusClient,
404
+ InvitationsModule: () => InvitationsModule,
405
+ DocsModule: () => DocsModule,
406
+ CiModule: () => CiModule,
407
+ AuthModule: () => AuthModule
408
+ });
409
+ module.exports = __toCommonJS(exports_src);
410
+
387
411
  class LocusClient {
388
412
  api;
389
413
  emitter;
@@ -473,70 +497,30 @@ class LocusClient {
473
497
  }
474
498
  }
475
499
  }
476
-
477
- // src/agent/worker.ts
478
- var exports_worker = {};
479
- __export(exports_worker, {
480
- AgentWorker: () => AgentWorker
500
+ var import_axios;
501
+ var init_src = __esm(() => {
502
+ init_events();
503
+ init_auth();
504
+ init_ci();
505
+ init_docs();
506
+ init_invitations();
507
+ init_organizations();
508
+ init_sprints();
509
+ init_tasks();
510
+ init_workspaces();
511
+ import_axios = __toESM(require("axios"));
512
+ init_events();
513
+ init_auth();
514
+ init_ci();
515
+ init_docs();
516
+ init_invitations();
517
+ init_organizations();
518
+ init_sprints();
519
+ init_tasks();
520
+ init_workspaces();
481
521
  });
482
- module.exports = __toCommonJS(exports_worker);
483
- var import_shared3 = require("@locusai/shared");
484
522
 
485
523
  // src/core/config.ts
486
- var import_node_path = require("node:path");
487
- var PROVIDER = {
488
- CLAUDE: "claude",
489
- CODEX: "codex"
490
- };
491
- var DEFAULT_MODEL = {
492
- [PROVIDER.CLAUDE]: "opus",
493
- [PROVIDER.CODEX]: "gpt-5.3-codex"
494
- };
495
- var LOCUS_SCHEMA_BASE_URL = "https://locusai.dev/schemas";
496
- var LOCUS_SCHEMAS = {
497
- config: `${LOCUS_SCHEMA_BASE_URL}/config.schema.json`,
498
- settings: `${LOCUS_SCHEMA_BASE_URL}/settings.schema.json`
499
- };
500
- var LOCUS_CONFIG = {
501
- dir: ".locus",
502
- configFile: "config.json",
503
- settingsFile: "settings.json",
504
- indexFile: "codebase-index.json",
505
- contextFile: "LOCUS.md",
506
- artifactsDir: "artifacts",
507
- documentsDir: "documents",
508
- sessionsDir: "sessions",
509
- reviewsDir: "reviews",
510
- plansDir: "plans",
511
- projectDir: "project",
512
- projectContextFile: "context.md",
513
- projectProgressFile: "progress.md"
514
- };
515
- var LOCUS_GITIGNORE_PATTERNS = [
516
- "# Locus AI - Session data (user-specific, can grow large)",
517
- ".locus/sessions/",
518
- "",
519
- "# Locus AI - Artifacts (local-only, user-specific)",
520
- ".locus/artifacts/",
521
- "",
522
- "# Locus AI - Review reports (generated per sprint)",
523
- ".locus/reviews/",
524
- "",
525
- "# Locus AI - Plans (generated per task)",
526
- ".locus/plans/",
527
- "",
528
- "# Locus AI - Agent worktrees (parallel execution)",
529
- ".locus-worktrees/",
530
- "",
531
- "# Locus AI - Settings (contains API key, telegram config, etc.)",
532
- ".locus/settings.json",
533
- "",
534
- "# Locus AI - Configuration (contains project context, progress, etc.)",
535
- ".locus/config.json",
536
- "",
537
- "# Locus AI - Project progress (contains project progress, etc.)",
538
- ".locus/project/progress.md"
539
- ];
540
524
  function getLocusPath(projectPath, fileName) {
541
525
  if (fileName === "projectContextFile" || fileName === "projectProgressFile") {
542
526
  return import_node_path.join(projectPath, LOCUS_CONFIG.dir, LOCUS_CONFIG.projectDir, LOCUS_CONFIG[fileName]);
@@ -547,123 +531,235 @@ function getAgentArtifactsPath(projectPath, agentId) {
547
531
  const shortId = agentId.slice(-8);
548
532
  return import_node_path.join(projectPath, LOCUS_CONFIG.dir, LOCUS_CONFIG.artifactsDir, shortId);
549
533
  }
550
-
551
- // src/ai/claude-runner.ts
552
- var import_node_child_process = require("node:child_process");
553
- var import_node_path3 = require("node:path");
534
+ var import_node_path, PROVIDER, DEFAULT_MODEL, LOCUS_SCHEMA_BASE_URL = "https://locusai.dev/schemas", LOCUS_SCHEMAS, LOCUS_CONFIG, LOCUS_GITIGNORE_PATTERNS;
535
+ var init_config = __esm(() => {
536
+ import_node_path = require("node:path");
537
+ PROVIDER = {
538
+ CLAUDE: "claude",
539
+ CODEX: "codex"
540
+ };
541
+ DEFAULT_MODEL = {
542
+ [PROVIDER.CLAUDE]: "opus",
543
+ [PROVIDER.CODEX]: "gpt-5.3-codex"
544
+ };
545
+ LOCUS_SCHEMAS = {
546
+ config: `${LOCUS_SCHEMA_BASE_URL}/config.schema.json`,
547
+ settings: `${LOCUS_SCHEMA_BASE_URL}/settings.schema.json`
548
+ };
549
+ LOCUS_CONFIG = {
550
+ dir: ".locus",
551
+ configFile: "config.json",
552
+ settingsFile: "settings.json",
553
+ indexFile: "codebase-index.json",
554
+ contextFile: "LOCUS.md",
555
+ artifactsDir: "artifacts",
556
+ documentsDir: "documents",
557
+ sessionsDir: "sessions",
558
+ reviewsDir: "reviews",
559
+ plansDir: "plans",
560
+ projectDir: "project",
561
+ projectContextFile: "context.md",
562
+ projectProgressFile: "progress.md"
563
+ };
564
+ LOCUS_GITIGNORE_PATTERNS = [
565
+ "# Locus AI - Session data (user-specific, can grow large)",
566
+ ".locus/sessions/",
567
+ "",
568
+ "# Locus AI - Artifacts (local-only, user-specific)",
569
+ ".locus/artifacts/",
570
+ "",
571
+ "# Locus AI - Review reports (generated per sprint)",
572
+ ".locus/reviews/",
573
+ "",
574
+ "# Locus AI - Plans (generated per task)",
575
+ ".locus/plans/",
576
+ "",
577
+ "# Locus AI - Agent worktrees (parallel execution)",
578
+ ".locus-worktrees/",
579
+ "",
580
+ "# Locus AI - Settings (contains API key, telegram config, etc.)",
581
+ ".locus/settings.json",
582
+ "",
583
+ "# Locus AI - Configuration (contains project context, progress, etc.)",
584
+ ".locus/config.json",
585
+ "",
586
+ "# Locus AI - Project progress (contains project progress, etc.)",
587
+ ".locus/project/progress.md"
588
+ ];
589
+ });
554
590
 
555
591
  // src/utils/colors.ts
556
- var ESC = "\x1B[";
557
- var RESET = `${ESC}0m`;
558
- var colors = {
559
- reset: RESET,
560
- bold: `${ESC}1m`,
561
- dim: `${ESC}2m`,
562
- italic: `${ESC}3m`,
563
- underline: `${ESC}4m`,
564
- black: `${ESC}30m`,
565
- red: `${ESC}31m`,
566
- green: `${ESC}32m`,
567
- yellow: `${ESC}33m`,
568
- blue: `${ESC}34m`,
569
- magenta: `${ESC}35m`,
570
- cyan: `${ESC}36m`,
571
- white: `${ESC}37m`,
572
- gray: `${ESC}90m`,
573
- brightRed: `${ESC}91m`,
574
- brightGreen: `${ESC}92m`,
575
- brightYellow: `${ESC}93m`,
576
- brightBlue: `${ESC}94m`,
577
- brightMagenta: `${ESC}95m`,
578
- brightCyan: `${ESC}96m`,
579
- brightWhite: `${ESC}97m`,
580
- bgBlack: `${ESC}40m`,
581
- bgRed: `${ESC}41m`,
582
- bgGreen: `${ESC}42m`,
583
- bgYellow: `${ESC}43m`,
584
- bgBlue: `${ESC}44m`,
585
- bgMagenta: `${ESC}45m`,
586
- bgCyan: `${ESC}46m`,
587
- bgWhite: `${ESC}47m`
588
- };
589
- var c = {
590
- text: (text, ...colorNames) => {
591
- const codes = colorNames.map((name) => colors[name]).join("");
592
- return `${codes}${text}${RESET}`;
593
- },
594
- bold: (t) => c.text(t, "bold"),
595
- dim: (t) => c.text(t, "dim"),
596
- red: (t) => c.text(t, "red"),
597
- green: (t) => c.text(t, "green"),
598
- yellow: (t) => c.text(t, "yellow"),
599
- blue: (t) => c.text(t, "blue"),
600
- magenta: (t) => c.text(t, "magenta"),
601
- cyan: (t) => c.text(t, "cyan"),
602
- gray: (t) => c.text(t, "gray"),
603
- white: (t) => c.text(t, "white"),
604
- brightBlue: (t) => c.text(t, "brightBlue"),
605
- bgBlue: (t) => c.text(t, "bgBlue", "white", "bold"),
606
- success: (t) => c.text(t, "green", "bold"),
607
- error: (t) => c.text(t, "red", "bold"),
608
- warning: (t) => c.text(t, "yellow", "bold"),
609
- info: (t) => c.text(t, "cyan", "bold"),
610
- primary: (t) => c.text(t, "blue", "bold"),
611
- secondary: (t) => c.text(t, "magenta", "bold"),
612
- header: (t) => c.text(` ${t} `, "bgBlue", "white", "bold"),
613
- step: (t) => c.text(` ${t} `, "bgCyan", "black", "bold"),
614
- underline: (t) => c.text(t, "underline")
615
- };
592
+ var ESC = "\x1B[", RESET, colors, c;
593
+ var init_colors = __esm(() => {
594
+ RESET = `${ESC}0m`;
595
+ colors = {
596
+ reset: RESET,
597
+ bold: `${ESC}1m`,
598
+ dim: `${ESC}2m`,
599
+ italic: `${ESC}3m`,
600
+ underline: `${ESC}4m`,
601
+ black: `${ESC}30m`,
602
+ red: `${ESC}31m`,
603
+ green: `${ESC}32m`,
604
+ yellow: `${ESC}33m`,
605
+ blue: `${ESC}34m`,
606
+ magenta: `${ESC}35m`,
607
+ cyan: `${ESC}36m`,
608
+ white: `${ESC}37m`,
609
+ gray: `${ESC}90m`,
610
+ brightRed: `${ESC}91m`,
611
+ brightGreen: `${ESC}92m`,
612
+ brightYellow: `${ESC}93m`,
613
+ brightBlue: `${ESC}94m`,
614
+ brightMagenta: `${ESC}95m`,
615
+ brightCyan: `${ESC}96m`,
616
+ brightWhite: `${ESC}97m`,
617
+ bgBlack: `${ESC}40m`,
618
+ bgRed: `${ESC}41m`,
619
+ bgGreen: `${ESC}42m`,
620
+ bgYellow: `${ESC}43m`,
621
+ bgBlue: `${ESC}44m`,
622
+ bgMagenta: `${ESC}45m`,
623
+ bgCyan: `${ESC}46m`,
624
+ bgWhite: `${ESC}47m`
625
+ };
626
+ c = {
627
+ text: (text, ...colorNames) => {
628
+ const codes = colorNames.map((name) => colors[name]).join("");
629
+ return `${codes}${text}${RESET}`;
630
+ },
631
+ bold: (t) => c.text(t, "bold"),
632
+ dim: (t) => c.text(t, "dim"),
633
+ red: (t) => c.text(t, "red"),
634
+ green: (t) => c.text(t, "green"),
635
+ yellow: (t) => c.text(t, "yellow"),
636
+ blue: (t) => c.text(t, "blue"),
637
+ magenta: (t) => c.text(t, "magenta"),
638
+ cyan: (t) => c.text(t, "cyan"),
639
+ gray: (t) => c.text(t, "gray"),
640
+ white: (t) => c.text(t, "white"),
641
+ brightBlue: (t) => c.text(t, "brightBlue"),
642
+ bgBlue: (t) => c.text(t, "bgBlue", "white", "bold"),
643
+ success: (t) => c.text(t, "green", "bold"),
644
+ error: (t) => c.text(t, "red", "bold"),
645
+ warning: (t) => c.text(t, "yellow", "bold"),
646
+ info: (t) => c.text(t, "cyan", "bold"),
647
+ primary: (t) => c.text(t, "blue", "bold"),
648
+ secondary: (t) => c.text(t, "magenta", "bold"),
649
+ header: (t) => c.text(` ${t} `, "bgBlue", "white", "bold"),
650
+ step: (t) => c.text(` ${t} `, "bgCyan", "black", "bold"),
651
+ underline: (t) => c.text(t, "underline")
652
+ };
653
+ });
616
654
 
617
655
  // src/utils/resolve-bin.ts
618
- var import_node_fs = require("node:fs");
619
- var import_node_os = require("node:os");
620
- var import_node_path2 = require("node:path");
621
- var EXTRA_BIN_DIRS = [
622
- import_node_path2.join(import_node_os.homedir(), ".local", "bin"),
623
- import_node_path2.join(import_node_os.homedir(), ".npm", "bin"),
624
- import_node_path2.join(import_node_os.homedir(), ".npm-global", "bin"),
625
- import_node_path2.join(import_node_os.homedir(), ".yarn", "bin"),
626
- "/usr/local/bin"
627
- ];
628
- function getNodeManagerDirs() {
629
- const dirs = [];
656
+ function getNvmNodeBinDir() {
630
657
  const nvmDir = process.env.NVM_DIR || import_node_path2.join(import_node_os.homedir(), ".nvm");
631
- const nvmCurrent = import_node_path2.join(nvmDir, "current", "bin");
632
- if (import_node_fs.existsSync(nvmCurrent)) {
633
- dirs.push(nvmCurrent);
658
+ const versionsDir = import_node_path2.join(nvmDir, "versions", "node");
659
+ if (!import_node_fs.existsSync(versionsDir))
660
+ return null;
661
+ let versions;
662
+ try {
663
+ versions = import_node_fs.readdirSync(versionsDir).filter((d) => d.startsWith("v"));
664
+ } catch {
665
+ return null;
634
666
  }
635
- const fnmDir = process.env.FNM_DIR || import_node_path2.join(import_node_os.homedir(), ".fnm");
636
- const fnmCurrent = import_node_path2.join(fnmDir, "current", "bin");
637
- if (import_node_fs.existsSync(fnmCurrent)) {
638
- dirs.push(fnmCurrent);
667
+ if (versions.length === 0)
668
+ return null;
669
+ const currentNodeVersion = `v${process.versions.node}`;
670
+ const currentBin = import_node_path2.join(versionsDir, currentNodeVersion, "bin");
671
+ if (versions.includes(currentNodeVersion) && import_node_fs.existsSync(currentBin)) {
672
+ return currentBin;
673
+ }
674
+ const aliasPath = import_node_path2.join(nvmDir, "alias", "default");
675
+ if (import_node_fs.existsSync(aliasPath)) {
676
+ try {
677
+ const alias = import_node_fs.readFileSync(aliasPath, "utf-8").trim();
678
+ const match = versions.find((v) => v === `v${alias}` || v.startsWith(`v${alias}.`));
679
+ if (match) {
680
+ const bin2 = import_node_path2.join(versionsDir, match, "bin");
681
+ if (import_node_fs.existsSync(bin2))
682
+ return bin2;
683
+ }
684
+ } catch {}
639
685
  }
640
- return dirs;
686
+ const sorted = versions.sort((a, b) => {
687
+ const pa = a.slice(1).split(".").map(Number);
688
+ const pb = b.slice(1).split(".").map(Number);
689
+ for (let i = 0;i < 3; i++) {
690
+ if ((pa[i] || 0) !== (pb[i] || 0))
691
+ return (pb[i] || 0) - (pa[i] || 0);
692
+ }
693
+ return 0;
694
+ });
695
+ const bin = import_node_path2.join(versionsDir, sorted[0], "bin");
696
+ return import_node_fs.existsSync(bin) ? bin : null;
697
+ }
698
+ function getFnmNodeBinDir() {
699
+ const fnmDir = process.env.FNM_DIR || import_node_path2.join(import_node_os.homedir(), ".fnm");
700
+ const currentBin = import_node_path2.join(fnmDir, "current", "bin");
701
+ if (import_node_fs.existsSync(currentBin))
702
+ return currentBin;
703
+ const aliasDir = import_node_path2.join(fnmDir, "aliases", "default");
704
+ if (import_node_fs.existsSync(aliasDir)) {
705
+ const bin = import_node_path2.join(aliasDir, "bin");
706
+ if (import_node_fs.existsSync(bin))
707
+ return bin;
708
+ }
709
+ return null;
641
710
  }
642
711
  function getAugmentedPath() {
643
712
  const currentPath = process.env.PATH || "";
644
713
  const currentDirs = new Set(currentPath.split(import_node_path2.delimiter));
645
- const extra = [...EXTRA_BIN_DIRS, ...getNodeManagerDirs()].filter((dir) => !currentDirs.has(dir) && import_node_fs.existsSync(dir));
714
+ const extra = [];
715
+ for (const dir of EXTRA_BIN_DIRS) {
716
+ if (!currentDirs.has(dir) && import_node_fs.existsSync(dir)) {
717
+ extra.push(dir);
718
+ }
719
+ }
720
+ const nvmBin = getNvmNodeBinDir();
721
+ if (nvmBin && !currentDirs.has(nvmBin)) {
722
+ extra.push(nvmBin);
723
+ }
724
+ const fnmBin = getFnmNodeBinDir();
725
+ if (fnmBin && !currentDirs.has(fnmBin)) {
726
+ extra.push(fnmBin);
727
+ }
646
728
  if (extra.length === 0)
647
729
  return currentPath;
648
730
  return currentPath + import_node_path2.delimiter + extra.join(import_node_path2.delimiter);
649
731
  }
650
732
  function getAugmentedEnv(overrides = {}) {
651
- return {
733
+ const env = {
652
734
  ...process.env,
653
735
  ...overrides,
654
736
  PATH: getAugmentedPath()
655
737
  };
656
- }
657
-
658
- // src/ai/claude-runner.ts
659
- var SANDBOX_SETTINGS = JSON.stringify({
660
- sandbox: {
661
- enabled: true,
662
- autoAllow: true,
663
- allowUnsandboxedCommands: false
738
+ for (const key of ENV_VARS_TO_STRIP) {
739
+ delete env[key];
664
740
  }
741
+ return env;
742
+ }
743
+ var import_node_fs, import_node_os, import_node_path2, EXTRA_BIN_DIRS, ENV_VARS_TO_STRIP;
744
+ var init_resolve_bin = __esm(() => {
745
+ import_node_fs = require("node:fs");
746
+ import_node_os = require("node:os");
747
+ import_node_path2 = require("node:path");
748
+ EXTRA_BIN_DIRS = [
749
+ import_node_path2.join(import_node_os.homedir(), ".local", "bin"),
750
+ import_node_path2.join(import_node_os.homedir(), ".npm", "bin"),
751
+ import_node_path2.join(import_node_os.homedir(), ".npm-global", "bin"),
752
+ import_node_path2.join(import_node_os.homedir(), ".yarn", "bin"),
753
+ import_node_path2.join(import_node_os.homedir(), ".bun", "bin"),
754
+ import_node_path2.join(import_node_os.homedir(), "Library", "pnpm"),
755
+ "/usr/local/bin",
756
+ "/opt/homebrew/bin",
757
+ "/opt/homebrew/sbin"
758
+ ];
759
+ ENV_VARS_TO_STRIP = ["ANTHROPIC_API_KEY", "OPENAI_API_KEY"];
665
760
  });
666
761
 
762
+ // src/ai/claude-runner.ts
667
763
  class ClaudeRunner {
668
764
  model;
669
765
  log;
@@ -735,11 +831,13 @@ class ClaudeRunner {
735
831
  this.activeProcess = claude;
736
832
  let buffer = "";
737
833
  let stderrBuffer = "";
834
+ let stderrFull = "";
738
835
  let resolveChunk = null;
739
836
  const chunkQueue = [];
740
837
  let processEnded = false;
741
838
  let errorMessage = "";
742
839
  let finalContent = "";
840
+ let lastResultContent = "";
743
841
  let isThinking = false;
744
842
  const enqueueChunk = (chunk) => {
745
843
  this.emitEventForChunk(chunk, isThinking);
@@ -777,6 +875,9 @@ class ClaudeRunner {
777
875
  for (const line of lines) {
778
876
  const chunk = this.parseStreamLineToChunk(line);
779
877
  if (chunk) {
878
+ if (chunk.type === "result") {
879
+ lastResultContent = chunk.content;
880
+ }
780
881
  enqueueChunk(chunk);
781
882
  }
782
883
  }
@@ -784,6 +885,7 @@ class ClaudeRunner {
784
885
  claude.stderr.on("data", (data) => {
785
886
  const chunk = data.toString();
786
887
  stderrBuffer += chunk;
888
+ stderrFull += chunk;
787
889
  const lines = stderrBuffer.split(`
788
890
  `);
789
891
  stderrBuffer = lines.pop() || "";
@@ -806,7 +908,8 @@ class ClaudeRunner {
806
908
  `);
807
909
  }
808
910
  if (code !== 0 && !errorMessage) {
809
- errorMessage = this.createExecutionError(code, stderrBuffer).message;
911
+ const detail = stderrFull.trim() || lastResultContent.trim();
912
+ errorMessage = this.createExecutionError(code, detail).message;
810
913
  this.eventEmitter?.emitErrorOccurred(errorMessage, `EXIT_${code}`);
811
914
  }
812
915
  signalEnd();
@@ -1015,7 +1118,8 @@ class ClaudeRunner {
1015
1118
  if (code === 0) {
1016
1119
  resolve2(finalResult);
1017
1120
  } else {
1018
- reject(this.createExecutionError(code, errorOutput));
1121
+ const detail = errorOutput.trim() || finalResult.trim();
1122
+ reject(this.createExecutionError(code, detail));
1019
1123
  }
1020
1124
  });
1021
1125
  claude.stdin.write(prompt);
@@ -1061,13 +1165,23 @@ ${c.primary("[Claude]")} ${c.bold(`Running ${content_block.name}...`)}
1061
1165
  return new Error(message);
1062
1166
  }
1063
1167
  }
1168
+ var import_node_child_process, import_node_path3, SANDBOX_SETTINGS;
1169
+ var init_claude_runner = __esm(() => {
1170
+ init_config();
1171
+ init_colors();
1172
+ init_resolve_bin();
1173
+ import_node_child_process = require("node:child_process");
1174
+ import_node_path3 = require("node:path");
1175
+ SANDBOX_SETTINGS = JSON.stringify({
1176
+ sandbox: {
1177
+ enabled: true,
1178
+ autoAllow: true,
1179
+ allowUnsandboxedCommands: false
1180
+ }
1181
+ });
1182
+ });
1064
1183
 
1065
1184
  // src/ai/codex-runner.ts
1066
- var import_node_child_process2 = require("node:child_process");
1067
- var import_node_crypto = require("node:crypto");
1068
- var import_node_fs2 = require("node:fs");
1069
- var import_node_os2 = require("node:os");
1070
- var import_node_path4 = require("node:path");
1071
1185
  class CodexRunner {
1072
1186
  projectPath;
1073
1187
  model;
@@ -1294,6 +1408,16 @@ class CodexRunner {
1294
1408
  return new Promise((resolve2) => setTimeout(resolve2, ms));
1295
1409
  }
1296
1410
  }
1411
+ var import_node_child_process2, import_node_crypto, import_node_fs2, import_node_os2, import_node_path4;
1412
+ var init_codex_runner = __esm(() => {
1413
+ init_config();
1414
+ init_resolve_bin();
1415
+ import_node_child_process2 = require("node:child_process");
1416
+ import_node_crypto = require("node:crypto");
1417
+ import_node_fs2 = require("node:fs");
1418
+ import_node_os2 = require("node:os");
1419
+ import_node_path4 = require("node:path");
1420
+ });
1297
1421
 
1298
1422
  // src/ai/factory.ts
1299
1423
  function createAiRunner(provider, config) {
@@ -1306,9 +1430,13 @@ function createAiRunner(provider, config) {
1306
1430
  return new ClaudeRunner(config.projectPath, model, config.log);
1307
1431
  }
1308
1432
  }
1433
+ var init_factory = __esm(() => {
1434
+ init_config();
1435
+ init_claude_runner();
1436
+ init_codex_runner();
1437
+ });
1309
1438
 
1310
1439
  // src/git/git-utils.ts
1311
- var import_node_child_process3 = require("node:child_process");
1312
1440
  function isGitAvailable() {
1313
1441
  try {
1314
1442
  import_node_child_process3.execFileSync("git", ["--version"], {
@@ -1399,23 +1527,126 @@ function getDefaultBranch(projectPath, remote = "origin") {
1399
1527
  }
1400
1528
  }
1401
1529
  }
1530
+ var import_node_child_process3;
1531
+ var init_git_utils = __esm(() => {
1532
+ import_node_child_process3 = require("node:child_process");
1533
+ });
1402
1534
 
1403
- // src/git/pr-service.ts
1404
- var import_node_child_process4 = require("node:child_process");
1405
- class PrService {
1406
- projectPath;
1407
- log;
1408
- constructor(projectPath, log) {
1409
- this.projectPath = projectPath;
1410
- this.log = log;
1535
+ // src/project/knowledge-base.ts
1536
+ class KnowledgeBase {
1537
+ contextPath;
1538
+ progressPath;
1539
+ constructor(projectPath) {
1540
+ this.contextPath = getLocusPath(projectPath, "projectContextFile");
1541
+ this.progressPath = getLocusPath(projectPath, "projectProgressFile");
1411
1542
  }
1412
- createPr(options) {
1413
- const {
1414
- task,
1415
- branch,
1416
- baseBranch: requestedBaseBranch,
1417
- agentId,
1418
- summary
1543
+ readContext() {
1544
+ if (!import_node_fs3.existsSync(this.contextPath)) {
1545
+ return "";
1546
+ }
1547
+ return import_node_fs3.readFileSync(this.contextPath, "utf-8");
1548
+ }
1549
+ readProgress() {
1550
+ if (!import_node_fs3.existsSync(this.progressPath)) {
1551
+ return "";
1552
+ }
1553
+ return import_node_fs3.readFileSync(this.progressPath, "utf-8");
1554
+ }
1555
+ updateContext(content) {
1556
+ this.ensureDir(this.contextPath);
1557
+ import_node_fs3.writeFileSync(this.contextPath, content);
1558
+ }
1559
+ updateProgress(entry) {
1560
+ this.ensureDir(this.progressPath);
1561
+ const existing = this.readProgress();
1562
+ const timestamp = (entry.timestamp ?? new Date).toISOString();
1563
+ const label = entry.role === "user" ? "User" : "Assistant";
1564
+ const line = `**${label}** (${timestamp}):
1565
+ ${entry.content}`;
1566
+ const updated = existing ? `${existing}
1567
+
1568
+ ---
1569
+
1570
+ ${line}` : `# Conversation History
1571
+
1572
+ ${line}`;
1573
+ import_node_fs3.writeFileSync(this.progressPath, updated);
1574
+ }
1575
+ getFullContext() {
1576
+ const context = this.readContext();
1577
+ const progress = this.readProgress();
1578
+ const parts = [];
1579
+ if (context.trim()) {
1580
+ parts.push(context.trim());
1581
+ }
1582
+ if (progress.trim()) {
1583
+ parts.push(progress.trim());
1584
+ }
1585
+ return parts.join(`
1586
+
1587
+ ---
1588
+
1589
+ `);
1590
+ }
1591
+ initialize(info) {
1592
+ this.ensureDir(this.contextPath);
1593
+ this.ensureDir(this.progressPath);
1594
+ const techStackList = info.techStack.map((t) => `- ${t}`).join(`
1595
+ `);
1596
+ const contextContent = `# Project: ${info.name}
1597
+
1598
+ ## Mission
1599
+ ${info.mission}
1600
+
1601
+ ## Tech Stack
1602
+ ${techStackList}
1603
+
1604
+ ## Architecture
1605
+ <!-- Describe your high-level architecture here -->
1606
+
1607
+ ## Key Decisions
1608
+ <!-- Document important technical decisions and their rationale -->
1609
+
1610
+ ## Feature Areas
1611
+ <!-- List your main feature areas and their status -->
1612
+ `;
1613
+ const progressContent = `# Conversation History
1614
+ `;
1615
+ import_node_fs3.writeFileSync(this.contextPath, contextContent);
1616
+ import_node_fs3.writeFileSync(this.progressPath, progressContent);
1617
+ }
1618
+ get exists() {
1619
+ return import_node_fs3.existsSync(this.contextPath) || import_node_fs3.existsSync(this.progressPath);
1620
+ }
1621
+ ensureDir(filePath) {
1622
+ const dir = import_node_path5.dirname(filePath);
1623
+ if (!import_node_fs3.existsSync(dir)) {
1624
+ import_node_fs3.mkdirSync(dir, { recursive: true });
1625
+ }
1626
+ }
1627
+ }
1628
+ var import_node_fs3, import_node_path5;
1629
+ var init_knowledge_base = __esm(() => {
1630
+ init_config();
1631
+ import_node_fs3 = require("node:fs");
1632
+ import_node_path5 = require("node:path");
1633
+ });
1634
+
1635
+ // src/git/pr-service.ts
1636
+ class PrService {
1637
+ projectPath;
1638
+ log;
1639
+ constructor(projectPath, log) {
1640
+ this.projectPath = projectPath;
1641
+ this.log = log;
1642
+ }
1643
+ createPr(options) {
1644
+ const {
1645
+ task,
1646
+ branch,
1647
+ baseBranch: requestedBaseBranch,
1648
+ agentId,
1649
+ summary
1419
1650
  } = options;
1420
1651
  const provider = detectRemoteProvider(this.projectPath);
1421
1652
  if (provider !== "github") {
@@ -1631,148 +1862,21 @@ class PrService {
1631
1862
  return match ? Number.parseInt(match[1], 10) : 0;
1632
1863
  }
1633
1864
  }
1634
-
1635
- // src/project/knowledge-base.ts
1636
- var import_node_fs3 = require("node:fs");
1637
- var import_node_path5 = require("node:path");
1638
- class KnowledgeBase {
1639
- contextPath;
1640
- progressPath;
1641
- constructor(projectPath) {
1642
- this.contextPath = getLocusPath(projectPath, "projectContextFile");
1643
- this.progressPath = getLocusPath(projectPath, "projectProgressFile");
1644
- }
1645
- readContext() {
1646
- if (!import_node_fs3.existsSync(this.contextPath)) {
1647
- return "";
1648
- }
1649
- return import_node_fs3.readFileSync(this.contextPath, "utf-8");
1650
- }
1651
- readProgress() {
1652
- if (!import_node_fs3.existsSync(this.progressPath)) {
1653
- return "";
1654
- }
1655
- return import_node_fs3.readFileSync(this.progressPath, "utf-8");
1656
- }
1657
- updateContext(content) {
1658
- this.ensureDir(this.contextPath);
1659
- import_node_fs3.writeFileSync(this.contextPath, content);
1660
- }
1661
- updateProgress(event) {
1662
- this.ensureDir(this.progressPath);
1663
- const existing = this.readProgress();
1664
- const timestamp = (event.timestamp ?? new Date).toISOString();
1665
- let entry = "";
1666
- switch (event.type) {
1667
- case "task_completed":
1668
- entry = `- [x] ${event.title} — completed ${timestamp}`;
1669
- break;
1670
- case "sprint_started":
1671
- entry = `
1672
- ## Current Sprint: ${event.title}
1673
- **Status:** ACTIVE | Started: ${timestamp}
1674
- `;
1675
- break;
1676
- case "sprint_completed":
1677
- entry = `
1678
- ### Sprint Completed: ${event.title} — ${timestamp}
1679
- `;
1680
- break;
1681
- case "blocker":
1682
- entry = `- BLOCKER: ${event.title}`;
1683
- break;
1684
- case "pr_opened":
1685
- entry = `- [ ] ${event.title} — PR opened ${timestamp}`;
1686
- break;
1687
- case "pr_reviewed":
1688
- entry = `- ${event.title} — reviewed ${timestamp}`;
1689
- break;
1690
- case "pr_merged":
1691
- entry = `- [x] ${event.title} — PR merged ${timestamp}`;
1692
- break;
1693
- case "exec_completed":
1694
- entry = `- [x] ${event.title} — exec ${timestamp}`;
1695
- break;
1696
- }
1697
- if (event.details) {
1698
- entry += `
1699
- ${event.details}`;
1700
- }
1701
- const updated = existing ? `${existing}
1702
- ${entry}` : `# Project Progress
1703
-
1704
- ${entry}`;
1705
- import_node_fs3.writeFileSync(this.progressPath, updated);
1706
- }
1707
- getFullContext() {
1708
- const context = this.readContext();
1709
- const progress = this.readProgress();
1710
- const parts = [];
1711
- if (context.trim()) {
1712
- parts.push(context.trim());
1713
- }
1714
- if (progress.trim()) {
1715
- parts.push(progress.trim());
1716
- }
1717
- return parts.join(`
1718
-
1719
- ---
1720
-
1721
- `);
1722
- }
1723
- initialize(info) {
1724
- this.ensureDir(this.contextPath);
1725
- this.ensureDir(this.progressPath);
1726
- const techStackList = info.techStack.map((t) => `- ${t}`).join(`
1727
- `);
1728
- const contextContent = `# Project: ${info.name}
1729
-
1730
- ## Mission
1731
- ${info.mission}
1732
-
1733
- ## Tech Stack
1734
- ${techStackList}
1735
-
1736
- ## Architecture
1737
- <!-- Describe your high-level architecture here -->
1738
-
1739
- ## Key Decisions
1740
- <!-- Document important technical decisions and their rationale -->
1741
-
1742
- ## Feature Areas
1743
- <!-- List your main feature areas and their status -->
1744
- `;
1745
- const progressContent = `# Project Progress
1746
-
1747
- No sprints started yet.
1748
- `;
1749
- import_node_fs3.writeFileSync(this.contextPath, contextContent);
1750
- import_node_fs3.writeFileSync(this.progressPath, progressContent);
1751
- }
1752
- get exists() {
1753
- return import_node_fs3.existsSync(this.contextPath) || import_node_fs3.existsSync(this.progressPath);
1754
- }
1755
- ensureDir(filePath) {
1756
- const dir = import_node_path5.dirname(filePath);
1757
- if (!import_node_fs3.existsSync(dir)) {
1758
- import_node_fs3.mkdirSync(dir, { recursive: true });
1759
- }
1760
- }
1761
- }
1762
-
1763
- // src/worktree/worktree-manager.ts
1764
- var import_node_child_process5 = require("node:child_process");
1765
- var import_node_fs4 = require("node:fs");
1766
- var import_node_path6 = require("node:path");
1865
+ var import_node_child_process4;
1866
+ var init_pr_service = __esm(() => {
1867
+ init_git_utils();
1868
+ import_node_child_process4 = require("node:child_process");
1869
+ });
1767
1870
 
1768
1871
  // src/worktree/worktree-config.ts
1769
- var WORKTREE_ROOT_DIR = ".locus-worktrees";
1770
- var WORKTREE_BRANCH_PREFIX = "agent";
1771
- var DEFAULT_WORKTREE_CONFIG = {
1772
- rootDir: WORKTREE_ROOT_DIR,
1773
- branchPrefix: WORKTREE_BRANCH_PREFIX,
1774
- cleanupPolicy: "retain-on-failure"
1775
- };
1872
+ var WORKTREE_ROOT_DIR = ".locus-worktrees", WORKTREE_BRANCH_PREFIX = "agent", DEFAULT_WORKTREE_CONFIG;
1873
+ var init_worktree_config = __esm(() => {
1874
+ DEFAULT_WORKTREE_CONFIG = {
1875
+ rootDir: WORKTREE_ROOT_DIR,
1876
+ branchPrefix: WORKTREE_BRANCH_PREFIX,
1877
+ cleanupPolicy: "retain-on-failure"
1878
+ };
1879
+ });
1776
1880
 
1777
1881
  // src/worktree/worktree-manager.ts
1778
1882
  class WorktreeManager {
@@ -1799,6 +1903,15 @@ class WorktreeManager {
1799
1903
  const worktreePath = import_node_path6.join(this.rootPath, worktreeDir);
1800
1904
  this.ensureDirectory(this.rootPath, "Worktree root");
1801
1905
  const baseBranch = options.baseBranch ?? this.config.baseBranch ?? this.getCurrentBranch();
1906
+ if (!this.branchExists(baseBranch)) {
1907
+ this.log(`Base branch "${baseBranch}" not found locally, fetching from origin`, "info");
1908
+ try {
1909
+ this.gitExec(["fetch", "origin", baseBranch], this.projectPath);
1910
+ this.gitExec(["branch", baseBranch, `origin/${baseBranch}`], this.projectPath);
1911
+ } catch {
1912
+ this.log(`Could not fetch/create local branch for "${baseBranch}", falling back to current branch`, "warn");
1913
+ }
1914
+ }
1802
1915
  this.log(`Creating worktree: ${worktreeDir} (branch: ${branch}, base: ${baseBranch})`, "info");
1803
1916
  if (import_node_fs4.existsSync(worktreePath)) {
1804
1917
  this.log(`Removing stale worktree directory: ${worktreePath}`, "warn");
@@ -2062,11 +2175,15 @@ class WorktreeManager {
2062
2175
  });
2063
2176
  }
2064
2177
  }
2178
+ var import_node_child_process5, import_node_fs4, import_node_path6;
2179
+ var init_worktree_manager = __esm(() => {
2180
+ init_worktree_config();
2181
+ import_node_child_process5 = require("node:child_process");
2182
+ import_node_fs4 = require("node:fs");
2183
+ import_node_path6 = require("node:path");
2184
+ });
2065
2185
 
2066
2186
  // src/core/prompt-builder.ts
2067
- var import_node_fs5 = require("node:fs");
2068
- var import_node_path7 = require("node:path");
2069
- var import_shared2 = require("@locusai/shared");
2070
2187
  class PromptBuilder {
2071
2188
  projectPath;
2072
2189
  constructor(projectPath) {
@@ -2362,6 +2479,13 @@ There is an index file in the .locus/codebase-index.json and if you need you can
2362
2479
  }
2363
2480
  }
2364
2481
  }
2482
+ var import_node_fs5, import_node_path7, import_shared2;
2483
+ var init_prompt_builder = __esm(() => {
2484
+ init_config();
2485
+ import_node_fs5 = require("node:fs");
2486
+ import_node_path7 = require("node:path");
2487
+ import_shared2 = require("@locusai/shared");
2488
+ });
2365
2489
 
2366
2490
  // src/agent/task-executor.ts
2367
2491
  class TaskExecutor {
@@ -2386,162 +2510,51 @@ class TaskExecutor {
2386
2510
  }
2387
2511
  }
2388
2512
  }
2513
+ var init_task_executor = __esm(() => {
2514
+ init_prompt_builder();
2515
+ });
2389
2516
 
2390
- // src/agent/worker.ts
2391
- function resolveProvider(value) {
2392
- if (!value || value.startsWith("--")) {
2393
- console.warn("Warning: --provider requires a value. Falling back to 'claude'.");
2394
- return PROVIDER.CLAUDE;
2395
- }
2396
- if (value === PROVIDER.CLAUDE || value === PROVIDER.CODEX)
2397
- return value;
2398
- console.warn(`Warning: invalid --provider value '${value}'. Falling back to 'claude'.`);
2399
- return PROVIDER.CLAUDE;
2400
- }
2401
-
2402
- class AgentWorker {
2517
+ // src/agent/git-workflow.ts
2518
+ class GitWorkflow {
2403
2519
  config;
2404
- client;
2405
- aiRunner;
2406
- taskExecutor;
2407
- knowledgeBase;
2408
- worktreeManager = null;
2409
- prService = null;
2410
- maxTasks = 50;
2411
- tasksCompleted = 0;
2412
- heartbeatInterval = null;
2413
- currentTaskId = null;
2414
- currentWorktreePath = null;
2415
- postCleanupDelayMs = 5000;
2416
- ghUsername = null;
2417
- constructor(config) {
2520
+ log;
2521
+ ghUsername;
2522
+ worktreeManager;
2523
+ prService;
2524
+ constructor(config, log, ghUsername) {
2418
2525
  this.config = config;
2526
+ this.log = log;
2527
+ this.ghUsername = ghUsername;
2419
2528
  const projectPath = config.projectPath || process.cwd();
2420
- this.client = new LocusClient({
2421
- baseUrl: config.apiBase,
2422
- token: config.apiKey,
2423
- retryOptions: {
2424
- maxRetries: 3,
2425
- initialDelay: 1000,
2426
- maxDelay: 5000,
2427
- factor: 2
2428
- }
2429
- });
2430
- const log = this.log.bind(this);
2431
- if (config.useWorktrees && !isGitAvailable()) {
2432
- this.log("git is not installed — worktree isolation will not work", "error");
2433
- config.useWorktrees = false;
2434
- }
2435
- if (config.autoPush && !isGhAvailable(projectPath)) {
2436
- this.log("GitHub CLI (gh) not available or not authenticated. Branch push can continue, but automatic PR creation may fail until gh is configured. Install from https://cli.github.com/", "warn");
2437
- }
2438
- if (config.autoPush) {
2439
- this.ghUsername = getGhUsername();
2440
- if (this.ghUsername) {
2441
- this.log(`GitHub user: ${this.ghUsername}`, "info");
2442
- }
2443
- }
2444
- const provider = config.provider ?? PROVIDER.CLAUDE;
2445
- this.aiRunner = createAiRunner(provider, {
2446
- projectPath,
2447
- model: config.model,
2448
- log
2449
- });
2450
- this.taskExecutor = new TaskExecutor({
2451
- aiRunner: this.aiRunner,
2452
- projectPath,
2453
- log
2454
- });
2455
- this.knowledgeBase = new KnowledgeBase(projectPath);
2456
- if (config.useWorktrees) {
2457
- this.worktreeManager = new WorktreeManager(projectPath, {
2458
- cleanupPolicy: "auto"
2459
- });
2460
- }
2461
- if (config.autoPush) {
2462
- this.prService = new PrService(projectPath, log);
2463
- }
2464
- const providerLabel = provider === "codex" ? "Codex" : "Claude";
2465
- this.log(`Using ${providerLabel} CLI for all phases`, "info");
2466
- if (config.useWorktrees) {
2467
- this.log("Per-task worktree isolation enabled", "info");
2468
- if (config.autoPush) {
2469
- this.log("Auto-push enabled: branches will be pushed to remote", "info");
2470
- }
2471
- }
2472
- }
2473
- log(message, level = "info") {
2474
- const timestamp = new Date().toISOString().split("T")[1]?.slice(0, 8) ?? "";
2475
- const colorFn = {
2476
- info: c.cyan,
2477
- success: c.green,
2478
- warn: c.yellow,
2479
- error: c.red
2480
- }[level];
2481
- const prefix = { info: "ℹ", success: "✓", warn: "⚠", error: "✗" }[level];
2482
- console.log(`${c.dim(`[${timestamp}]`)} ${c.bold(`[${this.config.agentId.slice(-8)}]`)} ${colorFn(`${prefix} ${message}`)}`);
2483
- }
2484
- async getActiveSprint() {
2485
- try {
2486
- if (this.config.sprintId) {
2487
- return await this.client.sprints.getById(this.config.sprintId, this.config.workspaceId);
2488
- }
2489
- return await this.client.sprints.getActive(this.config.workspaceId);
2490
- } catch (_error) {
2491
- return null;
2492
- }
2493
- }
2494
- async getNextTask() {
2495
- const maxRetries = 10;
2496
- for (let attempt = 1;attempt <= maxRetries; attempt++) {
2497
- try {
2498
- const task = await this.client.workspaces.dispatch(this.config.workspaceId, this.config.agentId, this.config.sprintId);
2499
- return task;
2500
- } catch (error) {
2501
- const isAxiosError = error != null && typeof error === "object" && "response" in error && typeof error.response?.status === "number";
2502
- const status = isAxiosError ? error.response.status : 0;
2503
- if (status === 404) {
2504
- this.log("No tasks available in the backlog.", "info");
2505
- return null;
2506
- }
2507
- const msg = error instanceof Error ? error.message : String(error);
2508
- if (attempt < maxRetries) {
2509
- this.log(`Nothing dispatched (attempt ${attempt}/${maxRetries}): ${msg}. Retrying in 30s...`, "warn");
2510
- await new Promise((r) => setTimeout(r, 30000));
2511
- } else {
2512
- this.log(`Nothing dispatched after ${maxRetries} attempts: ${msg}`, "warn");
2513
- return null;
2514
- }
2515
- }
2516
- }
2517
- return null;
2518
- }
2519
- createTaskWorktree(task) {
2520
- if (!this.worktreeManager) {
2521
- return {
2522
- worktreePath: null,
2523
- baseBranch: null,
2524
- executor: this.taskExecutor
2525
- };
2529
+ this.worktreeManager = config.useWorktrees ? new WorktreeManager(projectPath, { cleanupPolicy: "auto" }) : null;
2530
+ this.prService = config.autoPush ? new PrService(projectPath, log) : null;
2531
+ }
2532
+ createTaskWorktree(task, defaultExecutor) {
2533
+ if (!this.worktreeManager) {
2534
+ return {
2535
+ worktreePath: null,
2536
+ baseBranch: null,
2537
+ executor: defaultExecutor
2538
+ };
2526
2539
  }
2527
2540
  const slug = task.title.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "").slice(0, 40);
2528
2541
  const result = this.worktreeManager.create({
2529
2542
  taskId: task.id,
2530
2543
  taskSlug: slug,
2531
- agentId: this.config.agentId
2544
+ agentId: this.config.agentId,
2545
+ baseBranch: this.config.baseBranch
2532
2546
  });
2533
2547
  this.log(`Worktree created: ${result.worktreePath} (${result.branch})`, "info");
2534
- const log = this.log.bind(this);
2535
2548
  const provider = this.config.provider ?? PROVIDER.CLAUDE;
2536
2549
  const taskAiRunner = createAiRunner(provider, {
2537
2550
  projectPath: result.worktreePath,
2538
2551
  model: this.config.model,
2539
- log
2552
+ log: this.log
2540
2553
  });
2541
2554
  const taskExecutor = new TaskExecutor({
2542
2555
  aiRunner: taskAiRunner,
2543
2556
  projectPath: result.worktreePath,
2544
- log
2557
+ log: this.log
2545
2558
  });
2546
2559
  return {
2547
2560
  worktreePath: result.worktreePath,
@@ -2549,7 +2562,7 @@ class AgentWorker {
2549
2562
  executor: taskExecutor
2550
2563
  };
2551
2564
  }
2552
- commitAndPushWorktree(worktreePath, task, baseBranch) {
2565
+ commitAndPush(worktreePath, task, baseBranch) {
2553
2566
  if (!this.worktreeManager) {
2554
2567
  return { branch: null, pushed: false, pushFailed: false };
2555
2568
  }
@@ -2631,7 +2644,7 @@ ${trailers.join(`
2631
2644
  return { url: null, error: errorMessage };
2632
2645
  }
2633
2646
  }
2634
- cleanupTaskWorktree(worktreePath, keepBranch) {
2647
+ cleanupWorktree(worktreePath, keepBranch) {
2635
2648
  if (!this.worktreeManager || !worktreePath)
2636
2649
  return;
2637
2650
  try {
@@ -2640,11 +2653,205 @@ ${trailers.join(`
2640
2653
  } catch {
2641
2654
  this.log(`Could not clean up worktree: ${worktreePath}`, "warn");
2642
2655
  }
2643
- this.currentWorktreePath = null;
2656
+ }
2657
+ }
2658
+ var init_git_workflow = __esm(() => {
2659
+ init_factory();
2660
+ init_config();
2661
+ init_pr_service();
2662
+ init_worktree_manager();
2663
+ init_task_executor();
2664
+ });
2665
+
2666
+ // src/agent/worker-cli.ts
2667
+ var exports_worker_cli = {};
2668
+ __export(exports_worker_cli, {
2669
+ parseWorkerArgs: () => parseWorkerArgs
2670
+ });
2671
+ function resolveProvider(value) {
2672
+ if (!value || value.startsWith("--")) {
2673
+ console.warn("Warning: --provider requires a value. Falling back to 'claude'.");
2674
+ return PROVIDER.CLAUDE;
2675
+ }
2676
+ if (value === PROVIDER.CLAUDE || value === PROVIDER.CODEX)
2677
+ return value;
2678
+ console.warn(`Warning: invalid --provider value '${value}'. Falling back to 'claude'.`);
2679
+ return PROVIDER.CLAUDE;
2680
+ }
2681
+ function parseWorkerArgs(argv) {
2682
+ const args = argv.slice(2);
2683
+ const config = {};
2684
+ for (let i = 0;i < args.length; i++) {
2685
+ const arg = args[i];
2686
+ if (arg === "--agent-id")
2687
+ config.agentId = args[++i];
2688
+ else if (arg === "--workspace-id")
2689
+ config.workspaceId = args[++i];
2690
+ else if (arg === "--sprint-id")
2691
+ config.sprintId = args[++i];
2692
+ else if (arg === "--api-url")
2693
+ config.apiBase = args[++i];
2694
+ else if (arg === "--api-key")
2695
+ config.apiKey = args[++i];
2696
+ else if (arg === "--project-path")
2697
+ config.projectPath = args[++i];
2698
+ else if (arg === "--main-project-path")
2699
+ config.mainProjectPath = args[++i];
2700
+ else if (arg === "--model")
2701
+ config.model = args[++i];
2702
+ else if (arg === "--use-worktrees")
2703
+ config.useWorktrees = true;
2704
+ else if (arg === "--auto-push")
2705
+ config.autoPush = true;
2706
+ else if (arg === "--base-branch")
2707
+ config.baseBranch = args[++i];
2708
+ else if (arg === "--provider") {
2709
+ const value = args[i + 1];
2710
+ if (value && !value.startsWith("--"))
2711
+ i++;
2712
+ config.provider = resolveProvider(value);
2713
+ }
2714
+ }
2715
+ if (!config.agentId || !config.workspaceId || !config.apiBase || !config.apiKey || !config.projectPath) {
2716
+ console.error("Missing required arguments");
2717
+ process.exit(1);
2718
+ }
2719
+ return config;
2720
+ }
2721
+ var entrypoint;
2722
+ var init_worker_cli = __esm(() => {
2723
+ init_config();
2724
+ init_worker();
2725
+ entrypoint = process.argv[1]?.split(/[\\/]/).pop();
2726
+ if (entrypoint === "worker-cli.js" || entrypoint === "worker-cli.ts") {
2727
+ process.title = "locus-worker";
2728
+ const config = parseWorkerArgs(process.argv);
2729
+ const worker = new AgentWorker(config);
2730
+ worker.run().catch((err) => {
2731
+ console.error("Fatal worker error:", err);
2732
+ process.exit(1);
2733
+ });
2734
+ }
2735
+ });
2736
+
2737
+ // src/agent/worker.ts
2738
+ var exports_worker = {};
2739
+ __export(exports_worker, {
2740
+ AgentWorker: () => AgentWorker
2741
+ });
2742
+ module.exports = __toCommonJS(exports_worker);
2743
+
2744
+ class AgentWorker {
2745
+ config;
2746
+ client;
2747
+ aiRunner;
2748
+ taskExecutor;
2749
+ knowledgeBase;
2750
+ gitWorkflow;
2751
+ maxTasks = 50;
2752
+ tasksCompleted = 0;
2753
+ heartbeatInterval = null;
2754
+ currentTaskId = null;
2755
+ currentWorktreePath = null;
2756
+ postCleanupDelayMs = 5000;
2757
+ constructor(config) {
2758
+ this.config = config;
2759
+ const projectPath = config.projectPath || process.cwd();
2760
+ this.client = new LocusClient({
2761
+ baseUrl: config.apiBase,
2762
+ token: config.apiKey,
2763
+ retryOptions: {
2764
+ maxRetries: 3,
2765
+ initialDelay: 1000,
2766
+ maxDelay: 5000,
2767
+ factor: 2
2768
+ }
2769
+ });
2770
+ const log = this.log.bind(this);
2771
+ if (config.useWorktrees && !isGitAvailable()) {
2772
+ this.log("git is not installed — worktree isolation will not work", "error");
2773
+ config.useWorktrees = false;
2774
+ }
2775
+ if (config.autoPush && !isGhAvailable(projectPath)) {
2776
+ this.log("GitHub CLI (gh) not available or not authenticated. Branch push can continue, but automatic PR creation may fail until gh is configured. Install from https://cli.github.com/", "warn");
2777
+ }
2778
+ const ghUsername = config.autoPush ? getGhUsername() : null;
2779
+ if (ghUsername) {
2780
+ this.log(`GitHub user: ${ghUsername}`, "info");
2781
+ }
2782
+ const provider = config.provider ?? PROVIDER.CLAUDE;
2783
+ this.aiRunner = createAiRunner(provider, {
2784
+ projectPath,
2785
+ model: config.model,
2786
+ log
2787
+ });
2788
+ this.taskExecutor = new TaskExecutor({
2789
+ aiRunner: this.aiRunner,
2790
+ projectPath,
2791
+ log
2792
+ });
2793
+ this.knowledgeBase = new KnowledgeBase(projectPath);
2794
+ this.gitWorkflow = new GitWorkflow(config, log, ghUsername);
2795
+ const providerLabel = provider === "codex" ? "Codex" : "Claude";
2796
+ this.log(`Using ${providerLabel} CLI for all phases`, "info");
2797
+ if (config.useWorktrees) {
2798
+ this.log("Per-task worktree isolation enabled", "info");
2799
+ if (config.baseBranch) {
2800
+ this.log(`Base branch for worktrees: ${config.baseBranch}`, "info");
2801
+ }
2802
+ if (config.autoPush) {
2803
+ this.log("Auto-push enabled: branches will be pushed to remote", "info");
2804
+ }
2805
+ }
2806
+ }
2807
+ log(message, level = "info") {
2808
+ const timestamp = new Date().toISOString().split("T")[1]?.slice(0, 8) ?? "";
2809
+ const colorFn = {
2810
+ info: c.cyan,
2811
+ success: c.green,
2812
+ warn: c.yellow,
2813
+ error: c.red
2814
+ }[level];
2815
+ const prefix = { info: "ℹ", success: "✓", warn: "⚠", error: "✗" }[level];
2816
+ console.log(`${c.dim(`[${timestamp}]`)} ${c.bold(`[${this.config.agentId.slice(-8)}]`)} ${colorFn(`${prefix} ${message}`)}`);
2817
+ }
2818
+ async getActiveSprint() {
2819
+ try {
2820
+ if (this.config.sprintId) {
2821
+ return await this.client.sprints.getById(this.config.sprintId, this.config.workspaceId);
2822
+ }
2823
+ return await this.client.sprints.getActive(this.config.workspaceId);
2824
+ } catch (_error) {
2825
+ return null;
2826
+ }
2827
+ }
2828
+ async getNextTask() {
2829
+ const maxRetries = 10;
2830
+ for (let attempt = 1;attempt <= maxRetries; attempt++) {
2831
+ try {
2832
+ return await this.client.workspaces.dispatch(this.config.workspaceId, this.config.agentId, this.config.sprintId);
2833
+ } catch (error) {
2834
+ const isAxiosError = error != null && typeof error === "object" && "response" in error && typeof error.response?.status === "number";
2835
+ const status = isAxiosError ? error.response.status : 0;
2836
+ if (status === 404) {
2837
+ this.log("No tasks available in the backlog.", "info");
2838
+ return null;
2839
+ }
2840
+ const msg = error instanceof Error ? error.message : String(error);
2841
+ if (attempt < maxRetries) {
2842
+ this.log(`Nothing dispatched (attempt ${attempt}/${maxRetries}): ${msg}. Retrying in 30s...`, "warn");
2843
+ await new Promise((r) => setTimeout(r, 30000));
2844
+ } else {
2845
+ this.log(`Nothing dispatched after ${maxRetries} attempts: ${msg}`, "warn");
2846
+ return null;
2847
+ }
2848
+ }
2849
+ }
2850
+ return null;
2644
2851
  }
2645
2852
  async executeTask(task) {
2646
2853
  const fullTask = await this.client.tasks.getById(task.id, this.config.workspaceId);
2647
- const { worktreePath, baseBranch, executor } = this.createTaskWorktree(fullTask);
2854
+ const { worktreePath, baseBranch, executor } = this.gitWorkflow.createTaskWorktree(fullTask, this.taskExecutor);
2648
2855
  this.currentWorktreePath = worktreePath;
2649
2856
  let branchPushed = false;
2650
2857
  let keepBranch = false;
@@ -2656,7 +2863,7 @@ ${trailers.join(`
2656
2863
  let prError = null;
2657
2864
  let noChanges = false;
2658
2865
  if (result.success && worktreePath) {
2659
- const commitResult = this.commitAndPushWorktree(worktreePath, fullTask, baseBranch ?? undefined);
2866
+ const commitResult = this.gitWorkflow.commitAndPush(worktreePath, fullTask, baseBranch ?? undefined);
2660
2867
  taskBranch = commitResult.branch;
2661
2868
  branchPushed = commitResult.pushed;
2662
2869
  keepBranch = taskBranch !== null;
@@ -2667,7 +2874,7 @@ ${trailers.join(`
2667
2874
  this.log(`Preserving worktree after push failure: ${worktreePath}`, "warn");
2668
2875
  }
2669
2876
  if (branchPushed && taskBranch) {
2670
- const prResult = this.createPullRequest(fullTask, taskBranch, result.summary, baseBranch ?? undefined);
2877
+ const prResult = this.gitWorkflow.createPullRequest(fullTask, taskBranch, result.summary, baseBranch ?? undefined);
2671
2878
  prUrl = prResult.url;
2672
2879
  prError = prResult.error ?? null;
2673
2880
  if (!prUrl) {
@@ -2691,29 +2898,29 @@ ${trailers.join(`
2691
2898
  if (preserveWorktree || keepBranch) {
2692
2899
  this.currentWorktreePath = null;
2693
2900
  } else {
2694
- this.cleanupTaskWorktree(worktreePath, keepBranch);
2901
+ this.gitWorkflow.cleanupWorktree(worktreePath, keepBranch);
2902
+ this.currentWorktreePath = null;
2695
2903
  }
2696
2904
  }
2697
2905
  }
2698
- updateProgress(task, success) {
2906
+ updateProgress(task, summary) {
2699
2907
  try {
2700
- if (success) {
2701
- this.knowledgeBase.updateProgress({
2702
- type: "task_completed",
2703
- title: task.title,
2704
- details: `Agent: ${this.config.agentId.slice(-8)}`
2705
- });
2706
- this.log(`Updated progress.md: ${task.title}`, "info");
2707
- }
2908
+ this.knowledgeBase.updateProgress({
2909
+ role: "user",
2910
+ content: task.title
2911
+ });
2912
+ this.knowledgeBase.updateProgress({
2913
+ role: "assistant",
2914
+ content: summary
2915
+ });
2916
+ this.log(`Updated progress.md: ${task.title}`, "info");
2708
2917
  } catch (err) {
2709
2918
  this.log(`Failed to update progress: ${err instanceof Error ? err.message : String(err)}`, "warn");
2710
2919
  }
2711
2920
  }
2712
2921
  startHeartbeat() {
2713
2922
  this.sendHeartbeat();
2714
- this.heartbeatInterval = setInterval(() => {
2715
- this.sendHeartbeat();
2716
- }, 60000);
2923
+ this.heartbeatInterval = setInterval(() => this.sendHeartbeat(), 60000);
2717
2924
  }
2718
2925
  stopHeartbeat() {
2719
2926
  if (this.heartbeatInterval) {
@@ -2738,7 +2945,7 @@ ${trailers.join(`
2738
2945
  this.log("Received shutdown signal. Aborting...", "warn");
2739
2946
  this.aiRunner.abort();
2740
2947
  this.stopHeartbeat();
2741
- this.cleanupTaskWorktree(this.currentWorktreePath, false);
2948
+ this.gitWorkflow.cleanupWorktree(this.currentWorktreePath, false);
2742
2949
  process.exit(1);
2743
2950
  };
2744
2951
  process.on("SIGTERM", handleShutdown);
@@ -2794,16 +3001,7 @@ PR automation error: ${result.prError}` : "";
2794
3001
  text: `✅ ${result.summary}${branchInfo}${prInfo}${prErrorInfo}`
2795
3002
  });
2796
3003
  this.tasksCompleted++;
2797
- this.updateProgress(task, true);
2798
- if (result.prUrl) {
2799
- try {
2800
- this.knowledgeBase.updateProgress({
2801
- type: "pr_opened",
2802
- title: task.title,
2803
- details: `PR: ${result.prUrl}`
2804
- });
2805
- } catch {}
2806
- }
3004
+ this.updateProgress(task, result.summary);
2807
3005
  }
2808
3006
  } else {
2809
3007
  this.log(`Failed: ${task.title} - ${result.summary}`, "error");
@@ -2826,47 +3024,28 @@ PR automation error: ${result.prError}` : "";
2826
3024
  process.exit(0);
2827
3025
  }
2828
3026
  }
2829
- var workerEntrypoint = process.argv[1]?.split(/[\\/]/).pop();
2830
- if (workerEntrypoint === "worker.js" || workerEntrypoint === "worker.ts") {
2831
- process.title = "locus-worker";
2832
- const args = process.argv.slice(2);
2833
- const config = {};
2834
- for (let i = 0;i < args.length; i++) {
2835
- const arg = args[i];
2836
- if (arg === "--agent-id")
2837
- config.agentId = args[++i];
2838
- else if (arg === "--workspace-id")
2839
- config.workspaceId = args[++i];
2840
- else if (arg === "--sprint-id")
2841
- config.sprintId = args[++i];
2842
- else if (arg === "--api-url")
2843
- config.apiBase = args[++i];
2844
- else if (arg === "--api-key")
2845
- config.apiKey = args[++i];
2846
- else if (arg === "--project-path")
2847
- config.projectPath = args[++i];
2848
- else if (arg === "--main-project-path")
2849
- config.mainProjectPath = args[++i];
2850
- else if (arg === "--model")
2851
- config.model = args[++i];
2852
- else if (arg === "--use-worktrees")
2853
- config.useWorktrees = true;
2854
- else if (arg === "--auto-push")
2855
- config.autoPush = true;
2856
- else if (arg === "--provider") {
2857
- const value = args[i + 1];
2858
- if (value && !value.startsWith("--"))
2859
- i++;
2860
- config.provider = resolveProvider(value);
2861
- }
2862
- }
2863
- if (!config.agentId || !config.workspaceId || !config.apiBase || !config.apiKey || !config.projectPath) {
2864
- console.error("Missing required arguments");
2865
- process.exit(1);
3027
+ var import_shared3, workerEntrypoint;
3028
+ var init_worker = __esm(() => {
3029
+ init_factory();
3030
+ init_config();
3031
+ init_git_utils();
3032
+ init_src();
3033
+ init_knowledge_base();
3034
+ init_colors();
3035
+ init_git_workflow();
3036
+ init_task_executor();
3037
+ import_shared3 = require("@locusai/shared");
3038
+ workerEntrypoint = process.argv[1]?.split(/[\\/]/).pop();
3039
+ if (workerEntrypoint === "worker.js" || workerEntrypoint === "worker.ts") {
3040
+ process.title = "locus-worker";
3041
+ Promise.resolve().then(() => (init_worker_cli(), exports_worker_cli)).then(({ parseWorkerArgs: parseWorkerArgs2 }) => {
3042
+ const config = parseWorkerArgs2(process.argv);
3043
+ const worker = new AgentWorker(config);
3044
+ worker.run().catch((err) => {
3045
+ console.error("Fatal worker error:", err);
3046
+ process.exit(1);
3047
+ });
3048
+ });
2866
3049
  }
2867
- const worker = new AgentWorker(config);
2868
- worker.run().catch((err) => {
2869
- console.error("Fatal worker error:", err);
2870
- process.exit(1);
2871
- });
2872
- }
3050
+ });
3051
+ init_worker();