@serjm/deepseek-code 0.4.1 → 0.4.3

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/CHANGELOG.md CHANGED
@@ -1,5 +1,34 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.4.3 — Project Mode & Browser Control
4
+
5
+ ### Improvements
6
+ - Improved Chrome browser mode handling.
7
+ - `/chrome --headed` and `/chrome --headless` now switch the desired browser mode without launching Chrome.
8
+ - Browser checks remain headed by default and open visibly unless headless mode is explicitly selected.
9
+ - Browser test reports now show the actual browser mode.
10
+
11
+ ### Agent Quality
12
+ - Added Execution Policy to reduce unnecessary reads, repeated tool calls, temporary files, and over-scoped work.
13
+ - Added Source of Truth Policy to prevent invented versions, release notes, features, links, dates, and metrics.
14
+ - Added Project Acceptance Policy for web/container projects: build alone is not enough; dev server, browser page, error overlay, console, container build, and git hygiene must be checked when relevant.
15
+ - Added Failed Tool Calls Policy: failed commands must be reported and classified as critical or non-critical.
16
+
17
+ ## 0.4.2 — Update Visibility Improvements
18
+
19
+ ### New Features
20
+ - Added startup update notice that checks npm for a newer version.
21
+ - Added 24-hour cooldown to avoid checking npm on every launch.
22
+ - Added `/changelog` modes:
23
+ - `/changelog` shows the latest release notes.
24
+ - `/changelog full` shows the full changelog.
25
+ - `/changelog <version>` shows a specific version.
26
+
27
+ ### Reliability
28
+ - Update checks run in the background and do not block CLI startup.
29
+ - Network/offline errors are handled silently during startup.
30
+ - Fixed changelog section parsing on Windows CRLF files.
31
+
3
32
  ## 0.4.1 — Update Visibility
4
33
 
5
34
  ### New Features
package/README.md CHANGED
@@ -20,8 +20,12 @@
20
20
 
21
21
  ---
22
22
 
23
- ## What's new in 0.4.0
23
+ ## What's new in 0.4.x
24
24
 
25
+ - Better project-mode guardrails and visible browser-mode control.
26
+ - Startup update notice and improved `/changelog` modes.
27
+ - `/changelog` command to view release notes inside the CLI.
28
+ - `/update-check` command to check the latest npm version.
25
29
  - Stability and honest reporting improvements.
26
30
  - `/budget` commands to limit runaway agent loops.
27
31
  - Git Files section in Execution Summary.
package/README.ru.md CHANGED
@@ -20,8 +20,12 @@
20
20
 
21
21
  ---
22
22
 
23
- ## Что нового в 0.4.0
23
+ ## Что нового в 0.4.x
24
24
 
25
+ - Улучшенные правила project mode и наглядное управление режимом браузера.
26
+ - Уведомление об обновлениях при запуске и улучшенные режимы `/changelog`.
27
+ - `/changelog` показывает список изменений прямо в CLI.
28
+ - `/update-check` проверяет последнюю версию в npm.
25
29
  - Улучшена стабильность и честность отчётов.
26
30
  - `/budget` команды ограничивают runaway tool/API calls.
