ethos-cli 0.5.1 → 0.6.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.
@@ -1,10 +1,9 @@
1
- import { existsSync, readFileSync } from "node:fs";
2
- import { mkdir, readFile, writeFile } from "node:fs/promises";
3
- import { homedir } from "node:os";
4
- import { dirname, join } from "node:path";
1
+ import { readFileSync } from "node:fs";
2
+ import { resolveAgentClientId } from "../lib/agent-clients.js";
5
3
  import { withClient } from "../lib/command-helpers.js";
6
4
  import { isJsonMode } from "../lib/config.js";
7
5
  import { createFindPeopleHandoff } from "../lib/find-people-flow.js";
6
+ import { ALL_HOOK_AGENTS, detectHookAgents, installEagerOpenHook, parseHookAgents, uninstallEagerOpenHook, } from "../lib/hooks-installer.js";
8
7
  import { openInBrowser } from "../lib/open-browser.js";
9
8
  import { printResult } from "../lib/output.js";
10
9
  const SKILL = "find-people-at-companies";
@@ -12,9 +11,6 @@ const HOOK_EVENT = "UserPromptSubmit";
12
11
  // Explicit invocation only: the skill named in the prompt, with or without a
13
12
  // leading slash. Conservative on purpose so unrelated prompts never open a browser.
14
13
  const EAGER_OPEN_TRIGGER = /(^|\s)\/?find-people-at-companies\b/i;
