@visorcraft/idlehands 1.0.0 โ 1.0.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.
- package/README.md +151 -12
- package/dist/agent.js +21 -5
- package/dist/agent.js.map +1 -1
- package/dist/anton/controller.js +2 -2
- package/dist/bot/commands.js +29 -4
- package/dist/bot/commands.js.map +1 -1
- package/dist/bot/discord.js +93 -69
- package/dist/bot/discord.js.map +1 -1
- package/dist/bot/format.js +5 -0
- package/dist/bot/format.js.map +1 -1
- package/dist/bot/telegram.js +23 -7
- package/dist/bot/telegram.js.map +1 -1
- package/dist/cli/args.js +2 -1
- package/dist/cli/args.js.map +1 -1
- package/dist/cli/commands/session.js +26 -6
- package/dist/cli/commands/session.js.map +1 -1
- package/dist/cli/setup.js +133 -42
- package/dist/cli/setup.js.map +1 -1
- package/dist/config.js +30 -7
- package/dist/config.js.map +1 -1
- package/dist/index.js +8 -1
- package/dist/index.js.map +1 -1
- package/dist/tools.js +4 -0
- package/dist/tools.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,30 +1,169 @@
|
|
|
1
|
-
# Idle Hands
|
|
1
|
+
# Idle Hands
|
|
2
2
|
|
|
3
|
-
Local-first coding agent CLI for OpenAI-compatible endpoints
|
|
3
|
+
**Local-first coding agent CLI for OpenAI-compatible endpoints.**
|
|
4
|
+
Fast in the terminal, practical in production, and built to run close to your code.
|
|
4
5
|
|
|
5
|
-
|
|
6
|
+
๐ Full docs: https://visorcraft.github.io/IdleHands/
|
|
6
7
|
|
|
7
|
-
|
|
8
|
+
---
|
|
8
9
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
-
|
|
14
|
-
-
|
|
15
|
-
-
|
|
10
|
+
## Why Idle Hands
|
|
11
|
+
|
|
12
|
+
Idle Hands is built for people who want an agent that can actually ship work, not just chat:
|
|
13
|
+
|
|
14
|
+
- **TUI-first UX** for real daily use (streaming output, slash commands, approvals)
|
|
15
|
+
- **Runtime orchestration** (hosts/backends/models) for local + remote model stacks
|
|
16
|
+
- **Safety + approvals** with explicit modes (`plan`, `reject`, `default`, `auto-edit`, `yolo`)
|
|
17
|
+
- **Headless mode** for CI and scripts (`json`, `stream-json`, `--fail-on-error`, `--diff-only`)
|
|
18
|
+
- **Bot frontends** (Telegram + Discord) with service management
|
|
19
|
+
- **Trifecta subsystem** (Vault + Replay + Lens) for durable memory, reversibility, and context shaping
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## What makes Idle Hands unique: Trifecta
|
|
24
|
+
|
|
25
|
+
Trifecta is the integrated core of Idle Hands:
|
|
26
|
+
|
|
27
|
+
- **Vault** โ persistent memory + notes (`/vault`, `/note`, `/notes`)
|
|
28
|
+
- **Replay** โ file checkpoints and rewind/diff (`/checkpoints`, `/rewind`, `/diff`, `/undo`)
|
|
29
|
+
- **Lens** โ structural compression/indexing for better context usage
|
|
30
|
+
|
|
31
|
+
Runtime controls:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
--no-trifecta
|
|
35
|
+
--no-vault
|
|
36
|
+
--no-lens
|
|
37
|
+
--no-replay
|
|
38
|
+
--vault-mode active|passive|off
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Detailed docs: [Trifecta guide](https://visorcraft.github.io/IdleHands/guide/trifecta)
|
|
42
|
+
|
|
43
|
+
---
|
|
16
44
|
|
|
17
45
|
## Install
|
|
18
46
|
|
|
47
|
+
### npm (recommended)
|
|
48
|
+
|
|
19
49
|
```bash
|
|
20
|
-
npm i -g
|
|
50
|
+
npm i -g @visorcraft/idlehands@latest
|
|
21
51
|
idlehands --help
|
|
22
52
|
```
|
|
23
53
|
|
|
54
|
+
### Build from source
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
git clone https://github.com/visorcraft/idlehands.git
|
|
58
|
+
cd idlehands
|
|
59
|
+
npm i
|
|
60
|
+
npm run build
|
|
61
|
+
node dist/index.js --help
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Requirements:
|
|
65
|
+
|
|
66
|
+
- Node.js **24+**
|
|
67
|
+
- Linux (recommended target environment)
|
|
68
|
+
|
|
69
|
+
---
|
|
70
|
+
|
|
24
71
|
## Quick start
|
|
25
72
|
|
|
26
73
|
```bash
|
|
27
74
|
idlehands setup
|
|
28
75
|
idlehands
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
One-shot mode:
|
|
79
|
+
|
|
80
|
+
```bash
|
|
29
81
|
idlehands -p "run npm test and fix failures"
|
|
30
82
|
```
|
|
83
|
+
|
|
84
|
+
Project-scoped session:
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
idlehands --dir ~/projects/myapp
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Resume/fresh controls:
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
idlehands --continue
|
|
94
|
+
idlehands --resume
|
|
95
|
+
idlehands --resume my-session
|
|
96
|
+
idlehands --fresh
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
## Linux hardening (recommended)
|
|
102
|
+
|
|
103
|
+
If you run Idle Hands regularly on Linux, use a dedicated low-privilege account.
|
|
104
|
+
|
|
105
|
+
### 1) Create a restricted user
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
sudo useradd --system --create-home --home-dir /home/idlehands --shell /bin/bash idlehands
|
|
109
|
+
sudo passwd -l idlehands
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### 2) Give it only the project dirs it needs
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
sudo mkdir -p /home/idlehands/work
|
|
116
|
+
sudo chown -R idlehands:idlehands /home/idlehands
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### 3) Run Idle Hands as that user
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
sudo -u idlehands -H bash -lc 'idlehands setup'
|
|
123
|
+
sudo -u idlehands -H bash -lc 'idlehands --dir /home/idlehands/work'
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
This limits blast radius if the agent runs bad commands, and keeps your main user environment cleaner.
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
## Running bots as a service
|
|
131
|
+
|
|
132
|
+
Idle Hands can manage a user-level systemd service for bot frontends.
|
|
133
|
+
|
|
134
|
+
```bash
|
|
135
|
+
idlehands service install
|
|
136
|
+
idlehands service status
|
|
137
|
+
idlehands service start
|
|
138
|
+
idlehands service logs
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
To keep user services running after logout:
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
sudo loginctl enable-linger <user>
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
If you use a dedicated `idlehands` account, install/manage the service while logged in as that user.
|
|
148
|
+
|
|
149
|
+
---
|
|
150
|
+
|
|
151
|
+
## Approval modes at a glance
|
|
152
|
+
|
|
153
|
+
- `plan` โ dry plan only, no mutations
|
|
154
|
+
- `reject` โ non-interactive safe mode, rejects mutating operations
|
|
155
|
+
- `default` โ asks before risky actions
|
|
156
|
+
- `auto-edit` โ allows normal code-edit flow, still safety-aware
|
|
157
|
+
- `yolo` / `--no-confirm` โ no confirmations (fastest, riskiest)
|
|
158
|
+
|
|
159
|
+
---
|
|
160
|
+
|
|
161
|
+
## Documentation map
|
|
162
|
+
|
|
163
|
+
- [Getting Started](https://visorcraft.github.io/IdleHands/guide/getting-started)
|
|
164
|
+
- [Trifecta Guide](https://visorcraft.github.io/IdleHands/guide/trifecta)
|
|
165
|
+
- [Runtime Orchestration](https://visorcraft.github.io/IdleHands/guide/runtime)
|
|
166
|
+
- [Bots + Service](https://visorcraft.github.io/IdleHands/guide/bots)
|
|
167
|
+
- [CLI Reference](https://visorcraft.github.io/IdleHands/reference/cli)
|
|
168
|
+
- [Config Reference](https://visorcraft.github.io/IdleHands/reference/config)
|
|
169
|
+
- [Safety Model](https://visorcraft.github.io/IdleHands/reference/safety)
|
package/dist/agent.js
CHANGED
|
@@ -127,6 +127,7 @@ const SYSTEM_PROMPT = `You are a coding agent with filesystem and shell access.
|
|
|
127
127
|
Rules:
|
|
128
128
|
- Work in the current directory. Use relative paths for all file operations.
|
|
129
129
|
- Do the work directly. Do NOT use spawn_task to delegate the user's primary request โ only use it for genuinely independent subtasks that benefit from parallel execution.
|
|
130
|
+
- Never use spawn_task to bypass confirmation/safety restrictions (for example blocked package installs). If a command is blocked, adapt the plan or ask the user for approval mode changes.
|
|
130
131
|
- Read the target file before editing. You need the exact text for search/replace.
|
|
131
132
|
- Use read_file with search=... to jump to relevant code; avoid reading whole files.
|
|
132
133
|
- Use edit_file for surgical changes. Never rewrite entire files when a targeted edit works.
|
|
@@ -152,7 +153,7 @@ const DEFAULT_SUB_AGENT_SYSTEM_PROMPT = `You are a focused coding sub-agent. Exe
|
|
|
152
153
|
- When running commands in a subdirectory, use exec's cwd parameter โ NOT "cd /path && cmd".
|
|
153
154
|
- Run verification commands when relevant.
|
|
154
155
|
- Return a concise outcome summary.`;
|
|
155
|
-
const DEFAULT_SUB_AGENT_RESULT_TOKEN_CAP =
|
|
156
|
+
const DEFAULT_SUB_AGENT_RESULT_TOKEN_CAP = 4000;
|
|
156
157
|
const APPROVAL_MODE_SET = new Set(['plan', 'reject', 'default', 'auto-edit', 'yolo']);
|
|
157
158
|
const LSP_TOOL_NAMES = ['lsp_diagnostics', 'lsp_symbols', 'lsp_hover', 'lsp_definition', 'lsp_references'];
|
|
158
159
|
const LSP_TOOL_NAME_SET = new Set(LSP_TOOL_NAMES);
|
|
@@ -733,6 +734,17 @@ function userContentToText(content) {
|
|
|
733
734
|
.join('\n')
|
|
734
735
|
.trim();
|
|
735
736
|
}
|
|
737
|
+
function userDisallowsDelegation(content) {
|
|
738
|
+
const text = userContentToText(content).toLowerCase();
|
|
739
|
+
if (!text)
|
|
740
|
+
return false;
|
|
741
|
+
const mentionsDelegation = /\b(?:spawn[_\-\s]?task|sub[\-\s]?agents?|delegate|delegation)\b/.test(text);
|
|
742
|
+
if (!mentionsDelegation)
|
|
743
|
+
return false;
|
|
744
|
+
const negationNearDelegation = /\b(?:do not|don't|dont|no|without|avoid|skip|never)\b[^\n.]{0,90}\b(?:spawn[_\-\s]?task|sub[\-\s]?agents?|delegate|delegation)\b/.test(text) ||
|
|
745
|
+
/\b(?:spawn[_\-\s]?task|sub[\-\s]?agents?|delegate|delegation)\b[^\n.]{0,50}\b(?:do not|don't|dont|not allowed|forbidden|no)\b/.test(text);
|
|
746
|
+
return negationNearDelegation;
|
|
747
|
+
}
|
|
736
748
|
function supportsVisionModel(model, modelMeta, harness) {
|
|
737
749
|
if (typeof harness.supportsVision === 'boolean')
|
|
738
750
|
return harness.supportsVision;
|
|
@@ -967,7 +979,7 @@ export async function createSession(opts) {
|
|
|
967
979
|
const subDefaults = cfg.sub_agents ?? {};
|
|
968
980
|
const subMaxIter = Number.isFinite(subDefaults.max_iterations)
|
|
969
981
|
? Math.max(1, Math.floor(Number(subDefaults.max_iterations)))
|
|
970
|
-
:
|
|
982
|
+
: 50;
|
|
971
983
|
sessionMeta += `\n\n[Sub-agents] spawn_task is available (isolated context, sequential queue, default max_iterations=${subMaxIter}).`;
|
|
972
984
|
}
|
|
973
985
|
// Harness-driven suffix: append to first user message (NOT system prompt โ ยง9b KV cache rule)
|
|
@@ -1089,17 +1101,17 @@ export async function createSession(opts) {
|
|
|
1089
1101
|
? Math.max(1, Math.floor(Number(args.max_iterations)))
|
|
1090
1102
|
: (Number.isFinite(defaults.max_iterations)
|
|
1091
1103
|
? Math.max(1, Math.floor(Number(defaults.max_iterations)))
|
|
1092
|
-
:
|
|
1104
|
+
: 50);
|
|
1093
1105
|
const timeoutSec = Number.isFinite(args?.timeout_sec)
|
|
1094
1106
|
? Math.max(1, Math.floor(Number(args.timeout_sec)))
|
|
1095
1107
|
: (Number.isFinite(defaults.timeout_sec)
|
|
1096
1108
|
? Math.max(1, Math.floor(Number(defaults.timeout_sec)))
|
|
1097
|
-
: Math.max(
|
|
1109
|
+
: Math.max(60, cfg.timeout));
|
|
1098
1110
|
const subMaxTokens = Number.isFinite(args?.max_tokens)
|
|
1099
1111
|
? Math.max(128, Math.floor(Number(args.max_tokens)))
|
|
1100
1112
|
: (Number.isFinite(defaults.max_tokens)
|
|
1101
1113
|
? Math.max(128, Math.floor(Number(defaults.max_tokens)))
|
|
1102
|
-
:
|
|
1114
|
+
: maxTokens);
|
|
1103
1115
|
const resultTokenCap = Number.isFinite(defaults.result_token_cap)
|
|
1104
1116
|
? Math.max(256, Math.floor(Number(defaults.result_token_cap)))
|
|
1105
1117
|
: DEFAULT_SUB_AGENT_RESULT_TOKEN_CAP;
|
|
@@ -1678,6 +1690,7 @@ export async function createSession(opts) {
|
|
|
1678
1690
|
? Math.min(cfg.max_iterations, harness.quirks.maxIterationsOverride)
|
|
1679
1691
|
: cfg.max_iterations;
|
|
1680
1692
|
const wallStart = Date.now();
|
|
1693
|
+
const delegationForbiddenByUser = userDisallowsDelegation(instruction);
|
|
1681
1694
|
// Prepend session meta to the first user instruction (ยง9b: variable context
|
|
1682
1695
|
// goes in first user message, not system prompt, to preserve KV cache).
|
|
1683
1696
|
// This avoids two consecutive user messages without an assistant response.
|
|
@@ -1803,6 +1816,9 @@ export async function createSession(opts) {
|
|
|
1803
1816
|
return `${semantic}\n\n[lens] Structural skeleton:\n${structural}`;
|
|
1804
1817
|
};
|
|
1805
1818
|
const runSpawnTask = async (args) => {
|
|
1819
|
+
if (delegationForbiddenByUser) {
|
|
1820
|
+
throw new Error('spawn_task: blocked โ user explicitly asked for no delegation/sub-agents in this request. Continue directly in the current session.');
|
|
1821
|
+
}
|
|
1806
1822
|
return await runSpawnTaskCore(args, {
|
|
1807
1823
|
signal: hookObj.signal,
|
|
1808
1824
|
emitStatus: emitSubAgentStatus,
|