very-happy-cli 0.2.151 → 0.2.153

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 (65) hide show
  1. package/dist/{AcpBackend-blE9IEod.cjs → AcpBackend-DSt8nTYm.cjs} +2 -2
  2. package/dist/{AcpBackend-BjTA5gUB.mjs → AcpBackend-YDGv4E-g.mjs} +2 -2
  3. package/dist/{AcpSessionManager-D-9mrp40.mjs → AcpSessionManager-Cxf5HQX1.mjs} +1 -1
  4. package/dist/{AcpSessionManager-bF8PYu_B.cjs → AcpSessionManager-uov6I9C8.cjs} +9 -9
  5. package/dist/agentGuidance-BM4kMNyr.cjs +553 -0
  6. package/dist/agentGuidance-C8unGWp7.mjs +529 -0
  7. package/dist/{agentHome-D6MSfwnp.cjs → agentHome-BJ7bde35.cjs} +4 -4
  8. package/dist/{agentHome-CeFDNzvq.mjs → agentHome-D68Xh2ae.mjs} +4 -4
  9. package/dist/auto-C-90ojIH.cjs +472 -0
  10. package/dist/auto-CcXxfqkC.mjs +466 -0
  11. package/dist/codex/happyMcpStdioBridge.cjs +4 -2
  12. package/dist/codex/happyMcpStdioBridge.mjs +4 -2
  13. package/dist/{config-C-A1XF62.mjs → config-CHJ189UJ.mjs} +6 -6
  14. package/dist/{config-Cadw1sAk.cjs → config-D23kF0_u.cjs} +6 -6
  15. package/dist/{index-DYQgcgL2.mjs → index-BXA9fxbK.mjs} +6 -6
  16. package/dist/{index-CIyjJy8D.mjs → index-BacwoXzs.mjs} +748 -55
  17. package/dist/{index-DEp6CmmS.mjs → index-BgYZGODP.mjs} +20 -20
  18. package/dist/{index-vyRqLjDv.cjs → index-D49WvbDT.cjs} +6 -6
  19. package/dist/{index-BKGspSWk.cjs → index-DEOVn46R.cjs} +21 -21
  20. package/dist/{index-CqtfRzug.cjs → index-DHrwHxHf.cjs} +805 -105
  21. package/dist/index.cjs +5 -5
  22. package/dist/index.mjs +5 -5
  23. package/dist/install-BiOXBnnS.cjs +118 -0
  24. package/dist/install-Ca7_Z3bV.mjs +114 -0
  25. package/dist/{installPiTools-C-Lxv4Bt.mjs → installPiTools-Cj7yU9Fc.mjs} +2 -1
  26. package/dist/{installPiTools-BKnXjrVu.cjs → installPiTools-DuZ6jZvX.cjs} +3 -2
  27. package/dist/{installTerminalHooks-Bf_adbl3.cjs → installTerminalHooks-BF0kL0ae.cjs} +8 -8
  28. package/dist/{installTerminalHooks-Ch_yTtEA.mjs → installTerminalHooks-Bpl32K75.mjs} +7 -7
  29. package/dist/lib.cjs +4 -4
  30. package/dist/lib.mjs +4 -4
  31. package/dist/limits-BtAuKRl2.mjs +2080 -0
  32. package/dist/limits-DY_sFA3J.cjs +2175 -0
  33. package/dist/{mcp-zu8n8TkR.mjs → mcp-DQkmk4VV.mjs} +15 -8
  34. package/dist/{mcp-CMxEZ6-0.cjs → mcp-JpqI2POW.cjs} +15 -7
  35. package/dist/piExtension.d.mts +4 -1
  36. package/dist/piExtension.mjs +24 -1
  37. package/dist/{piRuntime-Cu6cXZ6N.mjs → piRuntime-DBpe12NN.mjs} +2 -2
  38. package/dist/{piRuntime-D3ny0mVg.cjs → piRuntime-DRbbeQ72.cjs} +2 -2
  39. package/dist/{piTerminal-M677UP7y.mjs → piTerminal-BoHvPEZ_.mjs} +8 -7
  40. package/dist/{piTerminal-kDSd_r9X.cjs → piTerminal-qdJ9KZVL.cjs} +8 -7
  41. package/dist/{runGemini-DuwdVV1O.cjs → runGemini-CtOEFvhf.cjs} +9 -9
  42. package/dist/{runGemini-DvJwQVGp.mjs → runGemini-YqH7a-7O.mjs} +8 -8
  43. package/dist/{runOpenClaw-BG82bF18.mjs → runOpenClaw-DSSyFQBb.mjs} +7 -7
  44. package/dist/{runOpenClaw-BsvtCE_H.cjs → runOpenClaw-DoqZXBvL.cjs} +7 -7
  45. package/dist/{send-aUkjhey7.cjs → send-9075kHZP.cjs} +6 -6
  46. package/dist/{send-o6WM-1G8.mjs → send-Cs-RwqNf.mjs} +6 -6
  47. package/dist/{sessions-DlU3foY_.mjs → sessions-iBnYzVUK.mjs} +6 -6
  48. package/dist/{sessions-B3vP18kz.cjs → sessions-qULzUzlI.cjs} +6 -6
  49. package/dist/{spawn-DxHIIqhi.mjs → spawn-C6fRkedx.mjs} +7 -32
  50. package/dist/{spawn-DH6_p4-f.cjs → spawn-od9V23cc.cjs} +8 -33
  51. package/dist/{teams-DoAA_CcN.mjs → teams-CkrWQ4uM.mjs} +16 -73
  52. package/dist/{teams-BW8D1n3o.cjs → teams-CrCzU-AV.cjs} +14 -71
  53. package/dist/terminalTitleSuggest-DKgX3osW.mjs +38 -0
  54. package/dist/terminalTitleSuggest-Dji8I0Jy.cjs +45 -0
  55. package/dist/{todo-Bcc2XxGh.cjs → todo-BCT_ZPuV.cjs} +4 -23
  56. package/dist/{todo-BhJbG5eq.mjs → todo-aCZmSt27.mjs} +2 -21
  57. package/dist/{types-BN3eiipi.mjs → types-BvqOVUOW.mjs} +214 -1022
  58. package/dist/{types-CPubg05Z.cjs → types-CUCU7Vw8.cjs} +43 -863
  59. package/package.json +7 -7
  60. package/dist/agentGuidance-BXa9ikrZ.cjs +0 -267
  61. package/dist/agentGuidance-Dvo3cZMW.mjs +0 -251
  62. package/dist/limits-DS-bnYYp.mjs +0 -1165
  63. package/dist/limits-DwcY3kyi.cjs +0 -1236
  64. package/dist/projectPath-CSpqWdxp.mjs +0 -10
  65. package/dist/projectPath-ChLhdOyt.cjs +0 -13
