@mmmbuto/nexuscli 0.9.6 → 0.9.7-001-termux

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.
@@ -3,6 +3,13 @@
3
3
  * Phase 7 - Testing & Deployment
4
4
  */
5
5
 
6
+ const fs = require('fs');
7
+ const path = require('path');
8
+ const os = require('os');
9
+
10
+ const TEST_HOME = fs.mkdtempSync(path.join(os.tmpdir(), 'nexuscli-home-'));
11
+ process.env.HOME = TEST_HOME;
12
+
6
13
  const WorkspaceManager = require('../services/workspace-manager');
7
14
  const CliLoader = require('../services/cli-loader');
8
15
  const SummaryGenerator = require('../services/summary-generator');
@@ -15,8 +22,10 @@ describe('WorkspaceManager', () => {
15
22
  });
16
23
 
17
24
  test('should validate workspace path', async () => {
18
- // Test with allowed path
19
- const validPath = '/var/www/myapp';
25
+ // Test with allowed path (Termux-first)
26
+ const baseDir = path.join(process.env.HOME || os.tmpdir(), '.nexuscli-test');
27
+ fs.mkdirSync(baseDir, { recursive: true });
28
+ const validPath = fs.mkdtempSync(path.join(baseDir, 'ws-'));
20
29
  const result = await manager.validateWorkspace(validPath);
21
30
  expect(result).toBe(validPath);
22
31
  });
@@ -28,7 +37,8 @@ describe('WorkspaceManager', () => {
28
37
  });
29
38
 
30
39
  test('should detect non-existent workspace', async () => {
31
- const nonExistent = '/var/nonexistent-workspace-12345';
40
+ const baseDir = path.join(process.env.HOME || os.tmpdir(), '.nexuscli-test');
41
+ const nonExistent = path.join(baseDir, 'nonexistent-workspace-12345');
32
42
  await expect(manager.validateWorkspace(nonExistent)).rejects.toThrow('does not exist');
33
43
  });
34
44
 