27
31
  - Execution Summary показывает Files: changed/untracked/dirty before run.
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/commands/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAA;AAgBlD,OAAO,KAAK,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAA;AACzE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAA;AAE1D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAA;AAItD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAA;AAKnD,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,cAAc,CAAA;IACtB,YAAY,EAAE,YAAY,CAAA;IAC1B,QAAQ,EAAE,WAAW,EAAE,CAAA;IACvB,WAAW,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,WAAW,EAAE,CAAC,CAAC,CAAA;IAChE,aAAa,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC,CAAA;IAC3D,YAAY,EAAE,CAAC,IAAI,EAAE,SAAS,KAAK,IAAI,CAAA;IACvC,iFAAiF;IACjF,aAAa,CAAC,EAAE,MAAM,IAAI,CAAA;IAC1B,iFAAiF;IACjF,aAAa,CAAC,EAAE,MAAM,IAAI,CAAA;IAC1B,gFAAgF;IAChF,YAAY,CAAC,EAAE,MAAM,IAAI,CAAA;IACzB,kGAAkG;IAClG,gBAAgB,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;IACzC,iEAAiE;IACjE,UAAU,CAAC,EAAE,MAAM,gBAAgB,GAAG,SAAS,CAAA;IAC/C,yDAAyD;IACzD,SAAS,CAAC,EAAE,MAAM,UAAU,GAAG,SAAS,CAAA;IACxC,6CAA6C;IAC7C,SAAS,CAAC,EAAE,CAAC,MAAM,EAAE,UAAU,GAAG,SAAS,KAAK,IAAI,CAAA;CACrD;AA0pCD,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAA;IACZ,WAAW,EAAE,MAAM,CAAA;IACnB,OAAO,EAAE,CAAC,GAAG,EAAE,mBAAmB,EAAE,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAA;CACvE;AAED,eAAO,MAAM,QAAQ,EAAE,YAAY,EAkClC,CAAA;AAED,eAAO,MAAM,aAAa,EAAE,MAAM,EAA8B,CAAA;AAEhE,eAAO,MAAM,WAAW,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAEnD,CAAA;AAgBD,wBAAsB,mBAAmB,CAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,mBAAmB,GAAG,OAAO,CAAC,OAAO,CAAC,CAapG"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/commands/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAA;AAgBlD,OAAO,KAAK,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAA;AACzE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAA;AAE1D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAA;AAItD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAA;AAMnD,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,cAAc,CAAA;IACtB,YAAY,EAAE,YAAY,CAAA;IAC1B,QAAQ,EAAE,WAAW,EAAE,CAAA;IACvB,WAAW,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,WAAW,EAAE,CAAC,CAAC,CAAA;IAChE,aAAa,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC,CAAA;IAC3D,YAAY,EAAE,CAAC,IAAI,EAAE,SAAS,KAAK,IAAI,CAAA;IACvC,iFAAiF;IACjF,aAAa,CAAC,EAAE,MAAM,IAAI,CAAA;IAC1B,iFAAiF;IACjF,aAAa,CAAC,EAAE,MAAM,IAAI,CAAA;IAC1B,gFAAgF;IAChF,YAAY,CAAC,EAAE,MAAM,IAAI,CAAA;IACzB,kGAAkG;IAClG,gBAAgB,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;IACzC,iEAAiE;IACjE,UAAU,CAAC,EAAE,MAAM,gBAAgB,GAAG,SAAS,CAAA;IAC/C,yDAAyD;IACzD,SAAS,CAAC,EAAE,MAAM,UAAU,GAAG,SAAS,CAAA;IACxC,6CAA6C;IAC7C,SAAS,CAAC,EAAE,CAAC,MAAM,EAAE,UAAU,GAAG,SAAS,KAAK,IAAI,CAAA;CACrD;AA8rCD,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAA;IACZ,WAAW,EAAE,MAAM,CAAA;IACnB,OAAO,EAAE,CAAC,GAAG,EAAE,mBAAmB,EAAE,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAA;CACvE;AAED,eAAO,MAAM,QAAQ,EAAE,YAAY,EAkClC,CAAA;AAED,eAAO,MAAM,aAAa,EAAE,MAAM,EAA8B,CAAA;AAEhE,eAAO,MAAM,WAAW,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAEnD,CAAA;AAgBD,wBAAsB,mBAAmB,CAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,mBAAmB,GAAG,OAAO,CAAC,OAAO,CAAC,CAapG"}
@@ -19,6 +19,7 @@ import { getDefaultTools, getToolsForMode } from '../tools/registry.js';
19
19
  import { browserTest, getLastBrowserTestResult, browserRealTest } from '../tools/chrome.js';
20
20
  import { chromeManager } from '../tools/chrome-manager.js';
21
21
  import { AUDIT_BUDGET_PRESET } from '../tools/types.js';
