miii-cli 0.2.0 → 0.2.2

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.
@@ -1,130 +0,0 @@
1
- // ANSI-formatted stdout output — goes into terminal scrollback
2
-
3
- const R = '\x1b[0m'
4
- const BOLD = '\x1b[1m'
5
- const DIM = '\x1b[2m'
6
-
7
- function bold(s: string) { return `${BOLD}${s}${R}` }
8
- function dim(s: string) { return `${DIM}${s}${R}` }
9
- function col(code: number, s: string) { return `\x1b[${code}m${s}${R}` }
10
-
11
- const blue = (s: string) => col(94, s)
12
- const green = (s: string) => col(92, s)
13
- const cyan = (s: string) => col(96, s)
14
- const gray = (s: string) => col(90, s)
15
- const yellow = (s: string) => col(93, s)
16
-
17
- function indent(text: string, pad = ' '): string {
18
- return text.split('\n').map(l => pad + l).join('\n')
19
- }
20
-
21
- function stripMarkdown(s: string): string {
22
- return s
23
- .replace(/\*\*\*(.+?)\*\*\*/g, '$1')
24
- .replace(/\*\*(.+?)\*\*/g, '$1')
25
- .replace(/\*(.+?)\*/g, '$1')
26
- .replace(/`([^`]+)`/g, '$1')
27
- .replace(/^#{1,6} /gm, '')
28
- }
29
-
30
- function formatContent(text: string): string {
31
- const lines = text.split('\n')
32
- let inCode = false
33
- const out: string[] = []
34
- for (const line of lines) {
35
- if (line.startsWith('<tool_call>') || line.startsWith('</tool_call>')) continue
36
- if (line.startsWith('```')) {
37
- inCode = !inCode
38
- out.push(' ' + dim(gray(line)))
39
- } else if (inCode) {
40
- out.push(' ' + yellow(line || ' '))
41
- } else {
42
- out.push(' ' + stripMarkdown(line || ''))
43
- }
44
- }
45
- return out.join('\n')
46
- }
47
-
48
- export function welcome(provider: string, model: string, cwd: string): void {
49
- const cols = Math.min(process.stdout.columns ?? 80, 100)
50
- const innerW = cols - 2
51
- const leftW = Math.floor(innerW * 0.44)
52
- const rightW = innerW - leftW - 1
53
-
54
- function vis(s: string): string { return s.replace(/\x1b\[[0-9;]*m/g, '') }
55
-
56
- function cell(s: string, w: number): string {
57
- const v = vis(s)
58
- if (v.length < w) return s + ' '.repeat(w - v.length)
59
- if (v.length === w) return s
60
- return v.slice(0, w - 1) + '…'
61
- }
62
-
63
- function row(l: string, r: string): string {
64
- return gray('│') + cell(l, leftW) + gray('│') + cell(r, rightW) + gray('│')
65
- }
66
-
67
- function blank(): string {
68
- return gray('│') + ' '.repeat(leftW) + gray('│') + ' '.repeat(rightW) + gray('│')
69
- }
70
-
71
- function rcmd(key: string, desc: string, keyW = 10): string {
72
- return ' ' + cyan(key) + ' '.repeat(Math.max(1, keyW - key.length)) + gray(desc)
73
- }
74
-
75
- const titleStr = '─ MIII - CLI '
76
- const dashCount = Math.max(0, cols - 2 - titleStr.length)
77
- const top = gray('╭') + gray('─') + bold(cyan(' MIII - CLI ')) + gray('─'.repeat(dashCount) + '╮')
78
- const bottom = gray('╰' + '─'.repeat(innerW) + '╯')
79
-
80
- const shortCwd = cwd.replace(process.env.HOME ?? '', '~')
81
-
82
- const lines = [
83
- top,
84
- blank(),
85
- row(` ${bold(cyan('MIII - CLI'))}`, ` ${bold(yellow('Getting started'))}`),
86
- row(` ${gray('Claude Code-level terminal')}`, rcmd('@filename', 'inject file into context')),
87
- row(` ${gray('workflows, local models.')}`, rcmd('/skill', 'run a skill or command')),
88
- row('', rcmd('/models', 'switch or pull models')),
89
- row('', rcmd('/list', 'list all skills')),
90
- row('', rcmd('/session', 'manage sessions')),
91
- blank(),
92
- row(` ${gray(provider + '/' + model)}`, ` ${bold(yellow('Tips'))}`),
93
- row(` ${gray(shortCwd)}`, rcmd('ctrl+c', 'stop thinking')),
94
- row('', rcmd('ctrl+c x2','exit')),
95
- blank(),
96
- bottom,
97
- ]
98
-
99
- process.stdout.write(lines.join('\n') + '\n')
100
- }
101
-
102
- export function userMsg(text: string): void {
103
- const atHighlighted = text.replace(/(@[\w./\-]+)/g, (m) => cyan(m))
104
- console.log(`\n${bold(blue('You'))}\n${indent(atHighlighted)}`)
105
- }
106
-
107
- export function assistantMsg(text: string): void {
108
- console.log(`\n${bold(green('miii'))}\n${formatContent(text)}`)
109
- }
110
-
111
- export function toolMsg(name: string, result: string): void {
112
- const preview = result.length > 250 ? result.slice(0, 250) + '…' : result
113
- const body = preview.trim()
114
- ? preview.split('\n').map(l => gray(' ' + l)).join('\n')
115
- : ''
116
- console.log(` ${green('✓')} ${cyan(name)}${body ? '\n' + body : ''}`)
117
- }
118
-
119
- export function systemMsg(text: string): void {
120
- console.log(gray(`─ ${text}`))
121
- }
122
-
123
- export function errorMsg(text: string): void {
124
- console.log(gray(`error: ${text}`))
125
- }
126
-
127
- export function divider(): void {
128
- const cols = process.stdout.columns ?? 80
129
- process.stdout.write(`${gray('─'.repeat(cols))}\n`)
130
- }
package/src/types.ts DELETED
@@ -1,26 +0,0 @@
1
- export type Role = 'user' | 'assistant' | 'system' | 'tool'
2
- export type Status = 'idle' | 'thinking' | 'tool'
3
-
4
- export interface Message {
5
- id: string
6
- role: Role
7
- content: string
8
- timestamp: number
9
- }
10
-
11
- export interface Config {
12
- model: string
13
- provider: 'ollama' | 'openai-compat'
14
- baseUrl: string
15
- systemPrompt?: string
16
- apiKey?: string
17
- }
18
-
19
- export interface ChatMessage {
20
- role: 'system' | 'user' | 'assistant'
21
- content: string
22
- }
23
-
24
- export function generateId(): string {
25
- return Math.random().toString(36).slice(2, 10)
26
- }
@@ -1,66 +0,0 @@
1
- import { workerData, parentPort } from 'worker_threads'
2
- import { readFileSync, statSync, readdirSync, existsSync } from 'fs'
3
- import { join, relative, extname } from 'path'
4
-
5
- const SKIP_DIRS = new Set([
6
- 'node_modules', 'dist', 'build', '.git', '.next', '.nuxt', '.svelte-kit',
7
- 'out', '__pycache__', '.cache', 'coverage', '.nyc_output', 'vendor',
8
- 'target', '.turbo', '.vercel', 'generated', '.gradle', '.expo',
9
- 'bin', 'obj', 'tmp', 'temp', 'logs',
10
- ])
11
- const SKIP_EXTS = new Set(['.map', '.lock', '.png', '.jpg', '.jpeg', '.gif', '.webp', '.ico', '.mp4', '.mp3', '.pdf', '.zip', '.tar', '.gz', '.exe', '.dll', '.so', '.dylib', '.wasm', '.class', '.pyc', '.ttf', '.woff', '.woff2'])
12
-
13
- interface Input {
14
- paths: string[]
15
- cwd: string
16
- }
17
-
18
- function safe(p: string): string | null {
19
- try {
20
- const s = statSync(p)
21
- if (s.size > 512 * 1024) return null
22
- return readFileSync(p, 'utf-8')
23
- } catch { return null }
24
- }
25
-
26
- function walk(dir: string, out: string[], cwd: string, depth = 0): void {
27
- if (depth > 4) return
28
- for (const name of readdirSync(dir)) {
29
- if (name.startsWith('.')) continue
30
- if (SKIP_DIRS.has(name)) continue
31
- if (SKIP_EXTS.has(extname(name))) continue
32
- if (name.endsWith('.d.ts') || name.endsWith('.js.map')) continue
33
- const full = join(dir, name)
34
- try {
35
- const s = statSync(full)
36
- if (s.isDirectory()) walk(full, out, cwd, depth + 1)
37
- else out.push(full)
38
- } catch {}
39
- }
40
- }
41
-
42
- function xmlAttr(s: string): string {
43
- return s.replace(/&/g, '&amp;').replace(/"/g, '&quot;').replace(/</g, '&lt;').replace(/>/g, '&gt;')
44
- }
45
-
46
- function build(input: Input): string {
47
- const parts: string[] = []
48
- for (const p of input.paths) {
49
- if (!existsSync(p)) continue
50
- const s = statSync(p)
51
- if (s.isFile()) {
52
- const content = safe(p)
53
- if (content !== null) parts.push(`<file path="${xmlAttr(relative(input.cwd, p))}">\n${content}\n</file>`)
54
- } else if (s.isDirectory()) {
55
- const files: string[] = []
56
- walk(p, files, input.cwd)
57
- for (const f of files.slice(0, 100)) {
58
- const content = safe(f)
59
- if (content !== null) parts.push(`<file path="${xmlAttr(relative(input.cwd, f))}">\n${content}\n</file>`)
60
- }
61
- }
62
- }
63
- return parts.join('\n\n')
64
- }
65
-
66
- parentPort?.postMessage({ context: build(workerData as Input) })
@@ -1,20 +0,0 @@
1
- import { workerData, parentPort } from 'worker_threads'
2
- import { createPatch, applyPatch } from 'diff'
3
-
4
- interface Input {
5
- action: 'diff' | 'apply'
6
- filename?: string
7
- oldContent?: string
8
- newContent?: string
9
- patch?: string
10
- }
11
-
12
- const inp = workerData as Input
13
-
14
- if (inp.action === 'diff') {
15
- const patch = createPatch(inp.filename ?? 'file', inp.oldContent ?? '', inp.newContent ?? '')
16
- parentPort?.postMessage({ patch })
17
- } else {
18
- const result = applyPatch(inp.oldContent ?? '', inp.patch ?? '')
19
- parentPort?.postMessage({ result: result === false ? null : result })
20
- }
@@ -1,19 +0,0 @@
1
- import { Worker } from 'worker_threads'
2
- import { fileURLToPath } from 'url'
3
- import { dirname, join } from 'path'
4
-
5
- const __dir = dirname(fileURLToPath(import.meta.url))
6
- const isDev = process.argv.some(a => a.includes('tsx')) || import.meta.url.endsWith('.ts')
7
- const ext = isDev ? '.ts' : '.js'
8
-
9
- export function spawnWorker<T>(name: string, data: unknown): Promise<T> {
10
- return new Promise((resolve, reject) => {
11
- const path = join(__dir, `${name}.worker${ext}`)
12
- const w = new Worker(path, {
13
- workerData: data,
14
- execArgv: isDev ? ['--import', 'tsx/esm'] : [],
15
- })
16
- w.once('message', (r: T) => { w.terminate(); resolve(r) })
17
- w.once('error', (e) => { w.terminate(); reject(e) })
18
- })
19
- }
package/tsconfig.json DELETED
@@ -1,18 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "target": "ES2022",
4
- "module": "ESNext",
5
- "moduleResolution": "bundler",
6
- "jsx": "react-jsx",
7
- "outDir": "./dist",
8
- "rootDir": "./src",
9
- "strict": true,
10
- "esModuleInterop": true,
11
- "skipLibCheck": true,
12
- "resolveJsonModule": true,
13
- "declaration": true,
14
- "sourceMap": true
15
- },
16
- "include": ["src/**/*"],
17
- "exclude": ["node_modules", "dist"]
18
- }