maesttro 0.2.1 → 0.4.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.
- package/README.md +18 -3
- package/dist/maesttro.js +177 -25
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -58,9 +58,10 @@ maesttro doctor # health check anytime
|
|
|
58
58
|
|
|
59
59
|
maesttro # from a repo root: new session clone + default client (opencode)
|
|
60
60
|
maesttro opencode # same, with opencode
|
|
61
|
+
maesttro --strict [client] # same, with no GitHub token in the runner (no `gh`)
|
|
61
62
|
maesttro bash # plain shell inside the runner, for debugging
|
|
62
63
|
maesttro sessions # list this repo's kept sessions
|
|
63
|
-
maesttro resume <id> # continue one
|
|
64
|
+
maesttro resume <id> # continue one, in the mode it started with
|
|
64
65
|
maesttro push [branch] # push a branch a session created (shows what goes out, asks first)
|
|
65
66
|
|
|
66
67
|
maesttro update # bump the package + rebuild image with refreshed configs
|
|
@@ -106,6 +107,15 @@ comments). It acts as the Maesttro GitHub App's bot:
|
|
|
106
107
|
- Commits are authored by you (your repo's `user.name` / `user.email`) and committed by the bot.
|
|
107
108
|
- The broker is reached over the tailnet. Off the tailnet, the session still starts, just without `gh`.
|
|
108
109
|
|
|
110
|
+
The write side is coarse. GitHub has no finer grant than "issues" and "pull
|
|
111
|
+
requests", so the same token that lets the agent comment and open PRs also lets
|
|
112
|
+
it edit label definitions and milestones, edit or close other people's issues and
|
|
113
|
+
comments, and submit reviews. If that is more than you want to hand an agent,
|
|
114
|
+
start the session with `maesttro --strict`: no token is minted and none reaches
|
|
115
|
+
the runner. Local git and `maesttro push` work as usual; `gh` and `git fetch` on a
|
|
116
|
+
private repo don't, and the agent hands GitHub actions to you instead. Resuming a
|
|
117
|
+
strict session stays strict; `maesttro resume <id> --no-strict` lifts it.
|
|
118
|
+
|
|
109
119
|
## Your instructions come along
|
|
110
120
|
|
|
111
121
|
At every container start, `maesttro configure` copies your host
|
|
@@ -145,7 +155,12 @@ thinking blocks shown. The two providers replace yours of the same name; the
|
|
|
145
155
|
rest (`mcp`, `plugin`, other providers) is merged. Removed secrets are listed
|
|
146
156
|
at launch by path. `{env:X}` / `$X` references are kept.
|
|
147
157
|
|
|
148
|
-
Claude Code
|
|
158
|
+
Claude Code keeps its own login and defaults. It is pointed at envoy's
|
|
159
|
+
`$MAESTTRO_URL/claude-code` (`ANTHROPIC_BASE_URL`), with Maesttro added to the
|
|
160
|
+
model picker as a custom option (`maesttro`), with its context window read
|
|
161
|
+
from envoy at launch. Anthropic models pass
|
|
162
|
+
through to Anthropic on your own subscription. These env entries are merged
|
|
163
|
+
into the runner's `~/.claude/settings.json` at every start.
|
|
149
164
|
|
|
150
165
|
---
|
|
151
166
|
|
|
@@ -169,7 +184,7 @@ untouched by an update.
|
|
|
169
184
|
| Mount | Source | Purpose |
|
|
170
185
|
|---|---|---|
|
|
171
186
|
| `/home/agent` | `maesttro-home` named volume | Per-client session state. Survives across runs, lives only in Docker. Holds no GitHub credential. |
|
|
172
|
-
| `/home/agent/.config/gh-maesttro` (read-only) | per-session temp dir on host | The session's GitHub token (`hosts.yml`). The CLI refreshes it before its 1 h expiry and revokes it on exit. |
|
|
187
|
+
| `/home/agent/.config/gh-maesttro` (read-only) | per-session temp dir on host | The session's GitHub token (`hosts.yml`). The CLI refreshes it before its 1 h expiry and revokes it on exit. Not mounted in `--strict` sessions, or when GitHub access is unavailable. |
|
|
173
188
|
| `<repo>/.git/objects` (read-only, same absolute path) | your repo's object store | Base history, borrowed by the clone through its `alternates`. The real `.git` is never mounted. |
|
|
174
189
|
| `/workspace` | the session clone (`.worktrees/<id>`) | The working directory: a `git clone --shared` detached at the default branch. |
|
|
175
190
|
| `/opt/maesttro`, `/opt/maesttro-host` (read-only) | this package; your instruction files | Used by `maesttro configure` at container start. |
|
package/dist/maesttro.js
CHANGED
|
@@ -461,6 +461,29 @@ var ORCHESTRATOR_MODEL = "maesttro";
|
|
|
461
461
|
function maesttroUrl(env = process.env) {
|
|
462
462
|
return (env.MAESTTRO_URL?.trim() || DEFAULT_MAESTTRO_URL).replace(/\/+$/, "");
|
|
463
463
|
}
|
|
464
|
+
function contextWindowOf(card) {
|
|
465
|
+
const { max_input_tokens: input, max_tokens: output } = card;
|
|
466
|
+
return typeof input === "number" && typeof output === "number" ? input + output : undefined;
|
|
467
|
+
}
|
|
468
|
+
async function fetchOrchestratorWindow({
|
|
469
|
+
url,
|
|
470
|
+
fetch: fetch2 = globalThis.fetch,
|
|
471
|
+
timeoutMs = 2000
|
|
472
|
+
}) {
|
|
473
|
+
try {
|
|
474
|
+
const response = await fetch2(`${url}/claude-code/v1/models`, {
|
|
475
|
+
signal: AbortSignal.timeout(timeoutMs)
|
|
476
|
+
});
|
|
477
|
+
if (!response.ok)
|
|
478
|
+
return;
|
|
479
|
+
const body = await response.json();
|
|
480
|
+
const cards = Array.isArray(body.data) ? body.data : [];
|
|
481
|
+
const card = cards.find(({ id }) => id === `claude-${ORCHESTRATOR_MODEL}`);
|
|
482
|
+
return card ? contextWindowOf(card) : undefined;
|
|
483
|
+
} catch {
|
|
484
|
+
return;
|
|
485
|
+
}
|
|
486
|
+
}
|
|
464
487
|
|
|
465
488
|
// src/provider-configs.ts
|
|
466
489
|
var PROVIDER_ID = "maesttro";
|
|
@@ -531,6 +554,20 @@ function opencodeConfig(base, url) {
|
|
|
531
554
|
}
|
|
532
555
|
};
|
|
533
556
|
}
|
|
557
|
+
function claudeSettings({
|
|
558
|
+
url,
|
|
559
|
+
orchestratorWindow
|
|
560
|
+
}) {
|
|
561
|
+
return {
|
|
562
|
+
env: {
|
|
563
|
+
ANTHROPIC_BASE_URL: `${url}/claude-code`,
|
|
564
|
+
ANTHROPIC_CUSTOM_MODEL_OPTION: ORCHESTRATOR_MODEL,
|
|
565
|
+
ANTHROPIC_CUSTOM_MODEL_OPTION_NAME: "Maesttro",
|
|
566
|
+
ANTHROPIC_CUSTOM_MODEL_OPTION_DESCRIPTION: "Maesttro orchestrator",
|
|
567
|
+
...orchestratorWindow ? { CLAUDE_CODE_MAX_CONTEXT_TOKENS: String(orchestratorWindow) } : {}
|
|
568
|
+
}
|
|
569
|
+
};
|
|
570
|
+
}
|
|
534
571
|
|
|
535
572
|
// src/secrets.ts
|
|
536
573
|
var SECRET_KEY = /api[_-]?key|token|secret|passw(or)?d|credential|authorization|bearer|private[_-]?key|cookie/i;
|
|
@@ -617,6 +654,8 @@ var OPENCODE_HOST_CONFIGS = [
|
|
|
617
654
|
".config/opencode/opencode.jsonc"
|
|
618
655
|
];
|
|
619
656
|
var OPENCODE_CONFIG = ".config/opencode/opencode.json";
|
|
657
|
+
var CLAUDE_SETTINGS = ".claude/settings.json";
|
|
658
|
+
var MERGED_INTO_HOME = [CLAUDE_SETTINGS];
|
|
620
659
|
var OPENCODE_TUI_STATE = ".local/state/opencode/kv.json";
|
|
621
660
|
var CURATED_OPENCODE_TUI_STATE = {
|
|
622
661
|
thinking_mode: "show",
|
|
@@ -652,11 +691,15 @@ function json(value) {
|
|
|
652
691
|
}
|
|
653
692
|
function buildClientConfigs({
|
|
654
693
|
host,
|
|
655
|
-
url
|
|
694
|
+
url,
|
|
695
|
+
orchestratorWindow
|
|
656
696
|
}) {
|
|
657
697
|
const opencode = redactSecrets(opencodeHostBase(host.opencode));
|
|
658
698
|
return {
|
|
659
|
-
files: {
|
|
699
|
+
files: {
|
|
700
|
+
[OPENCODE_CONFIG]: json(opencodeConfig(opencode.value, url)),
|
|
701
|
+
[CLAUDE_SETTINGS]: json(claudeSettings({ url, orchestratorWindow }))
|
|
702
|
+
},
|
|
660
703
|
removed: opencode.removed.map((path) => `opencode ${path}`)
|
|
661
704
|
};
|
|
662
705
|
}
|
|
@@ -679,7 +722,19 @@ function installClientConfigs({
|
|
|
679
722
|
}) {
|
|
680
723
|
if (!existsSync(source))
|
|
681
724
|
return false;
|
|
682
|
-
|
|
725
|
+
const merged = MERGED_INTO_HOME.map((file) => join2(source, file));
|
|
726
|
+
cpSync(source, home, {
|
|
727
|
+
recursive: true,
|
|
728
|
+
force: true,
|
|
729
|
+
filter: (path) => !merged.includes(path)
|
|
730
|
+
});
|
|
731
|
+
for (const file of MERGED_INTO_HOME) {
|
|
732
|
+
const staged = join2(source, file);
|
|
733
|
+
if (!existsSync(staged))
|
|
734
|
+
continue;
|
|
735
|
+
const target = join2(home, file);
|
|
736
|
+
writeJson(target, mergeJson(readJson(target), readJson(staged)));
|
|
737
|
+
}
|
|
683
738
|
return true;
|
|
684
739
|
}
|
|
685
740
|
function injectOpencodeTuiState({ home }) {
|
|
@@ -730,22 +785,41 @@ var IMAGE = "maesttro-runner:latest";
|
|
|
730
785
|
var VOLUME = process.env.MAESTTRO_VOLUME?.trim() || "maesttro-home";
|
|
731
786
|
|
|
732
787
|
// src/runner-context.ts
|
|
788
|
+
function githubMode(value) {
|
|
789
|
+
return value === "on" || value === "strict" ? value : "off";
|
|
790
|
+
}
|
|
791
|
+
function githubRules(context) {
|
|
792
|
+
switch (context.github) {
|
|
793
|
+
case "on":
|
|
794
|
+
return [
|
|
795
|
+
`- \`gh\` works for ${context.repo ?? "this repo"} only: issues, pull requests, comments. Its token is short-lived and refreshed for you.`,
|
|
796
|
+
"- Merging pull requests and deleting remote branches are not possible from here. A person merges."
|
|
797
|
+
];
|
|
798
|
+
case "strict":
|
|
799
|
+
return [
|
|
800
|
+
"- This session runs in strict mode: GitHub is disabled on purpose and the runner holds no GitHub credentials. `gh` does not work, and `git fetch` fails for private repositories.",
|
|
801
|
+
"- Do not look for credentials or ask the user for a token. Local git work still works.",
|
|
802
|
+
"- When a task needs GitHub (an issue, a pull request, a comment, a review, a label), write out what should be posted and hand it to the user."
|
|
803
|
+
];
|
|
804
|
+
case "off":
|
|
805
|
+
return [
|
|
806
|
+
"- GitHub access is unavailable in this session, so `gh` has no token. Local git work still works."
|
|
807
|
+
];
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
function afterPushRule(github) {
|
|
811
|
+
return github === "on" ? "- After the user confirms it is pushed, `git fetch` and continue. Open the pull request with `gh pr create --head <branch>` if asked to." : "- After the user confirms it is pushed, continue on the same branch. The user opens the pull request.";
|
|
812
|
+
}
|
|
733
813
|
function runnerContextFromEnv(env) {
|
|
734
814
|
return {
|
|
735
815
|
sessionId: env.MAESTTRO_SESSION_ID,
|
|
736
816
|
base: env.MAESTTRO_BASE,
|
|
737
817
|
repo: env.MAESTTRO_REPO,
|
|
738
|
-
|
|
818
|
+
github: githubMode(env.MAESTTRO_GITHUB)
|
|
739
819
|
};
|
|
740
820
|
}
|
|
741
821
|
function runnerInstructions(context) {
|
|
742
822
|
const base = context.base ?? "the default branch";
|
|
743
|
-
const github = context.githubAccess ? [
|
|
744
|
-
`- \`gh\` works for ${context.repo ?? "this repo"} only: issues, pull requests, comments. Its token is short-lived and refreshed for you.`,
|
|
745
|
-
"- Merging pull requests and deleting remote branches are not possible from here. A person merges."
|
|
746
|
-
] : [
|
|
747
|
-
"- GitHub access is unavailable in this session, so `gh` has no token. Local git work still works."
|
|
748
|
-
];
|
|
749
823
|
return [
|
|
750
824
|
"# Maesttro runner",
|
|
751
825
|
"",
|
|
@@ -762,11 +836,11 @@ function runnerInstructions(context) {
|
|
|
762
836
|
"",
|
|
763
837
|
"- `git push` does not work here: the runner has read-only access to the remote. This is deliberate.",
|
|
764
838
|
'- When a branch is ready, commit everything, then tell the user: "Ready to push: <branch>. Run `maesttro push <branch>` from the repository root."',
|
|
765
|
-
|
|
839
|
+
afterPushRule(context.github),
|
|
766
840
|
"",
|
|
767
841
|
"## GitHub",
|
|
768
842
|
"",
|
|
769
|
-
...
|
|
843
|
+
...githubRules(context),
|
|
770
844
|
""
|
|
771
845
|
].join(`
|
|
772
846
|
`);
|
|
@@ -1187,7 +1261,10 @@ var HELP = `maesttro — isolated runner for local coding agents
|
|
|
1187
1261
|
Usage:
|
|
1188
1262
|
maesttro Start a session from the repo root: new clone, default client (opencode)
|
|
1189
1263
|
maesttro <client> Same, with a specific client — see \`maesttro list\`
|
|
1190
|
-
maesttro
|
|
1264
|
+
maesttro --strict [client]
|
|
1265
|
+
Same, in strict mode: no GitHub token reaches the runner, so \`gh\` is off
|
|
1266
|
+
maesttro resume <id> Continue a kept session in its clone, in the mode it started with
|
|
1267
|
+
(\`--strict\` or \`--no-strict\` after the id changes it)
|
|
1191
1268
|
maesttro sessions List this repo's sessions (\`sessions remove <id>\` to drop one)
|
|
1192
1269
|
maesttro push [branch] Push a branch a session created, after showing what goes out
|
|
1193
1270
|
maesttro list Show available clients
|
|
@@ -1420,6 +1497,7 @@ function createSessionDir(parent = tmpdir2()) {
|
|
|
1420
1497
|
// src/github-session.ts
|
|
1421
1498
|
var UNAVAILABLE = "GitHub access unavailable";
|
|
1422
1499
|
var WITHOUT = "running without gh";
|
|
1500
|
+
var STRICT_NOTICE = "Strict mode: no GitHub token in the runner, so `gh` is off. Local git works; push with `maesttro push`.";
|
|
1423
1501
|
function refusalNotice(code, repo) {
|
|
1424
1502
|
switch (code) {
|
|
1425
1503
|
case "broker_unreachable":
|
|
@@ -1462,6 +1540,7 @@ function errorCode(error) {
|
|
|
1462
1540
|
function withoutGitHub(author, notice) {
|
|
1463
1541
|
const env = {
|
|
1464
1542
|
GH_CONFIG_DIR: CONTAINER_GH_CONFIG_DIR,
|
|
1543
|
+
GIT_TERMINAL_PROMPT: "0",
|
|
1465
1544
|
...identityEnv(author, undefined)
|
|
1466
1545
|
};
|
|
1467
1546
|
return {
|
|
@@ -1474,12 +1553,15 @@ function withoutGitHub(author, notice) {
|
|
|
1474
1553
|
async function openGitHubSession({
|
|
1475
1554
|
repo,
|
|
1476
1555
|
author,
|
|
1556
|
+
strict = false,
|
|
1477
1557
|
broker,
|
|
1478
1558
|
login,
|
|
1479
1559
|
log,
|
|
1480
1560
|
fetch: fetchImpl = fetch,
|
|
1481
1561
|
createDir = createSessionDir
|
|
1482
1562
|
}) {
|
|
1563
|
+
if (strict)
|
|
1564
|
+
return withoutGitHub(author, STRICT_NOTICE);
|
|
1483
1565
|
if (!repo)
|
|
1484
1566
|
return withoutGitHub(author);
|
|
1485
1567
|
const mint = async ({ interactive }) => {
|
|
@@ -2025,6 +2107,18 @@ Session ${session.id} kept. Continue with \`maesttro resume ${session.id}\`.`);
|
|
|
2025
2107
|
console.log("You have commits on a detached HEAD. Resume and `git switch -c <name>` to keep them.");
|
|
2026
2108
|
}
|
|
2027
2109
|
}
|
|
2110
|
+
function isStrict(requested, session) {
|
|
2111
|
+
return requested ?? session.strict ?? false;
|
|
2112
|
+
}
|
|
2113
|
+
function runnerGitHubMode({
|
|
2114
|
+
strict,
|
|
2115
|
+
repo,
|
|
2116
|
+
notice
|
|
2117
|
+
}) {
|
|
2118
|
+
if (strict)
|
|
2119
|
+
return "strict";
|
|
2120
|
+
return notice || !repo ? "off" : "on";
|
|
2121
|
+
}
|
|
2028
2122
|
function exitCodeOf(code, signal) {
|
|
2029
2123
|
if (code !== null)
|
|
2030
2124
|
return code;
|
|
@@ -2033,7 +2127,8 @@ function exitCodeOf(code, signal) {
|
|
|
2033
2127
|
var FORWARDED_SIGNALS = ["SIGINT", "SIGTERM", "SIGHUP"];
|
|
2034
2128
|
async function launch({
|
|
2035
2129
|
args,
|
|
2036
|
-
resume
|
|
2130
|
+
resume,
|
|
2131
|
+
strict: requestedStrict
|
|
2037
2132
|
}) {
|
|
2038
2133
|
const run = runCommand;
|
|
2039
2134
|
ensureReady();
|
|
@@ -2051,9 +2146,14 @@ async function launch({
|
|
|
2051
2146
|
refs: snapshotRefs(run, root),
|
|
2052
2147
|
sessionId: started.id
|
|
2053
2148
|
});
|
|
2054
|
-
const
|
|
2149
|
+
const strict = isStrict(requestedStrict, started);
|
|
2150
|
+
const saved = {
|
|
2151
|
+
...started,
|
|
2152
|
+
backup: started.backup ?? backup?.path,
|
|
2153
|
+
strict
|
|
2154
|
+
};
|
|
2055
2155
|
saveSession(saved);
|
|
2056
|
-
console.log(`Session ${saved.id}: clone .worktrees/${saved.id} at ${saved.base}${backup ? `, backup ${backup.reused ? "reused" : "written"}` : ""}.`);
|
|
2156
|
+
console.log(`Session ${saved.id}${strict ? " (strict)" : ""}: clone .worktrees/${saved.id} at ${saved.base}${backup ? `, backup ${backup.reused ? "reused" : "written"}` : ""}.`);
|
|
2057
2157
|
const repo = workspaceRepo(run, root);
|
|
2058
2158
|
if (repo && !resume) {
|
|
2059
2159
|
git(run, saved.worktree, [
|
|
@@ -2066,6 +2166,7 @@ async function launch({
|
|
|
2066
2166
|
const github = await openGitHubSession({
|
|
2067
2167
|
repo,
|
|
2068
2168
|
author: await resolveAuthor(root),
|
|
2169
|
+
strict,
|
|
2069
2170
|
broker: brokerUrl(),
|
|
2070
2171
|
login: {
|
|
2071
2172
|
clientId: githubAppClientId(),
|
|
@@ -2077,9 +2178,15 @@ async function launch({
|
|
|
2077
2178
|
if (github.notice)
|
|
2078
2179
|
console.log(github.notice);
|
|
2079
2180
|
const hostHome = homedir4();
|
|
2181
|
+
const url = maesttroUrl();
|
|
2182
|
+
const orchestratorWindow = await fetchOrchestratorWindow({ url });
|
|
2183
|
+
if (orchestratorWindow === undefined) {
|
|
2184
|
+
console.log("Couldn't read Maesttro's context window; Claude Code will assume its default.");
|
|
2185
|
+
}
|
|
2080
2186
|
const clientConfigs = buildClientConfigs({
|
|
2081
2187
|
host: readHostClientConfigs({ home: hostHome }),
|
|
2082
|
-
url
|
|
2188
|
+
url,
|
|
2189
|
+
orchestratorWindow
|
|
2083
2190
|
});
|
|
2084
2191
|
if (clientConfigs.removed.length > 0) {
|
|
2085
2192
|
console.log(`Kept out of the runner as secrets: ${clientConfigs.removed.join(", ")}`);
|
|
@@ -2098,7 +2205,7 @@ async function launch({
|
|
|
2098
2205
|
...github.env,
|
|
2099
2206
|
MAESTTRO_SESSION_ID: saved.id,
|
|
2100
2207
|
MAESTTRO_BASE: saved.base,
|
|
2101
|
-
MAESTTRO_GITHUB:
|
|
2208
|
+
MAESTTRO_GITHUB: runnerGitHubMode({ strict, repo, notice: github.notice }),
|
|
2102
2209
|
...hostIdentityEnv(),
|
|
2103
2210
|
...repo ? { MAESTTRO_REPO: repo } : {}
|
|
2104
2211
|
};
|
|
@@ -2287,7 +2394,8 @@ function sessionsCommand({ args }) {
|
|
|
2287
2394
|
for (const session of sessions) {
|
|
2288
2395
|
const branches = existsSync5(session.worktree) ? cloneBranches(session.worktree) : undefined;
|
|
2289
2396
|
const where = branches === undefined ? "clone missing" : branches.length > 0 ? `branches: ${branches.join(", ")}` : "no branches yet";
|
|
2290
|
-
|
|
2397
|
+
const mode = session.strict ? " strict" : "";
|
|
2398
|
+
console.log(` ${session.id} ${session.createdAt.slice(0, 16)} ${where}${mode}`);
|
|
2291
2399
|
}
|
|
2292
2400
|
console.log("\n`maesttro resume <id>` continues one; `maesttro sessions remove <id>` removes its clone.");
|
|
2293
2401
|
}
|
|
@@ -2369,11 +2477,48 @@ var defaultCommands = {
|
|
|
2369
2477
|
status,
|
|
2370
2478
|
print: (text) => console.log(text)
|
|
2371
2479
|
};
|
|
2480
|
+
var MODE_FLAGS = {
|
|
2481
|
+
"--strict": true,
|
|
2482
|
+
"--no-strict": false
|
|
2483
|
+
};
|
|
2484
|
+
function takeModeFlags(args) {
|
|
2485
|
+
let strict;
|
|
2486
|
+
let index = 0;
|
|
2487
|
+
while (index < args.length && Object.hasOwn(MODE_FLAGS, args[index] ?? "")) {
|
|
2488
|
+
strict = MODE_FLAGS[args[index] ?? ""];
|
|
2489
|
+
index++;
|
|
2490
|
+
}
|
|
2491
|
+
return {
|
|
2492
|
+
...strict === undefined ? {} : { strict },
|
|
2493
|
+
rest: args.slice(index)
|
|
2494
|
+
};
|
|
2495
|
+
}
|
|
2496
|
+
var NOT_LAUNCHING = new Set([
|
|
2497
|
+
"setup",
|
|
2498
|
+
"update",
|
|
2499
|
+
"doctor",
|
|
2500
|
+
"login",
|
|
2501
|
+
"logout",
|
|
2502
|
+
"status",
|
|
2503
|
+
"push",
|
|
2504
|
+
"sessions",
|
|
2505
|
+
"configure",
|
|
2506
|
+
"list",
|
|
2507
|
+
"help",
|
|
2508
|
+
"--help",
|
|
2509
|
+
"-h"
|
|
2510
|
+
]);
|
|
2372
2511
|
async function main(argv, commands = defaultCommands) {
|
|
2373
|
-
const
|
|
2512
|
+
const leading = takeModeFlags(argv);
|
|
2513
|
+
const [command, ...rest] = leading.rest;
|
|
2514
|
+
const mode = leading.strict === undefined ? {} : { strict: leading.strict };
|
|
2515
|
+
if (leading.strict !== undefined && command !== undefined && NOT_LAUNCHING.has(command)) {
|
|
2516
|
+
commands.print(`--strict and --no-strict apply to launching a session, not to \`${command}\`.`);
|
|
2517
|
+
return;
|
|
2518
|
+
}
|
|
2374
2519
|
switch (command) {
|
|
2375
2520
|
case undefined:
|
|
2376
|
-
await commands.launch({ args: [] });
|
|
2521
|
+
await commands.launch({ args: [], ...mode });
|
|
2377
2522
|
return;
|
|
2378
2523
|
case "setup":
|
|
2379
2524
|
await commands.setup({ args: rest });
|
|
@@ -2400,12 +2545,19 @@ async function main(argv, commands = defaultCommands) {
|
|
|
2400
2545
|
await commands.sessions({ args: rest });
|
|
2401
2546
|
return;
|
|
2402
2547
|
case "resume": {
|
|
2403
|
-
const
|
|
2548
|
+
const beforeId = takeModeFlags(rest);
|
|
2549
|
+
const [id, ...afterId] = beforeId.rest;
|
|
2550
|
+
const afterIdFlags = takeModeFlags(afterId);
|
|
2551
|
+
const strict = afterIdFlags.strict ?? beforeId.strict ?? leading.strict;
|
|
2404
2552
|
if (!id) {
|
|
2405
|
-
commands.print("Usage: maesttro resume <session-id> [client] [args...]");
|
|
2553
|
+
commands.print("Usage: maesttro resume <session-id> [--strict|--no-strict] [client] [args...]");
|
|
2406
2554
|
return;
|
|
2407
2555
|
}
|
|
2408
|
-
await commands.launch({
|
|
2556
|
+
await commands.launch({
|
|
2557
|
+
args: afterIdFlags.rest,
|
|
2558
|
+
resume: id,
|
|
2559
|
+
...strict === undefined ? {} : { strict }
|
|
2560
|
+
});
|
|
2409
2561
|
return;
|
|
2410
2562
|
}
|
|
2411
2563
|
case "configure":
|
|
@@ -2420,7 +2572,7 @@ async function main(argv, commands = defaultCommands) {
|
|
|
2420
2572
|
commands.print(HELP);
|
|
2421
2573
|
return;
|
|
2422
2574
|
default:
|
|
2423
|
-
await commands.launch({ args:
|
|
2575
|
+
await commands.launch({ args: leading.rest, ...mode });
|
|
2424
2576
|
}
|
|
2425
2577
|
}
|
|
2426
2578
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "maesttro",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Isolated Docker runner for local coding agents (opencode, claude, ...) under a dedicated bot GitHub identity, with curated configs baked in per client.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|