@hienlh/ppm 0.9.33 → 0.9.35

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 (34) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/dist/web/assets/{browser-tab-B9nNKjZX.js → browser-tab-DnIsHiCc.js} +1 -1
  3. package/dist/web/assets/{chat-tab-6XGhEKaC.js → chat-tab-il6D4jql.js} +2 -2
  4. package/dist/web/assets/{code-editor-DMZMpzt2.js → code-editor-BUc1jBqm.js} +1 -1
  5. package/dist/web/assets/{database-viewer-CnP1FFS2.js → database-viewer-TjRo2b8_.js} +1 -1
  6. package/dist/web/assets/{diff-viewer-Cvwd0XBO.js → diff-viewer-BMhCz0xk.js} +1 -1
  7. package/dist/web/assets/{extension-webview-DkhsRepr.js → extension-webview-DiVdlE2r.js} +1 -1
  8. package/dist/web/assets/{git-graph-C3670Nxm.js → git-graph-4eGJ8B1A.js} +1 -1
  9. package/dist/web/assets/{index-DjIQL8ar.js → index-BmcV1di6.js} +4 -4
  10. package/dist/web/assets/keybindings-store--5T5hsAj.js +1 -0
  11. package/dist/web/assets/{markdown-renderer-Co04dDdI.js → markdown-renderer-IyEzLrC6.js} +1 -1
  12. package/dist/web/assets/{postgres-viewer-D8K1qnnA.js → postgres-viewer-CSynGGkJ.js} +1 -1
  13. package/dist/web/assets/{settings-tab-64ODAeQZ.js → settings-tab-BdI4HhRa.js} +1 -1
  14. package/dist/web/assets/{sqlite-viewer-ClX7FICB.js → sqlite-viewer-C5mviyU5.js} +1 -1
  15. package/dist/web/assets/{terminal-tab-Dw4IKWGM.js → terminal-tab-CDyC1grg.js} +1 -1
  16. package/dist/web/assets/{use-monaco-theme-DA7EyR70.js → use-monaco-theme-DcVicB_i.js} +1 -1
  17. package/dist/web/index.html +1 -1
  18. package/dist/web/sw.js +1 -1
  19. package/package.json +1 -1
  20. package/src/server/index.ts +4 -4
  21. package/src/server/routes/settings.ts +14 -14
  22. package/src/services/db.service.ts +29 -29
  23. package/src/services/{clawbot/clawbot-memory.ts → ppmbot/ppmbot-memory.ts} +29 -29
  24. package/src/services/{clawbot/clawbot-service.ts → ppmbot/ppmbot-service.ts} +55 -38
  25. package/src/services/{clawbot/clawbot-session.ts → ppmbot/ppmbot-session.ts} +48 -37
  26. package/src/services/{clawbot/clawbot-streamer.ts → ppmbot/ppmbot-streamer.ts} +114 -80
  27. package/src/services/{clawbot/clawbot-telegram.ts → ppmbot/ppmbot-telegram.ts} +46 -18
  28. package/src/types/config.ts +3 -3
  29. package/src/types/{clawbot.ts → ppmbot.ts} +10 -10
  30. package/src/web/components/chat/chat-history-bar.tsx +2 -2
  31. package/src/web/components/settings/{clawbot-settings-section.tsx → ppmbot-settings-section.tsx} +7 -7
  32. package/src/web/components/settings/settings-tab.tsx +3 -3
  33. package/dist/web/assets/keybindings-store-DHh6rwm-.js +0 -1
  34. /package/src/services/{clawbot/clawbot-formatter.ts → ppmbot/ppmbot-formatter.ts} +0 -0
@@ -969,23 +969,23 @@ export function deleteExtensionStorage(extId: string): void {
969
969
  }
970
970
 
971
971
  // ---------------------------------------------------------------------------
972
- // ClawBot session helpers
972
+ // PPMBot session helpers
973
973
  // ---------------------------------------------------------------------------