package/dist/index.cjs CHANGED
@@ -1,10 +1,10 @@
1
1
  'use strict';
2
2
 
3
3
  require('chalk');
4
- require('./index-CqtfRzug.cjs');
5
- require('./limits-DwcY3kyi.cjs');
4
+ require('./index-DHrwHxHf.cjs');
5
+ require('./limits-DY_sFA3J.cjs');
6
6
  require('zod');
7
- require('./types-CPubg05Z.cjs');
7
+ require('./types-CUCU7Vw8.cjs');
8
8
  require('node:child_process');
9
9
  require('node:os');
10
10
  require('node:crypto');
@@ -12,10 +12,10 @@ require('cross-spawn');
12
12
  require('node:path');
13
13
  require('node:readline');
14
14
  require('node:fs');
15
- require('./projectPath-ChLhdOyt.cjs');
15
+ require('./terminalTitleSuggest-Dji8I0Jy.cjs');
16
16
  require('path');
17
17
  require('url');
18
- require('./agentGuidance-BXa9ikrZ.cjs');
18
+ require('./agentGuidance-BM4kMNyr.cjs');
19
19
  require('node:fs/promises');
20
20
  require('fs/promises');
21
21
  require('ink');
package/dist/index.mjs CHANGED
@@ -1,8 +1,8 @@
1
1
  import 'chalk';
2
- import './index-CIyjJy8D.mjs';
3
- import './limits-DS-bnYYp.mjs';
2
+ import './index-BacwoXzs.mjs';
3
+ import './limits-BtAuKRl2.mjs';
4
4
  import 'zod';
5
- import './types-BN3eiipi.mjs';
5
+ import './types-BvqOVUOW.mjs';
6
6
  import 'node:child_process';
7
7
  import 'node:os';
8
8
  import 'node:crypto';
@@ -10,10 +10,10 @@ import 'cross-spawn';
10
10
  import 'node:path';
11
11
  import 'node:readline';
12
12
  import 'node:fs';
13
- import './projectPath-CSpqWdxp.mjs';
13
+ import './terminalTitleSuggest-DKgX3osW.mjs';
14
14
  import 'path';
