pikiclaw 0.2.72 → 0.2.73

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.
@@ -259,7 +259,7 @@ export async function executeCommandAction(bot, chatId, action, opts = {}) {
259
259
  };
260
260
  case 'session.switch': {
261
261
  const chat = bot.chat(chatId);
262
- const result = await bot.fetchSessions(chat.agent);
262
+ const result = await bot.fetchSessions(chat.agent, bot.chatWorkdir(chatId));
263
263
  if (!result.ok)
264
264
  return { kind: 'noop', message: 'Failed to load sessions' };
265
265
  const session = result.sessions.find(entry => entry.sessionId === action.sessionId);
@@ -32,7 +32,7 @@ export function getStartData(bot, chatId) {
32
32
  return {
33
33
  ...intro,
34
34
  agent: cs.agent,
35
- workdir: bot.workdir,
35
+ workdir: bot.chatWorkdir(chatId),
36
36
  agentDetails,
37
37
  commands,
38
38
  };
@@ -61,7 +61,7 @@ export function summarizeSessionRun(session) {
61
61
  }
62
62
  export async function getSessionsPageData(bot, chatId, page, pageSize = 5) {
63
63
  const cs = bot.chat(chatId);
64
- const res = await bot.fetchSessions(cs.agent);
64
+ const res = await bot.fetchSessions(cs.agent, bot.chatWorkdir(chatId));
65
65
  const sessions = res.ok ? res.sessions : [];
66
66
  const total = sessions.length;
67
67
  const totalPages = Math.max(1, Math.ceil(total / pageSize));
@@ -139,7 +139,7 @@ export function getAgentsListData(bot, chatId) {
139
139
  }
140
140
  export function getSkillsListData(bot, chatId) {
141
141
  const cs = bot.chat(chatId);
142
- const skills = bot.fetchSkills().skills
142
+ const skills = bot.fetchSkills(bot.chatWorkdir(chatId)).skills
143
143
  .map(skill => {
144
144
  const command = buildSkillCommandName(skill.name);
145
145
  if (!command)
@@ -153,7 +153,7 @@ export function getSkillsListData(bot, chatId) {
153
153
  };
154
154
  })
155
155
  .filter((skill) => !!skill);
156
- return { agent: cs.agent, workdir: bot.workdir, skills };
156
+ return { agent: cs.agent, workdir: bot.chatWorkdir(chatId), skills };
157
157
  }
158
158
  function claudeModelSelectionKey(modelId) {
159
159
  const value = normalizeClaudeModelId(modelId).toLowerCase();
@@ -204,7 +204,7 @@ function buildEffortData(bot, agent) {
204
204
  export async function getModelsListData(bot, chatId) {
205
205
  const cs = bot.chat(chatId);
206
206
  const currentModel = bot.modelForAgent(cs.agent);
207
- const res = await bot.fetchModels(cs.agent);
207
+ const res = await bot.fetchModels(cs.agent, bot.chatWorkdir(chatId));
208
208
  return {
209
209
  agent: cs.agent,
210
210
  currentModel,
@@ -242,22 +242,22 @@ function relSkillPath(workdir, filePath) {
242
242
  return relative && !relative.startsWith('..') ? relative : filePath;
243
243
  }
244
244
  export function resolveSkillPrompt(bot, chatId, cmd, args) {
245
- const skills = bot.fetchSkills().skills;
245
+ const wd = bot.chatWorkdir(chatId);
246
+ const skills = bot.fetchSkills(wd).skills;
246
247
  const skill = indexSkillsByCommand(skills).get(cmd);
247
248
  if (!skill)
248
249
  return null;
249
- const cs = bot.chat(chatId);
250
250
  const extra = args.trim();
251
251
  const suffix = extra ? ` Additional context: ${extra}` : '';
252
- const workdirHint = `[Project directory: ${bot.workdir}]\n\n`;
252
+ const workdirHint = `[Project directory: ${wd}]\n\n`;
253
253
  let prompt;
254
- const paths = getProjectSkillPaths(bot.workdir, skill.name);
254
+ const paths = getProjectSkillPaths(wd, skill.name);
255
255
  const skillFile = paths.claudeSkillFile || paths.sharedSkillFile || paths.agentsSkillFile;
256
256
  if (skillFile) {
257
257
  prompt = `${workdirHint}Read the skill definition at \`${skillFile}\` and execute the instructions defined there.${suffix}`;
258
258
  }
259
259
  else {
260
- const fallbackPath = `${bot.workdir}/.pikiclaw/skills/${skill.name}/SKILL.md`;
260
+ const fallbackPath = `${wd}/.pikiclaw/skills/${skill.name}/SKILL.md`;
261
261
  prompt = `${workdirHint}Read the skill definition at \`${fallbackPath}\` and execute the instructions defined there.${suffix}`;
262
262
  }
263
263
  return { prompt, skillName: skill.name };
@@ -375,8 +375,9 @@ export class FeishuBot extends Bot {
375
375
  await ctx.reply(`**Workdir switched**\n\n\`${oldPath}\`\n↓\n\`${resolvedPath}\``);
376
376
  return;
377
377
  }
378
- const browsePath = path.dirname(this.workdir);
379
- const view = buildSwitchWorkdirCard(this.workdir, browsePath);
378
+ const wd = this.chatWorkdir(ctx.chatId);
379
+ const browsePath = path.dirname(wd);
380
+ const view = buildSwitchWorkdirCard(wd, browsePath);
380
381
  await ctx.channel.sendCard(ctx.chatId, view);
381
382
  }
382
383
  async cmdRestart(ctx) {
@@ -848,7 +849,7 @@ export class FeishuBot extends Bot {
848
849
  async cmdSkill(cmd, args, ctx) {
849
850
  const resolved = resolveSkillPrompt(this, ctx.chatId, cmd, args);
850
851
  if (!resolved) {
851
- await ctx.reply(`Skill not found for command /${cmd} in:\n\`${this.workdir}\``);
852
+ await ctx.reply(`Skill not found for command /${cmd} in:\n\`${this.chatWorkdir(ctx.chatId)}\``);
852
853
  return;
853
854
  }
854
855
  this.log(`skill: ${resolved.skillName} agent=${this.chat(ctx.chatId).agent}${args.trim() ? ` args="${args.trim()}"` : ''}`);
@@ -970,7 +971,8 @@ export class FeishuBot extends Bot {
970
971
  const browsePath = resolveFeishuRegisteredPath(parseInt(pathId, 10));
971
972
  if (!browsePath)
972
973
  return true;
973
- const view = buildSwitchWorkdirCard(this.workdir, browsePath, parseInt(pageRaw, 10) || 0);
974
+ const wd = this.chatWorkdir(ctx.chatId);
975
+ const view = buildSwitchWorkdirCard(wd, browsePath, parseInt(pageRaw, 10) || 0);
974
976
  await ctx.channel.editCard(ctx.chatId, ctx.messageId, view);
975
977
  return true;
976
978
  }
@@ -311,8 +311,9 @@ export class TelegramBot extends Bot {
311
311
  await ctx.reply(lines.join('\n'), { parseMode: 'HTML' });
312
312
  }
313
313
  async cmdSwitch(ctx) {
314
- const browsePath = path.dirname(this.workdir);
315
- const view = buildSwitchWorkdirView(this.workdir, browsePath);
314
+ const wd = this.chatWorkdir(ctx.chatId);
315
+ const browsePath = path.dirname(wd);
316
+ const view = buildSwitchWorkdirView(wd, browsePath);
316
317
  await ctx.reply(view.text, { parseMode: 'HTML', keyboard: view.keyboard });
317
318
  }
318
319
  async cmdHost(ctx) {
@@ -758,7 +759,8 @@ export class TelegramBot extends Bot {
758
759
  await ctx.answerCallback('Expired, use /switch again');
759
760
  return true;
760
761
  }
761
- const view = buildSwitchWorkdirView(this.workdir, browsePath, parseInt(pageRaw, 10) || 0);
762
+ const wd = this.chatWorkdir(ctx.chatId);
763
+ const view = buildSwitchWorkdirView(wd, browsePath, parseInt(pageRaw, 10) || 0);
762
764
  await ctx.editReply(ctx.messageId, view.text, { parseMode: 'HTML', keyboard: view.keyboard });
763
765
  await ctx.answerCallback();
764
766
  return true;
@@ -977,7 +979,7 @@ export class TelegramBot extends Bot {
977
979
  async cmdSkill(cmd, args, ctx) {
978
980
  const resolved = resolveSkillPrompt(this, ctx.chatId, cmd, args);
979
981
  if (!resolved) {
980
- await ctx.reply(`Skill not found for command /${cmd} in:\n<code>${escapeHtml(this.workdir)}</code>`, { parseMode: 'HTML' });
982
+ await ctx.reply(`Skill not found for command /${cmd} in:\n<code>${escapeHtml(this.chatWorkdir(ctx.chatId))}</code>`, { parseMode: 'HTML' });
981
983
  return;
982
984
  }
983
985
  this.log(`skill: ${resolved.skillName} agent=${this.chat(ctx.chatId).agent}${args.trim() ? ` args="${args.trim()}"` : ''}`);
package/dist/bot.js CHANGED
@@ -524,6 +524,10 @@ export class Bot {
524
524
  }
525
525
  return s;
526
526
  }
527
+ /** Effective workdir for a chat — per-chat override or global fallback. */
528
+ chatWorkdir(chatId) {
529
+ return this.chats.get(chatId)?.workdir || this.workdir;
530
+ }
527
531
  sessionKey(agent, sessionId) {
528
532
  return `${agent}:${sessionId}`;
529
533
  }
@@ -590,6 +594,7 @@ export class Bot {
590
594
  cs.workspacePath = session.workspacePath;
591
595
  cs.codexCumulative = session.codexCumulative;
592
596
  cs.modelId = session.modelId ?? null;
597
+ cs.workdir = session.workdir;
593
598
  if (previousSessionKey && previousSessionKey !== session.key)
594
599
  this.maybeEvictSessionRuntime(previousSessionKey);
595
600
  return;
@@ -601,8 +606,10 @@ export class Bot {
601
606
  if (previousSessionKey)
602
607
  this.maybeEvictSessionRuntime(previousSessionKey);
603
608
  }
604
- resetChatConversation(cs) {
609
+ resetChatConversation(cs, opts) {
605
610
  this.applySessionSelection(cs, null);
611
+ if (opts?.clearWorkdir)
612
+ cs.workdir = null;
606
613
  }
607
614
  adoptSession(cs, session) {
608
615
  if (!session.sessionId) {
@@ -655,9 +662,10 @@ export class Bot {
655
662
  const selected = this.getSelectedSession(cs);
656
663
  if (selected)
657
664
  return selected;
665
+ const wd = this.chatWorkdir(chatId);
658
666
  const staged = stageSessionFiles({
659
667
  agent: cs.agent,
660
- workdir: this.workdir,
668
+ workdir: wd,
661
669
  files: [],
662
670
  sessionId: null,
663
671
  title: title || 'New session',
@@ -1036,8 +1044,8 @@ export class Bot {
1036
1044
  modelForAgent(agent) {
1037
1045
  return this.agentConfigs[agent]?.model || '';
1038
1046
  }
1039
- fetchSessions(agent) {
1040
- return getSessions({ agent, workdir: this.workdir });
1047
+ fetchSessions(agent, workdir) {
1048
+ return getSessions({ agent, workdir: workdir || this.workdir });
1041
1049
  }
1042
1050
  fetchSessionTail(agent, sessionId, limit, workdir = this.workdir) {
1043
1051
  return getSessionTail({ agent, sessionId, workdir, limit });
@@ -1045,12 +1053,14 @@ export class Bot {
1045
1053
  fetchAgents(options = {}) {
1046
1054
  return listAgents(options);
1047
1055
  }
1048
- fetchSkills() {
1049
- initializeProjectSkills(this.workdir);
1050
- return listSkills(this.workdir);
1056
+ fetchSkills(workdir) {
1057
+ const wd = workdir || this.workdir;
1058
+ initializeProjectSkills(wd);
1059
+ return listSkills(wd);
1051
1060
  }
1052
- fetchModels(agent) {
1053
- return listModels(agent, { workdir: this.workdir, currentModel: this.modelForAgent(agent) });
1061
+ fetchModels(agent, workdir) {
1062
+ const wd = workdir || this.workdir;
1063
+ return listModels(agent, { workdir: wd, currentModel: this.modelForAgent(agent) });
1054
1064
  }
1055
1065
  setDefaultAgent(agent) {
1056
1066
  const next = normalizeAgent(agent);
@@ -1092,7 +1102,7 @@ export class Bot {
1092
1102
  return {
1093
1103
  version: VERSION, uptime: Date.now() - this.startedAt,
1094
1104
  memRss: mem.rss, memHeap: mem.heapUsed, pid: process.pid,
1095
- workdir: selectedSession?.workdir || this.workdir, agent: cs.agent, model, sessionId: cs.sessionId,
1105
+ workdir: this.chatWorkdir(chatId), agent: cs.agent, model, sessionId: cs.sessionId,
1096
1106
  workspacePath: cs.workspacePath ?? null,
1097
1107
  running: fallbackTask, activeTasksCount: this.activeTasks.size, stats: this.stats,
1098
1108
  usage: getUsage({ agent: cs.agent, model }),
@@ -1139,7 +1149,7 @@ export class Bot {
1139
1149
  }
1140
1150
  this.workdir = resolvedPath;
1141
1151
  for (const [, cs] of this.chats) {
1142
- this.resetChatConversation(cs);
1152
+ this.resetChatConversation(cs, { clearWorkdir: true });
1143
1153
  }
1144
1154
  for (const [key, session] of this.sessionStates) {
1145
1155
  if (session.workdir === old && !session.runningTaskIds.size)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pikiclaw",
3
- "version": "0.2.72",
3
+ "version": "0.2.73",
4
4
  "description": "Put the world's smartest AI agents in your pocket. Command local Claude & Gemini via IM. | 让最好用的 IM 变成你电脑上的顶级 Agent 控制台",
5
5
  "type": "module",
6
6
  "bin": {