@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.
Files changed (2) hide show
  1. package/bin/telegram.js +314 -130
  2. 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 join(projectPath, LOCUS_CONFIG.dir, LOCUS_CONFIG[fileName]);
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 join2 } from "node:path";
39378
+ import { delimiter, join as join3 } from "node:path";
39379
39379
  function getNvmNodeBinDir() {
39380
- const nvmDir = process.env.NVM_DIR || join2(homedir(), ".nvm");
39381
- const versionsDir = join2(nvmDir, "versions", "node");
39382
- if (!existsSync(versionsDir))
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 = readdirSync(versionsDir).filter((d) => d.startsWith("v"));
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 = join2(versionsDir, currentNodeVersion, "bin");
39394
- if (versions2.includes(currentNodeVersion) && existsSync(currentBin)) {
39393
+ const currentBin = join3(versionsDir, currentNodeVersion, "bin");
39394
+ if (versions2.includes(currentNodeVersion) && existsSync2(currentBin)) {
39395
39395
  return currentBin;
39396
39396
  }
39397
- const aliasPath = join2(nvmDir, "alias", "default");
39398
- if (existsSync(aliasPath)) {
39397
+ const aliasPath = join3(nvmDir, "alias", "default");
39398
+ if (existsSync2(aliasPath)) {
39399
39399
  try {
39400
- const alias = readFileSync(aliasPath, "utf-8").trim();
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 = join2(versionsDir, match, "bin");
39404
- if (existsSync(bin2))
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 = join2(versionsDir, sorted[0], "bin");
39419
- return existsSync(bin) ? bin : null;
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 || join2(homedir(), ".fnm");
39423
- const currentBin = join2(fnmDir, "current", "bin");
39424
- if (existsSync(currentBin))
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 = join2(fnmDir, "aliases", "default");
39427
- if (existsSync(aliasDir)) {
39428
- const bin = join2(aliasDir, "bin");
39429
- if (existsSync(bin))
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) && existsSync(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
- join2(homedir(), ".local", "bin"),
39470
- join2(homedir(), ".npm", "bin"),
39471
- join2(homedir(), ".npm-global", "bin"),
39472
- join2(homedir(), ".yarn", "bin"),
39473
- join2(homedir(), ".bun", "bin"),
39474
- join2(homedir(), "Library", "pnpm"),
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 existsSync2, readFileSync as readFileSync2, unlinkSync } from "node:fs";
39916
+ import { existsSync as existsSync3, readFileSync as readFileSync3, unlinkSync } from "node:fs";
39917
39917
  import { tmpdir } from "node:os";
39918
- import { join as join3 } from "node:path";
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 = join3(tmpdir(), `locus-codex-${randomUUID()}.txt`);
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 = join3(tmpdir(), `locus-codex-${randomUUID()}.txt`);
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 (existsSync2(outputPath)) {
40218
+ if (existsSync3(outputPath)) {
40219
40219
  try {
40220
- const text = readFileSync2(outputPath, "utf-8").trim();
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 (existsSync2(path))
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 existsSync3,
40272
+ existsSync as existsSync4,
40273
40273
  mkdirSync,
40274
- readdirSync as readdirSync2,
40275
- readFileSync as readFileSync3,
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 join4 } from "node:path";
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 = join4(this.discussionsDir, `${discussion.id}.json`);
40307
- const mdPath = join4(this.discussionsDir, `summary-${discussion.id}.md`);
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 = join4(this.discussionsDir, `${id}.json`);
40314
- if (!existsSync3(filePath)) {
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(readFileSync3(filePath, "utf-8"));
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 = readdirSync2(this.discussionsDir).filter((f) => f.endsWith(".json"));
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(readFileSync3(join4(this.discussionsDir, file2), "utf-8"));
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 = join4(this.discussionsDir, `${id}.json`);
40360
- const mdPath = join4(this.discussionsDir, `summary-${id}.md`);
40361
- if (existsSync3(jsonPath)) {
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 (existsSync3(mdPath)) {
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 (!existsSync3(this.discussionsDir)) {
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 existsSync4, readFileSync as readFileSync4 } from "node:fs";
40460
- import { join as join5 } from "node:path";
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 (existsSync4(contextPath)) {
40619
+ if (existsSync5(contextPath)) {
40620
40620
  try {
40621
- const context2 = readFileSync4(contextPath, "utf-8");
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 = join5(this.projectPath, "README.md");
40633
- if (existsSync4(readmePath)) {
40632
+ const readmePath = join6(this.projectPath, "README.md");
40633
+ if (existsSync5(readmePath)) {
40634
40634
  try {
40635
- const content = readFileSync4(readmePath, "utf-8");
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 (!existsSync4(learningsPath)) {
40654
+ if (!existsSync5(learningsPath)) {
40655
40655
  return null;
40656
40656
  }
40657
40657
  try {
40658
- const content = readFileSync4(learningsPath, "utf-8");
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 import_telegraf4 = __toESM(require_lib3(), 1);
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 existsSync5, readFileSync as readFileSync5 } from "node:fs";
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 (existsSync5(contextPath)) {
42149
+ if (existsSync6(contextPath)) {
41983
42150
  try {
41984
- const context2 = readFileSync5(contextPath, "utf-8");
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 (!existsSync5(learningsPath)) {
42163
+ if (!existsSync6(learningsPath)) {
41997
42164
  return null;
41998
42165
  }
41999
42166
  try {
42000
- const content = readFileSync5(learningsPath, "utf-8");
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 existsSync6,
42587
+ existsSync as existsSync7,
42421
42588
  mkdirSync as mkdirSync2,
42422
- readdirSync as readdirSync3,
42423
- readFileSync as readFileSync6,
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 join6 } from "node:path";
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 ?? join6(projectPath, LOCUS_CONFIG.dir, LOCUS_CONFIG.sessionsDir);
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 (!existsSync6(this.historyDir)) {
42611
+ if (!existsSync7(this.historyDir)) {
42445
42612
  mkdirSync2(this.historyDir, { recursive: true });
42446
42613
  }
42447
42614
  }
42448
42615
  getSessionPath(sessionId) {
42449
- return join6(this.historyDir, `${sessionId}.json`);
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 (!existsSync6(filePath)) {
42625
+ if (!existsSync7(filePath)) {
42459
42626
  return null;
42460
42627
  }
42461
42628
  try {
42462
- const content = readFileSync6(filePath, "utf-8");
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 (!existsSync6(filePath)) {
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 = readdirSync3(this.historyDir);
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 = readdirSync3(this.historyDir);
42721
+ const files = readdirSync4(this.historyDir);
42555
42722
  return files.filter((f) => f.endsWith(".json")).length;
42556
42723
  }
42557
42724
  sessionExists(sessionId) {
42558
- return existsSync6(this.getSessionPath(sessionId));
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 = readdirSync3(this.historyDir);
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(join6(this.historyDir, file2));
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 existsSync7 } from "node:fs";
42855
- import { dirname, join as join7 } from "node:path";
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
- join7(currentModuleDir, "..", "agent", "worker.js"),
43125
- join7(currentModuleDir, "agent", "worker.js"),
43126
- join7(currentModuleDir, "worker.js"),
43127
- join7(currentModuleDir, "..", "agent", "worker.ts")
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) => existsSync7(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 import_telegraf = __toESM(require_lib3(), 1);
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: import_telegraf.Markup.inlineKeyboard([
43245
- import_telegraf.Markup.button.callback("End Discussion", `end:discuss:${result.discussion.id}`)
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: import_telegraf.Markup.inlineKeyboard([
43273
- import_telegraf.Markup.button.callback("End Discussion", `end:discuss:${discussionId}`)
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
- import_telegraf.Markup.button.callback(`View ${d.id.slice(-6)}`, `view:discuss:${d.id}`),
43505
+ import_telegraf2.Markup.button.callback(`View ${d.id.slice(-6)}`, `view:discuss:${d.id}`),
43339
43506
  ...d.status !== "archived" ? [
43340
- import_telegraf.Markup.button.callback(`Archive ${d.id.slice(-6)}`, `archive:discuss:${d.id}`)
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: import_telegraf.Markup.inlineKeyboard(rows).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: import_telegraf.Markup.inlineKeyboard([
43384
- import_telegraf.Markup.button.callback("View Full Discussion", `view:discuss:${discussionId}`)
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: import_telegraf.Markup.inlineKeyboard([
43415
- import_telegraf.Markup.button.callback("View Full Discussion", `view:discuss:${discussionId}`)
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 existsSync8, readFileSync as readFileSync7, writeFileSync as writeFileSync3 } from "node:fs";
43788
- import { join as join8 } from "node:path";
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 join8(config2.projectPath, CONFIG_DIR, SETTINGS_FILE);
44333
+ return join9(config2.projectPath, CONFIG_DIR, SETTINGS_FILE);
44157
44334
  }
44158
44335
  function loadSettings(config2) {
44159
44336
  const path = getSettingsPath(config2);
44160
- if (!existsSync8(path))
44337
+ if (!existsSync9(path))
44161
44338
  return null;
44162
- const raw = readFileSync7(path, "utf-8");
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 join9 } from "node:path";
44663
+ import { join as join10 } from "node:path";
44487
44664
  function extraPathDirs() {
44488
44665
  const home = homedir2();
44489
44666
  return [
44490
- join9(home, ".bun", "bin"),
44491
- join9(home, ".nvm", "current", "bin"),
44492
- join9(home, ".local", "bin"),
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 &amp; Git:</b>
44729
44906
  /exec &lt;prompt&gt; — One-shot AI execution
44907
+ /artifacts — List knowledge artifacts
44908
+ /artifacts &lt;name&gt; — View an artifact
44730
44909
  /review [pr-number] — AI review of PR or changes
44731
44910
  /git &lt;command&gt; — Run whitelisted git/gh commands
44732
44911
  /dev &lt;command&gt; — 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 import_telegraf2 = __toESM(require_lib3(), 1);
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
- import_telegraf2.Markup.button.callback("✅ Approve", `approve:plan:${planId}`),
44796
- import_telegraf2.Markup.button.callback("❌ Cancel", `cancel:plan:${planId}`)
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
- ...import_telegraf2.Markup.inlineKeyboard(buttons)
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 import_telegraf3 = __toESM(require_lib3(), 1);
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
- import_telegraf3.Markup.button.callback("✅ Approve", `approve:task:${t.id}`),
45195
- import_telegraf3.Markup.button.callback("\uD83D\uDC41 View", `view:task:${t.id}`)
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
- ...import_telegraf3.Markup.inlineKeyboard(buttons)
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 join10 } from "node:path";
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 = join10(homedir3(), "Library/LaunchAgents/com.locus.telegram.plist");
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 join11 } from "node:path";
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 = join11(this.config.projectPath, "packages/cli/src/cli.ts");
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 import_telegraf4.Telegraf(config2.botToken, {
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 existsSync9, readFileSync as readFileSync8 } from "node:fs";
45855
- import { join as join12 } from "node:path";
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 = join12(projectPath, CONFIG_DIR2, SETTINGS_FILE2);
45861
- if (!existsSync9(settingsPath)) {
46044
+ const settingsPath = join13(projectPath, CONFIG_DIR2, SETTINGS_FILE2);
46045
+ if (!existsSync10(settingsPath)) {
45862
46046
  return null;
45863
46047
  }
45864
- const raw = readFileSync8(settingsPath, "utf-8");
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.2",
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.2",
35
- "@locusai/shared": "^0.14.2",
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
  },