miii-cli 0.1.5 → 0.1.7

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/README.md CHANGED
@@ -16,6 +16,12 @@
16
16
 
17
17
  ---
18
18
 
19
+ ## Why I built this
20
+
21
+ I couldn't find a local CLI AI tool that actually worked well. The ones that existed were either too clunky to set up, required cloud APIs, or had terminal output that was genuinely painful to read — weird formatting, broken renders, text that ran together. I wanted something that felt as clean as Claude Code but ran entirely on local models. So I built miii.
22
+
23
+ ---
24
+
19
25
  ## What is miii?
20
26
 
21
27
  `miii` is a terminal-native AI coding assistant powered by local models via [Ollama](https://ollama.com) or any OpenAI-compatible API (LM Studio, vLLM, Groq, Together, etc.).
package/dist/init.js CHANGED
@@ -21,8 +21,6 @@ export async function lazyInit() {
21
21
  await skills.loadAll();
22
22
  // Print welcome banner to scrollback BEFORE Ink starts
23
23
  welcome(config.provider, config.model, process.cwd());
24
- // Move cursor to last terminal row so Ink renders at absolute bottom
25
- process.stdout.write(`\x1b[${process.stdout.rows ?? 24};1H`);
26
24
  // Ink renders ONLY the input bar (small footprint at bottom)
27
25
  // patchConsole: true (default) ensures console.log output appears above Ink
28
26
  const sessionName = argv.session || 'default';
package/dist/init.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"init.js","sourceRoot":"","sources":["../src/init.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,KAAK,CAAA;AAC5B,OAAO,KAAK,MAAM,OAAO,CAAA;AACzB,OAAO,QAAQ,MAAM,UAAU,CAAA;AAC/B,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AACxC,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AAChD,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAA;AAC5C,OAAO,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAA;AAE1C,MAAM,CAAC,KAAK,UAAU,QAAQ;IAC5B,MAAM,IAAI,GAAG,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE;QAC3C,MAAM,EAAE,CAAC,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE,SAAS,CAAC;QAC/C,KAAK,EAAE,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,UAAU,EAAE,CAAC,EAAE,SAAS,EAAE;KAC7D,CAAC,CAAA;IAEF,MAAM,MAAM,GAAG,UAAU,EAAE,CAAA;IAC3B,IAAI,IAAI,CAAC,KAAK;QAAE,MAAM,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,CAAA;IACzC,IAAI,IAAI,CAAC,GAAG;QAAE,MAAM,CAAC,OAAO,GAAG,IAAI,CAAC,GAAG,CAAA;IACvC,IAAI,IAAI,CAAC,QAAQ;QAAE,MAAM,CAAC,QAAQ,GAAG,IAAI,CAAC,QAAkC,CAAA;IAE5E,MAAM,MAAM,GAAG,IAAI,WAAW,EAAE,CAAA;IAChC,MAAM,MAAM,CAAC,OAAO,EAAE,CAAA;IAEtB,uDAAuD;IACvD,OAAO,CAAC,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,KAAK,EAAE,OAAO,CAAC,GAAG,EAAE,CAAC,CAAA;IAErD,qEAAqE;IACrE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,QAAQ,OAAO,CAAC,MAAM,CAAC,IAAI,IAAI,EAAE,KAAK,CAAC,CAAA;IAE5D,6DAA6D;IAC7D,4EAA4E;IAC5E,MAAM,WAAW,GAAI,IAAI,CAAC,OAAkB,IAAI,SAAS,CAAA;IAEzD,MAAM,EAAE,aAAa,EAAE,GAAG,MAAM,CAC9B,KAAK,CAAC,aAAa,CAAC,QAAQ,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,EAAE,OAAO,EAAE,WAAW,EAAE,CAAC,EAC3F,EAAE,WAAW,EAAE,KAAK,EAAE,CACvB,CAAA;IAED,MAAM,aAAa,EAAE,CAAA;AACvB,CAAC"}
1
+ {"version":3,"file":"init.js","sourceRoot":"","sources":["../src/init.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,KAAK,CAAA;AAC5B,OAAO,KAAK,MAAM,OAAO,CAAA;AACzB,OAAO,QAAQ,MAAM,UAAU,CAAA;AAC/B,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AACxC,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AAChD,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAA;AAC5C,OAAO,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAA;AAE1C,MAAM,CAAC,KAAK,UAAU,QAAQ;IAC5B,MAAM,IAAI,GAAG,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE;QAC3C,MAAM,EAAE,CAAC,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE,SAAS,CAAC;QAC/C,KAAK,EAAE,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,UAAU,EAAE,CAAC,EAAE,SAAS,EAAE;KAC7D,CAAC,CAAA;IAEF,MAAM,MAAM,GAAG,UAAU,EAAE,CAAA;IAC3B,IAAI,IAAI,CAAC,KAAK;QAAE,MAAM,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,CAAA;IACzC,IAAI,IAAI,CAAC,GAAG;QAAE,MAAM,CAAC,OAAO,GAAG,IAAI,CAAC,GAAG,CAAA;IACvC,IAAI,IAAI,CAAC,QAAQ;QAAE,MAAM,CAAC,QAAQ,GAAG,IAAI,CAAC,QAAkC,CAAA;IAE5E,MAAM,MAAM,GAAG,IAAI,WAAW,EAAE,CAAA;IAChC,MAAM,MAAM,CAAC,OAAO,EAAE,CAAA;IAEtB,uDAAuD;IACvD,OAAO,CAAC,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,KAAK,EAAE,OAAO,CAAC,GAAG,EAAE,CAAC,CAAA;IAErD,6DAA6D;IAC7D,4EAA4E;IAC5E,MAAM,WAAW,GAAI,IAAI,CAAC,OAAkB,IAAI,SAAS,CAAA;IAEzD,MAAM,EAAE,aAAa,EAAE,GAAG,MAAM,CAC9B,KAAK,CAAC,aAAa,CAAC,QAAQ,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,EAAE,OAAO,EAAE,WAAW,EAAE,CAAC,EAC3F,EAAE,WAAW,EAAE,KAAK,EAAE,CACvB,CAAA;IAED,MAAM,aAAa,EAAE,CAAA;AACvB,CAAC"}
@@ -1,4 +1,5 @@
1
1
  import { readFile, writeFile, deleteFile, listFiles, createDir, moveFile, guardPath } from '../files/ops.js';
2
+ import { existsSync } from 'fs';
2
3
  import { exec } from 'child_process';
3
4
  import { promisify } from 'util';
4
5
  const run = promisify(exec);
@@ -28,6 +29,18 @@ export const tools = [
28
29
  return entries.map(e => `${e.type === 'dir' ? 'd' : 'f'} ${e.rel}`).join('\n');
29
30
  },
30
31
  },
32
+ {
33
+ name: 'create_file',
34
+ description: 'Create a new file — fails if file already exists',
35
+ params: '{"path": "string", "content": "string"}',
36
+ execute: async ({ path, content }) => {
37
+ const safe = guardPath(path);
38
+ if (existsSync(safe))
39
+ throw new Error(`file already exists: ${path}`);
40
+ writeFile(safe, content);
41
+ return `created: ${path}`;
42
+ },
43
+ },
31
44
  {
32
45
  name: 'edit_file',
33
46
  description: 'Write/overwrite file content',
@@ -93,6 +106,9 @@ Rules:
93
106
  - Show the full content when creating or editing
94
107
  - Never delete without confirming
95
108
  - Be concise
96
- - Output plain text only — no markdown, no headers, no bold/italic, no bullet points with *, no fenced code blocks with backticks. Use indentation and plain labels instead. This is a CLI terminal, not a chat UI${extra}`;
109
+ - Output plain text only — never use markdown formatting in your responses
110
+ - No headers (no #, ##), no bold (**text**), no italic (*text*), no bullet points with *, no horizontal rules (---)
111
+ - No fenced code blocks with backticks in prose — the ONLY exception is when writing actual file content (e.g. a .md file the user asked you to create or edit)
112
+ - Use plain indentation and labels for structure. This is a terminal, not a chat UI${extra}`;
97
113
  }
98
114
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,UAAU,EAAE,SAAS,EAAE,SAAS,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAC5G,OAAO,EAAE,UAAU,EAAE,MAAM,IAAI,CAAA;AAC/B,OAAO,EAAE,IAAI,EAAE,MAAM,eAAe,CAAA;AACpC,OAAO,EAAE,SAAS,EAAE,MAAM,MAAM,CAAA;AAEhC,MAAM,GAAG,GAAG,SAAS,CAAC,IAAI,CAAC,CAAA;AAC3B,MAAM,eAAe,GAAG,MAAM,CAAA;AAS9B,MAAM,CAAC,MAAM,KAAK,GAAW;IAC3B;QACE,IAAI,EAAE,WAAW;QACjB,WAAW,EAAE,oBAAoB;QACjC,MAAM,EAAE,oBAAoB;QAC5B,OAAO,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE;YAC1B,IAAI,CAAC;gBAAC,OAAO,QAAQ,CAAC,SAAS,CAAC,IAAc,CAAC,CAAC,CAAA;YAAC,CAAC;YAClD,OAAO,CAAC,EAAE,CAAC;gBAAC,MAAM,IAAI,KAAK,CAAC,cAAc,CAAC,EAAE,CAAC,CAAA;YAAC,CAAC;QAClD,CAAC;KACF;IACD;QACE,IAAI,EAAE,YAAY;QAClB,WAAW,EAAE,yBAAyB;QACtC,MAAM,EAAE,uDAAuD;QAC/D,OAAO,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,SAAS,GAAG,KAAK,EAAE,EAAE,EAAE;YAC7C,MAAM,OAAO,GAAG,SAAS,CAAC,SAAS,CAAC,IAAc,CAAC,EAAE,SAAoB,CAAC,CAAA;YAC1E,IAAI,CAAC,OAAO,CAAC,MAAM;gBAAE,OAAO,SAAS,CAAA;YACrC,OAAO,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,KAAK,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QACjF,CAAC;KACF;IACD;QACE,IAAI,EAAE,aAAa;QACnB,WAAW,EAAE,kDAAkD;QAC/D,MAAM,EAAE,yCAAyC;QACjD,OAAO,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE;YACnC,MAAM,IAAI,GAAG,SAAS,CAAC,IAAc,CAAC,CAAA;YACtC,IAAI,UAAU,CAAC,IAAI,CAAC;gBAAE,MAAM,IAAI,KAAK,CAAC,wBAAwB,IAAI,EAAE,CAAC,CAAA;YACrE,SAAS,CAAC,IAAI,EAAE,OAAiB,CAAC,CAAA;YAClC,OAAO,YAAY,IAAI,EAAE,CAAA;QAC3B,CAAC;KACF;IACD;QACE,IAAI,EAAE,WAAW;QACjB,WAAW,EAAE,8BAA8B;QAC3C,MAAM,EAAE,yCAAyC;QACjD,OAAO,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE;YACnC,SAAS,CAAC,SAAS,CAAC,IAAc,CAAC,EAAE,OAAiB,CAAC,CAAA;YACvD,OAAO,YAAY,IAAI,EAAE,CAAA;QAC3B,CAAC;KACF;IACD;QACE,IAAI,EAAE,aAAa;QACnB,WAAW,EAAE,eAAe;QAC5B,MAAM,EAAE,oBAAoB;QAC5B,OAAO,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE;YAC1B,UAAU,CAAC,SAAS,CAAC,IAAc,CAAC,CAAC,CAAA;YACrC,OAAO,YAAY,IAAI,EAAE,CAAA;QAC3B,CAAC;KACF;IACD;QACE,IAAI,EAAE,aAAa;QACnB,WAAW,EAAE,4BAA4B;QACzC,MAAM,EAAE,uBAAuB;QAC/B,OAAO,EAAE,KAAK,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE;YAC7B,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,GAAG,CAAC,OAAiB,EAAE,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,EAAE,OAAO,EAAE,eAAe,EAAE,CAAC,CAAA;YACzG,OAAO,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,WAAW,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,CAAA;QACtF,CAAC;KACF;IACD;QACE,IAAI,EAAE,eAAe;QACrB,WAAW,EAAE,8CAA8C;QAC3D,MAAM,EAAE,oBAAoB;QAC5B,OAAO,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE;YAC1B,SAAS,CAAC,SAAS,CAAC,IAAc,CAAC,CAAC,CAAA;YACpC,OAAO,YAAY,IAAI,EAAE,CAAA;QAC3B,CAAC;KACF;IACD;QACE,IAAI,EAAE,WAAW;QACjB,WAAW,EAAE,oCAAoC;QACjD,MAAM,EAAE,oCAAoC;QAC5C,OAAO,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,EAAE,EAAE;YAC9B,QAAQ,CAAC,SAAS,CAAC,IAAc,CAAC,EAAE,SAAS,CAAC,EAAY,CAAC,CAAC,CAAA;YAC5D,OAAO,UAAU,IAAI,MAAM,EAAE,EAAE,CAAA;QACjC,CAAC;KACF;CACF,CAAA;AAED,MAAM,UAAU,eAAe,CAAC,KAAK,GAAG,EAAE;IACxC,MAAM,QAAQ,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,MAAM,MAAM,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IACxF,OAAO;;;;;;;;EAQP,QAAQ;;;;;;;;;;;;qFAY2E,KAAK,EAAE,CAAA;AAC5F,CAAC"}
@@ -64,7 +64,7 @@ export function ModelPicker({ models, current, loading, error, pull, onSelect, o
64
64
  }
65
65
  }
66
66
  });
