@locusai/telegram 0.14.2 → 0.14.4
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 +314 -130
- package/package.json +3 -3
package/bin/telegram.js
CHANGED
|
@@ -38909,7 +38909,7 @@ var init_src2 = __esm(() => {
|
|
|
38909
38909
|
});
|
|
38910
38910
|
|
|
38911
38911
|
// ../sdk/src/core/config.ts
|
|
38912
|
-
import { join } from "node:path";
|
|
38912
|
+
import { join as join2 } from "node:path";
|
|
38913
38913
|
function isValidModelForProvider(provider, model) {
|
|
38914
38914
|
return PROVIDER_MODELS[provider].includes(model);
|
|
38915
38915
|
}
|
|
@@ -38917,7 +38917,7 @@ function getModelsForProvider(provider) {
|
|
|
38917
38917
|
return PROVIDER_MODELS[provider];
|
|
38918
38918
|
}
|
|
38919
38919
|
function getLocusPath(projectPath, fileName) {
|
|
38920
|
-
return
|
|
38920
|
+
return join2(projectPath, LOCUS_CONFIG.dir, LOCUS_CONFIG[fileName]);
|
|
38921
38921
|
}
|
|
38922
38922
|
var PROVIDER, CLAUDE_MODELS, CODEX_MODELS, PROVIDER_MODELS, DEFAULT_MODEL, LOCUS_SCHEMA_BASE_URL = "https://locusai.dev/schemas", LOCUS_SCHEMAS, LOCUS_CONFIG;
|
|
38923
38923
|
var init_config = __esm(() => {
|
|
@@ -39373,35 +39373,35 @@ var init_colors = __esm(() => {
|
|
|
39373
39373
|
});
|
|
39374
39374
|
|
|
39375
39375
|
// ../sdk/src/utils/resolve-bin.ts
|
|
39376
|
-
import { existsSync, readdirSync, readFileSync } from "node:fs";
|
|
39376
|
+
import { existsSync as existsSync2, readdirSync as readdirSync2, readFileSync as readFileSync2 } from "node:fs";
|
|
39377
39377
|
import { homedir } from "node:os";
|
|
39378
|
-
import { delimiter, join as
|
|
39378
|
+
import { delimiter, join as join3 } from "node:path";
|
|
39379
39379
|
function getNvmNodeBinDir() {
|
|
39380
|
-
const nvmDir = process.env.NVM_DIR ||
|
|
39381
|
-
const versionsDir =
|
|
39382
|
-
if (!
|
|
39380
|
+
const nvmDir = process.env.NVM_DIR || join3(homedir(), ".nvm");
|
|
39381
|
+
const versionsDir = join3(nvmDir, "versions", "node");
|
|
39382
|
+
if (!existsSync2(versionsDir))
|
|
39383
39383
|
return null;
|
|
39384
39384
|
let versions2;
|
|
39385
39385
|
try {
|
|
39386
|
-
versions2 =
|
|
39386
|
+
versions2 = readdirSync2(versionsDir).filter((d) => d.startsWith("v"));
|
|
39387
39387
|
} catch {
|
|
39388
39388
|
return null;
|
|
39389
39389
|
}
|
|
39390
39390
|
if (versions2.length === 0)
|
|
39391
39391
|
return null;
|
|
39392
39392
|
const currentNodeVersion = `v${process.versions.node}`;
|
|
39393
|
-
const currentBin =
|
|
39394
|
-
if (versions2.includes(currentNodeVersion) &&
|
|
39393
|
+
const currentBin = join3(versionsDir, currentNodeVersion, "bin");
|
|
39394
|
+
if (versions2.includes(currentNodeVersion) && existsSync2(currentBin)) {
|
|
39395
39395
|
return currentBin;
|
|
39396
39396
|
}
|
|
39397
|
-
const aliasPath =
|
|
39398
|
-
if (
|
|
39397
|
+
const aliasPath = join3(nvmDir, "alias", "default");
|
|
39398
|
+
if (existsSync2(aliasPath)) {
|
|
39399
39399
|
try {
|
|
39400
|
-
const alias =
|
|
39400
|
+
const alias = readFileSync2(aliasPath, "utf-8").trim();
|
|
39401
39401
|
const match = versions2.find((v) => v === `v${alias}` || v.startsWith(`v${alias}.`));
|
|
39402
39402
|
if (match) {
|
|
39403
|
-
const bin2 =
|
|
39404
|
-
if (
|
|
39403
|
+
const bin2 = join3(versionsDir, match, "bin");
|
|
39404
|
+
if (existsSync2(bin2))
|
|
39405
39405
|
return bin2;
|
|
39406
39406
|
}
|
|
39407
39407
|
} catch {}
|
|
@@ -39415,18 +39415,18 @@ function getNvmNodeBinDir() {
|
|
|
39415
39415
|
}
|
|
39416
39416
|
return 0;
|
|
39417
39417
|
});
|
|
39418
|
-
const bin =
|
|
39419
|
-
return
|
|
39418
|
+
const bin = join3(versionsDir, sorted[0], "bin");
|
|
39419
|
+
return existsSync2(bin) ? bin : null;
|
|
39420
39420
|
}
|
|
39421
39421
|
function getFnmNodeBinDir() {
|
|
39422
|
-
const fnmDir = process.env.FNM_DIR ||
|
|
39423
|
-
const currentBin =
|
|
39424
|
-
if (
|
|
39422
|
+
const fnmDir = process.env.FNM_DIR || join3(homedir(), ".fnm");
|
|
39423
|
+
const currentBin = join3(fnmDir, "current", "bin");
|
|
39424
|
+
if (existsSync2(currentBin))
|
|
39425
39425
|
return currentBin;
|
|
39426
|
-
const aliasDir =
|
|
39427
|
-
if (
|
|
39428
|
-
const bin =
|
|
39429
|
-
if (
|
|
39426
|
+
const aliasDir = join3(fnmDir, "aliases", "default");
|
|
39427
|
+
if (existsSync2(aliasDir)) {
|
|
39428
|
+
const bin = join3(aliasDir, "bin");
|
|
39429
|
+
if (existsSync2(bin))
|
|
39430
39430
|
return bin;
|
|
39431
39431
|
}
|
|
39432
39432
|
return null;
|
|
@@ -39436,7 +39436,7 @@ function getAugmentedPath() {
|
|
|
39436
39436
|
const currentDirs = new Set(currentPath.split(delimiter));
|
|
39437
39437
|
const extra = [];
|
|
39438
39438
|
for (const dir of EXTRA_BIN_DIRS) {
|
|
39439
|
-
if (!currentDirs.has(dir) &&
|
|
39439
|
+
if (!currentDirs.has(dir) && existsSync2(dir)) {
|
|
39440
39440
|
extra.push(dir);
|
|
39441
39441
|
}
|
|
39442
39442
|
}
|
|
@@ -39466,12 +39466,12 @@ function getAugmentedEnv(overrides = {}) {
|
|
|
39466
39466
|
var EXTRA_BIN_DIRS, ENV_VARS_TO_STRIP;
|
|
39467
39467
|
var init_resolve_bin = __esm(() => {
|
|
39468
39468
|
EXTRA_BIN_DIRS = [
|
|
39469
|
-
|
|
39470
|
-
|
|
39471
|
-
|
|
39472
|
-
|
|
39473
|
-
|
|
39474
|
-
|
|
39469
|
+
join3(homedir(), ".local", "bin"),
|
|
39470
|
+
join3(homedir(), ".npm", "bin"),
|
|
39471
|
+
join3(homedir(), ".npm-global", "bin"),
|
|
39472
|
+
join3(homedir(), ".yarn", "bin"),
|
|
39473
|
+
join3(homedir(), ".bun", "bin"),
|
|
39474
|
+
join3(homedir(), "Library", "pnpm"),
|
|
39475
39475
|
"/usr/local/bin",
|
|
39476
39476
|
"/opt/homebrew/bin",
|
|
39477
39477
|
"/opt/homebrew/sbin"
|
|
@@ -39913,9 +39913,9 @@ var init_claude_runner = __esm(() => {
|
|
|
39913
39913
|
// ../sdk/src/ai/codex-runner.ts
|
|
39914
39914
|
import { spawn as spawn2 } from "node:child_process";
|
|
39915
39915
|
import { randomUUID } from "node:crypto";
|
|
39916
|
-
import { existsSync as
|
|
39916
|
+
import { existsSync as existsSync3, readFileSync as readFileSync3, unlinkSync } from "node:fs";
|
|
39917
39917
|
import { tmpdir } from "node:os";
|
|
39918
|
-
import { join as
|
|
39918
|
+
import { join as join4 } from "node:path";
|
|
39919
39919
|
|
|
39920
39920
|
class CodexRunner {
|
|
39921
39921
|
projectPath;
|
|
@@ -39980,7 +39980,7 @@ class CodexRunner {
|
|
|
39980
39980
|
});
|
|
39981
39981
|
}
|
|
39982
39982
|
async* runStream(prompt) {
|
|
39983
|
-
const outputPath =
|
|
39983
|
+
const outputPath = join4(tmpdir(), `locus-codex-${randomUUID()}.txt`);
|
|
39984
39984
|
const args = this.buildArgs(outputPath);
|
|
39985
39985
|
this.eventEmitter?.emitSessionStarted({
|
|
39986
39986
|
model: this.model,
|
|
@@ -40144,7 +40144,7 @@ class CodexRunner {
|
|
|
40144
40144
|
}
|
|
40145
40145
|
executeRun(prompt) {
|
|
40146
40146
|
return new Promise((resolve2, reject) => {
|
|
40147
|
-
const outputPath =
|
|
40147
|
+
const outputPath = join4(tmpdir(), `locus-codex-${randomUUID()}.txt`);
|
|
40148
40148
|
const args = this.buildArgs(outputPath);
|
|
40149
40149
|
const codex = spawn2("codex", args, {
|
|
40150
40150
|
cwd: this.projectPath,
|
|
@@ -40215,9 +40215,9 @@ class CodexRunner {
|
|
|
40215
40215
|
return /^Plan update\b/.test(line);
|
|
40216
40216
|
}
|
|
40217
40217
|
readOutput(outputPath, fallback) {
|
|
40218
|
-
if (
|
|
40218
|
+
if (existsSync3(outputPath)) {
|
|
40219
40219
|
try {
|
|
40220
|
-
const text =
|
|
40220
|
+
const text = readFileSync3(outputPath, "utf-8").trim();
|
|
40221
40221
|
if (text)
|
|
40222
40222
|
return text;
|
|
40223
40223
|
} catch {}
|
|
@@ -40231,7 +40231,7 @@ class CodexRunner {
|
|
|
40231
40231
|
}
|
|
40232
40232
|
cleanupTempFile(path) {
|
|
40233
40233
|
try {
|
|
40234
|
-
if (
|
|
40234
|
+
if (existsSync3(path))
|
|
40235
40235
|
unlinkSync(path);
|
|
40236
40236
|
} catch {}
|
|
40237
40237
|
}
|
|
@@ -40269,14 +40269,14 @@ var init_factory = __esm(() => {
|
|
|
40269
40269
|
|
|
40270
40270
|
// ../sdk/src/discussion/discussion-manager.ts
|
|
40271
40271
|
import {
|
|
40272
|
-
existsSync as
|
|
40272
|
+
existsSync as existsSync4,
|
|
40273
40273
|
mkdirSync,
|
|
40274
|
-
readdirSync as
|
|
40275
|
-
readFileSync as
|
|
40274
|
+
readdirSync as readdirSync3,
|
|
40275
|
+
readFileSync as readFileSync4,
|
|
40276
40276
|
unlinkSync as unlinkSync2,
|
|
40277
40277
|
writeFileSync
|
|
40278
40278
|
} from "node:fs";
|
|
40279
|
-
import { join as
|
|
40279
|
+
import { join as join5 } from "node:path";
|
|
40280
40280
|
|
|
40281
40281
|
class DiscussionManager {
|
|
40282
40282
|
discussionsDir;
|
|
@@ -40303,30 +40303,30 @@ class DiscussionManager {
|
|
|
40303
40303
|
}
|
|
40304
40304
|
save(discussion) {
|
|
40305
40305
|
this.ensureDir();
|
|
40306
|
-
const jsonPath =
|
|
40307
|
-
const mdPath =
|
|
40306
|
+
const jsonPath = join5(this.discussionsDir, `${discussion.id}.json`);
|
|
40307
|
+
const mdPath = join5(this.discussionsDir, `summary-${discussion.id}.md`);
|
|
40308
40308
|
writeFileSync(jsonPath, JSON.stringify(discussion, null, 2), "utf-8");
|
|
40309
40309
|
writeFileSync(mdPath, this.toMarkdown(discussion), "utf-8");
|
|
40310
40310
|
}
|
|
40311
40311
|
load(id) {
|
|
40312
40312
|
this.ensureDir();
|
|
40313
|
-
const filePath =
|
|
40314
|
-
if (!
|
|
40313
|
+
const filePath = join5(this.discussionsDir, `${id}.json`);
|
|
40314
|
+
if (!existsSync4(filePath)) {
|
|
40315
40315
|
return null;
|
|
40316
40316
|
}
|
|
40317
40317
|
try {
|
|
40318
|
-
return JSON.parse(
|
|
40318
|
+
return JSON.parse(readFileSync4(filePath, "utf-8"));
|
|
40319
40319
|
} catch {
|
|
40320
40320
|
return null;
|
|
40321
40321
|
}
|
|
40322
40322
|
}
|
|
40323
40323
|
list(status) {
|
|
40324
40324
|
this.ensureDir();
|
|
40325
|
-
const files =
|
|
40325
|
+
const files = readdirSync3(this.discussionsDir).filter((f) => f.endsWith(".json"));
|
|
40326
40326
|
const discussions = [];
|
|
40327
40327
|
for (const file2 of files) {
|
|
40328
40328
|
try {
|
|
40329
|
-
const discussion = JSON.parse(
|
|
40329
|
+
const discussion = JSON.parse(readFileSync4(join5(this.discussionsDir, file2), "utf-8"));
|
|
40330
40330
|
if (!status || discussion.status === status) {
|
|
40331
40331
|
discussions.push(discussion);
|
|
40332
40332
|
}
|
|
@@ -40356,12 +40356,12 @@ class DiscussionManager {
|
|
|
40356
40356
|
}
|
|
40357
40357
|
delete(id) {
|
|
40358
40358
|
this.ensureDir();
|
|
40359
|
-
const jsonPath =
|
|
40360
|
-
const mdPath =
|
|
40361
|
-
if (
|
|
40359
|
+
const jsonPath = join5(this.discussionsDir, `${id}.json`);
|
|
40360
|
+
const mdPath = join5(this.discussionsDir, `summary-${id}.md`);
|
|
40361
|
+
if (existsSync4(jsonPath)) {
|
|
40362
40362
|
unlinkSync2(jsonPath);
|
|
40363
40363
|
}
|
|
40364
|
-
if (
|
|
40364
|
+
if (existsSync4(mdPath)) {
|
|
40365
40365
|
unlinkSync2(mdPath);
|
|
40366
40366
|
}
|
|
40367
40367
|
}
|
|
@@ -40446,7 +40446,7 @@ class DiscussionManager {
|
|
|
40446
40446
|
`);
|
|
40447
40447
|
}
|
|
40448
40448
|
ensureDir() {
|
|
40449
|
-
if (!
|
|
40449
|
+
if (!existsSync4(this.discussionsDir)) {
|
|
40450
40450
|
mkdirSync(this.discussionsDir, { recursive: true });
|
|
40451
40451
|
}
|
|
40452
40452
|
}
|
|
@@ -40456,8 +40456,8 @@ var init_discussion_manager = __esm(() => {
|
|
|
40456
40456
|
});
|
|
40457
40457
|
|
|
40458
40458
|
// ../sdk/src/core/prompt-builder.ts
|
|
40459
|
-
import { existsSync as
|
|
40460
|
-
import { join as
|
|
40459
|
+
import { existsSync as existsSync5, readFileSync as readFileSync5 } from "node:fs";
|
|
40460
|
+
import { join as join6 } from "node:path";
|
|
40461
40461
|
|
|
40462
40462
|
class PromptBuilder {
|
|
40463
40463
|
projectPath;
|
|
@@ -40616,9 +40616,9 @@ ${sections}
|
|
|
40616
40616
|
}
|
|
40617
40617
|
getProjectContext() {
|
|
40618
40618
|
const contextPath = getLocusPath(this.projectPath, "contextFile");
|
|
40619
|
-
if (
|
|
40619
|
+
if (existsSync5(contextPath)) {
|
|
40620
40620
|
try {
|
|
40621
|
-
const context2 =
|
|
40621
|
+
const context2 = readFileSync5(contextPath, "utf-8");
|
|
40622
40622
|
if (context2.trim().length > 20) {
|
|
40623
40623
|
return context2;
|
|
40624
40624
|
}
|
|
@@ -40629,10 +40629,10 @@ ${sections}
|
|
|
40629
40629
|
return this.getFallbackContext() || null;
|
|
40630
40630
|
}
|
|
40631
40631
|
getFallbackContext() {
|
|
40632
|
-
const readmePath =
|
|
40633
|
-
if (
|
|
40632
|
+
const readmePath = join6(this.projectPath, "README.md");
|
|
40633
|
+
if (existsSync5(readmePath)) {
|
|
40634
40634
|
try {
|
|
40635
|
-
const content =
|
|
40635
|
+
const content = readFileSync5(readmePath, "utf-8");
|
|
40636
40636
|
const limit = 1000;
|
|
40637
40637
|
return content.slice(0, limit) + (content.length > limit ? `
|
|
40638
40638
|
...(truncated)...` : "");
|
|
@@ -40651,11 +40651,11 @@ If you need more information about the project strategies, plans, or architectur
|
|
|
40651
40651
|
}
|
|
40652
40652
|
getLearningsContent() {
|
|
40653
40653
|
const learningsPath = getLocusPath(this.projectPath, "learningsFile");
|
|
40654
|
-
if (!
|
|
40654
|
+
if (!existsSync5(learningsPath)) {
|
|
40655
40655
|
return null;
|
|
40656
40656
|
}
|
|
40657
40657
|
try {
|
|
40658
|
-
const content =
|
|
40658
|
+
const content = readFileSync5(learningsPath, "utf-8");
|
|
40659
40659
|
const lines = content.split(`
|
|
40660
40660
|
`).filter((l) => l.startsWith("- "));
|
|
40661
40661
|
if (lines.length === 0) {
|
|
@@ -41099,7 +41099,7 @@ var init_worker = __esm(() => {
|
|
|
41099
41099
|
var import_config16 = __toESM(require_config(), 1);
|
|
41100
41100
|
|
|
41101
41101
|
// src/bot.ts
|
|
41102
|
-
var
|
|
41102
|
+
var import_telegraf5 = __toESM(require_lib3(), 1);
|
|
41103
41103
|
|
|
41104
41104
|
// src/callbacks.ts
|
|
41105
41105
|
init_src();
|
|
@@ -41220,6 +41220,173 @@ async function requireApiKey(ctx, config2, command) {
|
|
|
41220
41220
|
await ctx.reply(formatError2(`API key is required for /${command}. Run: locus config setup --api-key <KEY>`), { parse_mode: "HTML" });
|
|
41221
41221
|
return false;
|
|
41222
41222
|
}
|
|
41223
|
+
|
|
41224
|
+
// src/commands/artifacts.ts
|
|
41225
|
+
var import_telegraf = __toESM(require_lib3(), 1);
|
|
41226
|
+
import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
|
|
41227
|
+
import { join } from "node:path";
|
|
41228
|
+
var ARTIFACTS_DIR = ".locus/artifacts";
|
|
41229
|
+
function listArtifacts(projectPath) {
|
|
41230
|
+
const artifactsDir = join(projectPath, ARTIFACTS_DIR);
|
|
41231
|
+
if (!existsSync(artifactsDir)) {
|
|
41232
|
+
return [];
|
|
41233
|
+
}
|
|
41234
|
+
const files = readdirSync(artifactsDir).filter((f) => f.endsWith(".md"));
|
|
41235
|
+
return files.map((fileName) => {
|
|
41236
|
+
const filePath = join(artifactsDir, fileName);
|
|
41237
|
+
const stat = statSync(filePath);
|
|
41238
|
+
const name = fileName.replace(/\.md$/, "");
|
|
41239
|
+
return {
|
|
41240
|
+
name,
|
|
41241
|
+
fileName,
|
|
41242
|
+
createdAt: stat.birthtime,
|
|
41243
|
+
size: stat.size
|
|
41244
|
+
};
|
|
41245
|
+
}).sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime());
|
|
41246
|
+
}
|
|
41247
|
+
function formatSize(bytes) {
|
|
41248
|
+
if (bytes < 1024)
|
|
41249
|
+
return `${bytes}B`;
|
|
41250
|
+
const kb = bytes / 1024;
|
|
41251
|
+
if (kb < 1024)
|
|
41252
|
+
return `${kb.toFixed(1)}KB`;
|
|
41253
|
+
const mb = kb / 1024;
|
|
41254
|
+
return `${mb.toFixed(1)}MB`;
|
|
41255
|
+
}
|
|
41256
|
+
async function artifactsCommand(ctx, config2) {
|
|
41257
|
+
const text = (ctx.message && "text" in ctx.message ? ctx.message.text : "") || "";
|
|
41258
|
+
const args = text.replace(/^\/artifacts\s*/, "").trim();
|
|
41259
|
+
console.log(`[artifacts] Received: ${args || "(list)"}`);
|
|
41260
|
+
if (args) {
|
|
41261
|
+
await showArtifact(ctx, config2, args);
|
|
41262
|
+
return;
|
|
41263
|
+
}
|
|
41264
|
+
const artifacts = listArtifacts(config2.projectPath);
|
|
41265
|
+
if (artifacts.length === 0) {
|
|
41266
|
+
await ctx.reply(formatInfo("No artifacts found."), {
|
|
41267
|
+
parse_mode: "HTML"
|
|
41268
|
+
});
|
|
41269
|
+
return;
|
|
41270
|
+
}
|
|
41271
|
+
let msg = `<b>\uD83D\uDCC4 Artifacts</b> (${artifacts.length} total)
|
|
41272
|
+
|
|
41273
|
+
`;
|
|
41274
|
+
for (let i = 0;i < artifacts.length; i++) {
|
|
41275
|
+
const artifact = artifacts[i];
|
|
41276
|
+
const age = formatRelativeTime(artifact.createdAt);
|
|
41277
|
+
const size = formatSize(artifact.size);
|
|
41278
|
+
const index = String(i + 1).padStart(2, " ");
|
|
41279
|
+
msg += `${index}. <b>${escapeHtml(artifact.name)}</b>
|
|
41280
|
+
`;
|
|
41281
|
+
msg += ` <i>${age} • ${size}</i>
|
|
41282
|
+
`;
|
|
41283
|
+
}
|
|
41284
|
+
msg += `
|
|
41285
|
+
Tap an artifact below to view or convert to plan.`;
|
|
41286
|
+
const buttons = artifacts.slice(0, 10).map((artifact) => [
|
|
41287
|
+
import_telegraf.Markup.button.callback(`\uD83D\uDC41 ${artifact.name.slice(0, 30)}`, `view:artifact:${artifact.name.slice(0, 40)}`),
|
|
41288
|
+
import_telegraf.Markup.button.callback("\uD83D\uDCCB Plan", `plan:artifact:${artifact.name.slice(0, 40)}`)
|
|
41289
|
+
]);
|
|
41290
|
+
const parts = splitMessage(msg);
|
|
41291
|
+
for (let i = 0;i < parts.length; i++) {
|
|
41292
|
+
const isLast = i === parts.length - 1;
|
|
41293
|
+
await ctx.reply(parts[i], {
|
|
41294
|
+
parse_mode: "HTML",
|
|
41295
|
+
...isLast ? import_telegraf.Markup.inlineKeyboard(buttons) : {}
|
|
41296
|
+
});
|
|
41297
|
+
}
|
|
41298
|
+
}
|
|
41299
|
+
async function showArtifact(ctx, config2, name) {
|
|
41300
|
+
const artifactsDir = join(config2.projectPath, ARTIFACTS_DIR);
|
|
41301
|
+
const fileName = name.endsWith(".md") ? name : `${name}.md`;
|
|
41302
|
+
const filePath = join(artifactsDir, fileName);
|
|
41303
|
+
let content;
|
|
41304
|
+
let artifactName = name.replace(/\.md$/, "");
|
|
41305
|
+
if (existsSync(filePath)) {
|
|
41306
|
+
content = readFileSync(filePath, "utf-8");
|
|
41307
|
+
} else {
|
|
41308
|
+
const artifacts = listArtifacts(config2.projectPath);
|
|
41309
|
+
const matches = artifacts.filter((a) => a.name.toLowerCase().includes(name.toLowerCase()));
|
|
41310
|
+
if (matches.length === 1) {
|
|
41311
|
+
const matchPath = join(artifactsDir, matches[0].fileName);
|
|
41312
|
+
content = readFileSync(matchPath, "utf-8");
|
|
41313
|
+
artifactName = matches[0].name;
|
|
41314
|
+
} else if (matches.length > 1) {
|
|
41315
|
+
let msg2 = `<b>Multiple artifacts match "${escapeHtml(name)}":</b>
|
|
41316
|
+
|
|
41317
|
+
`;
|
|
41318
|
+
for (const m of matches) {
|
|
41319
|
+
msg2 += `• <b>${escapeHtml(m.name)}</b>
|
|
41320
|
+
`;
|
|
41321
|
+
}
|
|
41322
|
+
await ctx.reply(msg2, { parse_mode: "HTML" });
|
|
41323
|
+
return;
|
|
41324
|
+
} else {
|
|
41325
|
+
await ctx.reply(formatError2(`Artifact "${escapeHtml(name)}" not found.`), { parse_mode: "HTML" });
|
|
41326
|
+
return;
|
|
41327
|
+
}
|
|
41328
|
+
}
|
|
41329
|
+
const truncated = truncateOutput(escapeHtml(content), 3500);
|
|
41330
|
+
let msg = `<b>\uD83D\uDCC4 ${escapeHtml(artifactName)}</b>
|
|
41331
|
+
|
|
41332
|
+
`;
|
|
41333
|
+
msg += `<pre>${truncated}</pre>`;
|
|
41334
|
+
const buttons = import_telegraf.Markup.inlineKeyboard([
|
|
41335
|
+
[
|
|
41336
|
+
import_telegraf.Markup.button.callback("\uD83D\uDCCB Convert to Plan", `plan:artifact:${artifactName.slice(0, 40)}`)
|
|
41337
|
+
]
|
|
41338
|
+
]);
|
|
41339
|
+
const parts = splitMessage(msg);
|
|
41340
|
+
for (let i = 0;i < parts.length; i++) {
|
|
41341
|
+
const isLast = i === parts.length - 1;
|
|
41342
|
+
await ctx.reply(parts[i], {
|
|
41343
|
+
parse_mode: "HTML",
|
|
41344
|
+
...isLast ? buttons : {}
|
|
41345
|
+
});
|
|
41346
|
+
}
|
|
41347
|
+
}
|
|
41348
|
+
async function convertArtifactToPlan(ctx, config2, executor, name) {
|
|
41349
|
+
const artifactName = name.replace(/\.md$/, "");
|
|
41350
|
+
const artifactsDir = join(config2.projectPath, ARTIFACTS_DIR);
|
|
41351
|
+
const filePath = join(artifactsDir, `${artifactName}.md`);
|
|
41352
|
+
if (!existsSync(filePath)) {
|
|
41353
|
+
const artifacts = listArtifacts(config2.projectPath);
|
|
41354
|
+
const match = artifacts.find((a) => a.name.toLowerCase().includes(name.toLowerCase()));
|
|
41355
|
+
if (!match) {
|
|
41356
|
+
await ctx.reply(formatError2(`Artifact "${escapeHtml(name)}" not found.`), { parse_mode: "HTML" });
|
|
41357
|
+
return;
|
|
41358
|
+
}
|
|
41359
|
+
}
|
|
41360
|
+
await ctx.reply(formatInfo(`Converting <b>${escapeHtml(artifactName)}</b> to a plan...`), { parse_mode: "HTML" });
|
|
41361
|
+
const directive = `Prepare a plan according to the artifact: ${artifactName}`;
|
|
41362
|
+
const args = executor.buildArgs(["plan", directive, "--no-stream"]);
|
|
41363
|
+
executor.execute(args).then(async (result) => {
|
|
41364
|
+
const output = (result.stdout + result.stderr).trim();
|
|
41365
|
+
try {
|
|
41366
|
+
if (!output && result.exitCode !== 0) {
|
|
41367
|
+
await ctx.reply(formatError2("Plan conversion failed with no output."), { parse_mode: "HTML" });
|
|
41368
|
+
return;
|
|
41369
|
+
}
|
|
41370
|
+
const cleanOutput = truncateOutput(escapeHtml(output), 3500);
|
|
41371
|
+
let msg = `<b>\uD83D\uDCCB Plan from: ${escapeHtml(artifactName)}</b>
|
|
41372
|
+
|
|
41373
|
+
`;
|
|
41374
|
+
msg += `<pre>${cleanOutput}</pre>`;
|
|
41375
|
+
const parts = splitMessage(msg);
|
|
41376
|
+
for (const part of parts) {
|
|
41377
|
+
await ctx.reply(part, { parse_mode: "HTML" });
|
|
41378
|
+
}
|
|
41379
|
+
} catch (err) {
|
|
41380
|
+
console.error("[artifacts:plan] Failed to send result:", err);
|
|
41381
|
+
}
|
|
41382
|
+
}, async (err) => {
|
|
41383
|
+
try {
|
|
41384
|
+
await ctx.reply(formatError2(`Plan conversion failed: ${err instanceof Error ? err.message : String(err)}`), { parse_mode: "HTML" });
|
|
41385
|
+
} catch {
|
|
41386
|
+
console.error("[artifacts:plan] Failed to send error:", err);
|
|
41387
|
+
}
|
|
41388
|
+
});
|
|
41389
|
+
}
|
|
41223
41390
|
// ../sdk/src/agent/document-fetcher.ts
|
|
41224
41391
|
init_config();
|
|
41225
41392
|
|
|
@@ -41832,7 +41999,7 @@ ${insightsText.trimEnd()}
|
|
|
41832
41999
|
}
|
|
41833
42000
|
// ../sdk/src/discussion/discussion-facilitator.ts
|
|
41834
42001
|
init_config();
|
|
41835
|
-
import { existsSync as
|
|
42002
|
+
import { existsSync as existsSync6, readFileSync as readFileSync6 } from "node:fs";
|
|
41836
42003
|
class DiscussionFacilitator {
|
|
41837
42004
|
projectPath;
|
|
41838
42005
|
aiRunner;
|
|
@@ -41979,9 +42146,9 @@ class DiscussionFacilitator {
|
|
|
41979
42146
|
}
|
|
41980
42147
|
getProjectContext() {
|
|
41981
42148
|
const contextPath = getLocusPath(this.projectPath, "contextFile");
|
|
41982
|
-
if (
|
|
42149
|
+
if (existsSync6(contextPath)) {
|
|
41983
42150
|
try {
|
|
41984
|
-
const context2 =
|
|
42151
|
+
const context2 = readFileSync6(contextPath, "utf-8");
|
|
41985
42152
|
if (context2.trim().length > 20) {
|
|
41986
42153
|
return context2;
|
|
41987
42154
|
}
|
|
@@ -41993,11 +42160,11 @@ class DiscussionFacilitator {
|
|
|
41993
42160
|
}
|
|
41994
42161
|
getLearningsContent() {
|
|
41995
42162
|
const learningsPath = getLocusPath(this.projectPath, "learningsFile");
|
|
41996
|
-
if (!
|
|
42163
|
+
if (!existsSync6(learningsPath)) {
|
|
41997
42164
|
return null;
|
|
41998
42165
|
}
|
|
41999
42166
|
try {
|
|
42000
|
-
const content =
|
|
42167
|
+
const content = readFileSync6(learningsPath, "utf-8");
|
|
42001
42168
|
const lines = content.split(`
|
|
42002
42169
|
`).filter((l) => l.startsWith("- "));
|
|
42003
42170
|
if (lines.length === 0) {
|
|
@@ -42417,14 +42584,14 @@ class ExecEventEmitter {
|
|
|
42417
42584
|
// ../sdk/src/exec/history-manager.ts
|
|
42418
42585
|
init_config();
|
|
42419
42586
|
import {
|
|
42420
|
-
existsSync as
|
|
42587
|
+
existsSync as existsSync7,
|
|
42421
42588
|
mkdirSync as mkdirSync2,
|
|
42422
|
-
readdirSync as
|
|
42423
|
-
readFileSync as
|
|
42589
|
+
readdirSync as readdirSync4,
|
|
42590
|
+
readFileSync as readFileSync7,
|
|
42424
42591
|
rmSync,
|
|
42425
42592
|
writeFileSync as writeFileSync2
|
|
42426
42593
|
} from "node:fs";
|
|
42427
|
-
import { join as
|
|
42594
|
+
import { join as join7 } from "node:path";
|
|
42428
42595
|
var DEFAULT_MAX_SESSIONS = 30;
|
|
42429
42596
|
function generateSessionId2() {
|
|
42430
42597
|
const timestamp = Date.now().toString(36);
|
|
@@ -42436,17 +42603,17 @@ class HistoryManager {
|
|
|
42436
42603
|
historyDir;
|
|
42437
42604
|
maxSessions;
|
|
42438
42605
|
constructor(projectPath, options) {
|
|
42439
|
-
this.historyDir = options?.historyDir ??
|
|
42606
|
+
this.historyDir = options?.historyDir ?? join7(projectPath, LOCUS_CONFIG.dir, LOCUS_CONFIG.sessionsDir);
|
|
42440
42607
|
this.maxSessions = options?.maxSessions ?? DEFAULT_MAX_SESSIONS;
|
|
42441
42608
|
this.ensureHistoryDir();
|
|
42442
42609
|
}
|
|
42443
42610
|
ensureHistoryDir() {
|
|
42444
|
-
if (!
|
|
42611
|
+
if (!existsSync7(this.historyDir)) {
|
|
42445
42612
|
mkdirSync2(this.historyDir, { recursive: true });
|
|
42446
42613
|
}
|
|
42447
42614
|
}
|
|
42448
42615
|
getSessionPath(sessionId) {
|
|
42449
|
-
return
|
|
42616
|
+
return join7(this.historyDir, `${sessionId}.json`);
|
|
42450
42617
|
}
|
|
42451
42618
|
saveSession(session2) {
|
|
42452
42619
|
const filePath = this.getSessionPath(session2.id);
|
|
@@ -42455,11 +42622,11 @@ class HistoryManager {
|
|
|
42455
42622
|
}
|
|
42456
42623
|
loadSession(sessionId) {
|
|
42457
42624
|
const filePath = this.getSessionPath(sessionId);
|
|
42458
|
-
if (!
|
|
42625
|
+
if (!existsSync7(filePath)) {
|
|
42459
42626
|
return null;
|
|
42460
42627
|
}
|
|
42461
42628
|
try {
|
|
42462
|
-
const content =
|
|
42629
|
+
const content = readFileSync7(filePath, "utf-8");
|
|
42463
42630
|
return JSON.parse(content);
|
|
42464
42631
|
} catch {
|
|
42465
42632
|
return null;
|
|
@@ -42467,7 +42634,7 @@ class HistoryManager {
|
|
|
42467
42634
|
}
|
|
42468
42635
|
deleteSession(sessionId) {
|
|
42469
42636
|
const filePath = this.getSessionPath(sessionId);
|
|
42470
|
-
if (!
|
|
42637
|
+
if (!existsSync7(filePath)) {
|
|
42471
42638
|
return false;
|
|
42472
42639
|
}
|
|
42473
42640
|
try {
|
|
@@ -42478,7 +42645,7 @@ class HistoryManager {
|
|
|
42478
42645
|
}
|
|
42479
42646
|
}
|
|
42480
42647
|
listSessions(options) {
|
|
42481
|
-
const files =
|
|
42648
|
+
const files = readdirSync4(this.historyDir);
|
|
42482
42649
|
let sessions = [];
|
|
42483
42650
|
for (const file2 of files) {
|
|
42484
42651
|
if (file2.endsWith(".json")) {
|
|
@@ -42551,11 +42718,11 @@ class HistoryManager {
|
|
|
42551
42718
|
return deleted;
|
|
42552
42719
|
}
|
|
42553
42720
|
getSessionCount() {
|
|
42554
|
-
const files =
|
|
42721
|
+
const files = readdirSync4(this.historyDir);
|
|
42555
42722
|
return files.filter((f) => f.endsWith(".json")).length;
|
|
42556
42723
|
}
|
|
42557
42724
|
sessionExists(sessionId) {
|
|
42558
|
-
return
|
|
42725
|
+
return existsSync7(this.getSessionPath(sessionId));
|
|
42559
42726
|
}
|
|
42560
42727
|
findSessionByPartialId(partialId) {
|
|
42561
42728
|
const sessions = this.listSessions();
|
|
@@ -42569,12 +42736,12 @@ class HistoryManager {
|
|
|
42569
42736
|
return this.historyDir;
|
|
42570
42737
|
}
|
|
42571
42738
|
clearAllSessions() {
|
|
42572
|
-
const files =
|
|
42739
|
+
const files = readdirSync4(this.historyDir);
|
|
42573
42740
|
let deleted = 0;
|
|
42574
42741
|
for (const file2 of files) {
|
|
42575
42742
|
if (file2.endsWith(".json")) {
|
|
42576
42743
|
try {
|
|
42577
|
-
rmSync(
|
|
42744
|
+
rmSync(join7(this.historyDir, file2));
|
|
42578
42745
|
deleted++;
|
|
42579
42746
|
} catch {}
|
|
42580
42747
|
}
|
|
@@ -42851,8 +43018,8 @@ init_src2();
|
|
|
42851
43018
|
init_colors();
|
|
42852
43019
|
init_resolve_bin();
|
|
42853
43020
|
import { spawn as spawn3 } from "node:child_process";
|
|
42854
|
-
import { existsSync as
|
|
42855
|
-
import { dirname, join as
|
|
43021
|
+
import { existsSync as existsSync8 } from "node:fs";
|
|
43022
|
+
import { dirname, join as join8 } from "node:path";
|
|
42856
43023
|
import { fileURLToPath } from "node:url";
|
|
42857
43024
|
import { EventEmitter as EventEmitter4 } from "events";
|
|
42858
43025
|
|
|
@@ -43121,12 +43288,12 @@ ${agentId} finished (exit code: ${code})`);
|
|
|
43121
43288
|
const currentModulePath = fileURLToPath(import.meta.url);
|
|
43122
43289
|
const currentModuleDir = dirname(currentModulePath);
|
|
43123
43290
|
const potentialPaths = [
|
|
43124
|
-
|
|
43125
|
-
|
|
43126
|
-
|
|
43127
|
-
|
|
43291
|
+
join8(currentModuleDir, "..", "agent", "worker.js"),
|
|
43292
|
+
join8(currentModuleDir, "agent", "worker.js"),
|
|
43293
|
+
join8(currentModuleDir, "worker.js"),
|
|
43294
|
+
join8(currentModuleDir, "..", "agent", "worker.ts")
|
|
43128
43295
|
];
|
|
43129
|
-
return potentialPaths.find((p) =>
|
|
43296
|
+
return potentialPaths.find((p) => existsSync8(p));
|
|
43130
43297
|
}
|
|
43131
43298
|
}
|
|
43132
43299
|
function killProcessTree(proc) {
|
|
@@ -43176,7 +43343,7 @@ init_config();
|
|
|
43176
43343
|
init_colors();
|
|
43177
43344
|
|
|
43178
43345
|
// src/commands/discuss.ts
|
|
43179
|
-
var
|
|
43346
|
+
var import_telegraf2 = __toESM(require_lib3(), 1);
|
|
43180
43347
|
var activeDiscussions = new Map;
|
|
43181
43348
|
var facilitatorCache = new Map;
|
|
43182
43349
|
function getFacilitator(config2) {
|
|
@@ -43241,8 +43408,8 @@ ${escapeHtml(result.message)}`;
|
|
|
43241
43408
|
await ctx.reply(parts[i], {
|
|
43242
43409
|
parse_mode: "HTML",
|
|
43243
43410
|
...isLast ? {
|
|
43244
|
-
reply_markup:
|
|
43245
|
-
|
|
43411
|
+
reply_markup: import_telegraf2.Markup.inlineKeyboard([
|
|
43412
|
+
import_telegraf2.Markup.button.callback("End Discussion", `end:discuss:${result.discussion.id}`)
|
|
43246
43413
|
]).reply_markup
|
|
43247
43414
|
} : {}
|
|
43248
43415
|
});
|
|
@@ -43269,8 +43436,8 @@ async function continueDiscussionHandler(ctx, config2, message) {
|
|
|
43269
43436
|
await ctx.reply(responseParts[i], {
|
|
43270
43437
|
parse_mode: "HTML",
|
|
43271
43438
|
...isLast ? {
|
|
43272
|
-
reply_markup:
|
|
43273
|
-
|
|
43439
|
+
reply_markup: import_telegraf2.Markup.inlineKeyboard([
|
|
43440
|
+
import_telegraf2.Markup.button.callback("End Discussion", `end:discuss:${discussionId}`)
|
|
43274
43441
|
]).reply_markup
|
|
43275
43442
|
} : {}
|
|
43276
43443
|
});
|
|
@@ -43335,9 +43502,9 @@ async function discussionsCommand(ctx, config2) {
|
|
|
43335
43502
|
for (let i = 0;i < parts.length; i++) {
|
|
43336
43503
|
const isLast = i === parts.length - 1;
|
|
43337
43504
|
const buttons = discussions.slice(0, 5).flatMap((d) => [
|
|
43338
|
-
|
|
43505
|
+
import_telegraf2.Markup.button.callback(`View ${d.id.slice(-6)}`, `view:discuss:${d.id}`),
|
|
43339
43506
|
...d.status !== "archived" ? [
|
|
43340
|
-
|
|
43507
|
+
import_telegraf2.Markup.button.callback(`Archive ${d.id.slice(-6)}`, `archive:discuss:${d.id}`)
|
|
43341
43508
|
] : []
|
|
43342
43509
|
]);
|
|
43343
43510
|
const rows = [];
|
|
@@ -43346,7 +43513,7 @@ async function discussionsCommand(ctx, config2) {
|
|
|
43346
43513
|
}
|
|
43347
43514
|
await ctx.reply(parts[i], {
|
|
43348
43515
|
parse_mode: "HTML",
|
|
43349
|
-
...isLast && rows.length > 0 ? { reply_markup:
|
|
43516
|
+
...isLast && rows.length > 0 ? { reply_markup: import_telegraf2.Markup.inlineKeyboard(rows).reply_markup } : {}
|
|
43350
43517
|
});
|
|
43351
43518
|
}
|
|
43352
43519
|
} catch (err) {
|
|
@@ -43380,8 +43547,8 @@ ${escapeHtml(summary)}`;
|
|
|
43380
43547
|
await ctx.reply(parts[i], {
|
|
43381
43548
|
parse_mode: "HTML",
|
|
43382
43549
|
...isLast ? {
|
|
43383
|
-
reply_markup:
|
|
43384
|
-
|
|
43550
|
+
reply_markup: import_telegraf2.Markup.inlineKeyboard([
|
|
43551
|
+
import_telegraf2.Markup.button.callback("View Full Discussion", `view:discuss:${discussionId}`)
|
|
43385
43552
|
]).reply_markup
|
|
43386
43553
|
} : {}
|
|
43387
43554
|
});
|
|
@@ -43411,8 +43578,8 @@ ${escapeHtml(summary)}`;
|
|
|
43411
43578
|
await ctx.reply(parts[i], {
|
|
43412
43579
|
parse_mode: "HTML",
|
|
43413
43580
|
...isLast ? {
|
|
43414
|
-
reply_markup:
|
|
43415
|
-
|
|
43581
|
+
reply_markup: import_telegraf2.Markup.inlineKeyboard([
|
|
43582
|
+
import_telegraf2.Markup.button.callback("View Full Discussion", `view:discuss:${discussionId}`)
|
|
43416
43583
|
]).reply_markup
|
|
43417
43584
|
} : {}
|
|
43418
43585
|
});
|
|
@@ -43625,6 +43792,16 @@ ${escapeHtml(desc)}
|
|
|
43625
43792
|
const discussionId = ctx.match[1];
|
|
43626
43793
|
await archiveDiscussion(ctx, config2, discussionId);
|
|
43627
43794
|
});
|
|
43795
|
+
bot.action(/^view:artifact:(.+)$/, async (ctx) => {
|
|
43796
|
+
await ctx.answerCbQuery();
|
|
43797
|
+
const artifactName = ctx.match[1];
|
|
43798
|
+
await showArtifact(ctx, config2, artifactName);
|
|
43799
|
+
});
|
|
43800
|
+
bot.action(/^plan:artifact:(.+)$/, async (ctx) => {
|
|
43801
|
+
await ctx.answerCbQuery("Converting to plan…");
|
|
43802
|
+
const artifactName = ctx.match[1];
|
|
43803
|
+
await convertArtifactToPlan(ctx, config2, executor, artifactName);
|
|
43804
|
+
});
|
|
43628
43805
|
}
|
|
43629
43806
|
|
|
43630
43807
|
// src/commands/activity.ts
|
|
@@ -43784,8 +43961,8 @@ Total: ${agents.length} active agent(s)`;
|
|
|
43784
43961
|
}
|
|
43785
43962
|
}
|
|
43786
43963
|
// src/commands/config.ts
|
|
43787
|
-
import { existsSync as
|
|
43788
|
-
import { join as
|
|
43964
|
+
import { existsSync as existsSync9, readFileSync as readFileSync8, writeFileSync as writeFileSync3 } from "node:fs";
|
|
43965
|
+
import { join as join9 } from "node:path";
|
|
43789
43966
|
|
|
43790
43967
|
// src/command-whitelist.ts
|
|
43791
43968
|
var SAFE_BRANCH = /^[a-zA-Z0-9_\-./]+$/;
|
|
@@ -44153,13 +44330,13 @@ var USAGE = `<b>Usage:</b>
|
|
|
44153
44330
|
${Object.entries(ALLOWED_KEYS).map(([k, v]) => ` \`${k}\` — ${v.description}`).join(`
|
|
44154
44331
|
`)}`;
|
|
44155
44332
|
function getSettingsPath(config2) {
|
|
44156
|
-
return
|
|
44333
|
+
return join9(config2.projectPath, CONFIG_DIR, SETTINGS_FILE);
|
|
44157
44334
|
}
|
|
44158
44335
|
function loadSettings(config2) {
|
|
44159
44336
|
const path = getSettingsPath(config2);
|
|
44160
|
-
if (!
|
|
44337
|
+
if (!existsSync9(path))
|
|
44161
44338
|
return null;
|
|
44162
|
-
const raw =
|
|
44339
|
+
const raw = readFileSync8(path, "utf-8");
|
|
44163
44340
|
return JSON.parse(raw);
|
|
44164
44341
|
}
|
|
44165
44342
|
function saveSettings(config2, settings) {
|
|
@@ -44483,13 +44660,13 @@ import { spawn as spawn4 } from "node:child_process";
|
|
|
44483
44660
|
|
|
44484
44661
|
// src/env.ts
|
|
44485
44662
|
import { homedir as homedir2 } from "node:os";
|
|
44486
|
-
import { join as
|
|
44663
|
+
import { join as join10 } from "node:path";
|
|
44487
44664
|
function extraPathDirs() {
|
|
44488
44665
|
const home = homedir2();
|
|
44489
44666
|
return [
|
|
44490
|
-
|
|
44491
|
-
|
|
44492
|
-
|
|
44667
|
+
join10(home, ".bun", "bin"),
|
|
44668
|
+
join10(home, ".nvm", "current", "bin"),
|
|
44669
|
+
join10(home, ".local", "bin"),
|
|
44493
44670
|
"/usr/local/bin"
|
|
44494
44671
|
];
|
|
44495
44672
|
}
|
|
@@ -44727,6 +44904,8 @@ var HELP_TEXT = `<b>Locus Bot — Command Center</b>
|
|
|
44727
44904
|
|
|
44728
44905
|
\uD83D\uDD27 <b>Dev & Git:</b>
|
|
44729
44906
|
/exec <prompt> — One-shot AI execution
|
|
44907
|
+
/artifacts — List knowledge artifacts
|
|
44908
|
+
/artifacts <name> — View an artifact
|
|
44730
44909
|
/review [pr-number] — AI review of PR or changes
|
|
44731
44910
|
/git <command> — Run whitelisted git/gh commands
|
|
44732
44911
|
/dev <command> — Run lint, typecheck, build, test
|
|
@@ -44754,7 +44933,7 @@ async function helpCommand(ctx) {
|
|
|
44754
44933
|
await ctx.reply(HELP_TEXT, { parse_mode: "HTML" });
|
|
44755
44934
|
}
|
|
44756
44935
|
// src/commands/plan.ts
|
|
44757
|
-
var
|
|
44936
|
+
var import_telegraf3 = __toESM(require_lib3(), 1);
|
|
44758
44937
|
async function planCommand(ctx, executor) {
|
|
44759
44938
|
const text = (ctx.message && "text" in ctx.message ? ctx.message.text : "") || "";
|
|
44760
44939
|
const directive = text.replace(/^\/plan\s*/, "").trim();
|
|
@@ -44792,12 +44971,12 @@ async function plansCommand(ctx, executor) {
|
|
|
44792
44971
|
const planIds = extractPendingPlanIds(cleanOutput);
|
|
44793
44972
|
if (planIds.length > 0) {
|
|
44794
44973
|
const buttons = planIds.slice(0, 5).map((planId) => [
|
|
44795
|
-
|
|
44796
|
-
|
|
44974
|
+
import_telegraf3.Markup.button.callback("✅ Approve", `approve:plan:${planId}`),
|
|
44975
|
+
import_telegraf3.Markup.button.callback("❌ Cancel", `cancel:plan:${planId}`)
|
|
44797
44976
|
]);
|
|
44798
44977
|
await ctx.reply(formattedOutput, {
|
|
44799
44978
|
parse_mode: "HTML",
|
|
44800
|
-
...
|
|
44979
|
+
...import_telegraf3.Markup.inlineKeyboard(buttons)
|
|
44801
44980
|
});
|
|
44802
44981
|
} else {
|
|
44803
44982
|
await ctx.reply(formattedOutput, { parse_mode: "HTML" });
|
|
@@ -45135,7 +45314,7 @@ async function statusCommand(ctx, executor) {
|
|
|
45135
45314
|
}
|
|
45136
45315
|
// src/commands/tasks.ts
|
|
45137
45316
|
init_src();
|
|
45138
|
-
var
|
|
45317
|
+
var import_telegraf4 = __toESM(require_lib3(), 1);
|
|
45139
45318
|
var VALID_STATUSES = Object.values(TaskStatus);
|
|
45140
45319
|
var priorityIcon = {
|
|
45141
45320
|
CRITICAL: "\uD83D\uDD34",
|
|
@@ -45191,13 +45370,13 @@ async function tasksCommand(ctx, config2) {
|
|
|
45191
45370
|
const inReviewTasks = tasks2.filter((t) => t.status === "IN_REVIEW" /* IN_REVIEW */);
|
|
45192
45371
|
if (inReviewTasks.length > 0) {
|
|
45193
45372
|
const buttons = inReviewTasks.slice(0, 5).map((t) => [
|
|
45194
|
-
|
|
45195
|
-
|
|
45373
|
+
import_telegraf4.Markup.button.callback("✅ Approve", `approve:task:${t.id}`),
|
|
45374
|
+
import_telegraf4.Markup.button.callback("\uD83D\uDC41 View", `view:task:${t.id}`)
|
|
45196
45375
|
]);
|
|
45197
45376
|
await ctx.reply(msg.trim(), {
|
|
45198
45377
|
parse_mode: "HTML",
|
|
45199
45378
|
link_preview_options: { is_disabled: true },
|
|
45200
|
-
...
|
|
45379
|
+
...import_telegraf4.Markup.inlineKeyboard(buttons)
|
|
45201
45380
|
});
|
|
45202
45381
|
} else {
|
|
45203
45382
|
await ctx.reply(msg.trim(), {
|
|
@@ -45410,7 +45589,7 @@ async function approveTaskCommand(ctx, config2) {
|
|
|
45410
45589
|
// src/commands/upgrade.ts
|
|
45411
45590
|
import { spawn as spawn5 } from "node:child_process";
|
|
45412
45591
|
import { homedir as homedir3 } from "node:os";
|
|
45413
|
-
import { join as
|
|
45592
|
+
import { join as join11 } from "node:path";
|
|
45414
45593
|
function runCommand2(cmd, args, timeout) {
|
|
45415
45594
|
return new Promise((resolve2) => {
|
|
45416
45595
|
const proc = spawn5(cmd, args, {
|
|
@@ -45455,7 +45634,7 @@ function getRestartInfo() {
|
|
|
45455
45634
|
};
|
|
45456
45635
|
}
|
|
45457
45636
|
if (platform === "darwin") {
|
|
45458
|
-
const plistPath =
|
|
45637
|
+
const plistPath = join11(homedir3(), "Library/LaunchAgents/com.locus.telegram.plist");
|
|
45459
45638
|
return {
|
|
45460
45639
|
label: "launchctl (macOS)",
|
|
45461
45640
|
commands: [
|
|
@@ -45571,7 +45750,7 @@ async function workspaceCommand(ctx, config2) {
|
|
|
45571
45750
|
}
|
|
45572
45751
|
// src/executor.ts
|
|
45573
45752
|
import { spawn as spawn6 } from "node:child_process";
|
|
45574
|
-
import { join as
|
|
45753
|
+
import { join as join12 } from "node:path";
|
|
45575
45754
|
function timestamp2() {
|
|
45576
45755
|
return new Date().toLocaleTimeString("en-GB", { hour12: false });
|
|
45577
45756
|
}
|
|
@@ -45587,7 +45766,7 @@ class CliExecutor {
|
|
|
45587
45766
|
}
|
|
45588
45767
|
resolveCommand(args) {
|
|
45589
45768
|
if (this.config.testMode) {
|
|
45590
|
-
const cliPath =
|
|
45769
|
+
const cliPath = join12(this.config.projectPath, "packages/cli/src/cli.ts");
|
|
45591
45770
|
return { cmd: "bun", cmdArgs: ["run", cliPath, ...args] };
|
|
45592
45771
|
}
|
|
45593
45772
|
return { cmd: "locus", cmdArgs: args };
|
|
@@ -45748,7 +45927,7 @@ class CliExecutor {
|
|
|
45748
45927
|
|
|
45749
45928
|
// src/bot.ts
|
|
45750
45929
|
function createBot(config2) {
|
|
45751
|
-
const bot = new
|
|
45930
|
+
const bot = new import_telegraf5.Telegraf(config2.botToken, {
|
|
45752
45931
|
handlerTimeout: HANDLER_TIMEOUT
|
|
45753
45932
|
});
|
|
45754
45933
|
const executor = new CliExecutor(config2);
|
|
@@ -45793,6 +45972,7 @@ function createBot(config2) {
|
|
|
45793
45972
|
bot.command("enddiscuss", (ctx) => endDiscussCommand(ctx, config2));
|
|
45794
45973
|
bot.command("config", (ctx) => configCommand(ctx, config2));
|
|
45795
45974
|
bot.command("model", (ctx) => modelCommand(ctx, config2));
|
|
45975
|
+
bot.command("artifacts", (ctx) => artifactsCommand(ctx, config2));
|
|
45796
45976
|
bot.command("activity", (ctx) => activityCommand(ctx, config2));
|
|
45797
45977
|
bot.command("workspace", (ctx) => workspaceCommand(ctx, config2));
|
|
45798
45978
|
bot.command("status", (ctx) => statusCommand(ctx, executor));
|
|
@@ -45829,6 +46009,10 @@ function createBot(config2) {
|
|
|
45829
46009
|
{ command: "run", description: "Start agent on sprint tasks" },
|
|
45830
46010
|
{ command: "stop", description: "Stop all running processes" },
|
|
45831
46011
|
{ command: "exec", description: "One-shot AI execution" },
|
|
46012
|
+
{
|
|
46013
|
+
command: "artifacts",
|
|
46014
|
+
description: "List & manage knowledge artifacts"
|
|
46015
|
+
},
|
|
45832
46016
|
{ command: "review", description: "AI review of PR or changes" },
|
|
45833
46017
|
{ command: "activity", description: "Recent workspace activity" },
|
|
45834
46018
|
{ command: "workspace", description: "Workspace info & stats" },
|
|
@@ -45851,17 +46035,17 @@ function createBot(config2) {
|
|
|
45851
46035
|
|
|
45852
46036
|
// src/config.ts
|
|
45853
46037
|
var import_dotenv = __toESM(require_main(), 1);
|
|
45854
|
-
import { existsSync as
|
|
45855
|
-
import { join as
|
|
46038
|
+
import { existsSync as existsSync10, readFileSync as readFileSync9 } from "node:fs";
|
|
46039
|
+
import { join as join13 } from "node:path";
|
|
45856
46040
|
import_dotenv.default.config();
|
|
45857
46041
|
var SETTINGS_FILE2 = "settings.json";
|
|
45858
46042
|
var CONFIG_DIR2 = ".locus";
|
|
45859
46043
|
function loadSettings2(projectPath) {
|
|
45860
|
-
const settingsPath =
|
|
45861
|
-
if (!
|
|
46044
|
+
const settingsPath = join13(projectPath, CONFIG_DIR2, SETTINGS_FILE2);
|
|
46045
|
+
if (!existsSync10(settingsPath)) {
|
|
45862
46046
|
return null;
|
|
45863
46047
|
}
|
|
45864
|
-
const raw =
|
|
46048
|
+
const raw = readFileSync9(settingsPath, "utf-8");
|
|
45865
46049
|
return JSON.parse(raw);
|
|
45866
46050
|
}
|
|
45867
46051
|
function resolveConfig() {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@locusai/telegram",
|
|
3
|
-
"version": "0.14.
|
|
3
|
+
"version": "0.14.4",
|
|
4
4
|
"description": "Telegram bot for Locus - remote control your AI agents from Telegram",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -31,8 +31,8 @@
|
|
|
31
31
|
"author": "",
|
|
32
32
|
"license": "MIT",
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"@locusai/sdk": "^0.14.
|
|
35
|
-
"@locusai/shared": "^0.14.
|
|
34
|
+
"@locusai/sdk": "^0.14.4",
|
|
35
|
+
"@locusai/shared": "^0.14.4",
|
|
36
36
|
"dotenv": "^16.4.7",
|
|
37
37
|
"telegraf": "^4.16.3"
|
|
38
38
|
},
|