ethos-cli 0.6.0 → 0.7.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 +24 -34
- package/dist/commands/hooks.js.map +1 -1
- package/dist/lib/hooks-installer.d.ts +9 -25
- package/dist/lib/hooks-installer.js +36 -47
- package/dist/lib/hooks-installer.js.map +1 -1
- package/dist/scripts/install-hooks.js +12 -11
- package/dist/scripts/install-hooks.js.map +1 -1
- package/package.json +1 -1
- package/skills/find-people-at-companies.md +39 -40
package/dist/commands/hooks.js
CHANGED
|
@@ -3,7 +3,7 @@ import { resolveAgentClientId } from "../lib/agent-clients.js";
|
|
|
3
3
|
import { withClient } from "../lib/command-helpers.js";
|
|
4
4
|
import { isJsonMode } from "../lib/config.js";
|
|
5
5
|
import { createFindPeopleHandoff } from "../lib/find-people-flow.js";
|
|
6
|
-
import {
|
|
6
|
+
import { installEagerOpenHook, uninstallEagerOpenHook } from "../lib/hooks-installer.js";
|
|
7
7
|
import { openInBrowser } from "../lib/open-browser.js";
|
|
8
8
|
import { printResult } from "../lib/output.js";
|
|
9
9
|
const SKILL = "find-people-at-companies";
|
|
@@ -25,21 +25,20 @@ export function matchesEagerOpenTrigger(prompt) {
|
|
|
25
25
|
function registerEagerOpen(hooks) {
|
|
26
26
|
const eagerOpen = hooks
|
|
27
27
|
.command("eager-open")
|
|
28
|
-
.description(`${HOOK_EVENT} hook handler: when the ${SKILL} skill is invoked, pre-open the upload browser and inject the started handoff for the skill to reuse`);
|
|
28
|
+
.description(`${HOOK_EVENT} hook handler (Claude Code): when the ${SKILL} skill is invoked, pre-open the upload browser and inject the started handoff for the skill to reuse`);
|
|
29
29
|
eagerOpen.action(async () => {
|
|
30
30
|
try {
|
|
31
31
|
const prompt = readHookPrompt();
|
|
32
32
|
if (!matchesEagerOpenTrigger(prompt))
|
|
33
33
|
return; // not this skill - leave the prompt untouched
|
|
34
|
+
// Codex's upload is its agent-driven in-app Browser, which this hook can't
|
|
35
|
+
// open; the skill drives Codex, so the hook is a no-op there.
|
|
36
|
+
if (resolveAgentClientId("auto") === "codex")
|
|
37
|
+
return;
|
|
34
38
|
await withClient(eagerOpen, async (client, config) => {
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
|
|
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));
|
|
39
|
+
const handoff = await createFindPeopleHandoff(client, config, { agentClient: "claude-desktop" });
|
|
40
|
+
openInBrowser(handoff.handoff_url);
|
|
41
|
+
emitAdditionalContext(eagerOpenContext(handoff));
|
|
43
42
|
});
|
|
44
43
|
}
|
|
45
44
|
catch {
|
|
@@ -61,14 +60,10 @@ function readHookPrompt() {
|
|
|
61
60
|
return "";
|
|
62
61
|
}
|
|
63
62
|
}
|
|
64
|
-
function eagerOpenContext(handoff
|
|
63
|
+
function eagerOpenContext(handoff) {
|
|
65
64
|
const workspace = handoff.org_name ? ` in workspace "${handoff.org_name}"` : "";
|
|
66
|
-
|
|
67
|
-
|
|
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. ` +
|
|
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. ` +
|
|
72
67
|
`handoff_token=${handoff.handoff_token}. ` +
|
|
73
68
|
`Next: ask the user for their ICP and buying-signal brief while they upload, then run ` +
|
|
74
69
|
`\`ethos flows wait ${handoff.handoff_token} --json\` to capture the uploaded table id.`);
|
|
@@ -81,33 +76,28 @@ function emitAdditionalContext(additionalContext) {
|
|
|
81
76
|
function registerInstall(hooks) {
|
|
82
77
|
const install = hooks
|
|
83
78
|
.command("install")
|
|
84
|
-
.description("Register the find-people eager-open UserPromptSubmit hook in
|
|
85
|
-
.option("-a, --agents <list>", `comma-separated agents (default: auto-detect). Supported: ${ALL_HOOK_AGENTS.join(", ")}`);
|
|
79
|
+
.description("Register the find-people eager-open UserPromptSubmit hook in Claude Code (and prune any legacy Codex hook)");
|
|
86
80
|
install.action(async () => {
|
|
87
|
-
const
|
|
88
|
-
const
|
|
81
|
+
const { command, claude, codexPruned } = await installEagerOpenHook();
|
|
82
|
+
const notes = [`Claude Code: ${claude.action} (${claude.path})`];
|
|
83
|
+
if (codexPruned.action === "removed")
|
|
84
|
+
notes.push(`removed legacy Codex hook (${codexPruned.path})`);
|
|
89
85
|
printHooksResult(install, {
|
|
90
|
-
label:
|
|
91
|
-
|
|
92
|
-
: "No supported agents detected (claude-code, codex). Install an agent, then run `ethos hooks install`.",
|
|
93
|
-
data: { command, agents: results },
|
|
86
|
+
label: `Eager-open hook ${claude.action} for Claude Code (command: ${command})`,
|
|
87
|
+
data: { command, claude, codex_pruned: codexPruned, notes },
|
|
94
88
|
});
|
|
95
89
|
});
|
|
96
90
|
}
|
|
97
91
|
function registerUninstall(hooks) {
|
|
98
92
|
const uninstall = hooks
|
|
99
93
|
.command("uninstall")
|
|
100
|
-
.description("Remove the find-people eager-open hook from
|
|
101
|
-
.option("-a, --agents <list>", `comma-separated agents (default: all). Supported: ${ALL_HOOK_AGENTS.join(", ")}`);
|
|
94
|
+
.description("Remove the find-people eager-open hook from Claude Code (and any legacy Codex hook)");
|
|
102
95
|
uninstall.action(async () => {
|
|
103
|
-
const
|
|
104
|
-
const
|
|
105
|
-
const removed = results.reduce((total, entry) => total + entry.removed, 0);
|
|
96
|
+
const { claude, codex } = await uninstallEagerOpenHook();
|
|
97
|
+
const removed = [claude, codex].filter((entry) => entry.action === "removed");
|
|
106
98
|
printHooksResult(uninstall, {
|
|
107
|
-
label: removed
|
|
108
|
-
|
|
109
|
-
: "Nothing to remove.",
|
|
110
|
-
data: { agents: results },
|
|
99
|
+
label: removed.length ? `Removed the eager-open hook from ${removed.map((entry) => entry.path).join(", ")}` : "Nothing to remove.",
|
|
100
|
+
data: { claude, codex },
|
|
111
101
|
});
|
|
112
102
|
});
|
|
113
103
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hooks.js","sourceRoot":"","sources":["../../src/commands/hooks.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAEvC,OAAO,
|
|
1
|
+
{"version":3,"file":"hooks.js","sourceRoot":"","sources":["../../src/commands/hooks.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,39 +1,23 @@
|
|
|
1
|
-
export type
|
|
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;
|
|
1
|
+
export type HookFileResult = {
|
|
12
2
|
path: string;
|
|
13
|
-
action: "installed" | "updated";
|
|
14
|
-
};
|
|
15
|
-
export type HookRemoveResult = {
|
|
16
|
-
agent: HookAgentName;
|
|
17
|
-
path: string;
|
|
18
|
-
removed: number;
|
|
3
|
+
action: "installed" | "updated" | "removed" | "absent";
|
|
19
4
|
};
|
|
5
|
+
export declare function claudeCodeDetected(): boolean;
|
|
6
|
+
export declare function claudeSettingsPath(): string;
|
|
20
7
|
/**
|
|
21
8
|
* The command the hook runs. Bound to whichever CLI installs it: the dev CLI
|
|
22
9
|
* sets ETHOS_CLI_COMMAND (so `make dev` points the hook at localhost), while a
|
|
23
10
|
* normal `npm install` / global run has it unset and binds to prod `ethos`.
|
|
24
11
|
*/
|
|
25
12
|
export declare function eagerOpenCommand(): string;
|
|
26
|
-
export declare function detectHookAgents(): HookAgentName[];
|
|
27
13
|
export declare function installEagerOpenHook(options?: {
|
|
28
|
-
agents?: HookAgentName[];
|
|
29
14
|
command?: string;
|
|
30
15
|
}): Promise<{
|
|
31
16
|
command: string;
|
|
32
|
-
|
|
17
|
+
claude: HookFileResult;
|
|
18
|
+
codexPruned: HookFileResult;
|
|
33
19
|
}>;
|
|
34
|
-
export declare function uninstallEagerOpenHook(
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
results: HookRemoveResult[];
|
|
20
|
+
export declare function uninstallEagerOpenHook(): Promise<{
|
|
21
|
+
claude: HookFileResult;
|
|
22
|
+
codex: HookFileResult;
|
|
38
23
|
}>;
|
|
39
|
-
export declare function parseHookAgents(value: string | undefined): HookAgentName[] | undefined;
|
|
@@ -2,25 +2,23 @@ import { existsSync } from "node:fs";
|
|
|
2
2
|
import { mkdir, readFile, rename, rm, writeFile } from "node:fs/promises";
|
|
3
3
|
import { homedir } from "node:os";
|
|
4
4
|
import { dirname, join } from "node:path";
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
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);
|
|
5
|
+
// The eager-open hook is Claude Code only. On Codex the CSV upload happens in its
|
|
6
|
+
// agent-driven in-app Browser, which a hook cannot open, so a Codex hook would
|
|
7
|
+
// only create a duplicate handoff — the find-people skill drives Codex itself.
|
|
8
|
+
// ethos-cli <= 0.6.0 also installed a Codex hook; install prunes that legacy file.
|
|
20
9
|
const HOOK_EVENT = "UserPromptSubmit";
|
|
21
10
|
// Substring identifying our handler, so install/uninstall stay idempotent and
|
|
22
|
-
// migrate older
|
|
11
|
+
// migrate an older binding (e.g. a dev-CLI path) in place.
|
|
23
12
|
const HOOK_COMMAND_MARKER = "hooks eager-open";
|
|
13
|
+
export function claudeCodeDetected() {
|
|
14
|
+
return existsSync(process.env.CLAUDE_CONFIG_DIR || join(homedir(), ".claude"));
|
|
15
|
+
}
|
|
16
|
+
export function claudeSettingsPath() {
|
|
17
|
+
return join(process.env.CLAUDE_CONFIG_DIR || join(homedir(), ".claude"), "settings.json");
|
|
18
|
+
}
|
|
19
|
+
function legacyCodexHooksPath() {
|
|
20
|
+
return join(process.env.CODEX_HOME || join(homedir(), ".codex"), "hooks.json");
|
|
21
|
+
}
|
|
24
22
|
/**
|
|
25
23
|
* The command the hook runs. Bound to whichever CLI installs it: the dev CLI
|
|
26
24
|
* sets ETHOS_CLI_COMMAND (so `make dev` points the hook at localhost), while a
|
|
@@ -29,27 +27,30 @@ const HOOK_COMMAND_MARKER = "hooks eager-open";
|
|
|
29
27
|
export function eagerOpenCommand() {
|
|
30
28
|
return `${process.env.ETHOS_CLI_COMMAND || "ethos"} hooks eager-open`;
|
|
31
29
|
}
|
|
32
|
-
export function detectHookAgents() {
|
|
33
|
-
return ALL_HOOK_AGENTS.filter((name) => HOOK_AGENTS[name].detect());
|
|
34
|
-
}
|
|
35
30
|
export async function installEagerOpenHook(options = {}) {
|
|
36
31
|
const command = options.command ?? eagerOpenCommand();
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
32
|
+
const claude = await mergeHook(claudeSettingsPath(), command);
|
|
33
|
+
// Best-effort: remove any legacy Codex hook so it can't fire a duplicate
|
|
34
|
+
// handoff there. Never let a malformed Codex file fail the Claude install.
|
|
35
|
+
let codexPruned;
|
|
36
|
+
try {
|
|
37
|
+
codexPruned = await removeHook(legacyCodexHooksPath());
|
|
42
38
|
}
|
|
43
|
-
|
|
39
|
+
catch {
|
|
40
|
+
codexPruned = { path: legacyCodexHooksPath(), action: "absent" };
|
|
41
|
+
}
|
|
42
|
+
return { command, claude, codexPruned };
|
|
44
43
|
}
|
|
45
|
-
export async function uninstallEagerOpenHook(
|
|
46
|
-
const
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
44
|
+
export async function uninstallEagerOpenHook() {
|
|
45
|
+
const claude = await removeHook(claudeSettingsPath());
|
|
46
|
+
let codex;
|
|
47
|
+
try {
|
|
48
|
+
codex = await removeHook(legacyCodexHooksPath());
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
codex = { path: legacyCodexHooksPath(), action: "absent" };
|
|
51
52
|
}
|
|
52
|
-
return {
|
|
53
|
+
return { claude, codex };
|
|
53
54
|
}
|
|
54
55
|
async function mergeHook(path, command) {
|
|
55
56
|
const config = await readJsonObject(path);
|
|
@@ -72,11 +73,11 @@ async function mergeHook(path, command) {
|
|
|
72
73
|
}
|
|
73
74
|
async function removeHook(path) {
|
|
74
75
|
if (!existsSync(path))
|
|
75
|
-
return { path,
|
|
76
|
+
return { path, action: "absent" };
|
|
76
77
|
const config = await readJsonObject(path);
|
|
77
78
|
const list = config.hooks?.[HOOK_EVENT];
|
|
78
79
|
if (!config.hooks || !list)
|
|
79
|
-
return { path,
|
|
80
|
+
return { path, action: "absent" };
|
|
80
81
|
let removed = 0;
|
|
81
82
|
const kept = [];
|
|
82
83
|
for (const group of list) {
|
|
@@ -91,7 +92,7 @@ async function removeHook(path) {
|
|
|
91
92
|
else
|
|
92
93
|
delete config.hooks[HOOK_EVENT];
|
|
93
94
|
await writeJsonFile(path, config);
|
|
94
|
-
return { path, removed };
|
|
95
|
+
return { path, action: removed ? "removed" : "absent" };
|
|
95
96
|
}
|
|
96
97
|
async function readJsonObject(path) {
|
|
97
98
|
let raw;
|
|
@@ -133,16 +134,4 @@ async function writeJsonFile(path, value) {
|
|
|
133
134
|
throw error;
|
|
134
135
|
}
|
|
135
136
|
}
|
|
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
137
|
//# sourceMappingURL=hooks-installer.js.map
|
|
@@ -1 +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;
|
|
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,kFAAkF;AAClF,+EAA+E;AAC/E,+EAA+E;AAC/E,mFAAmF;AACnF,MAAM,UAAU,GAAG,kBAAkB,CAAC;AACtC,8EAA8E;AAC9E,2DAA2D;AAC3D,MAAM,mBAAmB,GAAG,kBAAkB,CAAC;AAQ/C,MAAM,UAAU,kBAAkB;IAChC,OAAO,UAAU,CAAC,OAAO,CAAC,GAAG,CAAC,iBAAiB,IAAI,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,CAAC,CAAC,CAAC;AACjF,CAAC;AAED,MAAM,UAAU,kBAAkB;IAChC,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,iBAAiB,IAAI,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,CAAC,EAAE,eAAe,CAAC,CAAC;AAC5F,CAAC;AAED,SAAS,oBAAoB;IAC3B,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,UAAU,IAAI,IAAI,CAAC,OAAO,EAAE,EAAE,QAAQ,CAAC,EAAE,YAAY,CAAC,CAAC;AACjF,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,gBAAgB;IAC9B,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,iBAAiB,IAAI,OAAO,mBAAmB,CAAC;AACxE,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,UAAgC,EAAE;IAElC,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,IAAI,gBAAgB,EAAE,CAAC;IACtD,MAAM,MAAM,GAAG,MAAM,SAAS,CAAC,kBAAkB,EAAE,EAAE,OAAO,CAAC,CAAC;IAC9D,yEAAyE;IACzE,2EAA2E;IAC3E,IAAI,WAA2B,CAAC;IAChC,IAAI,CAAC;QACH,WAAW,GAAG,MAAM,UAAU,CAAC,oBAAoB,EAAE,CAAC,CAAC;IACzD,CAAC;IAAC,MAAM,CAAC;QACP,WAAW,GAAG,EAAE,IAAI,EAAE,oBAAoB,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC;IACnE,CAAC;IACD,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC;AAC1C,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,sBAAsB;IAC1C,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,kBAAkB,EAAE,CAAC,CAAC;IACtD,IAAI,KAAqB,CAAC;IAC1B,IAAI,CAAC;QACH,KAAK,GAAG,MAAM,UAAU,CAAC,oBAAoB,EAAE,CAAC,CAAC;IACnD,CAAC;IAAC,MAAM,CAAC;QACP,KAAK,GAAG,EAAE,IAAI,EAAE,oBAAoB,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC;IAC7D,CAAC;IACD,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC;AAC3B,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,MAAM,EAAE,QAAQ,EAAE,CAAC;IACzD,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,MAAM,EAAE,QAAQ,EAAE,CAAC;IAE9D,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,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC;AAC1D,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"}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import {
|
|
2
|
-
// Runs on `npm install`. Re-binds the find-people eager-open hook to
|
|
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
|
-
//
|
|
1
|
+
import { claudeCodeDetected, installEagerOpenHook } from "../lib/hooks-installer.js";
|
|
2
|
+
// Runs on `npm install`. Re-binds the Claude Code find-people eager-open hook to
|
|
3
|
+
// the CLI 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` run
|
|
5
|
+
// rebinds it to the dev CLI for local testing). Also prunes any legacy Codex hook.
|
|
6
6
|
async function main() {
|
|
7
7
|
if (process.env.ETHOS_SKIP_HOOKS || process.env.ETHOS_SKIP_SKILLS) {
|
|
8
8
|
process.stderr.write("ethos: skipping eager-open hook install.\n");
|
|
@@ -13,12 +13,13 @@ async function main() {
|
|
|
13
13
|
process.stderr.write("ethos: detected sudo install - skipping hook install. Run `ethos hooks install` as your user.\n");
|
|
14
14
|
return;
|
|
15
15
|
}
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
16
|
+
if (!claudeCodeDetected())
|
|
17
|
+
return; // the hook is Claude Code only
|
|
18
|
+
const { command, claude, codexPruned } = await installEagerOpenHook();
|
|
19
|
+
let line = `ethos: eager-open hook ${claude.action} for Claude Code (command: ${command})`;
|
|
20
|
+
if (codexPruned.action === "removed")
|
|
21
|
+
line += "; removed legacy Codex hook";
|
|
22
|
+
process.stderr.write(line + "\n");
|
|
22
23
|
}
|
|
23
24
|
main().catch((err) => {
|
|
24
25
|
process.stderr.write(`ethos: hook install failed (continuing) - ${err instanceof Error ? err.message : String(err)}\nRun \`ethos hooks install\` manually after install completes.\n`);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"install-hooks.js","sourceRoot":"","sources":["../../src/scripts/install-hooks.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"install-hooks.js","sourceRoot":"","sources":["../../src/scripts/install-hooks.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AAErF,iFAAiF;AACjF,iFAAiF;AACjF,iFAAiF;AACjF,mFAAmF;AACnF,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,IAAI,CAAC,kBAAkB,EAAE;QAAE,OAAO,CAAC,+BAA+B;IAElE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,GAAG,MAAM,oBAAoB,EAAE,CAAC;IACtE,IAAI,IAAI,GAAG,0BAA0B,MAAM,CAAC,MAAM,8BAA8B,OAAO,GAAG,CAAC;IAC3F,IAAI,WAAW,CAAC,MAAM,KAAK,SAAS;QAAE,IAAI,IAAI,6BAA6B,CAAC;IAC5E,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC;AACpC,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
|
@@ -11,34 +11,28 @@ pass `--api-url` for local development or an explicitly requested environment.
|
|
|
11
11
|
|
|
12
12
|
## Required CLI
|
|
13
13
|
|
|
14
|
-
The Ethos CLI (`ethos`) is required.
|
|
15
|
-
upload browser straight away:
|
|
16
|
-
|
|
17
|
-
```sh
|
|
18
|
-
ethos flows start find-people-at-companies --agent-client auto --open --json
|
|
19
|
-
```
|
|
20
|
-
|
|
21
|
-
If that command fails because `ethos` is not installed or not available on
|
|
14
|
+
The Ethos CLI (`ethos`) is required. If `ethos` is not installed or not on
|
|
22
15
|
`PATH`, install the latest CLI and retry:
|
|
23
16
|
|
|
24
17
|
```sh
|
|
25
18
|
npm install -g ethos-cli@latest --prefer-online
|
|
26
19
|
```
|
|
27
20
|
|
|
28
|
-
If
|
|
29
|
-
`ethos auth login --agent-client auto`, then
|
|
30
|
-
|
|
31
|
-
|
|
21
|
+
If a command fails because the CLI is not authenticated, run
|
|
22
|
+
`ethos auth login --agent-client auto`, then retry. Do not run a separate
|
|
23
|
+
`ethos auth status` first; the flow commands already require auth and surface a
|
|
24
|
+
clear error when login is needed.
|
|
32
25
|
|
|
33
26
|
## Flow
|
|
34
27
|
|
|
35
28
|
Keep CSV upload in the browser-based Ethos product flow:
|
|
36
29
|
|
|
37
|
-
1.
|
|
38
|
-
2.
|
|
39
|
-
3.
|
|
40
|
-
4.
|
|
41
|
-
5.
|
|
30
|
+
1. Get the upload flow. On Claude Code a `UserPromptSubmit` hook may have already started one — if the conversation has an injected `handoff_token`, REUSE it and do NOT run `ethos flows start`. Otherwise run `ethos flows start find-people-at-companies --agent-client auto --json` and save `handoff_token` and `upload_url`. Add `--open` only on shell clients (Claude Code, Cursor) to open the OS browser; on Codex do NOT pass `--open`.
|
|
31
|
+
2. Open `upload_url` in the right surface. On Codex, open it in the in-app Browser — the default surface (OS-open, via `--open` or `open <url>`, is only a fallback if the in-app Browser is not working). On Claude Code and other shell clients, `--open` already opened the OS browser.
|
|
32
|
+
3. Immediately ask the user for ICP, buying signals, person targets, and max contacts while they upload.
|
|
33
|
+
4. Capture the uploaded table id with `ethos flows wait "$HANDOFF_TOKEN" --json`. Do not ask the user to paste the table URL unless `flows wait` cannot recover.
|
|
34
|
+
5. Once the brief is ready, inspect the table, create the `people_sourcing` agent, and create the agent-client-created column with the CLI.
|
|
35
|
+
6. Run the column for the FIRST 3 ROWS ONLY. Tell the user you're running for 3 rows to see if the results are good. If yes, the next step is to run on the whole table. If not, refine the agent based on their feedback so it finds the right people.
|
|
42
36
|
|
|
43
37
|
Do not run `ethos tables import-csv` for this skill unless the user explicitly
|
|
44
38
|
asks for a CLI-only/non-browser fallback. The browser upload is part of the
|
|
@@ -60,19 +54,25 @@ setup.
|
|
|
60
54
|
|
|
61
55
|
## Start Upload Flow
|
|
62
56
|
|
|
63
|
-
**Reuse a pre-started flow first
|
|
64
|
-
|
|
65
|
-
upload browser is already opening
|
|
66
|
-
`ethos flows start` again
|
|
67
|
-
`ethos flows wait <handoff_token>`.
|
|
68
|
-
|
|
57
|
+
**Reuse a pre-started flow first (Claude Code).** On Claude Code a
|
|
58
|
+
`UserPromptSubmit` hook may have already started the flow and injected a
|
|
59
|
+
`handoff_token` ("upload browser is already opening"). If so, do NOT run
|
|
60
|
+
`ethos flows start` again — reuse that `handoff_token`: skip to the brief and
|
|
61
|
+
then `ethos flows wait <handoff_token>`. The hook does not run on Codex, so there
|
|
62
|
+
you always run the command below.
|
|
69
63
|
|
|
70
|
-
|
|
64
|
+
Otherwise start the handoff through the CLI abstraction instead of constructing
|
|
65
|
+
client-specific URLs:
|
|
71
66
|
|
|
72
67
|
```sh
|
|
73
|
-
ethos flows start find-people-at-companies --agent-client auto --
|
|
68
|
+
ethos flows start find-people-at-companies --agent-client auto --json
|
|
74
69
|
```
|
|
75
70
|
|
|
71
|
+
Add `--open` only on shell clients (Claude Code, Cursor) so the CLI opens the OS
|
|
72
|
+
browser. On Codex, do NOT pass `--open` — open `upload_url` in the in-app Browser
|
|
73
|
+
(see Open Browser Upload); use `--open` there only as a fallback if the in-app
|
|
74
|
+
Browser is not working.
|
|
75
|
+
|
|
76
76
|
Save these fields from `response`:
|
|
77
77
|
|
|
78
78
|
- `handoff_token` (needed for `ethos flows wait`)
|
|
@@ -83,8 +83,7 @@ Save these fields from `response`:
|
|
|
83
83
|
- `next_prompt`
|
|
84
84
|
|
|
85
85
|
The `upload_url` is a handoff URL that verifies the browser session and
|
|
86
|
-
workspace before redirecting to the CSV upload modal.
|
|
87
|
-
already launched it in the default browser.
|
|
86
|
+
workspace before redirecting to the CSV upload modal.
|
|
88
87
|
|
|
89
88
|
If the detected client is wrong, rerun the same command with an explicit client,
|
|
90
89
|
such as `--agent-client codex` or `--agent-client claude-desktop`.
|
|
@@ -94,19 +93,19 @@ session.
|
|
|
94
93
|
|
|
95
94
|
## Open Browser Upload
|
|
96
95
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
`Import CSV` or take other browser actions after navigating unless
|
|
104
|
-
asks for help.
|
|
105
|
-
-
|
|
106
|
-
|
|
107
|
-
yourself, and only fall back to MCP browser tools, Chrome
|
|
108
|
-
browser plugins if that also fails. If every option
|
|
109
|
-
the user to open it.
|
|
96
|
+
Open the upload page in the browser surface for this client. Use `upload_url`
|
|
97
|
+
from the reused hook handoff (Claude Code) or from the `flows start` response.
|
|
98
|
+
|
|
99
|
+
- **Codex**: open `upload_url` in the **in-app Browser** — the default and only
|
|
100
|
+
surface you open. Do NOT also OS-open it. Only if the user says the in-app
|
|
101
|
+
Browser is not working, fall back to OS-opening it (`open "$UPLOAD_URL"`). Do
|
|
102
|
+
not click `Import CSV` or take other browser actions after navigating unless
|
|
103
|
+
the user asks for help.
|
|
104
|
+
- **Claude Code, Cursor, or another shell-based client**: `--open` already
|
|
105
|
+
opened the OS browser. If it failed (for example, no desktop session), run
|
|
106
|
+
`open "$UPLOAD_URL"` yourself, and only fall back to MCP browser tools, Chrome
|
|
107
|
+
connectors, or in-app browser plugins if that also fails. If every option
|
|
108
|
+
fails, show the URL and ask the user to open it.
|
|
110
109
|
|
|
111
110
|
Right after the browser opens, immediately ask `response.next_prompt`. If the
|
|
112
111
|
flow response is not available for some reason, ask:
|