@locusai/sdk 0.14.5 → 0.15.1

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.
@@ -307,6 +307,29 @@ var init_sprints = __esm(() => {
307
307
  };
308
308
  });
309
309
 
310
+ // src/modules/suggestions.ts
311
+ var SuggestionsModule;
312
+ var init_suggestions = __esm(() => {
313
+ SuggestionsModule = class SuggestionsModule extends BaseModule {
314
+ async create(workspaceId, data) {
315
+ const { data: res } = await this.api.post(`/workspaces/${workspaceId}/suggestions`, data);
316
+ return res.suggestion;
317
+ }
318
+ async list(workspaceId, params) {
319
+ const { data } = await this.api.get(`/workspaces/${workspaceId}/suggestions`, { params });
320
+ return data.suggestions;
321
+ }
322
+ async get(workspaceId, id) {
323
+ const { data } = await this.api.get(`/workspaces/${workspaceId}/suggestions/${id}`);
324
+ return data.suggestion;
325
+ }
326
+ async updateStatus(workspaceId, id, status) {
327
+ const { data } = await this.api.patch(`/workspaces/${workspaceId}/suggestions/${id}/status`, status);
328
+ return data.suggestion;
329
+ }
330
+ };
331
+ });
332
+
310
333
  // src/modules/tasks.ts
311
334
  var import_shared, TasksModule;
