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.
- package/dist/commands/hooks.js +31 -109
- package/dist/commands/hooks.js.map +1 -1
- package/dist/lib/hooks-installer.d.ts +39 -0
- package/dist/lib/hooks-installer.js +148 -0
- package/dist/lib/hooks-installer.js.map +1 -0
- package/dist/scripts/install-hooks.d.ts +1 -0
- package/dist/scripts/install-hooks.js +26 -0
- package/dist/scripts/install-hooks.js.map +1 -0
- package/package.json +1 -1
- package/postinstall.cjs +20 -8
package/dist/commands/hooks.js
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
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
|
|
40
|
-
|
|
41
|
-
|
|
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
|
-
|
|
66
|
-
|
|
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:
|
|
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 =
|
|
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:
|
|
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:
|
|
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
|
|
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":"AAAA,OAAO,EAAE,
|
|
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":"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"}
|
|
@@ -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
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
|
|
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(
|
|
15
|
+
if (!fs.existsSync(skillsTarget)) {
|
|
13
16
|
process.exit(0);
|
|
14
17
|
}
|
|
15
18
|
|
|
16
|
-
|
|
19
|
+
function warn(label, err) {
|
|
17
20
|
process.stderr.write(
|
|
18
|
-
"ethos:
|
|
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
|
-
|
|
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();
|