67
- return (_jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1, children: [_jsxs(Box, { marginBottom: 1, children: [_jsx(Text, { bold: true, color: "cyan", children: " models " }), loading && _jsx(Text, { color: "yellow", children: " loading..." }), error && _jsxs(Text, { color: "red", children: [" ", error] })] }), mode === 'list' && (_jsxs(_Fragment, { children: [models.map((m, i) => {
67
+ return (_jsxs(Box, { flexDirection: "column", flexGrow: 1, borderStyle: "round", borderColor: "cyan", paddingX: 1, children: [_jsxs(Box, { marginBottom: 1, children: [_jsx(Text, { bold: true, color: "cyan", children: " models " }), loading && _jsx(Text, { color: "yellow", children: " loading..." }), error && _jsxs(Text, { color: "red", children: [" ", error] })] }), mode === 'list' && (_jsxs(_Fragment, { children: [models.map((m, i) => {
68
68
  const active = i === idx;
69
69
  const isCurrent = m.name === current;
70
70
  const age = new Date(m.modified_at).toLocaleDateString();
@@ -1 +1 @@
1
- {"version":3,"file":"ModelPicker.js","sourceRoot":"","sources":["../../../src/tui/components/ModelPicker.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ModelPicker.js","sourceRoot":"","sources":["../../../src/tui/components/ModelPicker.tsx"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "miii-cli",
3
- "version": "0.1.5",
3
+ "version": "0.1.7",
4
4
  "type": "module",
5
- "description": "Local AI coding assistant for your terminal. No cloud. No API keys.",
5
+ "description": "Claude Code-level terminal workflows powered by your local models.",
6
6
  "license": "MIT",
7
7
  "engines": {
8
8
  "node": ">=18"
package/src/init.ts CHANGED
@@ -5,6 +5,7 @@ import { loadConfig } from './config.js'
5
5
  import { SkillLoader } from './skills/loader.js'
6
6
  import { InputBar } from './tui/InputBar.js'
7
7
  import { welcome } from './tui/printer.js'
8
+ import { ensureOllama } from './llm/ollama.js'
8
9
 
9
10
  export async function lazyInit(): Promise<void> {
10
11
  const argv = minimist(process.argv.slice(2), {
@@ -17,6 +18,10 @@ export async function lazyInit(): Promise<void> {
17
18
  if (argv.url) config.baseUrl = argv.url
18
19
  if (argv.provider) config.provider = argv.provider as typeof config.provider
19
20
 
21
+ if (config.provider === 'ollama') {
22
+ await ensureOllama(config.baseUrl)
23
+ }
24
+
20
25
  const skills = new SkillLoader()
21
26
  await skills.loadAll()
22
27
 
package/src/llm/ollama.ts CHANGED
@@ -1,3 +1,5 @@
1
+ import { execSync, spawn } from 'child_process'
2
+
1
3
  export interface OllamaModel {
2
4
  name: string
3
5
  size: number
@@ -51,6 +53,56 @@ export async function pullModel(
51
53
  }
52
54
  }
53
55
 
56
+ export async function ensureOllama(baseUrl: string): Promise<void> {
57
+ if (await isReachable(baseUrl)) return
58
+
59
+ if (!isBinaryInstalled()) {
60
+ process.stderr.write('\nOllama not found. Install it:\n\n')
61
+ if (process.platform === 'darwin') {
62
+ process.stderr.write(' brew install ollama\n')
63
+ process.stderr.write(' — or download: https://ollama.ai/download\n')
64
+ } else if (process.platform === 'linux') {
65
+ process.stderr.write(' curl -fsSL https://ollama.ai/install.sh | sh\n')
66
+ } else {
67
+ process.stderr.write(' https://ollama.ai/download\n')
68
+ }
69
+ process.stderr.write('\nThen run: ollama serve\n\n')
70
+ process.exit(1)
71
+ }
72
+
73
+ process.stderr.write('Ollama not running — starting ollama serve...\n')
74
+ spawn('ollama', ['serve'], { detached: true, stdio: 'ignore' }).unref()
75
+
76
+ for (let i = 0; i < 12; i++) {
77
+ await new Promise(r => setTimeout(r, 500))
78
+ if (await isReachable(baseUrl)) {
79
+ process.stderr.write('Ollama ready.\n')
80
+ return
81
+ }
82
+ }
83
+
84
+ process.stderr.write('Could not start Ollama. Run manually: ollama serve\n')
85
+ process.exit(1)
86
+ }
87
+
88
+ async function isReachable(baseUrl: string): Promise<boolean> {
89
+ try {
90
+ const res = await fetch(`${baseUrl}/api/tags`, { signal: AbortSignal.timeout(2000) })
91
+ return res.ok
92
+ } catch {
93
+ return false
94
+ }
95
+ }
96
+
97
+ function isBinaryInstalled(): boolean {
98
+ try {
99
+ execSync(process.platform === 'win32' ? 'where ollama' : 'which ollama', { stdio: 'ignore' })
100
+ return true
101
+ } catch {
102
+ return false
103
+ }
104
+ }
105
+
54
106
  export function fmtSize(bytes: number): string {
55
107
  if (bytes >= 1e9) return `${(bytes / 1e9).toFixed(1)}GB`
56
108
  if (bytes >= 1e6) return `${(bytes / 1e6).toFixed(0)}MB`
@@ -1,4 +1,5 @@
1
1
  import { readFile, writeFile, deleteFile, listFiles, createDir, moveFile, guardPath } from '../files/ops.js'
2
+ import { existsSync } from 'fs'
2
3
  import { exec } from 'child_process'
3
4
  import { promisify } from 'util'
4
5
 
@@ -32,6 +33,17 @@ export const tools: Tool[] = [
32
33
  return entries.map(e => `${e.type === 'dir' ? 'd' : 'f'} ${e.rel}`).join('\n')
33
34
  },
34
35
  },
36
+ {
37
+ name: 'create_file',
38
+ description: 'Create a new file — fails if file already exists',
39
+ params: '{"path": "string", "content": "string"}',
40
+ execute: async ({ path, content }) => {
41
+ const safe = guardPath(path as string)
42
+ if (existsSync(safe)) throw new Error(`file already exists: ${path}`)
43
+ writeFile(safe, content as string)
44
+ return `created: ${path}`
45
+ },
46
+ },
35
47
  {
36
48
  name: 'edit_file',
37
49
  description: 'Write/overwrite file content',
@@ -98,5 +110,8 @@ Rules:
98
110
  - Show the full content when creating or editing
99
111
  - Never delete without confirming
100
112
  - Be concise
101
- - Output plain text only — no markdown, no headers, no bold/italic, no bullet points with *, no fenced code blocks with backticks. Use indentation and plain labels instead. This is a CLI terminal, not a chat UI${extra}`
113
+ - Output plain text only — never use markdown formatting in your responses
114
+ - No headers (no #, ##), no bold (**text**), no italic (*text*), no bullet points with *, no horizontal rules (---)
115
+ - No fenced code blocks with backticks in prose — the ONLY exception is when writing actual file content (e.g. a .md file the user asked you to create or edit)
116
+ - Use plain indentation and labels for structure. This is a terminal, not a chat UI${extra}`
102
117
  }
@@ -51,6 +51,7 @@ export function InputBar({ config, skills, cwd, session }: Props) {
51
51
  const [currentModel, setCurrentModel] = useState(config.model)
52
52
  const [streamPreview, setStreamPreview] = useState('')
53
53
  const [sessionName, setSessionName] = useState(session)
54
+ const [planningMode, setPlanningMode] = useState(false)
54
55
 
55
56
  // picker opens on mount — force model selection every launch
56
57
  const [pickerOpen, setPickerOpen] = useState(true)
@@ -222,6 +223,8 @@ export function InputBar({ config, skills, cwd, session }: Props) {
222
223
  const newName = new Date().toISOString().slice(0, 19).replace(/[:T]/g, '-')
223
224
  historyRef.current = []
224
225
  setSessionName(newName)
226
+ setPlanningMode(false)
227
+ systemPromptRef.current = getSystemPrompt(`\n- CWD: ${cwd}`)
225
228
  printer.systemMsg(`new session → ${newName}`)
226
229
  return
227
230
  }
@@ -229,12 +232,54 @@ export function InputBar({ config, skills, cwd, session }: Props) {
229
232
  if (cmd === '/clear') {
230
233
  historyRef.current = []
231
234
  saveSession(sessionNameRef.current, [])
235
+ setPlanningMode(false)
236
+ systemPromptRef.current = getSystemPrompt(`\n- CWD: ${cwd}`)
232
237
  printer.systemMsg('chat cleared')
233
238
  return
234
239
  }
235
240
 
236
241
  if (cmd === '/exit') { process.exit(0) }
237
242
 
243
+ if (cmd === '/plan' || cmd.startsWith('/plan ')) {
244
+ const topic = cmd.slice(5).trim()
245
+ setPlanningMode(true)
246
+ systemPromptRef.current = getSystemPrompt(
247
+ `\n- CWD: ${cwd}\n- MODE: Planning assistant. Help the user plan step by step. Ask clarifying questions. Suggest concrete next steps. Use plain text only — no markdown, no headers, no bold, no bullets with asterisks, no backtick blocks. Use numbered lists and plain indentation for structure.`
248
+ )
249
+ const msg = topic
250
+ ? `I want to plan: ${topic}`
251
+ : 'I want to start planning. Help me think through my goals step by step.'
252
+ printer.userMsg(msg)
253
+ historyRef.current.push({ role: 'user', content: msg })
254
+ saveSession(sessionNameRef.current, historyRef.current)
255
+ await runLoop(buildContext())
256
+ return
257
+ }
258
+
259
+ if (cmd === '/plan:done') {
260
+ setPlanningMode(false)
261
+ systemPromptRef.current = getSystemPrompt(`\n- CWD: ${cwd}`)
262
+ printer.systemMsg('planning mode off')
263
+ return
264
+ }
265
+
266
+ if (cmd.startsWith('/plan:')) {
267
+ const subCmd = cmd.slice(6)
268
+ const subPrompts: Record<string, string> = {
269
+ next: 'What are the next concrete steps I should take?',
270
+ breakdown: 'Can you break this down into specific subtasks?',
271
+ review: 'Please review and critique our plan so far. What are we missing?',
272
+ }
273
+ const msg = subPrompts[subCmd]
274
+ if (msg) {
275
+ printer.userMsg(msg)
276
+ historyRef.current.push({ role: 'user', content: msg })
277
+ saveSession(sessionNameRef.current, historyRef.current)
278
+ await runLoop(buildContext())
279
+ return
280
+ }
281
+ }
282
+
238
283
  if (cmd === '/sessions') {
239
284
  const sessions = listSessions()
240
285
  if (!sessions.length) { printer.systemMsg('no saved sessions'); return }
@@ -339,6 +384,7 @@ export function InputBar({ config, skills, cwd, session }: Props) {
339
384
  status={status}
340
385
  skills={skillList}
341
386
  cwd={cwd}
387
+ planningMode={planningMode}
342
388
  onSubmit={handleSubmit}
343
389
  onAbort={handleAbort}
344
390
  />
@@ -16,6 +16,14 @@ const BUILTIN_COMMANDS: Skill[] = [
16
16
  { ns: 'builtin', name: 'session', description: 'switch session /session <name>' },
17
17
  { ns: 'builtin', name: 'exit', description: 'exit miii' },
18
18
  { ns: 'builtin', name: 'list', description: 'list all loaded skills' },
19
+ { ns: 'builtin', name: 'plan', description: 'start planning mode /plan [topic]' },
20
+ ]
21
+
22
+ const PLANNING_COMMANDS: Skill[] = [
23
+ { ns: 'plan', name: 'next', description: 'suggest next concrete steps' },
24
+ { ns: 'plan', name: 'breakdown', description: 'break current topic into subtasks' },
25
+ { ns: 'plan', name: 'review', description: 'review and critique the plan so far' },
26
+ { ns: 'plan', name: 'done', description: 'exit planning mode' },
19
27
  ]
20
28
 
21
29
  type Overlay = 'none' | 'command' | 'at'
@@ -24,11 +32,12 @@ interface Props {
24
32
  status: Status
25
33
  skills: Skill[]
26
34
  cwd: string
35
+ planningMode?: boolean
27
36
  onSubmit: (text: string) => void
28
37
  onAbort: () => void
29
38
  }
30
39
 
31
- export function InputArea({ status, skills, cwd, onSubmit, onAbort }: Props) {
40
+ export function InputArea({ status, skills, cwd, planningMode, onSubmit, onAbort }: Props) {
32
41
  const [lines, setLines] = useState<string[]>([''])
33
42
  const [cursor, setCursor] = useState({ row: 0, col: 0 })
34
43
  const [overlay, setOverlay] = useState<Overlay>('none')
@@ -42,8 +51,9 @@ export function InputArea({ status, skills, cwd, onSubmit, onAbort }: Props) {
42
51
  const allCommands = useMemo(() => {
43
52
  const builtinNames = new Set(BUILTIN_COMMANDS.map(b => b.name))
44
53
  const userSkills = skills.filter(s => !builtinNames.has(s.name))
45
- return [...BUILTIN_COMMANDS, ...userSkills]
46
- }, [skills])
54
+ const base = [...BUILTIN_COMMANDS, ...userSkills]
55
+ return planningMode ? [...PLANNING_COMMANDS, ...base] : base
56
+ }, [skills, planningMode])
47
57
 
48
58
  const isActive = status === 'idle'
49
59
  const fullInput = lines.join('\n')
@@ -245,6 +255,8 @@ export function InputArea({ status, skills, cwd, onSubmit, onAbort }: Props) {
245
255
  ? '↑↓ navigate enter select esc close'
246
256
  : overlay === 'at'
247
257
  ? '↑↓ navigate enter select esc close'
258
+ : planningMode
259
+ ? '📋 planning mode / suggestions enter send /plan:done to exit'
248
260
  : '@ file / command enter send ctrl+c exit'
249
261
 
250
262
  return (