@anh3d0nic/qwen-code-termux-ice 21.0.0 → 21.1.0

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/bin/qwen-ice CHANGED
@@ -1,363 +1,159 @@
1
1
  #!/data/data/com.termux/files/usr/bin/node
2
2
  /**
3
- * ❄️ Qwen Code ICE v16.0.6 Main Binary
4
- * Fixed: Auto-runs postinstall if core dir missing on fresh install
3
+ * ❄️ Qwen Code ICE — Launcher
4
+ * Wraps global Qwen Code CLI with ICE enhancements
5
5
  */
6
6
 
7
- const fs = require('fs');
8
- const path = require('path');
9
- const readline = require('readline');
10
7
  const { spawn } = require('child_process');
8
+ const path = require('path');
9
+ const fs = require('fs');
10
+ const { execSync } = require('child_process');
11
11
 
12
- const VERSION = '16.0.6';
13
- const INSTALL_DIR = process.env.HOME + '/.qwen-ice';
14
- const CONFIG_DIR = path.join(INSTALL_DIR, 'config');
15
- const MEMORY_DIR = path.join(INSTALL_DIR, 'memory');
16
- const LOGS_DIR = path.join(INSTALL_DIR, 'logs');
17
- const SCRIPTS_DIR = path.join(INSTALL_DIR, 'scripts');
18
- const CORE_DIR = path.join(INSTALL_DIR, 'core');
19
-
20
- const API_KEYS_FILE = path.join(CONFIG_DIR, 'api-keys.json');
21
- const SESSION_MEMORY_FILE = path.join(MEMORY_DIR, 'session_memory.json');
22
- const QUALITY_HISTORY_FILE = path.join(MEMORY_DIR, 'quality_history.json');
12
+ // User data directory
13
+ const HOME = process.env.HOME || '/data/data/com.termux/files/home';
14
+ const USER_ICE_DIR = path.join(HOME, '.qwen-ice-data');
15
+ const MEMORY_DIR = path.join(USER_ICE_DIR, 'memory');
23
16
  const SESSION_SUMMARY_FILE = path.join(MEMORY_DIR, 'session_summary.json');
24
- const YOLO_LOG = path.join(LOGS_DIR, 'yolo.log');
25
-
26
- // Parse CLI arguments
27
- const args = process.argv.slice(2);
28
- const yolo = args.includes('-y') || args.includes('--yolo');
29
- const versionFlag = args.includes('--version');
30
- const helpFlag = args.includes('--help');
31
- const statusFlag = args.includes('--status');
32
- const modelIndex = args.indexOf('--model');
33
- const selectedModel = modelIndex > -1 ? args[modelIndex + 1] : null;
34
-
35
- const command = args.find(arg => !arg.startsWith('-') && args.indexOf(arg) !== modelIndex && args.indexOf(arg) !== modelIndex + 1);
36
-
37
- function ensureDirs() {
38
- [CONFIG_DIR, MEMORY_DIR, LOGS_DIR].forEach(dir => {
39
- if (!fs.existsSync(dir)) {
40
- fs.mkdirSync(dir, { recursive: true });
41
- }
42
- });
43
- }
44
-
45
- function loadApiConfig() {
46
- try {
47
- if (fs.existsSync(API_KEYS_FILE)) {
48
- return JSON.parse(fs.readFileSync(API_KEYS_FILE, 'utf8'));
49
- }
50
- } catch (err) {}
51
- return {
52
- keys: [{
53
- id: 'qwen-oauth',
54
- provider: 'qwen',
55
- type: 'oauth',
56
- model: 'qwen3-coder-plus',
57
- active: true,
58
- note: 'Uses existing Qwen OAuth — do not modify'
59
- }],
60
- default_provider: 'qwen',
61
- fallback_order: ['qwen', 'gemini', 'groq', 'openai', 'dashscope']
62
- };
63
- }
17
+ const QUALITY_FILE = path.join(MEMORY_DIR, 'quality_history.json');
18
+ const MEMORY_FILE = path.join(MEMORY_DIR, 'session_memory.json');
64
19
 
65
- function loadSessionMemory() {
66
- try {
67
- if (fs.existsSync(SESSION_MEMORY_FILE)) {
68
- return JSON.parse(fs.readFileSync(SESSION_MEMORY_FILE, 'utf8'));
69
- }
70
- } catch (err) {}
71
- return { patterns: [], known_errors: [] };
72
- }
73
-
74
- function loadQualityHistory() {
75
- try {
76
- if (fs.existsSync(QUALITY_HISTORY_FILE)) {
77
- const data = JSON.parse(fs.readFileSync(QUALITY_HISTORY_FILE, 'utf8')); return Array.isArray(data) ? { scores: data } : data;
78
- }
79
- } catch (err) {}
80
- return { scores: [] };
81
- }
20
+ // Ensure user data directory exists
21
+ if (!fs.existsSync(USER_ICE_DIR)) fs.mkdirSync(USER_ICE_DIR, { recursive: true });
22
+ if (!fs.existsSync(MEMORY_DIR)) fs.mkdirSync(MEMORY_DIR, { recursive: true });
82
23
 
