ethos-cli 0.1.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.
Files changed (55) hide show
  1. package/README.md +121 -0
  2. package/dist/bin/ethos.d.ts +4 -0
  3. package/dist/bin/ethos.js +45 -0
  4. package/dist/bin/ethos.js.map +1 -0
  5. package/dist/commands/agents.d.ts +2 -0
  6. package/dist/commands/agents.js +162 -0
  7. package/dist/commands/agents.js.map +1 -0
  8. package/dist/commands/auth.d.ts +2 -0
  9. package/dist/commands/auth.js +180 -0
  10. package/dist/commands/auth.js.map +1 -0
  11. package/dist/commands/skills.d.ts +2 -0
  12. package/dist/commands/skills.js +90 -0
  13. package/dist/commands/skills.js.map +1 -0
  14. package/dist/commands/tables.d.ts +17 -0
  15. package/dist/commands/tables.js +534 -0
  16. package/dist/commands/tables.js.map +1 -0
  17. package/dist/lib/auth-store.d.ts +13 -0
  18. package/dist/lib/auth-store.js +47 -0
  19. package/dist/lib/auth-store.js.map +1 -0
  20. package/dist/lib/client.d.ts +19 -0
  21. package/dist/lib/client.js +69 -0
  22. package/dist/lib/client.js.map +1 -0
  23. package/dist/lib/command-helpers.d.ts +8 -0
  24. package/dist/lib/command-helpers.js +24 -0
  25. package/dist/lib/command-helpers.js.map +1 -0
  26. package/dist/lib/config.d.ts +12 -0
  27. package/dist/lib/config.js +38 -0
  28. package/dist/lib/config.js.map +1 -0
  29. package/dist/lib/output.d.ts +7 -0
  30. package/dist/lib/output.js +23 -0
  31. package/dist/lib/output.js.map +1 -0
  32. package/dist/lib/table-types.d.ts +138 -0
  33. package/dist/lib/table-types.js +2 -0
  34. package/dist/lib/table-types.js.map +1 -0
  35. package/dist/scripts/install-skills.d.ts +1 -0
  36. package/dist/scripts/install-skills.js +44 -0
  37. package/dist/scripts/install-skills.js.map +1 -0
  38. package/dist/skills-installer/agents.d.ts +9 -0
  39. package/dist/skills-installer/agents.js +31 -0
  40. package/dist/skills-installer/agents.js.map +1 -0
  41. package/dist/skills-installer/index.d.ts +4 -0
  42. package/dist/skills-installer/index.js +5 -0
  43. package/dist/skills-installer/index.js.map +1 -0
  44. package/dist/skills-installer/install.d.ts +15 -0
  45. package/dist/skills-installer/install.js +82 -0
  46. package/dist/skills-installer/install.js.map +1 -0
  47. package/dist/skills-installer/lockfile.d.ts +17 -0
  48. package/dist/skills-installer/lockfile.js +23 -0
  49. package/dist/skills-installer/lockfile.js.map +1 -0
  50. package/dist/skills-installer/skill.d.ts +9 -0
  51. package/dist/skills-installer/skill.js +70 -0
  52. package/dist/skills-installer/skill.js.map +1 -0
  53. package/package.json +48 -0
  54. package/postinstall.cjs +23 -0
  55. package/skills/find-people-at-companies.md +111 -0
