@link-assistant/hive-mind 1.72.1 → 1.72.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,19 @@
1
1
  # @link-assistant/hive-mind
2
2
 
3
+ ## 1.72.3
4
+
5
+ ### Patch Changes
6
+
7
+ - 502e78f: Use `lino-i18n` for Hive Mind translation loading and runtime lookup.
8
+
9
+ ## 1.72.2
10
+
11
+ ### Patch Changes
12
+
13
+ - 055a1a0: Fix `--auto-attach-solution-summary` so Codex-authored comments that use the
14
+ visible "Working session summary" heading are counted as AI comments instead of
15
+ being mistaken for hive-mind's automated summary comment.
16
+
3
17
  ## 1.72.1
4
18
 
5
19
  ### Patch Changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@link-assistant/hive-mind",
3
- "version": "1.72.1",
3
+ "version": "1.72.3",
4
4
  "description": "AI-powered issue solver and hive mind for collaborative problem solving",
5
5
  "main": "src/hive.mjs",
6
6
  "type": "module",
@@ -76,6 +76,7 @@
76
76
  "dayjs": "^1.11.19",
77
77
  "decimal.js-light": "^2.5.1",
78
78
  "lino-arguments": "^0.3.0",
79
+ "lino-i18n": "^0.1.1",
79
80
  "lino-objects-codec": "^0.3.6",
80
81
  "secretlint": "^11.2.5",
81
82
  "semver": "^7.7.3",
package/src/i18n.lib.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  // i18n module for hive-mind.
2
2
  // - Translation files live in src/locales/<locale>.lino and are stored
3
- // in Links Notation, parsed via lino-objects-codec.
3
+ // in Links Notation, parsed and resolved via lino-i18n.
4
4
  // - Supported locales: en (default fallback), ru, zh, hi.
5
5
  // - Two locale tracks: ui (user-facing strings) and work (AI prompts /
6
6
  // tool preferred language). Both default to the value of --language.
@@ -9,23 +9,31 @@
9
9
  // normalizeLocale, getUserLocale, setUserLocale, clearUserLocale,
10
10
  // resolveLocaleFromTelegramCtx.
11
11
 
12
- import { promises as fs } from 'fs';
13
12
  import path from 'path';
14
13
  import { fileURLToPath } from 'url';
15
- import { parseIndented } from 'lino-objects-codec';
14
+ import { createI18n } from 'lino-i18n';
15
+ import { loadLocalesFromFile } from 'lino-i18n/loaders';
16
16
 
17
17
  const __filename = fileURLToPath(import.meta.url);
18
18
  const __dirname = path.dirname(__filename);
19
19
 
20
20
  const DEFAULT_LOCALE = 'en';
21
21
  const SUPPORTED_LOCALES = ['en', 'ru', 'zh', 'hi'];
22
+ const LINO_COMPATIBILITY_ALIASES = ['collapseTail', 'parentLabel'];
22
23
 
23
24
  const localeCache = new Map(); // locale -> { key: string }
24
25
  const userLocales = new Map(); // userId/chatId -> locale (in-memory)
26
+ const localesDir = path.join(__dirname, 'locales');
25
27
 
26
28
  let currentUiLocale = DEFAULT_LOCALE;
27
29
  let currentWorkLocale = DEFAULT_LOCALE;
28
30
  let fallbackLoaded = false;
31
+ let i18n = createI18n({
32
+ locales: {},
33
+ defaultLocale: DEFAULT_LOCALE,
34
+ fallback: [DEFAULT_LOCALE],
35
+ compatibilityAliases: LINO_COMPATIBILITY_ALIASES,
36
+ });
29
37
 