312
335
  var init_tasks = __esm(() => {
@@ -483,6 +506,7 @@ var exports_src = {};
483
506
  __export(exports_src, {
484
507
  WorkspacesModule: () => WorkspacesModule,
485
508
  TasksModule: () => TasksModule,
509
+ SuggestionsModule: () => SuggestionsModule,
486
510
  SprintsModule: () => SprintsModule,
487
511
  OrganizationsModule: () => OrganizationsModule,
488
512
  LocusEvent: () => LocusEvent,
@@ -511,6 +535,7 @@ class LocusClient {
511
535
  docs;
512
536
  ci;
513
537
  instances;
538
+ suggestions;
514
539
  constructor(config) {
515
540
  this.emitter = new LocusEmitter;
516
541
  this.api = import_axios.default.create({
@@ -531,6 +556,7 @@ class LocusClient {
531
556
  this.docs = new DocsModule(this.api, this.emitter);
532
557
  this.ci = new CiModule(this.api, this.emitter);
533
558
  this.instances = new InstancesModule(this.api, this.emitter);
559
+ this.suggestions = new SuggestionsModule(this.api, this.emitter);
534
560
  if (config.retryOptions) {
535
561
  this.setupRetryInterceptor(config.retryOptions);
536
562
  }
@@ -600,6 +626,7 @@ var init_src = __esm(() => {
600
626
  init_invitations();
601
627
  init_organizations();
602
628
  init_sprints();
629
+ init_suggestions();
603
630
  init_tasks();
604
631
  init_workspaces();
605
632
  init_discussion_types();
@@ -612,6 +639,7 @@ var init_src = __esm(() => {
612
639
  init_invitations();
613
640
  init_organizations();
614
641
  init_sprints();
642
+ init_suggestions();
615
643
  init_tasks();
616
644
  init_workspaces();
617
645
  });
@@ -883,6 +911,7 @@ class ClaudeRunner {
883
911
  currentToolName;
884
912
  activeTools = new Map;
885
913
  activeProcess = null;
914
+ aborted = false;
886
915
  timeoutMs;
887
916
  constructor(projectPath, model = DEFAULT_MODEL[PROVIDER.CLAUDE], log, timeoutMs) {
888
917
  this.model = model;
@@ -895,6 +924,7 @@ class ClaudeRunner {
895
924
  }
896
925
  abort() {
897
926
  if (this.activeProcess && !this.activeProcess.killed) {
927
+ this.aborted = true;
898
928
  this.activeProcess.kill("SIGTERM");
899
929
  this.activeProcess = null;
900
930
  }
@@ -953,6 +983,7 @@ class ClaudeRunner {
953
983
  return args;
954
984
  }
955
985
  async* runStream(prompt) {
986
+ this.aborted = false;
956
987
  const args = this.buildCliArgs();
957
988
  const env = getAugmentedEnv({
958
989
  FORCE_COLOR: "1",
@@ -1047,7 +1078,7 @@ class ClaudeRunner {
1047
1078
  process.stderr.write(`${stderrBuffer}
1048
1079
  `);
1049
1080
  }
1050
- if (code !== 0 && !errorMessage) {
1081
+ if (code !== 0 && !errorMessage && !this.aborted) {
1051
1082
  const detail = stderrFull.trim() || lastResultContent.trim();
1052
1083
  errorMessage = this.createExecutionError(code, detail).message;
1053
1084
  this.eventEmitter?.emitErrorOccurred(errorMessage, `EXIT_${code}`);
@@ -1192,6 +1223,7 @@ class ClaudeRunner {
1192
1223
  return null;
1193
1224
  }
1194
1225
  executeRun(prompt) {
1226
+ this.aborted = false;
1195
1227
  return new Promise((resolve2, reject) => {
1196
1228
  const args = this.buildCliArgs();
1197
1229
  const env = getAugmentedEnv({
@@ -1244,7 +1276,7 @@ class ClaudeRunner {
1244
1276
  }
1245
1277
  process.stdout.write(`
1246
1278
  `);
1247
- if (code === 0) {
1279
+ if (code === 0 || this.aborted) {
1248
1280
  resolve2(finalResult);
1249
1281
  } else {
1250
1282
  const detail = errorOutput.trim() || finalResult.trim();
@@ -1311,6 +1343,7 @@ class CodexRunner {
1311
1343
  log;
1312
1344
  reasoningEffort;
1313
1345
  activeProcess = null;
1346
+ aborted = false;
1314
1347
  eventEmitter;
1315
1348
  currentToolName;
1316
1349
  timeoutMs;
@@ -1326,11 +1359,13 @@ class CodexRunner {
1326
1359
  }
1327
1360
  abort() {
1328
1361
  if (this.activeProcess && !this.activeProcess.killed) {
1362
+ this.aborted = true;
1329
1363
  this.activeProcess.kill("SIGTERM");
1330
1364
  this.activeProcess = null;
1331
1365
  }
1332
1366
  }
1333
1367
  async run(prompt) {
1368
+ this.aborted = false;
1334
1369
  const maxRetries = 3;
1335
1370
  let lastError = null;
1336
1371
  for (let attempt = 1;attempt <= maxRetries; attempt++) {
@@ -1446,7 +1481,7 @@ class CodexRunner {
1446
1481
  });
1447
1482
  codex.on("close", (code) => {
1448
1483
  this.activeProcess = null;
1449
- if (code === 0) {
1484
+ if (code === 0 || this.aborted) {
1450
1485
  const result = this.readOutput(outputPath, finalOutput);
1451
1486
  this.cleanupTempFile(outputPath);
1452
1487
  if (result && finalContent.trim().length === 0) {
@@ -1559,7 +1594,7 @@ class CodexRunner {
1559
1594
  });
1560
1595
  codex.on("close", (code) => {
1561
1596
  this.activeProcess = null;
1562
- if (code === 0) {
1597
+ if (code === 0 || this.aborted) {
1563
1598
  const result = this.readOutput(outputPath, output);
1564
1599
  this.cleanupTempFile(outputPath);
1565
1600
  resolve2(result);
@@ -10,6 +10,7 @@ export declare class ClaudeRunner implements AiRunner {
10
10
  private currentToolName?;
11
11
  private activeTools;
12
12
  private activeProcess;
13
+ private aborted;
13
14
  timeoutMs: number;
14
15
  constructor(projectPath: string, model?: string, log?: LogFn | undefined, timeoutMs?: number);
15
16
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"claude-runner.d.ts","sourceRoot":"","sources":["../../src/ai/claude-runner.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AACjE,OAAO,KAAK,EAAE,WAAW,EAAc,MAAM,kBAAkB,CAAC;AAGhE,OAAO,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AACrC,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAmC5C,qBAAa,YAAa,YAAW,QAAQ;IAUzC,OAAO,CAAC,KAAK;IACb,OAAO,CAAC,GAAG,CAAC;IAVd,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,YAAY,CAAC,CAAmB;IACxC,OAAO,CAAC,eAAe,CAAC,CAAS;IACjC,OAAO,CAAC,WAAW,CAA+C;IAClE,OAAO,CAAC,aAAa,CAA6B;IAClD,SAAS,EAAE,MAAM,CAAC;gBAGhB,WAAW,EAAE,MAAM,EACX,KAAK,GAAE,MAAuC,EAC9C,GAAG,CAAC,EAAE,KAAK,YAAA,EACnB,SAAS,CAAC,EAAE,MAAM;IAMpB;;OAEG;IACH,eAAe,CAAC,OAAO,EAAE,gBAAgB,GAAG,IAAI;IAIhD;;OAEG;IACH,KAAK,IAAI,IAAI;IAOP,GAAG,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IA8B1C,OAAO,CAAC,WAAW;IA0BnB,OAAO,CAAC,YAAY;IAgBb,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,cAAc,CAAC,WAAW,EAAE,IAAI,EAAE,OAAO,CAAC;IA0K5E;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAiCzB,OAAO,CAAC,sBAAsB;IAW9B,OAAO,CAAC,wBAAwB;IAYhC,OAAO,CAAC,kBAAkB;IAwE1B,OAAO,CAAC,UAAU;IA+ElB,OAAO,CAAC,gBAAgB;IAYxB,OAAO,CAAC,iBAAiB;IAYzB,OAAO,CAAC,WAAW;IAanB,OAAO,CAAC,kBAAkB;IAO1B,OAAO,CAAC,oBAAoB;CAO7B"}
1
+ {"version":3,"file":"claude-runner.d.ts","sourceRoot":"","sources":["../../src/ai/claude-runner.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AACjE,OAAO,KAAK,EAAE,WAAW,EAAc,MAAM,kBAAkB,CAAC;AAGhE,OAAO,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AACrC,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAmC5C,qBAAa,YAAa,YAAW,QAAQ;IAWzC,OAAO,CAAC,KAAK;IACb,OAAO,CAAC,GAAG,CAAC;IAXd,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,YAAY,CAAC,CAAmB;IACxC,OAAO,CAAC,eAAe,CAAC,CAAS;IACjC,OAAO,CAAC,WAAW,CAA+C;IAClE,OAAO,CAAC,aAAa,CAA6B;IAClD,OAAO,CAAC,OAAO,CAAS;IACxB,SAAS,EAAE,MAAM,CAAC;gBAGhB,WAAW,EAAE,MAAM,EACX,KAAK,GAAE,MAAuC,EAC9C,GAAG,CAAC,EAAE,KAAK,YAAA,EACnB,SAAS,CAAC,EAAE,MAAM;IAMpB;;OAEG;IACH,eAAe,CAAC,OAAO,EAAE,gBAAgB,GAAG,IAAI;IAIhD;;OAEG;IACH,KAAK,IAAI,IAAI;IAQP,GAAG,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IA8B1C,OAAO,CAAC,WAAW;IA0BnB,OAAO,CAAC,YAAY;IAgBb,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,cAAc,CAAC,WAAW,EAAE,IAAI,EAAE,OAAO,CAAC;IA4K5E;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAiCzB,OAAO,CAAC,sBAAsB;IAW9B,OAAO,CAAC,wBAAwB;IAYhC,OAAO,CAAC,kBAAkB;IAwE1B,OAAO,CAAC,UAAU;IAgFlB,OAAO,CAAC,gBAAgB;IAYxB,OAAO,CAAC,iBAAiB;IAYzB,OAAO,CAAC,WAAW;IAanB,OAAO,CAAC,kBAAkB;IAO1B,OAAO,CAAC,oBAAoB;CAO7B"}
@@ -8,6 +8,7 @@ export declare class CodexRunner implements AiRunner {
8
8
  private log?;
9
9
  private reasoningEffort?;
10
10
  private activeProcess;
11
+ private aborted;
11
12
  private eventEmitter?;
12
13
  private currentToolName?;
13
14
  timeoutMs: number;
@@ -1 +1 @@
1
- {"version":3,"file":"codex-runner.d.ts","sourceRoot":"","sources":["../../src/ai/codex-runner.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AACjE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAEpD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAK5C,qBAAa,WAAY,YAAW,QAAQ;IAOxC,OAAO,CAAC,WAAW;IACnB,OAAO,CAAC,KAAK;IACb,OAAO,CAAC,GAAG,CAAC;IACZ,OAAO,CAAC,eAAe,CAAC;IAT1B,OAAO,CAAC,aAAa,CAA6B;IAClD,OAAO,CAAC,YAAY,CAAC,CAAmB;IACxC,OAAO,CAAC,eAAe,CAAC,CAAS;IACjC,SAAS,EAAE,MAAM,CAAC;gBAGR,WAAW,EAAE,MAAM,EACnB,KAAK,GAAE,MAAsC,EAC7C,GAAG,CAAC,EAAE,KAAK,YAAA,EACX,eAAe,CAAC,EAAE,MAAM,YAAA,EAChC,SAAS,CAAC,EAAE,MAAM;IAKpB,eAAe,CAAC,OAAO,EAAE,gBAAgB,GAAG,IAAI;IAIhD;;OAEG;IACH,KAAK,IAAI,IAAI;IAOP,GAAG,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IA4B1C,OAAO,CAAC,WAAW;IA0BZ,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,cAAc,CAAC,WAAW,EAAE,IAAI,EAAE,OAAO,CAAC;IA8J5E,OAAO,CAAC,iBAAiB;IA+BzB,OAAO,CAAC,UAAU;IAyDlB,OAAO,CAAC,SAAS;IAqBjB;;;OAGG;IACH,OAAO,CAAC,eAAe;IAWvB,OAAO,CAAC,aAAa;IAKrB,OAAO,CAAC,UAAU;IAYlB,OAAO,CAAC,qBAAqB;IAY7B,OAAO,CAAC,eAAe;IAQvB,OAAO,CAAC,KAAK;CAGd"}
1
+ {"version":3,"file":"codex-runner.d.ts","sourceRoot":"","sources":["../../src/ai/codex-runner.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AACjE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAEpD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAK5C,qBAAa,WAAY,YAAW,QAAQ;IAQxC,OAAO,CAAC,WAAW;IACnB,OAAO,CAAC,KAAK;IACb,OAAO,CAAC,GAAG,CAAC;IACZ,OAAO,CAAC,eAAe,CAAC;IAV1B,OAAO,CAAC,aAAa,CAA6B;IAClD,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,YAAY,CAAC,CAAmB;IACxC,OAAO,CAAC,eAAe,CAAC,CAAS;IACjC,SAAS,EAAE,MAAM,CAAC;gBAGR,WAAW,EAAE,MAAM,EACnB,KAAK,GAAE,MAAsC,EAC7C,GAAG,CAAC,EAAE,KAAK,YAAA,EACX,eAAe,CAAC,EAAE,MAAM,YAAA,EAChC,SAAS,CAAC,EAAE,MAAM;IAKpB,eAAe,CAAC,OAAO,EAAE,gBAAgB,GAAG,IAAI;IAIhD;;OAEG;IACH,KAAK,IAAI,IAAI;IAQP,GAAG,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IA6B1C,OAAO,CAAC,WAAW;IA0BZ,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,cAAc,CAAC,WAAW,EAAE,IAAI,EAAE,OAAO,CAAC;IA8J5E,OAAO,CAAC,iBAAiB;IA+BzB,OAAO,CAAC,UAAU;IAyDlB,OAAO,CAAC,SAAS;IAqBjB;;;OAGG;IACH,OAAO,CAAC,eAAe;IAWvB,OAAO,CAAC,aAAa;IAKrB,OAAO,CAAC,UAAU;IAYlB,OAAO,CAAC,qBAAqB;IAY7B,OAAO,CAAC,eAAe;IAQvB,OAAO,CAAC,KAAK;CAGd"}
package/dist/events.d.ts CHANGED
@@ -15,8 +15,10 @@ export declare class LocusEmitter extends EventEmitter {
15
15
  on(event: LocusEvent.TOKEN_EXPIRED, listener: () => void): this;
16
16
  on(event: LocusEvent.AUTH_ERROR, listener: (error: Error) => void): this;
17
17
  on(event: LocusEvent.REQUEST_ERROR, listener: (error: Error) => void): this;
18
+ on(event: string, listener: (...args: unknown[]) => void): this;
18
19
  emit(event: LocusEvent.TOKEN_EXPIRED): boolean;
19
20
  emit(event: LocusEvent.AUTH_ERROR, error: Error): boolean;
20
21
  emit(event: LocusEvent.REQUEST_ERROR, error: Error): boolean;
22
+ emit(event: string, ...args: unknown[]): boolean;
21
23
  }
22
24
  //# sourceMappingURL=events.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"events.d.ts","sourceRoot":"","sources":["../src/events.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AAEtC,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAEhD,oBAAY,UAAU;IACpB,aAAa,kBAAkB;IAC/B,UAAU,eAAe;IACzB,aAAa,kBAAkB;CAChC;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,YAAY,CAAC,EAAE,YAAY,CAAC;CAC7B;AAED,qBAAa,YAAa,SAAQ,YAAY;IAC5C,EAAE,CAAC,KAAK,EAAE,UAAU,CAAC,aAAa,EAAE,QAAQ,EAAE,MAAM,IAAI,GAAG,IAAI;IAC/D,EAAE,CAAC,KAAK,EAAE,UAAU,CAAC,UAAU,EAAE,QAAQ,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,GAAG,IAAI;IACxE,EAAE,CAAC,KAAK,EAAE,UAAU,CAAC,aAAa,EAAE,QAAQ,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,GAAG,IAAI;IAQ3E,IAAI,CAAC,KAAK,EAAE,UAAU,CAAC,aAAa,GAAG,OAAO;IAC9C,IAAI,CAAC,KAAK,EAAE,UAAU,CAAC,UAAU,EAAE,KAAK,EAAE,KAAK,GAAG,OAAO;IACzD,IAAI,CAAC,KAAK,EAAE,UAAU,CAAC,aAAa,EAAE,KAAK,EAAE,KAAK,GAAG,OAAO;CAI7D"}
1
+ {"version":3,"file":"events.d.ts","sourceRoot":"","sources":["../src/events.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AACtC,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAEhD,oBAAY,UAAU;IACpB,aAAa,kBAAkB;IAC/B,UAAU,eAAe;IACzB,aAAa,kBAAkB;CAChC;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,YAAY,CAAC,EAAE,YAAY,CAAC;CAC7B;AAED,qBAAa,YAAa,SAAQ,YAAY;IAC5C,EAAE,CAAC,KAAK,EAAE,UAAU,CAAC,aAAa,EAAE,QAAQ,EAAE,MAAM,IAAI,GAAG,IAAI;IAC/D,EAAE,CAAC,KAAK,EAAE,UAAU,CAAC,UAAU,EAAE,QAAQ,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,GAAG,IAAI;IACxE,EAAE,CAAC,KAAK,EAAE,UAAU,CAAC,aAAa,EAAE,QAAQ,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,GAAG,IAAI;IAC3E,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI,GAAG,IAAI;IAQ/D,IAAI,CAAC,KAAK,EAAE,UAAU,CAAC,aAAa,GAAG,OAAO;IAC9C,IAAI,CAAC,KAAK,EAAE,UAAU,CAAC,UAAU,EAAE,KAAK,EAAE,KAAK,GAAG,OAAO;IACzD,IAAI,CAAC,KAAK,EAAE,UAAU,CAAC,aAAa,EAAE,KAAK,EAAE,KAAK,GAAG,OAAO;IAC5D,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,OAAO;CAIjD"}
@@ -16,6 +16,7 @@ export * from "./git/index.js";
16
16
  export * from "./index.js";
17
17
  export { AgentOrchestrator, type AgentState, type OrchestratorConfig, } from "./orchestrator/index.js";
18
18
  export * from "./planning/index.js";
19
+ export * from "./proposals/index.js";
19
20
  export { c } from "./utils/colors.js";
20
21
  export { extractJsonFromLLMOutput } from "./utils/json-extractor.js";
21
22
  export { parseJsonWithSchema } from "./utils/structured-output.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index-node.d.ts","sourceRoot":"","sources":["../src/index-node.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,cAAc,kBAAkB,CAAC;AAEjC,cAAc,eAAe,CAAC;AAE9B,cAAc,iBAAiB,CAAC;AAChC,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAEzD,cAAc,uBAAuB,CAAC;AAEtC,cAAc,iBAAiB,CAAC;AAEhC,cAAc,gBAAgB,CAAC;AAE/B,cAAc,YAAY,CAAC;AAE3B,OAAO,EACL,iBAAiB,EACjB,KAAK,UAAU,EACf,KAAK,kBAAkB,GACxB,MAAM,yBAAyB,CAAC;AAEjC,cAAc,qBAAqB,CAAC;AAEpC,OAAO,EAAE,CAAC,EAAE,MAAM,mBAAmB,CAAC;AACtC,OAAO,EAAE,wBAAwB,EAAE,MAAM,2BAA2B,CAAC;AACrE,OAAO,EAAE,mBAAmB,EAAE,MAAM,8BAA8B,CAAC"}
1
+ {"version":3,"file":"index-node.d.ts","sourceRoot":"","sources":["../src/index-node.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,cAAc,kBAAkB,CAAC;AAEjC,cAAc,eAAe,CAAC;AAE9B,cAAc,iBAAiB,CAAC;AAChC,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAEzD,cAAc,uBAAuB,CAAC;AAEtC,cAAc,iBAAiB,CAAC;AAEhC,cAAc,gBAAgB,CAAC;AAE/B,cAAc,YAAY,CAAC;AAE3B,OAAO,EACL,iBAAiB,EACjB,KAAK,UAAU,EACf,KAAK,kBAAkB,GACxB,MAAM,yBAAyB,CAAC;AAEjC,cAAc,qBAAqB,CAAC;AAEpC,cAAc,sBAAsB,CAAC;AAErC,OAAO,EAAE,CAAC,EAAE,MAAM,mBAAmB,CAAC;AACtC,OAAO,EAAE,wBAAwB,EAAE,MAAM,2BAA2B,CAAC;AACrE,OAAO,EAAE,mBAAmB,EAAE,MAAM,8BAA8B,CAAC"}
@@ -307,6 +307,29 @@ var init_sprints = __esm(() => {
307
307
  };
308
308
  });
309
309
 
310
+ // src/modules/suggestions.ts
311
+ var SuggestionsModule;
312
+ var init_suggestions = __esm(() => {
313
+ SuggestionsModule = class SuggestionsModule extends BaseModule {
314
+ async create(workspaceId, data) {
315
+ const { data: res } = await this.api.post(`/workspaces/${workspaceId}/suggestions`, data);
316
+ return res.suggestion;
317
+ }
318
+ async list(workspaceId, params) {
319
+ const { data } = await this.api.get(`/workspaces/${workspaceId}/suggestions`, { params });
320
+ return data.suggestions;
321
+ }
322
+ async get(workspaceId, id) {
323
+ const { data } = await this.api.get(`/workspaces/${workspaceId}/suggestions/${id}`);
324
+ return data.suggestion;
325
+ }
326
+ async updateStatus(workspaceId, id, status) {
327
+ const { data } = await this.api.patch(`/workspaces/${workspaceId}/suggestions/${id}/status`, status);
328
+ return data.suggestion;
329
+ }
330
+ };
331
+ });
332
+
310
333
  // src/modules/tasks.ts
311
334
  var import_shared, TasksModule;
312
335
  var init_tasks = __esm(() => {
@@ -483,6 +506,7 @@ var exports_src = {};
483
506
  __export(exports_src, {
484
507
  WorkspacesModule: () => WorkspacesModule,
485
508
  TasksModule: () => TasksModule,
509
+ SuggestionsModule: () => SuggestionsModule,
486
510
  SprintsModule: () => SprintsModule,
487
511
  OrganizationsModule: () => OrganizationsModule,
488
512
  LocusEvent: () => LocusEvent,
@@ -511,6 +535,7 @@ class LocusClient {
511
535
  docs;
512
536
  ci;
513
537
  instances;
538
+ suggestions;
514
539
  constructor(config) {
515
540
  this.emitter = new LocusEmitter;
516
541
  this.api = import_axios.default.create({
@@ -531,6 +556,7 @@ class LocusClient {
531
556
  this.docs = new DocsModule(this.api, this.emitter);
532
557
  this.ci = new CiModule(this.api, this.emitter);
533
558
  this.instances = new InstancesModule(this.api, this.emitter);
559
+ this.suggestions = new SuggestionsModule(this.api, this.emitter);
534
560
  if (config.retryOptions) {
535
561
  this.setupRetryInterceptor(config.retryOptions);
536
562
  }
@@ -600,6 +626,7 @@ var init_src = __esm(() => {
600
626
  init_invitations();
601
627
  init_organizations();
602
628
  init_sprints();
629
+ init_suggestions();
603
630
  init_tasks();
604
631
  init_workspaces();
605
632
  init_discussion_types();
@@ -612,6 +639,7 @@ var init_src = __esm(() => {
612
639
  init_invitations();
613
640
  init_organizations();
614
641
  init_sprints();
642
+ init_suggestions();
615
643
  init_tasks();
616
644
  init_workspaces();
617
645
  });
@@ -883,6 +911,7 @@ class ClaudeRunner {
883
911
  currentToolName;
884
912
  activeTools = new Map;
885
913
  activeProcess = null;
914
+ aborted = false;
886
915
  timeoutMs;
887
916
  constructor(projectPath, model = DEFAULT_MODEL[PROVIDER.CLAUDE], log, timeoutMs) {
888
917
  this.model = model;
@@ -895,6 +924,7 @@ class ClaudeRunner {
895
924
  }
896
925
  abort() {
897
926
  if (this.activeProcess && !this.activeProcess.killed) {
927
+ this.aborted = true;
898
928
  this.activeProcess.kill("SIGTERM");
899
929
  this.activeProcess = null;
900
930
  }
@@ -953,6 +983,7 @@ class ClaudeRunner {
953
983
  return args;
954
984
  }
955
985
  async* runStream(prompt) {
986
+ this.aborted = false;
956
987
  const args = this.buildCliArgs();
957
988
  const env = getAugmentedEnv({
958
989
  FORCE_COLOR: "1",
@@ -1047,7 +1078,7 @@ class ClaudeRunner {
1047
1078
  process.stderr.write(`${stderrBuffer}
1048
1079
  `);
1049
1080
  }
1050
- if (code !== 0 && !errorMessage) {
1081
+ if (code !== 0 && !errorMessage && !this.aborted) {
1051
1082
  const detail = stderrFull.trim() || lastResultContent.trim();
1052
1083
  errorMessage = this.createExecutionError(code, detail).message;
1053
1084
  this.eventEmitter?.emitErrorOccurred(errorMessage, `EXIT_${code}`);
@@ -1192,6 +1223,7 @@ class ClaudeRunner {
1192
1223
  return null;
1193
1224
  }
1194
1225
  executeRun(prompt) {
1226
+ this.aborted = false;
1195
1227
  return new Promise((resolve2, reject) => {
1196
1228
  const args = this.buildCliArgs();
1197
1229
  const env = getAugmentedEnv({
@@ -1244,7 +1276,7 @@ class ClaudeRunner {
1244
1276
  }
1245
1277
  process.stdout.write(`
1246
1278
  `);
1247
- if (code === 0) {
1279
+ if (code === 0 || this.aborted) {
1248
1280
  resolve2(finalResult);
1249
1281
  } else {
1250
1282
  const detail = errorOutput.trim() || finalResult.trim();
@@ -1311,6 +1343,7 @@ class CodexRunner {
1311
1343
  log;
1312
1344
  reasoningEffort;
1313
1345
  activeProcess = null;
1346
+ aborted = false;
1314
1347
  eventEmitter;
1315
1348
  currentToolName;
1316
1349
  timeoutMs;
@@ -1326,11 +1359,13 @@ class CodexRunner {
1326
1359
  }
1327
1360
  abort() {
1328
1361
  if (this.activeProcess && !this.activeProcess.killed) {
1362
+ this.aborted = true;
1329
1363
  this.activeProcess.kill("SIGTERM");
1330
1364
  this.activeProcess = null;
1331
1365
  }
1332
1366
  }
1333
1367
  async run(prompt) {
1368
+ this.aborted = false;
1334
1369
  const maxRetries = 3;
1335
1370
  let lastError = null;
1336
1371
  for (let attempt = 1;attempt <= maxRetries; attempt++) {
@@ -1446,7 +1481,7 @@ class CodexRunner {
1446
1481
  });
1447
1482
  codex.on("close", (code) => {
1448
1483
  this.activeProcess = null;
1449
- if (code === 0) {
1484
+ if (code === 0 || this.aborted) {
1450
1485
  const result = this.readOutput(outputPath, finalOutput);
1451
1486
  this.cleanupTempFile(outputPath);
1452
1487
  if (result && finalContent.trim().length === 0) {
@@ -1559,7 +1594,7 @@ class CodexRunner {
1559
1594
  });
1560
1595
  codex.on("close", (code) => {
1561
1596
  this.activeProcess = null;
1562
- if (code === 0) {
1597
+ if (code === 0 || this.aborted) {
1563
1598
  const result = this.readOutput(outputPath, output);
1564
1599
  this.cleanupTempFile(outputPath);
1565
1600
  resolve2(result);
@@ -2851,9 +2886,11 @@ __export(exports_index_node, {
2851
2886
  WorkspacesModule: () => WorkspacesModule,
2852
2887
  TasksModule: () => TasksModule,
2853
2888
  TaskExecutor: () => TaskExecutor,
2889
+ SuggestionsModule: () => SuggestionsModule,
2854
2890
  SprintsModule: () => SprintsModule,
2855
2891
  ReviewerWorker: () => ReviewerWorker,
2856
2892
  ReviewService: () => ReviewService,
2893
+ ProposalEngine: () => ProposalEngine,
2857
2894
  PromptBuilder: () => PromptBuilder,
2858
2895
  PrService: () => PrService,
2859
2896
  PlanningMeeting: () => PlanningMeeting,
@@ -2884,6 +2921,7 @@ __export(exports_index_node, {
2884
2921
  DiscussionFacilitator: () => DiscussionFacilitator,
2885
2922
  DEFAULT_MODEL: () => DEFAULT_MODEL,
2886
2923
  ContextTracker: () => ContextTracker,
2924
+ ContextGatherer: () => ContextGatherer,
2887
2925
  CodexRunner: () => CodexRunner,
2888
2926
  CodebaseIndexerService: () => CodebaseIndexerService,
2889
2927
  CodebaseIndexer: () => CodebaseIndexer,
@@ -5651,5 +5689,240 @@ class PlanningMeeting {
5651
5689
  };
5652
5690
  }
5653
5691
  }
5692
+ // src/proposals/context-gatherer.ts
5693
+ var import_node_child_process8 = require("node:child_process");
5694
+ var import_node_fs12 = require("node:fs");
5695
+ var import_node_path13 = require("node:path");
5696
+
5697
+ class ContextGatherer {
5698
+ async gather(projectPath, client, workspaceId) {
5699
+ const [activeSprint, allTasks, skippedSuggestions] = await Promise.all([
5700
+ this.fetchActiveSprint(client, workspaceId),
5701
+ this.fetchTasks(client, workspaceId),
5702
+ this.fetchSkippedSuggestions(client, workspaceId)
5703
+ ]);
5704
+ const sprintTasks = activeSprint ? allTasks.filter((t) => t.sprintId === activeSprint.id) : [];
5705
+ const backlogTasks = allTasks.filter((t) => !t.sprintId);
5706
+ const gitLog = this.readGitLog(projectPath);
5707
+ const artifactContents = this.readArtifacts(projectPath);
5708
+ const locusInstructions = this.readLocusInstructions(projectPath);
5709
+ return {
5710
+ activeSprint,
5711
+ sprintTasks,
5712
+ backlogTasks,
5713
+ gitLog,
5714
+ artifactContents,
5715
+ locusInstructions,
5716
+ skippedSuggestions
5717
+ };
5718
+ }
5719
+ async fetchActiveSprint(client, workspaceId) {
5720
+ try {
5721
+ return await client.sprints.getActive(workspaceId);
5722
+ } catch {
5723
+ return null;
5724
+ }
5725
+ }
5726
+ async fetchTasks(client, workspaceId) {
5727
+ try {
5728
+ return await client.tasks.list(workspaceId);
5729
+ } catch {
5730
+ return [];
5731
+ }
5732
+ }
5733
+ async fetchSkippedSuggestions(client, workspaceId) {
5734
+ try {
5735
+ return await client.suggestions.list(workspaceId, { status: "SKIPPED" });
5736
+ } catch {
5737
+ return [];
5738
+ }
5739
+ }
5740
+ readGitLog(projectPath) {
5741
+ try {
5742
+ return import_node_child_process8.execFileSync("git", ["log", "--oneline", "--no-decorate", "-n", "20"], {
5743
+ cwd: projectPath,
5744
+ encoding: "utf-8",
5745
+ timeout: 1e4,
5746
+ stdio: ["pipe", "pipe", "pipe"]
5747
+ }).trim();
5748
+ } catch {
5749
+ return "";
5750
+ }
5751
+ }
5752
+ readArtifacts(projectPath) {
5753
+ const artifactsDir = import_node_path13.join(projectPath, ".locus", "artifacts");
5754
+ if (!import_node_fs12.existsSync(artifactsDir))
5755
+ return [];
5756
+ try {
5757
+ const files = import_node_fs12.readdirSync(artifactsDir).filter((f) => f.endsWith(".md"));
5758
+ return files.slice(0, 10).map((name) => ({
5759
+ name,
5760
+ content: import_node_fs12.readFileSync(import_node_path13.join(artifactsDir, name), "utf-8").slice(0, 2000)
5761
+ }));
5762
+ } catch {
5763
+ return [];
5764
+ }
5765
+ }
5766
+ readLocusInstructions(projectPath) {
5767
+ const locusPath = import_node_path13.join(projectPath, ".locus", "LOCUS.md");
5768
+ if (!import_node_fs12.existsSync(locusPath))
5769
+ return null;
5770
+ try {
5771
+ return import_node_fs12.readFileSync(locusPath, "utf-8").slice(0, 3000);
5772
+ } catch {
5773
+ return null;
5774
+ }
5775
+ }
5776
+ }
5777
+ // src/proposals/proposal-engine.ts
5778
+ init_factory();
5779
+ var import_shared6 = require("@locusai/shared");
5780
+ class ProposalEngine {
5781
+ contextGatherer;
5782
+ constructor(contextGatherer) {
5783
+ this.contextGatherer = contextGatherer ?? new ContextGatherer;
5784
+ }
5785
+ async runProposalCycle(projectPath, client, workspaceId) {
5786
+ const context = await this.contextGatherer.gather(projectPath, client, workspaceId);
5787
+ return this.generateProposals(context, projectPath, client, workspaceId);
5788
+ }
5789
+ async generateProposals(context, projectPath, client, workspaceId) {
5790
+ const prompt = this.buildPrompt(context);
5791
+ const runner = createAiRunner(undefined, {
5792
+ projectPath,
5793
+ timeoutMs: 5 * 60 * 1000,
5794
+ maxTurns: 1
5795
+ });
5796
+ let aiResponse;
5797
+ try {
5798
+ aiResponse = await runner.run(prompt);
5799
+ } catch {
5800
+ return [];
5801
+ }
5802
+ const proposals = this.parseResponse(aiResponse);
5803
+ const created = [];
5804
+ for (const proposal of proposals) {
5805
+ if (this.isDuplicate(proposal.title, context.skippedSuggestions)) {
5806
+ continue;
5807
+ }
5808
+ try {
5809
+ const suggestion = await client.suggestions.create(workspaceId, {
5810
+ type: import_shared6.SuggestionType.NEXT_STEP,
5811
+ title: proposal.title,
5812
+ description: proposal.description,
5813
+ metadata: {
5814
+ complexity: proposal.complexity,
5815
+ relatedBacklogItem: proposal.relatedBacklogItem,
5816
+ source: "proposal-engine"
5817
+ }
5818
+ });
5819
+ created.push(suggestion);
5820
+ } catch {}
5821
+ }
5822
+ return created;
5823
+ }
5824
+ buildPrompt(context) {
5825
+ const sections = [];
5826
+ sections.push("You are a proactive software engineering advisor. Based on the project context below, propose 1-3 high-value next steps the team should take. Focus on actionable, impactful work.");
5827
+ if (context.activeSprint) {
5828
+ const sprintInfo = `Sprint: ${context.activeSprint.name} (${context.activeSprint.status})`;
5829
+ const tasksByStatus = this.groupTasksByStatus(context.sprintTasks);
5830
+ sections.push(`## Current Sprint
5831
+ ${sprintInfo}
5832
+ ${tasksByStatus}`);
5833
+ }
5834
+ if (context.backlogTasks.length > 0) {
5835
+ const backlogList = context.backlogTasks.slice(0, 15).map((t) => `- [${t.priority}] ${t.title}${t.description ? `: ${t.description.slice(0, 100)}` : ""}`).join(`
5836
+ `);
5837
+ sections.push(`## Backlog Items
5838
+ ${backlogList}`);
5839
+ }
5840
+ if (context.gitLog) {
5841
+ sections.push(`## Recent Commits (last 20)
5842
+ ${context.gitLog}`);
5843
+ }
5844
+ if (context.artifactContents.length > 0) {
5845
+ const artifacts = context.artifactContents.map((a) => `### ${a.name}
5846
+ ${a.content}`).join(`
5847
+
5848
+ `);
5849
+ sections.push(`## Product Context
5850
+ ${artifacts}`);
5851
+ }
5852
+ if (context.locusInstructions) {
5853
+ sections.push(`## Project Instructions
5854
+ ${context.locusInstructions}`);
5855
+ }
5856
+ if (context.skippedSuggestions.length > 0) {
5857
+ const skipped = context.skippedSuggestions.map((s) => `- ${s.title}`).join(`
5858
+ `);
5859
+ sections.push(`## Previously Skipped Proposals (do NOT re-propose these)
5860
+ ${skipped}`);
5861
+ }
5862
+ sections.push(`## Instructions
5863
+ Propose 1-3 high-value next steps. For each, respond with exactly this format:
5864
+
5865
+ PROPOSAL_START
5866
+ Title: <clear, concise title>
5867
+ Description: <what to do and why, 2-4 sentences>
5868
+ Complexity: <low|medium|high>
5869
+ Related Backlog: <title of related backlog item, or "none">
5870
+ PROPOSAL_END
5871
+
5872
+ Rules:
5873
+ - Focus on what would deliver the most value right now
5874
+ - Align with the current sprint goals when possible
5875
+ - Don't propose things that are already in progress
5876
+ - Don't re-propose previously skipped suggestions
5877
+ - Keep proposals specific and actionable`);
5878
+ return sections.join(`
5879
+
5880
+ `);
5881
+ }
5882
+ parseResponse(response) {
5883
+ const proposals = [];
5884
+ const blocks = response.split("PROPOSAL_START");
5885
+ for (const block of blocks) {
5886
+ const endIdx = block.indexOf("PROPOSAL_END");
5887
+ if (endIdx === -1)
5888
+ continue;
5889
+ const content = block.slice(0, endIdx).trim();
5890
+ const title = this.extractField(content, "Title");
5891
+ const description = this.extractField(content, "Description");
5892
+ const complexity = this.extractField(content, "Complexity") ?? "medium";
5893
+ const relatedRaw = this.extractField(content, "Related Backlog");
5894
+ const relatedBacklogItem = relatedRaw && relatedRaw.toLowerCase() !== "none" ? relatedRaw : null;
5895
+ if (title && description) {
5896
+ proposals.push({
5897
+ title: title.slice(0, 200),
5898
+ description: description.slice(0, 2000),
5899
+ complexity: complexity.toLowerCase(),
5900
+ relatedBacklogItem
5901
+ });
5902
+ }
5903
+ }
5904
+ return proposals.slice(0, 3);
5905
+ }
5906
+ extractField(content, field) {
5907
+ const regex = new RegExp(`^${field}:\\s*(.+)`, "im");
5908
+ const match = content.match(regex);
5909
+ return match ? match[1].trim() : null;
5910
+ }
5911
+ isDuplicate(title, skipped) {
5912
+ const normalized = title.toLowerCase().trim();
5913
+ return skipped.some((s) => {
5914
+ const skippedNorm = s.title.toLowerCase().trim();
5915
+ return skippedNorm === normalized || skippedNorm.includes(normalized) || normalized.includes(skippedNorm);
5916
+ });
5917
+ }
5918
+ groupTasksByStatus(tasks2) {
5919
+ const groups = {};
5920
+ for (const t of tasks2) {
5921
+ groups[t.status] = (groups[t.status] ?? 0) + 1;
5922
+ }
5923
+ return Object.entries(groups).map(([status, count]) => `- ${status}: ${count} task(s)`).join(`
5924
+ `);
5925
+ }
5926
+ }
5654
5927
  // src/index-node.ts
5655
5928
  init_colors();
package/dist/index.d.ts CHANGED
@@ -6,6 +6,7 @@ import { InstancesModule } from "./modules/instances.js";
6
6
  import { InvitationsModule } from "./modules/invitations.js";
7
7
  import { OrganizationsModule } from "./modules/organizations.js";
8
8
  import { SprintsModule } from "./modules/sprints.js";
9
+ import { SuggestionsModule } from "./modules/suggestions.js";
9
10
  import { TasksModule } from "./modules/tasks.js";
10
11
  import { WorkspacesModule } from "./modules/workspaces.js";
11
12
  export type { Discussion, DiscussionInsight, DiscussionMessage, } from "./discussion/discussion-types.js";
@@ -18,6 +19,7 @@ export * from "./modules/instances.js";
18
19
  export * from "./modules/invitations.js";
19
20
  export * from "./modules/organizations.js";
20
21
  export * from "./modules/sprints.js";
22
+ export * from "./modules/suggestions.js";
21
23
  export * from "./modules/tasks.js";
22
24
  export * from "./modules/workspaces.js";
23
25
  export declare class LocusClient {
@@ -32,6 +34,7 @@ export declare class LocusClient {
32
34
  readonly docs: DocsModule;
33
35
  readonly ci: CiModule;
34
36
  readonly instances: InstancesModule;
37
+ readonly suggestions: SuggestionsModule;
35
38
  constructor(config: LocusConfig);
36
39
  private setupRetryInterceptor;
37
40
  private setupInterceptors;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAc,MAAM,aAAa,CAAC;AACpE,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAC3C,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAC7D,OAAO,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AACjE,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAI3D,YAAY,EACV,UAAU,EACV,iBAAiB,EACjB,iBAAiB,GAClB,MAAM,kCAAkC,CAAC;AAC1C,OAAO,EACL,uBAAuB,EACvB,uBAAuB,EACvB,gBAAgB,GACjB,MAAM,kCAAkC,CAAC;AAE1C,cAAc,aAAa,CAAC;AAC5B,cAAc,mBAAmB,CAAC;AAClC,cAAc,iBAAiB,CAAC;AAChC,cAAc,mBAAmB,CAAC;AAClC,cAAc,wBAAwB,CAAC;AACvC,cAAc,0BAA0B,CAAC;AACzC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,sBAAsB,CAAC;AACrC,cAAc,oBAAoB,CAAC;AACnC,cAAc,yBAAyB,CAAC;AAExC,qBAAa,WAAW;IACtB,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAgB;IACpC,SAAgB,OAAO,EAAE,YAAY,CAAC;IAEtC,SAAgB,IAAI,EAAE,UAAU,CAAC;IACjC,SAAgB,KAAK,EAAE,WAAW,CAAC;IACnC,SAAgB,OAAO,EAAE,aAAa,CAAC;IACvC,SAAgB,UAAU,EAAE,gBAAgB,CAAC;IAC7C,SAAgB,aAAa,EAAE,mBAAmB,CAAC;IACnD,SAAgB,WAAW,EAAE,iBAAiB,CAAC;IAC/C,SAAgB,IAAI,EAAE,UAAU,CAAC;IACjC,SAAgB,EAAE,EAAE,QAAQ,CAAC;IAC7B,SAAgB,SAAS,EAAE,eAAe,CAAC;gBAE/B,MAAM,EAAE,WAAW;IA8B/B,OAAO,CAAC,qBAAqB;IAmC7B,OAAO,CAAC,iBAAiB;IAmDlB,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;CAOrC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAc,MAAM,aAAa,CAAC;AACpE,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAC3C,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAE7D,OAAO,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AACjE,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAC7D,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAI3D,YAAY,EACV,UAAU,EACV,iBAAiB,EACjB,iBAAiB,GAClB,MAAM,kCAAkC,CAAC;AAC1C,OAAO,EACL,uBAAuB,EACvB,uBAAuB,EACvB,gBAAgB,GACjB,MAAM,kCAAkC,CAAC;AAE1C,cAAc,aAAa,CAAC;AAC5B,cAAc,mBAAmB,CAAC;AAClC,cAAc,iBAAiB,CAAC;AAChC,cAAc,mBAAmB,CAAC;AAClC,cAAc,wBAAwB,CAAC;AACvC,cAAc,0BAA0B,CAAC;AAEzC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,sBAAsB,CAAC;AACrC,cAAc,0BAA0B,CAAC;AACzC,cAAc,oBAAoB,CAAC;AACnC,cAAc,yBAAyB,CAAC;AAExC,qBAAa,WAAW;IACtB,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAgB;IACpC,SAAgB,OAAO,EAAE,YAAY,CAAC;IAEtC,SAAgB,IAAI,EAAE,UAAU,CAAC;IACjC,SAAgB,KAAK,EAAE,WAAW,CAAC;IACnC,SAAgB,OAAO,EAAE,aAAa,CAAC;IACvC,SAAgB,UAAU,EAAE,gBAAgB,CAAC;IAC7C,SAAgB,aAAa,EAAE,mBAAmB,CAAC;IACnD,SAAgB,WAAW,EAAE,iBAAiB,CAAC;IAC/C,SAAgB,IAAI,EAAE,UAAU,CAAC;IACjC,SAAgB,EAAE,EAAE,QAAQ,CAAC;IAC7B,SAAgB,SAAS,EAAE,eAAe,CAAC;IAC3C,SAAgB,WAAW,EAAE,iBAAiB,CAAC;gBAEnC,MAAM,EAAE,WAAW;IA+B/B,OAAO,CAAC,qBAAqB;IAmC7B,OAAO,CAAC,iBAAiB;IAmDlB,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;CAOrC"}
package/dist/index.js CHANGED
@@ -307,6 +307,29 @@ var init_sprints = __esm(() => {
307
307
  };
308
308
  });
309
309
 
310
+ // src/modules/suggestions.ts
311
+ var SuggestionsModule;
312
+ var init_suggestions = __esm(() => {
313
+ SuggestionsModule = class SuggestionsModule extends BaseModule {
314
+ async create(workspaceId, data) {
315
+ const { data: res } = await this.api.post(`/workspaces/${workspaceId}/suggestions`, data);
316
+ return res.suggestion;
317
+ }
318
+ async list(workspaceId, params) {
319
+ const { data } = await this.api.get(`/workspaces/${workspaceId}/suggestions`, { params });
320
+ return data.suggestions;
321
+ }
322
+ async get(workspaceId, id) {
323
+ const { data } = await this.api.get(`/workspaces/${workspaceId}/suggestions/${id}`);
324
+ return data.suggestion;
325
+ }
326
+ async updateStatus(workspaceId, id, status) {
327
+ const { data } = await this.api.patch(`/workspaces/${workspaceId}/suggestions/${id}/status`, status);
328
+ return data.suggestion;
329
+ }
330
+ };
331
+ });
332
+
310
333
  // src/modules/tasks.ts
311
334
  var import_shared, TasksModule;
312
335
  var init_tasks = __esm(() => {
@@ -483,6 +506,7 @@ var exports_src = {};
483
506
  __export(exports_src, {
484
507
  WorkspacesModule: () => WorkspacesModule,
485
508
  TasksModule: () => TasksModule,
509
+ SuggestionsModule: () => SuggestionsModule,
486
510
  SprintsModule: () => SprintsModule,
487
511
  OrganizationsModule: () => OrganizationsModule,
488
512
  LocusEvent: () => LocusEvent,
@@ -511,6 +535,7 @@ class LocusClient {
511
535
  docs;
512
536
  ci;
513
537
  instances;
538
+ suggestions;
514
539
  constructor(config) {
515
540
  this.emitter = new LocusEmitter;
516
541
  this.api = import_axios.default.create({
@@ -531,6 +556,7 @@ class LocusClient {
531
556
  this.docs = new DocsModule(this.api, this.emitter);
532
557
  this.ci = new CiModule(this.api, this.emitter);
533
558
  this.instances = new InstancesModule(this.api, this.emitter);
559
+ this.suggestions = new SuggestionsModule(this.api, this.emitter);
534
560
  if (config.retryOptions) {
535
561
  this.setupRetryInterceptor(config.retryOptions);
536
562
  }
@@ -600,6 +626,7 @@ var init_src = __esm(() => {
600
626
  init_invitations();
601
627
  init_organizations();
602
628
  init_sprints();
629
+ init_suggestions();
603
630
  init_tasks();
604
631
  init_workspaces();
605
632
  init_discussion_types();
@@ -612,6 +639,7 @@ var init_src = __esm(() => {
612
639
  init_invitations();
613
640
  init_organizations();
614
641
  init_sprints();
642
+ init_suggestions();
615
643
  init_tasks();
616
644
  init_workspaces();
617
645
  });
@@ -0,0 +1,12 @@
1
+ import { CreateSuggestion, Suggestion, UpdateSuggestionStatus } from "@locusai/shared";
2
+ import { BaseModule } from "./base.js";
3
+ export interface SuggestionListOptions {
4
+ status?: string;
5
+ }
6
+ export declare class SuggestionsModule extends BaseModule {
7
+ create(workspaceId: string, data: CreateSuggestion): Promise<Suggestion>;
8
+ list(workspaceId: string, params?: SuggestionListOptions): Promise<Suggestion[]>;
9
+ get(workspaceId: string, id: string): Promise<Suggestion>;
10
+ updateStatus(workspaceId: string, id: string, status: UpdateSuggestionStatus): Promise<Suggestion>;
11
+ }
12
+ //# sourceMappingURL=suggestions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"suggestions.d.ts","sourceRoot":"","sources":["../../src/modules/suggestions.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,gBAAgB,EAChB,UAAU,EACV,sBAAsB,EACvB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAUvC,MAAM,WAAW,qBAAqB;IACpC,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,qBAAa,iBAAkB,SAAQ,UAAU;IACzC,MAAM,CACV,WAAW,EAAE,MAAM,EACnB,IAAI,EAAE,gBAAgB,GACrB,OAAO,CAAC,UAAU,CAAC;IAQhB,IAAI,CACR,WAAW,EAAE,MAAM,EACnB,MAAM,CAAC,EAAE,qBAAqB,GAC7B,OAAO,CAAC,UAAU,EAAE,CAAC;IAQlB,GAAG,CAAC,WAAW,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;IAOzD,YAAY,CAChB,WAAW,EAAE,MAAM,EACnB,EAAE,EAAE,MAAM,EACV,MAAM,EAAE,sBAAsB,GAC7B,OAAO,CAAC,UAAU,CAAC;CAOvB"}
@@ -0,0 +1,32 @@
1
+ import type { Sprint, Suggestion, Task } from "@locusai/shared";
2
+ import type { LocusClient } from "../index.js";
3
+ export interface ProposalContext {
4
+ /** Active sprint, if any */
5
+ activeSprint: Sprint | null;
6
+ /** Tasks in the current sprint */
7
+ sprintTasks: Task[];
8
+ /** Backlog tasks not yet assigned to a sprint */
9
+ backlogTasks: Task[];
10
+ /** Recent git commit log (last 20 commits) */
11
+ gitLog: string;
12
+ /** Product context from .locus/artifacts/ */
13
+ artifactContents: ArtifactFile[];
14
+ /** Project-specific instructions from .locus/LOCUS.md */
15
+ locusInstructions: string | null;
16
+ /** Previously skipped suggestions (for deduplication) */
17
+ skippedSuggestions: Suggestion[];
18
+ }
19
+ export interface ArtifactFile {
20
+ name: string;
21
+ content: string;
22
+ }
23
+ export declare class ContextGatherer {
24
+ gather(projectPath: string, client: LocusClient, workspaceId: string): Promise<ProposalContext>;
25
+ private fetchActiveSprint;
26
+ private fetchTasks;
27
+ private fetchSkippedSuggestions;
28
+ private readGitLog;
29
+ private readArtifacts;
30
+ private readLocusInstructions;
31
+ }
32
+ //# sourceMappingURL=context-gatherer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context-gatherer.d.ts","sourceRoot":"","sources":["../../src/proposals/context-gatherer.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAChE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAM/C,MAAM,WAAW,eAAe;IAC9B,4BAA4B;IAC5B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,kCAAkC;IAClC,WAAW,EAAE,IAAI,EAAE,CAAC;IACpB,iDAAiD;IACjD,YAAY,EAAE,IAAI,EAAE,CAAC;IACrB,8CAA8C;IAC9C,MAAM,EAAE,MAAM,CAAC;IACf,6CAA6C;IAC7C,gBAAgB,EAAE,YAAY,EAAE,CAAC;IACjC,yDAAyD;IACzD,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,yDAAyD;IACzD,kBAAkB,EAAE,UAAU,EAAE,CAAC;CAClC;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;AAMD,qBAAa,eAAe;IACpB,MAAM,CACV,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,WAAW,EACnB,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,eAAe,CAAC;YAkCb,iBAAiB;YAWjB,UAAU;YAWV,uBAAuB;IAerC,OAAO,CAAC,UAAU;IAiBlB,OAAO,CAAC,aAAa;IAgBrB,OAAO,CAAC,qBAAqB;CAU9B"}
@@ -0,0 +1,4 @@
1
+ export type { ArtifactFile, ProposalContext } from "./context-gatherer.js";
2
+ export { ContextGatherer } from "./context-gatherer.js";
3
+ export { ProposalEngine } from "./proposal-engine.js";
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/proposals/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAC3E,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC"}
@@ -0,0 +1,21 @@
1
+ import { type Suggestion } from "@locusai/shared";
2
+ import type { LocusClient } from "../index.js";
3
+ import { ContextGatherer, type ProposalContext } from "./context-gatherer.js";
4
+ export declare class ProposalEngine {
5
+ private readonly contextGatherer;
6
+ constructor(contextGatherer?: ContextGatherer);
7
+ /**
8
+ * Full proposal cycle: gather context → generate proposals → create suggestions.
9
+ */
10
+ runProposalCycle(projectPath: string, client: LocusClient, workspaceId: string): Promise<Suggestion[]>;
11
+ /**
12
+ * Generate proposals from gathered context, deduplicate, and create suggestions.
13
+ */
14
+ generateProposals(context: ProposalContext, projectPath: string, client: LocusClient, workspaceId: string): Promise<Suggestion[]>;
15
+ private buildPrompt;
16
+ private parseResponse;
17
+ private extractField;
18
+ private isDuplicate;
19
+ private groupTasksByStatus;
20
+ }
21
+ //# sourceMappingURL=proposal-engine.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"proposal-engine.d.ts","sourceRoot":"","sources":["../../src/proposals/proposal-engine.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,UAAU,EAAkB,MAAM,iBAAiB,CAAC;AAElE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,EAAE,eAAe,EAAE,KAAK,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAiB9E,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAkB;gBAEtC,eAAe,CAAC,EAAE,eAAe;IAI7C;;OAEG;IACG,gBAAgB,CACpB,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,WAAW,EACnB,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,UAAU,EAAE,CAAC;IASxB;;OAEG;IACG,iBAAiB,CACrB,OAAO,EAAE,eAAe,EACxB,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,WAAW,EACnB,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,UAAU,EAAE,CAAC;IAiDxB,OAAO,CAAC,WAAW;IA+EnB,OAAO,CAAC,aAAa;IA+BrB,OAAO,CAAC,YAAY;IAUpB,OAAO,CAAC,WAAW;IAiBnB,OAAO,CAAC,kBAAkB;CAU3B"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@locusai/sdk",
3
- "version": "0.14.5",
3
+ "version": "0.15.1",
4
4
  "type": "module",
5
5
  "main": "./src/index.ts",
6
6
  "types": "./src/index.ts",
@@ -23,7 +23,7 @@
23
23
  "README.md"
24
24
  ],
25
25
  "scripts": {
26
- "build": "bun build ./src/index.ts ./src/index-node.ts ./src/agent/worker.ts --outdir ./dist --target node --format cjs --external axios --external events --external globby --external @locusai/shared --external zod && tsc -p tsconfig.build.json --emitDeclarationOnly",
26
+ "build": "bun build ./src/index.ts ./src/index-node.ts ./src/agent/worker.ts --outdir ./dist --target node --format cjs --external axios --external events --external globby --external @locusai/shared --external zod --external node-cron && tsc -p tsconfig.build.json --emitDeclarationOnly",
27
27
  "dev": "tsc --watch",
28
28
  "lint": "biome lint .",
29
29
  "test": "bun test",
@@ -31,14 +31,16 @@
31
31
  "clean": "rm -rf node_modules"
32
32
  },
33
33
  "dependencies": {
34
- "@locusai/shared": "^0.14.5",
34
+ "@locusai/shared": "^0.15.1",
35
35
  "axios": "^1.13.2",
36
36
  "events": "^3.3.0",
37
37
  "globby": "^14.0.2",
38
+ "node-cron": "^3.0.3",
38
39
  "zod": "^4.3.6"
39
40
  },
40
41
  "devDependencies": {
41
- "typescript": "^5.8.3",
42
- "@types/node": "^22.10.7"
42
+ "@types/node": "^22.10.7",
43
+ "@types/node-cron": "^3.0.11",
44
+ "typescript": "^5.8.3"
43
45
  }
44
46
  }