83
- function loadSessionSummary() {
84
- try {
85
- if (fs.existsSync(SESSION_SUMMARY_FILE)) {
86
- return JSON.parse(fs.readFileSync(SESSION_SUMMARY_FILE, 'utf8'));
87
- }
88
- } catch (err) {}
89
- return { last_session: null, total_queries: 0, total_errors: 0 };
24
+ // ICE Core Functions
25
+ function loadJson(file, defaultVal) {
26
+ try { if (fs.existsSync(file)) return JSON.parse(fs.readFileSync(file, 'utf8')); } catch (e) {}
27
+ return defaultVal;
90
28
  }
91
29
 
92
- function logYolo(message) {
93
- if (yolo) {
94
- const timestamp = new Date().toISOString();
95
- const logLine = `[${timestamp}] ${message}\n`;
96
- try { fs.appendFileSync(YOLO_LOG, logLine); } catch (e) {}
97
- }
98
- }
99
-
100
- function printVersion() {
101
- const config = loadApiConfig();
102
- const memory = loadSessionMemory();
103
- const quality = loadQualityHistory();
104
- const summary = loadSessionSummary();
105
-
106
- const providers = [...new Set((config.keys || []).map(k => k.provider))];
107
- const avgQuality = quality.scores.length > 0
108
- ? (quality.scores.reduce((a, b) => a + (b.score || b), 0) / quality.scores.length).toFixed(1)
109
- : 'N/A';
110
-
111
- console.log(`
112
- ❄️ Qwen Code ICE v${VERSION}
113
- 📍 ${INSTALL_DIR}/
114
- 🤖 Default model: ${config.keys?.find(k => k.provider === config.default_provider)?.model || 'qwen3-coder-plus'} (OAuth)
115
- 🔑 APIs configured: ${providers.join(', ')}
116
- 🔄 Fallback order: ${config.fallback_order.join(' → ')}
117
- 🧠 Memory: ${memory.patterns?.length || 0} patterns learned
118
- 📊 Avg quality: ${avgQuality}/10 (last ${quality.scores.length} sessions)
119
- 📅 Built: 2026-03-22
120
- `);
121
- }
122
-
123
- function printHelp() {
124
- console.log(`
125
- ❄️ Qwen Code ICE v${VERSION} — Help
126
-
127
- USAGE:
128
- qwen-ice Interactive mode
129
- qwen-ice "query" Single query
130
- qwen-ice [options] With options
131
-
132
- OPTIONS:
133
- --version Show version info
134
- --help Show this help
135
- --status Show pipeline status
136
- -y, --yolo YOLO mode (auto-approve all)
137
- --model <provider> Use specific model (qwen, groq, gemini, openai, dashscope)
138
-
139
- COMMANDS:
140
- status Show status (keys, models, memory)
141
- reset Clear memory (keep API keys)
142
- uninstall Clean uninstall with backup
143
-
144
- EXAMPLES:
145
- qwen-ice "fix this error" Run single query
146
- qwen-ice -y "npm install" YOLO mode (auto-approve)
147
- qwen-ice --model groq Use Groq provider
148
- qwen-ice status Check status
149
- `);
150
- }
151
-
152
- function printStatus() {
153
- const config = loadApiConfig();
154
- const memory = loadSessionMemory();
155
- const quality = loadQualityHistory();
156
- const summary = loadSessionSummary();
157
-
158
- const avgQuality = quality.scores.length > 0
159
- ? (quality.scores.reduce((a, b) => a + (b.score || b), 0) / quality.scores.length).toFixed(1)
160
- : 'N/A';
161
-
162
- const getDirSize = (dir) => {
163
- if (!fs.existsSync(dir)) return 0;
164
- let size = 0;
165
- try {
166
- const entries = fs.readdirSync(dir, { withFileTypes: true });
167
- for (const entry of entries) {
168
- const filePath = path.join(dir, entry.name);
169
- if (entry.isDirectory()) size += getDirSize(filePath);
170
- else size += fs.statSync(filePath).size;
171
- }
172
- } catch (e) {}
173
- return size;
174
- };
175
-
176
- const configSize = (getDirSize(CONFIG_DIR) / 1024).toFixed(1);
177
- const memorySize = (getDirSize(MEMORY_DIR) / 1024).toFixed(1);
178
- const logsSize = (getDirSize(LOGS_DIR) / 1024).toFixed(1);
179
-
180
- console.log(`
181
- ❄️ Qwen Code ICE v${VERSION} — Status
182
-
183
- API KEYS:
184
- `);
185
-
186
- for (const key of config.keys || []) {
187
- const status = key.active ? '✅' : '❌';
188
- const label = key.type === 'oauth' ? '(OAuth)' : '';
189
- const calls = key.call_count || 0;
190
- const lastUsed = key.last_used ? new Date(key.last_used).toLocaleDateString() : 'never';
191
- console.log(`${status} ${key.id.padEnd(15)} ${label.padEnd(8)} — active, ${calls} calls${invalidMark}`);
192
- if (key.last_used) console.log(` last used: ${lastUsed}`);
193
- }
194
-
195
- console.log(`
196
- MEMORY:
197
- 🧠 Patterns learned: ${memory.patterns?.length || 0}
198
- ⚠️ Known errors: ${memory.known_errors?.length || 0}
199
- 📊 Avg quality score: ${avgQuality}/10
200
- 🕐 Last session: ${summary.last_session || 'N/A'}
201
-
202
- INSTALL:
203
- 📁 ${INSTALL_DIR}/
204
- 💾 Config: ${configSize} KB
205
- 🗃️ Memory: ${memorySize} KB
206
- 📋 Logs: ${logsSize} KB
207
-
208
- PIPELINE:
209
- 1. ColdStartIntelligence
210
- 2. PatternPreventer
211
- 3. IntentLadder
212
- 4. ConfidenceGating
213
- 5. SmartContext
214
- 6. ApiRouter
215
- 7. SelfCritiqueLoop
216
- 8. CodeQualityGate
217
- 9. AutoRunLoop
218
- 10. QualityBlocker
219
- 11. DomainMemory
220
- 12. QualityScorer
221
- 13. ConfidenceRecord
222
- 14. SessionSummary
223
- `);
30
+ function saveJson(file, data) {
31
+ if (!fs.existsSync(path.dirname(file))) fs.mkdirSync(path.dirname(file), { recursive: true });
32
+ fs.writeFileSync(file, JSON.stringify(data, null, 2));
224
33
  }
225
34
 
226
- function runApiSetup() {
227
- if (!fs.existsSync(scriptPath)) {
228
- return;
229
- }
230
- const child = spawn('node', [scriptPath], { stdio: 'inherit' });
231
- child.on('close', (code) => process.exit(code));
232
- }
35
+ let sessionStats = { queries: 0, patternsLearned: 0, errorsFixed: 0, intentCounts: {} };
233
36
 
234
- function runUninstall() {
235
- const scriptPath = path.join(CORE_DIR, 'uninstall.js');
236
- if (!fs.existsSync(scriptPath)) {
237
- console.error('❌ uninstall.js not found. Reinstall ICE.');
238
- return;
37
+ function showWelcome() {
38
+ const memory = loadJson(MEMORY_FILE, { user_patterns: [], known_errors: [] });
39
+ const quality = loadJson(QUALITY_FILE, { history: [] });
40
+ const summary = loadJson(SESSION_SUMMARY_FILE, { total_queries: 0, avg_quality: 0 });
41
+ const patternsCount = memory.user_patterns?.length || 0;
42
+ const avgQuality = quality.history?.length > 0 ? (quality.history.reduce((a, b) => a + (b.score || b), 0) / quality.history.length).toFixed(1) : 'N/A';
43
+ const totalQueries = summary.total_queries || 0;
44
+ if (patternsCount === 0 && totalQueries === 0) {
45
+ return '\n❄️ Welcome to Qwen Code ICE\n🚀 Real Qwen Code session with ICE enhancements\n🔑 Qwen OAuth enabled by default\n';
239
46
  }
240
- const child = spawn('node', [scriptPath], { stdio: 'inherit' });
241
- child.on('close', (code) => process.exit(code));
47
+ return `\n❄️ Qwen Code ICE Session ${totalQueries + 1}\n📊 Last: ${totalQueries} queries, ${avgQuality}/10 | 🧠 ${patternsCount} patterns\n`;
242
48
  }
243
49
 
244
- function resetMemory() {
245
- console.log('❄️ Resetting memory...\n');
246
- const emptyMemory = { patterns: [], known_errors: [], last_updated: new Date().toISOString() };
247
- try {
248
- fs.writeFileSync(SESSION_MEMORY_FILE, JSON.stringify(emptyMemory, null, 2));
249
- fs.writeFileSync(QUALITY_HISTORY_FILE, JSON.stringify({ scores: [] }, null, 2));
250
- fs.writeFileSync(path.join(MEMORY_DIR, 'confidence_history.json'), JSON.stringify({ thresholds: {} }, null, 2));
251
- fs.writeFileSync(SESSION_SUMMARY_FILE, JSON.stringify({ last_session: null, total_queries: 0, total_errors: 0 }, null, 2));
252
- console.log('✅ Memory cleared (API keys preserved)');
253
- } catch (err) {
254
- console.error('❌ Failed to reset memory:', err.message);
50
+ function preventPatterns(query) {
51
+ let enhanced = query;
52
+ const warnings = [];
53
+ if (query.toLowerCase().includes('sudo')) {
54
+ enhanced = enhanced.replace(/\bsudo\b\s*/gi, '');
55
+ warnings.push('🛡️ Auto-removed sudo (Termux uses pkg)');
255
56
  }
256
- }
257
-
258
- function printColdStart() {
259
- const memory = loadSessionMemory();
260
- const quality = loadQualityHistory();
261
- const summary = loadSessionSummary();
262
-
263
- const avgQuality = quality.scores.length > 0
264
- ? (quality.scores.reduce((a, b) => a + (b.score || b), 0) / quality.scores.length).toFixed(1)
265
- : 'N/A';
266
-
267
- const patternsCount = memory.patterns?.length || 0;
268
- const providers = loadApiConfig().keys?.filter(k => k.active).length || 0;
269
-
270
- if (patternsCount === 0 && summary.total_queries === 0) {
271
- } else {
272
- console.log(`\n❄️ Qwen Code ICE v${VERSION} ready`);
273
- console.log(`🧠 ${patternsCount} patterns loaded | 📊 Avg quality: ${avgQuality}/10 | 🔑 ${providers} providers active`);
274
- console.log(`Last session: ${summary.total_queries || 0} queries, ${summary.total_errors || 0} errors\n`);
57
+ if (query.includes('/usr/bin') || query.includes('/bin/')) {
58
+ enhanced = enhanced.replace(/\/usr\/bin/g, '$PREFIX/bin').replace(/\/bin\//g, '$PREFIX/bin/');
59
+ warnings.push('🛡️ Auto-fixed paths for Termux');
275
60
  }
276
- }
277
-
278
- function getLatestIceVersion() {
279
- // Check if core directory exists, run postinstall if not
280
- if (!fs.existsSync(CORE_DIR)) {
281
- const { execSync } = require('child_process');
282
- execSync('node ' + path.join(__dirname, '../scripts/postinstall.cjs'), { stdio: 'inherit' });
61
+ if (query.includes('apt-get')) {
62
+ enhanced = enhanced.replace(/apt-get/g, 'pkg');
63
+ warnings.push('🛡️ Replaced apt-get with pkg');
283
64
  }
284
- const versions = fs.readdirSync(CORE_DIR)
285
- .filter(f => f.startsWith('ice-v') && f.endsWith('.js'))
286
- .sort();
287
- if (!versions || versions.length === 0) {
288
- console.error('ICE core not found. Run: node scripts/postinstall.cjs');
289
- process.exit(1);
65
+ return { enhanced, warnings };
66
+ }
67
+
68
+ function analyzeIntent(query) {
69
+ let real = 'assistance';
70
+ if (query.includes('how to')) real = 'step-by-step';
71
+ else if (query.includes('fix') || query.includes('error')) real = 'working solution';
72
+ else if (query.includes('write') || query.includes('create')) real = 'code implementation';
73
+ let deep = 'Complete request successfully';
74
+ if (query.includes('fix') || query.includes('error')) deep = 'stable production code';
75
+ else if (query.includes('write') || query.includes('create')) deep = 'ship working feature';
76
+ if (!sessionStats.intentCounts[deep]) sessionStats.intentCounts[deep] = 0;
77
+ sessionStats.intentCounts[deep]++;
78
+ return { surface: query, real, deep };
79
+ }
80
+
81
+ function saveSessionSummary() {
82
+ const quality = loadJson(QUALITY_FILE, { history: [] });
83
+ const avgQuality = quality.history?.length > 0 ? (quality.history.reduce((a, b) => a + (b.score || b), 0) / quality.history.length).toFixed(1) : '0';
84
+ const topIntent = Object.entries(sessionStats.intentCounts).sort((a, b) => b[1] - a[1])[0]?.[0] || 'assistance';
85
+ const summary = {
86
+ date: new Date().toISOString(),
87
+ total_queries: sessionStats.queries,
88
+ avg_quality: parseFloat(avgQuality),
89
+ patterns_learned: sessionStats.patternsLearned,
90
+ errors_fixed: sessionStats.errorsFixed,
91
+ top_intent: topIntent
92
+ };
93
+ saveJson(SESSION_SUMMARY_FILE, summary);
94
+ console.log('\n📊 Session Summary:');
95
+ console.log(` Queries: ${summary.total_queries}`);
96
+ console.log(` Avg Quality: ${summary.avg_quality}/10`);
97
+ console.log(` Top Intent: ${summary.top_intent}\n`);
98
+ }
99
+
100
+ // Show ICE welcome
101
+ console.log(showWelcome());
102
+
103
+ // Get user query
104
+ const userQuery = process.argv.slice(2).join(' ');
105
+ let finalQuery = userQuery;
106
+
107
+ if (userQuery) {
108
+ // Apply ICE pre-processing
109
+ const patternResult = preventPatterns(userQuery);
110
+ finalQuery = patternResult.enhanced;
111
+ if (patternResult.warnings.length > 0) {
112
+ console.log('🛡️ ICE Pattern Prevention:');
113
+ patternResult.warnings.forEach(w => console.log(' ' + w));
114
+ console.log();
290
115
  }
291
- return versions[versions.length - 1] || 'ice-v15.js';
116
+ const intent = analyzeIntent(patternResult.enhanced);
117
+ console.log('🎯 ICE Intent Analysis:');
118
+ console.log(` Surface: "${intent.surface}"`);
119
+ console.log(` Real Need: ${intent.real}`);
120
+ console.log(` Deep Goal: ${intent.deep}`);
121
+ console.log();
292
122
  }
293
123
 
294
- async function processQuery(query) {
295
- try {
296
- // Dynamically load latest ICE version
297
- const latest = getLatestIceVersion();
298
- const iceModule = require(path.join(CORE_DIR, latest));
299
- const IceV16 = iceModule.IceV16 || iceModule.UnifiedPipeline;
300
- const ice = new IceV16({ yolo, model: selectedModel });
301
- return ice.process(query);
302
- } catch (err) {
303
- return { error: true, message: `Failed to process query: ${err.message}` };
304
- }
124
+ // Find global Qwen Code CLI
125
+ let qwenCliPath;
126
+ try {
127
+ qwenCliPath = execSync('which qwen', { encoding: 'utf8' }).trim();
128
+ } catch (e) {
129
+ qwenCliPath = '/data/data/com.termux/files/usr/lib/node_modules/@mmmbuto/qwen-code-termux/cli.js';
305
130
  }
306
131
 
307
- async function interactiveMode() {
308
- printColdStart();
309
- if (yolo) console.log(' YOLO MODE: All actions auto-approved\n');
310
-
311
- const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
312
-
313
- const ask = () => {
314
- rl.question('> ', (query) => {
315
- if (query.toLowerCase() === 'exit' || query.toLowerCase() === 'quit') {
316
- console.log('👋 Goodbye!');
317
- rl.close();
318
- return;
319
- }
320
- logYolo(`Query: ${query}`);
321
- processQuery(query).then(result => {
322
- if (result.error) console.log(`❌ ${result.message}`);
323
- else console.log(result.content);
324
- ask();
325
- });
326
- });
327
- };
328
-
329
- ask();
132
+ if (!fs.existsSync(qwenCliPath)) {
133
+ console.error('❌ Error: Qwen Code CLI not found');
134
+ console.error(' Please install: npm install -g @mmmbuto/qwen-code-termux');
135
+ process.exit(1);
330
136
  }
331
137
 
332
- async function singleQuery(query) {
333
- if (yolo) console.log(' YOLO MODE: All actions auto-approved\n');
334
- logYolo(`Query: ${query}`);
335
- const result = await processQuery(query);
336
- if (result.error) {
337
- console.log(`❌ ${result.message}`);
338
- process.exit(1);
339
- } else {
340
- console.log(result.content);
138
+ // Spawn Qwen Code CLI with ICE enhancements
139
+ const args = finalQuery ? ['-p', finalQuery] : [];
140
+ const qwen = spawn(process.execPath, [qwenCliPath, ...args], {
141
+ stdio: 'inherit',
142
+ env: {
143
+ ...process.env,
144
+ ICE_ENABLED: 'true',
145
+ ICE_SESSION_ID: Date.now().toString()
341
146
  }
342
- }
343
-
344
- async function main() {
345
- ensureDirs();
346
-
347
- if (versionFlag) { printVersion(); process.exit(0); }
348
- if (helpFlag) { printHelp(); process.exit(0); }
349
- if (statusFlag) { printStatus(); process.exit(0); }
350
-
351
- if (command === 'status' || args[0] === 'status') { printStatus(); process.exit(0); }
352
- if (command === 'reset' || args[0] === 'reset') { resetMemory(); process.exit(0); }
353
- if (command === 'uninstall' || args[0] === 'uninstall') { runUninstall(); return; }
147
+ });
354
148
 
355
- const query = command || args.find(arg => !arg.startsWith('-'));
356
- if (query) await singleQuery(query);
357
- else await interactiveMode();
358
- }
149
+ qwen.on('close', (code) => {
150
+ sessionStats.queries++;
151
+ saveSessionSummary();
152
+ process.exit(code);
153
+ });
359
154
 
360
- main().catch(err => {
361
- console.error('❌ Error:', err.message);
155
+ qwen.on('error', (err) => {
156
+ console.error('❌ Failed to launch Qwen Code:', err.message);
157
+ console.error(' CLI Path:', qwenCliPath);
362
158
  process.exit(1);
363
159
  });
@@ -0,0 +1,101 @@
1
+ /**
2
+ * ❄️ ICE Core - CommonJS Module
3
+ */
4
+ const fs = require('fs');
5
+ const path = require('path');
6
+
7
+ const HOME = process.env.HOME || '/data/data/com.termux/files/home';
8
+ const ICE_DIR = path.join(HOME, '.qwen-ice');
9
+ const MEMORY_DIR = path.join(ICE_DIR, 'memory');
10
+
11
+ const FILES = {
12
+ sessionMemory: path.join(MEMORY_DIR, 'session_memory.json'),
13
+ qualityHistory: path.join(MEMORY_DIR, 'quality_history.json'),
14
+ sessionSummary: path.join(MEMORY_DIR, 'session_summary.json'),
15
+ };
16
+
17
+ const TERMUX = { PREFIX: '/data/data/com.termux/files/usr', HOME: HOME };
18
+
19
+ let sessionStats = { queries: 0, errorsFixed: 0, patternsLearned: 0, intentCounts: {}, sessionId: Date.now() };
20
+
21
+ function loadJson(file, defaultVal) {
22
+ try { if (fs.existsSync(file)) return JSON.parse(fs.readFileSync(file, 'utf8')); } catch (e) {}
23
+ return defaultVal;
24
+ }
25
+
26
+ function saveJson(file, data) {
27
+ const dir = path.dirname(file);
28
+ if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
29
+ fs.writeFileSync(file, JSON.stringify(data, null, 2));
30
+ }
31
+
32
+ function showColdStartWelcome() {
33
+ const memory = loadJson(FILES.sessionMemory, { user_patterns: [], known_errors: [] });
34
+ const quality = loadJson(FILES.qualityHistory, { history: [] });
35
+ const summary = loadJson(FILES.sessionSummary, { total_queries: 0, avg_quality: 0 });
36
+ const patternsCount = memory.user_patterns?.length || 0;
37
+ const avgQuality = quality.history?.length > 0 ? (quality.history.reduce((a, b) => a + (b.score || b), 0) / quality.history.length).toFixed(1) : 'N/A';
38
+ const totalQueries = summary.total_queries || 0;
39
+ if (patternsCount === 0 && totalQueries === 0) return '\n❄️ Welcome to Qwen Code ICE v21.0.1\n🔑 Qwen OAuth enabled by default\n';
40
+ return `\n❄️ Qwen Code ICE v21.0.1\n📊 Last: ${totalQueries} queries, ${avgQuality}/10 | 🧠 ${patternsCount} patterns\n`;
41
+ }
42
+
43
+ function preventPatterns(query) {
44
+ let enhancedQuery = query;
45
+ const warnings = [];
46
+ const preAppliedFixes = [];
47
+ if (query.toLowerCase().includes('sudo')) {
48
+ enhancedQuery = enhancedQuery.replace(/\bsudo\b\s*/gi, '');
49
+ warnings.push('⚠️ Auto-removed sudo');
50
+ preAppliedFixes.push('sudo_stripped');
51
+ }
52
+ if (query.includes('/usr/bin') || query.includes('/bin/')) {
53
+ enhancedQuery = enhancedQuery.replace(/\/usr\/bin/g, TERMUX.PREFIX + '/bin').replace(/\/bin\//g, TERMUX.PREFIX + '/bin/');
54
+ warnings.push('⚠️ Auto-fixed paths');
55
+ preAppliedFixes.push('paths_fixed');
56
+ }
57
+ return { enhancedQuery, warnings, preAppliedFixes };
58
+ }
59
+
60
+ function analyzeIntent(query) {
61
+ const surface = query;
62
+ let real = 'assistance';
63
+ if (surface.includes('how to')) real = 'step-by-step';
64
+ else if (surface.includes('fix') || surface.includes('error')) real = 'working solution';
65
+ else if (surface.includes('write') || surface.includes('create')) real = 'code implementation';
66
+ let deep = real;
67
+ if (surface.includes('fix') || surface.includes('error')) deep = 'stable production code';
68
+ else if (surface.includes('write') || surface.includes('create')) deep = 'ship working feature';
69
+ if (!sessionStats.intentCounts[deep]) sessionStats.intentCounts[deep] = 0;
70
+ sessionStats.intentCounts[deep]++;
71
+ return { surface, real, deep };
72
+ }
73
+
74
+ function scoreResponse(response, codeSuccess) {
75
+ let score = 5;
76
+ if (codeSuccess) score += 3;
77
+ const tokenCount = Math.ceil(response.length / 4);
78
+ if (tokenCount < 200) score += 1;
79
+ if (!response.includes('sudo') && !response.includes('/usr/bin/')) score += 1;
80
+ score = Math.max(1, Math.min(10, score));
81
+ const history = loadJson(FILES.qualityHistory, { history: [] });
82
+ history.history.push({ score, timestamp: Date.now() });
83
+ if (history.history.length > 20) history.history.shift();
84
+ saveJson(FILES.qualityHistory, history);
85
+ return score;
86
+ }
87
+
88
+ function saveSessionSummary() {
89
+ const quality = loadJson(FILES.qualityHistory, { history: [] });
90
+ const avgQuality = quality.history?.length > 0 ? (quality.history.reduce((a, b) => a + (b.score || b), 0) / quality.history.length).toFixed(1) : '0';
91
+ const topIntent = Object.entries(sessionStats.intentCounts).sort((a, b) => b[1] - a[1])[0]?.[0] || 'assistance';
92
+ const summary = { date: new Date().toISOString(), total_queries: sessionStats.queries, avg_quality: parseFloat(avgQuality), patterns_learned: sessionStats.patternsLearned, errors_fixed: sessionStats.errorsFixed, top_intent: topIntent };
93
+ saveJson(FILES.sessionSummary, summary);
94
+ console.log('\n📊 Session:', summary.total_queries, 'queries,', summary.avg_quality + '/10 avg\n');
95
+ }
96
+
97
+ function getSessionStats() { return sessionStats; }
98
+ function incrementQueries() { sessionStats.queries++; }
99
+ function incrementErrorsFixed(count) { sessionStats.errorsFixed += count; }
100
+
101
+ module.exports = { showColdStartWelcome, preventPatterns, analyzeIntent, scoreResponse, saveSessionSummary, getSessionStats, incrementQueries, incrementErrorsFixed, loadJson, saveJson, TERMUX, FILES };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@anh3d0nic/qwen-code-termux-ice",
3
- "version": "21.0.0",
3
+ "version": "21.1.0",
4
4
  "description": "Qwen Code ICE v16.0.5 — Compounding Intelligence with proper npm install structure",
5
5
  "main": "core/ice-v16.js",
6
6
  "bin": {
@@ -43,6 +43,7 @@
43
43
  ],
44
44
  "preferGlobal": true,
45
45
  "files": [
46
+ "cli-dist/",
46
47
  "bin/",
47
48
  "core/",
48
49
  "config/",
@@ -1,234 +1,107 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
- * ❄️ ICE v15.0.0 — Postinstall Script
4
- * Runs automatically after: npm install -g @anh3d0nic/qwen-code-termux-ice
5
- *
6
- * Creates ~/.qwen-ice/ directory structure and copies ICE scripts
3
+ * ❄️ ICE Postinstall Script
4
+ * Sets up self-contained Qwen Code ICE installation
7
5
  */
8
6
 
9
7
  const fs = require('fs');
10
8
  const path = require('path');
11
9
 
12
- // Paths
13
10
  const HOME = process.env.HOME || '/data/data/com.termux/files/home';
14
- const USER_QWEN_DIR = path.join(HOME, '.qwen');
15
- const INSTALL_DIR = path.join(HOME, '.qwen-ice');
16
- const PACKAGE_DIR = __dirname + '/..';
11
+ const PREFIX = process.env.PREFIX || '/data/data/com.termux/files/usr';
12
+ const PACKAGE_DIR = path.join(__dirname, '..');
13
+ const INSTALL_DIR = path.join(HOME, '.qwen-ice-package');
14
+ const CLI_DIST_DIR = path.join(INSTALL_DIR, 'cli-dist');
17
15
 
18
- // Directories to create
19
- const DIRS = [
20
- INSTALL_DIR,
21
- path.join(INSTALL_DIR, 'core'),
22
- path.join(INSTALL_DIR, 'bin'),
23
- path.join(INSTALL_DIR, 'memory'),
24
- path.join(INSTALL_DIR, 'sessions'),
25
- path.join(INSTALL_DIR, 'config'),
26
- path.join(INSTALL_DIR, 'logs')
27
- ];
28
-
29
- // Files to copy from package to install dir
30
- const FILES_TO_COPY = [
31
- { src: 'scripts/ice-v15.js', dest: 'core/ice-v15.js' },
32
- { src: 'scripts/api-caller.js', dest: 'core/api-caller.js' },
33
- { src: 'scripts/ice-v14.js', dest: 'core/ice-v14.js' },
34
- { src: 'scripts/ice-v13.js', dest: 'core/ice-v13.js' },
35
- { src: 'bin/qwen-ice', dest: 'bin/qwen-ice' },
36
- ];
37
-
38
- // JSON files to preserve from ~/.qwen/
39
- const JSON_FILES_TO_PRESERVE = [
40
- 'session_memory.json',
41
- 'quality_history.json',
42
- 'confidence_history.json',
43
- 'session_summary.json',
44
- 'trend_history.json',
45
- 'ice_session.json',
46
- 'ice_active_context.json'
47
- ];
16
+ console.log('\n❄️ Qwen Code ICE — Postinstall\n');
48
17
 
49
- function ensureDir(dir) {
50
- if (!fs.existsSync(dir)) {
51
- fs.mkdirSync(dir, { recursive: true });
52
- console.log(`✅ Created ${dir}`);
53
- }
18
+ // Create installation directory
19
+ if (!fs.existsSync(INSTALL_DIR)) {
20
+ fs.mkdirSync(INSTALL_DIR, { recursive: true });
21
+ console.log('✅ Created', INSTALL_DIR);
54
22
  }
55
23
 
56
- function copyFile(src, dest) {
57
- const srcPath = path.join(PACKAGE_DIR, src);
58
- const destPath = path.join(INSTALL_DIR, dest);
59
-
60
- if (fs.existsSync(srcPath)) {
61
- const content = fs.readFileSync(srcPath, 'utf8');
62
-
63
- // Ensure correct shebang for Termux
64
- let fixedContent = content;
65
- if (content.startsWith('#!')) {
66
- const lines = content.split('\n');
67
- if (lines[0].includes('/usr/bin/env node') || lines[0].includes('/usr/bin/node')) {
68
- lines[0] = '#!/data/data/com.termux/files/usr/bin/node';
69
- fixedContent = lines.join('\n');
24
+ // Copy CLI dist
25
+ const srcCliDist = path.join(PACKAGE_DIR, 'packages', 'cli', 'dist');
26
+ if (fs.existsSync(srcCliDist)) {
27
+ const destCliDist = path.join(INSTALL_DIR, 'cli-dist');
28
+ if (!fs.existsSync(destCliDist)) fs.mkdirSync(destCliDist, { recursive: true });
29
+
30
+ // Copy files recursively
31
+ function copyDir(src, dest) {
32
+ if (!fs.existsSync(dest)) fs.mkdirSync(dest, { recursive: true });
33
+ const entries = fs.readdirSync(src, { withFileTypes: true });
34
+ for (const entry of entries) {
35
+ const srcPath = path.join(src, entry.name);
36
+ const destPath = path.join(dest, entry.name);
37
+ if (entry.isDirectory()) {
38
+ copyDir(srcPath, destPath);
39
+ } else {
40
+ fs.copyFileSync(srcPath, destPath);
70
41
  }
71
42
  }
72
-
73
- fs.writeFileSync(destPath, fixedContent);
74
- fs.chmodSync(destPath, 0o755);
75
- console.log(`✅ Copied ${src} → ${dest}`);
76
- } else {
77
- console.log(`⚠️ Source not found: ${srcPath}`);
78
43
  }
79
- }
80
-
81
- function preserveUserMemory() {
82
- let preserved = 0;
83
44
 
84
- JSON_FILES_TO_PRESERVE.forEach(file => {
85
- const srcPath = path.join(USER_QWEN_DIR, file);
86
- const destPath = path.join(INSTALL_DIR, 'memory', file);
87
-
88
- if (fs.existsSync(srcPath)) {
89
- try {
90
- const content = fs.readFileSync(srcPath, 'utf8');
91
- // Validate JSON
92
- JSON.parse(content);
93
- fs.writeFileSync(destPath, content);
94
- console.log(`✅ Preserved ${file}`);
95
- preserved++;
96
- } catch (e) {
97
- console.log(`⚠️ Corrupted ${file}, skipping`);
98
- }
99
- }
100
- });
101
-
102
- return preserved;
45
+ copyDir(srcCliDist, destCliDist);
46
+ console.log('✅ Copied Qwen Code CLI to cli-dist/');
47
+ } else {
48
+ console.log('⚠️ CLI dist not found at', srcCliDist);
103
49
  }
104
50
 
105
- function createDefaultConfigFiles() {
106
- const configDir = path.join(INSTALL_DIR, 'config');
107
- const memoryDir = path.join(INSTALL_DIR, 'memory');
108
-
109
- // Default API keys config
110
- const apiKeysFile = path.join(configDir, 'api-keys.json');
111
- if (!fs.existsSync(apiKeysFile)) {
112
- const defaultKeys = {
113
- keys: [
114
- {
115
- id: 'qwen-oauth',
116
- provider: 'qwen',
117
- type: 'oauth',
118
- model: 'qwen3-coder-plus',
119
- active: true,
120
- note: 'Uses existing Qwen OAuth — do not modify'
121
- }
122
- ],
123
- default_provider: 'qwen',
124
- fallback_order: ['qwen', 'gemini', 'groq', 'openai', 'dashscope']
125
- };
126
- fs.writeFileSync(apiKeysFile, JSON.stringify(defaultKeys, null, 2));
127
- console.log('✅ Created config/api-keys.json');
128
- }
129
-
130
- // Default settings
131
- const settingsFile = path.join(configDir, 'settings.json');
132
- if (!fs.existsSync(settingsFile)) {
133
- const defaultSettings = {
134
- version: '15.0.0',
135
- theme: 'default',
136
- yolo_mode: false,
137
- auto_approve_commands: [],
138
- max_context_length: 8192,
139
- quality_threshold: 5,
140
- created_at: new Date().toISOString()
141
- };
142
- fs.writeFileSync(settingsFile, JSON.stringify(defaultSettings, null, 2));
143
- console.log('✅ Created config/settings.json');
144
- }
145
-
146
- // Default memory files if not preserved
147
- const memoryFiles = {
148
- 'session_memory.json': { user_patterns: [], known_errors: [], project_state: {}, user_preferences: [] },
149
- 'quality_history.json': { history: [] },
150
- 'confidence_history.json': { history: [] },
151
- 'session_summary.json': { total_queries: 0, avg_quality: 0, patterns_learned: 0, errors_fixed: 0, top_intent: 'none' },
152
- 'trend_history.json': { trends: [], alerts: [] }
153
- };
154
-
155
- Object.entries(memoryFiles).forEach(([file, data]) => {
156
- const filePath = path.join(memoryDir, file);
157
- if (!fs.existsSync(filePath)) {
158
- fs.writeFileSync(filePath, JSON.stringify(data, null, 2));
159
- }
160
- });
51
+ // Create user data directory
52
+ const userDataDir = path.join(HOME, '.qwen-ice-data');
53
+ const memoryDir = path.join(userDataDir, 'memory');
54
+ if (!fs.existsSync(memoryDir)) {
55
+ fs.mkdirSync(memoryDir, { recursive: true });
56
+ console.log('✅ Created user data directory');
161
57
  }
162
58
 
163
- function createSymlinks() {
164
- const termuxPrefix = '/data/data/com.termux/files/usr';
165
- const binDir = path.join(termuxPrefix, 'bin');
166
-
167
- // Ensure bin directory exists
168
- if (!fs.existsSync(binDir)) {
169
- try {
170
- fs.mkdirSync(binDir, { recursive: true });
171
- } catch (e) {
172
- console.log(`⚠️ Cannot create ${binDir}, skipping symlinks`);
173
- return;
174
- }
175
- }
176
-
177
- // Create symlinks
178
- const symlinks = [
179
- { target: path.join(INSTALL_DIR, 'bin', 'qwen-ice'), link: path.join(binDir, 'qwen-ice') },
180
- ];
181
-
182
- symlinks.forEach(({ target, link }) => {
183
- try {
184
- if (fs.existsSync(link)) {
185
- fs.unlinkSync(link);
186
- }
187
- fs.symlinkSync(target, link);
188
- console.log(`✅ Symlink: ${link} → ${target}`);
189
- } catch (e) {
190
- console.log(`⚠️ Failed to create symlink ${link}: ${e.message}`);
59
+ // Create default config
60
+ const configFile = path.join(userDataDir, 'config.json');
61
+ if (!fs.existsSync(configFile)) {
62
+ const defaultConfig = {
63
+ provider: 'qwen',
64
+ model: 'qwen3-coder-plus',
65
+ oauth_enabled: true,
66
+ ice_enhancements: {
67
+ pattern_prevention: true,
68
+ intent_analysis: true,
69
+ session_summary: true
191
70
  }
192
- });
71
+ };
72
+ fs.writeFileSync(configFile, JSON.stringify(defaultConfig, null, 2));
73
+ console.log('✅ Created default config');
193
74
  }
194
75
 
195
- function main() {
196
- console.log('\n❄️ Qwen Code ICE v15.0.0 — Installing...\n');
197
-
198
- // Step 1: Create directories
199
- DIRS.forEach(ensureDir);
200
-
201
- // Step 2: Copy ICE scripts
202
- FILES_TO_COPY.forEach(({ src, dest }) => copyFile(src, dest));
203
-
204
- // Step 3: Preserve user memory
205
- const preserved = preserveUserMemory();
206
- if (preserved === 0) {
207
- console.log('ℹ️ No existing memory files found, creating defaults');
208
- }
209
-
210
- // Step 4: Create default config files
211
- createDefaultConfigFiles();
212
-
213
- // Step 5: Create symlinks
214
- createSymlinks();
215
-
216
- // Final message
217
- console.log('\n' + '='.repeat(60));
218
- console.log('✅ ICE v15.0.0 installed to ~/.qwen-ice');
219
- console.log('='.repeat(60));
220
- console.log('\n🚀 Commands:');
221
- console.log(' qwen-ice — Interactive mode');
222
- console.log(' qwen-ice "query" — Single query');
223
- console.log(' qwen-ice --version — Show version');
224
- console.log(' qwen-ice --help — Show help');
225
-
226
- console.log(' qwen-ice exit — Show session summary\n');
76
+ // Create symlinks
77
+ const binDir = path.join(PREFIX, 'bin');
78
+ if (!fs.existsSync(binDir)) {
79
+ try { fs.mkdirSync(binDir, { recursive: true }); } catch (e) {}
227
80
  }
228
81
 
229
- // Run if called directly
230
- if (require.main === module) {
231
- main();
82
+ const symlinks = [
83
+ { target: path.join(PACKAGE_DIR, 'bin', 'qwen-ice'), link: path.join(binDir, 'qwen-ice') }
84
+ ];
85
+
86
+ for (const { target, link } of symlinks) {
87
+ try {
88
+ if (fs.existsSync(link)) fs.unlinkSync(link);
89
+ fs.symlinkSync(target, link);
90
+ console.log('✅ Symlink:', link);
91
+ } catch (e) {
92
+ console.log('⚠️ Symlink failed:', e.message);
93
+ }
232
94
  }
233
95
 
234
- module.exports = { main };
96
+ console.log('\n' + '='.repeat(60));
97
+ console.log('✅ Qwen Code ICE installed successfully');
98
+ console.log('='.repeat(60));
99
+ console.log('\n🚀 Usage:');
100
+ console.log(' qwen-ice # Interactive session');
101
+ console.log(' qwen-ice "your query" # Single query');
102
+ console.log(' qwen-ice --version # Show version');
103
+ console.log(' qwen-ice --help # Show help');
104
+ console.log('\n📁 Installation:');
105
+ console.log(' Package:', INSTALL_DIR);
106
+ console.log(' User Data:', userDataDir);
107
+ console.log();