@locusai/telegram 0.15.5 → 0.16.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.
- package/bin/telegram.js +26 -762
- package/package.json +3 -13
package/bin/telegram.js
CHANGED
|
@@ -23113,32 +23113,6 @@ var init_auth = __esm(() => {
|
|
|
23113
23113
|
});
|
|
23114
23114
|
});
|
|
23115
23115
|
|
|
23116
|
-
// ../shared/src/models/autonomy.ts
|
|
23117
|
-
var RiskLevel, ChangeCategory, AutonomyRuleSchema;
|
|
23118
|
-
var init_autonomy = __esm(() => {
|
|
23119
|
-
init_zod();
|
|
23120
|
-
((RiskLevel2) => {
|
|
23121
|
-
RiskLevel2["LOW"] = "LOW";
|
|
23122
|
-
RiskLevel2["HIGH"] = "HIGH";
|
|
23123
|
-
})(RiskLevel ||= {});
|
|
23124
|
-
((ChangeCategory2) => {
|
|
23125
|
-
ChangeCategory2["FIX"] = "FIX";
|
|
23126
|
-
ChangeCategory2["REFACTOR"] = "REFACTOR";
|
|
23127
|
-
ChangeCategory2["STYLE"] = "STYLE";
|
|
23128
|
-
ChangeCategory2["DEPENDENCY"] = "DEPENDENCY";
|
|
23129
|
-
ChangeCategory2["FEATURE"] = "FEATURE";
|
|
23130
|
-
ChangeCategory2["ARCHITECTURE"] = "ARCHITECTURE";
|
|
23131
|
-
ChangeCategory2["DATABASE"] = "DATABASE";
|
|
23132
|
-
ChangeCategory2["AUTH"] = "AUTH";
|
|
23133
|
-
ChangeCategory2["API"] = "API";
|
|
23134
|
-
})(ChangeCategory ||= {});
|
|
23135
|
-
AutonomyRuleSchema = exports_external.object({
|
|
23136
|
-
category: exports_external.enum(ChangeCategory),
|
|
23137
|
-
riskLevel: exports_external.enum(RiskLevel),
|
|
23138
|
-
autoExecute: exports_external.boolean()
|
|
23139
|
-
});
|
|
23140
|
-
});
|
|
23141
|
-
|
|
23142
23116
|
// ../shared/src/models/aws-instance.ts
|
|
23143
23117
|
var InstanceAction, AwsCredentialsSchema, IntegrationSchema, AwsInstanceSchema, CreateAwsInstanceSchema, UpdateAwsInstanceSchema, SaveAwsCredentialsSchema, ProvisionAwsInstanceSchema, InstanceActionBodySchema, InstanceIdParamSchema, CIDR_REGEX, UpdateSecurityRulesSchema;
|
|
23144
23118
|
var init_aws_instance = __esm(() => {
|
|
@@ -23457,49 +23431,6 @@ var init_sprint = __esm(() => {
|
|
|
23457
23431
|
});
|
|
23458
23432
|
});
|
|
23459
23433
|
|
|
23460
|
-
// ../shared/src/models/suggestion.ts
|
|
23461
|
-
var SuggestionStatus, SuggestionType, SuggestionSchema, CreateSuggestionSchema, UpdateSuggestionStatusSchema;
|
|
23462
|
-
var init_suggestion = __esm(() => {
|
|
23463
|
-
init_zod();
|
|
23464
|
-
((SuggestionStatus2) => {
|
|
23465
|
-
SuggestionStatus2["NEW"] = "NEW";
|
|
23466
|
-
SuggestionStatus2["NOTIFIED"] = "NOTIFIED";
|
|
23467
|
-
SuggestionStatus2["ACTED_ON"] = "ACTED_ON";
|
|
23468
|
-
SuggestionStatus2["SKIPPED"] = "SKIPPED";
|
|
23469
|
-
SuggestionStatus2["EXPIRED"] = "EXPIRED";
|
|
23470
|
-
})(SuggestionStatus ||= {});
|
|
23471
|
-
((SuggestionType2) => {
|
|
23472
|
-
SuggestionType2["CODE_FIX"] = "CODE_FIX";
|
|
23473
|
-
SuggestionType2["DEPENDENCY_UPDATE"] = "DEPENDENCY_UPDATE";
|
|
23474
|
-
SuggestionType2["NEXT_STEP"] = "NEXT_STEP";
|
|
23475
|
-
SuggestionType2["REFACTOR"] = "REFACTOR";
|
|
23476
|
-
SuggestionType2["TEST_FIX"] = "TEST_FIX";
|
|
23477
|
-
})(SuggestionType ||= {});
|
|
23478
|
-
SuggestionSchema = exports_external.object({
|
|
23479
|
-
id: exports_external.string(),
|
|
23480
|
-
type: exports_external.enum(SuggestionType),
|
|
23481
|
-
status: exports_external.enum(SuggestionStatus),
|
|
23482
|
-
title: exports_external.string(),
|
|
23483
|
-
description: exports_external.string(),
|
|
23484
|
-
jobRunId: exports_external.string().optional(),
|
|
23485
|
-
workspaceId: exports_external.string(),
|
|
23486
|
-
createdAt: exports_external.string(),
|
|
23487
|
-
expiresAt: exports_external.string(),
|
|
23488
|
-
metadata: exports_external.record(exports_external.string(), exports_external.any()).optional()
|
|
23489
|
-
});
|
|
23490
|
-
CreateSuggestionSchema = exports_external.object({
|
|
23491
|
-
type: exports_external.enum(SuggestionType),
|
|
23492
|
-
title: exports_external.string().min(1, "Title is required"),
|
|
23493
|
-
description: exports_external.string().min(1, "Description is required"),
|
|
23494
|
-
jobRunId: exports_external.string().uuid().optional(),
|
|
23495
|
-
metadata: exports_external.record(exports_external.string(), exports_external.any()).optional(),
|
|
23496
|
-
expiresAt: exports_external.string().optional()
|
|
23497
|
-
});
|
|
23498
|
-
UpdateSuggestionStatusSchema = exports_external.object({
|
|
23499
|
-
status: exports_external.enum(SuggestionStatus)
|
|
23500
|
-
});
|
|
23501
|
-
});
|
|
23502
|
-
|
|
23503
23434
|
// ../shared/src/models/task.ts
|
|
23504
23435
|
var AcceptanceItemSchema, TaskSchema, CreateTaskSchema, UpdateTaskSchema, AddCommentSchema, DispatchTaskSchema, TaskIdParamSchema, TaskQuerySchema, TaskResponseSchema, TasksResponseSchema;
|
|
23505
23436
|
var init_task = __esm(() => {
|
|
@@ -23639,7 +23570,6 @@ var init_models = __esm(() => {
|
|
|
23639
23570
|
init_activity();
|
|
23640
23571
|
init_agent();
|
|
23641
23572
|
init_auth();
|
|
23642
|
-
init_autonomy();
|
|
23643
23573
|
init_aws_instance();
|
|
23644
23574
|
init_ci();
|
|
23645
23575
|
init_doc();
|
|
@@ -23647,7 +23577,6 @@ var init_models = __esm(() => {
|
|
|
23647
23577
|
init_invitation();
|
|
23648
23578
|
init_organization();
|
|
23649
23579
|
init_sprint();
|
|
23650
|
-
init_suggestion();
|
|
23651
23580
|
init_task();
|
|
23652
23581
|
init_user();
|
|
23653
23582
|
init_workspace();
|
|
@@ -38691,29 +38620,6 @@ var init_sprints = __esm(() => {
|
|
|
38691
38620
|
};
|
|
38692
38621
|
});
|
|
38693
38622
|
|
|
38694
|
-
// ../sdk/src/modules/suggestions.ts
|
|
38695
|
-
var SuggestionsModule;
|
|
38696
|
-
var init_suggestions = __esm(() => {
|
|
38697
|
-
SuggestionsModule = class SuggestionsModule extends BaseModule {
|
|
38698
|
-
async create(workspaceId, data) {
|
|
38699
|
-
const { data: res } = await this.api.post(`/workspaces/${workspaceId}/suggestions`, data);
|
|
38700
|
-
return res.suggestion;
|
|
38701
|
-
}
|
|
38702
|
-
async list(workspaceId, params) {
|
|
38703
|
-
const { data } = await this.api.get(`/workspaces/${workspaceId}/suggestions`, { params });
|
|
38704
|
-
return data.suggestions;
|
|
38705
|
-
}
|
|
38706
|
-
async get(workspaceId, id) {
|
|
38707
|
-
const { data } = await this.api.get(`/workspaces/${workspaceId}/suggestions/${id}`);
|
|
38708
|
-
return data.suggestion;
|
|
38709
|
-
}
|
|
38710
|
-
async updateStatus(workspaceId, id, status) {
|
|
38711
|
-
const { data } = await this.api.patch(`/workspaces/${workspaceId}/suggestions/${id}/status`, status);
|
|
38712
|
-
return data.suggestion;
|
|
38713
|
-
}
|
|
38714
|
-
};
|
|
38715
|
-
});
|
|
38716
|
-
|
|
38717
38623
|
// ../sdk/src/modules/tasks.ts
|
|
38718
38624
|
var TasksModule;
|
|
38719
38625
|
var init_tasks = __esm(() => {
|
|
@@ -38898,7 +38804,6 @@ class LocusClient {
|
|
|
38898
38804
|
docs;
|
|
38899
38805
|
ci;
|
|
38900
38806
|
instances;
|
|
38901
|
-
suggestions;
|
|
38902
38807
|
constructor(config2) {
|
|
38903
38808
|
this.emitter = new LocusEmitter;
|
|
38904
38809
|
this.api = axios_default.create({
|
|
@@ -38919,7 +38824,6 @@ class LocusClient {
|
|
|
38919
38824
|
this.docs = new DocsModule(this.api, this.emitter);
|
|
38920
38825
|
this.ci = new CiModule(this.api, this.emitter);
|
|
38921
38826
|
this.instances = new InstancesModule(this.api, this.emitter);
|
|
38922
|
-
this.suggestions = new SuggestionsModule(this.api, this.emitter);
|
|
38923
38827
|
if (config2.retryOptions) {
|
|
38924
38828
|
this.setupRetryInterceptor(config2.retryOptions);
|
|
38925
38829
|
}
|
|
@@ -38989,7 +38893,6 @@ var init_src2 = __esm(() => {
|
|
|
38989
38893
|
init_invitations();
|
|
38990
38894
|
init_organizations();
|
|
38991
38895
|
init_sprints();
|
|
38992
|
-
init_suggestions();
|
|
38993
38896
|
init_tasks();
|
|
38994
38897
|
init_workspaces();
|
|
38995
38898
|
init_discussion_types();
|
|
@@ -39001,7 +38904,6 @@ var init_src2 = __esm(() => {
|
|
|
39001
38904
|
init_invitations();
|
|
39002
38905
|
init_organizations();
|
|
39003
38906
|
init_sprints();
|
|
39004
|
-
init_suggestions();
|
|
39005
38907
|
init_tasks();
|
|
39006
38908
|
init_workspaces();
|
|
39007
38909
|
});
|
|
@@ -39567,7 +39469,6 @@ var init_resolve_bin = __esm(() => {
|
|
|
39567
39469
|
join3(homedir(), ".local", "bin"),
|
|
39568
39470
|
join3(homedir(), ".npm", "bin"),
|
|
39569
39471
|
join3(homedir(), ".npm-global", "bin"),
|
|
39570
|
-
join3(homedir(), ".npm-packages", "bin"),
|
|
39571
39472
|
join3(homedir(), ".yarn", "bin"),
|
|
39572
39473
|
join3(homedir(), ".bun", "bin"),
|
|
39573
39474
|
join3(homedir(), "Library", "pnpm"),
|
|
@@ -41205,7 +41106,7 @@ var init_worker = __esm(() => {
|
|
|
41205
41106
|
var import_config16 = __toESM(require_config(), 1);
|
|
41206
41107
|
|
|
41207
41108
|
// src/bot.ts
|
|
41208
|
-
var
|
|
41109
|
+
var import_telegraf5 = __toESM(require_lib3(), 1);
|
|
41209
41110
|
|
|
41210
41111
|
// src/callbacks.ts
|
|
41211
41112
|
init_src();
|
|
@@ -43445,241 +43346,6 @@ var PlannerOutputSchema = exports_external.object({
|
|
|
43445
43346
|
});
|
|
43446
43347
|
// ../sdk/src/planning/planning-meeting.ts
|
|
43447
43348
|
init_config();
|
|
43448
|
-
// ../sdk/src/proposals/context-gatherer.ts
|
|
43449
|
-
import { execFileSync as execFileSync4 } from "node:child_process";
|
|
43450
|
-
import { existsSync as existsSync9, readdirSync as readdirSync5, readFileSync as readFileSync8 } from "node:fs";
|
|
43451
|
-
import { join as join9 } from "node:path";
|
|
43452
|
-
|
|
43453
|
-
class ContextGatherer {
|
|
43454
|
-
async gather(projectPath, client, workspaceId) {
|
|
43455
|
-
const [activeSprint, allTasks, skippedSuggestions] = await Promise.all([
|
|
43456
|
-
this.fetchActiveSprint(client, workspaceId),
|
|
43457
|
-
this.fetchTasks(client, workspaceId),
|
|
43458
|
-
this.fetchSkippedSuggestions(client, workspaceId)
|
|
43459
|
-
]);
|
|
43460
|
-
const sprintTasks = activeSprint ? allTasks.filter((t) => t.sprintId === activeSprint.id) : [];
|
|
43461
|
-
const backlogTasks = allTasks.filter((t) => !t.sprintId);
|
|
43462
|
-
const gitLog = this.readGitLog(projectPath);
|
|
43463
|
-
const artifactContents = this.readArtifacts(projectPath);
|
|
43464
|
-
const locusInstructions = this.readLocusInstructions(projectPath);
|
|
43465
|
-
return {
|
|
43466
|
-
activeSprint,
|
|
43467
|
-
sprintTasks,
|
|
43468
|
-
backlogTasks,
|
|
43469
|
-
gitLog,
|
|
43470
|
-
artifactContents,
|
|
43471
|
-
locusInstructions,
|
|
43472
|
-
skippedSuggestions
|
|
43473
|
-
};
|
|
43474
|
-
}
|
|
43475
|
-
async fetchActiveSprint(client, workspaceId) {
|
|
43476
|
-
try {
|
|
43477
|
-
return await client.sprints.getActive(workspaceId);
|
|
43478
|
-
} catch {
|
|
43479
|
-
return null;
|
|
43480
|
-
}
|
|
43481
|
-
}
|
|
43482
|
-
async fetchTasks(client, workspaceId) {
|
|
43483
|
-
try {
|
|
43484
|
-
return await client.tasks.list(workspaceId);
|
|
43485
|
-
} catch {
|
|
43486
|
-
return [];
|
|
43487
|
-
}
|
|
43488
|
-
}
|
|
43489
|
-
async fetchSkippedSuggestions(client, workspaceId) {
|
|
43490
|
-
try {
|
|
43491
|
-
return await client.suggestions.list(workspaceId, { status: "SKIPPED" });
|
|
43492
|
-
} catch {
|
|
43493
|
-
return [];
|
|
43494
|
-
}
|
|
43495
|
-
}
|
|
43496
|
-
readGitLog(projectPath) {
|
|
43497
|
-
try {
|
|
43498
|
-
return execFileSync4("git", ["log", "--oneline", "--no-decorate", "-n", "20"], {
|
|
43499
|
-
cwd: projectPath,
|
|
43500
|
-
encoding: "utf-8",
|
|
43501
|
-
timeout: 1e4,
|
|
43502
|
-
stdio: ["pipe", "pipe", "pipe"]
|
|
43503
|
-
}).trim();
|
|
43504
|
-
} catch {
|
|
43505
|
-
return "";
|
|
43506
|
-
}
|
|
43507
|
-
}
|
|
43508
|
-
readArtifacts(projectPath) {
|
|
43509
|
-
const artifactsDir = join9(projectPath, ".locus", "artifacts");
|
|
43510
|
-
if (!existsSync9(artifactsDir))
|
|
43511
|
-
return [];
|
|
43512
|
-
try {
|
|
43513
|
-
const files = readdirSync5(artifactsDir).filter((f) => f.endsWith(".md"));
|
|
43514
|
-
return files.slice(0, 10).map((name) => ({
|
|
43515
|
-
name,
|
|
43516
|
-
content: readFileSync8(join9(artifactsDir, name), "utf-8").slice(0, 2000)
|
|
43517
|
-
}));
|
|
43518
|
-
} catch {
|
|
43519
|
-
return [];
|
|
43520
|
-
}
|
|
43521
|
-
}
|
|
43522
|
-
readLocusInstructions(projectPath) {
|
|
43523
|
-
const locusPath = join9(projectPath, ".locus", "LOCUS.md");
|
|
43524
|
-
if (!existsSync9(locusPath))
|
|
43525
|
-
return null;
|
|
43526
|
-
try {
|
|
43527
|
-
return readFileSync8(locusPath, "utf-8").slice(0, 3000);
|
|
43528
|
-
} catch {
|
|
43529
|
-
return null;
|
|
43530
|
-
}
|
|
43531
|
-
}
|
|
43532
|
-
}
|
|
43533
|
-
// ../sdk/src/proposals/proposal-engine.ts
|
|
43534
|
-
init_src();
|
|
43535
|
-
init_factory();
|
|
43536
|
-
class ProposalEngine {
|
|
43537
|
-
contextGatherer;
|
|
43538
|
-
constructor(contextGatherer) {
|
|
43539
|
-
this.contextGatherer = contextGatherer ?? new ContextGatherer;
|
|
43540
|
-
}
|
|
43541
|
-
async runProposalCycle(projectPath, client, workspaceId) {
|
|
43542
|
-
const context2 = await this.contextGatherer.gather(projectPath, client, workspaceId);
|
|
43543
|
-
return this.generateProposals(context2, projectPath, client, workspaceId);
|
|
43544
|
-
}
|
|
43545
|
-
async generateProposals(context2, projectPath, client, workspaceId) {
|
|
43546
|
-
const prompt = this.buildPrompt(context2);
|
|
43547
|
-
const runner = createAiRunner(undefined, {
|
|
43548
|
-
projectPath,
|
|
43549
|
-
timeoutMs: 5 * 60 * 1000,
|
|
43550
|
-
maxTurns: 1
|
|
43551
|
-
});
|
|
43552
|
-
let aiResponse;
|
|
43553
|
-
try {
|
|
43554
|
-
aiResponse = await runner.run(prompt);
|
|
43555
|
-
} catch {
|
|
43556
|
-
return [];
|
|
43557
|
-
}
|
|
43558
|
-
const proposals = this.parseResponse(aiResponse);
|
|
43559
|
-
const created = [];
|
|
43560
|
-
for (const proposal of proposals) {
|
|
43561
|
-
if (this.isDuplicate(proposal.title, context2.skippedSuggestions)) {
|
|
43562
|
-
continue;
|
|
43563
|
-
}
|
|
43564
|
-
try {
|
|
43565
|
-
const suggestion2 = await client.suggestions.create(workspaceId, {
|
|
43566
|
-
type: "NEXT_STEP" /* NEXT_STEP */,
|
|
43567
|
-
title: proposal.title,
|
|
43568
|
-
description: proposal.description,
|
|
43569
|
-
metadata: {
|
|
43570
|
-
complexity: proposal.complexity,
|
|
43571
|
-
relatedBacklogItem: proposal.relatedBacklogItem,
|
|
43572
|
-
source: "proposal-engine"
|
|
43573
|
-
}
|
|
43574
|
-
});
|
|
43575
|
-
created.push(suggestion2);
|
|
43576
|
-
} catch {}
|
|
43577
|
-
}
|
|
43578
|
-
return created;
|
|
43579
|
-
}
|
|
43580
|
-
buildPrompt(context2) {
|
|
43581
|
-
const sections = [];
|
|
43582
|
-
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.");
|
|
43583
|
-
if (context2.activeSprint) {
|
|
43584
|
-
const sprintInfo = `Sprint: ${context2.activeSprint.name} (${context2.activeSprint.status})`;
|
|
43585
|
-
const tasksByStatus = this.groupTasksByStatus(context2.sprintTasks);
|
|
43586
|
-
sections.push(`## Current Sprint
|
|
43587
|
-
${sprintInfo}
|
|
43588
|
-
${tasksByStatus}`);
|
|
43589
|
-
}
|
|
43590
|
-
if (context2.backlogTasks.length > 0) {
|
|
43591
|
-
const backlogList = context2.backlogTasks.slice(0, 15).map((t) => `- [${t.priority}] ${t.title}${t.description ? `: ${t.description.slice(0, 100)}` : ""}`).join(`
|
|
43592
|
-
`);
|
|
43593
|
-
sections.push(`## Backlog Items
|
|
43594
|
-
${backlogList}`);
|
|
43595
|
-
}
|
|
43596
|
-
if (context2.gitLog) {
|
|
43597
|
-
sections.push(`## Recent Commits (last 20)
|
|
43598
|
-
${context2.gitLog}`);
|
|
43599
|
-
}
|
|
43600
|
-
if (context2.artifactContents.length > 0) {
|
|
43601
|
-
const artifacts = context2.artifactContents.map((a) => `### ${a.name}
|
|
43602
|
-
${a.content}`).join(`
|
|
43603
|
-
|
|
43604
|
-
`);
|
|
43605
|
-
sections.push(`## Product Context
|
|
43606
|
-
${artifacts}`);
|
|
43607
|
-
}
|
|
43608
|
-
if (context2.locusInstructions) {
|
|
43609
|
-
sections.push(`## Project Instructions
|
|
43610
|
-
${context2.locusInstructions}`);
|
|
43611
|
-
}
|
|
43612
|
-
if (context2.skippedSuggestions.length > 0) {
|
|
43613
|
-
const skipped = context2.skippedSuggestions.map((s) => `- ${s.title}`).join(`
|
|
43614
|
-
`);
|
|
43615
|
-
sections.push(`## Previously Skipped Proposals (do NOT re-propose these)
|
|
43616
|
-
${skipped}`);
|
|
43617
|
-
}
|
|
43618
|
-
sections.push(`## Instructions
|
|
43619
|
-
Propose 1-3 high-value next steps. For each, respond with exactly this format:
|
|
43620
|
-
|
|
43621
|
-
PROPOSAL_START
|
|
43622
|
-
Title: <clear, concise title>
|
|
43623
|
-
Description: <what to do and why, 2-4 sentences>
|
|
43624
|
-
Complexity: <low|medium|high>
|
|
43625
|
-
Related Backlog: <title of related backlog item, or "none">
|
|
43626
|
-
PROPOSAL_END
|
|
43627
|
-
|
|
43628
|
-
Rules:
|
|
43629
|
-
- Focus on what would deliver the most value right now
|
|
43630
|
-
- Align with the current sprint goals when possible
|
|
43631
|
-
- Don't propose things that are already in progress
|
|
43632
|
-
- Don't re-propose previously skipped suggestions
|
|
43633
|
-
- Keep proposals specific and actionable`);
|
|
43634
|
-
return sections.join(`
|
|
43635
|
-
|
|
43636
|
-
`);
|
|
43637
|
-
}
|
|
43638
|
-
parseResponse(response) {
|
|
43639
|
-
const proposals = [];
|
|
43640
|
-
const blocks = response.split("PROPOSAL_START");
|
|
43641
|
-
for (const block of blocks) {
|
|
43642
|
-
const endIdx = block.indexOf("PROPOSAL_END");
|
|
43643
|
-
if (endIdx === -1)
|
|
43644
|
-
continue;
|
|
43645
|
-
const content = block.slice(0, endIdx).trim();
|
|
43646
|
-
const title = this.extractField(content, "Title");
|
|
43647
|
-
const description = this.extractField(content, "Description");
|
|
43648
|
-
const complexity = this.extractField(content, "Complexity") ?? "medium";
|
|
43649
|
-
const relatedRaw = this.extractField(content, "Related Backlog");
|
|
43650
|
-
const relatedBacklogItem = relatedRaw && relatedRaw.toLowerCase() !== "none" ? relatedRaw : null;
|
|
43651
|
-
if (title && description) {
|
|
43652
|
-
proposals.push({
|
|
43653
|
-
title: title.slice(0, 200),
|
|
43654
|
-
description: description.slice(0, 2000),
|
|
43655
|
-
complexity: complexity.toLowerCase(),
|
|
43656
|
-
relatedBacklogItem
|
|
43657
|
-
});
|
|
43658
|
-
}
|
|
43659
|
-
}
|
|
43660
|
-
return proposals.slice(0, 3);
|
|
43661
|
-
}
|
|
43662
|
-
extractField(content, field) {
|
|
43663
|
-
const regex = new RegExp(`^${field}:\\s*(.+)`, "im");
|
|
43664
|
-
const match = content.match(regex);
|
|
43665
|
-
return match ? match[1].trim() : null;
|
|
43666
|
-
}
|
|
43667
|
-
isDuplicate(title, skipped) {
|
|
43668
|
-
const normalized = title.toLowerCase().trim();
|
|
43669
|
-
return skipped.some((s) => {
|
|
43670
|
-
const skippedNorm = s.title.toLowerCase().trim();
|
|
43671
|
-
return skippedNorm === normalized || skippedNorm.includes(normalized) || normalized.includes(skippedNorm);
|
|
43672
|
-
});
|
|
43673
|
-
}
|
|
43674
|
-
groupTasksByStatus(tasks2) {
|
|
43675
|
-
const groups = {};
|
|
43676
|
-
for (const t of tasks2) {
|
|
43677
|
-
groups[t.status] = (groups[t.status] ?? 0) + 1;
|
|
43678
|
-
}
|
|
43679
|
-
return Object.entries(groups).map(([status, count]) => `- ${status}: ${count} task(s)`).join(`
|
|
43680
|
-
`);
|
|
43681
|
-
}
|
|
43682
|
-
}
|
|
43683
43349
|
// ../sdk/src/index-node.ts
|
|
43684
43350
|
init_colors();
|
|
43685
43351
|
|
|
@@ -44143,218 +43809,6 @@ ${escapeHtml(desc)}
|
|
|
44143
43809
|
const artifactName = ctx.match[1];
|
|
44144
43810
|
await convertArtifactToPlan(ctx, config2, executor, artifactName);
|
|
44145
43811
|
});
|
|
44146
|
-
bot.action(/^proposal_start_(.+)$/, async (ctx) => {
|
|
44147
|
-
await ctx.answerCbQuery("Starting proposal…");
|
|
44148
|
-
const suggestionId = ctx.match[1];
|
|
44149
|
-
if (!config2.apiKey) {
|
|
44150
|
-
await ctx.reply(formatError2("API key is required to act on proposals."), {
|
|
44151
|
-
parse_mode: "HTML"
|
|
44152
|
-
});
|
|
44153
|
-
return;
|
|
44154
|
-
}
|
|
44155
|
-
try {
|
|
44156
|
-
const { client, workspaceId } = await getClientAndWorkspace(config2);
|
|
44157
|
-
const suggestion2 = await client.suggestions.updateStatus(workspaceId, suggestionId, { status: "ACTED_ON" /* ACTED_ON */ });
|
|
44158
|
-
try {
|
|
44159
|
-
await ctx.editMessageText(`▶️ Proposal "<b>${escapeHtml(suggestion2.title)}</b>" accepted. Starting planning…`, { parse_mode: "HTML" });
|
|
44160
|
-
} catch {
|
|
44161
|
-
await ctx.reply(`▶️ Proposal "<b>${escapeHtml(suggestion2.title)}</b>" accepted. Starting planning…`, { parse_mode: "HTML" });
|
|
44162
|
-
}
|
|
44163
|
-
const planPrompt = `${suggestion2.title}
|
|
44164
|
-
|
|
44165
|
-
${suggestion2.description}`;
|
|
44166
|
-
const args = executor.buildArgs(["plan", planPrompt], {
|
|
44167
|
-
needsApiKey: true
|
|
44168
|
-
});
|
|
44169
|
-
const result = await executor.execute(args);
|
|
44170
|
-
const output = (result.stdout + result.stderr).trim();
|
|
44171
|
-
if (output) {
|
|
44172
|
-
const parts = splitMessage(formatCommandOutput("locus plan", output, result.exitCode));
|
|
44173
|
-
for (const part of parts) {
|
|
44174
|
-
await ctx.reply(part, { parse_mode: "HTML" });
|
|
44175
|
-
}
|
|
44176
|
-
}
|
|
44177
|
-
} catch (err) {
|
|
44178
|
-
console.error("[callback:proposal_start] Failed:", err);
|
|
44179
|
-
await ctx.reply(formatError2(`Failed to start proposal: ${err instanceof Error ? err.message : String(err)}`), { parse_mode: "HTML" });
|
|
44180
|
-
}
|
|
44181
|
-
});
|
|
44182
|
-
bot.action(/^proposal_skip_(.+)$/, async (ctx) => {
|
|
44183
|
-
await ctx.answerCbQuery("Skipping…");
|
|
44184
|
-
const suggestionId = ctx.match[1];
|
|
44185
|
-
if (!config2.apiKey) {
|
|
44186
|
-
await ctx.reply(formatError2("API key is required to act on proposals."), {
|
|
44187
|
-
parse_mode: "HTML"
|
|
44188
|
-
});
|
|
44189
|
-
return;
|
|
44190
|
-
}
|
|
44191
|
-
try {
|
|
44192
|
-
const { client, workspaceId } = await getClientAndWorkspace(config2);
|
|
44193
|
-
const suggestion2 = await client.suggestions.updateStatus(workspaceId, suggestionId, { status: "SKIPPED" /* SKIPPED */ });
|
|
44194
|
-
try {
|
|
44195
|
-
await ctx.editMessageText(`⏭ Proposal "<b>${escapeHtml(suggestion2.title)}</b>" skipped. Won't suggest this again.`, { parse_mode: "HTML" });
|
|
44196
|
-
} catch {
|
|
44197
|
-
await ctx.reply(`⏭ Proposal "<b>${escapeHtml(suggestion2.title)}</b>" skipped. Won't suggest this again.`, { parse_mode: "HTML" });
|
|
44198
|
-
}
|
|
44199
|
-
} catch (err) {
|
|
44200
|
-
console.error("[callback:proposal_skip] Failed:", err);
|
|
44201
|
-
await ctx.reply(formatError2(`Failed to skip proposal: ${err instanceof Error ? err.message : String(err)}`), { parse_mode: "HTML" });
|
|
44202
|
-
}
|
|
44203
|
-
});
|
|
44204
|
-
bot.action(/^proposal_details_(.+)$/, async (ctx) => {
|
|
44205
|
-
await ctx.answerCbQuery();
|
|
44206
|
-
const suggestionId = ctx.match[1];
|
|
44207
|
-
if (!config2.apiKey) {
|
|
44208
|
-
await ctx.reply(formatError2("API key is required to view proposal details."), { parse_mode: "HTML" });
|
|
44209
|
-
return;
|
|
44210
|
-
}
|
|
44211
|
-
try {
|
|
44212
|
-
const { client, workspaceId } = await getClientAndWorkspace(config2);
|
|
44213
|
-
const suggestion2 = await client.suggestions.get(workspaceId, suggestionId);
|
|
44214
|
-
const complexityMap = {
|
|
44215
|
-
low: "2/5",
|
|
44216
|
-
medium: "3/5",
|
|
44217
|
-
high: "4/5"
|
|
44218
|
-
};
|
|
44219
|
-
const complexity = complexityMap[String(suggestion2.metadata?.complexity).toLowerCase()] ?? "—";
|
|
44220
|
-
const relatedTo = suggestion2.metadata?.relatedBacklogItem || "New initiative";
|
|
44221
|
-
let msg = `\uD83D\uDCCB <b>Proposal Details</b>
|
|
44222
|
-
|
|
44223
|
-
`;
|
|
44224
|
-
msg += `<b>Title:</b> ${escapeHtml(suggestion2.title)}
|
|
44225
|
-
`;
|
|
44226
|
-
msg += `<b>Type:</b> ${escapeHtml(suggestion2.type)}
|
|
44227
|
-
`;
|
|
44228
|
-
msg += `<b>Status:</b> ${escapeHtml(suggestion2.status)}
|
|
44229
|
-
`;
|
|
44230
|
-
msg += `<b>Complexity:</b> ${escapeHtml(complexity)}
|
|
44231
|
-
`;
|
|
44232
|
-
msg += `<b>Related to:</b> ${escapeHtml(relatedTo)}
|
|
44233
|
-
`;
|
|
44234
|
-
msg += `<b>Created:</b> ${escapeHtml(suggestion2.createdAt)}
|
|
44235
|
-
`;
|
|
44236
|
-
msg += `<b>Expires:</b> ${escapeHtml(suggestion2.expiresAt)}
|
|
44237
|
-
`;
|
|
44238
|
-
if (suggestion2.jobRunId) {
|
|
44239
|
-
msg += `<b>Job Run:</b> <code>${escapeHtml(suggestion2.jobRunId)}</code>
|
|
44240
|
-
`;
|
|
44241
|
-
}
|
|
44242
|
-
msg += `
|
|
44243
|
-
<b>Description:</b>
|
|
44244
|
-
${escapeHtml(truncateOutput(suggestion2.description, 2000))}`;
|
|
44245
|
-
if (suggestion2.metadata && Object.keys(suggestion2.metadata).length > 0) {
|
|
44246
|
-
const {
|
|
44247
|
-
complexity: _c,
|
|
44248
|
-
relatedBacklogItem: _r,
|
|
44249
|
-
...rest
|
|
44250
|
-
} = suggestion2.metadata;
|
|
44251
|
-
if (Object.keys(rest).length > 0) {
|
|
44252
|
-
msg += `
|
|
44253
|
-
|
|
44254
|
-
<b>Metadata:</b>
|
|
44255
|
-
<pre>${escapeHtml(JSON.stringify(rest, null, 2).slice(0, 500))}</pre>`;
|
|
44256
|
-
}
|
|
44257
|
-
}
|
|
44258
|
-
const parts = splitMessage(msg);
|
|
44259
|
-
for (const part of parts) {
|
|
44260
|
-
await ctx.reply(part, {
|
|
44261
|
-
parse_mode: "HTML",
|
|
44262
|
-
link_preview_options: { is_disabled: true }
|
|
44263
|
-
});
|
|
44264
|
-
}
|
|
44265
|
-
} catch (err) {
|
|
44266
|
-
console.error("[callback:proposal_details] Failed:", err);
|
|
44267
|
-
await ctx.reply(formatError2(`Failed to fetch proposal: ${err instanceof Error ? err.message : String(err)}`), { parse_mode: "HTML" });
|
|
44268
|
-
}
|
|
44269
|
-
});
|
|
44270
|
-
bot.action(/^suggestion_fix_(.+)$/, async (ctx) => {
|
|
44271
|
-
await ctx.answerCbQuery("Applying fix…");
|
|
44272
|
-
const suggestionId = ctx.match[1];
|
|
44273
|
-
if (!config2.apiKey) {
|
|
44274
|
-
await ctx.reply(formatError2("API key is required to act on suggestions."), { parse_mode: "HTML" });
|
|
44275
|
-
return;
|
|
44276
|
-
}
|
|
44277
|
-
try {
|
|
44278
|
-
const { client, workspaceId } = await getClientAndWorkspace(config2);
|
|
44279
|
-
const suggestion2 = await client.suggestions.updateStatus(workspaceId, suggestionId, { status: "ACTED_ON" /* ACTED_ON */ });
|
|
44280
|
-
try {
|
|
44281
|
-
await ctx.editMessageText(formatSuccess(`Suggestion "<b>${escapeHtml(suggestion2.title)}</b>" marked as fixed.`), { parse_mode: "HTML" });
|
|
44282
|
-
} catch {
|
|
44283
|
-
await ctx.reply(formatSuccess(`Suggestion "<b>${escapeHtml(suggestion2.title)}</b>" marked as fixed.`), { parse_mode: "HTML" });
|
|
44284
|
-
}
|
|
44285
|
-
} catch (err) {
|
|
44286
|
-
console.error("[callback:suggestion_fix] Failed:", err);
|
|
44287
|
-
await ctx.reply(formatError2(`Failed to update suggestion: ${err instanceof Error ? err.message : String(err)}`), { parse_mode: "HTML" });
|
|
44288
|
-
}
|
|
44289
|
-
});
|
|
44290
|
-
bot.action(/^suggestion_skip_(.+)$/, async (ctx) => {
|
|
44291
|
-
await ctx.answerCbQuery("Skipping…");
|
|
44292
|
-
const suggestionId = ctx.match[1];
|
|
44293
|
-
if (!config2.apiKey) {
|
|
44294
|
-
await ctx.reply(formatError2("API key is required to act on suggestions."), { parse_mode: "HTML" });
|
|
44295
|
-
return;
|
|
44296
|
-
}
|
|
44297
|
-
try {
|
|
44298
|
-
const { client, workspaceId } = await getClientAndWorkspace(config2);
|
|
44299
|
-
const suggestion2 = await client.suggestions.updateStatus(workspaceId, suggestionId, { status: "SKIPPED" /* SKIPPED */ });
|
|
44300
|
-
try {
|
|
44301
|
-
await ctx.editMessageText(`⏭ Suggestion "<b>${escapeHtml(suggestion2.title)}</b>" skipped.`, { parse_mode: "HTML" });
|
|
44302
|
-
} catch {
|
|
44303
|
-
await ctx.reply(`⏭ Suggestion "<b>${escapeHtml(suggestion2.title)}</b>" skipped.`, { parse_mode: "HTML" });
|
|
44304
|
-
}
|
|
44305
|
-
} catch (err) {
|
|
44306
|
-
console.error("[callback:suggestion_skip] Failed:", err);
|
|
44307
|
-
await ctx.reply(formatError2(`Failed to update suggestion: ${err instanceof Error ? err.message : String(err)}`), { parse_mode: "HTML" });
|
|
44308
|
-
}
|
|
44309
|
-
});
|
|
44310
|
-
bot.action(/^suggestion_details_(.+)$/, async (ctx) => {
|
|
44311
|
-
await ctx.answerCbQuery();
|
|
44312
|
-
const suggestionId = ctx.match[1];
|
|
44313
|
-
if (!config2.apiKey) {
|
|
44314
|
-
await ctx.reply(formatError2("API key is required to view suggestion details."), { parse_mode: "HTML" });
|
|
44315
|
-
return;
|
|
44316
|
-
}
|
|
44317
|
-
try {
|
|
44318
|
-
const { client, workspaceId } = await getClientAndWorkspace(config2);
|
|
44319
|
-
const suggestion2 = await client.suggestions.get(workspaceId, suggestionId);
|
|
44320
|
-
let msg = `\uD83D\uDCCB <b>Suggestion Details</b>
|
|
44321
|
-
|
|
44322
|
-
`;
|
|
44323
|
-
msg += `<b>Title:</b> ${escapeHtml(suggestion2.title)}
|
|
44324
|
-
`;
|
|
44325
|
-
msg += `<b>Type:</b> ${escapeHtml(suggestion2.type)}
|
|
44326
|
-
`;
|
|
44327
|
-
msg += `<b>Status:</b> ${escapeHtml(suggestion2.status)}
|
|
44328
|
-
`;
|
|
44329
|
-
msg += `<b>Created:</b> ${escapeHtml(suggestion2.createdAt)}
|
|
44330
|
-
`;
|
|
44331
|
-
msg += `<b>Expires:</b> ${escapeHtml(suggestion2.expiresAt)}
|
|
44332
|
-
`;
|
|
44333
|
-
if (suggestion2.jobRunId) {
|
|
44334
|
-
msg += `<b>Job Run:</b> <code>${escapeHtml(suggestion2.jobRunId)}</code>
|
|
44335
|
-
`;
|
|
44336
|
-
}
|
|
44337
|
-
msg += `
|
|
44338
|
-
<b>Description:</b>
|
|
44339
|
-
${escapeHtml(truncateOutput(suggestion2.description, 2000))}`;
|
|
44340
|
-
if (suggestion2.metadata && Object.keys(suggestion2.metadata).length > 0) {
|
|
44341
|
-
msg += `
|
|
44342
|
-
|
|
44343
|
-
<b>Metadata:</b>
|
|
44344
|
-
<pre>${escapeHtml(JSON.stringify(suggestion2.metadata, null, 2).slice(0, 500))}</pre>`;
|
|
44345
|
-
}
|
|
44346
|
-
const parts = splitMessage(msg);
|
|
44347
|
-
for (const part of parts) {
|
|
44348
|
-
await ctx.reply(part, {
|
|
44349
|
-
parse_mode: "HTML",
|
|
44350
|
-
link_preview_options: { is_disabled: true }
|
|
44351
|
-
});
|
|
44352
|
-
}
|
|
44353
|
-
} catch (err) {
|
|
44354
|
-
console.error("[callback:suggestion_details] Failed:", err);
|
|
44355
|
-
await ctx.reply(formatError2(`Failed to fetch suggestion: ${err instanceof Error ? err.message : String(err)}`), { parse_mode: "HTML" });
|
|
44356
|
-
}
|
|
44357
|
-
});
|
|
44358
43812
|
}
|
|
44359
43813
|
|
|
44360
43814
|
// src/commands/activity.ts
|
|
@@ -44514,8 +43968,8 @@ Total: ${agents.length} active agent(s)`;
|
|
|
44514
43968
|
}
|
|
44515
43969
|
}
|
|
44516
43970
|
// src/commands/config.ts
|
|
44517
|
-
import { existsSync as
|
|
44518
|
-
import { join as
|
|
43971
|
+
import { existsSync as existsSync9, readFileSync as readFileSync8, writeFileSync as writeFileSync3 } from "node:fs";
|
|
43972
|
+
import { join as join9 } from "node:path";
|
|
44519
43973
|
|
|
44520
43974
|
// src/command-whitelist.ts
|
|
44521
43975
|
var SAFE_BRANCH = /^[a-zA-Z0-9_\-./]+$/;
|
|
@@ -44883,13 +44337,13 @@ var USAGE = `<b>Usage:</b>
|
|
|
44883
44337
|
${Object.entries(ALLOWED_KEYS).map(([k, v]) => ` \`${k}\` — ${v.description}`).join(`
|
|
44884
44338
|
`)}`;
|
|
44885
44339
|
function getSettingsPath(config2) {
|
|
44886
|
-
return
|
|
44340
|
+
return join9(config2.projectPath, CONFIG_DIR, SETTINGS_FILE);
|
|
44887
44341
|
}
|
|
44888
44342
|
function loadSettings(config2) {
|
|
44889
44343
|
const path = getSettingsPath(config2);
|
|
44890
|
-
if (!
|
|
44344
|
+
if (!existsSync9(path))
|
|
44891
44345
|
return null;
|
|
44892
|
-
const raw =
|
|
44346
|
+
const raw = readFileSync8(path, "utf-8");
|
|
44893
44347
|
return JSON.parse(raw);
|
|
44894
44348
|
}
|
|
44895
44349
|
function saveSettings(config2, settings) {
|
|
@@ -45212,67 +44666,16 @@ async function dashboardCommand(ctx, config2) {
|
|
|
45212
44666
|
import { spawn as spawn4 } from "node:child_process";
|
|
45213
44667
|
|
|
45214
44668
|
// src/env.ts
|
|
45215
|
-
import { existsSync as existsSync11, readdirSync as readdirSync6, readFileSync as readFileSync10 } from "node:fs";
|
|
45216
44669
|
import { homedir as homedir2 } from "node:os";
|
|
45217
|
-
import { join as
|
|
45218
|
-
function resolveNvmBinDir() {
|
|
45219
|
-
const nvmDir = process.env.NVM_DIR || join11(homedir2(), ".nvm");
|
|
45220
|
-
const versionsDir = join11(nvmDir, "versions", "node");
|
|
45221
|
-
if (!existsSync11(versionsDir))
|
|
45222
|
-
return null;
|
|
45223
|
-
let versions2;
|
|
45224
|
-
try {
|
|
45225
|
-
versions2 = readdirSync6(versionsDir).filter((d) => d.startsWith("v"));
|
|
45226
|
-
} catch {
|
|
45227
|
-
return null;
|
|
45228
|
-
}
|
|
45229
|
-
if (versions2.length === 0)
|
|
45230
|
-
return null;
|
|
45231
|
-
const currentNodeVersion = `v${process.versions.node}`;
|
|
45232
|
-
const currentBin = join11(versionsDir, currentNodeVersion, "bin");
|
|
45233
|
-
if (versions2.includes(currentNodeVersion) && existsSync11(currentBin)) {
|
|
45234
|
-
return currentBin;
|
|
45235
|
-
}
|
|
45236
|
-
const aliasPath = join11(nvmDir, "alias", "default");
|
|
45237
|
-
if (existsSync11(aliasPath)) {
|
|
45238
|
-
try {
|
|
45239
|
-
const alias = readFileSync10(aliasPath, "utf-8").trim();
|
|
45240
|
-
const match = versions2.find((v) => v === `v${alias}` || v.startsWith(`v${alias}.`));
|
|
45241
|
-
if (match) {
|
|
45242
|
-
const bin2 = join11(versionsDir, match, "bin");
|
|
45243
|
-
if (existsSync11(bin2))
|
|
45244
|
-
return bin2;
|
|
45245
|
-
}
|
|
45246
|
-
} catch {}
|
|
45247
|
-
}
|
|
45248
|
-
const sorted = versions2.sort((a, b) => {
|
|
45249
|
-
const pa = a.slice(1).split(".").map(Number);
|
|
45250
|
-
const pb = b.slice(1).split(".").map(Number);
|
|
45251
|
-
for (let i = 0;i < 3; i++) {
|
|
45252
|
-
if ((pa[i] || 0) !== (pb[i] || 0))
|
|
45253
|
-
return (pb[i] || 0) - (pa[i] || 0);
|
|
45254
|
-
}
|
|
45255
|
-
return 0;
|
|
45256
|
-
});
|
|
45257
|
-
const bin = join11(versionsDir, sorted[0], "bin");
|
|
45258
|
-
return existsSync11(bin) ? bin : null;
|
|
45259
|
-
}
|
|
44670
|
+
import { join as join10 } from "node:path";
|
|
45260
44671
|
function extraPathDirs() {
|
|
45261
44672
|
const home = homedir2();
|
|
45262
|
-
|
|
45263
|
-
|
|
45264
|
-
|
|
45265
|
-
|
|
45266
|
-
join11(home, ".npm-global", "bin"),
|
|
44673
|
+
return [
|
|
44674
|
+
join10(home, ".bun", "bin"),
|
|
44675
|
+
join10(home, ".nvm", "current", "bin"),
|
|
44676
|
+
join10(home, ".local", "bin"),
|
|
45267
44677
|
"/usr/local/bin"
|
|
45268
44678
|
];
|
|
45269
|
-
const nvmBin = resolveNvmBinDir();
|
|
45270
|
-
if (nvmBin)
|
|
45271
|
-
dirs.push(nvmBin);
|
|
45272
|
-
const fnmCurrent = join11(home, ".fnm", "current", "bin");
|
|
45273
|
-
if (existsSync11(fnmCurrent))
|
|
45274
|
-
dirs.push(fnmCurrent);
|
|
45275
|
-
return dirs;
|
|
45276
44679
|
}
|
|
45277
44680
|
function buildSpawnEnv() {
|
|
45278
44681
|
const existing = process.env.PATH ?? "";
|
|
@@ -46193,7 +45596,7 @@ async function approveTaskCommand(ctx, config2) {
|
|
|
46193
45596
|
// src/commands/upgrade.ts
|
|
46194
45597
|
import { spawn as spawn5 } from "node:child_process";
|
|
46195
45598
|
import { homedir as homedir3 } from "node:os";
|
|
46196
|
-
import { join as
|
|
45599
|
+
import { join as join11 } from "node:path";
|
|
46197
45600
|
function runCommand2(cmd, args, timeout) {
|
|
46198
45601
|
return new Promise((resolve2) => {
|
|
46199
45602
|
const proc = spawn5(cmd, args, {
|
|
@@ -46232,11 +45635,13 @@ function getRestartInfo() {
|
|
|
46232
45635
|
if (platform === "linux") {
|
|
46233
45636
|
return {
|
|
46234
45637
|
label: "systemd (Linux)",
|
|
46235
|
-
commands: [
|
|
45638
|
+
commands: [
|
|
45639
|
+
{ cmd: "sudo", args: ["systemctl", "restart", "locus-telegram"] }
|
|
45640
|
+
]
|
|
46236
45641
|
};
|
|
46237
45642
|
}
|
|
46238
45643
|
if (platform === "darwin") {
|
|
46239
|
-
const plistPath =
|
|
45644
|
+
const plistPath = join11(homedir3(), "Library/LaunchAgents/com.locus.telegram.plist");
|
|
46240
45645
|
return {
|
|
46241
45646
|
label: "launchctl (macOS)",
|
|
46242
45647
|
commands: [
|
|
@@ -46352,7 +45757,7 @@ async function workspaceCommand(ctx, config2) {
|
|
|
46352
45757
|
}
|
|
46353
45758
|
// src/executor.ts
|
|
46354
45759
|
import { spawn as spawn6 } from "node:child_process";
|
|
46355
|
-
import { join as
|
|
45760
|
+
import { join as join12 } from "node:path";
|
|
46356
45761
|
function timestamp2() {
|
|
46357
45762
|
return new Date().toLocaleTimeString("en-GB", { hour12: false });
|
|
46358
45763
|
}
|
|
@@ -46368,7 +45773,7 @@ class CliExecutor {
|
|
|
46368
45773
|
}
|
|
46369
45774
|
resolveCommand(args) {
|
|
46370
45775
|
if (this.config.testMode) {
|
|
46371
|
-
const cliPath =
|
|
45776
|
+
const cliPath = join12(this.config.projectPath, "packages/cli/src/cli.ts");
|
|
46372
45777
|
return { cmd: "bun", cmdArgs: ["run", cliPath, ...args] };
|
|
46373
45778
|
}
|
|
46374
45779
|
return { cmd: "locus", cmdArgs: args };
|
|
@@ -46527,90 +45932,12 @@ class CliExecutor {
|
|
|
46527
45932
|
}
|
|
46528
45933
|
}
|
|
46529
45934
|
|
|
46530
|
-
// src/notifications.ts
|
|
46531
|
-
var import_telegraf5 = __toESM(require_lib3(), 1);
|
|
46532
|
-
var PROPOSALS_GENERATED = "PROPOSALS_GENERATED";
|
|
46533
|
-
var COMPLEXITY_DISPLAY = {
|
|
46534
|
-
low: "2/5",
|
|
46535
|
-
medium: "3/5",
|
|
46536
|
-
high: "4/5"
|
|
46537
|
-
};
|
|
46538
|
-
var SUGGESTION_TYPE_ICONS = {
|
|
46539
|
-
CODE_FIX: "\uD83D\uDD27",
|
|
46540
|
-
DEPENDENCY_UPDATE: "\uD83D\uDCE6",
|
|
46541
|
-
NEXT_STEP: "➡️",
|
|
46542
|
-
REFACTOR: "♻️",
|
|
46543
|
-
TEST_FIX: "\uD83E\uDDEA"
|
|
46544
|
-
};
|
|
46545
|
-
|
|
46546
|
-
class Notifier {
|
|
46547
|
-
bot;
|
|
46548
|
-
chatId;
|
|
46549
|
-
constructor(bot, chatId) {
|
|
46550
|
-
this.bot = bot;
|
|
46551
|
-
this.chatId = chatId;
|
|
46552
|
-
}
|
|
46553
|
-
connect(emitter) {
|
|
46554
|
-
emitter.on(PROPOSALS_GENERATED, (payload) => {
|
|
46555
|
-
for (const suggestion2 of payload.suggestions) {
|
|
46556
|
-
this.notifyProposal(suggestion2).catch((err) => console.error("[notifier] Failed to send proposal:", err));
|
|
46557
|
-
}
|
|
46558
|
-
});
|
|
46559
|
-
}
|
|
46560
|
-
async notifyProposal(suggestion2) {
|
|
46561
|
-
const complexity = COMPLEXITY_DISPLAY[String(suggestion2.metadata?.complexity).toLowerCase()] ?? "3/5";
|
|
46562
|
-
const relatedTo = suggestion2.metadata?.relatedBacklogItem || "New initiative";
|
|
46563
|
-
let msg = `\uD83D\uDCA1 <b>Proposal:</b> ${escapeHtml(suggestion2.title)}
|
|
46564
|
-
|
|
46565
|
-
`;
|
|
46566
|
-
msg += `${escapeHtml(truncateOutput(suggestion2.description, 800))}
|
|
46567
|
-
|
|
46568
|
-
`;
|
|
46569
|
-
msg += `<b>Complexity:</b> ${escapeHtml(complexity)}
|
|
46570
|
-
`;
|
|
46571
|
-
msg += `<b>Related to:</b> ${escapeHtml(relatedTo)}`;
|
|
46572
|
-
const buttons = [
|
|
46573
|
-
[
|
|
46574
|
-
import_telegraf5.Markup.button.callback("▶️ Start", `proposal_start_${suggestion2.id}`),
|
|
46575
|
-
import_telegraf5.Markup.button.callback("⏭ Skip", `proposal_skip_${suggestion2.id}`),
|
|
46576
|
-
import_telegraf5.Markup.button.callback("\uD83D\uDCCB Details", `proposal_details_${suggestion2.id}`)
|
|
46577
|
-
]
|
|
46578
|
-
];
|
|
46579
|
-
await this.bot.telegram.sendMessage(this.chatId, msg, {
|
|
46580
|
-
parse_mode: "HTML",
|
|
46581
|
-
...import_telegraf5.Markup.inlineKeyboard(buttons)
|
|
46582
|
-
});
|
|
46583
|
-
}
|
|
46584
|
-
async notifySuggestion(suggestion2) {
|
|
46585
|
-
const icon = SUGGESTION_TYPE_ICONS[suggestion2.type] ?? "\uD83D\uDCA1";
|
|
46586
|
-
let msg = `${icon} <b>Suggestion:</b> ${escapeHtml(suggestion2.title)}
|
|
46587
|
-
|
|
46588
|
-
`;
|
|
46589
|
-
msg += `${escapeHtml(truncateOutput(suggestion2.description, 800))}
|
|
46590
|
-
`;
|
|
46591
|
-
msg += `
|
|
46592
|
-
<b>Type:</b> ${escapeHtml(suggestion2.type)}`;
|
|
46593
|
-
const buttons = [
|
|
46594
|
-
[
|
|
46595
|
-
import_telegraf5.Markup.button.callback("\uD83D\uDD27 Fix", `suggestion_fix_${suggestion2.id}`),
|
|
46596
|
-
import_telegraf5.Markup.button.callback("⏭ Skip", `suggestion_skip_${suggestion2.id}`),
|
|
46597
|
-
import_telegraf5.Markup.button.callback("\uD83D\uDCCB Details", `suggestion_details_${suggestion2.id}`)
|
|
46598
|
-
]
|
|
46599
|
-
];
|
|
46600
|
-
await this.bot.telegram.sendMessage(this.chatId, msg, {
|
|
46601
|
-
parse_mode: "HTML",
|
|
46602
|
-
...import_telegraf5.Markup.inlineKeyboard(buttons)
|
|
46603
|
-
});
|
|
46604
|
-
}
|
|
46605
|
-
}
|
|
46606
|
-
|
|
46607
45935
|
// src/bot.ts
|
|
46608
45936
|
function createBot(config2) {
|
|
46609
|
-
const bot = new
|
|
45937
|
+
const bot = new import_telegraf5.Telegraf(config2.botToken, {
|
|
46610
45938
|
handlerTimeout: HANDLER_TIMEOUT
|
|
46611
45939
|
});
|
|
46612
45940
|
const executor = new CliExecutor(config2);
|
|
46613
|
-
const notifier = new Notifier(bot, config2.chatId);
|
|
46614
45941
|
bot.use(async (ctx, next) => {
|
|
46615
45942
|
const chatId = ctx.chat?.id;
|
|
46616
45943
|
if (chatId !== config2.chatId) {
|
|
@@ -46710,22 +46037,22 @@ function createBot(config2) {
|
|
|
46710
46037
|
{ command: "upgrade", description: "Upgrade Locus CLI & restart bot" },
|
|
46711
46038
|
{ command: "help", description: "Show all commands" }
|
|
46712
46039
|
]).catch((err) => console.error("Failed to set bot commands:", err));
|
|
46713
|
-
return
|
|
46040
|
+
return bot;
|
|
46714
46041
|
}
|
|
46715
46042
|
|
|
46716
46043
|
// src/config.ts
|
|
46717
46044
|
var import_dotenv = __toESM(require_main(), 1);
|
|
46718
|
-
import { existsSync as
|
|
46719
|
-
import { join as
|
|
46045
|
+
import { existsSync as existsSync10, readFileSync as readFileSync9 } from "node:fs";
|
|
46046
|
+
import { join as join13 } from "node:path";
|
|
46720
46047
|
import_dotenv.default.config();
|
|
46721
46048
|
var SETTINGS_FILE2 = "settings.json";
|
|
46722
46049
|
var CONFIG_DIR2 = ".locus";
|
|
46723
46050
|
function loadSettings2(projectPath) {
|
|
46724
|
-
const settingsPath =
|
|
46725
|
-
if (!
|
|
46051
|
+
const settingsPath = join13(projectPath, CONFIG_DIR2, SETTINGS_FILE2);
|
|
46052
|
+
if (!existsSync10(settingsPath)) {
|
|
46726
46053
|
return null;
|
|
46727
46054
|
}
|
|
46728
|
-
const raw =
|
|
46055
|
+
const raw = readFileSync9(settingsPath, "utf-8");
|
|
46729
46056
|
return JSON.parse(raw);
|
|
46730
46057
|
}
|
|
46731
46058
|
function resolveConfig() {
|
|
@@ -46762,66 +46089,6 @@ function resolveConfig() {
|
|
|
46762
46089
|
};
|
|
46763
46090
|
}
|
|
46764
46091
|
|
|
46765
|
-
// src/scheduler.ts
|
|
46766
|
-
var PROPOSAL_INTERVAL_MS = 6 * 60 * 60 * 1000;
|
|
46767
|
-
|
|
46768
|
-
class ProposalScheduler {
|
|
46769
|
-
config;
|
|
46770
|
-
notifier;
|
|
46771
|
-
timer = null;
|
|
46772
|
-
running = false;
|
|
46773
|
-
constructor(config2, notifier) {
|
|
46774
|
-
this.config = config2;
|
|
46775
|
-
this.notifier = notifier;
|
|
46776
|
-
}
|
|
46777
|
-
async start() {
|
|
46778
|
-
if (!this.config.apiKey) {
|
|
46779
|
-
console.log("[scheduler] API key not configured — proposal scheduling disabled");
|
|
46780
|
-
return;
|
|
46781
|
-
}
|
|
46782
|
-
console.log("[scheduler] Starting proposal scheduler (every 6 hours)");
|
|
46783
|
-
this.runCycle();
|
|
46784
|
-
this.timer = setInterval(() => this.runCycle(), PROPOSAL_INTERVAL_MS);
|
|
46785
|
-
}
|
|
46786
|
-
stop() {
|
|
46787
|
-
if (this.timer) {
|
|
46788
|
-
clearInterval(this.timer);
|
|
46789
|
-
this.timer = null;
|
|
46790
|
-
}
|
|
46791
|
-
console.log("[scheduler] Proposal scheduler stopped");
|
|
46792
|
-
}
|
|
46793
|
-
async runCycle() {
|
|
46794
|
-
if (this.running) {
|
|
46795
|
-
console.log("[scheduler] Proposal cycle already in progress, skipping");
|
|
46796
|
-
return;
|
|
46797
|
-
}
|
|
46798
|
-
this.running = true;
|
|
46799
|
-
const ts = new Date().toLocaleTimeString();
|
|
46800
|
-
console.log(`[scheduler] Running proposal cycle at ${ts}`);
|
|
46801
|
-
try {
|
|
46802
|
-
const client = createClient(this.config);
|
|
46803
|
-
const workspaceId = await resolveWorkspaceId(client, this.config);
|
|
46804
|
-
const engine = new ProposalEngine;
|
|
46805
|
-
const suggestions2 = await engine.runProposalCycle(this.config.projectPath, client, workspaceId);
|
|
46806
|
-
if (suggestions2.length > 0) {
|
|
46807
|
-
console.log(`[scheduler] ${suggestions2.length} proposal(s) generated`);
|
|
46808
|
-
for (const s of suggestions2) {
|
|
46809
|
-
console.log(`[scheduler] - ${s.title}`);
|
|
46810
|
-
this.notifier.notifyProposal(s).catch((err) => {
|
|
46811
|
-
console.error("[scheduler] Failed to notify proposal:", err);
|
|
46812
|
-
});
|
|
46813
|
-
}
|
|
46814
|
-
} else {
|
|
46815
|
-
console.log("[scheduler] No new proposals generated");
|
|
46816
|
-
}
|
|
46817
|
-
} catch (err) {
|
|
46818
|
-
console.error(`[scheduler] Proposal cycle failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
46819
|
-
} finally {
|
|
46820
|
-
this.running = false;
|
|
46821
|
-
}
|
|
46822
|
-
}
|
|
46823
|
-
}
|
|
46824
|
-
|
|
46825
46092
|
// src/index.ts
|
|
46826
46093
|
async function main() {
|
|
46827
46094
|
console.log("Locus Telegram Bot");
|
|
@@ -46833,13 +46100,10 @@ async function main() {
|
|
|
46833
46100
|
console.log(`API Key: ${config2.apiKey ? "configured" : "not set"}`);
|
|
46834
46101
|
console.log(`----------------------------------------------
|
|
46835
46102
|
`);
|
|
46836
|
-
const
|
|
46837
|
-
const scheduler = new ProposalScheduler(config2, notifier);
|
|
46838
|
-
await scheduler.start();
|
|
46103
|
+
const bot = createBot(config2);
|
|
46839
46104
|
const shutdown = (signal) => {
|
|
46840
46105
|
console.log(`
|
|
46841
46106
|
Received ${signal}. Shutting down...`);
|
|
46842
|
-
scheduler.stop();
|
|
46843
46107
|
bot.stop(signal);
|
|
46844
46108
|
process.exit(0);
|
|
46845
46109
|
};
|
package/package.json
CHANGED
|
@@ -1,18 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@locusai/telegram",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.16.1",
|
|
4
4
|
"description": "Telegram bot for Locus - remote control your AI agents from Telegram",
|
|
5
5
|
"type": "module",
|
|
6
|
-
"main": "./src/lib.ts",
|
|
7
|
-
"types": "./src/lib.ts",
|
|
8
|
-
"exports": {
|
|
9
|
-
".": {
|
|
10
|
-
"types": "./src/lib.ts",
|
|
11
|
-
"development": "./src/lib.ts",
|
|
12
|
-
"import": "./src/lib.ts",
|
|
13
|
-
"default": "./src/lib.ts"
|
|
14
|
-
}
|
|
15
|
-
},
|
|
16
6
|
"bin": {
|
|
17
7
|
"locus-telegram": "./bin/telegram.js"
|
|
18
8
|
},
|
|
@@ -41,8 +31,8 @@
|
|
|
41
31
|
"author": "",
|
|
42
32
|
"license": "MIT",
|
|
43
33
|
"dependencies": {
|
|
44
|
-
"@locusai/sdk": "^0.
|
|
45
|
-
"@locusai/shared": "^0.
|
|
34
|
+
"@locusai/sdk": "^0.16.1",
|
|
35
|
+
"@locusai/shared": "^0.16.1",
|
|
46
36
|
"dotenv": "^16.4.7",
|
|
47
37
|
"telegraf": "^4.16.3"
|
|
48
38
|
},
|