@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 CHANGED
@@ -1,30 +1,169 @@
1
- # Idle Hands v1.0.0
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
- ๐Ÿ“– Full documentation at [https://visorcraft.github.io/IdleHands/](https://visorcraft.github.io/IdleHands/)
6
+ ๐Ÿ“š Full docs: https://visorcraft.github.io/IdleHands/
6
7
 
7
- ## Key features
8
+ ---
8
9
 
9
- - Fullscreen TUI by default for TTY sessions (`--no-tui` for classic mode)
10
- - Runtime orchestration across hosts, backends, and models
11
- - Trifecta subsystem: Vault memory, Replay checkpoints, Lens indexing
12
- - Approval modes (`plan`, `reject`, `default`, `auto-edit`, `yolo`) + safety tiers
13
- - `--non-interactive` mode for CI/pipelines (rejects unconfirmed operations)
14
- - Telegram and Discord bot frontends with systemd user service support
15
- - Headless/CI output modes (`json`, `stream-json`)
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 https://github.com/visorcraft/IdleHands/releases/download/v1.0.0/idlehands-1.0.0.tgz
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 = 2000;
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
- : 10;
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
- : 10);
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(30, Math.min(600, cfg.timeout)));
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
- : Math.max(256, Math.min(8192, maxTokens)));
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,