@@ -0,0 +1,23 @@
1
+ import { mkdir, readFile, writeFile } from "node:fs/promises";
2
+ import { homedir } from "node:os";
3
+ import { dirname, join } from "node:path";
4
+ export function lockPath() {
5
+ return join(homedir(), ".ethos", "skills.lock.json");
6
+ }
7
+ export async function readLockfile() {
8
+ try {
9
+ const parsed = JSON.parse(await readFile(lockPath(), "utf-8"));
10
+ if (parsed?.schemaVersion === 1)
11
+ return parsed;
12
+ }
13
+ catch {
14
+ // Missing or malformed lockfiles are treated as empty.
15
+ }
16
+ return { schemaVersion: 1, ethosVersion: "", skills: {} };
17
+ }
18
+ export async function writeLockfile(lock) {
19
+ const target = lockPath();
20
+ await mkdir(dirname(target), { recursive: true });
21
+ await writeFile(target, JSON.stringify(lock, null, 2) + "\n");
22
+ }
23
+ //# sourceMappingURL=lockfile.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lockfile.js","sourceRoot":"","sources":["../../src/skills-installer/lockfile.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC9D,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAgB1C,MAAM,UAAU,QAAQ;IACtB,OAAO,IAAI,CAAC,OAAO,EAAE,EAAE,QAAQ,EAAE,kBAAkB,CAAC,CAAC;AACvD,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,YAAY;IAChC,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,QAAQ,CAAC,QAAQ,EAAE,EAAE,OAAO,CAAC,CAAC,CAAC;QAC/D,IAAI,MAAM,EAAE,aAAa,KAAK,CAAC;YAAE,OAAO,MAAkB,CAAC;IAC7D,CAAC;IAAC,MAAM,CAAC;QACP,uDAAuD;IACzD,CAAC;IACD,OAAO,EAAE,aAAa,EAAE,CAAC,EAAE,YAAY,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;AAC5D,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,IAAc;IAChD,MAAM,MAAM,GAAG,QAAQ,EAAE,CAAC;IAC1B,MAAM,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAClD,MAAM,SAAS,CAAC,MAAM,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;AAChE,CAAC"}
@@ -0,0 +1,9 @@
1
+ export interface Skill {
2
+ name: string;
3
+ description: string;
4
+ sourcePath: string;
5
+ hash: string;
6
+ }
7
+ export declare function sanitizeName(name: string): string;
8
+ export declare function isPathSafe(base: string, target: string): boolean;
9
+ export declare function discoverBundledSkills(rootDir: string): Promise<Skill[]>;
@@ -0,0 +1,70 @@
1
+ import { createHash } from "node:crypto";
2
+ import { readFile, readdir } from "node:fs/promises";
3
+ import { join, normalize, resolve, sep } from "node:path";
4
+ export function sanitizeName(name) {
5
+ return (name
6
+ .toLowerCase()
7
+ .replace(/[^a-z0-9._]+/g, "-")
8
+ .replace(/^[.\-]+|[.\-]+$/g, "")
9
+ .substring(0, 255) || "unnamed-skill");
10
+ }
11
+ export function isPathSafe(base, target) {
12
+ const normalizedBase = normalize(resolve(base));
13
+ const normalizedTarget = normalize(resolve(target));
14
+ return (normalizedTarget === normalizedBase ||
15
+ normalizedTarget.startsWith(normalizedBase + sep));
16
+ }
17
+ function parseFrontmatter(content) {
18
+ const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---/);
19
+ if (!match)
20
+ return null;
21
+ const result = {};
22
+ for (const line of match[1].split(/\r?\n/)) {
23
+ const kv = line.match(/^([a-zA-Z_][a-zA-Z0-9_-]*):\s*(.*)$/);
24
+ if (!kv)
25
+ continue;
26
+ let value = kv[2].trim();
27
+ if ((value.startsWith('"') && value.endsWith('"')) ||
28
+ (value.startsWith("'") && value.endsWith("'"))) {
29
+ value = value.slice(1, -1);
30
+ }
31
+ result[kv[1]] = value;
32
+ }
33
+ return result;
34
+ }
35
+ async function readSkillFile(filePath, fallbackName) {
36
+ let content;
37
+ try {
38
+ content = await readFile(filePath, "utf-8");
39
+ }
40
+ catch {
41
+ return null;
42
+ }
43
+ const frontmatter = parseFrontmatter(content);
44
+ if (!frontmatter?.description)
45
+ return null;
46
+ return {
47
+ name: sanitizeName(frontmatter.name ?? fallbackName),
48
+ description: frontmatter.description,
49
+ sourcePath: filePath,
50
+ hash: createHash("sha256").update(content).digest("hex"),
51
+ };
52
+ }
53
+ export async function discoverBundledSkills(rootDir) {
54
+ const entries = await readdir(rootDir, { withFileTypes: true }).catch(() => []);
55
+ const skills = [];
56
+ for (const entry of entries) {
57
+ if (entry.isFile() && entry.name.endsWith(".md") && entry.name !== "README.md") {
58
+ const skill = await readSkillFile(join(rootDir, entry.name), entry.name.slice(0, -3));
59
+ if (skill)
60
+ skills.push(skill);
61
+ }
62
+ else if (entry.isDirectory()) {
63
+ const skill = await readSkillFile(join(rootDir, entry.name, "SKILL.md"), entry.name);
64
+ if (skill)
65
+ skills.push(skill);
66
+ }
67
+ }
68
+ return skills;
69
+ }
70
+ //# sourceMappingURL=skill.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skill.js","sourceRoot":"","sources":["../../src/skills-installer/skill.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,48 @@
1
+ {
2
+ "name": "ethos-cli",
3
+ "version": "0.1.0",
4
+ "description": "Commander-based CLI for Ethos table operations",
5
+ "homepage": "https://github.com/cluster-software/ethos#readme",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/cluster-software/ethos.git",
9
+ "directory": "ethos-cli"
10
+ },
11
+ "bugs": {
12
+ "url": "https://github.com/cluster-software/ethos/issues"
13
+ },
14
+ "type": "module",
15
+ "bin": {
16
+ "ethos": "dist/bin/ethos.js"
17
+ },
18
+ "files": [
19
+ "dist",
20
+ "skills",
21
+ "postinstall.cjs",
22
+ "README.md"
23
+ ],
24
+ "scripts": {
25
+ "build": "node -e \"require('node:fs').rmSync('dist',{recursive:true,force:true})\" && tsc",
26
+ "dev": "tsx src/bin/ethos.ts",
27
+ "postinstall": "node ./postinstall.cjs",
28
+ "test": "vitest run",
29
+ "test:watch": "vitest",
30
+ "prepublishOnly": "npm run build",
31
+ "changeset": "changeset",
32
+ "version": "changeset version",
33
+ "release": "changeset publish"
34
+ },
35
+ "dependencies": {
36
+ "commander": "^12.1.0"
37
+ },
38
+ "devDependencies": {
39
+ "@changesets/cli": "^2.31.0",
40
+ "@types/node": "^22.10.0",
41
+ "tsx": "^4.19.2",
42
+ "typescript": "^5.7.0",
43
+ "vitest": "^4.1.6"
44
+ },
45
+ "engines": {
46
+ "node": ">=20"
47
+ }
48
+ }
@@ -0,0 +1,23 @@
1
+ // npm postinstall bootstrap.
2
+ //
3
+ // The published package contains dist/, but dev checkouts may run npm install
4
+ // before a build. In that case this no-ops. Installer failures are logged and
5
+ // swallowed so npm install itself never fails.
6
+ "use strict";
7
+ const fs = require("node:fs");
8
+ const path = require("node:path");
9
+
10
+ const target = path.join(__dirname, "dist", "scripts", "install-skills.js");
11
+
12
+ if (!fs.existsSync(target)) {
13
+ process.exit(0);
14
+ }
15
+
16
+ import(target).catch((err) => {
17
+ process.stderr.write(
18
+ "ethos: skills install crashed (continuing) - " +
19
+ (err && err.message ? err.message : String(err)) +
20
+ "\n",
21
+ );
22
+ process.exit(0);
23
+ });
@@ -0,0 +1,111 @@
1
+ ---
2
+ name: find-people-at-companies
3
+ description: Import a company CSV into Ethos, collect ICP and buying-signal criteria, create a people_sourcing agent, run it on the first rows, and open the table. Use when the user asks to find ICP-matching people at companies from a CSV or invokes /find-people-at-companies.
4
+ ---
5
+
6
+ # find-people-at-companies
7
+
8
+ Use this when the user wants Ethos to source people at a list of companies.
9
+ The production CLI default is `https://api.ethos.hello-cluster.com`, so only
10
+ pass `--api-url` for local development or an explicitly requested environment.
11
+
12
+ ## Inputs
13
+
14
+ If the user has not already provided these, ask only for what is missing:
15
+
16
+ - Company CSV path.
17
+ - ICP fit criteria for companies.
18
+ - Buying-signal criteria to prioritize.
19
+ - Person titles/functions to return.
20
+ - Desired max people per company. Default to 3 when unspecified.
21
+
22
+ ## Authenticate
23
+
24
+ Check auth first:
25
+
26
+ ```sh
27
+ ethos auth status --json
28
+ ```
29
+
30
+ If unauthenticated, run:
31
+
32
+ ```sh
33
+ ethos auth login
34
+ ```
35
+
36
+ The browser claim flow handles production auth. Do not ask for API tokens unless
37
+ the user explicitly wants a non-browser setup.
38
+
39
+ ## Import The CSV
40
+
41
+ ```sh
42
+ ethos tables import-csv "$CSV_PATH" --entity-type company --json
43
+ ```
44
+
45
+ Save `response.table_id`, `response.view_ids[0]`, and the relevant company
46
+ input column ids from `response.columns`.
47
+
48
+ ## Create The Agent
49
+
50
+ Create a `people_sourcing` agent. Use these abilities by default:
51
+
52
+ ```sh
53
+ ethos agents create \
54
+ --name "People sourcing - <brief name>" \
55
+ --workflow-type people_sourcing \
56
+ --ability web-search \
57
+ --ability web-fetch \
58
+ --ability linkedin-company-lookup \
59
+ --ability linkedin-profile-lookup \
60
+ --instructions "$AGENT_INSTRUCTIONS" \
61
+ --json
62
+ ```
63
+
64
+ Only add `--ability browser-use --tool-preset browser` when the requested
65
+ signals require genuine interactivity, such as submitting forms, navigating a
66
+ dynamic gated site, or retrieving information that normal search/fetch tools
67
+ cannot access. Do not add browser use just for search results, static websites,
68
+ LinkedIn lookup, press releases, jobs pages, or company pages.
69
+
70
+ The instructions should include:
71
+
72
+ - The user's ICP and buying-signal criteria.
73
+ - The exact types of people to return.
74
+ - Any exclusions the user stated.
75
+ - Return 0 people with `not_icp_reason` when the company is not ICP.
76
+ - Prefer founders, CEOs, GTM leaders, sales leaders, partnerships, revenue
77
+ operations, or founding GTM when relevant.
78
+ - Avoid technical-only founders or CTOs unless the user explicitly asks for
79
+ technical buyers.
80
+ - Avoid marketing-only people who do not appear to touch sales when the user is
81
+ looking for GTM or sales ownership.
82
+ - Cite public source URLs for every person and fit claim.
83
+
84
+ ## Source People
85
+
86
+ Run against the imported table view:
87
+
88
+ ```sh
89
+ ethos tables source-people "$TABLE_ID" \
90
+ --view-id "$VIEW_ID" \
91
+ --agent-id "$AGENT_ID" \
92
+ --input-column "$COMPANY_INPUT_COLUMN_ID" \
93
+ --targeting-brief "$TARGETING_BRIEF" \
94
+ --max-contacts-per-company "$MAX_PEOPLE_PER_COMPANY" \
95
+ --created-by-codex \
96
+ --json
97
+ ```
98
+
99
+ Use multiple `--input-column` flags when the table has separate useful company
100
+ fields such as name, domain, LinkedIn URL, and description.
101
+
102
+ ## Open And Report
103
+
104
+ Open the table in the production app:
105
+
106
+ ```sh
107
+ open "https://ethos.hello-cluster.com/tables/$TABLE_ID"
108
+ ```
109
+
110
+ Then report the table URL, run id, and the agent id. Keep the response concise
111
+ and mention that the run is visible in Ethos.