@emend-ai/utim 1.0.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.
- package/LICENSE +21 -0
- package/README.md +82 -0
- package/bin/utim.js +85 -0
- package/package.json +44 -0
- package/scripts/postinstall.js +90 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 UTIM Dev Team
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# UTIM CLI Agent: Enterprise Coder Assistant
|
|
2
|
+
|
|
3
|
+
UTIM is an agentic developer CLI assistant designed to automate coding tasks directly inside your local terminal, featuring robust safety controls, self-healing quality gates, and local-first semantic memory.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## š Quick Start
|
|
8
|
+
|
|
9
|
+
### 1. Installation
|
|
10
|
+
Install the package from your local source directory:
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
# Basic installation
|
|
14
|
+
pip install .
|
|
15
|
+
|
|
16
|
+
# Recommended: Full installation (includes semantic vector RAG & web search)
|
|
17
|
+
pip install ".[full]"
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
### 2. Provider & Model Configuration
|
|
21
|
+
Start the chat session:
|
|
22
|
+
```bash
|
|
23
|
+
utim
|
|
24
|
+
```
|
|
25
|
+
On first run, UTIM checks for `.utim/config.json`. If it does not exist, it runs a provider and model configuration wizard. You can configure:
|
|
26
|
+
- **Default Providers**: OpenRouter, OpenAI, custom servers, etc.
|
|
27
|
+
- **Model Picker**: Press `Ctrl+M` in the chat terminal at any time to configure, pick, add, or delete LLMs.
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## ā” Main CLI Commands
|
|
32
|
+
|
|
33
|
+
- **`utim`**: Starts the interactive chat terminal (TUI).
|
|
34
|
+
- **`utim task "<prompt>"`**: Executes a single task autonomously from the command line and exits. In an interactive terminal (`stdin` is a TTY) file writes and commands prompt for confirmation; when piped/scripted they run in auto-accept mode.
|
|
35
|
+
- **`utim --dry-run`**: Starts the session in **Dry-Run Mode** (all code modifications and shell commands are simulated, not written/executed).
|
|
36
|
+
- **`utim --sandbox`**: Runs all mutating shell command proposals in the intelligent local sandbox (untrusted commands will block until approved).
|
|
37
|
+
- **`utim doctor`** / **`utim init`** / **`utim reset`**: Administrative commands for state diagnosis, initialization, and factory resets.
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## š ļø In-Chat Slash Commands
|
|
42
|
+
|
|
43
|
+
Inside the interactive chat terminal, type these slash commands for direct workspace control:
|
|
44
|
+
|
|
45
|
+
- **`/undo`**: Reverts the last assistant action, restoring files to their exact "before" state and rolling back messages.
|
|
46
|
+
- **`/redo`**: Re-applies the last undone turn, re-writing files and restoring conversation logs.
|
|
47
|
+
- **`/rewind <turn_index>`**: Rolls back the entire session to a specific conversation turn.
|
|
48
|
+
- **`/doctor`**: Run diagnostics on environment variables, Python version, dependencies, API model connections, and MCP server status.
|
|
49
|
+
- **`/report`**: Generates a support bundle under `.utim_tmp/report_bundle.zip` (automatically redacts secrets, passwords, or personal names/files).
|
|
50
|
+
- **`/reset`**: Wipes the current chat history without deleting persistent local vector memory.
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## š§ Architecture & How it Works
|
|
55
|
+
|
|
56
|
+
1. **Local Memory (`.utim/memory.json` & ChromaDB)**:
|
|
57
|
+
- Global user preferences, rules, and facts are synced to a semantic vector database (`.utim_tmp/vector_db`).
|
|
58
|
+
- Relevant memories are dynamically fetched via semantic similarity (RAG) and injected into the system prompt context, preventing prompt bloating.
|
|
59
|
+
2. **Undo/Redo Stack & Session State**:
|
|
60
|
+
- Every file change (writes, batch string edits, moves, deletions) computes a diff snapshot.
|
|
61
|
+
- The entire stack is serialized dynamically to `.utim/session_state.json`. You can close your shell, shut down your computer, and resume later with intact rollback features.
|
|
62
|
+
3. **Workspace Boundary & Safety Controls**:
|
|
63
|
+
- Prior to writing files, UTIM performs **Pre-Commit Syntax Checks** (AST compilation for Python, JSON loads, JS/TS checks).
|
|
64
|
+
- If tests are available (`pytest`, `npm test`, etc.), UTIM runs them in a background **Regression testing loop**, prompting the model to self-heal code errors if assertions fail.
|
|
65
|
+
- **Interactive TUI mode** (`utim`): every file mutation shows an interactive diff dialog; the developer accepts, edits, or rejects individual hunks before they are applied.
|
|
66
|
+
- **CLI task mode** (`utim task`): when running in a real terminal, destructive operations (`rm`, package installs, `>` redirects) prompt for `y/n` confirmation. When stdin is piped/non-interactive all edits are auto-accepted.
|
|
67
|
+
- **Sandbox mode** (`utim --sandbox`): classifies every terminal command as safe or risky and blocks risky commands until explicitly approved.
|
|
68
|
+
|
|
69
|
+
---
|
|
70
|
+
|
|
71
|
+
## ā ļø What this Tool Can and Cannot Do
|
|
72
|
+
|
|
73
|
+
### Can Do:
|
|
74
|
+
- Read, write, and patch codebases safely.
|
|
75
|
+
- Install and coordinate custom MCP (Model Context Protocol) servers.
|
|
76
|
+
- Self-heal syntax and test errors before files are written.
|
|
77
|
+
- Revert any file modification instantly.
|
|
78
|
+
|
|
79
|
+
### Cannot Do:
|
|
80
|
+
- **No Remote Code Execution**: Runs locally on your machine.
|
|
81
|
+
- **Unverified Sensitive Reads Blocked**: Reading files or memory matching sensitive keys (like passwords, secret codes, or personal data) is blocked unless verified via your configured verification code.
|
|
82
|
+
- **No Auto-Deletions**: Any command that deletes files outside the working directory is blocked automatically.
|
package/bin/utim.js
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const { spawnSync, spawn } = require('child_process');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const fs = require('fs');
|
|
6
|
+
|
|
7
|
+
const args = process.argv.slice(2);
|
|
8
|
+
|
|
9
|
+
// āā Find a working Python 3 interpreter āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
10
|
+
function findPython() {
|
|
11
|
+
for (const candidate of ['python3', 'python']) {
|
|
12
|
+
try {
|
|
13
|
+
const r = spawnSync(candidate, ['--version'], { encoding: 'utf8', timeout: 3000, shell: true });
|
|
14
|
+
const out = (r.stdout || '') + (r.stderr || '');
|
|
15
|
+
if (out.includes('Python 3')) return candidate;
|
|
16
|
+
} catch (_) {}
|
|
17
|
+
}
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// āā Attempt to launch a command, return true if successful āāāāāāāāāāāāāāāāāāā
|
|
22
|
+
function tryLaunch(cmd, launchArgs) {
|
|
23
|
+
try {
|
|
24
|
+
const probe = spawnSync(cmd, ['--version'], { shell: true, timeout: 3000, encoding: 'utf8' });
|
|
25
|
+
const out = (probe.stdout || '') + (probe.stderr || '');
|
|
26
|
+
// accept if exit 0 OR if it printed something (some tools exit non-0 for --version)
|
|
27
|
+
if (probe.status === 0 || out.length > 0) {
|
|
28
|
+
const proc = spawn(cmd, launchArgs, { stdio: 'inherit', shell: true });
|
|
29
|
+
proc.on('exit', (code) => process.exit(code == null ? 0 : code));
|
|
30
|
+
return true;
|
|
31
|
+
}
|
|
32
|
+
} catch (_) {}
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// āā Try the installed console_script entry point āāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
37
|
+
function tryEntryPoint() {
|
|
38
|
+
return tryLaunch('utim-agent-core', args);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// āā Try via `python -m utim_cli.utim` āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
42
|
+
function tryModule(python) {
|
|
43
|
+
try {
|
|
44
|
+
const probe = spawnSync(python, ['-m', 'utim_cli', '--help'], { shell: true, timeout: 5000 });
|
|
45
|
+
if (probe.status === 0) {
|
|
46
|
+
const proc = spawn(python, ['-m', 'utim_cli.utim', ...args], { stdio: 'inherit', shell: true });
|
|
47
|
+
proc.on('exit', (code) => process.exit(code == null ? 0 : code));
|
|
48
|
+
return true;
|
|
49
|
+
}
|
|
50
|
+
} catch (_) {}
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// āā Last resort: install from PyPI and retry āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
55
|
+
function selfInstallAndRun(python) {
|
|
56
|
+
console.error('\nā First run: installing UTIM Python engine from PyPI...');
|
|
57
|
+
const install = spawnSync(python, ['-m', 'pip', 'install', '--upgrade', 'utim-cli'], {
|
|
58
|
+
stdio: 'inherit',
|
|
59
|
+
shell: true,
|
|
60
|
+
});
|
|
61
|
+
if (install.status === 0) {
|
|
62
|
+
// Retry the entry point after install
|
|
63
|
+
return tryEntryPoint() || tryModule(python);
|
|
64
|
+
}
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// āā Main āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
69
|
+
const python = findPython();
|
|
70
|
+
|
|
71
|
+
const ok =
|
|
72
|
+
tryEntryPoint() ||
|
|
73
|
+
(python && tryModule(python)) ||
|
|
74
|
+
(python && selfInstallAndRun(python));
|
|
75
|
+
|
|
76
|
+
if (!ok) {
|
|
77
|
+
console.error('\nError: UTIM Engine could not start.');
|
|
78
|
+
if (!python) {
|
|
79
|
+
console.error('Python 3 was not found. On Termux run: pkg install python');
|
|
80
|
+
} else {
|
|
81
|
+
console.error(`Tried Python at: ${python}`);
|
|
82
|
+
console.error('Run manually: pip install emendai-utim-agent or pip3 install --upgrade utim_cli');
|
|
83
|
+
}
|
|
84
|
+
process.exit(1);
|
|
85
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@emend-ai/utim",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "UTIM ā Universal Terminal Intelligence Manager. An agentic AI coding assistant for your terminal.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"ai",
|
|
7
|
+
"agent",
|
|
8
|
+
"cli",
|
|
9
|
+
"coding-assistant",
|
|
10
|
+
"agentic",
|
|
11
|
+
"llm",
|
|
12
|
+
"mcp",
|
|
13
|
+
"utim"
|
|
14
|
+
],
|
|
15
|
+
"homepage": "https://utim.ai",
|
|
16
|
+
"bugs": {
|
|
17
|
+
"url": "https://github.com/emendai/utim/issues"
|
|
18
|
+
},
|
|
19
|
+
"repository": {
|
|
20
|
+
"type": "git",
|
|
21
|
+
"url": "https://github.com/emendai/utim.git"
|
|
22
|
+
},
|
|
23
|
+
"license": "MIT",
|
|
24
|
+
"author": "Emendai <support@emendai.com>",
|
|
25
|
+
"bin": {
|
|
26
|
+
"utim": "bin/utim.js"
|
|
27
|
+
},
|
|
28
|
+
"scripts": {
|
|
29
|
+
"postinstall": "node scripts/postinstall.js"
|
|
30
|
+
},
|
|
31
|
+
"files": [
|
|
32
|
+
"bin/utim.js",
|
|
33
|
+
"scripts/postinstall.js",
|
|
34
|
+
"README.md",
|
|
35
|
+
"LICENSE"
|
|
36
|
+
],
|
|
37
|
+
"engines": {
|
|
38
|
+
"node": ">=16.0.0"
|
|
39
|
+
},
|
|
40
|
+
"publishConfig": {
|
|
41
|
+
"access": "public",
|
|
42
|
+
"registry": "https://registry.npmjs.org/"
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cross-platform postinstall script for @emend-ai/utim.
|
|
3
|
+
*
|
|
4
|
+
* After `npm install -g @emend-ai/utim`, this script runs automatically to:
|
|
5
|
+
* 1. Locate a Python 3 interpreter on the user's machine.
|
|
6
|
+
* 2. Install the UTIM Python engine from PyPI: pip install utim-cli
|
|
7
|
+
* 3. Set the execute bit on bin/utim.js (Unix / Termux only).
|
|
8
|
+
*
|
|
9
|
+
* The Python source code is NOT bundled inside this npm package.
|
|
10
|
+
* It is fetched from PyPI at install time.
|
|
11
|
+
*/
|
|
12
|
+
const { spawnSync } = require('child_process');
|
|
13
|
+
const path = require('path');
|
|
14
|
+
const os = require('os');
|
|
15
|
+
const fs = require('fs');
|
|
16
|
+
|
|
17
|
+
const pkgDir = path.resolve(__dirname, '..');
|
|
18
|
+
|
|
19
|
+
// āā Find a usable Python 3 interpreter āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
20
|
+
function findPython() {
|
|
21
|
+
for (const candidate of ['python3', 'python']) {
|
|
22
|
+
try {
|
|
23
|
+
const r = spawnSync(candidate, ['--version'], {
|
|
24
|
+
encoding: 'utf8',
|
|
25
|
+
timeout: 4000,
|
|
26
|
+
shell: true,
|
|
27
|
+
});
|
|
28
|
+
const out = (r.stdout || '') + (r.stderr || '');
|
|
29
|
+
if (out.includes('Python 3')) return candidate;
|
|
30
|
+
} catch (_) {}
|
|
31
|
+
}
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// āā Install UTIM Python engine from PyPI āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
36
|
+
function installFromPyPI(python) {
|
|
37
|
+
console.log('\nā Installing UTIM Python engine from PyPI (pip install utim-cli)...\n');
|
|
38
|
+
try {
|
|
39
|
+
const r = spawnSync(
|
|
40
|
+
python,
|
|
41
|
+
['-m', 'pip', 'install', '--upgrade', 'utim-cli'],
|
|
42
|
+
{ stdio: 'inherit', shell: true }
|
|
43
|
+
);
|
|
44
|
+
return r.status === 0;
|
|
45
|
+
} catch (_) {
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// āā Set execute bit on Unix / Termux āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
51
|
+
function setExecuteBit() {
|
|
52
|
+
if (os.platform() !== 'win32') {
|
|
53
|
+
const binFile = path.join(pkgDir, 'bin', 'utim.js');
|
|
54
|
+
try {
|
|
55
|
+
fs.chmodSync(binFile, 0o755);
|
|
56
|
+
} catch (_) {
|
|
57
|
+
// non-fatal ā npm usually sets the bit itself via package.json "bin"
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// āā Main āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
63
|
+
const python = findPython();
|
|
64
|
+
|
|
65
|
+
if (!python) {
|
|
66
|
+
console.error(
|
|
67
|
+
'\nā Python 3 not found on this machine.\n' +
|
|
68
|
+
' Please install Python 3.9+ from https://python.org and then run:\n' +
|
|
69
|
+
' pip install utim-cli\n' +
|
|
70
|
+
' to finish setting up UTIM.\n'
|
|
71
|
+
);
|
|
72
|
+
// Exit 0 so npm does not roll back the install ā user can pip install manually
|
|
73
|
+
process.exit(0);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const ok = installFromPyPI(python);
|
|
77
|
+
setExecuteBit();
|
|
78
|
+
|
|
79
|
+
if (ok) {
|
|
80
|
+
console.log('\nā
UTIM installed successfully. Run utim to get started.\n');
|
|
81
|
+
} else {
|
|
82
|
+
console.warn(
|
|
83
|
+
'\nā pip install failed. Try running manually:\n' +
|
|
84
|
+
' pip install utim-cli\n' +
|
|
85
|
+
' or:\n' +
|
|
86
|
+
' pip3 install utim-cli\n'
|
|
87
|
+
);
|
|
88
|
+
// Exit 0 ā don't break the npm install; user can manually pip install
|
|
89
|
+
process.exit(0);
|
|
90
|
+
}
|