maestro-skills 0.1.1
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/.github/workflows/ci.yml +26 -0
- package/.github/workflows/publish-npm.yml +30 -0
- package/CONTRIBUTING.md +31 -0
- package/LICENSE +21 -0
- package/README.md +300 -0
- package/SECURITY.md +33 -0
- package/docs/github-workflow.md +96 -0
- package/docs/maestro-skills-cli.md +113 -0
- package/package.json +35 -0
- package/packages/maestro-skills/README.md +37 -0
- package/packages/maestro-skills/agents.json +36 -0
- package/packages/maestro-skills/bin/cli.js +37 -0
- package/packages/maestro-skills/lib/detect-agents.js +28 -0
- package/packages/maestro-skills/lib/install.js +58 -0
- package/packages/maestro-skills/lib/paths.js +42 -0
- package/packages/maestro-skills/lib/remove.js +71 -0
- package/packages/maestro-skills/lib/run-manifest.js +92 -0
- package/packages/maestro-skills/lib/setup.js +115 -0
- package/packages/maestro-skills/package.json +47 -0
- package/packages/maestro-skills/test/agents.test.js +17 -0
- package/packages/rodovalhofs-maestro/agents.json +36 -0
- package/packages/rodovalhofs-maestro/bin/cli.js +10 -0
- package/packages/rodovalhofs-maestro/lib/detect-agents.js +28 -0
- package/packages/rodovalhofs-maestro/lib/install.js +58 -0
- package/packages/rodovalhofs-maestro/lib/paths.js +42 -0
- package/packages/rodovalhofs-maestro/lib/remove.js +71 -0
- package/packages/rodovalhofs-maestro/lib/run-manifest.js +92 -0
- package/packages/rodovalhofs-maestro/lib/setup.js +115 -0
- package/packages/rodovalhofs-maestro/package.json +33 -0
- package/scripts/sync-skill-to-cli.mjs +75 -0
- package/scripts/sync-templates.ps1 +22 -0
- package/skills/maestro/SKILL.md +272 -0
- package/skills/maestro/maestro-exclude.example.txt +6 -0
- package/skills/maestro/scripts/bm25.py +70 -0
- package/skills/maestro/scripts/build_manifest.py +183 -0
- package/skills/maestro/scripts/concept_gaps.py +196 -0
- package/skills/maestro/scripts/domains.py +148 -0
- package/skills/maestro/scripts/intents.py +167 -0
- package/skills/maestro/scripts/maestro_paths.py +41 -0
- package/skills/maestro/scripts/route_tasks.py +101 -0
- package/skills/maestro/scripts/routing.py +106 -0
- package/skills/maestro/scripts/search_skills.py +287 -0
- package/skills/maestro/scripts/synonyms.py +47 -0
- package/templates/.github/ISSUE_TEMPLATE/bug_report.yml +34 -0
- package/templates/.github/ISSUE_TEMPLATE/chore.yml +17 -0
- package/templates/.github/ISSUE_TEMPLATE/config.yml +1 -0
- package/templates/.github/ISSUE_TEMPLATE/feature_request.yml +27 -0
- package/templates/.github/workflows/ci-failure-to-issue.yml +47 -0
- package/templates/.github/workflows/ci.yml +27 -0
- package/templates/CONTRIBUTING.md +22 -0
- package/templates/labels.json +12 -0
- package/templates/pull_request_template.md +18 -0
- package/tests/fixtures/sample-manifest.json +76 -0
- package/tests/test_concept_gaps.py +63 -0
- package/tests/test_maestro_paths.py +29 -0
- package/tests/test_search_routing.py +161 -0
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# maestro-skills
|
|
2
|
+
|
|
3
|
+
CLI de instalação interativa do Maestro — estilo `npx ctx7 setup`.
|
|
4
|
+
|
|
5
|
+
## Instalação
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npx maestro-skills setup
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Fallback (bin curto `maestro`):
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npx @rodovalhofs/maestro setup
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Agentes suportados (v1)
|
|
18
|
+
|
|
19
|
+
| Agente | Pasta global | Flag |
|
|
20
|
+
|--------|--------------|------|
|
|
21
|
+
| Cursor | `~/.cursor/skills` | `--cursor` |
|
|
22
|
+
| Claude Code | `~/.claude/skills` | `--claude` |
|
|
23
|
+
| Codex | `~/.codex/skills` | `--codex` |
|
|
24
|
+
| Universal | `~/.agents/skills` | `--universal` |
|
|
25
|
+
|
|
26
|
+
## Comandos
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
npx maestro-skills setup # interativo
|
|
30
|
+
npx maestro-skills setup --codex -y # só Codex, sem prompts
|
|
31
|
+
npx maestro-skills setup --project # só o projeto atual
|
|
32
|
+
npx maestro-skills remove # desinstalar
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Índice de skills: `~/.maestro/skills-manifest.json`
|
|
36
|
+
|
|
37
|
+
Documentação completa: [docs/maestro-skills-cli.md](../../docs/maestro-skills-cli.md)
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"agents": [
|
|
3
|
+
{
|
|
4
|
+
"id": "cursor",
|
|
5
|
+
"label": "Cursor",
|
|
6
|
+
"globalSkillsDir": ".cursor/skills",
|
|
7
|
+
"projectSkillsDir": ".cursor/skills",
|
|
8
|
+
"detectDirs": [".cursor"],
|
|
9
|
+
"skillsCliAgent": "cursor"
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
"id": "claude",
|
|
13
|
+
"label": "Claude Code",
|
|
14
|
+
"globalSkillsDir": ".claude/skills",
|
|
15
|
+
"projectSkillsDir": ".claude/skills",
|
|
16
|
+
"detectDirs": [".claude"],
|
|
17
|
+
"skillsCliAgent": "claude"
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
"id": "codex",
|
|
21
|
+
"label": "Codex",
|
|
22
|
+
"globalSkillsDir": ".codex/skills",
|
|
23
|
+
"projectSkillsDir": ".codex/skills",
|
|
24
|
+
"detectDirs": [".codex"],
|
|
25
|
+
"skillsCliAgent": "codex"
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
"id": "universal",
|
|
29
|
+
"label": "Universal (~/.agents/skills)",
|
|
30
|
+
"globalSkillsDir": ".agents/skills",
|
|
31
|
+
"projectSkillsDir": ".agents/skills",
|
|
32
|
+
"detectDirs": [".agents"],
|
|
33
|
+
"skillsCliAgent": null
|
|
34
|
+
}
|
|
35
|
+
]
|
|
36
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Command } from "commander";
|
|
3
|
+
import { runRemove } from "../lib/remove.js";
|
|
4
|
+
import { runSetup } from "../lib/setup.js";
|
|
5
|
+
|
|
6
|
+
const program = new Command();
|
|
7
|
+
|
|
8
|
+
program
|
|
9
|
+
.name("maestro-skills")
|
|
10
|
+
.description("Install and configure the Maestro skill orchestrator for multiple AI agents")
|
|
11
|
+
.version("0.1.1");
|
|
12
|
+
|
|
13
|
+
program
|
|
14
|
+
.command("setup")
|
|
15
|
+
.description("Interactive setup — choose agents, scope, and build manifest")
|
|
16
|
+
.option("--project", "Install only in the current project")
|
|
17
|
+
.option("--cursor", "Target Cursor only")
|
|
18
|
+
.option("--claude", "Target Claude Code only")
|
|
19
|
+
.option("--codex", "Target Codex only")
|
|
20
|
+
.option("--universal", "Target ~/.agents/skills only")
|
|
21
|
+
.option("-y, --yes", "Skip prompts (defaults: detected agents, no skills.sh)")
|
|
22
|
+
.option("--no-skills-registry", "Skip skills.sh registration prompt")
|
|
23
|
+
.action(async (opts) => {
|
|
24
|
+
await runSetup(opts);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
program
|
|
28
|
+
.command("remove")
|
|
29
|
+
.description("Remove Maestro skill from selected agents")
|
|
30
|
+
.option("-y, --yes", "Remove from all last-known destinations without prompts")
|
|
31
|
+
.option("--all", "Consider all known agent paths")
|
|
32
|
+
.option("--clean-home", "With --yes, also remove ~/.maestro/")
|
|
33
|
+
.action(async (opts) => {
|
|
34
|
+
await runRemove(opts);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
program.parseAsync(process.argv);
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { loadAgents } from "./paths.js";
|
|
5
|
+
|
|
6
|
+
export function detectAgents({ project = false, cwd = process.cwd() } = {}) {
|
|
7
|
+
const agents = loadAgents();
|
|
8
|
+
const base = project ? cwd : homedir();
|
|
9
|
+
|
|
10
|
+
return agents.map((agent) => {
|
|
11
|
+
const detected = agent.detectDirs.some((dir) => existsSync(join(base, dir)));
|
|
12
|
+
return {
|
|
13
|
+
...agent,
|
|
14
|
+
detected: detected || !project,
|
|
15
|
+
skillsPath: join(base, project ? agent.projectSkillsDir : agent.globalSkillsDir),
|
|
16
|
+
};
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function filterAgentsByFlags(agents, flags) {
|
|
21
|
+
const ids = [];
|
|
22
|
+
if (flags.cursor) ids.push("cursor");
|
|
23
|
+
if (flags.claude) ids.push("claude");
|
|
24
|
+
if (flags.codex) ids.push("codex");
|
|
25
|
+
if (flags.universal) ids.push("universal");
|
|
26
|
+
if (!ids.length) return agents;
|
|
27
|
+
return agents.filter((a) => ids.includes(a.id));
|
|
28
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { cpSync, existsSync, mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { dirname, join } from "node:path";
|
|
3
|
+
import { getMaestroPaths, skillSourceDir } from "./paths.js";
|
|
4
|
+
|
|
5
|
+
export function copySkillTo(destDir) {
|
|
6
|
+
const src = skillSourceDir();
|
|
7
|
+
const dest = join(destDir, "maestro");
|
|
8
|
+
rmSync(dest, { recursive: true, force: true });
|
|
9
|
+
mkdirSync(dirname(dest), { recursive: true });
|
|
10
|
+
cpSync(src, dest, { recursive: true });
|
|
11
|
+
const pycache = join(dest, "scripts", "__pycache__");
|
|
12
|
+
if (existsSync(pycache)) rmSync(pycache, { recursive: true, force: true });
|
|
13
|
+
return dest;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function migrateLegacyFiles() {
|
|
17
|
+
const paths = getMaestroPaths();
|
|
18
|
+
mkdirSync(paths.home, { recursive: true });
|
|
19
|
+
const migrated = [];
|
|
20
|
+
|
|
21
|
+
if (!existsSync(paths.manifest) && existsSync(paths.legacyManifest)) {
|
|
22
|
+
renameSync(paths.legacyManifest, paths.manifest);
|
|
23
|
+
migrated.push("skills-manifest.json");
|
|
24
|
+
}
|
|
25
|
+
if (!existsSync(paths.exclude) && existsSync(paths.legacyExclude)) {
|
|
26
|
+
renameSync(paths.legacyExclude, paths.exclude);
|
|
27
|
+
migrated.push("maestro-exclude.txt");
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const example = join(skillSourceDir(), "maestro-exclude.example.txt");
|
|
31
|
+
if (!existsSync(paths.exclude) && existsSync(example)) {
|
|
32
|
+
cpSync(example, paths.exclude);
|
|
33
|
+
migrated.push("maestro-exclude.txt (from example)");
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return migrated;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function saveSetupConfig(config) {
|
|
40
|
+
const { config: configPath } = getMaestroPaths();
|
|
41
|
+
mkdirSync(dirname(configPath), { recursive: true });
|
|
42
|
+
writeFileSync(configPath, JSON.stringify(config, null, 2), "utf8");
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function loadSetupConfig() {
|
|
46
|
+
const { config: configPath } = getMaestroPaths();
|
|
47
|
+
if (!existsSync(configPath)) return null;
|
|
48
|
+
return JSON.parse(readFileSync(configPath, "utf8"));
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function removeSkillFrom(destDir) {
|
|
52
|
+
const dest = join(destDir, "maestro");
|
|
53
|
+
if (existsSync(dest)) {
|
|
54
|
+
rmSync(dest, { recursive: true, force: true });
|
|
55
|
+
return true;
|
|
56
|
+
}
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import { dirname, join } from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
|
|
6
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
7
|
+
const PACKAGE_ROOT = join(__dirname, "..");
|
|
8
|
+
|
|
9
|
+
export function getMaestroHome() {
|
|
10
|
+
return process.env.MAESTRO_HOME || join(homedir(), ".maestro");
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function getMaestroPaths() {
|
|
14
|
+
const home = getMaestroHome();
|
|
15
|
+
return {
|
|
16
|
+
home,
|
|
17
|
+
manifest: join(home, "skills-manifest.json"),
|
|
18
|
+
exclude: join(home, "maestro-exclude.txt"),
|
|
19
|
+
config: join(home, "config.json"),
|
|
20
|
+
legacyManifest: join(homedir(), ".cursor", "skills-manifest.json"),
|
|
21
|
+
legacyExclude: join(homedir(), ".cursor", "maestro-exclude.txt"),
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function loadAgents() {
|
|
26
|
+
const raw = readFileSync(join(PACKAGE_ROOT, "agents.json"), "utf8");
|
|
27
|
+
return JSON.parse(raw).agents;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function resolveSkillsDir(agent, { project = false, cwd = process.cwd() } = {}) {
|
|
31
|
+
const base = project ? cwd : homedir();
|
|
32
|
+
const rel = project ? agent.projectSkillsDir : agent.globalSkillsDir;
|
|
33
|
+
return join(base, rel);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function skillSourceDir() {
|
|
37
|
+
return join(PACKAGE_ROOT, "skill");
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function bundledBuildManifest() {
|
|
41
|
+
return join(skillSourceDir(), "scripts", "build_manifest.py");
|
|
42
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import * as p from "@clack/prompts";
|
|
2
|
+
import { rmSync, existsSync } from "node:fs";
|
|
3
|
+
import { loadSetupConfig, removeSkillFrom } from "./install.js";
|
|
4
|
+
import { getMaestroPaths } from "./paths.js";
|
|
5
|
+
import { detectAgents } from "./detect-agents.js";
|
|
6
|
+
|
|
7
|
+
export async function runRemove(options) {
|
|
8
|
+
p.intro("Maestro remove");
|
|
9
|
+
|
|
10
|
+
const config = loadSetupConfig();
|
|
11
|
+
let targets = config?.agents || [];
|
|
12
|
+
|
|
13
|
+
if (!targets.length || options.all) {
|
|
14
|
+
const agents = detectAgents({ project: false });
|
|
15
|
+
targets = agents.map((a) => ({
|
|
16
|
+
id: a.id,
|
|
17
|
+
label: a.label,
|
|
18
|
+
path: `${a.skillsPath}/maestro`.replace(/\\/g, "/"),
|
|
19
|
+
skillsPath: a.skillsPath,
|
|
20
|
+
}));
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (!options.yes) {
|
|
24
|
+
const selected = await p.multiselect({
|
|
25
|
+
message: "Remove Maestro skill from:",
|
|
26
|
+
options: targets.map((t) => ({
|
|
27
|
+
value: t.id,
|
|
28
|
+
label: t.label,
|
|
29
|
+
hint: t.skillsPath || t.path,
|
|
30
|
+
})),
|
|
31
|
+
required: true,
|
|
32
|
+
initialValues: targets.map((t) => t.id),
|
|
33
|
+
});
|
|
34
|
+
if (p.isCancel(selected)) {
|
|
35
|
+
p.cancel("Remove cancelled.");
|
|
36
|
+
process.exit(0);
|
|
37
|
+
}
|
|
38
|
+
targets = targets.filter((t) => selected.includes(t.id));
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
let cleanHome = false;
|
|
42
|
+
if (!options.yes) {
|
|
43
|
+
const clean = await p.confirm({
|
|
44
|
+
message: "Also remove ~/.maestro/ (manifest, exclude, config)?",
|
|
45
|
+
initialValue: false,
|
|
46
|
+
});
|
|
47
|
+
if (p.isCancel(clean)) {
|
|
48
|
+
p.cancel("Remove cancelled.");
|
|
49
|
+
process.exit(0);
|
|
50
|
+
}
|
|
51
|
+
cleanHome = Boolean(clean);
|
|
52
|
+
} else if (options.cleanHome) {
|
|
53
|
+
cleanHome = true;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
for (const t of targets) {
|
|
57
|
+
const dir = t.skillsPath || t.path.replace(/\/maestro$/, "");
|
|
58
|
+
const removed = removeSkillFrom(dir);
|
|
59
|
+
p.log.info(removed ? `Removed ${dir}/maestro` : `Not found: ${dir}/maestro`);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (cleanHome) {
|
|
63
|
+
const { home } = getMaestroPaths();
|
|
64
|
+
if (existsSync(home)) {
|
|
65
|
+
rmSync(home, { recursive: true, force: true });
|
|
66
|
+
p.log.info(`Removed ${home}`);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
p.outro("Maestro removed.");
|
|
71
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { spawnSync } from "node:child_process";
|
|
2
|
+
import { existsSync } from "node:fs";
|
|
3
|
+
import { bundledBuildManifest, getMaestroPaths } from "./paths.js";
|
|
4
|
+
|
|
5
|
+
function pythonCommand() {
|
|
6
|
+
if (process.platform === "win32") {
|
|
7
|
+
const py = spawnSync("py", ["-3", "--version"], { encoding: "utf8" });
|
|
8
|
+
if (py.status === 0) return ["py", "-3"];
|
|
9
|
+
}
|
|
10
|
+
const python3 = spawnSync("python3", ["--version"], { encoding: "utf8" });
|
|
11
|
+
if (python3.status === 0) return ["python3"];
|
|
12
|
+
const python = spawnSync("python", ["--version"], { encoding: "utf8" });
|
|
13
|
+
if (python.status === 0) return ["python"];
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function runBuildManifest({ projectRoot = process.cwd(), quiet = false } = {}) {
|
|
18
|
+
const script = bundledBuildManifest();
|
|
19
|
+
if (!existsSync(script)) {
|
|
20
|
+
return { ok: false, error: `build_manifest.py not found at ${script}` };
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const py = pythonCommand();
|
|
24
|
+
if (!py) {
|
|
25
|
+
return {
|
|
26
|
+
ok: false,
|
|
27
|
+
error: "Python 3.12+ not found. Install Python and ensure `py -3` or `python3` works.",
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const { manifest } = getMaestroPaths();
|
|
32
|
+
const args = [...py, script, "--project-root", projectRoot, "--output", manifest];
|
|
33
|
+
const result = spawnSync(args[0], args.slice(1), {
|
|
34
|
+
encoding: "utf8",
|
|
35
|
+
stdio: quiet ? "pipe" : "inherit",
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
if (result.status !== 0) {
|
|
39
|
+
return {
|
|
40
|
+
ok: false,
|
|
41
|
+
error: result.stderr || `build_manifest exited with code ${result.status}`,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const line = (result.stdout || "").trim().split("\n").pop();
|
|
46
|
+
return { ok: true, message: line || `Manifest written to ${manifest}` };
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export async function registerOnSkillsSh(agents, { yes = false } = {}) {
|
|
50
|
+
const withCli = agents.filter((a) => a.skillsCliAgent);
|
|
51
|
+
if (!withCli.length) return { ok: true, skipped: true };
|
|
52
|
+
|
|
53
|
+
const args = [
|
|
54
|
+
"skills",
|
|
55
|
+
"add",
|
|
56
|
+
"rodovalhofs/maestro",
|
|
57
|
+
"--skill",
|
|
58
|
+
"maestro",
|
|
59
|
+
"-g",
|
|
60
|
+
"-y",
|
|
61
|
+
];
|
|
62
|
+
|
|
63
|
+
for (const agent of withCli) {
|
|
64
|
+
args.push("-a", agent.skillsCliAgent);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const uniqueAgents = [...new Set(withCli.map((a) => a.skillsCliAgent))];
|
|
68
|
+
const finalArgs = [
|
|
69
|
+
"skills",
|
|
70
|
+
"add",
|
|
71
|
+
"rodovalhofs/maestro",
|
|
72
|
+
"--skill",
|
|
73
|
+
"maestro",
|
|
74
|
+
"-g",
|
|
75
|
+
"-y",
|
|
76
|
+
...uniqueAgents.flatMap((a) => ["-a", a]),
|
|
77
|
+
];
|
|
78
|
+
|
|
79
|
+
const result = spawnSync("npx", finalArgs, {
|
|
80
|
+
encoding: "utf8",
|
|
81
|
+
stdio: yes ? "pipe" : "inherit",
|
|
82
|
+
shell: process.platform === "win32",
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
if (result.status !== 0) {
|
|
86
|
+
return {
|
|
87
|
+
ok: false,
|
|
88
|
+
error: "npx skills add failed (optional step). Install Node.js or run manually.",
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
return { ok: true };
|
|
92
|
+
}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import * as p from "@clack/prompts";
|
|
2
|
+
import { detectAgents, filterAgentsByFlags } from "./detect-agents.js";
|
|
3
|
+
import {
|
|
4
|
+
copySkillTo,
|
|
5
|
+
migrateLegacyFiles,
|
|
6
|
+
saveSetupConfig,
|
|
7
|
+
} from "./install.js";
|
|
8
|
+
import { getMaestroPaths } from "./paths.js";
|
|
9
|
+
import { registerOnSkillsSh, runBuildManifest } from "./run-manifest.js";
|
|
10
|
+
|
|
11
|
+
export async function runSetup(options) {
|
|
12
|
+
const project = Boolean(options.project);
|
|
13
|
+
const cwd = process.cwd();
|
|
14
|
+
|
|
15
|
+
p.intro(project ? "Maestro setup (this project)" : "Maestro setup (global)");
|
|
16
|
+
|
|
17
|
+
let agents = detectAgents({ project, cwd });
|
|
18
|
+
agents = filterAgentsByFlags(agents, options);
|
|
19
|
+
|
|
20
|
+
if (!options.cursor && !options.claude && !options.codex && !options.universal && !options.yes) {
|
|
21
|
+
const selected = await p.multiselect({
|
|
22
|
+
message: "Which agents should receive the Maestro skill?",
|
|
23
|
+
options: agents.map((a) => ({
|
|
24
|
+
value: a.id,
|
|
25
|
+
label: a.label,
|
|
26
|
+
hint: a.skillsPath,
|
|
27
|
+
})),
|
|
28
|
+
required: true,
|
|
29
|
+
initialValues: agents.filter((a) => a.detected).map((a) => a.id),
|
|
30
|
+
});
|
|
31
|
+
if (p.isCancel(selected)) {
|
|
32
|
+
p.cancel("Setup cancelled.");
|
|
33
|
+
process.exit(0);
|
|
34
|
+
}
|
|
35
|
+
agents = agents.filter((a) => selected.includes(a.id));
|
|
36
|
+
} else if (options.cursor || options.claude || options.codex || options.universal) {
|
|
37
|
+
agents = filterAgentsByFlags(agents, options);
|
|
38
|
+
} else if (options.yes) {
|
|
39
|
+
agents = agents.filter((a) => a.detected);
|
|
40
|
+
if (!agents.length) agents = detectAgents({ project, cwd });
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (!agents.length) {
|
|
44
|
+
p.cancel("No agents selected.");
|
|
45
|
+
process.exit(1);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
let registerSkillsSh = false;
|
|
49
|
+
if (!options.yes && !options.noSkillsRegistry) {
|
|
50
|
+
const reg = await p.confirm({
|
|
51
|
+
message: "Register install on skills.sh via npx skills add? (optional telemetry)",
|
|
52
|
+
initialValue: false,
|
|
53
|
+
});
|
|
54
|
+
if (p.isCancel(reg)) {
|
|
55
|
+
p.cancel("Setup cancelled.");
|
|
56
|
+
process.exit(0);
|
|
57
|
+
}
|
|
58
|
+
registerSkillsSh = Boolean(reg);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const spinner = p.spinner();
|
|
62
|
+
const installed = [];
|
|
63
|
+
|
|
64
|
+
for (const agent of agents) {
|
|
65
|
+
spinner.start(`Installing Maestro for ${agent.label}…`);
|
|
66
|
+
try {
|
|
67
|
+
const dest = copySkillTo(agent.skillsPath);
|
|
68
|
+
installed.push({ id: agent.id, label: agent.label, path: dest });
|
|
69
|
+
spinner.stop(`Installed → ${dest}`);
|
|
70
|
+
} catch (err) {
|
|
71
|
+
spinner.stop(`Failed for ${agent.label}`);
|
|
72
|
+
p.log.error(String(err.message || err));
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const migrated = migrateLegacyFiles();
|
|
77
|
+
if (migrated.length) {
|
|
78
|
+
p.log.info(`Migrated legacy files: ${migrated.join(", ")}`);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
spinner.start("Building skills manifest…");
|
|
82
|
+
const manifestResult = runBuildManifest({ projectRoot: cwd, quiet: true });
|
|
83
|
+
if (manifestResult.ok) {
|
|
84
|
+
spinner.stop(manifestResult.message);
|
|
85
|
+
} else {
|
|
86
|
+
spinner.stop("Manifest build failed");
|
|
87
|
+
p.log.warn(manifestResult.error);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (registerSkillsSh) {
|
|
91
|
+
spinner.start("Registering on skills.sh…");
|
|
92
|
+
const reg = await registerOnSkillsSh(agents, { yes: options.yes });
|
|
93
|
+
if (reg.ok && !reg.skipped) spinner.stop("Registered on skills.sh");
|
|
94
|
+
else if (!reg.ok) {
|
|
95
|
+
spinner.stop("skills.sh registration skipped");
|
|
96
|
+
p.log.warn(reg.error);
|
|
97
|
+
} else spinner.stop("No skills CLI agents selected");
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const paths = getMaestroPaths();
|
|
101
|
+
saveSetupConfig({
|
|
102
|
+
version: 1,
|
|
103
|
+
installedAt: new Date().toISOString(),
|
|
104
|
+
scope: project ? "project" : "global",
|
|
105
|
+
projectRoot: project ? cwd : null,
|
|
106
|
+
agents: installed,
|
|
107
|
+
maestroHome: paths.home,
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
p.note(
|
|
111
|
+
installed.map((i) => `${i.label}\n ${i.path}`).join("\n\n"),
|
|
112
|
+
"Installed paths",
|
|
113
|
+
);
|
|
114
|
+
p.outro("Maestro setup complete. Invoke with $maestro or /maestro in your agent.");
|
|
115
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "maestro-skills",
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "Interactive setup for the Maestro skill orchestrator (multi-agent)",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "Yuri Rodovalho",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/rodovalhofs/maestro.git",
|
|
10
|
+
"directory": "packages/maestro-skills"
|
|
11
|
+
},
|
|
12
|
+
"homepage": "https://github.com/rodovalhofs/maestro#installation",
|
|
13
|
+
"bugs": {
|
|
14
|
+
"url": "https://github.com/rodovalhofs/maestro/issues"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [
|
|
17
|
+
"maestro",
|
|
18
|
+
"cursor",
|
|
19
|
+
"claude",
|
|
20
|
+
"codex",
|
|
21
|
+
"agent-skills",
|
|
22
|
+
"skills",
|
|
23
|
+
"orchestrator"
|
|
24
|
+
],
|
|
25
|
+
"type": "module",
|
|
26
|
+
"engines": {
|
|
27
|
+
"node": ">=18"
|
|
28
|
+
},
|
|
29
|
+
"bin": {
|
|
30
|
+
"maestro-skills": "./bin/cli.js"
|
|
31
|
+
},
|
|
32
|
+
"files": [
|
|
33
|
+
"bin",
|
|
34
|
+
"lib",
|
|
35
|
+
"skill",
|
|
36
|
+
"agents.json",
|
|
37
|
+
"README.md"
|
|
38
|
+
],
|
|
39
|
+
"scripts": {
|
|
40
|
+
"prepack": "node ../../scripts/sync-skill-to-cli.mjs",
|
|
41
|
+
"test": "node --test test/agents.test.js"
|
|
42
|
+
},
|
|
43
|
+
"dependencies": {
|
|
44
|
+
"@clack/prompts": "^0.10.1",
|
|
45
|
+
"commander": "^13.1.0"
|
|
46
|
+
}
|
|
47
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import { join, dirname } from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
import { test } from "node:test";
|
|
5
|
+
import assert from "node:assert/strict";
|
|
6
|
+
|
|
7
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
8
|
+
const agents = JSON.parse(
|
|
9
|
+
readFileSync(join(__dirname, "..", "agents.json"), "utf8"),
|
|
10
|
+
).agents;
|
|
11
|
+
|
|
12
|
+
test("agents.json includes codex", () => {
|
|
13
|
+
const codex = agents.find((a) => a.id === "codex");
|
|
14
|
+
assert.ok(codex);
|
|
15
|
+
assert.equal(codex.globalSkillsDir, ".codex/skills");
|
|
16
|
+
assert.equal(codex.skillsCliAgent, "codex");
|
|
17
|
+
});
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"agents": [
|
|
3
|
+
{
|
|
4
|
+
"id": "cursor",
|
|
5
|
+
"label": "Cursor",
|
|
6
|
+
"globalSkillsDir": ".cursor/skills",
|
|
7
|
+
"projectSkillsDir": ".cursor/skills",
|
|
8
|
+
"detectDirs": [".cursor"],
|
|
9
|
+
"skillsCliAgent": "cursor"
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
"id": "claude",
|
|
13
|
+
"label": "Claude Code",
|
|
14
|
+
"globalSkillsDir": ".claude/skills",
|
|
15
|
+
"projectSkillsDir": ".claude/skills",
|
|
16
|
+
"detectDirs": [".claude"],
|
|
17
|
+
"skillsCliAgent": "claude"
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
"id": "codex",
|
|
21
|
+
"label": "Codex",
|
|
22
|
+
"globalSkillsDir": ".codex/skills",
|
|
23
|
+
"projectSkillsDir": ".codex/skills",
|
|
24
|
+
"detectDirs": [".codex"],
|
|
25
|
+
"skillsCliAgent": "codex"
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
"id": "universal",
|
|
29
|
+
"label": "Universal (~/.agents/skills)",
|
|
30
|
+
"globalSkillsDir": ".agents/skills",
|
|
31
|
+
"projectSkillsDir": ".agents/skills",
|
|
32
|
+
"detectDirs": [".agents"],
|
|
33
|
+
"skillsCliAgent": null
|
|
34
|
+
}
|
|
35
|
+
]
|
|
36
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Command } from "commander";
|
|
3
|
+
import { runRemove } from "../lib/remove.js";
|
|
4
|
+
import { runSetup } from "../lib/setup.js";
|
|
5
|
+
|
|
6
|
+
const program = new Command();
|
|
7
|
+
program.name("maestro").description("Maestro skill orchestrator (@rodovalhofs/maestro)").version("0.1.0");
|
|
8
|
+
program.command("setup").description("Interactive setup").option("--project").option("--cursor").option("--claude").option("--codex").option("--universal").option("-y, --yes").option("--no-skills-registry").action(runSetup);
|
|
9
|
+
program.command("remove").description("Remove Maestro").option("-y, --yes").option("--all").option("--clean-home").action(runRemove);
|
|
10
|
+
program.parseAsync(process.argv);
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { loadAgents } from "./paths.js";
|
|
5
|
+
|
|
6
|
+
export function detectAgents({ project = false, cwd = process.cwd() } = {}) {
|
|
7
|
+
const agents = loadAgents();
|
|
8
|
+
const base = project ? cwd : homedir();
|
|
9
|
+
|
|
10
|
+
return agents.map((agent) => {
|
|
11
|
+
const detected = agent.detectDirs.some((dir) => existsSync(join(base, dir)));
|
|
12
|
+
return {
|
|
13
|
+
...agent,
|
|
14
|
+
detected: detected || !project,
|
|
15
|
+
skillsPath: join(base, project ? agent.projectSkillsDir : agent.globalSkillsDir),
|
|
16
|
+
};
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function filterAgentsByFlags(agents, flags) {
|
|
21
|
+
const ids = [];
|
|
22
|
+
if (flags.cursor) ids.push("cursor");
|
|
23
|
+
if (flags.claude) ids.push("claude");
|
|
24
|
+
if (flags.codex) ids.push("codex");
|
|
25
|
+
if (flags.universal) ids.push("universal");
|
|
26
|
+
if (!ids.length) return agents;
|
|
27
|
+
return agents.filter((a) => ids.includes(a.id));
|
|
28
|
+
}
|