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.
Files changed (56) hide show
  1. package/.github/workflows/ci.yml +26 -0
  2. package/.github/workflows/publish-npm.yml +30 -0
  3. package/CONTRIBUTING.md +31 -0
  4. package/LICENSE +21 -0
  5. package/README.md +300 -0
  6. package/SECURITY.md +33 -0
  7. package/docs/github-workflow.md +96 -0
  8. package/docs/maestro-skills-cli.md +113 -0
  9. package/package.json +35 -0
  10. package/packages/maestro-skills/README.md +37 -0
  11. package/packages/maestro-skills/agents.json +36 -0
  12. package/packages/maestro-skills/bin/cli.js +37 -0
  13. package/packages/maestro-skills/lib/detect-agents.js +28 -0
  14. package/packages/maestro-skills/lib/install.js +58 -0
  15. package/packages/maestro-skills/lib/paths.js +42 -0
  16. package/packages/maestro-skills/lib/remove.js +71 -0
  17. package/packages/maestro-skills/lib/run-manifest.js +92 -0
  18. package/packages/maestro-skills/lib/setup.js +115 -0
  19. package/packages/maestro-skills/package.json +47 -0
  20. package/packages/maestro-skills/test/agents.test.js +17 -0
  21. package/packages/rodovalhofs-maestro/agents.json +36 -0
  22. package/packages/rodovalhofs-maestro/bin/cli.js +10 -0
  23. package/packages/rodovalhofs-maestro/lib/detect-agents.js +28 -0
  24. package/packages/rodovalhofs-maestro/lib/install.js +58 -0
  25. package/packages/rodovalhofs-maestro/lib/paths.js +42 -0
  26. package/packages/rodovalhofs-maestro/lib/remove.js +71 -0
  27. package/packages/rodovalhofs-maestro/lib/run-manifest.js +92 -0
  28. package/packages/rodovalhofs-maestro/lib/setup.js +115 -0
  29. package/packages/rodovalhofs-maestro/package.json +33 -0
  30. package/scripts/sync-skill-to-cli.mjs +75 -0
  31. package/scripts/sync-templates.ps1 +22 -0
  32. package/skills/maestro/SKILL.md +272 -0
  33. package/skills/maestro/maestro-exclude.example.txt +6 -0
  34. package/skills/maestro/scripts/bm25.py +70 -0
  35. package/skills/maestro/scripts/build_manifest.py +183 -0
  36. package/skills/maestro/scripts/concept_gaps.py +196 -0
  37. package/skills/maestro/scripts/domains.py +148 -0
  38. package/skills/maestro/scripts/intents.py +167 -0
  39. package/skills/maestro/scripts/maestro_paths.py +41 -0
  40. package/skills/maestro/scripts/route_tasks.py +101 -0
  41. package/skills/maestro/scripts/routing.py +106 -0
  42. package/skills/maestro/scripts/search_skills.py +287 -0
  43. package/skills/maestro/scripts/synonyms.py +47 -0
  44. package/templates/.github/ISSUE_TEMPLATE/bug_report.yml +34 -0
  45. package/templates/.github/ISSUE_TEMPLATE/chore.yml +17 -0
  46. package/templates/.github/ISSUE_TEMPLATE/config.yml +1 -0
  47. package/templates/.github/ISSUE_TEMPLATE/feature_request.yml +27 -0
  48. package/templates/.github/workflows/ci-failure-to-issue.yml +47 -0
  49. package/templates/.github/workflows/ci.yml +27 -0
  50. package/templates/CONTRIBUTING.md +22 -0
  51. package/templates/labels.json +12 -0
  52. package/templates/pull_request_template.md +18 -0
  53. package/tests/fixtures/sample-manifest.json +76 -0
  54. package/tests/test_concept_gaps.py +63 -0
  55. package/tests/test_maestro_paths.py +29 -0
  56. 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
+ }