@olhapi/maestro 0.1.5-rc.11 → 0.1.5-rc.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.
@@ -0,0 +1,110 @@
1
+ const fs = require("node:fs");
2
+ const os = require("node:os");
3
+ const path = require("node:path");
4
+
5
+ const maestroSkillName = "maestro";
6
+
7
+ function bundledSkillDir(baseDir = path.join(__dirname, "..")) {
8
+ const packagedPath = path.join(baseDir, "share", "skills", maestroSkillName);
9
+ if (fs.existsSync(packagedPath)) {
10
+ return packagedPath;
11
+ }
12
+ return path.resolve(baseDir, "..", "..", "..", "skills", maestroSkillName);
13
+ }
14
+
15
+ function bundledSkillPaths(options = {}) {
16
+ const fsModule = options.fs || fs;
17
+ const root = bundledSkillDir(options.baseDir);
18
+ const paths = [];
19
+
20
+ walkFiles(root, root, fsModule, (relativePath) => {
21
+ paths.push(relativePath);
22
+ });
23
+ return paths.sort();
24
+ }
25
+
26
+ function readBundledSkillFile(relativePath, options = {}) {
27
+ const fsModule = options.fs || fs;
28
+ return fsModule.readFileSync(path.join(bundledSkillDir(options.baseDir), relativePath));
29
+ }
30
+
31
+ function installBundledSkills(options = {}) {
32
+ const homeDir = options.homeDir || os.homedir();
33
+ const targets = options.targets || [
34
+ path.join(homeDir, ".agents", "skills", maestroSkillName),
35
+ path.join(homeDir, ".claude", "skills", maestroSkillName),
36
+ ];
37
+ const sourceRoot = bundledSkillDir(options.baseDir);
38
+ const fsModule = options.fs || fs;
39
+
40
+ for (const target of targets) {
41
+ installTree(sourceRoot, target, fsModule);
42
+ }
43
+
44
+ return targets;
45
+ }
46
+
47
+ function installTree(sourceRoot, dest, fsModule) {
48
+ const parent = path.dirname(dest);
49
+ fsModule.mkdirSync(parent, { recursive: true });
50
+
51
+ const tmpDir = fsModule.mkdtempSync(path.join(parent, `.${path.basename(dest)}.tmp-`));
52
+ try {
53
+ copyTree(sourceRoot, tmpDir, fsModule);
54
+
55
+ const backupDir = `${dest}.bak`;
56
+ fsModule.rmSync(backupDir, { force: true, recursive: true });
57
+ let hadBackup = false;
58
+ if (fsModule.existsSync(dest)) {
59
+ fsModule.renameSync(dest, backupDir);
60
+ hadBackup = true;
61
+ }
62
+
63
+ try {
64
+ fsModule.renameSync(tmpDir, dest);
65
+ } catch (error) {
66
+ if (hadBackup) {
67
+ try {
68
+ fsModule.renameSync(backupDir, dest);
69
+ } catch (restoreError) {
70
+ throw new Error(`install bundled skill: ${error.message} (failed to restore previous install: ${restoreError.message})`);
71
+ }
72
+ }
73
+ throw new Error(`install bundled skill: ${error.message}`);
74
+ }
75
+
76
+ fsModule.rmSync(backupDir, { force: true, recursive: true });
77
+ } finally {
78
+ fsModule.rmSync(tmpDir, { force: true, recursive: true });
79
+ }
80
+ }
81
+
82
+ function copyTree(sourceRoot, destRoot, fsModule) {
83
+ walkFiles(sourceRoot, sourceRoot, fsModule, (relativePath, sourcePath, stat) => {
84
+ const targetPath = path.join(destRoot, relativePath);
85
+ fsModule.mkdirSync(path.dirname(targetPath), { recursive: true });
86
+ const mode = stat.mode & 0o111 ? 0o755 : 0o644;
87
+ fsModule.writeFileSync(targetPath, fsModule.readFileSync(sourcePath), { mode });
88
+ fsModule.chmodSync(targetPath, mode);
89
+ });
90
+ }
91
+
92
+ function walkFiles(root, currentRoot, fsModule, visitor) {
93
+ const entries = fsModule.readdirSync(currentRoot, { withFileTypes: true });
94
+ for (const entry of entries) {
95
+ const absolutePath = path.join(currentRoot, entry.name);
96
+ if (entry.isDirectory()) {
97
+ walkFiles(root, absolutePath, fsModule, visitor);
98
+ continue;
99
+ }
100
+ const relativePath = path.relative(root, absolutePath);
101
+ visitor(relativePath, absolutePath, fsModule.statSync(absolutePath));
102
+ }
103
+ }
104
+
105
+ module.exports = {
106
+ bundledSkillDir,
107
+ bundledSkillPaths,
108
+ installBundledSkills,
109
+ readBundledSkillFile,
110
+ };
@@ -0,0 +1,82 @@
1
+ const fs = require("node:fs");
2
+ const os = require("node:os");
3
+ const path = require("node:path");
4
+
5
+ const DEFAULT_IMAGE_REPOSITORY = "ghcr.io/olhapi/maestro";
6
+
7
+ function sanitizeVersion(version) {
8
+ return String(version || "").trim().replace(/^v/, "");
9
+ }
10
+
11
+ function launcherDir(homeDir = os.homedir()) {
12
+ return path.join(homeDir, ".maestro", "launcher");
13
+ }
14
+
15
+ function runtimeStatePath(homeDir = os.homedir()) {
16
+ return path.join(launcherDir(homeDir), "runtime.json");
17
+ }
18
+
19
+ function imageRefForVersion(version, imageRepository = DEFAULT_IMAGE_REPOSITORY) {
20
+ const tag = sanitizeVersion(version);
21
+ if (!tag) {
22
+ throw new Error("image version is required");
23
+ }
24
+ return `${imageRepository}:${tag}`;
25
+ }
26
+
27
+ function readRuntimeState(options = {}) {
28
+ const fsModule = options.fs || fs;
29
+ const homeDir = options.homeDir || os.homedir();
30
+ const statePath = runtimeStatePath(homeDir);
31
+ if (!fsModule.existsSync(statePath)) {
32
+ return null;
33
+ }
34
+ const raw = fsModule.readFileSync(statePath, "utf8");
35
+ const parsed = JSON.parse(raw);
36
+ if (!parsed || typeof parsed !== "object" || typeof parsed.image !== "string") {
37
+ throw new Error(`invalid runtime state at ${statePath}`);
38
+ }
39
+ return parsed;
40
+ }
41
+
42
+ function writeRuntimeState(image, options = {}) {
43
+ const fsModule = options.fs || fs;
44
+ const homeDir = options.homeDir || os.homedir();
45
+ const statePath = runtimeStatePath(homeDir);
46
+ fsModule.mkdirSync(path.dirname(statePath), { recursive: true });
47
+ const payload = {
48
+ image,
49
+ updated_at: new Date().toISOString(),
50
+ };
51
+ fsModule.writeFileSync(statePath, `${JSON.stringify(payload, null, 2)}\n`);
52
+ return payload;
53
+ }
54
+
55
+ function resolveImageRef(options = {}) {
56
+ const env = options.env || process.env;
57
+ const runtimeState = options.runtimeState === undefined ? readRuntimeState(options) : options.runtimeState;
58
+ const packageVersion = sanitizeVersion(options.packageVersion);
59
+ const imageRepository = options.imageRepository || DEFAULT_IMAGE_REPOSITORY;
60
+
61
+ if (typeof env.MAESTRO_IMAGE === "string" && env.MAESTRO_IMAGE.trim() !== "") {
62
+ return env.MAESTRO_IMAGE.trim();
63
+ }
64
+ if (runtimeState && typeof runtimeState.image === "string" && runtimeState.image.trim() !== "") {
65
+ return runtimeState.image.trim();
66
+ }
67
+ if (!packageVersion) {
68
+ throw new Error("package version is required to resolve the default runtime image");
69
+ }
70
+ return imageRefForVersion(packageVersion, imageRepository);
71
+ }
72
+
73
+ module.exports = {
74
+ DEFAULT_IMAGE_REPOSITORY,
75
+ imageRefForVersion,
76
+ launcherDir,
77
+ readRuntimeState,
78
+ resolveImageRef,
79
+ runtimeStatePath,
80
+ sanitizeVersion,
81
+ writeRuntimeState,
82
+ };
package/package.json CHANGED
@@ -1,24 +1,28 @@
1
1
  {
2
2
  "name": "@olhapi/maestro",
3
- "version": "0.1.5-rc.11",
4
- "description": "Maestro CLI packaged for global npm installation",
3
+ "version": "0.1.5-rc.14",
4
+ "description": "Maestro Docker-backed launcher for global npm installation",
5
5
  "license": "MIT",
6
+ "engines": {
7
+ "node": ">=24"
8
+ },
6
9
  "bin": {
7
- "maestro": "bin/maestro.js"
10
+ "maestro": "bin/maestro"
8
11
  },
9
12
  "files": [
13
+ "bin/maestro",
14
+ "bin/maestro.cmd",
15
+ "bin/maestro.ps1",
10
16
  "bin/maestro.js",
11
- "lib/get-exe-path.js",
17
+ "lib/browser.js",
18
+ "lib/cli.js",
19
+ "lib/docker-plan.js",
20
+ "lib/install-skills.js",
21
+ "lib/runtime-state.js",
22
+ "share/skills/maestro",
12
23
  "LICENSE",
13
24
  "README.md"
14
25
  ],
15
- "optionalDependencies": {
16
- "@olhapi/maestro-darwin-arm64": "0.1.5-rc.11",
17
- "@olhapi/maestro-darwin-x64": "0.1.5-rc.11",
18
- "@olhapi/maestro-linux-x64-gnu": "0.1.5-rc.11",
19
- "@olhapi/maestro-linux-arm64-gnu": "0.1.5-rc.11",
20
- "@olhapi/maestro-win32-x64": "0.1.5-rc.11"
21
- },
22
26
  "repository": {
23
27
  "type": "git",
24
28
  "url": "git+https://github.com/olhapi/maestro.git"
@@ -0,0 +1,38 @@
1
+ ---
2
+ name: maestro
3
+ description: Use Maestro to initialize workflows, run the local loop, bridge MCP, and manage projects, epics, issues, and readiness checks.
4
+ ---
5
+
6
+ # Maestro CLI Skill
7
+
8
+ Use this skill when a task involves Maestro's local orchestration flow, repo setup, queue management, or readiness checks.
9
+
10
+ ## Prefer Maestro when
11
+
12
+ - You need to create or refresh `WORKFLOW.md`.
13
+ - You want to start or supervise the local daemon.
14
+ - You need to connect another agent to the live queue through MCP.
15
+ - You are creating, updating, moving, or inspecting projects, epics, or issues.
16
+ - You want to verify repo readiness before launch.
17
+
18
+ ## Start with the smallest command that fits
19
+
20
+ - `maestro workflow init .` for repo bootstrap.
21
+ - `maestro run` for the local loop and dashboard.
22
+ - `maestro mcp` only after the daemon is already running.
23
+ - `maestro verify`, `maestro doctor`, or `maestro spec-check` for readiness and validation.
24
+
25
+ ## Common flows
26
+
27
+ - Setup and launch: see [setup](references/setup.md)
28
+ - Day-to-day operations: see [operations](references/operations.md)
29
+ - Projects and issues: see [project-work](references/project-work.md)
30
+ - Readiness checks: see [readiness](references/readiness.md)
31
+
32
+ ## Working style
33
+
34
+ - Prefer exact, repo-local commands over generic advice.
35
+ - Keep changes scoped to the issue or project being worked.
36
+ - Use `--json` when the output will feed another tool or agent.
37
+ - Reuse the live daemon and existing workflow file before inventing a new setup.
38
+
@@ -0,0 +1,21 @@
1
+ # Operations
2
+
3
+ Use these commands when the daemon is running and you want to inspect or steer live work.
4
+
5
+ ## Status and runtime
6
+
7
+ ```bash
8
+ maestro status
9
+ maestro status --dashboard --api-url http://127.0.0.1:8787
10
+ maestro sessions --api-url http://127.0.0.1:8787
11
+ maestro events --api-url http://127.0.0.1:8787
12
+ maestro runtime-series --api-url http://127.0.0.1:8787
13
+ ```
14
+
15
+ ## When to use
16
+
17
+ - Use `status` for a quick health check.
18
+ - Use `sessions` when multiple runs are in flight.
19
+ - Use `events` when you need a recent timeline of what happened.
20
+ - Use `runtime-series` when you need token or activity trends over time.
21
+
@@ -0,0 +1,44 @@
1
+ # Projects and issues
2
+
3
+ Use these commands for the local queue and issue lifecycle.
4
+
5
+ ## Projects
6
+
7
+ ```bash
8
+ maestro project create <name> --repo <repo_path>
9
+ maestro project list
10
+ maestro project show <id>
11
+ maestro project update <id>
12
+ maestro project delete <id>
13
+ ```
14
+
15
+ ## Epics
16
+
17
+ ```bash
18
+ maestro epic create <name> --project <project_id>
19
+ maestro epic list
20
+ maestro epic show <id>
21
+ maestro epic update <id>
22
+ maestro epic delete <id>
23
+ ```
24
+
25
+ ## Issues
26
+
27
+ ```bash
28
+ maestro issue create <title>
29
+ maestro issue list
30
+ maestro issue show <identifier>
31
+ maestro issue update <identifier>
32
+ maestro issue move <identifier> <state>
33
+ maestro issue block <identifier> <blocker_identifier...>
34
+ maestro issue unblock <identifier> <blocker_identifier...>
35
+ maestro issue comments list <identifier>
36
+ maestro issue assets add <identifier> <path>
37
+ ```
38
+
39
+ ## Guidance
40
+
41
+ - Use `--project` and labels to keep work scoped.
42
+ - Use `move` when you need to change queue state, not when you only want to update metadata.
43
+ - Use comments and assets for review context rather than burying details in the issue title.
44
+
@@ -0,0 +1,18 @@
1
+ # Readiness
2
+
3
+ Use these commands before launch or when the repo needs a sanity check.
4
+
5
+ ## Validation commands
6
+
7
+ ```bash
8
+ maestro verify
9
+ maestro doctor
10
+ maestro spec-check
11
+ ```
12
+
13
+ ## Guidance
14
+
15
+ - Use `verify` when you want the normal readiness check and remediation guidance.
16
+ - Use `doctor` when you want the same checks with a different presentation.
17
+ - Use `spec-check` when you want a read-only validation pass.
18
+
@@ -0,0 +1,45 @@
1
+ # Setup
2
+
3
+ Use these commands when you are preparing a repo or teaching another agent how to connect to Maestro.
4
+
5
+ ## Bundle install
6
+
7
+ Install the Maestro skill bundle into the current user's personal skill directories:
8
+
9
+ ```bash
10
+ maestro install --skills
11
+ ```
12
+
13
+ The installer writes the skill to:
14
+
15
+ - `~/.agents/skills/maestro` for Codex
16
+ - `~/.claude/skills/maestro` for Claude Code
17
+
18
+ ## Workflow bootstrap
19
+
20
+ Create or refresh the repo contract:
21
+
22
+ ```bash
23
+ maestro workflow init .
24
+ ```
25
+
26
+ Use `--defaults` when you want the non-interactive scaffold. Use `--force` only when overwriting an existing file is intentional.
27
+
28
+ ## Launch the loop
29
+
30
+ Start the daemon after the workflow file is in place:
31
+
32
+ ```bash
33
+ maestro run
34
+ ```
35
+
36
+ ## Attach MCP
37
+
38
+ Bridge the live daemon over stdio for a connected coding agent:
39
+
40
+ ```bash
41
+ maestro mcp
42
+ ```
43
+
44
+ Start `maestro run` first, then point the agent at the same database.
45
+
@@ -1,118 +0,0 @@
1
- const fs = require("node:fs");
2
- const path = require("node:path");
3
-
4
- const supportedTargets = Object.freeze({
5
- "darwin:arm64": {
6
- packageName: "@olhapi/maestro-darwin-arm64",
7
- label: "darwin/arm64",
8
- },
9
- "darwin:x64": {
10
- packageName: "@olhapi/maestro-darwin-x64",
11
- label: "darwin/x64",
12
- },
13
- "linux:x64": {
14
- packageName: "@olhapi/maestro-linux-x64-gnu",
15
- label: "linux/x64 (glibc)",
16
- },
17
- "linux:arm64": {
18
- packageName: "@olhapi/maestro-linux-arm64-gnu",
19
- label: "linux/arm64 (glibc)",
20
- },
21
- "win32:x64": {
22
- packageName: "@olhapi/maestro-win32-x64",
23
- label: "win32/x64",
24
- },
25
- });
26
-
27
- function resolveTarget(platform, arch) {
28
- return supportedTargets[`${platform}:${arch}`] ?? null;
29
- }
30
-
31
- function supportedTargetSummary() {
32
- return Object.values(supportedTargets)
33
- .map((target) => target.label)
34
- .join(", ");
35
- }
36
-
37
- function readProcessReport() {
38
- if (!process.report || typeof process.report.getReport !== "function") {
39
- return null;
40
- }
41
- try {
42
- return process.report.getReport();
43
- } catch {
44
- return null;
45
- }
46
- }
47
-
48
- function hasGlibcRuntime(report) {
49
- const version = report && report.header && report.header.glibcVersionRuntime;
50
- return typeof version === "string" && version.trim() !== "";
51
- }
52
-
53
- function buildInstallError(platform, arch, expectedPackageName, report) {
54
- const parts = [
55
- `Maestro npm install supports ${supportedTargetSummary()}.`,
56
- `Could not resolve a packaged binary for ${platform}/${arch}.`,
57
- ];
58
- if (expectedPackageName) {
59
- parts.push(`Expected package: ${expectedPackageName}.`);
60
- }
61
- if (platform === "linux") {
62
- if (!hasGlibcRuntime(report)) {
63
- parts.push("Linux npm packages currently target glibc only. Alpine and other musl-based distros should build from source or use Docker.");
64
- } else {
65
- parts.push("If your install is on glibc Linux, reinstalling can restore a missing optional dependency. Alpine and other musl-based distros should build from source or use Docker.");
66
- }
67
- } else {
68
- parts.push("If your platform is unsupported, build from source or use Docker.");
69
- }
70
- return parts.join(" ");
71
- }
72
-
73
- function resolveExePath(options = {}) {
74
- const platform = options.platform ?? process.platform;
75
- const arch = options.arch ?? process.arch;
76
- const pathModule = options.pathModule ?? path;
77
- const resolvePackageJson =
78
- options.resolvePackageJson ??
79
- ((packageName) => require.resolve(`${packageName}/package.json`));
80
- const existsSync = options.existsSync ?? fs.existsSync;
81
- const report = options.report ?? readProcessReport();
82
-
83
- const target = resolveTarget(platform, arch);
84
- if (!target) {
85
- throw new Error(buildInstallError(platform, arch, null, report));
86
- }
87
-
88
- let packageJsonPath;
89
- try {
90
- packageJsonPath = resolvePackageJson(target.packageName);
91
- } catch {
92
- throw new Error(buildInstallError(platform, arch, target.packageName, report));
93
- }
94
-
95
- let exePath = pathModule.join(
96
- pathModule.dirname(packageJsonPath),
97
- "lib",
98
- platform === "win32" ? "maestro.exe" : "maestro",
99
- );
100
- if (platform === "win32" && exePath.length >= 248 && !exePath.startsWith("\\\\?\\")) {
101
- exePath = `\\\\?\\${exePath}`;
102
- }
103
- if (!existsSync(exePath)) {
104
- throw new Error(`Executable not found in ${target.packageName}: ${exePath}`);
105
- }
106
- return exePath;
107
- }
108
-
109
- function getExePath() {
110
- return resolveExePath();
111
- }
112
-
113
- module.exports = {
114
- buildInstallError,
115
- getExePath,
116
- resolveExePath,
117
- resolveTarget,
118
- };