@visorcraft/idlehands 0.9.1

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.
Files changed (197) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +30 -0
  3. package/dist/agent.js +2604 -0
  4. package/dist/agent.js.map +1 -0
  5. package/dist/anton/controller.js +341 -0
  6. package/dist/anton/controller.js.map +1 -0
  7. package/dist/anton/lock.js +110 -0
  8. package/dist/anton/lock.js.map +1 -0
  9. package/dist/anton/parser.js +303 -0
  10. package/dist/anton/parser.js.map +1 -0
  11. package/dist/anton/prompt.js +203 -0
  12. package/dist/anton/prompt.js.map +1 -0
  13. package/dist/anton/reporter.js +119 -0
  14. package/dist/anton/reporter.js.map +1 -0
  15. package/dist/anton/session.js +51 -0
  16. package/dist/anton/session.js.map +1 -0
  17. package/dist/anton/types.js +7 -0
  18. package/dist/anton/types.js.map +1 -0
  19. package/dist/anton/verifier.js +263 -0
  20. package/dist/anton/verifier.js.map +1 -0
  21. package/dist/bench/compare.js +239 -0
  22. package/dist/bench/compare.js.map +1 -0
  23. package/dist/bench/debug_hooks.js +17 -0
  24. package/dist/bench/debug_hooks.js.map +1 -0
  25. package/dist/bench/json_extract.js +22 -0
  26. package/dist/bench/json_extract.js.map +1 -0
  27. package/dist/bench/openclaw.js +86 -0
  28. package/dist/bench/openclaw.js.map +1 -0
  29. package/dist/bench/report.js +116 -0
  30. package/dist/bench/report.js.map +1 -0
  31. package/dist/bench/runner.js +312 -0
  32. package/dist/bench/runner.js.map +1 -0
  33. package/dist/bench/types.js +2 -0
  34. package/dist/bench/types.js.map +1 -0
  35. package/dist/bot/commands.js +444 -0
  36. package/dist/bot/commands.js.map +1 -0
  37. package/dist/bot/confirm-discord.js +133 -0
  38. package/dist/bot/confirm-discord.js.map +1 -0
  39. package/dist/bot/confirm-telegram.js +290 -0
  40. package/dist/bot/confirm-telegram.js.map +1 -0
  41. package/dist/bot/discord.js +826 -0
  42. package/dist/bot/discord.js.map +1 -0
  43. package/dist/bot/format.js +210 -0
  44. package/dist/bot/format.js.map +1 -0
  45. package/dist/bot/session-manager.js +270 -0
  46. package/dist/bot/session-manager.js.map +1 -0
  47. package/dist/bot/telegram.js +678 -0
  48. package/dist/bot/telegram.js.map +1 -0
  49. package/dist/cli/agent-turn.js +45 -0
  50. package/dist/cli/agent-turn.js.map +1 -0
  51. package/dist/cli/args.js +236 -0
  52. package/dist/cli/args.js.map +1 -0
  53. package/dist/cli/bot.js +252 -0
  54. package/dist/cli/bot.js.map +1 -0
  55. package/dist/cli/build-repl-context.js +365 -0
  56. package/dist/cli/build-repl-context.js.map +1 -0
  57. package/dist/cli/command-registry.js +20 -0
  58. package/dist/cli/command-registry.js.map +1 -0
  59. package/dist/cli/commands/anton.js +271 -0
  60. package/dist/cli/commands/anton.js.map +1 -0
  61. package/dist/cli/commands/editing.js +328 -0
  62. package/dist/cli/commands/editing.js.map +1 -0
  63. package/dist/cli/commands/model.js +274 -0
  64. package/dist/cli/commands/model.js.map +1 -0
  65. package/dist/cli/commands/project.js +255 -0
  66. package/dist/cli/commands/project.js.map +1 -0
  67. package/dist/cli/commands/runtime.js +63 -0
  68. package/dist/cli/commands/runtime.js.map +1 -0
  69. package/dist/cli/commands/session.js +281 -0
  70. package/dist/cli/commands/session.js.map +1 -0
  71. package/dist/cli/commands/tools.js +126 -0
  72. package/dist/cli/commands/tools.js.map +1 -0
  73. package/dist/cli/commands/trifecta.js +221 -0
  74. package/dist/cli/commands/trifecta.js.map +1 -0
  75. package/dist/cli/commands/tui.js +17 -0
  76. package/dist/cli/commands/tui.js.map +1 -0
  77. package/dist/cli/init.js +222 -0
  78. package/dist/cli/init.js.map +1 -0
  79. package/dist/cli/input.js +360 -0
  80. package/dist/cli/input.js.map +1 -0
  81. package/dist/cli/oneshot.js +254 -0
  82. package/dist/cli/oneshot.js.map +1 -0
  83. package/dist/cli/repl-context.js +2 -0
  84. package/dist/cli/repl-context.js.map +1 -0
  85. package/dist/cli/runtime-cmds.js +811 -0
  86. package/dist/cli/runtime-cmds.js.map +1 -0
  87. package/dist/cli/service.js +145 -0
  88. package/dist/cli/service.js.map +1 -0
  89. package/dist/cli/session-state.js +130 -0
  90. package/dist/cli/session-state.js.map +1 -0
  91. package/dist/cli/setup.js +815 -0
  92. package/dist/cli/setup.js.map +1 -0
  93. package/dist/cli/shell.js +79 -0
  94. package/dist/cli/shell.js.map +1 -0
  95. package/dist/cli/status.js +392 -0
  96. package/dist/cli/status.js.map +1 -0
  97. package/dist/cli/watch.js +33 -0
  98. package/dist/cli/watch.js.map +1 -0
  99. package/dist/client.js +676 -0
  100. package/dist/client.js.map +1 -0
  101. package/dist/commands.js +194 -0
  102. package/dist/commands.js.map +1 -0
  103. package/dist/config.js +507 -0
  104. package/dist/config.js.map +1 -0
  105. package/dist/confirm/auto.js +13 -0
  106. package/dist/confirm/auto.js.map +1 -0
  107. package/dist/confirm/headless.js +41 -0
  108. package/dist/confirm/headless.js.map +1 -0
  109. package/dist/confirm/terminal.js +90 -0
  110. package/dist/confirm/terminal.js.map +1 -0
  111. package/dist/context.js +49 -0
  112. package/dist/context.js.map +1 -0
  113. package/dist/git.js +136 -0
  114. package/dist/git.js.map +1 -0
  115. package/dist/harnesses.js +171 -0
  116. package/dist/harnesses.js.map +1 -0
  117. package/dist/history.js +139 -0
  118. package/dist/history.js.map +1 -0
  119. package/dist/index.js +700 -0
  120. package/dist/index.js.map +1 -0
  121. package/dist/indexer.js +374 -0
  122. package/dist/indexer.js.map +1 -0
  123. package/dist/jsonrpc.js +76 -0
  124. package/dist/jsonrpc.js.map +1 -0
  125. package/dist/lens.js +525 -0
  126. package/dist/lens.js.map +1 -0
  127. package/dist/lsp.js +605 -0
  128. package/dist/lsp.js.map +1 -0
  129. package/dist/markdown.js +275 -0
  130. package/dist/markdown.js.map +1 -0
  131. package/dist/mcp.js +554 -0
  132. package/dist/mcp.js.map +1 -0
  133. package/dist/recovery.js +178 -0
  134. package/dist/recovery.js.map +1 -0
  135. package/dist/replay.js +132 -0
  136. package/dist/replay.js.map +1 -0
  137. package/dist/replay_cli.js +24 -0
  138. package/dist/replay_cli.js.map +1 -0
  139. package/dist/runtime/executor.js +418 -0
  140. package/dist/runtime/executor.js.map +1 -0
  141. package/dist/runtime/planner.js +197 -0
  142. package/dist/runtime/planner.js.map +1 -0
  143. package/dist/runtime/store.js +289 -0
  144. package/dist/runtime/store.js.map +1 -0
  145. package/dist/runtime/types.js +2 -0
  146. package/dist/runtime/types.js.map +1 -0
  147. package/dist/safety.js +446 -0
  148. package/dist/safety.js.map +1 -0
  149. package/dist/spinner.js +224 -0
  150. package/dist/spinner.js.map +1 -0
  151. package/dist/sys/context.js +124 -0
  152. package/dist/sys/context.js.map +1 -0
  153. package/dist/sys/snapshot.sh +97 -0
  154. package/dist/term.js +61 -0
  155. package/dist/term.js.map +1 -0
  156. package/dist/themes.js +135 -0
  157. package/dist/themes.js.map +1 -0
  158. package/dist/tools.js +1114 -0
  159. package/dist/tools.js.map +1 -0
  160. package/dist/tui/branch-picker.js +65 -0
  161. package/dist/tui/branch-picker.js.map +1 -0
  162. package/dist/tui/command-handler.js +108 -0
  163. package/dist/tui/command-handler.js.map +1 -0
  164. package/dist/tui/confirm.js +90 -0
  165. package/dist/tui/confirm.js.map +1 -0
  166. package/dist/tui/controller.js +463 -0
  167. package/dist/tui/controller.js.map +1 -0
  168. package/dist/tui/event-bridge.js +44 -0
  169. package/dist/tui/event-bridge.js.map +1 -0
  170. package/dist/tui/events.js +2 -0
  171. package/dist/tui/events.js.map +1 -0
  172. package/dist/tui/keymap.js +144 -0
  173. package/dist/tui/keymap.js.map +1 -0
  174. package/dist/tui/layout.js +11 -0
  175. package/dist/tui/layout.js.map +1 -0
  176. package/dist/tui/render.js +186 -0
  177. package/dist/tui/render.js.map +1 -0
  178. package/dist/tui/screen.js +48 -0
  179. package/dist/tui/screen.js.map +1 -0
  180. package/dist/tui/state.js +167 -0
  181. package/dist/tui/state.js.map +1 -0
  182. package/dist/tui/theme.js +70 -0
  183. package/dist/tui/theme.js.map +1 -0
  184. package/dist/tui/types.js +2 -0
  185. package/dist/tui/types.js.map +1 -0
  186. package/dist/types.js +2 -0
  187. package/dist/types.js.map +1 -0
  188. package/dist/upgrade.js +412 -0
  189. package/dist/upgrade.js.map +1 -0
  190. package/dist/utils.js +87 -0
  191. package/dist/utils.js.map +1 -0
  192. package/dist/vault.js +520 -0
  193. package/dist/vault.js.map +1 -0
  194. package/dist/vim.js +160 -0
  195. package/dist/vim.js.map +1 -0
  196. package/package.json +67 -0
  197. package/src/sys/snapshot.sh +97 -0
