ikie-cli 0.1.33 → 0.1.34

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/dist/tree.js ADDED
@@ -0,0 +1,266 @@
1
+ import { readdirSync, statSync, existsSync } from 'fs';
2
+ import { join, extname } from 'path';
3
+ import { c } from './theme.js';
4
+ const DEFAULT_EXCLUDE = [
5
+ 'node_modules',
6
+ '.git',
7
+ 'dist',
8
+ 'build',
9
+ '.next',
10
+ '.nuxt',
11
+ 'coverage',
12
+ '.cache',
13
+ 'tmp',
14
+ 'temp',
15
+ ];
16
+ /**
17
+ * Get icon for file based on extension
18
+ */
19
+ function getFileIcon(filename, isDirectory) {
20
+ if (isDirectory)
21
+ return '📁';
22
+ const ext = extname(filename).toLowerCase();
23
+ const name = filename.toLowerCase();
24
+ // Special files
25
+ if (name === 'package.json')
26
+ return '📦';
27
+ if (name === 'package-lock.json')
28
+ return '🔒';
29
+ if (name === 'yarn.lock')
30
+ return '🔒';
31
+ if (name === 'tsconfig.json')
32
+ return '⚙️';
33
+ if (name === '.gitignore')
34
+ return '🚫';
35
+ if (name === 'readme.md' || name === 'readme')
36
+ return '📖';
37
+ if (name === 'license' || name === 'license.md')
38
+ return '📜';
39
+ if (name === '.env' || name.startsWith('.env.'))
40
+ return '🔑';
41
+ if (name === 'dockerfile')
42
+ return '🐳';
43
+ // By extension
44
+ switch (ext) {
45
+ case '.ts':
46
+ case '.tsx': return '🔷';
47
+ case '.js':
48
+ case '.jsx': return '🟨';
49
+ case '.json': return '📋';
50
+ case '.md': return '📝';
51
+ case '.css':
52
+ case '.scss':
53
+ case '.sass':
54
+ case '.less': return '🎨';
55
+ case '.html': return '🌐';
56
+ case '.py': return '🐍';
57
+ case '.rs': return '🦀';
58
+ case '.go': return '🐹';
59
+ case '.java': return '☕';
60
+ case '.cpp':
61
+ case '.c':
62
+ case '.h': return '⚡';
63
+ case '.sh':
64
+ case '.bash':
65
+ case '.zsh': return '🔧';
66
+ case '.yml':
67
+ case '.yaml': return '⚙️';
68
+ case '.xml': return '📰';
69
+ case '.sql': return '🗄️';
70
+ case '.png':
71
+ case '.jpg':
72
+ case '.jpeg':
73
+ case '.gif':
74
+ case '.svg':
75
+ case '.webp': return '🖼️';
76
+ case '.mp4':
77
+ case '.mov':
78
+ case '.avi': return '🎬';
79
+ case '.mp3':
80
+ case '.wav':
81
+ case '.ogg': return '🎵';
82
+ case '.zip':
83
+ case '.tar':
84
+ case '.gz':
85
+ case '.rar': return '📦';
86
+ case '.pdf': return '📕';
87
+ case '.txt': return '📄';
88
+ default: return '📄';
89
+ }
90
+ }
91
+ /**
92
+ * Format file size in human-readable format
93
+ */
94
+ function formatSize(bytes) {
95
+ if (bytes < 1024)
96
+ return `${bytes}B`;
97
+ if (bytes < 1024 * 1024)
98
+ return `${(bytes / 1024).toFixed(1)}KB`;
99
+ if (bytes < 1024 * 1024 * 1024)
100
+ return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;
101
+ return `${(bytes / (1024 * 1024 * 1024)).toFixed(1)}GB`;
102
+ }
103
+ /**
104
+ * Get color for file based on type
105
+ */
106
+ function getFileColor(filename, isDirectory) {
107
+ if (isDirectory)
108
+ return c.primary.bold;
109
+ const ext = extname(filename).toLowerCase();
110
+ const name = filename.toLowerCase();
111
+ // Hidden files
112
+ if (name.startsWith('.'))
113
+ return c.dim;
114
+ // Config files
115
+ if (name.includes('config') || ext === '.json' || ext === '.yml' || ext === '.yaml') {
116
+ return c.secondary;
117
+ }
118
+ // Source code
119
+ if (['.ts', '.tsx', '.js', '.jsx', '.py', '.rs', '.go', '.java'].includes(ext)) {
120
+ return c.white;
121
+ }
122
+ // Documentation
123
+ if (ext === '.md' || name === 'readme') {
124
+ return c.info;
125
+ }
126
+ // Executables and scripts
127
+ if (['.sh', '.bash', '.zsh'].includes(ext)) {
128
+ return c.success;
129
+ }
130
+ return c.muted;
131
+ }
132
+ /**
133
+ * Build tree structure recursively
134
+ */
135
+ function buildTree(dirPath, prefix, options, results) {
136
+ const currentDepth = options.currentDepth || 0;
137
+ const maxDepth = options.maxDepth || 3;
138
+ const showHidden = options.showHidden || false;
139
+ const showSize = options.showSize || false;
140
+ const exclude = [...DEFAULT_EXCLUDE, ...(options.exclude || [])];
141
+ if (currentDepth >= maxDepth)
142
+ return;
143
+ let entries;
144
+ try {
145
+ entries = readdirSync(dirPath);
146
+ }
147
+ catch {
148
+ return;
149
+ }
150
+ // Filter entries
151
+ entries = entries.filter(entry => {
152
+ // Exclude hidden files unless showHidden is true
153
+ if (!showHidden && entry.startsWith('.'))
154
+ return false;
155
+ // Exclude specified patterns
156
+ if (exclude.includes(entry))
157
+ return false;
158
+ return true;
159
+ });
160
+ // Sort: directories first, then files, alphabetically within each group
161
+ entries.sort((a, b) => {
162
+ const aPath = join(dirPath, a);
163
+ const bPath = join(dirPath, b);
164
+ try {
165
+ const aIsDir = statSync(aPath).isDirectory();
166
+ const bIsDir = statSync(bPath).isDirectory();
167
+ if (aIsDir !== bIsDir)
168
+ return aIsDir ? -1 : 1;
169
+ return a.localeCompare(b);
170
+ }
171
+ catch {
172
+ return 0;
173
+ }
174
+ });
175
+ entries.forEach((entry, index) => {
176
+ const isLast = index === entries.length - 1;
177
+ const entryPath = join(dirPath, entry);
178
+ let stats;
179
+ try {
180
+ stats = statSync(entryPath);
181
+ }
182
+ catch {
183
+ return;
184
+ }
185
+ const isDirectory = stats.isDirectory();
186
+ const icon = getFileIcon(entry, isDirectory);
187
+ const color = getFileColor(entry, isDirectory);
188
+ // Build the tree branch characters
189
+ const branch = isLast ? '└── ' : '├── ';
190
+ const connector = isLast ? ' ' : '│ ';
191
+ // Build the line
192
+ let line = `${prefix}${branch}${icon} ${color(entry)}`;
193
+ // Add size info if requested
194
+ if (showSize && !isDirectory) {
195
+ line += ` ${c.dim(formatSize(stats.size))}`;
196
+ }
197
+ results.push(line);
198
+ // Recurse into directories
199
+ if (isDirectory) {
200
+ buildTree(entryPath, prefix + connector, { ...options, currentDepth: currentDepth + 1 }, results);
201
+ }
202
+ });
203
+ }
204
+ /**
205
+ * Generate a visual file tree
206
+ */
207
+ export function generateTree(rootPath, options = {}) {
208
+ if (!existsSync(rootPath)) {
209
+ return c.error(`Path not found: ${rootPath}`);
210
+ }
211
+ const stats = statSync(rootPath);
212
+ if (!stats.isDirectory()) {
213
+ return c.error(`Not a directory: ${rootPath}`);
214
+ }
215
+ const results = [];
216
+ const rootIcon = '📁';
217
+ const rootName = rootPath === '.' ? process.cwd().split('/').pop() || '.' : rootPath;
218
+ // Add root
219
+ results.push(`${rootIcon} ${c.primary.bold(rootName)}/`);
220
+ // Build tree
221
+ buildTree(rootPath, '', { ...options, currentDepth: 0 }, results);
222
+ // Add summary
223
+ const fileCount = results.length - 1; // Exclude root
224
+ results.push('');
225
+ results.push(c.muted(` ${fileCount} items shown`));
226
+ if ((options.currentDepth || 0) >= (options.maxDepth || 3)) {
227
+ results.push(c.muted(` (depth limit: ${options.maxDepth || 3})`));
228
+ }
229
+ return results.join('\n');
230
+ }
231
+ /**
232
+ * Parse tree command arguments
233
+ */
234
+ export function parseTreeArgs(args) {
235
+ const options = {
236
+ maxDepth: 3,
237
+ showHidden: false,
238
+ showSize: false,
239
+ exclude: [],
240
+ };
241
+ let path = '.';
242
+ for (let i = 0; i < args.length; i++) {
243
+ const arg = args[i];
244
+ if (arg === '-a' || arg === '--all') {
245
+ options.showHidden = true;
246
+ }
247
+ else if (arg === '-s' || arg === '--size') {
248
+ options.showSize = true;
249
+ }
250
+ else if (arg === '-d' || arg === '--depth') {
251
+ const depth = parseInt(args[i + 1], 10);
252
+ if (!isNaN(depth) && depth > 0) {
253
+ options.maxDepth = depth;
254
+ i++; // Skip next arg
255
+ }
256
+ }
257
+ else if (arg.startsWith('-')) {
258
+ // Unknown flag, ignore
259
+ }
260
+ else {
261
+ // Assume it's a path
262
+ path = arg;
263
+ }
264
+ }
265
+ return { path, options };
266
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ikie-cli",
3
- "version": "0.1.33",
3
+ "version": "0.1.34",
4
4
  "description": "Agentic coding CLI — your terminal AI pair programmer",
5
5
  "type": "module",
6
6
  "bin": {
@@ -29,6 +29,7 @@
29
29
  "build": "tsc",
30
30
  "dev": "tsx src/index.ts",
31
31
  "start": "node dist/index.js",
32
+ "test": "node --import tsx --test src/agent.test.ts src/mcp-manager.test.ts src/skills.test.ts",
32
33
  "prepublishOnly": "npm run build"
33
34
  },
34
35
  "dependencies": {