15
15
  import 'url';
16
- import './agentGuidance-Dvo3cZMW.mjs';
16
+ import './agentGuidance-C8unGWp7.mjs';
17
17
  import 'node:fs/promises';
18
18
  import 'fs/promises';
19
19
  import 'ink';
@@ -0,0 +1,118 @@
1
+ 'use strict';
2
+
3
+ var node_crypto = require('node:crypto');
4
+ var promises = require('node:fs/promises');
5
+ var path = require('node:path');
6
+ var index = require('./index-DHrwHxHf.cjs');
7
+
8
+ async function assertPrivateDestination(directory) {
9
+ for (let path$1 = directory; ; path$1 = path.dirname(path$1)) {
10
+ try {
11
+ const stat = await promises.lstat(path$1);
12
+ if (stat.isSymbolicLink() || !stat.isDirectory()) throw new Error("Refusing symlink or non-directory in managed skill destination: " + path$1);
13
+ } catch (error) {
14
+ if (error.code !== "ENOENT") throw error;
15
+ }
16
+ if (path.dirname(path$1) === path$1) break;
17
+ }
18
+ }
19
+ const digest = (text) => node_crypto.createHash("sha256").update(text).digest("hex");
20
+ async function read(path) {
21
+ try {
22
+ const stat = await promises.lstat(path);
23
+ if (!stat.isFile() || stat.isSymbolicLink()) throw new Error("Refusing non-regular managed file");
24
+ return await promises.readFile(path, "utf8");
25
+ } catch (error) {
26
+ if (error.code === "ENOENT") return null;
27
+ throw error;
28
+ }
29
+ }
30
+ async function publish(path, content) {
31
+ const temporary = `${path}.${node_crypto.randomUUID()}.new`;
32
+ try {
33
+ await promises.writeFile(temporary, content, { flag: "wx", mode: 384 });
34
+ await promises.rename(temporary, path);
35
+ } finally {
36
+ await promises.unlink(temporary).catch(() => {
37
+ });
38
+ }
39
+ }
40
+ async function installManagedSkill(skill, options) {
41
+ if (!/^[a-z0-9][a-z0-9-]{0,63}$/.test(skill.name)) throw new Error("Invalid managed skill name");
42
+ const directory = path.join(path.resolve(options.home), ".local", "share", "very-happy", "skills", skill.name);
43
+ await assertPrivateDestination(directory);
44
+ const file = path.join(directory, "SKILL.md");
45
+ const manifest = path.join(directory, ".very-happy-managed.json");
46
+ const current = await read(file);
47
+ const managed = await read(manifest);
48
+ const previous = managed ? JSON.parse(managed) : null;
49
+ if (previous && previous.owner !== "very-happy") throw new Error("Existing manifest is not owned by Very Happy");
50
+ if (current !== null && (!previous || previous.owner !== "very-happy" || previous.sha256 !== digest(current) && previous.pendingSha256 !== digest(current))) {
51
+ throw new Error("Existing skill is not owned by Very Happy or was edited; preserve it and resolve manually");
52
+ }
53
+ const action = options.uninstall ? current === null && managed === null ? "unchanged" : "remove" : current === skill.content && !previous?.pendingSha256 ? "unchanged" : "write";
54
+ if (options.apply && action !== "unchanged") {
55
+ await assertPrivateDestination(directory);
56
+ if (options.uninstall) {
57
+ if (current !== null) await promises.unlink(file);
58
+ if (managed !== null) await promises.unlink(manifest);
59
+ await promises.rmdir(directory).catch(() => {
60
+ });
61
+ } else {
62
+ await promises.mkdir(directory, { recursive: true });
63
+ const nextHash = digest(skill.content);
64
+ await publish(manifest, JSON.stringify({ owner: "very-happy", sha256: current === null ? null : digest(current), pendingSha256: nextHash, version: 1 }));
65
+ await publish(file, skill.content);
66
+ await publish(manifest, JSON.stringify({ owner: "very-happy", sha256: nextHash, version: 1 }));
67
+ }
68
+ }
69
+ return { host: options.host, skill: skill.name, path: file, action, applied: !!options.apply, discovery: "No host skill directories are modified. Read this absolute skill path in a managed session.", managedSessions: skill.managedSessions };
70
+ }
71
+ async function installTeamSkill(options) {
72
+ return installManagedSkill({ name: "very-happy-teams", content: index.TEAM_SKILL, managedSessions: "Team tools are injected by Very Happy. Standalone agents require a managed session connection." }, options);
73
+ }
74
+
75
+ const AUTOMATION_SKILL = `---
76
+ name: very-happy-automations
77
+ description: Create and operate Very Happy automations \u2014 cron / interval / one-shot / manual triggers that spawn an agent session, continue a sticky session, or run a script on a chosen machine, with tracked runs. Use when asked to schedule recurring work, react to external events with an agent session, or inspect why an automated run failed.
78
+ ---
79
+
80
+ # Very Happy Automations
81
+
82
+ An automation is account-level: a trigger (cron with time zone, fixed interval, one time, or manual only), an action (spawn a session, continue a sticky session, send into a fixed session, or run a script), and one machine that executes it. Every execution is a run with a status (queued \u2192 claimed \u2192 running \u2192 done | failed | skipped | expired | cancelled), an optional session link, a summary and an attention flag.
83
+
84
+ ## Surfaces
85
+
86
+ - Managed sessions (Claude, Codex, pi): the \`automation_*\` MCP tools \u2014 \`automation_list\`, \`automation_get\`, \`automation_create\`, \`automation_update\`, \`automation_pause\`, \`automation_resume\`, \`automation_delete\`, \`automation_run\`, \`automation_fire\`, \`automation_runs\`, \`automation_report\`, \`automation_ack\`.
87
+ - Terminal: \`very-happy auto --help\` (same operations; \`--json\` for scripts). Fire from any local process with \`very-happy auto fire <name> [--payload-json \u2026] [--dedupe-key \u2026] [--wait]\`.
88
+ - The machine defaults to the one you are running on. If the tools answer that automations are not enabled, the server gate is off; do not work around it.
89
+
90
+ ## Creating one
91
+
92
+ Pick the trigger, then the action. \`spawn\` takes an agent (claude / codex / pi / gemini / openclaw), an absolute directory and a prompt; defaults match the web launcher (yolo for claude / codex / pi). \`script\` takes an argv array (no shell): quote nothing, pass arguments as separate items. Prompts and argv may use \`{{payload}}\`, \`{{payload.field.path}}\` (JSON payload), \`{{run.id}}\`, \`{{automation.name}}\` and \`{{now}}\`.
93
+
94
+ Use \`sticky: { key }\` on a spawn action when repeated events about the same subject should continue one conversation: the key is a template (for example \`{{payload.conversationId}}\`). A run whose rendered key matches a live session sends the prompt into that session; otherwise it spawns a new one and remembers it. A dead or archived session is replaced on the next run.
95
+
96
+ \`concurrency: skip\` (default) records a run as skipped while the previous one is still running; \`queue\` waits. \`maxRuntimeMs\` bounds a run (default 6h; scripts 30m).
97
+
98
+ ## Event triggers
99
+
100
+ There is no inbound webhook. A private adapter (an IM bot, a watcher, a systemd unit) calls \`very-happy auto fire <name>\` with a payload and a dedupe key; the same key within 24h returns the original run instead of starting another. Prefer \`manual\` triggers for automations that only ever fire this way.
101
+
102
+ ## Finishing a run
103
+
104
+ When you are the session an automation started (\`VH_AUTOMATION_RUN_ID\` is set) or continued, finish by calling \`automation_report\` with \`status: done\` and a short summary, or \`status: failed\` with the error. Without an explicit report the daemon marks the run done when your turn ends and uses your last message as the summary \u2014 explicit reports are exact, so prefer them for long or multi-turn work. Set \`needsAttention\` with a reason when a human must look (blocked, ambiguous input, partial result).
105
+
106
+ ## Operating
107
+
108
+ Inspect \`automation_runs\` (or \`very-happy auto runs --attention\`) before changing anything. Runs stuck in queued mean the machine's daemon is offline; expired means the lease or the runtime bound lapsed. Acknowledge attention with \`automation_ack\` after handling it. Updates need the current \`version\` from \`automation_get\`; read again on a conflict. Do not create automations that spawn other automations, and never put credentials into prompts, payloads or script arguments \u2014 scripts inherit the daemon's environment.
109
+ `;
110
+
111
+ const AUTOMATION_SKILL_NAME = "very-happy-automations";
112
+ async function installAutomationSkill(options) {
113
+ return installManagedSkill({ name: AUTOMATION_SKILL_NAME, content: AUTOMATION_SKILL, managedSessions: "automation_* tools are injected into managed Claude, Codex and pi sessions; `very-happy auto` works from any terminal on a logged-in machine." }, options);
114
+ }
115
+
116
+ exports.AUTOMATION_SKILL = AUTOMATION_SKILL;
117
+ exports.installAutomationSkill = installAutomationSkill;
118
+ exports.installTeamSkill = installTeamSkill;
@@ -0,0 +1,114 @@
1
+ import { createHash, randomUUID } from 'node:crypto';
2
+ import { unlink, rmdir, mkdir, lstat, readFile, writeFile, rename } from 'node:fs/promises';
3
+ import { join, resolve, dirname } from 'node:path';
4
+ import { z as TEAM_SKILL } from './index-BacwoXzs.mjs';
5
+
6
+ async function assertPrivateDestination(directory) {
7
+ for (let path = directory; ; path = dirname(path)) {
8
+ try {
9
+ const stat = await lstat(path);
10
+ if (stat.isSymbolicLink() || !stat.isDirectory()) throw new Error("Refusing symlink or non-directory in managed skill destination: " + path);
11
+ } catch (error) {
12
+ if (error.code !== "ENOENT") throw error;
13
+ }
14
+ if (dirname(path) === path) break;
15
+ }
16
+ }
17
+ const digest = (text) => createHash("sha256").update(text).digest("hex");
18
+ async function read(path) {
19
+ try {
20
+ const stat = await lstat(path);
21
+ if (!stat.isFile() || stat.isSymbolicLink()) throw new Error("Refusing non-regular managed file");
22
+ return await readFile(path, "utf8");
23
+ } catch (error) {
24
+ if (error.code === "ENOENT") return null;
25
+ throw error;
26
+ }
27
+ }
28
+ async function publish(path, content) {
29
+ const temporary = `${path}.${randomUUID()}.new`;
30
+ try {
31
+ await writeFile(temporary, content, { flag: "wx", mode: 384 });
32
+ await rename(temporary, path);
33
+ } finally {
34
+ await unlink(temporary).catch(() => {
35
+ });
36
+ }
37
+ }
38
+ async function installManagedSkill(skill, options) {
39
+ if (!/^[a-z0-9][a-z0-9-]{0,63}$/.test(skill.name)) throw new Error("Invalid managed skill name");
40
+ const directory = join(resolve(options.home), ".local", "share", "very-happy", "skills", skill.name);
41
+ await assertPrivateDestination(directory);
42
+ const file = join(directory, "SKILL.md");
43
+ const manifest = join(directory, ".very-happy-managed.json");
44
+ const current = await read(file);
45
+ const managed = await read(manifest);
46
+ const previous = managed ? JSON.parse(managed) : null;
47
+ if (previous && previous.owner !== "very-happy") throw new Error("Existing manifest is not owned by Very Happy");
48
+ if (current !== null && (!previous || previous.owner !== "very-happy" || previous.sha256 !== digest(current) && previous.pendingSha256 !== digest(current))) {
49
+ throw new Error("Existing skill is not owned by Very Happy or was edited; preserve it and resolve manually");
50
+ }
51
+ const action = options.uninstall ? current === null && managed === null ? "unchanged" : "remove" : current === skill.content && !previous?.pendingSha256 ? "unchanged" : "write";
52
+ if (options.apply && action !== "unchanged") {
53
+ await assertPrivateDestination(directory);
54
+ if (options.uninstall) {
55
+ if (current !== null) await unlink(file);
56
+ if (managed !== null) await unlink(manifest);
57
+ await rmdir(directory).catch(() => {
58
+ });
59
+ } else {
60
+ await mkdir(directory, { recursive: true });
61
+ const nextHash = digest(skill.content);
62
+ await publish(manifest, JSON.stringify({ owner: "very-happy", sha256: current === null ? null : digest(current), pendingSha256: nextHash, version: 1 }));
63
+ await publish(file, skill.content);
64
+ await publish(manifest, JSON.stringify({ owner: "very-happy", sha256: nextHash, version: 1 }));
65
+ }
66
+ }
67
+ return { host: options.host, skill: skill.name, path: file, action, applied: !!options.apply, discovery: "No host skill directories are modified. Read this absolute skill path in a managed session.", managedSessions: skill.managedSessions };
68
+ }
69
+ async function installTeamSkill(options) {
70
+ return installManagedSkill({ name: "very-happy-teams", content: TEAM_SKILL, managedSessions: "Team tools are injected by Very Happy. Standalone agents require a managed session connection." }, options);
71
+ }
72
+
73
+ const AUTOMATION_SKILL = `---
74
+ name: very-happy-automations
75
+ description: Create and operate Very Happy automations \u2014 cron / interval / one-shot / manual triggers that spawn an agent session, continue a sticky session, or run a script on a chosen machine, with tracked runs. Use when asked to schedule recurring work, react to external events with an agent session, or inspect why an automated run failed.
76
+ ---
77
+
78
+ # Very Happy Automations
79
+
80
+ An automation is account-level: a trigger (cron with time zone, fixed interval, one time, or manual only), an action (spawn a session, continue a sticky session, send into a fixed session, or run a script), and one machine that executes it. Every execution is a run with a status (queued \u2192 claimed \u2192 running \u2192 done | failed | skipped | expired | cancelled), an optional session link, a summary and an attention flag.
81
+
82
+ ## Surfaces
83
+
84
+ - Managed sessions (Claude, Codex, pi): the \`automation_*\` MCP tools \u2014 \`automation_list\`, \`automation_get\`, \`automation_create\`, \`automation_update\`, \`automation_pause\`, \`automation_resume\`, \`automation_delete\`, \`automation_run\`, \`automation_fire\`, \`automation_runs\`, \`automation_report\`, \`automation_ack\`.
85
+ - Terminal: \`very-happy auto --help\` (same operations; \`--json\` for scripts). Fire from any local process with \`very-happy auto fire <name> [--payload-json \u2026] [--dedupe-key \u2026] [--wait]\`.
86
+ - The machine defaults to the one you are running on. If the tools answer that automations are not enabled, the server gate is off; do not work around it.
87
+
88
+ ## Creating one
89
+
90
+ Pick the trigger, then the action. \`spawn\` takes an agent (claude / codex / pi / gemini / openclaw), an absolute directory and a prompt; defaults match the web launcher (yolo for claude / codex / pi). \`script\` takes an argv array (no shell): quote nothing, pass arguments as separate items. Prompts and argv may use \`{{payload}}\`, \`{{payload.field.path}}\` (JSON payload), \`{{run.id}}\`, \`{{automation.name}}\` and \`{{now}}\`.
91
+
92
+ Use \`sticky: { key }\` on a spawn action when repeated events about the same subject should continue one conversation: the key is a template (for example \`{{payload.conversationId}}\`). A run whose rendered key matches a live session sends the prompt into that session; otherwise it spawns a new one and remembers it. A dead or archived session is replaced on the next run.
93
+
94
+ \`concurrency: skip\` (default) records a run as skipped while the previous one is still running; \`queue\` waits. \`maxRuntimeMs\` bounds a run (default 6h; scripts 30m).
95
+
96
+ ## Event triggers
97
+
98
+ There is no inbound webhook. A private adapter (an IM bot, a watcher, a systemd unit) calls \`very-happy auto fire <name>\` with a payload and a dedupe key; the same key within 24h returns the original run instead of starting another. Prefer \`manual\` triggers for automations that only ever fire this way.
99
+
100
+ ## Finishing a run
101
+
102
+ When you are the session an automation started (\`VH_AUTOMATION_RUN_ID\` is set) or continued, finish by calling \`automation_report\` with \`status: done\` and a short summary, or \`status: failed\` with the error. Without an explicit report the daemon marks the run done when your turn ends and uses your last message as the summary \u2014 explicit reports are exact, so prefer them for long or multi-turn work. Set \`needsAttention\` with a reason when a human must look (blocked, ambiguous input, partial result).
103
+
104
+ ## Operating
105
+
106
+ Inspect \`automation_runs\` (or \`very-happy auto runs --attention\`) before changing anything. Runs stuck in queued mean the machine's daemon is offline; expired means the lease or the runtime bound lapsed. Acknowledge attention with \`automation_ack\` after handling it. Updates need the current \`version\` from \`automation_get\`; read again on a conflict. Do not create automations that spawn other automations, and never put credentials into prompts, payloads or script arguments \u2014 scripts inherit the daemon's environment.
107
+ `;
108
+
109
+ const AUTOMATION_SKILL_NAME = "very-happy-automations";
110
+ async function installAutomationSkill(options) {
111
+ return installManagedSkill({ name: AUTOMATION_SKILL_NAME, content: AUTOMATION_SKILL, managedSessions: "automation_* tools are injected into managed Claude, Codex and pi sessions; `very-happy auto` works from any terminal on a logged-in machine." }, options);
112
+ }
113
+
114
+ export { AUTOMATION_SKILL as A, installTeamSkill as a, installAutomationSkill as i };
@@ -3,9 +3,10 @@ import { homedir } from 'node:os';
3
3
  import { join, resolve, dirname } from 'node:path';