22
+ import { checkLatestVersion } from './update-checker.js';
22
23
  // ─── Helpers ─────────────────────────────────────────────────────────────────
23
24
  function generateFollowups(lastContent) {
24
25
  const suggestions = [];
@@ -76,7 +77,7 @@ const RU_DESCRIPTIONS = {
76
77
  '/last-browser-test': 'Показать последний отчёт browser-test',
77
78
  '/chrome': 'Режим Chrome: --headed|--headless|-s',
78
79
  '/budget': 'Бюджет: /budget status|off|audit|small',
79
- '/changelog': 'Показать список изменений',
80
+ '/changelog': 'Показать изменения: /changelog [full|version]',
80
81
  '/update-check': 'Проверить новую версию',
81
82
  };
82
83
  function getDescription(name) {
@@ -793,6 +794,7 @@ async function cmdTools(ctx) {
793
794
  }
794
795
  async function cmdBrowserTest(ctx, input) {
795
796
  // Parse flags: /browser-test --headed or /browser-test --headless
797
+ // Default uses the configured chromeHeadless mode
796
798
  const parts = input.trim().split(/\s+/);
797
799
  const flag = parts.length > 1 ? parts[1].toLowerCase() : '';
798
800
  let headless;
@@ -803,7 +805,7 @@ async function cmdBrowserTest(ctx, input) {
803
805
  headless = true;
804
806
  }
805
807
  else {
806
- headless = false; // default: headed (видимое окно)
808
+ headless = ctx.config.chromeHeadless ?? false; // default from config
807
809
  }
808
810
  ctx.setStatusText('Запуск browser test...');
809
811
  // Сохраняем текущий режим Chrome, чтобы восстановить после теста
@@ -835,12 +837,14 @@ async function cmdBrowserRealTest(ctx, input) {
835
837
  const parts = input.trim().split(/\s+/).slice(1);
836
838
  const saveReport = parts.includes('--save-report');
837
839
  const headless = parts.includes('--headless');
840
+ const headed = parts.includes('--headed');
841
+ const effectiveHeadless = headed ? false : headless ? true : (ctx.config.chromeHeadless ?? false);
838
842
  const siteArgs = parts.filter(p => !p.startsWith('--'));
839
843
  const sites = siteArgs.length > 0 ? siteArgs : undefined;
840
844
  ctx.setStatusText('Real site smoke-test...');
841
845
  const prevState = chromeManager.getState();
842
846
  try {
843
- const report = await browserRealTest({ sites, headless });
847
+ const report = await browserRealTest({ sites, headless: effectiveHeadless });
844
848
  if (saveReport) {
845
849
  const { writeFile } = await import('node:fs/promises');
846
850
  await writeFile('BROWSER_REAL_TEST_REPORT.md', report, 'utf8');
@@ -865,13 +869,18 @@ async function cmdBrowserRealTest(ctx, input) {
865
869
  async function cmdChrome(ctx, input) {
866
870
  const parts = input.trim().split(/\s+/);
867
871
  const flag = parts.length > 1 ? parts[1].toLowerCase() : '';
868
- // No flag — show status
872
+ // No flag — show status only
869
873
  if (!flag || flag === '--status' || flag === '-s') {
870
874
  const state = chromeManager.getState();
871
- const modeStr = state.connected
872
- ? (state.headless ? 'headless (фоновый)' : 'headed (видимое окно)')
873
- : 'не запущен';
874
- ctx.addServiceNotice?.(`Chrome: ${modeStr}${state.connected ? ` | PID: ${state.managedProcessPid ?? '—'} | Порт: ${state.debugPort}` : ''}`);
875
+ const desiredMode = ctx.config.chromeHeadless ? 'headless' : 'headed';
876
+ const runningStr = state.connected
877
+ ? (state.headless ? 'headless' : 'headed')
878
+ : 'not running';
879
+ let notice = `Browser desired mode: ${desiredMode}\nRunning Chrome: ${runningStr}`;
880
+ if (state.connected) {
881
+ notice += ` | PID: ${state.managedProcessPid ?? '—'} | Port: ${state.debugPort}`;
882
+ }
883
+ ctx.addServiceNotice?.(notice);
875
884
  return true;
876
885
  }
877
886
  // Determine desired mode
@@ -883,21 +892,30 @@ async function cmdChrome(ctx, input) {
883
892
  desiredHeadless = false;
884
893
  }
885
894
  else {
886
- ctx.addServiceNotice?.('[err] /chrome: используйте --headed, --headless или без флага для статуса');
895
+ ctx.addServiceNotice?.('[err] /chrome: use --headed, --headless or no flag for status');
887
896
  return true;
888
897
  }
889
- try {
890
- await chromeManager.ensureMode(desiredHeadless);
891
- const state = chromeManager.getState();
892
- const modeStr = state.headless ? 'headless (фоновый)' : 'headed (видимое окно)';
893
- // Save to config
894
- ctx.config.chromeHeadless = state.headless;
895
- saveConfig({ chromeHeadless: state.headless }).catch(() => { });
896
- ctx.addServiceNotice?.(`Chrome: ${modeStr} | PID: ${state.managedProcessPid ?? '—'} | Порт: ${state.debugPort}`);
897
- }
898
- catch (err) {
899
- ctx.addServiceNotice?.(`[err] Chrome: ${String(err)}`);
900
- }
898
+ // Capture runtime state BEFORE changing the desired mode
899
+ const stateBefore = chromeManager.getState();
900
+ const wasRunning = stateBefore.connected;
901
+ const actualRunningMode = wasRunning ? (stateBefore.headless ? 'headless' : 'headed') : null;
902
+ // Set the mode WITHOUT launching the browser
903
+ chromeManager.setHeadlessMode(desiredHeadless);
904
+ // Save to config
905
+ ctx.config.chromeHeadless = desiredHeadless;
906
+ saveConfig({ chromeHeadless: desiredHeadless }).catch(() => { });
907
+ // Build status message
908
+ const desiredModeStr = desiredHeadless ? 'headless' : 'headed';
909
+ const runningStr = wasRunning ? actualRunningMode : 'not running';
910
+ let notice = `Browser desired mode: ${desiredModeStr}\nRunning Chrome: ${runningStr}`;
911
+ if (wasRunning) {
912
+ notice += ` | PID: ${stateBefore.managedProcessPid ?? '—'} | Port: ${stateBefore.debugPort}`;
913
+ }
914
+ // Warn if Chrome is running in a different mode
915
+ if (wasRunning && stateBefore.headless !== desiredHeadless) {
916
+ notice += `\n\n⚠️ Chrome is currently running in ${actualRunningMode} mode.\nNew mode will be used on next browser start.`;
917
+ }
918
+ ctx.addServiceNotice?.(notice);
901
919
  return true;
902
920
  }
903
921
  async function cmdLastBrowserTest(ctx) {
@@ -1015,32 +1033,83 @@ async function cmdBudget(ctx, input) {
1015
1033
  return true;
1016
1034
  }
1017
1035
  // ─── Changelog and Update Check ──────────────────────────────────────────────
1018
- function semverCompare(a, b) {
1019
- const pa = a.split('.').map(Number);
1020
- const pb = b.split('.').map(Number);
1021
- for (let i = 0; i < 3; i++) {
1022
- if ((pa[i] ?? 0) > (pb[i] ?? 0))
1023
- return 1;
1024
- if ((pa[i] ?? 0) < (pb[i] ?? 0))
1025
- return -1;
1026
- }
1027
- return 0;
1036
+ function parseChangelogSections(content) {
1037
+ const sections = [];
1038
+ const lines = content.split(/\r?\n/);
1039
+ let currentVersion = '';
1040
+ let currentLines = [];
1041
+ for (const line of lines) {
1042
+ const match = line.match(/^##\s+(\d+\.\d+\.\d+)/);
1043
+ if (match) {
1044
+ if (currentVersion) {
1045
+ sections.push({ version: currentVersion, content: currentLines.join('\n') });
1046
+ }
1047
+ currentVersion = match[1];
1048
+ currentLines = [line];
1049
+ }
1050
+ else if (currentVersion) {
1051
+ currentLines.push(line);
1052
+ }
1053
+ }
1054
+ // Push last section
1055
+ if (currentVersion) {
1056
+ sections.push({ version: currentVersion, content: currentLines.join('\n') });
1057
+ }
1058
+ return sections;
1028
1059
  }
1029
- async function cmdChangelog(ctx) {
1060
+ async function cmdChangelog(ctx, input) {
1030
1061
  const { readFileSync } = await import('node:fs');
1031
1062
  const { resolve, dirname } = await import('node:path');
1032
1063
  const { fileURLToPath } = await import('node:url');
1033
1064
  const __filename = fileURLToPath(import.meta.url);
1034
1065
  const __dirname = dirname(__filename);
1035
1066
  const changelogPath = resolve(__dirname, '../../CHANGELOG.md');
1067
+ const arg = input.slice('/changelog'.length).trim();
1036
1068
  try {
1037
1069
  const content = readFileSync(changelogPath, 'utf-8');
1038
- // Show up to 4000 chars to avoid overwhelming the chat
1039
- const truncated = content.length > 4000 ? content.slice(0, 3997) + '...' : content;
1040
- ctx.setMessages(prev => [...prev, {
1041
- role: 'assistant',
1042
- content: truncated,
1043
- }]);
1070
+ // full mode show entire file with 4000 char limit (current behavior)
1071
+ if (arg === 'full') {
1072
+ const truncated = content.length > 4000 ? content.slice(0, 3997) + '...' : content;
1073
+ ctx.setMessages(prev => [...prev, {
1074
+ role: 'assistant',
1075
+ content: truncated,
1076
+ }]);
1077
+ return true;
1078
+ }
1079
+ // Parse sections
1080
+ const sections = parseChangelogSections(content);
1081
+ // No arg — show only the latest release
1082
+ if (!arg) {
1083
+ if (sections.length === 0) {
1084
+ // fallback: no sections found, show whole file
1085
+ const truncated = content.length > 4000 ? content.slice(0, 3997) + '...' : content;
1086
+ ctx.setMessages(prev => [...prev, {
1087
+ role: 'assistant',
1088
+ content: truncated,
1089
+ }]);
1090
+ return true;
1091
+ }
1092
+ const latest = sections[0].content;
1093
+ ctx.setMessages(prev => [...prev, {
1094
+ role: 'assistant',
1095
+ content: latest.length > 4000 ? latest.slice(0, 3997) + '...' : latest,
1096
+ }]);
1097
+ return true;
1098
+ }
1099
+ // Try to find specific version
1100
+ const section = sections.find(s => s.version === arg);
1101
+ if (section) {
1102
+ ctx.setMessages(prev => [...prev, {
1103
+ role: 'assistant',
1104
+ content: section.content.length > 4000 ? section.content.slice(0, 3997) + '...' : section.content,
1105
+ }]);
1106
+ }
1107
+ else {
1108
+ ctx.setMessages(prev => [...prev, {
1109
+ role: 'assistant',
1110
+ content: `Changelog version not found: ${arg}`,
1111
+ }]);
1112
+ }
1044
1113
  }
1045
1114
  catch {
1046
1115
  ctx.setMessages(prev => [...prev, {
@@ -1051,79 +1120,36 @@ async function cmdChangelog(ctx) {
1051
1120
  return true;
1052
1121
  }
1053
1122
  async function cmdUpdateCheck(ctx) {
1054
- const { readFileSync } = await import('node:fs');
1055
- const { resolve, dirname } = await import('node:path');
1056
- const { fileURLToPath } = await import('node:url');
1057
- const { get } = await import('node:https');
1058
- // Get current version from package.json
1059
- const __filename = fileURLToPath(import.meta.url);
1060
- const __dirname = dirname(__filename);
1061
- const pkgPath = resolve(__dirname, '../../package.json');
1062
- let currentVersion;
1063
- try {
1064
- const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
1065
- currentVersion = pkg.version;
1066
- }
1067
- catch {
1123
+ const result = await checkLatestVersion();
1124
+ if (!result) {
1068
1125
  ctx.setMessages(prev => [...prev, {
1069
1126
  role: 'assistant',
1070
- content: 'Could not read package version. Check your installation.',
1127
+ content: 'Could not check for updates. Try again later.',
1071
1128
  }]);
1072
1129
  return true;
1073
1130
  }
1074
- // Check npm registry for latest version
1075
- try {
1076
- const latest = await new Promise((resolve, reject) => {
1077
- const url = 'https://registry.npmjs.org/@serjm%2Fdeepseek-code/latest';
1078
- const req = get(url, { timeout: 10000 }, (res) => {
1079
- let data = '';
1080
- res.on('data', chunk => { data += chunk; });
1081
- res.on('end', () => {
1082
- try {
1083
- const json = JSON.parse(data);
1084
- resolve(json.version);
1085
- }
1086
- catch {
1087
- reject(new Error('Invalid response from registry'));
1088
- }
1089
- });
1090
- });
1091
- req.on('error', reject);
1092
- req.on('timeout', () => {
1093
- req.destroy();
1094
- reject(new Error('Request timed out'));
1095
- });
1096
- });
1097
- const cmp = semverCompare(latest, currentVersion);
1098
- if (cmp > 0) {
1099
- ctx.setMessages(prev => [...prev, {
1100
- role: 'assistant',
1101
- content: [
1102
- '## Update available',
1103
- '',
1104
- `**Current:** ${currentVersion}`,
1105
- `**Latest:** ${latest}`,
1106
- '',
1107
- 'Run:',
1108
- '```',
1109
- 'npm install -g @serjm/deepseek-code@latest',
1110
- '```',
1111
- '',
1112
- 'Run `/changelog` to see what\'s new.',
1113
- ].join('\n'),
1114
- }]);
1115
- }
1116
- else {
1117
- ctx.setMessages(prev => [...prev, {
1118
- role: 'assistant',
1119
- content: `You are up to date: **${currentVersion}**`,
1120
- }]);
1121
- }
1131
+ if (result.hasUpdate) {
1132
+ ctx.setMessages(prev => [...prev, {
1133
+ role: 'assistant',
1134
+ content: [
1135
+ '## Update available',
1136
+ '',
1137
+ `**Current:** ${result.current}`,
1138
+ `**Latest:** ${result.latest}`,
1139
+ '',
1140
+ 'Run:',
1141
+ '```',
1142
+ 'npm install -g @serjm/deepseek-code@latest',
1143
+ '```',
1144
+ '',
1145
+ 'Run `/changelog` to see what\'s new.',
1146
+ ].join('\n'),
1147
+ }]);
1122
1148
  }
1123
- catch {
1149
+ else {
1124
1150
  ctx.setMessages(prev => [...prev, {
1125
1151
  role: 'assistant',
1126
- content: 'Could not check for updates. Try again later.',
1152
+ content: `You are up to date: **${result.current}**`,
1127
1153
  }]);
1128
1154
  }
1129
1155
  return true;
@@ -1160,7 +1186,7 @@ export const COMMANDS = [
1160
1186
  { name: '/last-browser-test', description: 'Show last browser test report', handler: cmdLastBrowserTest },
1161
1187
  { name: '/chrome', description: 'Chrome mode: --headed|--headless|-s', handler: cmdChrome },
1162
1188
  { name: '/budget', description: 'Budget: /budget status|off|audit|small', handler: cmdBudget },
1163
- { name: '/changelog', description: 'Show changelog', handler: cmdChangelog },
1189
+ { name: '/changelog', description: 'Show changelog: /changelog [full|version]', handler: cmdChangelog },
1164
1190
  { name: '/update-check', description: 'Check latest npm version', handler: cmdUpdateCheck },
1165
1191
  ];
1166
1192
  export const COMMAND_NAMES = COMMANDS.map(c => c.name);