@ecmaos/coreutils 0.5.2 → 0.6.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.
@@ -0,0 +1,172 @@
1
+ import path from 'path'
2
+ import type { Kernel, Process, Shell, Terminal } from '@ecmaos/types'
3
+ import { TerminalCommand } from '../shared/terminal-command.js'
4
+ import { writelnStderr } from '../shared/helpers.js'
5
+
6
+ function printUsage(process: Process | undefined, terminal: Terminal): void {
7
+ const usage = `Usage: history [OPTION]... [N]
8
+ Display or manipulate the command history.
9
+
10
+ N display the last N entries
11
+ -c clear the history list
12
+ -d N delete the history entry at position N
13
+ -r reload the history file (useful after manual edits)
14
+ --help display this help and exit
15
+
16
+ If N is provided without options, display the last N entries.
17
+ If no arguments are provided, display all history entries.
18
+
19
+ Note: History is automatically saved on each command execution.`
20
+ writelnStderr(process, terminal, usage)
21
+ }
22
+
23
+ async function readHistoryFile(shell: Shell, kernel: Kernel): Promise<string[]> {
24
+ const home = shell.env.get('HOME') || '/root'
25
+ const historyPath = path.join(home, '.history')
26
+
27
+ try {
28
+ if (!kernel.filesystem?.fs) {
29
+ return []
30
+ }
31
+
32
+ const exists = await kernel.filesystem.fs.exists(historyPath)
33
+ if (!exists) {
34
+ return []
35
+ }
36
+
37
+ const content = await kernel.filesystem.fs.readFile(historyPath, 'utf-8')
38
+ const lines = content.split('\n').filter(line => line.length > 0)
39
+ return lines
40
+ } catch {
41
+ return []
42
+ }
43
+ }
44
+
45
+ async function writeHistoryFile(shell: Shell, kernel: Kernel, lines: string[]): Promise<void> {
46
+ const home = shell.env.get('HOME') || '/root'
47
+ const historyPath = path.join(home, '.history')
48
+
49
+ if (!kernel.filesystem?.fs) {
50
+ throw new Error('Filesystem not available')
51
+ }
52
+
53
+ const content = lines.join('\n')
54
+ await kernel.filesystem.fs.writeFile(historyPath, content, 'utf-8')
55
+ }
56
+
57
+ export function createCommand(kernel: Kernel, shell: Shell, terminal: Terminal): TerminalCommand {
58
+ return new TerminalCommand({
59
+ command: 'history',
60
+ description: 'Display or manipulate the command history',
61
+ kernel,
62
+ shell,
63
+ terminal,
64
+ run: async (pid: number, argv: string[]) => {
65
+ const process = kernel.processes.get(pid) as Process | undefined
66
+
67
+ if (!process) return 1
68
+
69
+ if (argv.length > 0 && (argv[0] === '--help' || argv[0] === '-h')) {
70
+ printUsage(process, terminal)
71
+ return 0
72
+ }
73
+
74
+ const writer = process.stdout.getWriter()
75
+
76
+ try {
77
+ let clearHistory = false
78
+ let deleteIndex: number | null = null
79
+ let readHistory = false
80
+ let numEntries: number | null = null
81
+
82
+ for (let i = 0; i < argv.length; i++) {
83
+ const arg = argv[i]
84
+ if (!arg) continue
85
+
86
+ if (arg === '-c') {
87
+ clearHistory = true
88
+ } else if (arg === '-r') {
89
+ readHistory = true
90
+ } else if (arg === '-d') {
91
+ if (i + 1 < argv.length) {
92
+ i++
93
+ const nextArg = argv[i]
94
+ if (nextArg !== undefined) {
95
+ const index = parseInt(nextArg, 10)
96
+ if (!isNaN(index)) {
97
+ deleteIndex = index
98
+ } else {
99
+ await writelnStderr(process, terminal, `history: invalid history number '${nextArg}'`)
100
+ return 1
101
+ }
102
+ }
103
+ } else {
104
+ await writelnStderr(process, terminal, 'history: -d requires a history number')
105
+ return 1
106
+ }
107
+ } else if (!arg.startsWith('-')) {
108
+ const num = parseInt(arg, 10)
109
+ if (!isNaN(num)) {
110
+ numEntries = num
111
+ }
112
+ }
113
+ }
114
+
115
+ if (clearHistory) {
116
+ const uid = shell.credentials.uid
117
+ await terminal.clearHistory(uid)
118
+ return 0
119
+ }
120
+
121
+ if (deleteIndex !== null) {
122
+ const lines = await readHistoryFile(shell, kernel)
123
+ if (deleteIndex < 1 || deleteIndex > lines.length) {
124
+ await writelnStderr(process, terminal, `history: history number '${deleteIndex}' out of range`)
125
+ return 1
126
+ }
127
+ const newLines = lines.filter((_, index) => index !== deleteIndex - 1)
128
+ await writeHistoryFile(shell, kernel, newLines)
129
+
130
+ const uid = shell.credentials.uid
131
+ await terminal.reloadHistory(uid).catch(() => {})
132
+ return 0
133
+ }
134
+
135
+ if (readHistory) {
136
+ const uid = shell.credentials.uid
137
+ await terminal.reloadHistory(uid).catch(() => {})
138
+ return 0
139
+ }
140
+
141
+ const lines = await readHistoryFile(shell, kernel)
142
+
143
+ if (lines.length === 0) {
144
+ return 0
145
+ }
146
+
147
+ const displayLines = numEntries !== null
148
+ ? lines.slice(-numEntries)
149
+ : lines
150
+
151
+ const startIndex = numEntries !== null
152
+ ? lines.length - numEntries + 1
153
+ : 1
154
+
155
+ for (let i = 0; i < displayLines.length; i++) {
156
+ const lineNumber = startIndex + i
157
+ const line = displayLines[i]
158
+ const output = ` ${lineNumber} ${line}\n`
159
+ await writer.write(new TextEncoder().encode(output))
160
+ }
161
+
162
+ return 0
163
+ } catch (error) {
164
+ const errorMessage = error instanceof Error ? error.message : String(error)
165
+ await writelnStderr(process, terminal, `history: ${errorMessage}`)
166
+ return 1
167
+ } finally {
168
+ writer.releaseLock()
169
+ }
170
+ }
171
+ })
172
+ }
@@ -14,7 +14,7 @@ List information about the FILEs (the current directory by default).
14
14
  writelnStderr(process, terminal, usage)