30
38
  export function getSupportedLocales() {
31
39
  return [...SUPPORTED_LOCALES];
@@ -46,31 +54,22 @@ export function detectLocale() {
46
54
  }
47
55
 
48
56
  async function readLocaleFile(locale) {
49
- const localesDir = path.join(__dirname, 'locales');
50
57
  const linoFile = path.join(localesDir, `${locale}.lino`);
51
- const data = await fs.readFile(linoFile, 'utf-8');
52
- return parseIndentedToFlatMap(data);
53
- }
54
-
55
- // parseIndented returns { id, obj } where obj is the key->value map.
56
- // Some keys contain dots (e.g., error.invalid_github_url). The parser
57
- // supports them when the key is a plain reference (no spaces/quotes).
58
- function unescapeString(s) {
59
- // Convert literal escape sequences (e.g., "\n" inside a quoted string in
60
- // Links Notation) into the corresponding JS characters. This keeps the
61
- // .lino files single-line and human-friendly.
62
- return s.replace(/\\n/g, '\n').replace(/\\t/g, '\t').replace(/\\r/g, '\r').replace(/\\\\/g, '\\');
63
- }
64
-
65
- function parseIndentedToFlatMap(text) {
66
- const parsed = parseIndented({ text });
67
- // parsed: { id: <localeName>, obj: { key: value, ... } }
68
- if (!parsed || !parsed.obj) return {};
69
- const out = {};
70
- for (const [k, v] of Object.entries(parsed.obj)) {
71
- out[k] = typeof v === 'string' ? unescapeString(v) : String(v);
72
- }
73
- return out;
58
+ const loaded = await loadLocalesFromFile(linoFile, {
59
+ compatibilityAliases: LINO_COMPATIBILITY_ALIASES,
60
+ });
61
+ const match = loaded.find(catalogue => catalogue.locale === locale) || loaded[0];
62
+ return match?.translations || {};
63
+ }
64
+
65
+ function refreshI18nRuntime() {
66
+ i18n = createI18n({
67
+ locales: Object.fromEntries(localeCache.entries()),
68
+ defaultLocale: currentUiLocale,
69
+ fallback: [DEFAULT_LOCALE],
70
+ compatibilityAliases: LINO_COMPATIBILITY_ALIASES,
71
+ });
72
+ i18n.setLocale(currentUiLocale);
74
73
  }
75
74
 
76
75
  export async function loadTranslations(locale) {
@@ -83,6 +82,7 @@ export async function loadTranslations(locale) {
83
82
  translations = {};
84
83
  }
85
84
  localeCache.set(locale, translations);
85
+ refreshI18nRuntime();
86
86
 
87
87
  // Always have the fallback (English) ready
88
88
  if (!fallbackLoaded && locale !== DEFAULT_LOCALE) {
@@ -93,6 +93,7 @@ export async function loadTranslations(locale) {
93
93
  localeCache.set(DEFAULT_LOCALE, {});
94
94
  }
95
95
  fallbackLoaded = true;
96
+ refreshI18nRuntime();
96
97
  } else if (locale === DEFAULT_LOCALE) {
97
98
  fallbackLoaded = true;
98
99
  }
@@ -157,10 +158,8 @@ export function t(key, params = {}, options = {}) {
157
158
  } else {
158
159
  locale = currentUiLocale;
159
160
  }
160
- const main = localeCache.get(locale) || {};
161
- const fallback = localeCache.get(DEFAULT_LOCALE) || {};
162
- const value = main[key] ?? fallback[key] ?? key;
163
- return applyParams(value, params);
161
+ const value = i18n.t(key, params, { ...options, locale });
162
+ return typeof value === 'string' ? value : applyParams(String(value), params);
164
163
  }
165
164
 
166
165
  // Convenience helper for work-language strings (AI prompts).
@@ -185,12 +184,16 @@ export function setLocale(locale) {
185
184
  if (normalized) {
186
185
  currentUiLocale = normalized;
187
186
  currentWorkLocale = normalized;
187
+ i18n.setLocale(normalized);
188
188
  }
189
189
  }
190
190
 
191
191
  export function setUiLocale(locale) {
192
192
  const normalized = normalizeLocale(locale);
193
- if (normalized) currentUiLocale = normalized;
193
+ if (normalized) {
194
+ currentUiLocale = normalized;
195
+ i18n.setLocale(normalized);
196
+ }
194
197
  }
195
198
 
196
199
  export function setWorkLocale(locale) {