@@ -41,7 +51,7 @@ describe('WorkspaceManager', () => {
41
51
 
42
52
  test('should extract title from messages', () => {
43
53
  const messages = [
44
- { display: 'Implement user authentication feature' },
54
+ { role: 'user', display: 'Implement user authentication feature' },
45
55
  { display: 'Follow up on previous discussion' }
46
56
  ];
47
57
  const title = manager.extractTitle(messages);
@@ -62,11 +72,6 @@ describe('CliLoader', () => {
62
72
  expect(loader.claudePath).toBe(expectedPath);
63
73
  });
64
74
 
65
- test('should initialize with correct historyPath', () => {
66
- const expectedPath = require('path').join(process.env.HOME, '.claude', 'history.jsonl');
67
- expect(loader.historyPath).toBe(expectedPath);
68
- });
69
-
70
75
  test('should have loadMessagesFromCLI method', () => {
71
76
  expect(typeof loader.loadMessagesFromCLI).toBe('function');
72
77
  });
@@ -147,7 +152,9 @@ describe('SummaryGenerator', () => {
147
152
  describe('Integration - Service Interactions', () => {
148
153
  test('WorkspaceManager should use consistent path resolution', async () => {
149
154
  const manager = new WorkspaceManager();
150
- const testPath = '/var/www/myapp';
155
+ const baseDir = path.join(process.env.HOME || os.tmpdir(), '.nexuscli-test');
156
+ fs.mkdirSync(baseDir, { recursive: true });
157
+ const testPath = fs.mkdtempSync(path.join(baseDir, 'ws-'));
151
158
  const validated = await manager.validateWorkspace(testPath);
152
159
  expect(validated).toBe(testPath);
153
160
  });
@@ -0,0 +1,100 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+ const { fixTermuxPath } = require('./workspace');
4
+
5
+ const ATTACHMENT_LINE_REGEX = /^\s*\[Attached:\s*(.+?)\s*\]\s*$/;
6
+ const IMAGE_EXTS = new Set([
7
+ '.png', '.jpg', '.jpeg', '.gif', '.webp', '.bmp', '.tiff', '.tif', '.svg', '.heic'
8
+ ]);
9
+
10
+ function parseAttachmentMarkers(message = '') {
11
+ if (!message) {
12
+ return { cleanMessage: message, paths: [] };
13
+ }
14
+
15
+ const lines = message.split('\n');
16
+ const paths = [];
17
+ const kept = [];
18
+
19
+ for (const line of lines) {
20
+ const match = line.match(ATTACHMENT_LINE_REGEX);
21
+ if (match) {
22
+ paths.push(match[1]);
23
+ continue;
24
+ }
25
+ kept.push(line);
26
+ }
27
+
28
+ const cleanMessage = kept.join('\n').trim();
29
+ return { cleanMessage, paths };
30
+ }
31
+
32
+ function isImageAttachment(attachment) {
33
+ const mimeType = attachment?.mimeType || '';
34
+ if (mimeType.startsWith('image/')) return true;
35
+ const ext = path.extname(attachment?.path || '').toLowerCase();
36
+ return IMAGE_EXTS.has(ext);
37
+ }
38
+
39
+ function normalizeAttachmentPath(rawPath, workspacePath) {
40
+ if (!rawPath || typeof rawPath !== 'string') return null;
41
+ let fixedPath = fixTermuxPath(rawPath);
42
+ if (!path.isAbsolute(fixedPath) && workspacePath) {
43
+ fixedPath = path.resolve(workspacePath, fixedPath);
44
+ }
45
+ return fixedPath;
46
+ }
47
+
48
+ function normalizeAttachments({ message, rawMessage, attachments, workspacePath }) {
49
+ const parsed = parseAttachmentMarkers(message || '');
50
+ const promptMessage = rawMessage != null ? rawMessage : (parsed.cleanMessage || message || '');
51
+
52
+ const incoming = [];
53
+ if (Array.isArray(attachments)) {
54
+ incoming.push(...attachments);
55
+ }
56
+ if (parsed.paths.length > 0) {
57
+ parsed.paths.forEach((p) => incoming.push({ path: p }));
58
+ }
59
+
60
+ const seen = new Set();
61
+ const normalized = [];
62
+
63
+ for (const entry of incoming) {
64
+ if (!entry) continue;
65
+ const rawPath = typeof entry === 'string' ? entry : entry.path;
66
+ const resolvedPath = normalizeAttachmentPath(rawPath, workspacePath);
67
+ if (!resolvedPath) continue;
68
+ if (!fs.existsSync(resolvedPath)) continue;
69
+ if (seen.has(resolvedPath)) continue;
70
+
71
+ seen.add(resolvedPath);
72
+ const record = {
73
+ ...entry,
74
+ path: resolvedPath,
75
+ name: entry.name || path.basename(resolvedPath)
76
+ };
77
+ normalized.push(record);
78
+ }
79
+
80
+ const attachmentPaths = normalized.map((att) => att.path);
81
+ const includeDirectories = Array.from(
82
+ new Set(attachmentPaths.map((p) => path.dirname(p)))
83
+ );
84
+ const imageFiles = normalized.filter(isImageAttachment).map((att) => att.path);
85
+
86
+ return {
87
+ promptMessage,
88
+ attachments: normalized,
89
+ attachmentPaths,
90
+ includeDirectories,
91
+ imageFiles,
92
+ cleanMessage: parsed.cleanMessage
93
+ };
94
+ }
95
+
96
+ module.exports = {
97
+ normalizeAttachments,
98
+ parseAttachmentMarkers,
99
+ isImageAttachment
100
+ };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@mmmbuto/nexuscli",
3
- "version": "0.9.6",
4
- "description": "NexusCLI - TRI CLI Control Plane (Claude/Codex/Gemini)",
3
+ "version": "0.9.7-001-termux",
4
+ "description": "NexusCLI - TRI CLI Control Plane (Claude/Codex/Gemini/Qwen)",
5
5
  "main": "lib/server/server.js",
6
6
  "bin": {
7
7
  "nexuscli": "bin/nexuscli.js"