@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.
- package/dist/agent/git-workflow.d.ts +44 -0
- package/dist/agent/git-workflow.d.ts.map +1 -0
- package/dist/agent/index.d.ts +2 -0
- package/dist/agent/index.d.ts.map +1 -1
- package/dist/agent/reviewer-worker.d.ts.map +1 -1
- package/dist/agent/worker-cli.d.ts +6 -0
- package/dist/agent/worker-cli.d.ts.map +1 -0
- package/dist/agent/worker-types.d.ts +44 -0
- package/dist/agent/worker-types.d.ts.map +1 -0
- package/dist/agent/worker.d.ts +12 -48
- package/dist/agent/worker.d.ts.map +1 -1
- package/dist/agent/worker.js +1026 -847
- package/dist/ai/claude-runner.d.ts.map +1 -1
- package/dist/index-node.d.ts +1 -1
- package/dist/index-node.d.ts.map +1 -1
- package/dist/index-node.js +1660 -1133
- package/dist/index.js +363 -316
- package/dist/orchestrator/agent-pool.d.ts +59 -0
- package/dist/orchestrator/agent-pool.d.ts.map +1 -0
- package/dist/orchestrator/execution.d.ts +55 -0
- package/dist/orchestrator/execution.d.ts.map +1 -0
- package/dist/orchestrator/index.d.ts +91 -0
- package/dist/orchestrator/index.d.ts.map +1 -0
- package/dist/orchestrator/tier-merge.d.ts +50 -0
- package/dist/orchestrator/tier-merge.d.ts.map +1 -0
- package/dist/orchestrator/types.d.ts +45 -0
- package/dist/orchestrator/types.d.ts.map +1 -0
- package/dist/planning/agents/cross-task-reviewer.d.ts.map +1 -1
- package/dist/planning/agents/sprint-organizer.d.ts.map +1 -1
- package/dist/planning/plan-manager.d.ts.map +1 -1
- package/dist/planning/sprint-plan.d.ts +2 -0
- package/dist/planning/sprint-plan.d.ts.map +1 -1
- package/dist/project/knowledge-base.d.ts +4 -5
- package/dist/project/knowledge-base.d.ts.map +1 -1
- package/dist/utils/resolve-bin.d.ts +3 -0
- package/dist/utils/resolve-bin.d.ts.map +1 -1
- package/dist/worktree/worktree-manager.d.ts.map +1 -1
- package/package.json +2 -2
- package/dist/orchestrator.d.ts +0 -124
- package/dist/orchestrator.d.ts.map +0 -1
package/dist/agent/worker.js
CHANGED
|
@@ -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
|
|
62
|
-
var
|
|
63
|
-
(
|
|
64
|
-
LocusEvent2
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
class LocusEmitter extends import_events.EventEmitter {
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
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
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
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
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
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
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
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
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
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
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
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
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
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
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
tasks = tasks
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
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
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
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
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
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
|
-
|
|
552
|
-
|
|
553
|
-
|
|
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
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
}
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
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
|
-
|
|
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
|
|
632
|
-
if (import_node_fs.existsSync(
|
|
633
|
-
|
|
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
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
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
|
-
|
|
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 = [
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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/
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
this.
|
|
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
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
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
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
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
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
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/
|
|
2391
|
-
|
|
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
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
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.
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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.
|
|
2901
|
+
this.gitWorkflow.cleanupWorktree(worktreePath, keepBranch);
|
|
2902
|
+
this.currentWorktreePath = null;
|
|
2695
2903
|
}
|
|
2696
2904
|
}
|
|
2697
2905
|
}
|
|
2698
|
-
updateProgress(task,
|
|
2906
|
+
updateProgress(task, summary) {
|
|
2699
2907
|
try {
|
|
2700
|
-
|
|
2701
|
-
|
|
2702
|
-
|
|
2703
|
-
|
|
2704
|
-
|
|
2705
|
-
|
|
2706
|
-
|
|
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.
|
|
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,
|
|
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
|
|
2830
|
-
|
|
2831
|
-
|
|
2832
|
-
|
|
2833
|
-
|
|
2834
|
-
|
|
2835
|
-
|
|
2836
|
-
|
|
2837
|
-
|
|
2838
|
-
|
|
2839
|
-
|
|
2840
|
-
|
|
2841
|
-
|
|
2842
|
-
|
|
2843
|
-
|
|
2844
|
-
|
|
2845
|
-
|
|
2846
|
-
|
|
2847
|
-
|
|
2848
|
-
|
|
2849
|
-
|
|
2850
|
-
|
|
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
|
-
|
|
2868
|
-
|
|
2869
|
-
console.error("Fatal worker error:", err);
|
|
2870
|
-
process.exit(1);
|
|
2871
|
-
});
|
|
2872
|
-
}
|
|
3050
|
+
});
|
|
3051
|
+
init_worker();
|