@jait/gateway 0.1.64 → 0.1.66

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 (52) hide show
  1. package/dist/db/migrations.d.ts.map +1 -1
  2. package/dist/db/migrations.js +55 -0
  3. package/dist/db/migrations.js.map +1 -1
  4. package/dist/db/schema.d.ts +392 -0
  5. package/dist/db/schema.d.ts.map +1 -1
  6. package/dist/db/schema.js +31 -0
  7. package/dist/db/schema.js.map +1 -1
  8. package/dist/index.d.ts.map +1 -1
  9. package/dist/index.js +49 -0
  10. package/dist/index.js.map +1 -1
  11. package/dist/routes/network.d.ts +3 -1
  12. package/dist/routes/network.d.ts.map +1 -1
  13. package/dist/routes/network.js +268 -13
  14. package/dist/routes/network.js.map +1 -1
  15. package/dist/routes/plans.d.ts +30 -0
  16. package/dist/routes/plans.d.ts.map +1 -0
  17. package/dist/routes/plans.js +328 -0
  18. package/dist/routes/plans.js.map +1 -0
  19. package/dist/routes/repositories.d.ts +2 -0
  20. package/dist/routes/repositories.d.ts.map +1 -1
  21. package/dist/routes/repositories.js +161 -1
  22. package/dist/routes/repositories.js.map +1 -1
  23. package/dist/routes/threads.d.ts.map +1 -1
  24. package/dist/routes/threads.js +10 -1
  25. package/dist/routes/threads.js.map +1 -1
  26. package/dist/server.d.ts +2 -0
  27. package/dist/server.d.ts.map +1 -1
  28. package/dist/server.js +14 -1
  29. package/dist/server.js.map +1 -1
  30. package/dist/services/plans.d.ts +47 -0
  31. package/dist/services/plans.d.ts.map +1 -0
  32. package/dist/services/plans.js +108 -0
  33. package/dist/services/plans.js.map +1 -0
  34. package/dist/services/repositories.d.ts +1 -0
  35. package/dist/services/repositories.d.ts.map +1 -1
  36. package/dist/services/repositories.js.map +1 -1
  37. package/dist/tools/network-tools.d.ts +3 -0
  38. package/dist/tools/network-tools.d.ts.map +1 -1
  39. package/dist/tools/network-tools.js +99 -1
  40. package/dist/tools/network-tools.js.map +1 -1
  41. package/dist/ws.d.ts +2 -0
  42. package/dist/ws.d.ts.map +1 -1
  43. package/dist/ws.js +3 -0
  44. package/dist/ws.js.map +1 -1
  45. package/package.json +2 -2
  46. package/web-dist/assets/index-CD3glXmS.css +32 -0
  47. package/web-dist/assets/{index-BYcgrcMm.js → index-ZIbq9Hg7.js} +2 -2
  48. package/web-dist/assets/index-l2kzxway.js +799 -0
  49. package/web-dist/assets/{web-Dh9DfBu3.js → web-xVXcdViE.js} +1 -1
  50. package/web-dist/index.html +2 -2
  51. package/web-dist/assets/index-DkVzT5Nb.js +0 -734
  52. package/web-dist/assets/index-DxsTQ8KW.css +0 -32
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Automation Plan REST routes.
3
+ *
4
+ * GET /api/repos/:repoId/plans — list plans for a repo
5
+ * POST /api/repos/:repoId/plans — create a plan
6
+ * GET /api/plans/:id — get a plan
7
+ * PATCH /api/plans/:id — update plan
8
+ * DELETE /api/plans/:id — delete a plan
9
+ * POST /api/plans/:id/generate — AI-generate tasks
10
+ * POST /api/plans/:id/tasks/:taskId/start — start a single task
11
+ * POST /api/plans/:id/start — start all approved tasks
12
+ */
13
+ import type { FastifyInstance } from "fastify";
14
+ import type { AppConfig } from "../config.js";
15
+ import type { PlanService } from "../services/plans.js";
16
+ import type { RepositoryService } from "../services/repositories.js";
17
+ import type { ThreadService } from "../services/threads.js";
18
+ import type { ProviderRegistry } from "../providers/registry.js";
19
+ import type { UserService } from "../services/users.js";
20
+ import type { WsControlPlane } from "../ws.js";
21
+ export interface PlanRouteDeps {
22
+ planService: PlanService;
23
+ repoService: RepositoryService;
24
+ threadService?: ThreadService;
25
+ providerRegistry?: ProviderRegistry;
26
+ userService?: UserService;
27
+ ws?: WsControlPlane;
28
+ }
29
+ export declare function registerPlanRoutes(app: FastifyInstance, config: AppConfig, deps: PlanRouteDeps): void;
30
+ //# sourceMappingURL=plans.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plans.d.ts","sourceRoot":"","sources":["../../src/routes/plans.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAC/C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,KAAK,EAAE,WAAW,EAAY,MAAM,sBAAsB,CAAC;AAElE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AACrE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAC5D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AACjE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACxD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAM/C,MAAM,WAAW,aAAa;IAC5B,WAAW,EAAE,WAAW,CAAC;IACzB,WAAW,EAAE,iBAAiB,CAAC;IAC/B,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;IACpC,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,EAAE,CAAC,EAAE,cAAc,CAAC;CACrB;AAED,wBAAgB,kBAAkB,CAChC,GAAG,EAAE,eAAe,EACpB,MAAM,EAAE,SAAS,EACjB,IAAI,EAAE,aAAa,GAClB,IAAI,CAmUN"}
@@ -0,0 +1,328 @@
1
+ /**
2
+ * Automation Plan REST routes.
3
+ *
4
+ * GET /api/repos/:repoId/plans — list plans for a repo
5
+ * POST /api/repos/:repoId/plans — create a plan
6
+ * GET /api/plans/:id — get a plan
7
+ * PATCH /api/plans/:id — update plan
8
+ * DELETE /api/plans/:id — delete a plan
9
+ * POST /api/plans/:id/generate — AI-generate tasks
10
+ * POST /api/plans/:id/tasks/:taskId/start — start a single task
11
+ * POST /api/plans/:id/start — start all approved tasks
12
+ */
13
+ import { newTaskId } from "../services/plans.js";
14
+ import { requireAuth } from "../security/http-auth.js";
15
+ import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
16
+ import { join } from "node:path";
17
+ export function registerPlanRoutes(app, config, deps) {
18
+ const { planService, repoService, userService, ws } = deps;
19
+ function broadcastPlanEvent(event, data) {
20
+ if (!ws)
21
+ return;
22
+ ws.broadcastAll({
23
+ type: `plan.${event}`,
24
+ sessionId: "",
25
+ timestamp: new Date().toISOString(),
26
+ payload: data,
27
+ });
28
+ }
29
+ // ── LIST plans for a repo ────────────────────────────────────────
30
+ app.get("/api/repos/:repoId/plans", async (request, reply) => {
31
+ const user = await requireAuth(request, reply, config.jwtSecret);
32
+ if (!user)
33
+ return;
34
+ const plans = planService.listByRepo(request.params.repoId);
35
+ return { plans };
36
+ });
37
+ // ── CREATE plan ──────────────────────────────────────────────────
38
+ app.post("/api/repos/:repoId/plans", async (request, reply) => {
39
+ const user = await requireAuth(request, reply, config.jwtSecret);
40
+ if (!user)
41
+ return;
42
+ const repo = repoService.getById(request.params.repoId);
43
+ if (!repo) {
44
+ return reply.status(404).send({ error: "Repository not found" });
45
+ }
46
+ const body = request.body;
47
+ const plan = planService.create({
48
+ repoId: request.params.repoId,
49
+ userId: user.id,
50
+ title: body.title ?? "New Plan",
51
+ tasks: body.tasks,
52
+ });
53
+ broadcastPlanEvent("created", { plan });
54
+ return reply.status(201).send({ plan });
55
+ });
56
+ // ── GET plan ─────────────────────────────────────────────────────
57
+ app.get("/api/plans/:id", async (request, reply) => {
58
+ const user = await requireAuth(request, reply, config.jwtSecret);
59
+ if (!user)
60
+ return;
61
+ const plan = planService.getById(request.params.id);
62
+ if (!plan) {
63
+ return reply.status(404).send({ error: "Plan not found" });
64
+ }
65
+ return { plan };
66
+ });
67
+ // ── UPDATE plan ──────────────────────────────────────────────────
68
+ app.patch("/api/plans/:id", async (request, reply) => {
69
+ const user = await requireAuth(request, reply, config.jwtSecret);
70
+ if (!user)
71
+ return;
72
+ const body = request.body;
73
+ const plan = planService.update(request.params.id, {
74
+ title: body.title,
75
+ status: body.status === "draft" || body.status === "active" || body.status === "completed" || body.status === "archived"
76
+ ? body.status
77
+ : undefined,
78
+ tasks: body.tasks,
79
+ });
80
+ if (!plan) {
81
+ return reply.status(404).send({ error: "Plan not found" });
82
+ }
83
+ broadcastPlanEvent("updated", { plan });
84
+ return { plan };
85
+ });
86
+ // ── DELETE plan ──────────────────────────────────────────────────
87
+ app.delete("/api/plans/:id", async (request, reply) => {
88
+ const user = await requireAuth(request, reply, config.jwtSecret);
89
+ if (!user)
90
+ return;
91
+ const existing = planService.getById(request.params.id);
92
+ if (!existing) {
93
+ return reply.status(404).send({ error: "Plan not found" });
94
+ }
95
+ planService.delete(request.params.id);
96
+ broadcastPlanEvent("deleted", { planId: request.params.id });
97
+ return { ok: true };
98
+ });
99
+ // ── GENERATE tasks via LLM ───────────────────────────────────────
100
+ app.post("/api/plans/:id/generate", async (request, reply) => {
101
+ const user = await requireAuth(request, reply, config.jwtSecret);
102
+ if (!user)
103
+ return;
104
+ const plan = planService.getById(request.params.id);
105
+ if (!plan) {
106
+ return reply.status(404).send({ error: "Plan not found" });
107
+ }
108
+ const repo = repoService.getById(plan.repoId);
109
+ if (!repo) {
110
+ return reply.status(404).send({ error: "Repository not found" });
111
+ }
112
+ const body = request.body;
113
+ const userPromptHint = (body?.prompt ?? "").trim();
114
+ // Gather repo context
115
+ let repoContext = "";
116
+ if (existsSync(repo.localPath)) {
117
+ repoContext = gatherRepoContext(repo.localPath);
118
+ }
119
+ // Include strategy if available
120
+ const strategySection = repo.strategy?.trim()
121
+ ? `\n\n### Repository Strategy\n${repo.strategy.trim()}`
122
+ : "";
123
+ const systemPrompt = [
124
+ "You are a senior engineering manager planning work for an AI coding agent.",
125
+ "Given a repository's context, generate a list of concrete, actionable tasks",
126
+ "that can each be executed as an independent agent thread.",
127
+ "Each task should be small enough to complete in one session (a few files at most).",
128
+ "Tasks can run in parallel unless they have explicit dependencies.",
129
+ "",
130
+ "Respond with a JSON array of task objects. Each object must have:",
131
+ ' { "title": "short title", "description": "detailed instruction for the agent" }',
132
+ "",
133
+ "Output ONLY the JSON array, no markdown fences, no explanation.",
134
+ ].join("\n");
135
+ const userContent = [
136
+ userPromptHint ? `The user wants: ${userPromptHint}\n` : "",
137
+ `Repository: ${repo.name}`,
138
+ strategySection,
139
+ repoContext ? `\n\nRepository files:\n${repoContext}` : "",
140
+ ].join("\n");
141
+ // Resolve API keys
142
+ const apiKeys = userService?.getSettings(user.id).apiKeys ?? {};
143
+ const apiKey = apiKeys["OPENAI_API_KEY"]?.trim() || config.openaiApiKey;
144
+ const baseUrl = (apiKeys["OPENAI_BASE_URL"]?.trim() || config.openaiBaseUrl).replace(/\/+$/, "");
145
+ const model = apiKeys["OPENAI_MODEL"]?.trim() || config.openaiModel;
146
+ if (!apiKey && config.llmProvider === "openai") {
147
+ return reply.status(400).send({ error: "No API key configured." });
148
+ }
149
+ try {
150
+ let rawJson;
151
+ if (apiKey || config.llmProvider === "openai") {
152
+ const response = await fetch(`${baseUrl}/chat/completions`, {
153
+ method: "POST",
154
+ headers: {
155
+ "Content-Type": "application/json",
156
+ Authorization: `Bearer ${apiKey}`,
157
+ },
158
+ body: JSON.stringify({
159
+ model,
160
+ temperature: 0.4,
161
+ max_tokens: 3000,
162
+ messages: [
163
+ { role: "system", content: systemPrompt },
164
+ { role: "user", content: userContent },
165
+ ],
166
+ }),
167
+ });
168
+ if (!response.ok)
169
+ throw new Error(`LLM API returned ${response.status}`);
170
+ const data = await response.json();
171
+ rawJson = data.choices?.[0]?.message?.content?.trim() ?? "[]";
172
+ }
173
+ else {
174
+ const response = await fetch(`${config.ollamaUrl.replace(/\/+$/, "")}/api/chat`, {
175
+ method: "POST",
176
+ headers: { "Content-Type": "application/json" },
177
+ body: JSON.stringify({
178
+ model: config.ollamaModel,
179
+ stream: false,
180
+ messages: [
181
+ { role: "system", content: systemPrompt },
182
+ { role: "user", content: userContent },
183
+ ],
184
+ }),
185
+ });
186
+ if (!response.ok)
187
+ throw new Error(`Ollama API returned ${response.status}`);
188
+ const data = await response.json();
189
+ rawJson = data.message?.content?.trim() ?? "[]";
190
+ }
191
+ // Strip markdown fences if the model wrapped the output
192
+ rawJson = rawJson.replace(/^```(?:json)?\s*/i, "").replace(/\s*```$/i, "");
193
+ let generated;
194
+ try {
195
+ generated = JSON.parse(rawJson);
196
+ }
197
+ catch {
198
+ return reply.status(500).send({ error: "LLM returned invalid JSON", raw: rawJson });
199
+ }
200
+ if (!Array.isArray(generated)) {
201
+ return reply.status(500).send({ error: "LLM did not return an array" });
202
+ }
203
+ const tasks = generated
204
+ .filter((t) => t.title)
205
+ .map((t) => ({
206
+ id: newTaskId(),
207
+ title: t.title ?? "Untitled task",
208
+ description: t.description ?? "",
209
+ status: "proposed",
210
+ }));
211
+ // Merge with existing tasks (append generated ones)
212
+ const allTasks = [...plan.tasks, ...tasks];
213
+ const updated = planService.update(plan.id, { tasks: allTasks });
214
+ if (updated)
215
+ broadcastPlanEvent("updated", { plan: updated });
216
+ return { plan: updated, generated: tasks.length };
217
+ }
218
+ catch (err) {
219
+ const msg = err instanceof Error ? err.message : String(err);
220
+ return reply.status(500).send({ error: `Task generation failed: ${msg}` });
221
+ }
222
+ });
223
+ // ── START a single task ──────────────────────────────────────────
224
+ app.post("/api/plans/:id/tasks/:taskId/start", async (request, reply) => {
225
+ const user = await requireAuth(request, reply, config.jwtSecret);
226
+ if (!user)
227
+ return;
228
+ const plan = planService.getById(request.params.id);
229
+ if (!plan)
230
+ return reply.status(404).send({ error: "Plan not found" });
231
+ const task = plan.tasks.find((t) => t.id === request.params.taskId);
232
+ if (!task)
233
+ return reply.status(404).send({ error: "Task not found in plan" });
234
+ if (task.threadId) {
235
+ return reply.status(409).send({ error: "Task already has a thread" });
236
+ }
237
+ const repo = repoService.getById(plan.repoId);
238
+ if (!repo)
239
+ return reply.status(404).send({ error: "Repository not found" });
240
+ // The actual thread creation and start is handled by the frontend
241
+ // the task info — the frontend will create the thread using the
242
+ // existing agentsApi.createThread + startThread flow, then PATCH
243
+ // the plan task with the threadId. This keeps the plan routes simple
244
+ // and avoids duplicating the complex thread start logic.
245
+ // Mark task as approved if still proposed
246
+ if (task.status === "proposed") {
247
+ planService.updateTask(plan.id, task.id, { status: "approved" });
248
+ }
249
+ const updatedPlan = planService.getById(plan.id);
250
+ if (updatedPlan)
251
+ broadcastPlanEvent("updated", { plan: updatedPlan });
252
+ return {
253
+ task,
254
+ repo: {
255
+ id: repo.id,
256
+ name: repo.name,
257
+ localPath: repo.localPath,
258
+ defaultBranch: repo.defaultBranch,
259
+ githubUrl: repo.githubUrl,
260
+ },
261
+ };
262
+ });
263
+ // ── START all approved tasks ─────────────────────────────────────
264
+ app.post("/api/plans/:id/start", async (request, reply) => {
265
+ const user = await requireAuth(request, reply, config.jwtSecret);
266
+ if (!user)
267
+ return;
268
+ const plan = planService.getById(request.params.id);
269
+ if (!plan)
270
+ return reply.status(404).send({ error: "Plan not found" });
271
+ const approved = plan.tasks.filter((t) => t.status === "approved" && !t.threadId);
272
+ if (approved.length === 0) {
273
+ return reply.status(400).send({ error: "No approved tasks to start" });
274
+ }
275
+ // Mark plan as active
276
+ planService.update(plan.id, { status: "active" });
277
+ const repo = repoService.getById(plan.repoId);
278
+ if (!repo)
279
+ return reply.status(404).send({ error: "Repository not found" });
280
+ // Return the list of tasks to start — the frontend handles
281
+ // thread creation for each one (parallelizing as it sees fit).
282
+ const updatedPlan = planService.getById(plan.id);
283
+ if (updatedPlan)
284
+ broadcastPlanEvent("updated", { plan: updatedPlan });
285
+ return {
286
+ tasks: approved,
287
+ repo: {
288
+ id: repo.id,
289
+ name: repo.name,
290
+ localPath: repo.localPath,
291
+ defaultBranch: repo.defaultBranch,
292
+ githubUrl: repo.githubUrl,
293
+ },
294
+ };
295
+ });
296
+ }
297
+ // ── Helpers ──────────────────────────────────────────────────────────
298
+ function gatherRepoContext(repoPath) {
299
+ const sections = [];
300
+ const MAX_FILE_SIZE = 8000;
301
+ const keyFiles = [
302
+ "package.json", "README.md", "AGENTS.md", "CLAUDE.md",
303
+ ".github/copilot-instructions.md",
304
+ "Cargo.toml", "pyproject.toml", "go.mod",
305
+ "Makefile", "Dockerfile", "docker-compose.yml",
306
+ "tsconfig.json",
307
+ ];
308
+ for (const file of keyFiles) {
309
+ const fullPath = join(repoPath, file);
310
+ try {
311
+ if (existsSync(fullPath) && statSync(fullPath).isFile()) {
312
+ const content = readFileSync(fullPath, "utf-8").slice(0, MAX_FILE_SIZE);
313
+ sections.push(`### ${file}\n\`\`\`\n${content}\n\`\`\``);
314
+ }
315
+ }
316
+ catch { /* skip */ }
317
+ }
318
+ try {
319
+ const entries = readdirSync(repoPath, { withFileTypes: true })
320
+ .slice(0, 50)
321
+ .map((e) => `${e.isDirectory() ? "📁" : "📄"} ${e.name}`)
322
+ .join("\n");
323
+ sections.unshift(`### Directory listing\n\`\`\`\n${entries}\n\`\`\``);
324
+ }
325
+ catch { /* skip */ }
326
+ return sections.join("\n\n");
327
+ }
328
+ //# sourceMappingURL=plans.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plans.js","sourceRoot":"","sources":["../../src/routes/plans.ts"],"names":[],"mappings":"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"}
@@ -9,9 +9,11 @@
9
9
  import type { FastifyInstance } from "fastify";