15
15
  }
16
16
 
17
- function truncateInfo(text: string, maxWidth: number = 40): string {
17
+ function truncateInfo(text: string, maxWidth: number = 35): string {
18
18
  if (!text) return text
19
19
 
20
20
  // Strip ANSI codes to get visible length
@@ -0,0 +1,68 @@
1
+ import type { Kernel, Process, Shell, Terminal } from '@ecmaos/types'
2
+ import { TerminalCommand } from '../shared/terminal-command.js'
3
+ import { writelnStderr, writelnStdout } from '../shared/helpers.js'
4
+
5
+ function printUsage(process: Process | undefined, terminal: Terminal): void {
6
+ const usage = `Usage: tty [TTY_NUMBER]
7
+ Print the current TTY number or switch to a different TTY.
8
+
9
+ TTY_NUMBER switch to the specified TTY (0-9)
10
+ --help display this help and exit
11
+
12
+ If no TTY_NUMBER is provided, prints the current TTY number.
13
+ If TTY_NUMBER is provided, switches to that TTY.`
14
+ writelnStderr(process, terminal, usage)
15
+ }
16
+
17
+ export function createCommand(kernel: Kernel, shell: Shell, terminal: Terminal): TerminalCommand {
18
+ return new TerminalCommand({
19
+ command: 'tty',
20
+ description: 'Print the current TTY number or switch to a different TTY',
21
+ kernel,
22
+ shell,
23
+ terminal,
24
+ run: async (pid: number, argv: string[]) => {
25
+ const process = kernel.processes.get(pid) as Process | undefined
26
+
27
+ if (!process) return 1
28
+
29
+ if (argv.length > 0 && (argv[0] === '--help' || argv[0] === '-h')) {
30
+ printUsage(process, terminal)
31
+ return 0
32
+ }
33
+
34
+ if (argv.length === 0) {
35
+ await writelnStdout(process, terminal, kernel.activeTty.toString())
36
+ return 0
37
+ }
38
+
39
+ if (argv.length > 1) {
40
+ await writelnStderr(process, terminal, 'tty: too many arguments')
41
+ await writelnStderr(process, terminal, "Try 'tty --help' for more information.")
42
+ return 1
43
+ }
44
+
45
+ const ttyNumber = parseInt(argv[0] ?? '0', 10)
46
+
47
+ if (isNaN(ttyNumber)) {
48
+ await writelnStderr(process, terminal, `tty: invalid TTY number '${argv[0]}'`)
49
+ await writelnStderr(process, terminal, "Try 'tty --help' for more information.")
50
+ return 1
51
+ }
52
+
53
+ if (ttyNumber < 0 || ttyNumber > 9) {
54
+ await writelnStderr(process, terminal, `tty: TTY number must be between 0 and 9`)
55
+ await writelnStderr(process, terminal, "Try 'tty --help' for more information.")
56
+ return 1
57
+ }
58
+
59
+ try {
60
+ await kernel.switchTty(ttyNumber)
61
+ return 0
62
+ } catch (error) {
63
+ await writelnStderr(process, terminal, `tty: failed to switch to TTY ${ttyNumber}: ${error instanceof Error ? error.message : String(error)}`)
64
+ return 1
65
+ }
66
+ }
67
+ })
68
+ }
@@ -0,0 +1,307 @@
1
+ import path from 'path'
2
+ import type { Kernel, Process, Shell, Terminal } from '@ecmaos/types'
3
+ import { TerminalCommand } from '../shared/terminal-command.js'
4
+ import { writelnStderr } from '../shared/helpers.js'
5
+
6
+ function printUsage(process: Process | undefined, terminal: Terminal): void {
7
+ const usage = `Usage: vim [OPTION]... [FILE]...
8
+ Vi IMproved - a text editor.
9
+
10
+ FILE file(s) to edit
11
+ --help, -h display this help and exit
12
+
13
+ Examples:
14
+ vim file.txt edit file.txt
15
+ vim file1.txt file2.txt edit multiple files`
16
+ writelnStderr(process, terminal, usage)
17
+ }
18
+
19
+ export function createCommand(kernel: Kernel, shell: Shell, terminal: Terminal): TerminalCommand {
20
+ return new TerminalCommand({
21
+ command: 'vim',
22
+ description: 'Vi IMproved - a text editor',
23
+ kernel,
24
+ shell,
25
+ terminal,
26
+ run: async (pid: number, argv: string[]) => {
27
+ const process = kernel.processes.get(pid) as Process | undefined
28
+
29
+ if (!process) return 1
30
+
31
+ if (argv.length > 0 && (argv[0] === '--help' || argv[0] === '-h')) {
32
+ printUsage(process, terminal)
33
+ return 0
34
+ }
35
+
36
+ try {
37
+ const { VimWasm, checkBrowserCompatibility } = await import('vim-wasm/vimwasm.js')
38
+
39
+ const compatibilityError = checkBrowserCompatibility()
40
+ if (compatibilityError !== undefined) {
41
+ await writelnStderr(process, terminal, `vim: ${compatibilityError}`)
42
+ return 1
43
+ }
44
+
45
+ const files: string[] = []
46
+ for (const arg of argv) {
47
+ if (arg && !arg.startsWith('-')) {
48
+ files.push(arg)
49
+ }
50
+ }
51
+
52
+ if (files.length === 0) {
53
+ await writelnStderr(process, terminal, 'vim: no file specified')
54
+ await writelnStderr(process, terminal, "Try 'vim --help' for more information.")
55
+ return 1
56
+ }
57
+
58
+ const fileContents: Record<string, string> = {}
59
+ const dirs = new Set<string>()
60
+ const cmdArgs: string[] = []
61
+
62
+ const cwd = shell.cwd
63
+ let currentCwd = cwd
64
+ while (currentCwd !== '/' && currentCwd !== '') {
65
+ dirs.add(currentCwd)
66
+ currentCwd = path.dirname(currentCwd)
67
+ }
68
+
69
+ const homeDir = shell.expandTilde('~')
70
+ if (homeDir && homeDir !== '~') {
71
+ let currentDir = homeDir
72
+ while (currentDir !== '/' && currentDir !== '') {
73
+ dirs.add(currentDir)
74
+ currentDir = path.dirname(currentDir)
75
+ }
76
+
77
+ const vimrcPath = path.join(homeDir, '.vim', 'vimrc')
78
+ const vimrcExists = await shell.context.fs.promises.exists(vimrcPath)
79
+ if (vimrcExists) {
80
+ const vimrcContent = await shell.context.fs.promises.readFile(vimrcPath, 'utf-8')
81
+ const vimrcVirtualPath = '/home/web_user/.vim/vimrc'
82
+ fileContents[vimrcVirtualPath] = vimrcContent
83
+ }
84
+ }
85
+
86
+ for (const file of files) {
87
+ const expandedPath = shell.expandTilde(file)
88
+ const fullPath = path.resolve(shell.cwd, expandedPath)
89
+
90
+ const exists = await shell.context.fs.promises.exists(fullPath)
91
+
92
+ if (exists) {
93
+ const stats = await shell.context.fs.promises.stat(fullPath)
94
+ if (stats.isDirectory()) {
95
+ await writelnStderr(process, terminal, `vim: ${file}: Is a directory`)
96
+ return 1
97
+ }
98
+
99
+ const content = await shell.context.fs.promises.readFile(fullPath, 'utf-8')
100
+ fileContents[fullPath] = content
101
+ } else {
102
+ fileContents[fullPath] = ''
103
+ }
104
+
105
+ const dir = path.dirname(fullPath)
106
+ let currentDir = dir
107
+ while (currentDir !== '/' && currentDir !== '') {
108
+ dirs.add(currentDir)
109
+ currentDir = path.dirname(currentDir)
110
+ }
111
+
112
+ cmdArgs.push(fullPath)
113
+ }
114
+
115
+ const emscriptenDefaultDirs = new Set(['/', '/tmp', '/home', '/home/web_user', '/home/web_user/.vim', '/dev'])
116
+ const dirsArray = Array.from(dirs)
117
+ .filter(d => !emscriptenDefaultDirs.has(d))
118
+ .sort((a, b) => a.length - b.length)
119
+
120
+ const container = document.createElement('div')
121
+ container.style.width = '100%'
122
+ container.style.height = '100%'
123
+ container.style.display = 'flex'
124
+ container.style.flexDirection = 'column'
125
+ container.style.background = '#1e1e1e'
126
+ container.style.overflow = 'hidden'
127
+
128
+ const canvas = document.createElement('canvas')
129
+ canvas.id = 'vim-canvas'
130
+ canvas.style.width = '100%'
131
+ canvas.style.height = '100%'
132
+ canvas.style.flex = '1'
133
+
134
+ const input = document.createElement('input')
135
+ input.id = 'vim-input'
136
+ input.type = 'text'
137
+ input.autocomplete = 'off'
138
+ input.autofocus = true
139
+ input.style.position = 'absolute'
140
+ input.style.left = '-9999px'
141
+ input.style.width = '1px'
142
+ input.style.height = '1px'
143
+ input.style.opacity = '0'
144
+
145
+ container.appendChild(canvas)
146
+ container.appendChild(input)
147
+
148
+ const firstFile = files.length > 0 ? files[0] : undefined
149
+ const windowTitle = firstFile ? path.basename(firstFile) : 'vim'
150
+ const win = kernel.windows.create({
151
+ title: windowTitle,
152
+ width: 900,
153
+ height: 700,
154
+ max: false
155
+ })
156
+
157
+ win.mount(container)
158
+
159
+ let workerScriptPath: string
160
+ try {
161
+ workerScriptPath = new URL('vim-wasm/vim.js', import.meta.url).href
162
+ } catch {
163
+ await writelnStderr(process, terminal, 'vim: failed to resolve worker script path. Please ensure vim-wasm is properly installed.')
164
+ win.close()
165
+ return 1
166
+ }
167
+
168
+ const vim = new VimWasm({
169
+ canvas,
170
+ input,
171
+ workerScriptPath
172
+ })
173
+
174
+ let exitCode = 0
175
+ let vimExited = false
176
+ let resizeObserver: ResizeObserver | null = null
177
+
178
+ vim.onFileExport = async (fullpath: string, contents: ArrayBuffer) => {
179
+ try {
180
+ const text = new TextDecoder().decode(contents)
181
+ await shell.context.fs.promises.writeFile(fullpath, text, 'utf-8')
182
+ } catch (error) {
183
+ await writelnStderr(process, terminal, `vim: error writing file ${fullpath}: ${error instanceof Error ? error.message : 'Unknown error'}`)
184
+ }
185
+ }
186
+
187
+ vim.onVimExit = (status: number) => {
188
+ vimExited = true
189
+ exitCode = status === 0 ? 0 : 1
190
+ if (resizeObserver) {
191
+ resizeObserver.disconnect()
192
+ resizeObserver = null
193
+ }
194
+ win.close()
195
+ }
196
+
197
+ vim.onError = async (err: Error) => {
198
+ console.error('[vim] Error callback triggered:', err)
199
+ console.error('[vim] Error message:', err.message)
200
+ console.error('[vim] Error stack:', err.stack)
201
+ await writelnStderr(process, terminal, `vim: error: ${err.message}`)
202
+ if (!vimExited) {
203
+ exitCode = 1
204
+ if (resizeObserver) {
205
+ resizeObserver.disconnect()
206
+ resizeObserver = null
207
+ }
208
+ win.close()
209
+ }
210
+ }
211
+
212
+ vim.onTitleUpdate = (title: string) => {
213
+ win.setTitle(title || windowTitle)
214
+ }
215
+
216
+ vim.onVimInit = async () => {
217
+ try {
218
+ await vim.cmdline('autocmd BufWritePost * :export')
219
+ } catch (error) {
220
+ console.error('[vim] Failed to set up auto-export:', error)
221
+ }
222
+
223
+ const handleResize = () => {
224
+ if (vimExited || !vim.isRunning()) return
225
+
226
+ const rect = container.getBoundingClientRect()
227
+ const width = Math.floor(rect.width)
228
+ const height = Math.floor(rect.height)
229
+
230
+ if (width > 0 && height > 0) {
231
+ const dpr = window.devicePixelRatio || 1
232
+ canvas.width = width * dpr
233
+ canvas.height = height * dpr
234
+
235
+ vim.resize(width, height)
236
+ }
237
+ }
238
+
239
+ resizeObserver = new ResizeObserver(handleResize)
240
+ resizeObserver.observe(container)
241
+ }
242
+
243
+ for (const filePath of Object.keys(fileContents)) {
244
+ const parentDir = path.dirname(filePath)
245
+ if (!dirsArray.includes(parentDir)) {
246
+ console.warn(`[vim] WARNING: Parent directory ${parentDir} of file ${filePath} is not in dirs array!`)
247
+ }
248
+ if (dirsArray.includes(filePath)) {
249
+ console.error(`[vim] ERROR: File path ${filePath} is also in dirs array! This will cause ENOTDIR error.`)
250
+ }
251
+ }
252
+
253
+ for (const dirPath of dirsArray) {
254
+ if (fileContents[dirPath] !== undefined) {
255
+ console.error(`[vim] ERROR: Directory path ${dirPath} is also in files object! This will cause ENOTDIR error.`)
256
+ }
257
+ }
258
+
259
+ for (let i = 0; i < dirsArray.length; i++) {
260
+ const dir = dirsArray[i]
261
+ if (dir) {
262
+ const parent = path.dirname(dir)
263
+ if (parent !== dir && !dirsArray.slice(0, i).includes(parent)) {
264
+ console.warn(`[vim] WARNING: Directory ${dir} has parent ${parent} that comes after it in the array!`)
265
+ }
266
+ }
267
+ }
268
+
269
+ try {
270
+ const startOptions = {
271
+ files: fileContents,
272
+ dirs: dirsArray,
273
+ cmdArgs,
274
+ debug: false
275
+ }
276
+
277
+ vim.start(startOptions)
278
+ } catch (startError) {
279
+ console.error('[vim] Error calling vim.start():', startError)
280
+ await writelnStderr(process, terminal, `vim: failed to start: ${startError instanceof Error ? startError.message : 'Unknown error'}`)
281
+ if (startError instanceof Error && startError.stack) {
282
+ console.error('[vim] Start error stack:', startError.stack)
283
+ }
284
+ win.close()
285
+ return 1
286
+ }
287
+
288
+ return new Promise<number>((resolve) => {
289
+ const checkExit = () => {
290
+ if (vimExited) {
291
+ resolve(exitCode)
292
+ } else {
293
+ setTimeout(checkExit, 100)
294
+ }
295
+ }
296
+ checkExit()
297
+ })
298
+ } catch (error) {
299
+ await writelnStderr(process, terminal, `vim: ${error instanceof Error ? error.message : 'Unknown error'}`)
300
+ if (error instanceof Error && error.stack) {
301
+ console.error(error.stack)
302
+ }
303
+ return 1
304
+ }
305
+ }
306
+ })
307
+ }
package/src/index.ts CHANGED
@@ -21,6 +21,7 @@ import { createCommand as createGrep } from './commands/grep.js'
21
21
  import { createCommand as createGroups } from './commands/groups.js'
22
22
  import { createCommand as createHash } from './commands/hash.js'
23
23
  import { createCommand as createHead } from './commands/head.js'
24
+ import { createCommand as createHistory } from './commands/history.js'
24
25
  import { createCommand as createHostname } from './commands/hostname.js'
25
26
  import { createCommand as createLn } from './commands/ln.js'
26
27
  import { createCommand as createLs } from './commands/ls.js'
@@ -85,6 +86,7 @@ import { createCommand as createTest } from './commands/test.js'
85
86
  import { createCommand as createTime } from './commands/time.js'
86
87
  import { createCommand as createTr } from './commands/tr.js'
87
88
  import { createCommand as createTrue } from './commands/true.js'
89
+ import { createCommand as createTty } from './commands/tty.js'
88
90
  import { createCommand as createUname } from './commands/uname.js'
89
91
  import { createCommand as createUmount } from './commands/umount.js'
90
92
  import { createCommand as createUnexpand } from './commands/unexpand.js'
@@ -99,6 +101,8 @@ import { createCommand as createZip } from './commands/zip.js'
99
101
  import { createCommand as createUnzip } from './commands/unzip.js'
100
102
  import { createCommand as createVideo } from './commands/video.js'
101
103
  import { createCommand as createView } from './commands/view.js'
104
+ import { createCommand as createVim } from './commands/vim.js'
105
+ import { createCommand as createGit } from './commands/git.js'
102
106
 
103
107
  // Export individual command factories
104
108
  export { createCommand as createCat } from './commands/cat.js'
@@ -113,6 +117,7 @@ export { createCommand as createFetch } from './commands/fetch.js'
113
117
  export { createCommand as createGrep } from './commands/grep.js'
114
118
  export { createCommand as createGroups } from './commands/groups.js'
115
119
  export { createCommand as createHash } from './commands/hash.js'
120
+ export { createCommand as createHistory } from './commands/history.js'
116
121
  export { createCommand as createLn } from './commands/ln.js'
117
122
  export { createCommand as createLs } from './commands/ls.js'
118
123
  export { createCommand as createMkdir } from './commands/mkdir.js'
@@ -161,6 +166,7 @@ export { createCommand as createSleep } from './commands/sleep.js'
161
166
  export { createCommand as createSort } from './commands/sort.js'
162
167
  export { createCommand as createTest } from './commands/test.js'
163
168
  export { createCommand as createTr } from './commands/tr.js'
169
+ export { createCommand as createTty } from './commands/tty.js'
164
170
  export { createCommand as createUname } from './commands/uname.js'
165
171
  export { createCommand as createUmount } from './commands/umount.js'
166
172
  export { createCommand as createUptime } from './commands/uptime.js'
@@ -174,6 +180,8 @@ export { createCommand as createZip } from './commands/zip.js'
174
180
  export { createCommand as createUnzip } from './commands/unzip.js'
175
181
  export { createCommand as createVideo } from './commands/video.js'
176
182
  export { createCommand as createView } from './commands/view.js'
183
+ export { createCommand as createVim } from './commands/vim.js'
184
+ export { createCommand as createGit } from './commands/git.js'
177
185
 
178
186
  /**
179
187
  * Creates all coreutils commands.
@@ -195,6 +203,7 @@ export function createAllCommands(kernel: Kernel, shell: Shell, terminal: Termin
195
203
  groups: createGroups(kernel, shell, terminal),
196
204
  hash: createHash(kernel, shell, terminal),
197
205
  head: createHead(kernel, shell, terminal),
206
+ history: createHistory(kernel, shell, terminal),
198
207
  hostname: createHostname(kernel, shell, terminal),
199
208
  ln: createLn(kernel, shell, terminal),
200
209
  ls: createLs(kernel, shell, terminal),
@@ -259,6 +268,7 @@ export function createAllCommands(kernel: Kernel, shell: Shell, terminal: Termin
259
268
  time: createTime(kernel, shell, terminal),
260
269
  tr: createTr(kernel, shell, terminal),
261
270
  true: createTrue(kernel, shell, terminal),
271
+ tty: createTty(kernel, shell, terminal),
262
272
  uname: createUname(kernel, shell, terminal),
263
273
  umount: createUmount(kernel, shell, terminal),
264
274
  unexpand: createUnexpand(kernel, shell, terminal),
@@ -272,7 +282,9 @@ export function createAllCommands(kernel: Kernel, shell: Shell, terminal: Termin
272
282
  zip: createZip(kernel, shell, terminal),
273
283
  unzip: createUnzip(kernel, shell, terminal),
274
284
  video: createVideo(kernel, shell, terminal),
275
- view: createView(kernel, shell, terminal)
285
+ view: createView(kernel, shell, terminal),
286
+ vim: createVim(kernel, shell, terminal),
287
+ git: createGit(kernel, shell, terminal)
276
288
  }
277
289
  }
278
290