balladeer 1.0.7 → 1.0.11
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/dist/agent.d.ts +1 -1
- package/dist/agent.js +12 -0
- package/dist/cli.d.ts +3 -1
- package/dist/cli.js +127 -15
- package/dist/commands/discover.js +2 -2
- package/dist/commands/guidance.d.ts +12 -0
- package/dist/commands/guidance.js +45 -5
- package/dist/commands/mcp.js +45 -3
- package/dist/commands/prepare.js +2 -2
- package/dist/commands/propose.js +2 -2
- package/dist/commands/session.js +2 -2
- package/dist/commands/setup.d.ts +15 -0
- package/dist/commands/setup.js +51 -15
- package/dist/commands/status.js +2 -2
- package/dist/currency.d.ts +3 -0
- package/dist/currency.js +8 -0
- package/dist/guidance-hook.mjs +200 -5
- package/dist/guidance.js +4 -0
- package/dist/install.d.ts +4 -0
- package/dist/install.js +9 -1
- package/dist/open-browser.d.ts +13 -0
- package/dist/open-browser.js +30 -0
- package/dist/quiet.d.ts +25 -0
- package/dist/quiet.js +73 -0
- package/dist/release.d.ts +6 -0
- package/dist/release.js +6 -0
- package/dist/remove-earlier.d.ts +68 -0
- package/dist/remove-earlier.js +378 -0
- package/dist/repository.d.ts +9 -0
- package/dist/repository.js +31 -5
- package/dist/self-update.d.ts +11 -0
- package/dist/self-update.js +59 -0
- package/dist/user-scope.d.ts +71 -0
- package/dist/user-scope.js +267 -0
- package/dist/wire.d.ts +15 -2
- package/dist/wire.js +1 -1
- package/package.json +3 -2
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Install Balladeer once per laptop.
|
|
3
|
+
*
|
|
4
|
+
* Setup used to write two files into one repository checkout, uncommitted: an
|
|
5
|
+
* `.mcp.json` entry and a hook in `.claude/settings.json`. A coding host lists
|
|
6
|
+
* a project server only when that exact folder is open, so every other clone,
|
|
7
|
+
* every worktree and every teammate saw nothing, and `/mcp` on two laptops
|
|
8
|
+
* showed only the legacy server. This registers the same server and the same
|
|
9
|
+
* hook at the host's user scope instead: one entry that runs in every session,
|
|
10
|
+
* and decides at run time, from the folder's git remote, which connection it
|
|
11
|
+
* is for. Nothing to commit and nothing per folder.
|
|
12
|
+
*
|
|
13
|
+
* Every write here is an additive merge that refuses to touch an entry it does
|
|
14
|
+
* not own, exactly as the project writers do. What it owns is recognised by
|
|
15
|
+
* shape, never by trust in the name.
|
|
16
|
+
*/
|
|
17
|
+
export declare const USER_SCOPE_OWNER = "Balladeer current guidance (user scope, loader 1)";
|
|
18
|
+
export type UserScopeHost = "claude" | "codex";
|
|
19
|
+
export type UserScopeWrite = Readonly<{
|
|
20
|
+
host: UserScopeHost;
|
|
21
|
+
path: string;
|
|
22
|
+
status: "current" | "written" | "refused";
|
|
23
|
+
reason?: string;
|
|
24
|
+
}>;
|
|
25
|
+
/**
|
|
26
|
+
* The command a host runs to reach Balladeer, with no repository named: the
|
|
27
|
+
* directory decides.
|
|
28
|
+
*
|
|
29
|
+
* With a durable command installed, the entries name it by absolute path:
|
|
30
|
+
* no npx, so a session start costs node's startup and nothing else, and no
|
|
31
|
+
* PATH, so a host launched from the dock finds it. A laptop where the durable
|
|
32
|
+
* install was refused gets the `npx` form on the current major, which works
|
|
33
|
+
* everywhere and costs a registry lookup per session. The checkout form
|
|
34
|
+
* exists so this repository's own tests can point a host at source.
|
|
35
|
+
*/
|
|
36
|
+
export declare function userScopeCommand(published: boolean, installed?: string): {
|
|
37
|
+
command: string;
|
|
38
|
+
args: string[];
|
|
39
|
+
};
|
|
40
|
+
/** `~/.claude.json` carries the user-scope MCP list under `mcpServers`. */
|
|
41
|
+
export declare function mergeClaudeUserMcp(home: string, published: boolean, installed?: string): UserScopeWrite;
|
|
42
|
+
/** Ours when it runs `npx` on our published specifier, or `node` on this
|
|
43
|
+
* checkout's entry file, with `mcp` as the subcommand and no repository named:
|
|
44
|
+
* a repository argument at user scope would pin every folder to one connection. */
|
|
45
|
+
export declare function isOurUserEntry(entry: unknown): boolean;
|
|
46
|
+
/**
|
|
47
|
+
* Where `ensureInstalled` puts the durable command: Volta's shim directory, an
|
|
48
|
+
* npm global prefix's `bin` with the package beside it under `lib/node_modules`,
|
|
49
|
+
* or this product's own fallback prefix under the person's home.
|
|
50
|
+
*/
|
|
51
|
+
export declare function isInstalledCommandPath(command: string): boolean;
|
|
52
|
+
/** `~/.claude/settings.json` carries user-scope hooks. */
|
|
53
|
+
export declare function mergeClaudeUserHooks(home: string, published: boolean, installed?: string): UserScopeWrite;
|
|
54
|
+
/** `~/.codex/config.toml` carries both the server and the hooks for Codex, in
|
|
55
|
+
* one fenced block this command owns end to end. */
|
|
56
|
+
export declare function mergeCodexUserConfig(home: string, published: boolean, installed?: string): UserScopeWrite;
|
|
57
|
+
/** A copy of this command running out of a source checkout registers the
|
|
58
|
+
* checkout; any installed or npx copy registers the published package. */
|
|
59
|
+
export declare function runningFromCheckout(entry?: string): boolean;
|
|
60
|
+
/** The home whose host files get the entries. `BALLADEER_USER_HOME` exists for
|
|
61
|
+
* this repository's own tests, which cannot move HOME under a Volta-managed
|
|
62
|
+
* Node without losing Node. */
|
|
63
|
+
export declare function userHome(environment?: NodeJS.ProcessEnv): string;
|
|
64
|
+
/** Register both hosts. Codex is skipped, not refused, on a laptop without it. */
|
|
65
|
+
export declare function installUserScope(options: {
|
|
66
|
+
environment: NodeJS.ProcessEnv;
|
|
67
|
+
published: boolean;
|
|
68
|
+
home?: string;
|
|
69
|
+
/** The durable command's absolute path, when one is installed. */
|
|
70
|
+
installed?: string;
|
|
71
|
+
}): UserScopeWrite[];
|
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
import { existsSync, lstatSync, mkdirSync, readFileSync } from "node:fs";
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import { basename, dirname, isAbsolute, join, resolve } from "node:path";
|
|
4
|
+
import { parse } from "@iarna/toml";
|
|
5
|
+
import { HOOK_SPECIFIER, checkoutEntryPath } from "./release.js";
|
|
6
|
+
import { writeJsonAtomically } from "./mcp-config.js";
|
|
7
|
+
/**
|
|
8
|
+
* Install Balladeer once per laptop.
|
|
9
|
+
*
|
|
10
|
+
* Setup used to write two files into one repository checkout, uncommitted: an
|
|
11
|
+
* `.mcp.json` entry and a hook in `.claude/settings.json`. A coding host lists
|
|
12
|
+
* a project server only when that exact folder is open, so every other clone,
|
|
13
|
+
* every worktree and every teammate saw nothing, and `/mcp` on two laptops
|
|
14
|
+
* showed only the legacy server. This registers the same server and the same
|
|
15
|
+
* hook at the host's user scope instead: one entry that runs in every session,
|
|
16
|
+
* and decides at run time, from the folder's git remote, which connection it
|
|
17
|
+
* is for. Nothing to commit and nothing per folder.
|
|
18
|
+
*
|
|
19
|
+
* Every write here is an additive merge that refuses to touch an entry it does
|
|
20
|
+
* not own, exactly as the project writers do. What it owns is recognised by
|
|
21
|
+
* shape, never by trust in the name.
|
|
22
|
+
*/
|
|
23
|
+
export const USER_SCOPE_OWNER = "Balladeer current guidance (user scope, loader 1)";
|
|
24
|
+
const CLAUDE_EVENTS = ["SessionStart", "UserPromptSubmit", "SubagentStart"];
|
|
25
|
+
/**
|
|
26
|
+
* The command a host runs to reach Balladeer, with no repository named: the
|
|
27
|
+
* directory decides.
|
|
28
|
+
*
|
|
29
|
+
* With a durable command installed, the entries name it by absolute path:
|
|
30
|
+
* no npx, so a session start costs node's startup and nothing else, and no
|
|
31
|
+
* PATH, so a host launched from the dock finds it. A laptop where the durable
|
|
32
|
+
* install was refused gets the `npx` form on the current major, which works
|
|
33
|
+
* everywhere and costs a registry lookup per session. The checkout form
|
|
34
|
+
* exists so this repository's own tests can point a host at source.
|
|
35
|
+
*/
|
|
36
|
+
export function userScopeCommand(published, installed) {
|
|
37
|
+
if (installed !== undefined)
|
|
38
|
+
return { command: installed, args: [] };
|
|
39
|
+
return published
|
|
40
|
+
? { command: "npx", args: ["-y", HOOK_SPECIFIER] }
|
|
41
|
+
: { command: "node", args: [checkoutEntryPath()] };
|
|
42
|
+
}
|
|
43
|
+
function ordinaryFile(path) {
|
|
44
|
+
return !existsSync(path) || (lstatSync(path).isFile() && !lstatSync(path).isSymbolicLink());
|
|
45
|
+
}
|
|
46
|
+
/** `~/.claude.json` carries the user-scope MCP list under `mcpServers`. */
|
|
47
|
+
export function mergeClaudeUserMcp(home, published, installed) {
|
|
48
|
+
const path = join(home, ".claude.json");
|
|
49
|
+
const host = "claude";
|
|
50
|
+
if (!ordinaryFile(path))
|
|
51
|
+
return { host, path, status: "refused", reason: "not an ordinary file" };
|
|
52
|
+
let root = {};
|
|
53
|
+
if (existsSync(path)) {
|
|
54
|
+
try {
|
|
55
|
+
const parsed = JSON.parse(readFileSync(path, "utf8"));
|
|
56
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
|
|
57
|
+
throw new Error();
|
|
58
|
+
root = parsed;
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
return { host, path, status: "refused", reason: "could not read valid JSON" };
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
const servers = root.mcpServers && typeof root.mcpServers === "object" && !Array.isArray(root.mcpServers)
|
|
65
|
+
? root.mcpServers
|
|
66
|
+
: {};
|
|
67
|
+
const { command, args } = userScopeCommand(published, installed);
|
|
68
|
+
const expected = { type: "stdio", command, args: [...args, "mcp"] };
|
|
69
|
+
const current = servers.balladeer;
|
|
70
|
+
if (current !== undefined && !isOurUserEntry(current)) {
|
|
71
|
+
return {
|
|
72
|
+
host,
|
|
73
|
+
path,
|
|
74
|
+
status: "refused",
|
|
75
|
+
reason: "an entry named balladeer that is not Balladeer's is already there",
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
if (current !== undefined && JSON.stringify(current) === JSON.stringify(expected))
|
|
79
|
+
return { host, path, status: "current" };
|
|
80
|
+
root.mcpServers = { ...servers, balladeer: expected };
|
|
81
|
+
writeJsonAtomically(path, JSON.stringify(root, null, 2) + "\n");
|
|
82
|
+
return { host, path, status: "written" };
|
|
83
|
+
}
|
|
84
|
+
/** Ours when it runs `npx` on our published specifier, or `node` on this
|
|
85
|
+
* checkout's entry file, with `mcp` as the subcommand and no repository named:
|
|
86
|
+
* a repository argument at user scope would pin every folder to one connection. */
|
|
87
|
+
export function isOurUserEntry(entry) {
|
|
88
|
+
if (!entry || typeof entry !== "object")
|
|
89
|
+
return false;
|
|
90
|
+
const record = entry;
|
|
91
|
+
if (!Array.isArray(record.args) || record.args.some((a) => typeof a !== "string"))
|
|
92
|
+
return false;
|
|
93
|
+
const args = record.args;
|
|
94
|
+
if (record.command === "npx")
|
|
95
|
+
return (args.length === 3 &&
|
|
96
|
+
args[0] === "-y" &&
|
|
97
|
+
/^balladeer@(?:latest|1|\d+\.\d+\.\d+)$/.test(args[1]) &&
|
|
98
|
+
args[2] === "mcp");
|
|
99
|
+
if (record.command === "node" || String(record.command).endsWith("/node"))
|
|
100
|
+
return args.length === 2 && args[0] === checkoutEntryPath() && args[1] === "mcp";
|
|
101
|
+
// The durable command by absolute path: ours when it sits where this
|
|
102
|
+
// product's installer puts it and is asked for `mcp` alone. The July client
|
|
103
|
+
// also registered an absolute `balladeer mcp`, from its own home, so the
|
|
104
|
+
// name is not enough; the place is what says whose it is.
|
|
105
|
+
if (typeof record.command === "string" && isAbsolute(record.command))
|
|
106
|
+
return isInstalledCommandPath(record.command) && args.length === 1 && args[0] === "mcp";
|
|
107
|
+
return false;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Where `ensureInstalled` puts the durable command: Volta's shim directory, an
|
|
111
|
+
* npm global prefix's `bin` with the package beside it under `lib/node_modules`,
|
|
112
|
+
* or this product's own fallback prefix under the person's home.
|
|
113
|
+
*/
|
|
114
|
+
export function isInstalledCommandPath(command) {
|
|
115
|
+
if (basename(command) !== "balladeer")
|
|
116
|
+
return false;
|
|
117
|
+
const normalized = command.replaceAll("\\", "/");
|
|
118
|
+
if (/\/\.volta\/bin\/balladeer$/.test(normalized))
|
|
119
|
+
return true;
|
|
120
|
+
if (/\/\.local\/share\/balladeer\/npm\/bin\/balladeer$/.test(normalized))
|
|
121
|
+
return true;
|
|
122
|
+
const bin = dirname(command);
|
|
123
|
+
return (basename(bin) === "bin" &&
|
|
124
|
+
existsSync(join(dirname(bin), "lib", "node_modules", "balladeer", "package.json")));
|
|
125
|
+
}
|
|
126
|
+
/** `~/.claude/settings.json` carries user-scope hooks. */
|
|
127
|
+
export function mergeClaudeUserHooks(home, published, installed) {
|
|
128
|
+
const path = join(home, ".claude", "settings.json");
|
|
129
|
+
const host = "claude";
|
|
130
|
+
if (!ordinaryFile(path))
|
|
131
|
+
return { host, path, status: "refused", reason: "not an ordinary file" };
|
|
132
|
+
let root = {};
|
|
133
|
+
if (existsSync(path)) {
|
|
134
|
+
try {
|
|
135
|
+
const parsed = JSON.parse(readFileSync(path, "utf8"));
|
|
136
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
|
|
137
|
+
throw new Error();
|
|
138
|
+
root = parsed;
|
|
139
|
+
}
|
|
140
|
+
catch {
|
|
141
|
+
return { host, path, status: "refused", reason: "could not read valid JSON" };
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
const hooks = root.hooks && typeof root.hooks === "object" && !Array.isArray(root.hooks)
|
|
145
|
+
? { ...root.hooks }
|
|
146
|
+
: {};
|
|
147
|
+
const { command, args } = userScopeCommand(published, installed);
|
|
148
|
+
const line = [command, ...args, "guidance", "--hook", "claude"].join(" ");
|
|
149
|
+
let changed = false;
|
|
150
|
+
for (const event of CLAUDE_EVENTS) {
|
|
151
|
+
const rows = hooks[event] === undefined ? [] : hooks[event];
|
|
152
|
+
if (!Array.isArray(rows))
|
|
153
|
+
return { host, path, status: "refused", reason: `hooks.${event} is not a list` };
|
|
154
|
+
const own = rows.filter((row) => row?.hooks?.some((h) => h.statusMessage === USER_SCOPE_OWNER));
|
|
155
|
+
const expected = {
|
|
156
|
+
hooks: [
|
|
157
|
+
{
|
|
158
|
+
type: "command",
|
|
159
|
+
command: `${line} --event ${event}`,
|
|
160
|
+
// npx resolves from its cache in well under this; the first run on
|
|
161
|
+
// a laptop that has never fetched the package needs the network.
|
|
162
|
+
timeout: 10,
|
|
163
|
+
statusMessage: USER_SCOPE_OWNER,
|
|
164
|
+
},
|
|
165
|
+
],
|
|
166
|
+
};
|
|
167
|
+
if (own.length > 1)
|
|
168
|
+
return { host, path, status: "refused", reason: `two Balladeer hooks under ${event}` };
|
|
169
|
+
if (own.length === 1 && JSON.stringify(own[0]) === JSON.stringify(expected))
|
|
170
|
+
continue;
|
|
171
|
+
hooks[event] = [...rows.filter((row) => !own.includes(row)), expected];
|
|
172
|
+
changed = true;
|
|
173
|
+
}
|
|
174
|
+
if (!changed)
|
|
175
|
+
return { host, path, status: "current" };
|
|
176
|
+
root.hooks = hooks;
|
|
177
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
178
|
+
writeJsonAtomically(path, JSON.stringify(root, null, 2) + "\n");
|
|
179
|
+
return { host, path, status: "written" };
|
|
180
|
+
}
|
|
181
|
+
const CODEX_START = "# balladeer:user:start";
|
|
182
|
+
const CODEX_END = "# balladeer:user:end";
|
|
183
|
+
/** `~/.codex/config.toml` carries both the server and the hooks for Codex, in
|
|
184
|
+
* one fenced block this command owns end to end. */
|
|
185
|
+
export function mergeCodexUserConfig(home, published, installed) {
|
|
186
|
+
const path = join(home, ".codex", "config.toml");
|
|
187
|
+
const host = "codex";
|
|
188
|
+
if (!ordinaryFile(path))
|
|
189
|
+
return { host, path, status: "refused", reason: "not an ordinary file" };
|
|
190
|
+
let previous = "";
|
|
191
|
+
try {
|
|
192
|
+
if (existsSync(path))
|
|
193
|
+
previous = readFileSync(path, "utf8");
|
|
194
|
+
parse(previous);
|
|
195
|
+
}
|
|
196
|
+
catch {
|
|
197
|
+
return { host, path, status: "refused", reason: "could not read valid TOML" };
|
|
198
|
+
}
|
|
199
|
+
const { command, args } = userScopeCommand(published, installed);
|
|
200
|
+
const line = [command, ...args, "guidance", "--hook", "codex"].join(" ");
|
|
201
|
+
const block = [
|
|
202
|
+
CODEX_START,
|
|
203
|
+
"[mcp_servers.balladeer]",
|
|
204
|
+
`command = ${JSON.stringify(command)}`,
|
|
205
|
+
`args = ${JSON.stringify([...args, "mcp"])}`,
|
|
206
|
+
...CLAUDE_EVENTS.flatMap((event) => [
|
|
207
|
+
`[[hooks.${event}]]`,
|
|
208
|
+
`[[hooks.${event}.hooks]]`,
|
|
209
|
+
'type = "command"',
|
|
210
|
+
`command = ${JSON.stringify(`${line} --event ${event}`)}`,
|
|
211
|
+
"timeout = 10",
|
|
212
|
+
"additionalContextLimit = 0",
|
|
213
|
+
]),
|
|
214
|
+
CODEX_END,
|
|
215
|
+
].join("\n");
|
|
216
|
+
const starts = [...previous.matchAll(/^# balladeer:user:start\r?$/gm)];
|
|
217
|
+
const ends = [...previous.matchAll(/^# balladeer:user:end\r?$/gm)];
|
|
218
|
+
if (starts.length !== ends.length || starts.length > 1)
|
|
219
|
+
return { host, path, status: "refused", reason: "the Balladeer block is malformed" };
|
|
220
|
+
if (starts[0] && ends[0]) {
|
|
221
|
+
if (starts[0].index >= ends[0].index)
|
|
222
|
+
return { host, path, status: "refused", reason: "the Balladeer block is malformed" };
|
|
223
|
+
const old = previous.slice(starts[0].index, ends[0].index + CODEX_END.length);
|
|
224
|
+
if (old === block)
|
|
225
|
+
return { host, path, status: "current" };
|
|
226
|
+
const after = previous.slice(0, starts[0].index) + block + previous.slice(ends[0].index + CODEX_END.length);
|
|
227
|
+
parse(after);
|
|
228
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
229
|
+
writeJsonAtomically(path, after);
|
|
230
|
+
return { host, path, status: "written" };
|
|
231
|
+
}
|
|
232
|
+
if (/^\[mcp_servers\.balladeer\]/m.test(previous))
|
|
233
|
+
return {
|
|
234
|
+
host,
|
|
235
|
+
path,
|
|
236
|
+
status: "refused",
|
|
237
|
+
reason: "an mcp_servers.balladeer table that is not Balladeer's is already there",
|
|
238
|
+
};
|
|
239
|
+
const after = previous + (previous.endsWith("\n") || previous === "" ? "" : "\n") + block + "\n";
|
|
240
|
+
parse(after);
|
|
241
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
242
|
+
writeJsonAtomically(path, after);
|
|
243
|
+
return { host, path, status: "written" };
|
|
244
|
+
}
|
|
245
|
+
/** A copy of this command running out of a source checkout registers the
|
|
246
|
+
* checkout; any installed or npx copy registers the published package. */
|
|
247
|
+
export function runningFromCheckout(entry = process.argv[1] ?? "") {
|
|
248
|
+
const normalized = entry.replaceAll("\\", "/");
|
|
249
|
+
return normalized.includes("/packages/cli/dist/") && !normalized.includes("/node_modules/");
|
|
250
|
+
}
|
|
251
|
+
/** The home whose host files get the entries. `BALLADEER_USER_HOME` exists for
|
|
252
|
+
* this repository's own tests, which cannot move HOME under a Volta-managed
|
|
253
|
+
* Node without losing Node. */
|
|
254
|
+
export function userHome(environment = process.env) {
|
|
255
|
+
return resolve(environment.BALLADEER_USER_HOME?.trim() || environment.HOME?.trim() || homedir());
|
|
256
|
+
}
|
|
257
|
+
/** Register both hosts. Codex is skipped, not refused, on a laptop without it. */
|
|
258
|
+
export function installUserScope(options) {
|
|
259
|
+
const home = options.home ?? userHome(options.environment);
|
|
260
|
+
const writes = [
|
|
261
|
+
mergeClaudeUserMcp(home, options.published, options.installed),
|
|
262
|
+
mergeClaudeUserHooks(home, options.published, options.installed),
|
|
263
|
+
];
|
|
264
|
+
if (existsSync(join(home, ".codex")))
|
|
265
|
+
writes.push(mergeCodexUserConfig(home, options.published, options.installed));
|
|
266
|
+
return writes;
|
|
267
|
+
}
|
package/dist/wire.d.ts
CHANGED
|
@@ -5,10 +5,10 @@
|
|
|
5
5
|
* runtime dependency at all: Node 22 builtins and global fetch, nothing else.
|
|
6
6
|
* A contract test compares the scope list below against the server's.
|
|
7
7
|
*/
|
|
8
|
-
export declare const CLI_VERSION = "1.0.
|
|
8
|
+
export declare const CLI_VERSION = "1.0.11";
|
|
9
9
|
export declare const CLI_INVOCATION = "npx -y balladeer@latest";
|
|
10
10
|
export declare const CLIENT_HEADER = "x-balladeer-client";
|
|
11
|
-
export declare const CLIENT_HEADER_VALUE = "balladeer/1.0.
|
|
11
|
+
export declare const CLIENT_HEADER_VALUE = "balladeer/1.0.11";
|
|
12
12
|
export declare const DEFAULT_CONTROL_PLANE = "https://envelopes.balladeer.ai";
|
|
13
13
|
export type DelegatedScope = "repository:enroll" | "agent:issue" | "ci:connect" | "workspace:invite" | "candidate:propose";
|
|
14
14
|
export declare const DELEGATED_SCOPES: readonly DelegatedScope[];
|
|
@@ -222,6 +222,19 @@ export type ClaudeDesktopStep = Readonly<{
|
|
|
222
222
|
reason?: string;
|
|
223
223
|
}>;
|
|
224
224
|
export type JsonStep = Readonly<{
|
|
225
|
+
/** The earlier Balladeer found on this machine and what setup did about it. */
|
|
226
|
+
step: "earlier";
|
|
227
|
+
removed: readonly {
|
|
228
|
+
path: string;
|
|
229
|
+
found: string;
|
|
230
|
+
backup?: string;
|
|
231
|
+
}[];
|
|
232
|
+
failed: readonly {
|
|
233
|
+
path: string;
|
|
234
|
+
found: string;
|
|
235
|
+
reason: string;
|
|
236
|
+
}[];
|
|
237
|
+
}> | Readonly<{
|
|
225
238
|
step: "guidance_loader";
|
|
226
239
|
status: "installed_needs_host_trust" | "installed_trust_unverified" | "unavailable" | "not_applicable";
|
|
227
240
|
changed: boolean;
|
package/dist/wire.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* runtime dependency at all: Node 22 builtins and global fetch, nothing else.
|
|
6
6
|
* A contract test compares the scope list below against the server's.
|
|
7
7
|
*/
|
|
8
|
-
export const CLI_VERSION = "1.0.
|
|
8
|
+
export const CLI_VERSION = "1.0.11";
|
|
9
9
|
export const CLI_INVOCATION = "npx -y balladeer@latest";
|
|
10
10
|
export const CLIENT_HEADER = "x-balladeer-client";
|
|
11
11
|
export const CLIENT_HEADER_VALUE = `balladeer/${CLI_VERSION}`;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "balladeer",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.11",
|
|
4
4
|
"description": "Set up Balladeer from your terminal, or from a coding agent's.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"private": false,
|
|
@@ -22,7 +22,8 @@
|
|
|
22
22
|
},
|
|
23
23
|
"scripts": {
|
|
24
24
|
"build": "tsc -p tsconfig.json && node build-guidance.mjs",
|
|
25
|
-
"typecheck": "tsc -p tsconfig.json --noEmit"
|
|
25
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
26
|
+
"prepublishOnly": "pnpm --dir ../.. compat:release"
|
|
26
27
|
},
|
|
27
28
|
"devDependencies": {
|
|
28
29
|
"typescript": "5.9.3",
|