10
10
  import type { AppConfig } from "../config.js";
11
11
  import type { RepositoryService } from "../services/repositories.js";
12
+ import type { UserService } from "../services/users.js";
12
13
  import type { WsControlPlane } from "../ws.js";
13
14
  export interface RepoRouteDeps {
14
15
  repoService: RepositoryService;
16
+ userService?: UserService;
15
17
  ws?: WsControlPlane;
16
18
  }
17
19
  export declare function registerRepoRoutes(app: FastifyInstance, config: AppConfig, deps: RepoRouteDeps): void;
@@ -1 +1 @@
1
- {"version":3,"file":"repositories.d.ts","sourceRoot":"","sources":["../../src/routes/repositories.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAC/C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AACrE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAI/C,MAAM,WAAW,aAAa;IAC5B,WAAW,EAAE,iBAAiB,CAAC;IAC/B,EAAE,CAAC,EAAE,cAAc,CAAC;CACrB;AAED,wBAAgB,kBAAkB,CAChC,GAAG,EAAE,eAAe,EACpB,MAAM,EAAE,SAAS,EACjB,IAAI,EAAE,aAAa,GAClB,IAAI,CAyGN"}
1
+ {"version":3,"file":"repositories.d.ts","sourceRoot":"","sources":["../../src/routes/repositories.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAC/C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AACrE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACxD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAM/C,MAAM,WAAW,aAAa;IAC5B,WAAW,EAAE,iBAAiB,CAAC;IAC/B,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,EAAE,CAAC,EAAE,cAAc,CAAC;CACrB;AAED,wBAAgB,kBAAkB,CAChC,GAAG,EAAE,eAAe,EACpB,MAAM,EAAE,SAAS,EACjB,IAAI,EAAE,aAAa,GAClB,IAAI,CAkPN"}
@@ -7,8 +7,10 @@
7
7
  * DELETE /api/repos/:id — delete a repository
8
8
  */
9
9
  import { requireAuth } from "../security/http-auth.js";
10
+ import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
11
+ import { join } from "node:path";
10
12
  export function registerRepoRoutes(app, config, deps) {
11
- const { repoService, ws } = deps;
13
+ const { repoService, userService, ws } = deps;
12
14
  /** Broadcast a repo event over WS to all clients */
13
15
  function broadcastRepoEvent(event, data) {
14
16
  if (!ws)
@@ -74,6 +76,121 @@ export function registerRepoRoutes(app, config, deps) {
74
76
  broadcastRepoEvent("updated", { repo });
75
77
  return { repo };
76
78
  });
79
+ // ── STRATEGY ─────────────────────────────────────────────────────
80
+ /** Get strategy markdown for a repo */
81
+ app.get("/api/repos/:id/strategy", async (request, reply) => {
82
+ const user = await requireAuth(request, reply, config.jwtSecret);
83
+ if (!user)
84
+ return;
85
+ const repo = repoService.getById(request.params.id);
86
+ if (!repo) {
87
+ return reply.status(404).send({ error: "Repository not found" });
88
+ }
89
+ return { strategy: repo.strategy ?? "" };
90
+ });
91
+ /** Update strategy markdown for a repo */
92
+ app.put("/api/repos/:id/strategy", async (request, reply) => {
93
+ const user = await requireAuth(request, reply, config.jwtSecret);
94
+ if (!user)
95
+ return;
96
+ const body = request.body;
97
+ if (typeof body.strategy !== "string") {
98
+ return reply.status(400).send({ error: "strategy must be a string" });
99
+ }
100
+ const repo = repoService.update(request.params.id, { strategy: body.strategy });
101
+ if (!repo) {
102
+ return reply.status(404).send({ error: "Repository not found" });
103
+ }
104
+ broadcastRepoEvent("updated", { repo });
105
+ return { strategy: repo.strategy ?? "" };
106
+ });
107
+ /** Generate a strategy by analyzing the repo with an LLM */
108
+ app.post("/api/repos/:id/strategy/generate", async (request, reply) => {
109
+ const user = await requireAuth(request, reply, config.jwtSecret);
110
+ if (!user)
111
+ return;
112
+ const repo = repoService.getById(request.params.id);
113
+ if (!repo) {
114
+ return reply.status(404).send({ error: "Repository not found" });
115
+ }
116
+ // Gather context from the repo if locally accessible
117
+ let repoContext = "";
118
+ if (existsSync(repo.localPath)) {
119
+ repoContext = gatherRepoContext(repo.localPath);
120
+ }
121
+ // Build LLM prompt
122
+ const systemPrompt = [
123
+ "You are a senior developer writing a strategy document for an AI coding agent.",
124
+ "The strategy tells the agent how to work in this repository — build commands,",
125
+ "test commands, coding conventions, project structure, workflow guidelines, etc.",
126
+ "Output ONLY markdown, no wrapping code fences. Be concise and practical.",
127
+ ].join(" ");
128
+ const userPrompt = repoContext
129
+ ? `Generate a strategy document for the repository "${repo.name}".\n\nHere is context from the repo:\n\n${repoContext}`
130
+ : `Generate a strategy document for a repository named "${repo.name}". Since the repo isn't accessible locally, generate a sensible default template.`;
131
+ // Resolve API keys
132
+ const apiKeys = userService?.getSettings(user.id).apiKeys ?? {};
133
+ const apiKey = apiKeys["OPENAI_API_KEY"]?.trim() || config.openaiApiKey;
134
+ const baseUrl = (apiKeys["OPENAI_BASE_URL"]?.trim() || config.openaiBaseUrl).replace(/\/+$/, "");
135
+ const model = apiKeys["OPENAI_MODEL"]?.trim() || config.openaiModel;
136
+ if (!apiKey && config.llmProvider === "openai") {
137
+ return reply.status(400).send({ error: "No API key configured. Set an OpenAI API key in settings." });
138
+ }
139
+ try {
140
+ let generated;
141
+ if (apiKey || config.llmProvider === "openai") {
142
+ const response = await fetch(`${baseUrl}/chat/completions`, {
143
+ method: "POST",
144
+ headers: {
145
+ "Content-Type": "application/json",
146
+ Authorization: `Bearer ${apiKey}`,
147
+ },
148
+ body: JSON.stringify({
149
+ model,
150
+ temperature: 0.3,
151
+ max_tokens: 2000,
152
+ messages: [
153
+ { role: "system", content: systemPrompt },
154
+ { role: "user", content: userPrompt },
155
+ ],
156
+ }),
157
+ });
158
+ if (!response.ok) {
159
+ throw new Error(`LLM API returned ${response.status}`);
160
+ }
161
+ const data = await response.json();
162
+ generated = data.choices?.[0]?.message?.content?.trim() ?? "";
163
+ }
164
+ else {
165
+ // Ollama fallback
166
+ const response = await fetch(`${config.ollamaUrl.replace(/\/+$/, "")}/api/chat`, {
167
+ method: "POST",
168
+ headers: { "Content-Type": "application/json" },
169
+ body: JSON.stringify({
170
+ model: config.ollamaModel,
171
+ stream: false,
172
+ messages: [
173
+ { role: "system", content: systemPrompt },
174
+ { role: "user", content: userPrompt },
175
+ ],
176
+ }),
177
+ });
178
+ if (!response.ok) {
179
+ throw new Error(`Ollama API returned ${response.status}`);
180
+ }
181
+ const data = await response.json();
182
+ generated = data.message?.content?.trim() ?? "";
183
+ }
184
+ if (!generated) {
185
+ return reply.status(500).send({ error: "LLM returned empty strategy" });
186
+ }
187
+ return { strategy: generated };
188
+ }
189
+ catch (err) {
190
+ const msg = err instanceof Error ? err.message : String(err);
191
+ return reply.status(500).send({ error: `Strategy generation failed: ${msg}` });
192
+ }
193
+ });
77
194
  // ── DELETE ───────────────────────────────────────────────────────
78
195
  app.delete("/api/repos/:id", async (request, reply) => {
79
196
  const user = await requireAuth(request, reply, config.jwtSecret);
@@ -88,4 +205,47 @@ export function registerRepoRoutes(app, config, deps) {
88
205
  return { ok: true };
89
206
  });
90
207
  }
208
+ // ── Helpers ──────────────────────────────────────────────────────────
209
+ /** Read key files from a repo to build context for strategy generation. */
210
+ function gatherRepoContext(repoPath) {
211
+ const sections = [];
212
+ const MAX_FILE_SIZE = 8000; // bytes
213
+ // Key files to look for
214
+ const keyFiles = [
215
+ "package.json",
216
+ "README.md",
217
+ "AGENTS.md",
218
+ "CLAUDE.md",
219
+ ".github/copilot-instructions.md",
220
+ "Cargo.toml",
221
+ "pyproject.toml",
222
+ "go.mod",
223
+ "Makefile",
224
+ "Dockerfile",
225
+ "docker-compose.yml",
226
+ "tsconfig.json",
227
+ ".eslintrc.json",
228
+ "biome.json",
229
+ ];
230
+ for (const file of keyFiles) {
231
+ const fullPath = join(repoPath, file);
232
+ try {
233
+ if (existsSync(fullPath) && statSync(fullPath).isFile()) {
234
+ const content = readFileSync(fullPath, "utf-8").slice(0, MAX_FILE_SIZE);
235
+ sections.push(`### ${file}\n\`\`\`\n${content}\n\`\`\``);
236
+ }
237
+ }
238
+ catch { /* skip unreadable files */ }
239
+ }
240
+ // Add top-level directory listing
241
+ try {
242
+ const entries = readdirSync(repoPath, { withFileTypes: true })
243
+ .slice(0, 50)
244
+ .map((e) => `${e.isDirectory() ? "📁" : "📄"} ${e.name}`)
245
+ .join("\n");
246
+ sections.unshift(`### Directory listing\n\`\`\`\n${entries}\n\`\`\``);
247
+ }
248
+ catch { /* skip */ }
249
+ return sections.join("\n\n");
250
+ }
91
251
  //# sourceMappingURL=repositories.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"repositories.js","sourceRoot":"","sources":["../../src/routes/repositories.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"repositories.js","sourceRoot":"","sources":["../../src/routes/repositories.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"threads.d.ts","sourceRoot":"","sources":["../../src/routes/threads.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAC/C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAC5D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AACjE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAI/C,OAAO,EAA0C,KAAK,gBAAgB,EAAE,KAAK,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACvH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACxD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AASrE,MAAM,WAAW,eAAe;IAC9B,aAAa,EAAE,aAAa,CAAC;IAC7B,gBAAgB,EAAE,gBAAgB,CAAC;IACnC,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,WAAW,CAAC,EAAE,iBAAiB,CAAC;IAChC,EAAE,CAAC,EAAE,cAAc,CAAC;IACpB,UAAU,CAAC,EAAE;QACX,gBAAgB,CACd,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,gBAAgB,EACxB,aAAa,CAAC,EAAE,MAAM,EACtB,aAAa,CAAC,EAAE,OAAO,EACvB,UAAU,CAAC,EAAE,MAAM,EACnB,WAAW,CAAC,EAAE,MAAM,GACnB,OAAO,CAAC,aAAa,CAAC,CAAC;KAC3B,CAAC;CACH;AAED,wBAAgB,oBAAoB,CAClC,GAAG,EAAE,eAAe,EACpB,MAAM,EAAE,SAAS,EACjB,IAAI,EAAE,eAAe,GACpB,IAAI,CA+uBN"}
1
+ {"version":3,"file":"threads.d.ts","sourceRoot":"","sources":["../../src/routes/threads.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAC/C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAC5D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AACjE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAI/C,OAAO,EAA0C,KAAK,gBAAgB,EAAE,KAAK,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACvH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACxD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AASrE,MAAM,WAAW,eAAe;IAC9B,aAAa,EAAE,aAAa,CAAC;IAC7B,gBAAgB,EAAE,gBAAgB,CAAC;IACnC,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,WAAW,CAAC,EAAE,iBAAiB,CAAC;IAChC,EAAE,CAAC,EAAE,cAAc,CAAC;IACpB,UAAU,CAAC,EAAE;QACX,gBAAgB,CACd,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,gBAAgB,EACxB,aAAa,CAAC,EAAE,MAAM,EACtB,aAAa,CAAC,EAAE,OAAO,EACvB,UAAU,CAAC,EAAE,MAAM,EACnB,WAAW,CAAC,EAAE,MAAM,GACnB,OAAO,CAAC,aAAa,CAAC,CAAC;KAC3B,CAAC;CACH;AAED,wBAAgB,oBAAoB,CAClC,GAAG,EAAE,eAAe,EACpB,MAAM,EAAE,SAAS,EACjB,IAAI,EAAE,eAAe,GACpB,IAAI,CA2vBN"}
@@ -385,9 +385,18 @@ export function registerThreadRoutes(app, config, deps) {
385
385
  }
386
386
  // ── Send the actual coding turn ────────────────────────
387
387
  if (message) {
388
+ // Look up repository strategy to prepend as agent instructions
389
+ let fullMessage = message;
390
+ if (repoService && workingDirectory) {
391
+ const matchingRepo = repoService.list().find((r) => workingDirectory.startsWith(r.localPath) ||
392
+ workingDirectory.includes(r.name));
393
+ if (matchingRepo?.strategy?.trim()) {
394
+ fullMessage = `<repository-strategy>\n${matchingRepo.strategy.trim()}\n</repository-strategy>\n\n${message}`;
395
+ }
396
+ }
388
397
  const userActivity = threadService.addActivity(id, "message", message.slice(0, 500), { role: "user", content: message });
389
398
  broadcastThreadEvent(id, "activity", { activity: userActivity });
390
- await provider.sendTurn(session.id, message);
399
+ await provider.sendTurn(session.id, fullMessage);
391
400
  }
392
401
  }
393
402
  catch (err) {