minionsai 0.1.13 → 0.1.14

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,21 +0,0 @@
1
- export interface BundledSkillMeta {
2
- id: string;
3
- name: string;
4
- description: string;
5
- key: string;
6
- source: 'Minions bundled';
7
- bundled: true;
8
- readOnly: true;
9
- autoIncluded: true;
10
- }
11
- export interface BundledSkill extends BundledSkillMeta {
12
- filePath: string;
13
- }
14
- export declare function resolveBundledSkillsDir(): string;
15
- export declare function listBundledSkills(): Promise<BundledSkill[]>;
16
- export declare function getBundledSkillWithContent(id: string): Promise<{
17
- skill: BundledSkill;
18
- content: string;
19
- } | null>;
20
- export declare function toSkillMeta(skill: BundledSkill): BundledSkillMeta;
21
- export declare function ensureBundledSkillsLinked(): void;
@@ -1,156 +0,0 @@
1
- import { existsSync, lstatSync, mkdirSync, readlinkSync, symlinkSync, unlinkSync } from 'node:fs';
2
- import { readdir, readFile } from 'node:fs/promises';
3
- import { dirname, join, relative, resolve } from 'node:path';
4
- import { fileURLToPath } from 'node:url';
5
- import { resolveHermesHome } from '../paths.js';
6
- const VALID_SKILL_ID = /^[a-z0-9][a-z0-9._-]*$/;
7
- function serverDir() {
8
- return dirname(fileURLToPath(import.meta.url));
9
- }
10
- export function resolveBundledSkillsDir() {
11
- const here = serverDir();
12
- const candidates = [
13
- resolve(process.cwd(), 'skills'),
14
- resolve(here, '../../skills'),
15
- resolve(here, '../../../skills'),
16
- resolve(here, '../../../../skills'),
17
- ];
18
- return candidates.find((candidate) => existsSync(candidate)) ?? candidates[0];
19
- }
20
- function stripQuotes(value) {
21
- const trimmed = value.trim();
22
- if ((trimmed.startsWith('"') && trimmed.endsWith('"'))
23
- || (trimmed.startsWith("'") && trimmed.endsWith("'"))) {
24
- return trimmed.slice(1, -1);
25
- }
26
- return trimmed;
27
- }
28
- function parseFrontmatter(content) {
29
- if (!content.startsWith('---\n'))
30
- return {};
31
- const end = content.indexOf('\n---', 4);
32
- if (end === -1)
33
- return {};
34
- const fields = {};
35
- const raw = content.slice(4, end);
36
- for (const line of raw.split('\n')) {
37
- const match = /^([A-Za-z0-9_-]+):\s*(.*)$/.exec(line);
38
- if (!match)
39
- continue;
40
- fields[match[1]] = stripQuotes(match[2]);
41
- }
42
- return fields;
43
- }
44
- function bodyWithoutFrontmatter(content) {
45
- if (!content.startsWith('---\n'))
46
- return content;
47
- const end = content.indexOf('\n---', 4);
48
- return end === -1 ? content : content.slice(end + 4);
49
- }
50
- function firstBodyLine(body) {
51
- for (const line of body.split('\n')) {
52
- const trimmed = line.trim();
53
- if (trimmed && !trimmed.startsWith('#'))
54
- return trimmed;
55
- }
56
- return '';
57
- }
58
- function firstHeading(body) {
59
- for (const line of body.split('\n')) {
60
- const match = /^#\s+(.+)$/.exec(line.trim());
61
- if (match)
62
- return match[1].trim();
63
- }
64
- return null;
65
- }
66
- function humanizeSkillId(id) {
67
- return id
68
- .split(/[-_]+/)
69
- .filter(Boolean)
70
- .map((part) => part.charAt(0).toUpperCase() + part.slice(1))
71
- .join(' ');
72
- }
73
- function parseSkillContent(id, filePath, content) {
74
- const frontmatter = parseFrontmatter(content);
75
- const body = bodyWithoutFrontmatter(content);
76
- const name = firstHeading(body) || humanizeSkillId(frontmatter.name || id);
77
- const description = frontmatter.description || firstBodyLine(body);
78
- return {
79
- id,
80
- name,
81
- description,
82
- key: `minions/${id}`,
83
- source: 'Minions bundled',
84
- bundled: true,
85
- readOnly: true,
86
- autoIncluded: true,
87
- filePath,
88
- };
89
- }
90
- export async function listBundledSkills() {
91
- const root = resolveBundledSkillsDir();
92
- let entries;
93
- try {
94
- entries = await readdir(root, { withFileTypes: true });
95
- }
96
- catch {
97
- return [];
98
- }
99
- const skills = await Promise.all(entries
100
- .filter((entry) => entry.isDirectory() && VALID_SKILL_ID.test(entry.name))
101
- .map(async (entry) => {
102
- const id = entry.name;
103
- const filePath = join(root, id, 'SKILL.md');
104
- try {
105
- const content = await readFile(filePath, 'utf-8');
106
- return parseSkillContent(id, filePath, content);
107
- }
108
- catch {
109
- return null;
110
- }
111
- }));
112
- return skills
113
- .filter((skill) => skill !== null)
114
- .sort((a, b) => a.name.localeCompare(b.name));
115
- }
116
- export async function getBundledSkillWithContent(id) {
117
- if (!VALID_SKILL_ID.test(id))
118
- return null;
119
- const root = resolveBundledSkillsDir();
120
- const filePath = join(root, id, 'SKILL.md');
121
- try {
122
- const content = await readFile(filePath, 'utf-8');
123
- return { skill: parseSkillContent(id, filePath, content), content };
124
- }
125
- catch {
126
- return null;
127
- }
128
- }
129
- export function toSkillMeta(skill) {
130
- const { filePath: _filePath, ...meta } = skill;
131
- return meta;
132
- }
133
- export function ensureBundledSkillsLinked() {
134
- const source = resolveBundledSkillsDir();
135
- if (!existsSync(source))
136
- return;
137
- const target = join(resolveHermesHome(), 'skills', 'minions');
138
- mkdirSync(dirname(target), { recursive: true });
139
- try {
140
- const stat = lstatSync(target);
141
- if (!stat.isSymbolicLink()) {
142
- console.warn(`Bundled skills not linked: ${target} already exists and is not a symlink.`);
143
- return;
144
- }
145
- const current = readlinkSync(target);
146
- const resolvedCurrent = resolve(dirname(target), current);
147
- if (relative(source, resolvedCurrent) === '')
148
- return;
149
- unlinkSync(target);
150
- }
151
- catch (error) {
152
- if (error.code !== 'ENOENT')
153
- throw error;
154
- }
155
- symlinkSync(source, target, 'dir');
156
- }
@@ -1,41 +0,0 @@
1
- ---
2
- name: lead-generation
3
- description: This skill should be used for lead generation or prospecting tasks. It guides the agent through a lightweight workflow: understand the target, collect sample leads, validate columns and quality, then continue collection into a local CSV file.
4
- ---
5
-
6
- # Lead Generation
7
-
8
- Guide users from a vague lead generation goal to a validated CSV-producing workflow.
9
-
10
- ## Workflow
11
-
12
- ### Phase 1: Understand the Target
13
-
14
- If the user hasn't already given enough context to start, you can ask 1 or 2 follow up questions max:
15
-
16
- - **Who** — ideal customer profile (industry, company size, role/title, geography)
17
- - **Why** — what the leads are for (outbound sales, partnerships, recruiting, research)
18
-
19
- You can follow up later if needed, after you do a sample run.
20
-
21
- ### Phase 2: Test Leads
22
-
23
- Collect 2-3 leads and present them in a table. Choose columns that make sense for the user's specific task — there is no fixed schema. The columns should emerge naturally from the targeting criteria and what information is discoverable.
24
-
25
- Present the test leads and let the user react. They may want to adjust columns, targeting, or format — follow their lead. Do not scale collection until the user signals they're happy with the results.
26
-
27
- ### Phase 3: Continue Collection
28
-
29
- Once the user signs off, continue collection. Focus on:
30
-
31
- - **Batch size** — default to 3-5 leads per run unless the user specifies otherwise.
32
- - **CSV location** — store in the working directory. Suggest a descriptive filename (e.g., `leads-saas-founders.csv`). Create the file with headers and the test leads already included.
33
- - **Instructions** — any continued collection should be self-contained: describe the ICP, the CSV path, the columns, and the number of leads to collect per run.
34
-
35
- ### Guidance
36
-
37
- - Always start with test leads. Never scale collection before validating the target and columns.
38
- - Wait for the user to signal they're happy before continuing collection — don't ask for permission, just don't proceed until they indicate satisfaction.
39
- - If the user has a specific source in mind, incorporate it. Otherwise, choose appropriate sources.
40
- - If the user already has a CSV or lead list, continue appending to it rather than starting fresh.
41
- - Bias toward doing and showing over asking. Present what was done and let the user course-correct.