4
4
  import { pathToFileURL } from 'node:url';
5
5
  import { randomUUID } from 'node:crypto';
6
- import { p as projectPath } from './projectPath-CSpqWdxp.mjs';
6
+ import { p as projectPath } from './terminalTitleSuggest-DKgX3osW.mjs';
7
7
  import 'path';
8
8
  import 'url';
9
+ import 'zod';
9
10
 
10
11
  const HEADER = "// Managed by Very Happy: native Pi terminal tools.\n";
11
12
  const PI_TOOLS_HELP = `very-happy install-pi-tools - enable tools in native Pi
@@ -5,9 +5,10 @@ var os = require('node:os');
5
5
  var path = require('node:path');
6
6
  var node_url = require('node:url');
7
7
  var node_crypto = require('node:crypto');
8
- var projectPath = require('./projectPath-ChLhdOyt.cjs');
8
+ var terminalTitleSuggest = require('./terminalTitleSuggest-Dji8I0Jy.cjs');
9
9
  require('path');
10
10
  require('url');
11
+ require('zod');
11
12
 
12
13
  const HEADER = "// Managed by Very Happy: native Pi terminal tools.\n";
13
14
  const PI_TOOLS_HELP = `very-happy install-pi-tools - enable tools in native Pi
@@ -51,7 +52,7 @@ async function installPiTools(remove = false) {
51
52
  await promises.unlink(target);
52
53
  return target;
53
54
  }