15
- // Substring used to find/replace/remove our hook entry idempotently.
16
- const HOOK_COMMAND_MARKER = "hooks eager-open";
17
- const ALL_HOOK_AGENTS = ["claude-code"];
18
14
  export function registerHooks(program) {
19
15
  const hooks = program
20
16
  .command("hooks")
@@ -36,9 +32,14 @@ function registerEagerOpen(hooks) {
36
32
  if (!matchesEagerOpenTrigger(prompt))
37
33
  return; // not this skill - leave the prompt untouched
38
34
  await withClient(eagerOpen, async (client, config) => {
39
- const handoff = await createFindPeopleHandoff(client, config, { agentClient: "auto" });
40
- openInBrowser(handoff.handoff_url);
41
- emitAdditionalContext(eagerOpenContext(handoff));
35
+ const clientId = resolveAgentClientId("auto");
36
+ const handoff = await createFindPeopleHandoff(client, config, { agentClient: clientId });
37
+ // Codex uploads in its in-app Browser, a surface only the agent can open;
38
+ // the OS opener would surface the wrong window. So only shell clients
39
+ // (Claude Code) open here — on Codex we inject the URL for the agent to open.
40
+ if (clientId !== "codex")
41
+ openInBrowser(handoff.handoff_url);
42
+ emitAdditionalContext(eagerOpenContext(handoff, clientId));
42
43
  });
43
44
  }
44
45
  catch {
@@ -60,10 +61,14 @@ function readHookPrompt() {
60
61
  return "";
61
62
  }
62
63
  }
63
- function eagerOpenContext(handoff) {
64
+ function eagerOpenContext(handoff, clientId) {
64
65
  const workspace = handoff.org_name ? ` in workspace "${handoff.org_name}"` : "";
65
- return (`Ethos find-people: the CSV upload browser is already opening${workspace} (pre-started by a hook). ` +
66
- `Reuse this flow when running the ${SKILL} skill - do NOT run \`ethos flows start\` again. ` +
66
+ // Codex can't be OS-opened by the hook, so tell the agent to open it in-app;
67
+ // Claude Code's OS browser is already opening.
68
+ const lead = clientId === "codex"
69
+ ? `Ethos find-people: a CSV upload flow is ready${workspace}. Open ${handoff.upload_url} in your in-app Browser`
70
+ : `Ethos find-people: the CSV upload browser is already opening${workspace} (pre-started by a hook)`;
71
+ return (`${lead}. Reuse this flow when running the ${SKILL} skill - do NOT run \`ethos flows start\` again. ` +
67
72
  `handoff_token=${handoff.handoff_token}. ` +
68
73
  `Next: ask the user for their ICP and buying-signal brief while they upload, then run ` +
69
74
  `\`ethos flows wait ${handoff.handoff_token} --json\` to capture the uploaded table id.`);
@@ -76,18 +81,15 @@ function emitAdditionalContext(additionalContext) {
76
81
  function registerInstall(hooks) {
77
82
  const install = hooks
78
83
  .command("install")
79
- .description("Register the find-people eager-open UserPromptSubmit hook in your agent config")
80
- .option("-a, --agents <list>", `comma-separated agents (default: claude-code). Supported: ${ALL_HOOK_AGENTS.join(", ")}`);
84
+ .description("Register the find-people eager-open UserPromptSubmit hook in your agent config(s)")
85
+ .option("-a, --agents <list>", `comma-separated agents (default: auto-detect). Supported: ${ALL_HOOK_AGENTS.join(", ")}`);
81
86
  install.action(async () => {
82
- const agents = parseHookAgents(install.opts().agents);
83
- const command = `${ethosCliCommand()} hooks eager-open`;
84
- const results = [];
85
- for (const agent of agents) {
86
- if (agent === "claude-code")
87
- results.push(await installClaudeHook(command));
88
- }
87
+ const agents = parseHookAgents(install.opts().agents) ?? detectHookAgents();
88
+ const { command, results } = await installEagerOpenHook({ agents });
89
89
  printHooksResult(install, {
90
- label: `Eager-open hook installed for ${results.map((entry) => entry.agent).join(", ")} (command: ${command})`,
90
+ label: results.length
91
+ ? `Eager-open hook installed for ${results.map((entry) => entry.agent).join(", ")} (command: ${command})`
92
+ : "No supported agents detected (claude-code, codex). Install an agent, then run `ethos hooks install`.",
91
93
  data: { command, agents: results },
92
94
  });
93
95
  });
@@ -95,100 +97,20 @@ function registerInstall(hooks) {
95
97
  function registerUninstall(hooks) {
96
98
  const uninstall = hooks
97
99
  .command("uninstall")
98
- .description("Remove the find-people eager-open hook from your agent config")
99
- .option("-a, --agents <list>", `comma-separated agents (default: claude-code). Supported: ${ALL_HOOK_AGENTS.join(", ")}`);
100
+ .description("Remove the find-people eager-open hook from your agent config(s)")
101
+ .option("-a, --agents <list>", `comma-separated agents (default: all). Supported: ${ALL_HOOK_AGENTS.join(", ")}`);
100
102
  uninstall.action(async () => {
101
103
  const agents = parseHookAgents(uninstall.opts().agents);
102
- const results = [];
103
- for (const agent of agents) {
104
- if (agent === "claude-code")
105
- results.push(await uninstallClaudeHook());
106
- }
104
+ const { results } = await uninstallEagerOpenHook({ agents });
107
105
  const removed = results.reduce((total, entry) => total + entry.removed, 0);
108
106
  printHooksResult(uninstall, {
109
- label: removed ? `Removed the eager-open hook from ${results.map((entry) => entry.agent).join(", ")}` : "Nothing to remove.",
107
+ label: removed
108
+ ? `Removed the eager-open hook from ${results.filter((entry) => entry.removed).map((entry) => entry.agent).join(", ")}`
109
+ : "Nothing to remove.",
110
110
  data: { agents: results },
111
111
  });
112
112
  });
113
113
  }
114
- async function installClaudeHook(command) {
115
- const path = claudeSettingsPath();
116
- const settings = await readClaudeSettings(path);
117
- const allHooks = (settings.hooks ??= {});
118
- const list = (allHooks[HOOK_EVENT] ??= []);
119
- let updated = false;
120
- for (const group of list) {
121
- for (const entry of group.hooks ?? []) {
122
- if (typeof entry.command === "string" && entry.command.includes(HOOK_COMMAND_MARKER)) {
123
- entry.type = "command";
124
- entry.command = command;
125
- updated = true;
126
- }
127
- }
128
- }
129
- if (!updated) {
130
- list.push({ hooks: [{ type: "command", command }] });
131
- }
132
- await writeJsonFile(path, settings);
133
- return { agent: "claude-code", path, action: updated ? "updated" : "installed", command };
134
- }
135
- async function uninstallClaudeHook() {
136
- const path = claudeSettingsPath();
137
- if (!existsSync(path))
138
- return { agent: "claude-code", path, removed: 0 };
139
- const settings = await readClaudeSettings(path);
140
- const list = settings.hooks?.[HOOK_EVENT];
141
- if (!settings.hooks || !list)
142
- return { agent: "claude-code", path, removed: 0 };
143
- let removed = 0;
144
- const kept = [];
145
- for (const group of list) {
146
- const groupHooks = group.hooks ?? [];
147
- const filtered = groupHooks.filter((entry) => !(typeof entry.command === "string" && entry.command.includes(HOOK_COMMAND_MARKER)));
148
- removed += groupHooks.length - filtered.length;
149
- if (filtered.length)
150
- kept.push({ ...group, hooks: filtered });
151
- }
152
- if (kept.length)
153
- settings.hooks[HOOK_EVENT] = kept;
154
- else
155
- delete settings.hooks[HOOK_EVENT];
156
- await writeJsonFile(path, settings);
157
- return { agent: "claude-code", path, removed };
158
- }
159
- function ethosCliCommand() {
160
- return process.env.ETHOS_CLI_COMMAND || "ethos";
161
- }
162
- function claudeSettingsPath() {
163
- const base = process.env.CLAUDE_CONFIG_DIR || join(homedir(), ".claude");
164
- return join(base, "settings.json");
165
- }
166
- async function readClaudeSettings(path) {
167
- try {
168
- const raw = await readFile(path, "utf8");
169
- const parsed = raw.trim() ? JSON.parse(raw) : {};
170
- return parsed && typeof parsed === "object" ? parsed : {};
171
- }
172
- catch {
173
- return {};
174
- }
175
- }
176
- async function writeJsonFile(path, value) {
177
- await mkdir(dirname(path), { recursive: true });
178
- await writeFile(path, `${JSON.stringify(value, null, 2)}\n`);
179
- }
180
- function parseHookAgents(value) {
181
- if (!value)
182
- return ["claude-code"];
183
- const parsed = [];
184
- for (const raw of value.split(",").map((part) => part.trim()).filter(Boolean)) {
185
- if (ALL_HOOK_AGENTS.includes(raw))
186
- parsed.push(raw);
187
- else
188
- throw new Error(`unknown hook agent: ${raw} (supported: ${ALL_HOOK_AGENTS.join(", ")})`);
189
- }
190
- return parsed.length ? parsed : ["claude-code"];
191
- }
192
114
  function printHooksResult(command, result) {
193
115
  printResult({ apiUrl: "", apiKey: "", json: isJsonMode(command) }, result);
194
116
  }
@@ -1 +1 @@
1
- {"version":3,"file":"hooks.js","sourceRoot":"","sources":["../../src/commands/hooks.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"hooks.js","sourceRoot":"","sources":["../../src/commands/hooks.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,39 @@
1
+ export type HookAgentName = "claude-code" | "codex";
2
+ export interface HookAgent {
3
+ name: HookAgentName;
4
+ displayName: string;
5
+ settingsFile(): string;
6
+ detect(): boolean;
7
+ }
8
+ export declare const HOOK_AGENTS: Record<HookAgentName, HookAgent>;
9
+ export declare const ALL_HOOK_AGENTS: HookAgentName[];
10
+ export type HookInstallResult = {
11
+ agent: HookAgentName;
12
+ path: string;
13
+ action: "installed" | "updated";
14
+ };
15
+ export type HookRemoveResult = {
16
+ agent: HookAgentName;
17
+ path: string;
18
+ removed: number;
19
+ };
20
+ /**
21
+ * The command the hook runs. Bound to whichever CLI installs it: the dev CLI
22
+ * sets ETHOS_CLI_COMMAND (so `make dev` points the hook at localhost), while a
23
+ * normal `npm install` / global run has it unset and binds to prod `ethos`.
24
+ */
25
+ export declare function eagerOpenCommand(): string;
26
+ export declare function detectHookAgents(): HookAgentName[];
27
+ export declare function installEagerOpenHook(options?: {
28
+ agents?: HookAgentName[];
29
+ command?: string;
30
+ }): Promise<{
31
+ command: string;
32
+ results: HookInstallResult[];
33
+ }>;
34
+ export declare function uninstallEagerOpenHook(options?: {
35
+ agents?: HookAgentName[];
36
+ }): Promise<{
37
+ results: HookRemoveResult[];
38
+ }>;
39
+ export declare function parseHookAgents(value: string | undefined): HookAgentName[] | undefined;
@@ -0,0 +1,148 @@
1
+ import { existsSync } from "node:fs";
2
+ import { mkdir, readFile, rename, rm, writeFile } from "node:fs/promises";
3
+ import { homedir } from "node:os";
4
+ import { dirname, join } from "node:path";
5
+ export const HOOK_AGENTS = {
6
+ "claude-code": {
7
+ name: "claude-code",
8
+ displayName: "Claude Code",
9
+ settingsFile: () => join(process.env.CLAUDE_CONFIG_DIR || join(homedir(), ".claude"), "settings.json"),
10
+ detect: () => existsSync(process.env.CLAUDE_CONFIG_DIR || join(homedir(), ".claude")),
11
+ },
12
+ codex: {
13
+ name: "codex",
14
+ displayName: "Codex",
15
+ settingsFile: () => join(process.env.CODEX_HOME || join(homedir(), ".codex"), "hooks.json"),
16
+ detect: () => existsSync(process.env.CODEX_HOME || join(homedir(), ".codex")),
17
+ },
18
+ };
19
+ export const ALL_HOOK_AGENTS = Object.keys(HOOK_AGENTS);
20
+ const HOOK_EVENT = "UserPromptSubmit";
21
+ // Substring identifying our handler, so install/uninstall stay idempotent and
22
+ // migrate older bindings (e.g. a dev-CLI path) in place.
23
+ const HOOK_COMMAND_MARKER = "hooks eager-open";
24
+ /**
25
+ * The command the hook runs. Bound to whichever CLI installs it: the dev CLI
26
+ * sets ETHOS_CLI_COMMAND (so `make dev` points the hook at localhost), while a
27
+ * normal `npm install` / global run has it unset and binds to prod `ethos`.
28
+ */
29
+ export function eagerOpenCommand() {
30
+ return `${process.env.ETHOS_CLI_COMMAND || "ethos"} hooks eager-open`;
31
+ }
32
+ export function detectHookAgents() {
33
+ return ALL_HOOK_AGENTS.filter((name) => HOOK_AGENTS[name].detect());
34
+ }
35
+ export async function installEagerOpenHook(options = {}) {
36
+ const command = options.command ?? eagerOpenCommand();
37
+ const agents = options.agents ?? detectHookAgents();
38
+ const results = [];
39
+ for (const name of agents) {
40
+ const merged = await mergeHook(HOOK_AGENTS[name].settingsFile(), command);
41
+ results.push({ agent: name, ...merged });
42
+ }
43
+ return { command, results };
44
+ }
45
+ export async function uninstallEagerOpenHook(options = {}) {
46
+ const agents = options.agents ?? ALL_HOOK_AGENTS;
47
+ const results = [];
48
+ for (const name of agents) {
49
+ const removed = await removeHook(HOOK_AGENTS[name].settingsFile());
50
+ results.push({ agent: name, ...removed });
51
+ }
52
+ return { results };
53
+ }
54
+ async function mergeHook(path, command) {
55
+ const config = await readJsonObject(path);
56
+ const allHooks = (config.hooks ??= {});
57
+ const list = (allHooks[HOOK_EVENT] ??= []);
58
+ let updated = false;
59
+ for (const group of list) {
60
+ for (const entry of group.hooks ?? []) {
61
+ if (typeof entry.command === "string" && entry.command.includes(HOOK_COMMAND_MARKER)) {
62
+ entry.type = "command";
63
+ entry.command = command;
64
+ updated = true;
65
+ }
66
+ }
67
+ }
68
+ if (!updated)
69
+ list.push({ hooks: [{ type: "command", command }] });
70
+ await writeJsonFile(path, config);
71
+ return { path, action: updated ? "updated" : "installed" };
72
+ }
73
+ async function removeHook(path) {
74
+ if (!existsSync(path))
75
+ return { path, removed: 0 };
76
+ const config = await readJsonObject(path);
77
+ const list = config.hooks?.[HOOK_EVENT];
78
+ if (!config.hooks || !list)
79
+ return { path, removed: 0 };
80
+ let removed = 0;
81
+ const kept = [];
82
+ for (const group of list) {
83
+ const groupHooks = group.hooks ?? [];
84
+ const filtered = groupHooks.filter((entry) => !(typeof entry.command === "string" && entry.command.includes(HOOK_COMMAND_MARKER)));
85
+ removed += groupHooks.length - filtered.length;
86
+ if (filtered.length)
87
+ kept.push({ ...group, hooks: filtered });
88
+ }
89
+ if (kept.length)
90
+ config.hooks[HOOK_EVENT] = kept;
91
+ else
92
+ delete config.hooks[HOOK_EVENT];
93
+ await writeJsonFile(path, config);
94
+ return { path, removed };
95
+ }
96
+ async function readJsonObject(path) {
97
+ let raw;
98
+ try {
99
+ raw = await readFile(path, "utf8");
100
+ }
101
+ catch (error) {
102
+ if (error.code === "ENOENT")
103
+ return {}; // no config yet - create it
104
+ throw error; // can't read (e.g. permissions) - don't risk clobbering it
105
+ }
106
+ if (!raw.trim())
107
+ return {};
108
+ // Refuse to parse-and-overwrite a config that exists but is malformed, or the
109
+ // merged write-back would destroy the user's real settings (model, secrets...).
110
+ try {
111
+ const parsed = JSON.parse(raw);
112
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
113
+ throw new Error("expected a JSON object");
114
+ }
115
+ return parsed;
116
+ }
117
+ catch (error) {
118
+ throw new Error(`Refusing to modify ${path}: not valid JSON (${error instanceof Error ? error.message : String(error)}). Fix or remove it, then retry.`);
119
+ }
120
+ }
121
+ async function writeJsonFile(path, value) {
122
+ await mkdir(dirname(path), { recursive: true });
123
+ // Atomic write: a direct writeFile could leave the user's config truncated if
124
+ // interrupted mid-write. Write a sibling temp file, then rename over the target
125
+ // (rename is atomic), so the real file is only ever the old or the complete new.
126
+ const tmp = `${path}.${process.pid}.${Date.now()}.tmp`;
127
+ await writeFile(tmp, `${JSON.stringify(value, null, 2)}\n`);
128
+ try {
129
+ await rename(tmp, path);
130
+ }
131
+ catch (error) {
132
+ await rm(tmp, { force: true }).catch(() => { });
133
+ throw error;
134
+ }
135
+ }
136
+ export function parseHookAgents(value) {
137
+ if (!value)
138
+ return undefined;
139
+ const parsed = [];
140
+ for (const raw of value.split(",").map((part) => part.trim()).filter(Boolean)) {
141
+ if (ALL_HOOK_AGENTS.includes(raw))
142
+ parsed.push(raw);
143
+ else
144
+ throw new Error(`unknown hook agent: ${raw} (supported: ${ALL_HOOK_AGENTS.join(", ")})`);
145
+ }
146
+ return parsed.length ? parsed : undefined;
147
+ }
148
+ //# sourceMappingURL=hooks-installer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hooks-installer.js","sourceRoot":"","sources":["../../src/lib/hooks-installer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC1E,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAe1C,MAAM,CAAC,MAAM,WAAW,GAAqC;IAC3D,aAAa,EAAE;QACb,IAAI,EAAE,aAAa;QACnB,WAAW,EAAE,aAAa;QAC1B,YAAY,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,iBAAiB,IAAI,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,CAAC,EAAE,eAAe,CAAC;QACtG,MAAM,EAAE,GAAG,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC,GAAG,CAAC,iBAAiB,IAAI,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,CAAC,CAAC;KACtF;IACD,KAAK,EAAE;QACL,IAAI,EAAE,OAAO;QACb,WAAW,EAAE,OAAO;QACpB,YAAY,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,UAAU,IAAI,IAAI,CAAC,OAAO,EAAE,EAAE,QAAQ,CAAC,EAAE,YAAY,CAAC;QAC3F,MAAM,EAAE,GAAG,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC,GAAG,CAAC,UAAU,IAAI,IAAI,CAAC,OAAO,EAAE,EAAE,QAAQ,CAAC,CAAC;KAC9E;CACF,CAAC;AAEF,MAAM,CAAC,MAAM,eAAe,GAAG,MAAM,CAAC,IAAI,CAAC,WAAW,CAAoB,CAAC;AAE3E,MAAM,UAAU,GAAG,kBAAkB,CAAC;AACtC,8EAA8E;AAC9E,yDAAyD;AACzD,MAAM,mBAAmB,GAAG,kBAAkB,CAAC;AAS/C;;;;GAIG;AACH,MAAM,UAAU,gBAAgB;IAC9B,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,iBAAiB,IAAI,OAAO,mBAAmB,CAAC;AACxE,CAAC;AAED,MAAM,UAAU,gBAAgB;IAC9B,OAAO,eAAe,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC;AACtE,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,UAA0D,EAAE;IAE5D,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,IAAI,gBAAgB,EAAE,CAAC;IACtD,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,gBAAgB,EAAE,CAAC;IACpD,MAAM,OAAO,GAAwB,EAAE,CAAC;IACxC,KAAK,MAAM,IAAI,IAAI,MAAM,EAAE,CAAC;QAC1B,MAAM,MAAM,GAAG,MAAM,SAAS,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,YAAY,EAAE,EAAE,OAAO,CAAC,CAAC;QAC1E,OAAO,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,MAAM,EAAE,CAAC,CAAC;IAC3C,CAAC;IACD,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC;AAC9B,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAC1C,UAAwC,EAAE;IAE1C,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,eAAe,CAAC;IACjD,MAAM,OAAO,GAAuB,EAAE,CAAC;IACvC,KAAK,MAAM,IAAI,IAAI,MAAM,EAAE,CAAC;QAC1B,MAAM,OAAO,GAAG,MAAM,UAAU,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,YAAY,EAAE,CAAC,CAAC;QACnE,OAAO,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,OAAO,EAAE,CAAC,CAAC;IAC5C,CAAC;IACD,OAAO,EAAE,OAAO,EAAE,CAAC;AACrB,CAAC;AAED,KAAK,UAAU,SAAS,CAAC,IAAY,EAAE,OAAe;IACpD,MAAM,MAAM,GAAG,MAAM,cAAc,CAAC,IAAI,CAAC,CAAC;IAC1C,MAAM,QAAQ,GAAG,CAAC,MAAM,CAAC,KAAK,KAAK,EAAE,CAAC,CAAC;IACvC,MAAM,IAAI,GAAG,CAAC,QAAQ,CAAC,UAAU,CAAC,KAAK,EAAE,CAAC,CAAC;IAE3C,IAAI,OAAO,GAAG,KAAK,CAAC;IACpB,KAAK,MAAM,KAAK,IAAI,IAAI,EAAE,CAAC;QACzB,KAAK,MAAM,KAAK,IAAI,KAAK,CAAC,KAAK,IAAI,EAAE,EAAE,CAAC;YACtC,IAAI,OAAO,KAAK,CAAC,OAAO,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAC,EAAE,CAAC;gBACrF,KAAK,CAAC,IAAI,GAAG,SAAS,CAAC;gBACvB,KAAK,CAAC,OAAO,GAAG,OAAO,CAAC;gBACxB,OAAO,GAAG,IAAI,CAAC;YACjB,CAAC;QACH,CAAC;IACH,CAAC;IACD,IAAI,CAAC,OAAO;QAAE,IAAI,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC,CAAC;IAEnE,MAAM,aAAa,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IAClC,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;AAC7D,CAAC;AAED,KAAK,UAAU,UAAU,CAAC,IAAY;IACpC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC;IACnD,MAAM,MAAM,GAAG,MAAM,cAAc,CAAC,IAAI,CAAC,CAAC;IAC1C,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK,EAAE,CAAC,UAAU,CAAC,CAAC;IACxC,IAAI,CAAC,MAAM,CAAC,KAAK,IAAI,CAAC,IAAI;QAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC;IAExD,IAAI,OAAO,GAAG,CAAC,CAAC;IAChB,MAAM,IAAI,GAAgB,EAAE,CAAC;IAC7B,KAAK,MAAM,KAAK,IAAI,IAAI,EAAE,CAAC;QACzB,MAAM,UAAU,GAAG,KAAK,CAAC,KAAK,IAAI,EAAE,CAAC;QACrC,MAAM,QAAQ,GAAG,UAAU,CAAC,MAAM,CAChC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,KAAK,CAAC,OAAO,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAC,CAAC,CAC/F,CAAC;QACF,OAAO,IAAI,UAAU,CAAC,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAC;QAC/C,IAAI,QAAQ,CAAC,MAAM;YAAE,IAAI,CAAC,IAAI,CAAC,EAAE,GAAG,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC;IAChE,CAAC;IAED,IAAI,IAAI,CAAC,MAAM;QAAE,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,GAAG,IAAI,CAAC;;QAC5C,OAAO,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;IAErC,MAAM,aAAa,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IAClC,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;AAC3B,CAAC;AAED,KAAK,UAAU,cAAc,CAAC,IAAY;IACxC,IAAI,GAAW,CAAC;IAChB,IAAI,CAAC;QACH,GAAG,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IACrC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAK,KAA+B,CAAC,IAAI,KAAK,QAAQ;YAAE,OAAO,EAAE,CAAC,CAAC,4BAA4B;QAC/F,MAAM,KAAK,CAAC,CAAC,2DAA2D;IAC1E,CAAC;IACD,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE;QAAE,OAAO,EAAE,CAAC;IAC3B,8EAA8E;IAC9E,gFAAgF;IAChF,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAC/B,IAAI,CAAC,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;YACnE,MAAM,IAAI,KAAK,CAAC,wBAAwB,CAAC,CAAC;QAC5C,CAAC;QACD,OAAO,MAAqB,CAAC;IAC/B,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,IAAI,KAAK,CACb,sBAAsB,IAAI,qBAAqB,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,kCAAkC,CACxI,CAAC;IACJ,CAAC;AACH,CAAC;AAED,KAAK,UAAU,aAAa,CAAC,IAAY,EAAE,KAAc;IACvD,MAAM,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAChD,8EAA8E;IAC9E,gFAAgF;IAChF,iFAAiF;IACjF,MAAM,GAAG,GAAG,GAAG,IAAI,IAAI,OAAO,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,EAAE,MAAM,CAAC;IACvD,MAAM,SAAS,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC;IAC5D,IAAI,CAAC;QACH,MAAM,MAAM,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IAC1B,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,EAAE,CAAC,GAAG,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;QAC/C,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,KAAyB;IACvD,IAAI,CAAC,KAAK;QAAE,OAAO,SAAS,CAAC;IAC7B,MAAM,MAAM,GAAoB,EAAE,CAAC;IACnC,KAAK,MAAM,GAAG,IAAI,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC;QAC9E,IAAK,eAA4B,CAAC,QAAQ,CAAC,GAAG,CAAC;YAAE,MAAM,CAAC,IAAI,CAAC,GAAoB,CAAC,CAAC;;YAC9E,MAAM,IAAI,KAAK,CAAC,uBAAuB,GAAG,gBAAgB,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAChG,CAAC;IACD,OAAO,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC;AAC5C,CAAC"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,26 @@
1
+ import { HOOK_AGENTS, detectHookAgents, installEagerOpenHook } from "../lib/hooks-installer.js";
2
+ // Runs on `npm install`. Re-binds the find-people eager-open hook to the CLI
3
+ // being installed — for a normal global install that is prod `ethos`, so
4
+ // installing through npm always returns the hook to production (a `make dev`
5
+ // run rebinds it to the dev CLI for local testing).
6
+ async function main() {
7
+ if (process.env.ETHOS_SKIP_HOOKS || process.env.ETHOS_SKIP_SKILLS) {
8
+ process.stderr.write("ethos: skipping eager-open hook install.\n");
9
+ return;
10
+ }
11
+ const uid = typeof process.getuid === "function" ? process.getuid() : null;
12
+ if (uid === 0 && process.env.SUDO_USER) {
13
+ process.stderr.write("ethos: detected sudo install - skipping hook install. Run `ethos hooks install` as your user.\n");
14
+ return;
15
+ }
16
+ const agents = detectHookAgents();
17
+ if (!agents.length)
18
+ return;
19
+ const { command, results } = await installEagerOpenHook({ agents });
20
+ const targets = results.map((entry) => HOOK_AGENTS[entry.agent].displayName).join(", ");
21
+ process.stderr.write(`ethos: eager-open hook installed for ${targets} (command: ${command})\n`);
22
+ }
23
+ main().catch((err) => {
24
+ process.stderr.write(`ethos: hook install failed (continuing) - ${err instanceof Error ? err.message : String(err)}\nRun \`ethos hooks install\` manually after install completes.\n`);
25
+ });
26
+ //# sourceMappingURL=install-hooks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"install-hooks.js","sourceRoot":"","sources":["../../src/scripts/install-hooks.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AAEhG,6EAA6E;AAC7E,yEAAyE;AACzE,6EAA6E;AAC7E,oDAAoD;AACpD,KAAK,UAAU,IAAI;IACjB,IAAI,OAAO,CAAC,GAAG,CAAC,gBAAgB,IAAI,OAAO,CAAC,GAAG,CAAC,iBAAiB,EAAE,CAAC;QAClE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,4CAA4C,CAAC,CAAC;QACnE,OAAO;IACT,CAAC;IAED,MAAM,GAAG,GAAG,OAAO,OAAO,CAAC,MAAM,KAAK,UAAU,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;IAC3E,IAAI,GAAG,KAAK,CAAC,IAAI,OAAO,CAAC,GAAG,CAAC,SAAS,EAAE,CAAC;QACvC,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,iGAAiG,CAClG,CAAC;QACF,OAAO;IACT,CAAC;IAED,MAAM,MAAM,GAAG,gBAAgB,EAAE,CAAC;IAClC,IAAI,CAAC,MAAM,CAAC,MAAM;QAAE,OAAO;IAE3B,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,MAAM,oBAAoB,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC;IACpE,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,WAAW,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACxF,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,wCAAwC,OAAO,cAAc,OAAO,KAAK,CAAC,CAAC;AAClG,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;IACnB,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,6CACE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CACjD,mEAAmE,CACpE,CAAC;AACJ,CAAC,CAAC,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ethos-cli",
3
- "version": "0.5.1",
3
+ "version": "0.6.0",
4
4
  "description": "Commander-based CLI for Ethos table operations",
5
5
  "homepage": "https://github.com/cluster-software/ethos#readme",
6
6
  "repository": {
package/postinstall.cjs CHANGED
@@ -6,18 +6,30 @@
6
6
  "use strict";
7
7
  const fs = require("node:fs");
8
8
  const path = require("node:path");
9
+ const { pathToFileURL } = require("node:url");
9
10
 
10
- const target = path.join(__dirname, "dist", "scripts", "install-skills.js");
11
+ const distScripts = path.join(__dirname, "dist", "scripts");
12
+ const skillsTarget = path.join(distScripts, "install-skills.js");
13
+ const hooksTarget = path.join(distScripts, "install-hooks.js");
11
14
 
12
- if (!fs.existsSync(target)) {
15
+ if (!fs.existsSync(skillsTarget)) {
13
16
  process.exit(0);
14
17
  }
15
18
 
16
- import(target).catch((err) => {
19
+ function warn(label, err) {
17
20
  process.stderr.write(
18
- "ethos: skills install crashed (continuing) - " +
19
- (err && err.message ? err.message : String(err)) +
20
- "\n",
21
+ "ethos: " + label + " crashed (continuing) - " + (err && err.message ? err.message : String(err)) + "\n",
21
22
  );
22
- process.exit(0);
23
- });
23
+ }
24
+
25
+ // Each script self-invokes its own async main(); we let the event loop drain
26
+ // naturally rather than force-exiting, so installs finish writing.
27
+ async function bootstrap() {
28
+ // pathToFileURL: dynamic import() of an absolute path needs a file:// URL on Windows.
29
+ await import(pathToFileURL(skillsTarget).href).catch((err) => warn("skills install", err));
30
+ if (fs.existsSync(hooksTarget)) {
31
+ await import(pathToFileURL(hooksTarget).href).catch((err) => warn("hook install", err));
32
+ }
33
+ }
34
+
35
+ bootstrap();