974
974
 
975
- import type { ClawBotSessionRow, ClawBotMemoryRow, ClawBotPairedChat } from "../types/clawbot.ts";
975
+ import type { PPMBotSessionRow, PPMBotMemoryRow, PPMBotPairedChat } from "../types/ppmbot.ts";
976
976
 
977
- export function getActiveClawBotSession(
977
+ export function getActivePPMBotSession(
978
978
  telegramChatId: string,
979
979
  projectName: string,
980
- ): ClawBotSessionRow | null {
980
+ ): PPMBotSessionRow | null {
981
981
  return getDb().query(
982
982
  `SELECT * FROM clawbot_sessions
983
983
  WHERE telegram_chat_id = ? AND project_name = ? AND is_active = 1
984
984
  ORDER BY last_message_at DESC LIMIT 1`,
985
- ).get(telegramChatId, projectName) as ClawBotSessionRow | null;
985
+ ).get(telegramChatId, projectName) as PPMBotSessionRow | null;
986
986
  }
987
987
 
988
- export function createClawBotSession(
988
+ export function createPPMBotSession(
989
989
  telegramChatId: string,
990
990
  sessionId: string,
991
991
  providerId: string,
@@ -999,34 +999,34 @@ export function createClawBotSession(
999
999
  ).run(telegramChatId, sessionId, providerId, projectName, projectPath);
1000
1000
  }
1001
1001
 
1002
- export function deactivateClawBotSession(sessionId: string): void {
1002
+ export function deactivatePPMBotSession(sessionId: string): void {
1003
1003
  getDb().query(
1004
1004
  "UPDATE clawbot_sessions SET is_active = 0 WHERE session_id = ?",
1005
1005
  ).run(sessionId);
1006
1006
  }
1007
1007
 
1008
- export function touchClawBotSession(sessionId: string): void {
1008
+ export function touchPPMBotSession(sessionId: string): void {
1009
1009
  getDb().query(
1010
1010
  "UPDATE clawbot_sessions SET last_message_at = unixepoch() WHERE session_id = ?",
1011
1011
  ).run(sessionId);
1012
1012
  }
1013
1013
 
1014
- export function getRecentClawBotSessions(
1014
+ export function getRecentPPMBotSessions(
1015
1015
  telegramChatId: string,
1016
1016
  limit = 10,
1017
- ): ClawBotSessionRow[] {
1017
+ ): PPMBotSessionRow[] {
1018
1018
  return getDb().query(
1019
1019
  `SELECT * FROM clawbot_sessions
1020
1020
  WHERE telegram_chat_id = ?
1021
1021
  ORDER BY last_message_at DESC LIMIT ?`,
1022
- ).all(telegramChatId, limit) as ClawBotSessionRow[];
1022
+ ).all(telegramChatId, limit) as PPMBotSessionRow[];
1023
1023
  }
1024
1024
 
1025
1025
  // ---------------------------------------------------------------------------
1026
- // ClawBot memory helpers
1026
+ // PPMBot memory helpers
1027
1027
  // ---------------------------------------------------------------------------
1028
1028
 
1029
- export function insertClawBotMemory(
1029
+ export function insertPPMBotMemory(
1030
1030
  project: string,
1031
1031
  content: string,
1032
1032
  category: string,
@@ -1040,11 +1040,11 @@ export function insertClawBotMemory(
1040
1040
  return Number(result.lastInsertRowid);
1041
1041
  }
1042
1042
 
1043
- export function searchClawBotMemories(
1043
+ export function searchPPMBotMemories(
1044
1044
  project: string,
1045
1045
  query: string,
1046
1046
  limit = 20,
1047
- ): Array<ClawBotMemoryRow & { rank: number }> {
1047
+ ): Array<PPMBotMemoryRow & { rank: number }> {
1048
1048
  return getDb().query(
1049
1049
  `SELECT m.*, fts.rank
1050
1050
  FROM clawbot_memories m
@@ -1054,23 +1054,23 @@ export function searchClawBotMemories(
1054
1054
  AND m.superseded_by IS NULL
1055
1055
  ORDER BY fts.rank
1056
1056
  LIMIT ?`,
1057
- ).all(query, project, limit) as Array<ClawBotMemoryRow & { rank: number }>;
1057
+ ).all(query, project, limit) as Array<PPMBotMemoryRow & { rank: number }>;
1058
1058
  }
1059
1059
 
1060
- export function getClawBotMemories(
1060
+ export function getPPMBotMemories(
1061
1061
  project: string,
1062
1062
  limit = 20,
1063
- ): ClawBotMemoryRow[] {
1063
+ ): PPMBotMemoryRow[] {
1064
1064
  return getDb().query(
1065
1065
  `SELECT * FROM clawbot_memories
1066
1066
  WHERE project IN (?, '_global')
1067
1067
  AND superseded_by IS NULL
1068
1068
  ORDER BY importance DESC, updated_at DESC
1069
1069
  LIMIT ?`,
1070
- ).all(project, limit) as ClawBotMemoryRow[];
1070
+ ).all(project, limit) as PPMBotMemoryRow[];
1071
1071
  }
1072
1072
 
1073
- export function supersedeClawBotMemory(
1073
+ export function supersedePPMBotMemory(
1074
1074
  oldId: number,
1075
1075
  newId: number,
1076
1076
  ): void {
@@ -1079,7 +1079,7 @@ export function supersedeClawBotMemory(
1079
1079
  ).run(newId, oldId);
1080
1080
  }
1081
1081
 
1082
- export function deleteClawBotMemoriesByTopic(
1082
+ export function deletePPMBotMemoriesByTopic(
1083
1083
  project: string,
1084
1084
  topic: string,
1085
1085
  ): number {
@@ -1097,7 +1097,7 @@ export function deleteClawBotMemoriesByTopic(
1097
1097
  return matches.length;
1098
1098
  }
1099
1099
 
1100
- export function decayClawBotMemories(): void {
1100
+ export function decayPPMBotMemories(): void {
1101
1101
  getDb().query(
1102
1102
  `UPDATE clawbot_memories
1103
1103
  SET importance = importance * 0.95,
@@ -1112,7 +1112,7 @@ export function decayClawBotMemories(): void {
1112
1112
  }
1113
1113
 
1114
1114
  // ---------------------------------------------------------------------------
1115
- // ClawBot pairing helpers
1115
+ // PPMBot pairing helpers
1116
1116
  // ---------------------------------------------------------------------------
1117
1117
 
1118
1118
  export function createPairingRequest(
@@ -1147,22 +1147,22 @@ export function revokePairing(chatId: string): void {
1147
1147
  ).run(chatId);
1148
1148
  }
1149
1149
 
1150
- export function getPairingByCode(code: string): ClawBotPairedChat | null {
1150
+ export function getPairingByCode(code: string): PPMBotPairedChat | null {
1151
1151
  return getDb().query(
1152
1152
  "SELECT * FROM clawbot_paired_chats WHERE pairing_code = ? AND status = 'pending'",
1153
- ).get(code) as ClawBotPairedChat | null;
1153
+ ).get(code) as PPMBotPairedChat | null;
1154
1154
  }
1155
1155
 
1156
- export function getPairingByChatId(chatId: string): ClawBotPairedChat | null {
1156
+ export function getPairingByChatId(chatId: string): PPMBotPairedChat | null {
1157
1157
  return getDb().query(
1158
1158
  "SELECT * FROM clawbot_paired_chats WHERE telegram_chat_id = ?",
1159
- ).get(chatId) as ClawBotPairedChat | null;
1159
+ ).get(chatId) as PPMBotPairedChat | null;
1160
1160
  }
1161
1161
 
1162
- export function listPairedChats(): ClawBotPairedChat[] {
1162
+ export function listPairedChats(): PPMBotPairedChat[] {
1163
1163
  return getDb().query(
1164
1164
  "SELECT * FROM clawbot_paired_chats WHERE status != 'revoked' ORDER BY created_at DESC",
1165
- ).all() as ClawBotPairedChat[];
1165
+ ).all() as PPMBotPairedChat[];
1166
1166
  }
1167
1167
 
1168
1168
  export function isPairedChat(chatId: string): boolean {
@@ -1,17 +1,17 @@
1
1
  import {
2
- insertClawBotMemory,
3
- searchClawBotMemories,
4
- getClawBotMemories,
5
- supersedeClawBotMemory,
6
- deleteClawBotMemoriesByTopic,
7
- decayClawBotMemories,
2
+ insertPPMBotMemory,
3
+ searchPPMBotMemories,
4
+ getPPMBotMemories,
5
+ supersedePPMBotMemory,
6
+ deletePPMBotMemoriesByTopic,
7
+ decayPPMBotMemories,
8
8
  getDb,
9
9
  } from "../db.service.ts";
10
10
  import { configService } from "../config.service.ts";
11
11
  import type {
12
- ClawBotMemoryCategory,
12
+ PPMBotMemoryCategory,
13
13
  MemoryRecallResult,
14
- } from "../../types/clawbot.ts";
14
+ } from "../../types/ppmbot.ts";
15
15
  import type { ProjectConfig } from "../../types/config.ts";
16
16
 
17
17
  /** Max memories per project before pruning */
@@ -20,11 +20,11 @@ const MAX_MEMORIES_PER_PROJECT = 500;
20
20
  /** Fact extracted from AI response */
21
21
  interface ExtractedFact {
22
22
  content: string;
23
- category: ClawBotMemoryCategory;
23
+ category: PPMBotMemoryCategory;
24
24
  importance?: number;
25
25
  }
26
26
 
27
- export class ClawBotMemory {
27
+ export class PPMBotMemory {
28
28
  /**
29
29
  * Recall relevant memories for a project.
30
30
  * If query provided, use FTS5 search. Otherwise return top by importance.
@@ -34,7 +34,7 @@ export class ClawBotMemory {
34
34
  const sanitized = this.sanitizeFtsQuery(query);
35
35
  if (sanitized) {
36
36
  try {
37
- const results = searchClawBotMemories(project, sanitized, limit);
37
+ const results = searchPPMBotMemories(project, sanitized, limit);
38
38
  return results.map((r) => ({
39
39
  id: r.id,
40
40
  content: r.content,
@@ -49,7 +49,7 @@ export class ClawBotMemory {
49
49
  }
50
50
  }
51
51
 
52
- const rows = getClawBotMemories(project, limit);
52
+ const rows = getPPMBotMemories(project, limit);
53
53
  return rows.map((r) => ({
54
54
  id: r.id,
55
55
  content: r.content,
@@ -94,7 +94,7 @@ export class ClawBotMemory {
94
94
 
95
95
  const existingId = this.findSimilar(project, fact.content);
96
96
 
97
- const newId = insertClawBotMemory(
97
+ const newId = insertPPMBotMemory(
98
98
  project,
99
99
  fact.content.trim(),
100
100
  fact.category || "fact",
@@ -103,7 +103,7 @@ export class ClawBotMemory {
103
103
  );
104
104
 
105
105
  if (existingId) {
106
- supersedeClawBotMemory(existingId, newId);
106
+ supersedePPMBotMemory(existingId, newId);
107
107
  }
108
108
 
109
109
  inserted++;
@@ -117,13 +117,13 @@ export class ClawBotMemory {
117
117
  saveOne(
118
118
  project: string,
119
119
  content: string,
120
- category: ClawBotMemoryCategory = "fact",
120
+ category: PPMBotMemoryCategory = "fact",
121
121
  sessionId?: string,
122
122
  ): number {
123
123
  const existingId = this.findSimilar(project, content);
124
- const newId = insertClawBotMemory(project, content.trim(), category, 1.0, sessionId);
124
+ const newId = insertPPMBotMemory(project, content.trim(), category, 1.0, sessionId);
125
125
  if (existingId) {
126
- supersedeClawBotMemory(existingId, newId);
126
+ supersedePPMBotMemory(existingId, newId);
127
127
  }
128
128
  return newId;
129
129
  }
@@ -132,12 +132,12 @@ export class ClawBotMemory {
132
132
  forget(project: string, topic: string): number {
133
133
  const sanitized = this.sanitizeFtsQuery(topic);
134
134
  if (!sanitized) return 0;
135
- return deleteClawBotMemoriesByTopic(project, sanitized);
135
+ return deletePPMBotMemoriesByTopic(project, sanitized);
136
136
  }
137
137
 
138
138
  /** Get summary of all active memories for a project */
139
139
  getSummary(project: string, limit = 30): MemoryRecallResult[] {
140
- const rows = getClawBotMemories(project, limit);
140
+ const rows = getPPMBotMemories(project, limit);
141
141
  return rows.map((r) => ({
142
142
  id: r.id,
143
143
  content: r.content,
@@ -214,7 +214,7 @@ If nothing worth remembering, return []`;
214
214
  }))
215
215
  .filter((f) => f.content.length > 0);
216
216
  } catch {
217
- console.warn("[clawbot-memory] Failed to parse extraction response");
217
+ console.warn("[ppmbot-memory] Failed to parse extraction response");
218
218
  return [];
219
219
  }
220
220
  }
@@ -225,7 +225,7 @@ If nothing worth remembering, return []`;
225
225
  */
226
226
  extractiveMemoryFallback(conversationText: string): ExtractedFact[] {
227
227
  const facts: ExtractedFact[] = [];
228
- const patterns: Array<{ re: RegExp; category: ClawBotMemoryCategory }> = [
228
+ const patterns: Array<{ re: RegExp; category: PPMBotMemoryCategory }> = [
229
229
  { re: /(?:decided|chose|went with|picked|selected)\s+(.{10,100})/gi, category: "decision" },
230
230
  { re: /(?:prefer|always use|like to|rather)\s+(.{10,80})/gi, category: "preference" },
231
231
  { re: /(?:uses?|built with|stack is|powered by|database is)\s+(.{5,80})/gi, category: "architecture" },
@@ -246,9 +246,9 @@ If nothing worth remembering, return []`;
246
246
  /** Run importance decay on old memories */
247
247
  runDecay(): void {
248
248
  try {
249
- decayClawBotMemories();
249
+ decayPPMBotMemories();
250
250
  } catch (err) {
251
- console.error("[clawbot-memory] Decay error:", (err as Error).message);
251
+ console.error("[ppmbot-memory] Decay error:", (err as Error).message);
252
252
  }
253
253
  }
254
254
 
@@ -272,7 +272,7 @@ If nothing worth remembering, return []`;
272
272
  )`,
273
273
  ).run(project, excess);
274
274
  } catch (err) {
275
- console.error("[clawbot-memory] Prune error:", (err as Error).message);
275
+ console.error("[ppmbot-memory] Prune error:", (err as Error).message);
276
276
  }
277
277
  }
278
278
 
@@ -301,7 +301,7 @@ If nothing worth remembering, return []`;
301
301
  if (!query) return null;
302
302
 
303
303
  try {
304
- const results = searchClawBotMemories(project, query, 3);
304
+ const results = searchPPMBotMemories(project, query, 3);
305
305
  if (results.length > 0 && results[0]!.rank < -5) {
306
306
  return results[0]!.id;
307
307
  }
@@ -322,12 +322,12 @@ If nothing worth remembering, return []`;
322
322
  }
323
323
 
324
324
  /** Validate category string against known values */
325
- private validateCategory(cat: string): ClawBotMemoryCategory {
326
- const valid: ClawBotMemoryCategory[] = [
325
+ private validateCategory(cat: string): PPMBotMemoryCategory {
326
+ const valid: PPMBotMemoryCategory[] = [
327
327
  "fact", "decision", "preference", "architecture", "issue",
328
328
  ];
329
- return valid.includes(cat as ClawBotMemoryCategory)
330
- ? (cat as ClawBotMemoryCategory)
329
+ return valid.includes(cat as PPMBotMemoryCategory)
330
+ ? (cat as PPMBotMemoryCategory)
331
331
  : "fact";
332
332
  }
333
333
  }
@@ -6,21 +6,21 @@ import {
6
6
  createPairingRequest,
7
7
  approvePairing,
8
8
  } from "../db.service.ts";
9
- import { ClawBotTelegram } from "./clawbot-telegram.ts";
10
- import { ClawBotSessionManager } from "./clawbot-session.ts";
11
- import { ClawBotMemory } from "./clawbot-memory.ts";
12
- import { streamToTelegram } from "./clawbot-streamer.ts";
13
- import { escapeHtml } from "./clawbot-formatter.ts";
14
- import type { TelegramUpdate, ClawBotCommand } from "../../types/clawbot.ts";
15
- import type { ClawBotConfig, TelegramConfig, PermissionMode } from "../../types/config.ts";
9
+ import { PPMBotTelegram } from "./ppmbot-telegram.ts";
10
+ import { PPMBotSessionManager } from "./ppmbot-session.ts";
11
+ import { PPMBotMemory } from "./ppmbot-memory.ts";
12
+ import { streamToTelegram } from "./ppmbot-streamer.ts";
13
+ import { escapeHtml } from "./ppmbot-formatter.ts";
14
+ import type { TelegramUpdate, PPMBotCommand } from "../../types/ppmbot.ts";
15
+ import type { PPMBotConfig, TelegramConfig, PermissionMode } from "../../types/config.ts";
16
16
  import type { SendMessageOpts } from "../../types/chat.ts";
17
17
 
18
18
  const CONTEXT_WINDOW_THRESHOLD = 80;
19
19
 
20
- class ClawBotService {
21
- private telegram: ClawBotTelegram | null = null;
22
- private sessions = new ClawBotSessionManager();
23
- private memory = new ClawBotMemory();
20
+ class PPMBotService {
21
+ private telegram: PPMBotTelegram | null = null;
22
+ private sessions = new PPMBotSessionManager();
23
+ private memory = new PPMBotMemory();
24
24
  private running = false;
25
25
 
26
26
  /** Debounce timers per chatId */
@@ -39,20 +39,20 @@ class ClawBotService {
39
39
  // ── Lifecycle ─────────────────────────────────────────────────
40
40
 
41
41
  async start(): Promise<void> {
42
- const clawbotConfig = this.getConfig();
43
- if (!clawbotConfig?.enabled) {
44
- console.log("[clawbot] Disabled in config");
42
+ const ppmbotConfig = this.getConfig();
43
+ if (!ppmbotConfig?.enabled) {
44
+ console.log("[ppmbot] Disabled in config");
45
45
  return;
46
46
  }
47
47
 
48
48
  const telegramConfig = configService.get("telegram") as TelegramConfig | undefined;
49
49
  if (!telegramConfig?.bot_token) {
50
- console.log("[clawbot] No bot token configured");
50
+ console.log("[ppmbot] No bot token configured");
51
51
  return;
52
52
  }
53
53
 
54
54
  try {
55
- this.telegram = new ClawBotTelegram(telegramConfig.bot_token);
55
+ this.telegram = new PPMBotTelegram(telegramConfig.bot_token);
56
56
  this.running = true;
57
57
 
58
58
  // Run memory decay on startup
@@ -61,9 +61,9 @@ class ClawBotService {
61
61
  // Start polling (non-blocking)
62
62
  this.telegram.startPolling((update) => this.handleUpdate(update));
63
63
 
64
- console.log("[clawbot] Started");
64
+ console.log("[ppmbot] Started");
65
65
  } catch (err) {
66
- console.error("[clawbot] Start failed:", (err as Error).message);
66
+ console.error("[ppmbot] Start failed:", (err as Error).message);
67
67
  }
68
68
  }
69
69
 
@@ -78,7 +78,7 @@ class ClawBotService {
78
78
  this.processing.clear();
79
79
  this.messageQueue.clear();
80
80
 
81
- console.log("[clawbot] Stopped");
81
+ console.log("[ppmbot] Stopped");
82
82
  }
83
83
 
84
84
  get isRunning(): boolean {
@@ -89,7 +89,7 @@ class ClawBotService {
89
89
  async notifyPairingApproved(chatId: string): Promise<void> {
90
90
  await this.telegram?.sendMessage(
91
91
  Number(chatId),
92
- "✅ Pairing approved! You can now chat with ClawBot.\n\nSend /start to begin.",
92
+ "✅ Pairing approved! You can now chat with PPMBot.\n\nSend /start to begin.",
93
93
  );
94
94
  }
95
95
 
@@ -113,14 +113,14 @@ class ClawBotService {
113
113
  createPairingRequest(chatId, String(userId), displayName, code);
114
114
  await this.telegram!.sendMessage(
115
115
  Number(chatId),
116
- `🔐 Pairing required.\n\nYour pairing code: <code>${code}</code>\n\nEnter this code in PPM Settings → ClawBot → Pair Device to approve access.`,
116
+ `🔐 Pairing required.\n\nYour pairing code: <code>${code}</code>\n\nEnter this code in PPM Settings → PPMBot → Pair Device to approve access.`,
117
117
  );
118
118
  return;
119
119
  }
120
120
  if (pairing.status === "pending") {
121
121
  await this.telegram!.sendMessage(
122
122
  Number(chatId),
123
- `⏳ Pairing pending approval.\n\nCode: <code>${pairing.pairing_code}</code>\nAsk the PPM owner to approve in Settings → ClawBot.`,
123
+ `⏳ Pairing pending approval.\n\nCode: <code>${pairing.pairing_code}</code>\nAsk the PPM owner to approve in Settings → PPMBot.`,
124
124
  );
125
125
  return;
126
126
  }
@@ -130,7 +130,7 @@ class ClawBotService {
130
130
  }
131
131
 
132
132
  // Try parsing as command
133
- const command = ClawBotTelegram.parseCommand(message);
133
+ const command = PPMBotTelegram.parseCommand(message);
134
134
  if (command) {
135
135
  await this.handleCommand(command);
136
136
  return;
@@ -145,7 +145,7 @@ class ClawBotService {
145
145
 
146
146
  // ── Command Handlers ────────────────────────────────────────────
147
147
 
148
- private async handleCommand(cmd: ClawBotCommand): Promise<void> {
148
+ private async handleCommand(cmd: PPMBotCommand): Promise<void> {
149
149
  const chatId = String(cmd.chatId);
150
150
  const tg = this.telegram!;
151
151
 
@@ -174,18 +174,35 @@ class ClawBotService {
174
174
 
175
175
  private async cmdStart(chatId: string): Promise<void> {
176
176
  const projects = this.sessions.getProjectNames();
177
- let text = "<b>🤖 ClawBot</b>\n\nI'm your PPM assistant on Telegram.\n\n";
177
+ let text = "<b>🤖 PPMBot</b>\n\n";
178
+ text += "Hey! I'm your AI coding assistant, right here in Telegram.\n";
179
+ text += "Ask me anything — code questions, debugging, project tasks.\n\n";
178
180
  if (projects.length) {
179
- text += "<b>Available projects:</b>\n";
181
+ text += "<b>Your projects:</b>\n";
180
182
  for (const name of projects) {
181
- text += `• <code>${escapeHtml(name)}</code>\n`;
183
+ text += ` • <code>${escapeHtml(name)}</code>\n`;
182
184
  }
183
- text += "\nSwitch project: /project &lt;name&gt;";
185
+ text += "\nSwitch: /project &lt;name&gt;";
184
186
  } else {
185
- text += "No projects configured. Add projects in PPM settings.";
187
+ text += "No projects configured I'll use a default workspace.";
186
188
  }
187
- text += "\n\nSend /help for all commands.";
189
+ text += "\n\nJust send a message to start chatting, or /help for commands.";
188
190
  await this.telegram!.sendMessage(Number(chatId), text);
191
+
192
+ // Identity onboarding: if no identity memories exist, ask user
193
+ const identityMemories = this.memory.recall("_global", "user identity name role");
194
+ if (identityMemories.length === 0) {
195
+ await this.telegram!.sendMessage(
196
+ Number(chatId),
197
+ "📝 <b>Quick intro?</b>\n\n" +
198
+ "I don't know much about you yet! Tell me:\n" +
199
+ "• Your name\n" +
200
+ "• What you work on (language, stack, role)\n" +
201
+ "• Preferred response language (English, Vietnamese, etc.)\n\n" +
202
+ "I'll remember your preferences for future chats.\n" +
203
+ "Or skip this and just start chatting!",
204
+ );
205
+ }
189
206
  }
190
207
 
191
208
  private async cmdProject(chatId: string, args: string): Promise<void> {
@@ -310,7 +327,7 @@ class ClawBotService {
310
327
  }
311
328
 
312
329
  private async cmdHelp(chatId: string): Promise<void> {
313
- const text = `<b>ClawBot Commands</b>
330
+ const text = `<b>PPMBot Commands</b>
314
331
 
315
332
  /start — Greeting + list projects
316
333
  /project &lt;name&gt; — Switch project
@@ -421,7 +438,7 @@ class ClawBotService {
421
438
  await this.rotateSession(chatId, session.projectName);
422
439
  }
423
440
  } catch (err) {
424
- console.error(`[clawbot] processMessage error for ${chatId}:`, (err as Error).message);
441
+ console.error(`[ppmbot] processMessage error for ${chatId}:`, (err as Error).message);
425
442
  await this.telegram?.sendMessage(
426
443
  Number(chatId),
427
444
  `❌ ${escapeHtml((err as Error).message)}`,
@@ -462,15 +479,15 @@ class ClawBotService {
462
479
  const facts = this.memory.parseExtractionResponse(responseText);
463
480
  if (facts.length > 0) {
464
481
  const count = this.memory.save(session.projectName, facts, session.sessionId);
465
- console.log(`[clawbot] Saved ${count} memories for ${session.projectName}`);
482
+ console.log(`[ppmbot] Saved ${count} memories for ${session.projectName}`);
466
483
  } else {
467
484
  // Fallback: regex-based extraction
468
485
  // Note: we don't have conversation history text here easily,
469
486
  // so regex fallback only triggers when AI extraction fails
470
- console.log("[clawbot] No memories extracted via AI");
487
+ console.log("[ppmbot] No memories extracted via AI");
471
488
  }
472
489
  } catch (err) {
473
- console.warn("[clawbot] Memory save failed:", (err as Error).message);
490
+ console.warn("[ppmbot] Memory save failed:", (err as Error).message);
474
491
  }
475
492
  }
476
493
 
@@ -492,9 +509,9 @@ class ClawBotService {
492
509
  return Array.from(bytes, (b) => chars[b % chars.length]).join("");
493
510
  }
494
511
 
495
- private getConfig(): ClawBotConfig | undefined {
496
- return configService.get("clawbot") as ClawBotConfig | undefined;
512
+ private getConfig(): PPMBotConfig | undefined {
513
+ return configService.get("clawbot") as PPMBotConfig | undefined;
497
514
  }
498
515
  }
499
516
 
500
- export const clawbotService = new ClawBotService();
517
+ export const ppmbotService = new PPMBotService();