54
- const content = piToolsLoader(path.resolve(projectPath.projectPath(), "dist/piExtension.mjs"));
55
+ const content = piToolsLoader(path.resolve(terminalTitleSuggest.projectPath(), "dist/piExtension.mjs"));
55
56
  await promises.mkdir(path.dirname(target), { recursive: true, mode: 448 });
56
57
  const temporary = `${target}.${node_crypto.randomUUID()}.new`;
57
58
  try {
@@ -5,21 +5,21 @@ var path = require('node:path');
5
5
  var os = require('node:os');
6
6
  var node_child_process = require('node:child_process');
7
7
  var chalk = require('chalk');
8
- var projectPath = require('./projectPath-ChLhdOyt.cjs');
9
- var index = require('./index-CqtfRzug.cjs');
8
+ var terminalTitleSuggest = require('./terminalTitleSuggest-Dji8I0Jy.cjs');
9
+ var index = require('./index-DHrwHxHf.cjs');
10
10
  require('path');
11
11
  require('url');
12
+ require('zod');
12
13
  require('node:crypto');
13
- require('./types-CPubg05Z.cjs');
14
+ require('./types-CUCU7Vw8.cjs');
14
15
  require('axios');
15
- require('./limits-DwcY3kyi.cjs');
16
+ require('./limits-DY_sFA3J.cjs');
16
17
  require('node:fs/promises');
17
- require('zod');
18
18
  require('tweetnacl');
19
19
  require('node:util');
20
+ require('@paralleldrive/cuid2');
20
21
  require('node:events');
21
22
  require('socket.io-client');
22
- require('@paralleldrive/cuid2');
23
23
  require('child_process');
24
24
  require('util');
25
25
  require('fs/promises');
@@ -31,7 +31,7 @@ require('fs');
31
31
  require('node:readline');
32
32
  require('@anthropic-ai/sandbox-runtime');
33
33
  require('expo-server-sdk');
34
- require('./agentGuidance-BXa9ikrZ.cjs');
34
+ require('./agentGuidance-BM4kMNyr.cjs');
35
35
  require('ink');
36
36
  require('react');
37
37
  require('@anthropic-ai/claude-agent-sdk');
@@ -153,7 +153,7 @@ function claudeSettingsPath() {
153
153
  return path.join(configDir, "settings.json");
154
154
  }
155
155
  function terminalMirrorHookCommand() {
156
- const script = path.resolve(projectPath.projectPath(), "scripts", TERMINAL_MIRROR_FORWARDER_BASENAME);
156
+ const script = path.resolve(terminalTitleSuggest.projectPath(), "scripts", TERMINAL_MIRROR_FORWARDER_BASENAME);
157
157
  return `[ -f "${script}" ] && node "${script}" || true`;
158
158
  }
159
159
  async function installTerminalHooks(opts = {}) {
@@ -3,21 +3,21 @@ import { join, resolve } from 'node:path';
3
3
  import { homedir } from 'node:os';
4
4
  import { spawnSync } from 'node:child_process';
5
5
  import chalk from 'chalk';
6
- import { p as projectPath } from './projectPath-CSpqWdxp.mjs';
7
- import { t as tmuxSupportsSessionEnv } from './index-CIyjJy8D.mjs';
6
+ import { p as projectPath } from './terminalTitleSuggest-DKgX3osW.mjs';
7
+ import { j as tmuxSupportsSessionEnv } from './index-BacwoXzs.mjs';
8
8
  import 'path';
9
9
  import 'url';
10
+ import 'zod';
10
11
  import 'node:crypto';
11
- import './types-BN3eiipi.mjs';
12
+ import './types-BvqOVUOW.mjs';
12
13
  import 'axios';
13
- import './limits-DS-bnYYp.mjs';
14
+ import './limits-BtAuKRl2.mjs';
14
15
  import 'node:fs/promises';
15
- import 'zod';
16
16
  import 'tweetnacl';
17
17
  import 'node:util';
18
+ import '@paralleldrive/cuid2';
18
19
  import 'node:events';
19
20
  import 'socket.io-client';
20
- import '@paralleldrive/cuid2';
21
21
  import 'child_process';
22
22
  import 'util';
23
23
  import 'fs/promises';
@@ -29,7 +29,7 @@ import 'fs';
29
29
  import 'node:readline';
30
30
  import '@anthropic-ai/sandbox-runtime';
31
31
  import 'expo-server-sdk';
32
- import './agentGuidance-Dvo3cZMW.mjs';
32
+ import './agentGuidance-C8unGWp7.mjs';
33
33
  import 'ink';
34
34
  import 'react';
35
35
  import '@anthropic-ai/claude-agent-sdk';
package/dist/lib.cjs CHANGED
@@ -1,12 +1,11 @@
1
1
  'use strict';
2
2
 
3
- var index = require('./types-CPubg05Z.cjs');
4
- var persistence = require('./limits-DwcY3kyi.cjs');
3
+ var index = require('./types-CUCU7Vw8.cjs');
4
+ var persistence = require('./limits-DY_sFA3J.cjs');
5
5
  require('axios');
6
6
  require('node:events');
7
7
  require('socket.io-client');
8
8
  require('zod');
9
- require('@paralleldrive/cuid2');
10
9
  require('node:crypto');
11
10
  require('child_process');
12
11
  require('util');
@@ -14,9 +13,10 @@ require('fs/promises');
14
13
  require('crypto');
15
14
  require('path');
16
15
  require('cross-spawn');
17
- require('./projectPath-ChLhdOyt.cjs');
16
+ require('./terminalTitleSuggest-Dji8I0Jy.cjs');
18
17
  require('url');
19
18
  require('os');
19
+ require('@paralleldrive/cuid2');
20
20
  require('node:fs/promises');
21
21
  require('node:path');
22
22
  require('node:os');
package/dist/lib.mjs CHANGED
@@ -1,10 +1,9 @@
1
- export { A as ApiClient, a as ApiSessionClient, R as RawJSONLinesSchema } from './types-BN3eiipi.mjs';
2
- export { c as configuration, l as logger } from './limits-DS-bnYYp.mjs';
1
+ export { A as ApiClient, a as ApiSessionClient, R as RawJSONLinesSchema } from './types-BvqOVUOW.mjs';
2
+ export { c as configuration, l as logger } from './limits-BtAuKRl2.mjs';
3
3
  import 'axios';
4
4
  import 'node:events';
5
5
  import 'socket.io-client';
6
6
  import 'zod';
7
- import '@paralleldrive/cuid2';
8
7
  import 'node:crypto';
9
8
  import 'child_process';
10
9
  import 'util';
@@ -12,9 +11,10 @@ import 'fs/promises';
12
11
  import 'crypto';
13
12
  import 'path';
14
13
  import 'cross-spawn';
15
- import './projectPath-CSpqWdxp.mjs';
14
+ import './terminalTitleSuggest-DKgX3osW.mjs';
16
15
  import 'url';
17
16
  import 'os';
17
+ import '@paralleldrive/cuid2';
18
18
  import 'node:fs/promises';
19
19
  import 'node:path';
20
20
  import 'node:os';