@@ -0,0 +1,224 @@
1
+ /**
2
+ * CLI spinner and tool call visualization (Phase 7).
3
+ *
4
+ * Shows an animated spinner while waiting for the first token,
5
+ * then transitions to streaming text. Tool calls get one-line
6
+ * summaries before and after execution.
7
+ */
8
+ const FRAMES = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
9
+ const INTERVAL = 80; // ms per frame
10
+ export class CliSpinner {
11
+ frame = 0;
12
+ timer = null;
13
+ startTime = Date.now();
14
+ currentTool = null;
15
+ firstDelta = false;
16
+ enabled;
17
+ verbose;
18
+ S;
19
+ constructor(opts) {
20
+ this.S = opts.styler;
21
+ this.enabled = opts.enabled !== false && process.stdout.isTTY === true;
22
+ this.verbose = opts.verbose ?? false;
23
+ }
24
+ /** Start the spinner. Call before ask(). */
25
+ start() {
26
+ if (!this.enabled)
27
+ return;
28
+ this.startTime = Date.now();
29
+ this.frame = 0;
30
+ this.firstDelta = false;
31
+ this.currentTool = null;
32
+ this.timer = setInterval(() => this.render(), INTERVAL);
33
+ }
34
+ /** Called on first token — stop spinner, let streaming begin. */
35
+ onFirstDelta() {
36
+ if (this.firstDelta)
37
+ return;
38
+ this.firstDelta = true;
39
+ this.clearLine();
40
+ this.stopTimer();
41
+ }
42
+ /** Called before a tool executes. */
43
+ onToolCall(event) {
44
+ if (!this.enabled) {
45
+ // Fallback for non-TTY
46
+ process.stderr.write(`◆ ${event.name} ${this.argSummary(event)}\n`);
47
+ return;
48
+ }
49
+ this.clearLine();
50
+ this.stopTimer();
51
+ const summary = this.argSummary(event);
52
+ const dim = this.S.dim;
53
+ process.stderr.write(dim(` ◆ ${event.name} ${summary}`));
54
+ process.stderr.write('\n');
55
+ // Restart spinner for tool execution
56
+ this.currentTool = event.name;
57
+ this.startTime = Date.now();
58
+ this.timer = setInterval(() => this.render(), INTERVAL);
59
+ }
60
+ /** Called after a tool completes. */
61
+ onToolResult(event) {
62
+ this.clearLine();
63
+ this.stopTimer();
64
+ this.currentTool = null;
65
+ if (!this.enabled) {
66
+ const icon = event.success ? '✓' : '✗';
67
+ process.stderr.write(` ${icon} ${event.name}: ${event.summary}\n`);
68
+ return;
69
+ }
70
+ const icon = event.success
71
+ ? this.S.green('✓')
72
+ : this.S.red('✗');
73
+ const summary = event.summary.length > 120
74
+ ? event.summary.slice(0, 117) + '...'
75
+ : event.summary;
76
+ process.stderr.write(` ${icon} ${this.S.dim(`${event.name}: ${summary}`)}\n`);
77
+ // Phase 7: rich display (only in verbose mode or for key outputs)
78
+ if (this.verbose) {
79
+ this.renderRichOutput(event);
80
+ }
81
+ else {
82
+ // In normal mode, show compact exec output and diffs
83
+ if (event.diff) {
84
+ this.renderDiff(event.diff);
85
+ }
86
+ if (event.execOutput) {
87
+ this.renderExecOutput(event.execOutput, 5);
88
+ }
89
+ if (event.searchMatches?.length) {
90
+ this.renderSearchMatches(event.searchMatches, 5);
91
+ }
92
+ }
93
+ }
94
+ /** Render rich output in verbose mode. */
95
+ renderRichOutput(event) {
96
+ if (event.diff) {
97
+ this.renderDiff(event.diff);
98
+ }
99
+ if (event.execOutput) {
100
+ this.renderExecOutput(event.execOutput, 20);
101
+ }
102
+ if (event.searchMatches?.length) {
103
+ this.renderSearchMatches(event.searchMatches, 20);
104
+ }
105
+ }
106
+ /** Render an inline colored diff (unified format). */
107
+ renderDiff(diff) {
108
+ const lines = diff.split('\n');
109
+ for (const line of lines) {
110
+ if (line.startsWith('+++') || line.startsWith('---') || line.startsWith('@@')) {
111
+ process.stderr.write(` ${this.S.dim(line)}\n`);
112
+ }
113
+ else if (line.startsWith('+') && !line.startsWith('+++')) {
114
+ process.stderr.write(` ${this.S.green(line)}\n`);
115
+ }
116
+ else if (line.startsWith('-') && !line.startsWith('---')) {
117
+ process.stderr.write(` ${this.S.red(line)}\n`);
118
+ }
119
+ else if (line.startsWith('[+')) {
120
+ process.stderr.write(` ${this.S.dim(line)}\n`);
121
+ }
122
+ else {
123
+ process.stderr.write(` ${this.S.dim(line)}\n`);
124
+ }
125
+ }
126
+ }
127
+ /** Render exec stdout (dimmed, limited lines). */
128
+ renderExecOutput(output, maxLines) {
129
+ const lines = output.split('\n');
130
+ const show = lines.slice(0, maxLines);
131
+ for (const line of show) {
132
+ process.stderr.write(` ${this.S.dim(line)}\n`);
133
+ }
134
+ if (lines.length > maxLines) {
135
+ process.stderr.write(` ${this.S.dim(`[+${lines.length - maxLines} more lines]`)}\n`);
136
+ }
137
+ }
138
+ /** Render search matches with highlights. */
139
+ renderSearchMatches(matches, maxLines) {
140
+ const show = matches.slice(0, maxLines);
141
+ for (const match of show) {
142
+ // Format: "file:line:content" — highlight the file:line prefix
143
+ const idx = match.indexOf(':');
144
+ if (idx > 0) {
145
+ const nextIdx = match.indexOf(':', idx + 1);
146
+ if (nextIdx > 0) {
147
+ const prefix = match.slice(0, nextIdx + 1);
148
+ const content = match.slice(nextIdx + 1);
149
+ process.stderr.write(` ${this.S.cyan(prefix)}${this.S.dim(content)}\n`);
150
+ continue;
151
+ }
152
+ }
153
+ process.stderr.write(` ${this.S.dim(match)}\n`);
154
+ }
155
+ if (matches.length > maxLines) {
156
+ process.stderr.write(` ${this.S.dim(`[+${matches.length - maxLines} more matches]`)}\n`);
157
+ }
158
+ }
159
+ /** Stop spinner completely. Call after ask() returns. */
160
+ stop() {
161
+ this.clearLine();
162
+ this.stopTimer();
163
+ }
164
+ render() {
165
+ if (this.firstDelta && !this.currentTool)
166
+ return;
167
+ const elapsed = ((Date.now() - this.startTime) / 1000).toFixed(1);
168
+ const f = FRAMES[this.frame % FRAMES.length];
169
+ this.frame++;
170
+ let text;
171
+ if (this.currentTool) {
172
+ text = `${f} Running ${this.currentTool}... (${elapsed}s)`;
173
+ }
174
+ else {
175
+ text = `${f} Thinking... (${elapsed}s)`;
176
+ }
177
+ process.stderr.write(`\r${this.S.dim(text)}`);
178
+ }
179
+ clearLine() {
180
+ if (!this.enabled)
181
+ return;
182
+ process.stderr.write('\r\x1b[K');
183
+ }
184
+ stopTimer() {
185
+ if (this.timer) {
186
+ clearInterval(this.timer);
187
+ this.timer = null;
188
+ }
189
+ }
190
+ argSummary(event) {
191
+ const a = event.args;
192
+ switch (event.name) {
193
+ case 'read_file': {
194
+ const p = a.path ?? a.file_path ?? '';
195
+ const range = a.offset && a.limit ? ` (lines ${a.offset}-${Number(a.offset) + Number(a.limit)})` : '';
196
+ const search = a.search ? ` search="${a.search}"` : '';
197
+ return `${p}${range}${search}`;
198
+ }
199
+ case 'write_file':
200
+ return `${a.path ?? a.file_path ?? ''}`;
201
+ case 'edit_file': {
202
+ const p = a.path ?? a.file_path ?? '';
203
+ const old = typeof a.old_string === 'string' ? a.old_string : '';
204
+ const lines = old.split('\n').length;
205
+ return `${p} (replacing ${lines} line${lines !== 1 ? 's' : ''})`;
206
+ }
207
+ case 'insert_file':
208
+ return `${a.path ?? a.file_path ?? ''} at line ${a.line ?? '?'}`;
209
+ case 'exec': {
210
+ const cmd = typeof a.command === 'string' ? a.command : '';
211
+ return cmd.length > 80 ? cmd.slice(0, 77) + '...' : cmd;
212
+ }
213
+ case 'list_dir':
214
+ return `${a.path ?? '.'}`;
215
+ case 'search_files':
216
+ return `"${a.pattern ?? ''}" in ${a.path ?? '.'}`;
217
+ case 'undo_path':
218
+ return `${a.path ?? '(last edit)'}`;
219
+ default:
220
+ return Object.keys(a).slice(0, 3).map(k => `${k}=${JSON.stringify(a[k]).slice(0, 30)}`).join(' ');
221
+ }
222
+ }
223
+ }
224
+ //# sourceMappingURL=spinner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spinner.js","sourceRoot":"","sources":["../src/spinner.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,124 @@
1
+ /**
2
+ * sys_context tool — Scoped system snapshot (Phase 9a).
3
+ *
4
+ * Collects system info on demand via snapshot.sh.
5
+ * Cached for 60s to avoid re-running `df`, `systemctl`, etc. every tool cycle.
6
+ */
7
+ import { spawn, spawnSync } from 'node:child_process';
8
+ import path from 'node:path';
9
+ import { existsSync } from 'node:fs';
10
+ import { fileURLToPath } from 'node:url';
11
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
12
+ // In dist/, __dirname is dist/sys/ — script is at src/sys/snapshot.sh relative to repo root.
13
+ // In source, __dirname is src/sys/ — script is co-located.
14
+ const SNAPSHOT_SCRIPT = path.join(__dirname, 'snapshot.sh');
15
+ const VALID_SCOPES = ['all', 'services', 'network', 'disk', 'packages'];
16
+ // Cache: scope → { text, timestamp }
17
+ const cache = new Map();
18
+ const CACHE_TTL_MS = 60_000; // 60 seconds
19
+ /**
20
+ * Detect the package manager on this system.
21
+ * Returns: 'apt' | 'dnf' | 'pacman' | 'unknown'
22
+ */
23
+ export function detectPackageManager() {
24
+ const has = (cmd) => spawnSync('bash', ['-lc', `command -v ${cmd}`], { stdio: 'ignore' }).status === 0;
25
+ if (has('apt'))
26
+ return 'apt';
27
+ if (has('dnf'))
28
+ return 'dnf';
29
+ if (has('pacman'))
30
+ return 'pacman';
31
+ return 'unknown';
32
+ }
33
+ /**
34
+ * Run snapshot.sh with the given scope and return the output.
35
+ * Caches results for 60s per scope.
36
+ */
37
+ export async function collectSnapshot(scope = 'all') {
38
+ // Check cache
39
+ const cached = cache.get(scope);
40
+ if (cached && (Date.now() - cached.ts) < CACHE_TTL_MS) {
41
+ return cached.text;
42
+ }
43
+ const text = await runSnapshotScript(scope);
44
+ cache.set(scope, { text, ts: Date.now() });
45
+ return text;
46
+ }
47
+ /** Clear snapshot cache (for testing). */
48
+ export function clearSnapshotCache() {
49
+ cache.clear();
50
+ }
51
+ /**
52
+ * Resolve the snapshot script path. In the built dist, the script lives in
53
+ * src/sys/snapshot.sh relative to the repo root. We try the source path first,
54
+ * then fall back to a co-located path.
55
+ */
56
+ function resolveSnapshotScript() {
57
+ // Try likely locations in source checkout and installed package.
58
+ const candidates = [
59
+ SNAPSHOT_SCRIPT, // co-located (src/sys/ or dist/sys/)
60
+ path.join(process.cwd(), 'src', 'sys', 'snapshot.sh'), // repo root → source
61
+ path.join(process.cwd(), 'dist', 'sys', 'snapshot.sh'), // repo root → dist
62
+ ];
63
+ for (const c of candidates) {
64
+ if (existsSync(c))
65
+ return c;
66
+ }
67
+ // Fall back to the canonical source path for a clear error.
68
+ return SNAPSHOT_SCRIPT;
69
+ }
70
+ async function runSnapshotScript(scope) {
71
+ const scriptPath = resolveSnapshotScript();
72
+ return new Promise((resolve, reject) => {
73
+ const child = spawn('bash', [scriptPath, scope], {
74
+ stdio: ['ignore', 'pipe', 'pipe'],
75
+ timeout: 5000, // 5s hard limit
76
+ });
77
+ const chunks = [];
78
+ const errChunks = [];
79
+ child.stdout.on('data', (d) => chunks.push(d));
80
+ child.stderr.on('data', (d) => errChunks.push(d));
81
+ child.on('close', (code) => {
82
+ const out = Buffer.concat(chunks).toString('utf8').trim();
83
+ if (code !== 0) {
84
+ const err = Buffer.concat(errChunks).toString('utf8').trim();
85
+ reject(new Error(`snapshot.sh exited ${code}: ${err || out}`));
86
+ return;
87
+ }
88
+ resolve(out);
89
+ });
90
+ child.on('error', (e) => reject(e));
91
+ });
92
+ }
93
+ /**
94
+ * Tool handler for sys_context.
95
+ * Matches the ToolContext pattern used by other tools.
96
+ */
97
+ export async function sys_context(_ctx, args) {
98
+ const scope = typeof args?.scope === 'string' ? args.scope.toLowerCase() : 'all';
99
+ if (!VALID_SCOPES.includes(scope)) {
100
+ throw new Error(`sys_context: invalid scope "${scope}". Valid: ${VALID_SCOPES.join(', ')}`);
101
+ }
102
+ return collectSnapshot(scope);
103
+ }
104
+ /**
105
+ * Tool schema for sys_context (registered only when sys mode is active).
106
+ */
107
+ export const SYS_CONTEXT_SCHEMA = {
108
+ type: 'function',
109
+ function: {
110
+ name: 'sys_context',
111
+ description: 'Get system information snapshot. Use this when you need to know about the OS, services, network, disk, or packages.',
112
+ parameters: {
113
+ type: 'object',
114
+ properties: {
115
+ scope: {
116
+ type: 'string',
117
+ enum: ['all', 'services', 'network', 'disk', 'packages'],
118
+ description: 'What info to collect. "all" returns everything (~500 tokens). Individual scopes return ~100 tokens each.',
119
+ },
120
+ },
121
+ },
122
+ },
123
+ };
124
+ //# sourceMappingURL=context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.js","sourceRoot":"","sources":["../../src/sys/context.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,97 @@
1
+ #!/bin/bash
2
+ # System context snapshot for Idle Hands (Phase 9)
3
+ # Usage: snapshot.sh [scope]
4
+ # Scope: all | services | network | disk | packages
5
+ # Target: <500 tokens for full scope, <100 tokens per individual scope, <2s to collect
6
+
7
+ set -euo pipefail
8
+ SCOPE="${1:-all}"
9
+
10
+ collect_os() {
11
+ echo "OS: $(cat /etc/os-release 2>/dev/null | grep '^PRETTY_NAME' | cut -d'"' -f2 || uname -s) ($(uname -m))"
12
+ echo "Kernel: $(uname -r)"
13
+ echo "Hostname: $(hostname)"
14
+ echo "Uptime: $(uptime -p 2>/dev/null | sed 's/^up //' || echo 'unknown')"
15
+ # CPU
16
+ local cpus
17
+ cpus=$(nproc 2>/dev/null || echo '?')
18
+ local model
19
+ model=$(grep -m1 'model name' /proc/cpuinfo 2>/dev/null | cut -d: -f2 | xargs || echo 'unknown')
20
+ echo "CPU: ${model} (${cpus} cores)"
21
+ # RAM
22
+ if command -v free &>/dev/null; then
23
+ local total avail
24
+ total=$(free -h | awk '/^Mem:/{print $2}')
25
+ avail=$(free -h | awk '/^Mem:/{print $7}')
26
+ echo "RAM: ${total} (${avail} available)"
27
+ fi
28
+ }
29
+
30
+ collect_disk() {
31
+ if command -v df &>/dev/null; then
32
+ echo "Disk:"
33
+ df -h --output=target,size,used,avail,pcent / /home 2>/dev/null | tail -n +2 | while read -r mount size used avail pct; do
34
+ echo " ${mount} ${pct} of ${size} (${avail} free)"
35
+ done
36
+ fi
37
+ }
38
+
39
+ collect_network() {
40
+ if command -v ip &>/dev/null; then
41
+ echo "Network:"
42
+ ip -br addr 2>/dev/null | grep -v '^lo ' | while read -r iface state addrs; do
43
+ echo " ${iface} ${state} ${addrs}"
44
+ done
45
+ fi
46
+ }
47
+
48
+ collect_services() {
49
+ if command -v systemctl &>/dev/null; then
50
+ echo "Services (active): $(systemctl list-units --state=active --type=service --no-legend --no-pager 2>/dev/null | awk '{print $1}' | sed 's/\.service$//' | head -20 | tr '\n' ', ' | sed 's/,$//')"
51
+ local failed
52
+ failed=$(systemctl list-units --state=failed --type=service --no-legend --no-pager 2>/dev/null | awk '{print $1}' | sed 's/\.service$//' | tr '\n' ', ' | sed 's/,$//')
53
+ echo "Services (failed): ${failed:-none}"
54
+ fi
55
+ }
56
+
57
+ collect_packages() {
58
+ if command -v apt &>/dev/null; then
59
+ local installed upgradable
60
+ installed=$(dpkg -l 2>/dev/null | grep -c '^ii' || echo '?')
61
+ upgradable=$(apt list --upgradable 2>/dev/null | grep -c 'upgradable' || echo '0')
62
+ echo "Packages (apt): ${installed} installed, ${upgradable} upgradable"
63
+ elif command -v dnf &>/dev/null; then
64
+ local installed
65
+ installed=$(dnf list installed 2>/dev/null | tail -n +2 | wc -l || echo '?')
66
+ echo "Packages (dnf): ${installed} installed"
67
+ elif command -v pacman &>/dev/null; then
68
+ local installed
69
+ installed=$(pacman -Q 2>/dev/null | wc -l || echo '?')
70
+ echo "Packages (pacman): ${installed} installed"
71
+ fi
72
+ # Last boot
73
+ if command -v who &>/dev/null; then
74
+ echo "Last boot: $(who -b 2>/dev/null | awk '{print $3, $4}' || echo 'unknown')"
75
+ fi
76
+ }
77
+
78
+ echo "[System context]"
79
+ case "$SCOPE" in
80
+ all)
81
+ collect_os
82
+ collect_disk
83
+ collect_network
84
+ collect_services
85
+ collect_packages
86
+ ;;
87
+ services) collect_services ;;
88
+ network) collect_network ;;
89
+ disk) collect_disk ;;
90
+ packages) collect_packages ;;
91
+ *)
92
+ echo "Unknown scope: $SCOPE"
93
+ echo "Valid scopes: all, services, network, disk, packages"
94
+ exit 1
95
+ ;;
96
+ esac
97
+ echo "[End system context]"
package/dist/term.js ADDED
@@ -0,0 +1,61 @@
1
+ import pc from 'picocolors';
2
+ export function resolveColorMode(mode) {
3
+ const env = process.env;
4
+ // Standard opt-out
5
+ if ('NO_COLOR' in env)
6
+ return { enabled: false };
7
+ // Explicit force/disable
8
+ if (env.FORCE_COLOR === '0')
9
+ return { enabled: false };
10
+ if (env.FORCE_COLOR && env.FORCE_COLOR !== '0')
11
+ return { enabled: true };
12
+ if (mode === 'always')
13
+ return { enabled: true };
14
+ if (mode === 'never')
15
+ return { enabled: false };
16
+ // auto
17
+ return { enabled: !!process.stdout.isTTY };
18
+ }
19
+ export function makeStyler(enabled, theme) {
20
+ const wrap = (fn) => (s) => (enabled ? fn(s) : s);
21
+ const t = theme;
22
+ return {
23
+ enabled,
24
+ dim: wrap(t?.dim ?? pc.dim),
25
+ bold: wrap(t?.bold ?? pc.bold),
26
+ red: wrap(t?.red ?? pc.red),
27
+ yellow: wrap(t?.yellow ?? pc.yellow),
28
+ green: wrap(t?.green ?? pc.green),
29
+ cyan: wrap(t?.cyan ?? pc.cyan),
30
+ magenta: wrap(t?.magenta ?? pc.magenta),
31
+ blue: wrap(t?.blue ?? pc.blue)
32
+ };
33
+ }
34
+ export function colorizeUnifiedDiff(diff, s) {
35
+ const out = [];
36
+ for (const line of diff.split(/\r?\n/)) {
37
+ if (line.startsWith('+++') || line.startsWith('---') || line.startsWith('@@')) {
38
+ out.push(s.dim(line));
39
+ }
40
+ else if (line.startsWith('+') && !line.startsWith('+++')) {
41
+ out.push(s.green(line));
42
+ }
43
+ else if (line.startsWith('-') && !line.startsWith('---')) {
44
+ out.push(s.red(line));
45
+ }
46
+ else {
47
+ out.push(line);
48
+ }
49
+ }
50
+ return out.join('\n');
51
+ }
52
+ export function banner(title, s) {
53
+ return s.blue(s.bold(title));
54
+ }
55
+ export function warn(msg, s) {
56
+ return s.yellow('WARN') + s.dim(': ') + msg;
57
+ }
58
+ export function err(msg, s) {
59
+ return s.red('ERROR') + s.dim(': ') + msg;
60
+ }
61
+ //# sourceMappingURL=term.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"term.js","sourceRoot":"","sources":["../src/term.ts"],"names":[],"mappings":"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"}
package/dist/themes.js ADDED
@@ -0,0 +1,135 @@
1
+ /**
2
+ * Built-in and custom theme system (Phase 14a).
3
+ *
4
+ * Themes are ANSI color palette mappings applied to the Styler.
5
+ * Each theme remaps the Styler's color slots (red, yellow, green,
6
+ * cyan, magenta, blue, dim, bold) to different picocolors functions.
7
+ *
8
+ * Built-in: default, dark, light, minimal, hacker
9
+ * Custom: ~/.config/idlehands/themes/<name>.json
10
+ */
11
+ import pc from 'picocolors';
12
+ import fs from 'node:fs/promises';
13
+ import path from 'node:path';
14
+ import { configDir } from './utils.js';
15
+ // All supported picocolors primitives.
16
+ const PC = {
17
+ dim: pc.dim,
18
+ bold: pc.bold,
19
+ italic: pc.italic,
20
+ underline: pc.underline,
21
+ inverse: pc.inverse,
22
+ red: pc.red,
23
+ yellow: pc.yellow,
24
+ green: pc.green,
25
+ cyan: pc.cyan,
26
+ magenta: pc.magenta,
27
+ blue: pc.blue,
28
+ white: pc.white,
29
+ gray: pc.gray,
30
+ black: pc.black,
31
+ };
32
+ /** Resolve a palette string like "bold+cyan" to a composed function. */
33
+ function resolveColor(spec) {
34
+ const parts = spec.split('+').map(p => p.trim().toLowerCase());
35
+ const fns = parts.map(p => PC[p]).filter(Boolean);
36
+ if (fns.length === 0)
37
+ return undefined;
38
+ if (fns.length === 1)
39
+ return fns[0];
40
+ return (s) => fns.reduce((acc, fn) => fn(acc), s);
41
+ }
42
+ /** The default (identity) palette — no remapping. */
43
+ const DEFAULT_PALETTE = {
44
+ dim: 'dim', bold: 'bold', red: 'red', yellow: 'yellow',
45
+ green: 'green', cyan: 'cyan', magenta: 'magenta', blue: 'blue',
46
+ };
47
+ const BUILTIN_PALETTES = {
48
+ default: {},
49
+ dark: {
50
+ cyan: 'bold+cyan',
51
+ magenta: 'bold+magenta',
52
+ blue: 'bold+blue',
53
+ },
54
+ light: {
55
+ dim: 'gray',
56
+ cyan: 'blue',
57
+ blue: 'cyan',
58
+ },
59
+ minimal: {
60
+ yellow: 'dim',
61
+ green: 'dim',
62
+ cyan: 'dim',
63
+ magenta: 'dim',
64
+ blue: 'dim',
65
+ },
66
+ hacker: {
67
+ bold: 'bold+green',
68
+ yellow: 'green',
69
+ cyan: 'green',
70
+ magenta: 'green',
71
+ blue: 'green',
72
+ },
73
+ };
74
+ export const BUILTIN_THEME_NAMES = Object.keys(BUILTIN_PALETTES);
75
+ /** Resolve a raw palette to concrete functions, falling back to defaults. */
76
+ export function resolvePalette(palette) {
77
+ const out = {};
78
+ for (const slot of Object.keys(DEFAULT_PALETTE)) {
79
+ const spec = palette[slot] ?? DEFAULT_PALETTE[slot];
80
+ const fn = resolveColor(spec);
81
+ out[slot] = fn ?? PC[DEFAULT_PALETTE[slot]];
82
+ }
83
+ return out;
84
+ }
85
+ /** Get a built-in theme's resolved functions. */
86
+ export function builtinTheme(name) {
87
+ const p = BUILTIN_PALETTES[name];
88
+ if (!p)
89
+ return undefined;
90
+ return resolvePalette(p);
91
+ }
92
+ /** Custom themes directory. */
93
+ export function customThemesDir() {
94
+ return path.join(configDir(), 'themes');
95
+ }
96
+ /** Load a custom theme from ~/.config/idlehands/themes/<name>.json */
97
+ export async function loadCustomTheme(name) {
98
+ const filePath = path.join(customThemesDir(), `${name}.json`);
99
+ try {
100
+ const raw = await fs.readFile(filePath, 'utf8');
101
+ const parsed = JSON.parse(raw);
102
+ if (!parsed || typeof parsed !== 'object')
103
+ return undefined;
104
+ return resolvePalette(parsed);
105
+ }
106
+ catch {
107
+ return undefined;
108
+ }
109
+ }
110
+ /** Resolve a theme by name: built-in first, then custom file. */
111
+ export async function resolveTheme(name) {
112
+ const builtin = builtinTheme(name);
113
+ if (builtin)
114
+ return builtin;
115
+ return loadCustomTheme(name);
116
+ }
117
+ /** List available theme names (built-in + custom). */
118
+ export async function listThemes() {
119
+ const builtin = [...BUILTIN_THEME_NAMES];
120
+ const custom = [];
121
+ try {
122
+ const dir = customThemesDir();
123
+ const entries = await fs.readdir(dir, { withFileTypes: true });
124
+ for (const e of entries) {
125
+ if (e.isFile() && e.name.endsWith('.json')) {
126
+ custom.push(e.name.replace(/\.json$/, ''));
127
+ }
128
+ }
129
+ }
130
+ catch {
131
+ // no custom themes dir
132
+ }
133
+ return { builtin, custom };
134
+ }
135
+ //# sourceMappingURL=themes.js.map