@heyditto/cli 3.8.1 → 3.10.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 +44 -3
- package/dist/agents/codexapp.js +5 -4
- package/dist/agents/codexapp.js.map +1 -1
- package/dist/agents/launch.js +17 -7
- package/dist/agents/launch.js.map +1 -1
- package/dist/cli.js +17 -6
- package/dist/cli.js.map +1 -1
- package/dist/commands.js +41 -38
- package/dist/commands.js.map +1 -1
- package/dist/profile-state.d.ts +58 -0
- package/dist/profile-state.js +86 -0
- package/dist/profile-state.js.map +1 -0
- package/dist/profiles.d.ts +73 -0
- package/dist/profiles.js +325 -0
- package/dist/profiles.js.map +1 -0
- package/dist/remote/plugin-server.js +2 -2
- package/dist/remote/plugin-server.js.map +1 -1
- package/dist/review-commands.d.ts +4 -0
- package/dist/review-commands.js +87 -8
- package/dist/review-commands.js.map +1 -1
- package/dist/review-recommendation.d.ts +11 -0
- package/dist/review-recommendation.js +37 -0
- package/dist/review-recommendation.js.map +1 -1
- package/dist/review-trace.d.ts +22 -0
- package/dist/review-trace.js +265 -0
- package/dist/review-trace.js.map +1 -0
- package/dist/store.d.ts +10 -1
- package/dist/store.js +7 -2
- package/dist/store.js.map +1 -1
- package/dist/t3/setup.d.ts +27 -0
- package/dist/t3/setup.js +65 -8
- package/dist/t3/setup.js.map +1 -1
- package/dist/update-lock.json +1 -1
- package/docs/t3-code.md +44 -0
- package/package.json +1 -1
package/dist/profiles.js
ADDED
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `heyditto profile`: named endpoint profiles, and the one place the CLI asks
|
|
3
|
+
* "which endpoint does the user mean when they did not say?".
|
|
4
|
+
*
|
|
5
|
+
* Selection order for a command that reads the default endpoint:
|
|
6
|
+
* --profile <name> (this command only)
|
|
7
|
+
* DITTO_PROFILE (this shell only)
|
|
8
|
+
* the active profile (`heyditto profile use <name>`)
|
|
9
|
+
* With no saved profiles, the implicit `default` profile is `defaultEndpoint`,
|
|
10
|
+
* so an existing install behaves exactly as before.
|
|
11
|
+
*/
|
|
12
|
+
import { Option } from "commander";
|
|
13
|
+
import { findEndpoint, listEndpoints } from "./api.js";
|
|
14
|
+
import { LEGACY_PROFILE, materialize, profileView, profilesPatch, setProfileEndpointPatch, unknownProfileMessage, validateProfileName, } from "./profile-state.js";
|
|
15
|
+
import { readStoredAuth, updateStoredAuth } from "./store.js";
|
|
16
|
+
let flagOverride;
|
|
17
|
+
/** Set by `--profile` for the duration of one command. */
|
|
18
|
+
export function setProfileOverride(name) {
|
|
19
|
+
flagOverride = name?.trim() || undefined;
|
|
20
|
+
}
|
|
21
|
+
/** The profile the user named for this command (flag, then env), if any. */
|
|
22
|
+
export function requestedProfile() {
|
|
23
|
+
if (flagOverride)
|
|
24
|
+
return { name: flagOverride, source: "flag" };
|
|
25
|
+
const env = process.env.DITTO_PROFILE?.trim();
|
|
26
|
+
return env ? { name: env, source: "env" } : undefined;
|
|
27
|
+
}
|
|
28
|
+
function select(stored, view = profileView(stored)) {
|
|
29
|
+
const requested = requestedProfile();
|
|
30
|
+
if (requested) {
|
|
31
|
+
const profile = view.profiles[requested.name];
|
|
32
|
+
if (!profile) {
|
|
33
|
+
const via = requested.source === "env" ? " (from DITTO_PROFILE)" : "";
|
|
34
|
+
throw new Error(unknownProfileMessage(requested.name, view) + via);
|
|
35
|
+
}
|
|
36
|
+
return { name: requested.name, profile, source: requested.source, implicit: view.implicit };
|
|
37
|
+
}
|
|
38
|
+
return { name: view.active, profile: view.profiles[view.active], source: "active", implicit: view.implicit };
|
|
39
|
+
}
|
|
40
|
+
export async function selectedProfile() {
|
|
41
|
+
return select(await readStoredAuth());
|
|
42
|
+
}
|
|
43
|
+
/** The endpoint used when --endpoint is omitted: the selected profile's. */
|
|
44
|
+
export async function readDefaultEndpoint() {
|
|
45
|
+
return (await selectedProfile()).profile.endpoint?.trim() || undefined;
|
|
46
|
+
}
|
|
47
|
+
/** Saves (or clears) the endpoint of the selected profile; other profiles are untouched. */
|
|
48
|
+
export async function writeDefaultEndpoint(endpoint) {
|
|
49
|
+
const stored = await readStoredAuth();
|
|
50
|
+
const selected = select(stored);
|
|
51
|
+
await updateStoredAuth(setProfileEndpointPatch(stored, selected.name, endpoint));
|
|
52
|
+
return selected.name;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* The endpoint of a profile the user explicitly named (--profile or
|
|
56
|
+
* DITTO_PROFILE), for a launch that should treat it like --endpoint. Undefined
|
|
57
|
+
* when no profile was named; an error when the named one has no endpoint.
|
|
58
|
+
*/
|
|
59
|
+
export async function explicitProfileEndpoint() {
|
|
60
|
+
const requested = requestedProfile();
|
|
61
|
+
if (!requested)
|
|
62
|
+
return undefined;
|
|
63
|
+
const selected = await selectedProfile();
|
|
64
|
+
const endpoint = selected.profile.endpoint?.trim();
|
|
65
|
+
if (!endpoint) {
|
|
66
|
+
throw new Error(`profile "${selected.name}" has no endpoint. Set one with \`heyditto profile set ${selected.name} --endpoint <slug>\`.`);
|
|
67
|
+
}
|
|
68
|
+
return { name: selected.name, endpoint, source: requested.source };
|
|
69
|
+
}
|
|
70
|
+
/** After an endpoint's slug changes, follow it in every profile that named it. */
|
|
71
|
+
export async function renameEndpointInProfiles(oldSlug, newSlug) {
|
|
72
|
+
const stored = await readStoredAuth();
|
|
73
|
+
const view = profileView(stored);
|
|
74
|
+
if (view.implicit) {
|
|
75
|
+
if (stored?.defaultEndpoint === oldSlug)
|
|
76
|
+
await updateStoredAuth({ defaultEndpoint: newSlug });
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
let changed = false;
|
|
80
|
+
const profiles = {};
|
|
81
|
+
for (const [name, profile] of Object.entries(view.profiles)) {
|
|
82
|
+
if (profile.endpoint === oldSlug) {
|
|
83
|
+
profiles[name] = { ...profile, endpoint: newSlug };
|
|
84
|
+
changed = true;
|
|
85
|
+
}
|
|
86
|
+
else
|
|
87
|
+
profiles[name] = profile;
|
|
88
|
+
}
|
|
89
|
+
if (changed)
|
|
90
|
+
await updateStoredAuth(profilesPatch(profiles, view.active));
|
|
91
|
+
}
|
|
92
|
+
/** After an endpoint is archived, drop it from every profile that named it (by slug or id). */
|
|
93
|
+
export async function forgetEndpointInProfiles(refs) {
|
|
94
|
+
const stored = await readStoredAuth();
|
|
95
|
+
const view = profileView(stored);
|
|
96
|
+
const matches = (value) => value !== undefined && refs.includes(value);
|
|
97
|
+
if (view.implicit) {
|
|
98
|
+
if (matches(stored?.defaultEndpoint))
|
|
99
|
+
await updateStoredAuth({ defaultEndpoint: undefined });
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
let changed = false;
|
|
103
|
+
const profiles = {};
|
|
104
|
+
for (const [name, profile] of Object.entries(view.profiles)) {
|
|
105
|
+
if (matches(profile.endpoint)) {
|
|
106
|
+
const { endpoint: _dropped, ...rest } = profile;
|
|
107
|
+
profiles[name] = rest;
|
|
108
|
+
changed = true;
|
|
109
|
+
}
|
|
110
|
+
else
|
|
111
|
+
profiles[name] = profile;
|
|
112
|
+
}
|
|
113
|
+
if (changed)
|
|
114
|
+
await updateStoredAuth(profilesPatch(profiles, view.active));
|
|
115
|
+
}
|
|
116
|
+
const tagged = new WeakSet();
|
|
117
|
+
/** `--profile <name>` for a command that reads the default endpoint. */
|
|
118
|
+
export function addProfileOption(cmd) {
|
|
119
|
+
tagged.add(cmd);
|
|
120
|
+
return cmd.option("--profile <name>", "use this endpoint profile for this command only (default: DITTO_PROFILE, then the active profile)");
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Applies `--profile` before any action runs. Only commands tagged by
|
|
124
|
+
* addProfileOption are read, so `repo apply --profile` and `review local
|
|
125
|
+
* --profile`, which mean something else, are left alone.
|
|
126
|
+
*/
|
|
127
|
+
export function installProfileHook(program) {
|
|
128
|
+
program.hook("preAction", (_root, action) => {
|
|
129
|
+
setProfileOverride(tagged.has(action) ? action.getOptionValue("profile") : undefined);
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
const json = (options) => options.output === "json";
|
|
133
|
+
const print = (value) => { process.stdout.write(`${JSON.stringify(value, null, 2)}\n`); };
|
|
134
|
+
function inferenceOf(raw) {
|
|
135
|
+
if (raw === undefined)
|
|
136
|
+
return undefined;
|
|
137
|
+
if (raw !== "router" && raw !== "cliproxy")
|
|
138
|
+
throw new Error("--inference must be router or cliproxy");
|
|
139
|
+
return raw;
|
|
140
|
+
}
|
|
141
|
+
/** Resolves an endpoint ref through the catalog and returns its canonical slug. */
|
|
142
|
+
async function canonicalEndpoint(ref) {
|
|
143
|
+
const catalog = await listEndpoints();
|
|
144
|
+
const match = findEndpoint(catalog.endpoints, ref.trim());
|
|
145
|
+
if (!match)
|
|
146
|
+
throw new Error(`no endpoint named "${ref.trim()}". Available: ${catalog.endpoints.map((e) => e.slug).join(", ") || "(none)"}`);
|
|
147
|
+
return match.slug;
|
|
148
|
+
}
|
|
149
|
+
function listJSON(view) {
|
|
150
|
+
return {
|
|
151
|
+
activeProfile: view.active,
|
|
152
|
+
implicit: view.implicit,
|
|
153
|
+
profiles: Object.keys(view.profiles).sort().map((name) => ({
|
|
154
|
+
name,
|
|
155
|
+
endpoint: view.profiles[name].endpoint ?? null,
|
|
156
|
+
inference: view.profiles[name].inference ?? "cliproxy",
|
|
157
|
+
active: name === view.active,
|
|
158
|
+
})),
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
export async function cmdProfileList(options) {
|
|
162
|
+
const stored = await readStoredAuth();
|
|
163
|
+
const view = profileView(stored);
|
|
164
|
+
const requested = requestedProfile();
|
|
165
|
+
if (json(options))
|
|
166
|
+
return print({ ...listJSON(view), ...(requested ? { selected: requested.name, selectedBy: requested.source } : {}) });
|
|
167
|
+
const rows = listJSON(view).profiles;
|
|
168
|
+
const width = Math.max(...rows.map((r) => r.name.length), 4);
|
|
169
|
+
process.stdout.write(` ${"NAME".padEnd(width)} ENDPOINT (INFERENCE)\n`);
|
|
170
|
+
for (const row of rows) {
|
|
171
|
+
process.stdout.write(`${row.active ? "*" : " "} ${row.name.padEnd(width)} ${row.endpoint ?? "(none)"} (${row.inference})\n`);
|
|
172
|
+
}
|
|
173
|
+
if (view.implicit)
|
|
174
|
+
process.stdout.write("\nNo profiles saved yet; `default` is your saved default endpoint. Create one with `heyditto profile create <name> --endpoint <slug>`.\n");
|
|
175
|
+
if (requested)
|
|
176
|
+
process.stdout.write(`\n${requested.source === "env" ? "DITTO_PROFILE" : "--profile"} selects "${requested.name}" for this command.\n`);
|
|
177
|
+
}
|
|
178
|
+
export async function cmdProfileCreate(rawName, options) {
|
|
179
|
+
const name = validateProfileName(rawName);
|
|
180
|
+
const inference = inferenceOf(options.inference);
|
|
181
|
+
const assertCreatable = (view) => {
|
|
182
|
+
if (!view.implicit && Object.hasOwn(view.profiles, name))
|
|
183
|
+
throw new Error(`profile "${name}" already exists; change it with \`heyditto profile set ${name}\``);
|
|
184
|
+
if (view.implicit && name === LEGACY_PROFILE && view.profiles[LEGACY_PROFILE].endpoint) {
|
|
185
|
+
throw new Error(`"${LEGACY_PROFILE}" already names your saved default endpoint; change it with \`heyditto profile set ${LEGACY_PROFILE} --endpoint <slug>\``);
|
|
186
|
+
}
|
|
187
|
+
};
|
|
188
|
+
// Fail fast on a name clash before the network lookup...
|
|
189
|
+
assertCreatable(profileView(await readStoredAuth()));
|
|
190
|
+
const endpoint = await canonicalEndpoint(options.endpoint);
|
|
191
|
+
// ...but build the write from state read after it: another heyditto may
|
|
192
|
+
// have changed profiles while the lookup was in flight, and a patch built
|
|
193
|
+
// from the earlier snapshot would silently drop that change.
|
|
194
|
+
const stored = await readStoredAuth();
|
|
195
|
+
const view = profileView(stored);
|
|
196
|
+
assertCreatable(view);
|
|
197
|
+
const { profiles, active } = materialize(stored);
|
|
198
|
+
const created = { endpoint, ...(inference ? { inference } : {}), createdAt: new Date().toISOString() };
|
|
199
|
+
const nextActive = options.use || active === undefined ? name : active;
|
|
200
|
+
await updateStoredAuth(profilesPatch({ ...profiles, [name]: created }, nextActive));
|
|
201
|
+
if (json(options))
|
|
202
|
+
return print({ name, endpoint, inference: inference ?? "cliproxy", active: nextActive === name, activeProfile: nextActive });
|
|
203
|
+
process.stdout.write(`Created profile ${name} → ${endpoint}${nextActive === name ? " (active)" : ""}.\n`);
|
|
204
|
+
if (view.implicit && profiles[LEGACY_PROFILE]) {
|
|
205
|
+
process.stdout.write(`Your previous default endpoint is saved as profile "${LEGACY_PROFILE}"${nextActive === LEGACY_PROFILE ? " and stays active" : ""}.\n`);
|
|
206
|
+
}
|
|
207
|
+
if (nextActive !== name)
|
|
208
|
+
process.stdout.write(`Switch with \`heyditto profile use ${name}\`, or pass --profile ${name} to one command.\n`);
|
|
209
|
+
}
|
|
210
|
+
export async function cmdProfileSet(rawName, options) {
|
|
211
|
+
const name = rawName.trim();
|
|
212
|
+
const inference = inferenceOf(options.inference);
|
|
213
|
+
if (options.endpoint === undefined && inference === undefined)
|
|
214
|
+
throw new Error("nothing to change: pass --endpoint and/or --inference");
|
|
215
|
+
const stored = await readStoredAuth();
|
|
216
|
+
const view = profileView(stored);
|
|
217
|
+
if (!Object.hasOwn(view.profiles, name))
|
|
218
|
+
throw new Error(unknownProfileMessage(name, view));
|
|
219
|
+
const endpoint = options.endpoint !== undefined ? await canonicalEndpoint(options.endpoint) : undefined;
|
|
220
|
+
let profiles;
|
|
221
|
+
let active;
|
|
222
|
+
if (view.implicit && inference === undefined) {
|
|
223
|
+
await updateStoredAuth({ defaultEndpoint: endpoint });
|
|
224
|
+
}
|
|
225
|
+
else {
|
|
226
|
+
({ profiles, active } = materialize(stored));
|
|
227
|
+
// An implicit default with no endpoint is not materialized; recreate it.
|
|
228
|
+
const current = profiles[name] ?? {};
|
|
229
|
+
const next = { ...current, ...(endpoint ? { endpoint } : {}), ...(inference ? { inference } : {}) };
|
|
230
|
+
await updateStoredAuth(profilesPatch({ ...profiles, [name]: next }, active ?? name));
|
|
231
|
+
}
|
|
232
|
+
const after = profileView(await readStoredAuth()).profiles[name];
|
|
233
|
+
if (json(options))
|
|
234
|
+
return print({ name, endpoint: after.endpoint ?? null, inference: after.inference ?? "cliproxy" });
|
|
235
|
+
process.stdout.write(`Profile ${name} → ${after.endpoint ?? "(none)"} (${after.inference ?? "cliproxy"}).\n`);
|
|
236
|
+
}
|
|
237
|
+
export async function cmdProfileUse(rawName, options) {
|
|
238
|
+
const name = rawName.trim();
|
|
239
|
+
const stored = await readStoredAuth();
|
|
240
|
+
const view = profileView(stored);
|
|
241
|
+
if (!Object.hasOwn(view.profiles, name))
|
|
242
|
+
throw new Error(unknownProfileMessage(name, view));
|
|
243
|
+
if (!view.implicit)
|
|
244
|
+
await updateStoredAuth(profilesPatch(view.profiles, name));
|
|
245
|
+
const endpoint = view.profiles[name].endpoint ?? null;
|
|
246
|
+
if (json(options))
|
|
247
|
+
return print({ activeProfile: name, endpoint });
|
|
248
|
+
process.stdout.write(`Active profile: ${name} → ${endpoint ?? "(no endpoint yet)"}. New heyditto sessions use it; existing T3 providers keep their own endpoints.\n`);
|
|
249
|
+
if (process.env.DITTO_PROFILE?.trim() && process.env.DITTO_PROFILE.trim() !== name) {
|
|
250
|
+
process.stderr.write(`Note: DITTO_PROFILE=${process.env.DITTO_PROFILE.trim()} still overrides it in this shell.\n`);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
export async function cmdProfileRename(rawOld, rawNew, options) {
|
|
254
|
+
const from = rawOld.trim();
|
|
255
|
+
const to = validateProfileName(rawNew);
|
|
256
|
+
const stored = await readStoredAuth();
|
|
257
|
+
const view = profileView(stored);
|
|
258
|
+
if (!Object.hasOwn(view.profiles, from))
|
|
259
|
+
throw new Error(unknownProfileMessage(from, view));
|
|
260
|
+
if (from === to)
|
|
261
|
+
throw new Error("the new name is the same as the old one");
|
|
262
|
+
if (Object.hasOwn(view.profiles, to))
|
|
263
|
+
throw new Error(`profile "${to}" already exists`);
|
|
264
|
+
const { profiles, active } = materialize(stored);
|
|
265
|
+
const renamed = {};
|
|
266
|
+
for (const [name, profile] of Object.entries(profiles))
|
|
267
|
+
renamed[name === from ? to : name] = profile;
|
|
268
|
+
if (!Object.hasOwn(renamed, to))
|
|
269
|
+
renamed[to] = { ...view.profiles[from] };
|
|
270
|
+
await updateStoredAuth(profilesPatch(renamed, active === from || active === undefined ? to : active));
|
|
271
|
+
if (json(options))
|
|
272
|
+
return print({ renamed: from, to });
|
|
273
|
+
process.stdout.write(`Renamed profile ${from} → ${to}. T3 providers created from it keep their instance id and endpoint.\n`);
|
|
274
|
+
}
|
|
275
|
+
export async function cmdProfileDelete(rawName, options) {
|
|
276
|
+
const name = rawName.trim();
|
|
277
|
+
const stored = await readStoredAuth();
|
|
278
|
+
const view = profileView(stored);
|
|
279
|
+
if (!Object.hasOwn(view.profiles, name))
|
|
280
|
+
throw new Error(unknownProfileMessage(name, view));
|
|
281
|
+
if (name === view.active)
|
|
282
|
+
throw new Error(`profile "${name}" is active; switch to another with \`heyditto profile use <name>\` before deleting it`);
|
|
283
|
+
const { profiles, active } = materialize(stored);
|
|
284
|
+
const { [name]: _removed, ...rest } = profiles;
|
|
285
|
+
await updateStoredAuth(profilesPatch(rest, active));
|
|
286
|
+
if (json(options))
|
|
287
|
+
return print({ deleted: name });
|
|
288
|
+
process.stdout.write(`Deleted profile ${name}. Its endpoint and any T3 provider made from it are unchanged.\n`);
|
|
289
|
+
}
|
|
290
|
+
export function registerProfileCommands(program, addExamples, outputOption) {
|
|
291
|
+
const profile = program
|
|
292
|
+
.command("profile")
|
|
293
|
+
.description("named endpoint profiles: switch which endpoint (and its MCP tools) new sessions use")
|
|
294
|
+
.summary("manage named endpoint profiles")
|
|
295
|
+
.showHelpAfterError()
|
|
296
|
+
.addHelpText("after", `
|
|
297
|
+
A profile names an endpoint. The active one is used when --endpoint is
|
|
298
|
+
omitted; --profile <name> or DITTO_PROFILE=<name> picks another for one
|
|
299
|
+
command or one shell. Profiles are local to this machine and hold no keys.
|
|
300
|
+
(\`heyditto profiles\` is an older alias of \`heyditto endpoints\`.)`);
|
|
301
|
+
addExamples(profile.command("list", { isDefault: true }).description("list profiles (* = active)").addOption(outputOption()).action(cmdProfileList), ` heyditto profile
|
|
302
|
+
heyditto profile list --output json`);
|
|
303
|
+
addExamples(profile
|
|
304
|
+
.command("create")
|
|
305
|
+
.description("save a profile for an endpoint (your current default is kept as profile \"default\")")
|
|
306
|
+
.argument("<name>", "profile name (lowercase letters, digits, - or _)")
|
|
307
|
+
.requiredOption("--endpoint <slug>", "endpoint slug or id")
|
|
308
|
+
.addOption(new Option("--inference <mode>", "inference route for `heyditto t3 setup --profile` (default cliproxy)").choices(["cliproxy", "router"]))
|
|
309
|
+
.option("--use", "make it the active profile")
|
|
310
|
+
.addOption(outputOption())
|
|
311
|
+
.action(cmdProfileCreate), ` heyditto profile create subnet --endpoint my-subnet-endpoint
|
|
312
|
+
heyditto profile create work --endpoint work-router --inference router --use`);
|
|
313
|
+
profile
|
|
314
|
+
.command("set")
|
|
315
|
+
.description("change a profile's endpoint or inference route")
|
|
316
|
+
.argument("<name>")
|
|
317
|
+
.option("--endpoint <slug>", "endpoint slug or id")
|
|
318
|
+
.addOption(new Option("--inference <mode>", "inference route for T3 setup").choices(["cliproxy", "router"]))
|
|
319
|
+
.addOption(outputOption())
|
|
320
|
+
.action(cmdProfileSet);
|
|
321
|
+
profile.command("use").alias("switch").description("make a profile active for new sessions").argument("<name>").addOption(outputOption()).action(cmdProfileUse);
|
|
322
|
+
profile.command("rename").description("rename a profile").argument("<old>").argument("<new>").addOption(outputOption()).action(cmdProfileRename);
|
|
323
|
+
profile.command("delete").alias("rm").description("delete a profile (not the active one)").argument("<name>").addOption(outputOption()).action(cmdProfileDelete);
|
|
324
|
+
}
|
|
325
|
+
//# sourceMappingURL=profiles.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"profiles.js","sourceRoot":"","sources":["../src/profiles.ts"],"names":[],"mappings":"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"}
|
|
@@ -6,7 +6,7 @@ import { fileURLToPath } from "node:url";
|
|
|
6
6
|
import { apiFetch, findEndpoint, listEndpoints } from "../api.js";
|
|
7
7
|
import { resolveApiKey } from "../config.js";
|
|
8
8
|
import { findProfileEndpoint, repoDefaultProfile } from "../dittoconfig/profiles.js";
|
|
9
|
-
import {
|
|
9
|
+
import { readDefaultEndpoint } from "../profiles.js";
|
|
10
10
|
import { runChannelServer } from "./channel-server.js";
|
|
11
11
|
import { remoteSessionURL, UNLINKABLE_SESSION_NOTICE } from "./session-link.js";
|
|
12
12
|
/** Only these locally constructed setup errors may be shown verbatim. API,
|
|
@@ -30,7 +30,7 @@ export async function preparePluginSession(cwd = process.env.DITTO_REMOTE_CWD ??
|
|
|
30
30
|
const catalog = await listEndpoints();
|
|
31
31
|
const explicit = process.env.DITTO_REMOTE_ENDPOINT?.trim();
|
|
32
32
|
const profile = explicit ? undefined : await repoDefaultProfile(cwd);
|
|
33
|
-
const selected = explicit ?? (await
|
|
33
|
+
const selected = explicit ?? (await readDefaultEndpoint());
|
|
34
34
|
const endpoint = profile
|
|
35
35
|
? await findProfileEndpoint(profile, catalog.endpoints)
|
|
36
36
|
: selected ? findEndpoint(catalog.endpoints, selected) : catalog.endpoints.length === 1 ? catalog.endpoints[0] : undefined;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin-server.js","sourceRoot":"","sources":["../../src/remote/plugin-server.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAClE,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AACrF,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"plugin-server.js","sourceRoot":"","sources":["../../src/remote/plugin-server.ts"],"names":[],"mappings":"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"}
|
|
@@ -93,7 +93,11 @@ interface LocalOptions extends ScopeOptions {
|
|
|
93
93
|
timeout?: string;
|
|
94
94
|
requestId?: string;
|
|
95
95
|
gate?: boolean;
|
|
96
|
+
trace?: string;
|
|
97
|
+
issue?: string;
|
|
96
98
|
}
|
|
99
|
+
/** Exit code for a completed local review whose verdict requests changes. */
|
|
100
|
+
export declare const REQUEST_CHANGES_EXIT = 2;
|
|
97
101
|
export declare function cmdReviewLocal(options: LocalOptions): Promise<void>;
|
|
98
102
|
export declare function cmdReviewLocalStatus(id: string, options: ScopeOptions & {
|
|
99
103
|
gate?: boolean;
|
package/dist/review-commands.js
CHANGED
|
@@ -7,7 +7,8 @@ import { cmdReviewAccess, diagnoseLinkRefusal, guideAccessForAdd } from "./githu
|
|
|
7
7
|
import { registerReviewFindingCommands } from "./review-findings.js";
|
|
8
8
|
import { readStoredAuth } from "./store.js";
|
|
9
9
|
import { captureReviewSnapshot, reviewGit } from "./review-snapshot.js";
|
|
10
|
-
import { findingsList, formatRecommendation, isVisibleFinding, gateExitCode, malformedLocalRun, recommendLocalReview } from "./review-recommendation.js";
|
|
10
|
+
import { findingsList, formatRecommendation, isVisibleFinding, gateExitCode, malformedLocalRun, recommendLocalReview, scopeOutcome } from "./review-recommendation.js";
|
|
11
|
+
import { freezeAgentTrace, readAgentTrace } from "./review-trace.js";
|
|
11
12
|
/** The server's bounds (backend reviewLimits). */
|
|
12
13
|
const MIN_MAX_MINUTES = 5;
|
|
13
14
|
const MAX_MAX_MINUTES = 45;
|
|
@@ -28,6 +29,8 @@ export async function listReviewRepositories(company) {
|
|
|
28
29
|
const out = await apiFetch(`/api/v5/review${scopeQuery(company)}`);
|
|
29
30
|
return out.repositories ?? [];
|
|
30
31
|
}
|
|
32
|
+
/** Exit code for a completed local review whose verdict requests changes. */
|
|
33
|
+
export const REQUEST_CHANGES_EXIT = 2;
|
|
31
34
|
function repositoryFromRemote(remote) {
|
|
32
35
|
const match = /^(?:git@github\.com:|https?:\/\/github\.com\/|ssh:\/\/git@github\.com\/)([^/\s]+\/[^/\s]+?)(?:\.git)?\/?$/.exec(remote.trim());
|
|
33
36
|
if (!match)
|
|
@@ -54,6 +57,9 @@ function printLocalRun(run, options) {
|
|
|
54
57
|
}
|
|
55
58
|
else
|
|
56
59
|
process.stdout.write("No review result is available for this run.\n");
|
|
60
|
+
printScope(result?.scope);
|
|
61
|
+
if (result?.verdict)
|
|
62
|
+
printVerdict(result.verdict);
|
|
57
63
|
if (!result?.complete || run.status !== "completed")
|
|
58
64
|
process.stdout.write("Review incomplete; check coverage and errors before relying on this result.\n");
|
|
59
65
|
if (result?.local?.contextComplete === false)
|
|
@@ -70,6 +76,46 @@ function printLocalRun(run, options) {
|
|
|
70
76
|
process.stdout.write(formatRecommendation(recommendation));
|
|
71
77
|
return recommendation;
|
|
72
78
|
}
|
|
79
|
+
function printScope(scope) {
|
|
80
|
+
if (!scope)
|
|
81
|
+
return;
|
|
82
|
+
const name = scope.key ? `${scope.key}${scope.url ? ` (${scope.url})` : ""}` : "the agent's task prompt";
|
|
83
|
+
if (scope.status !== "checked") {
|
|
84
|
+
const why = { no_items: "no checkable written requirements", not_found: "issue not found; scope not checked", unavailable: "issue could not be read; scope not checked", incomplete: "scope check incomplete" };
|
|
85
|
+
process.stdout.write(`\nLinked issue ${name}: ${why[scope.status] ?? scope.status}${scope.error ? ` (${scope.error})` : ""}\n`);
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
const outcome = scopeOutcome({ scope });
|
|
89
|
+
if (outcome.kind === "unchecked") {
|
|
90
|
+
process.stdout.write(`\nLinked issue ${name}: ${outcome.reason}\n`);
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
const items = (Array.isArray(scope.items) ? scope.items : []).filter((i) => typeof i === "object" && i !== null);
|
|
94
|
+
// The recommendation's rule: required unless explicitly false, met only when true.
|
|
95
|
+
const required = items.filter(i => i.required !== false);
|
|
96
|
+
process.stdout.write(`\nLinked issue ${name}: ${required.filter(i => i.met === true).length} of ${required.length} written requirements met${scope.error ? ` (${scope.error})` : ""}\n`);
|
|
97
|
+
for (const it of items) {
|
|
98
|
+
const mark = it.required === false ? "-" : it.met === true ? "✓" : "✗";
|
|
99
|
+
const detail = it.met === true ? (it.evidence_refs ?? []).slice(0, 3).join(", ") : it.reason ?? "";
|
|
100
|
+
process.stdout.write(` ${mark} ${it.scope_item}${detail ? `\n ${detail}` : ""}\n`);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
function printVerdict(v) {
|
|
104
|
+
if (v.event === "REQUEST_CHANGES") {
|
|
105
|
+
const reasons = [];
|
|
106
|
+
if (v.blockingFindings)
|
|
107
|
+
reasons.push(`${v.blockingFindings} blocking finding${v.blockingFindings === 1 ? "" : "s"} (${v.blockingSeverity ?? "high"} or above)`);
|
|
108
|
+
if (v.scopeUnmet?.length)
|
|
109
|
+
reasons.push(`${v.scopeUnmet.length} written requirement${v.scopeUnmet.length === 1 ? "" : "s"} of ${v.scopeKey || "the task"} not met`);
|
|
110
|
+
process.stdout.write(`\nVerdict: changes requested — ${reasons.join("; ")}\n`);
|
|
111
|
+
for (const it of v.scopeUnmet ?? [])
|
|
112
|
+
process.stdout.write(` missing: "${it.scope_item}"\n`);
|
|
113
|
+
}
|
|
114
|
+
else if (v.event === "APPROVE")
|
|
115
|
+
process.stdout.write("\nVerdict: approved\n");
|
|
116
|
+
else
|
|
117
|
+
process.stdout.write(`\nVerdict: no verdict (${v.reason === "scope_unverified" ? "the linked issue's requirements could not be checked" : "the review is incomplete"})\n`);
|
|
118
|
+
}
|
|
73
119
|
async function pollLocalRun(route, signal) {
|
|
74
120
|
for (let attempt = 0;; attempt++) {
|
|
75
121
|
try {
|
|
@@ -105,6 +151,12 @@ export async function cmdReviewLocal(options) {
|
|
|
105
151
|
const context = options.contextFile ? new TextDecoder("utf-8", { fatal: true }).decode(await readFile(options.contextFile)) : undefined;
|
|
106
152
|
if (context && (Buffer.byteLength(context) > 16000 || context.includes("\0")))
|
|
107
153
|
throw new Error("--context-file must contain at most 16000 UTF-8 bytes without NUL characters");
|
|
154
|
+
const issueKey = options.issue?.trim().toUpperCase();
|
|
155
|
+
if (issueKey && !/^[A-Z][A-Z0-9_]{1,9}-[1-9][0-9]{0,8}$/.test(issueKey))
|
|
156
|
+
throw new Error("--issue must be an issue key such as DITTO-123");
|
|
157
|
+
// The trace is only read; it stays where the agent wrote it.
|
|
158
|
+
const trace = options.trace ? await readAgentTrace(options.trace) : undefined;
|
|
159
|
+
const linked = { ...(issueKey ? { issueKey } : {}), ...(trace ? { trace } : {}) };
|
|
108
160
|
// Preview deliberately has no auth or network dependency.
|
|
109
161
|
const company = options.preview ? undefined : await resolveScope(options);
|
|
110
162
|
const repos = options.preview ? [] : await listReviewRepositories(company);
|
|
@@ -116,7 +168,7 @@ export async function cmdReviewLocal(options) {
|
|
|
116
168
|
process.on("SIGTERM", onTerm);
|
|
117
169
|
const timer = setTimeout(() => { timedOut = true; controller.abort(); }, timeout);
|
|
118
170
|
const requestId = options.requestId?.toLowerCase() ?? randomUUID();
|
|
119
|
-
const recovery = () => {
|
|
171
|
+
const recovery = async () => {
|
|
120
172
|
const args = ["heyditto", "review", "local", "--request-id", requestId, "--profile", profile];
|
|
121
173
|
if (capturedRoot)
|
|
122
174
|
args.push("--directory", capturedRoot);
|
|
@@ -126,6 +178,18 @@ export async function cmdReviewLocal(options) {
|
|
|
126
178
|
args.push("--base", options.base);
|
|
127
179
|
if (options.contextFile)
|
|
128
180
|
args.push("--context-file", path.resolve(options.contextFile));
|
|
181
|
+
// The session file keeps growing; recover with the exact trace sent.
|
|
182
|
+
if (trace) {
|
|
183
|
+
// Never fall back to the live session file: it may have grown since.
|
|
184
|
+
try {
|
|
185
|
+
args.push("--trace", await freezeAgentTrace(trace, requestId));
|
|
186
|
+
}
|
|
187
|
+
catch (error) {
|
|
188
|
+
return `unavailable: the exact trace could not be saved (${error instanceof Error ? error.message : String(error)})`;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
if (issueKey)
|
|
192
|
+
args.push("--issue", issueKey);
|
|
129
193
|
if (options.repository)
|
|
130
194
|
args.push("--repository", options.repository);
|
|
131
195
|
if (company)
|
|
@@ -139,7 +203,9 @@ export async function cmdReviewLocal(options) {
|
|
|
139
203
|
const preview = {
|
|
140
204
|
requestId, profile, snapshotDigest: snapshot.digest, mode: snapshot.mode, baseSha: snapshot.baseSha,
|
|
141
205
|
changedPaths: snapshot.changedPaths, baseFileCount: snapshot.baseFiles.length, headFileCount: snapshot.headFiles.length,
|
|
142
|
-
contentBytes: captured.bytes, payloadBytes: Buffer.byteLength(JSON.stringify({ requestId, snapshot, profile, context })),
|
|
206
|
+
contentBytes: captured.bytes, payloadBytes: Buffer.byteLength(JSON.stringify({ requestId, snapshot, profile, context, ...linked })),
|
|
207
|
+
...(issueKey ? { issueKey } : {}),
|
|
208
|
+
...(trace ? { trace: { format: trace.format, promptBytes: Buffer.byteLength(trace.initialPrompt), steps: trace.steps.length, truncated: trace.truncated === true } } : {}),
|
|
143
209
|
contextComplete: snapshot.contextComplete, omittedContext: snapshot.omittedContext, skipped: snapshot.skipped,
|
|
144
210
|
};
|
|
145
211
|
if (options.preview) {
|
|
@@ -148,6 +214,10 @@ export async function cmdReviewLocal(options) {
|
|
|
148
214
|
process.stdout.write(`Local review preview · ${profile}\nSnapshot: ${snapshot.digest}\nSelected changes: ${snapshot.changedPaths.length}\nContext files: ${snapshot.headFiles.length} (${captured.bytes} decoded bytes; ${preview.payloadBytes} payload bytes)\n`);
|
|
149
215
|
for (const file of snapshot.changedPaths)
|
|
150
216
|
process.stdout.write(` ${file}\n`);
|
|
217
|
+
if (issueKey)
|
|
218
|
+
process.stdout.write(`Linked issue: ${issueKey}\n`);
|
|
219
|
+
if (trace)
|
|
220
|
+
process.stdout.write(`Agent trace: ${trace.format}, ${trace.steps.length} steps${trace.truncated ? " (middle steps omitted)" : ""}\n`);
|
|
151
221
|
process.stdout.write(`Omitted context: ${snapshot.omittedContext.length}; skipped files: ${snapshot.skipped.length}\nNo source uploaded; no review started.\n`);
|
|
152
222
|
return;
|
|
153
223
|
}
|
|
@@ -173,7 +243,7 @@ export async function cmdReviewLocal(options) {
|
|
|
173
243
|
// Complete the admission response even on Ctrl-C so a returned run can be
|
|
174
244
|
// cancelled. An uncertain POST is never retried automatically.
|
|
175
245
|
let run = await apiFetch(`/api/v5/review/repositories/${encodeURIComponent(repo.id)}/local${scopeQuery(company)}`, {
|
|
176
|
-
method: "POST", body: { requestId, snapshot, profile, ...(context ? { context } : {}) }, signal: AbortSignal.timeout(30000),
|
|
246
|
+
method: "POST", body: { requestId, snapshot, profile, ...(context ? { context } : {}), ...linked }, signal: AbortSignal.timeout(30000),
|
|
177
247
|
});
|
|
178
248
|
try {
|
|
179
249
|
runPath(run?.runId);
|
|
@@ -213,6 +283,13 @@ export async function cmdReviewLocal(options) {
|
|
|
213
283
|
process.exitCode = gateExitCode(recommendation);
|
|
214
284
|
else if (["failed", "cancelled", "partial"].includes(run.status) || (run.status === "completed" && run.result?.complete !== true) || malformedLocalRun(run).length)
|
|
215
285
|
process.exitCode = 1;
|
|
286
|
+
// Without --gate only completeness and the linked issue's written scope
|
|
287
|
+
// set the exit status (findings alone keep 0, as before), by the rule
|
|
288
|
+
// --gate uses: 2 for an unmet requirement, 1 for a scope not fully checked.
|
|
289
|
+
else if (scopeOutcome(run.result).kind === "changes")
|
|
290
|
+
process.exitCode = REQUEST_CHANGES_EXIT;
|
|
291
|
+
else if (scopeOutcome(run.result).kind === "unchecked")
|
|
292
|
+
process.exitCode = 1;
|
|
216
293
|
return;
|
|
217
294
|
}
|
|
218
295
|
if (!ACTIVE_REVIEW_STATUSES.has(run.status))
|
|
@@ -234,7 +311,7 @@ export async function cmdReviewLocal(options) {
|
|
|
234
311
|
if (interrupted || timedOut) {
|
|
235
312
|
process.stderr.write(`${timedOut ? "Local review timed out" : "Local review interrupted"}${runId ? ` (run ${runId})` : posted ? `; admission outcome is unknown (request ${requestId})` : ""}.\n`);
|
|
236
313
|
if (posted && !runId)
|
|
237
|
-
process.stderr.write(`Recover the same admission: ${recovery()}\nChanged source, profile or context is refused; no automatic retry was made.\n`);
|
|
314
|
+
process.stderr.write(`Recover the same admission: ${await recovery()}\nChanged source, profile or context is refused; no automatic retry was made.\n`);
|
|
238
315
|
if (isJSON(options)) {
|
|
239
316
|
const status = timedOut ? "timed_out" : "interrupted";
|
|
240
317
|
writeJSON({ status, requestId, ...(runId ? { runId } : {}), recommendation: { verdict: "none", approved: false, reasons: [timedOut ? "local review timed out; a timed-out run is never approval" : "local review interrupted; an interrupted run is never approval"], blocking: 0, nits: 0 } });
|
|
@@ -248,7 +325,7 @@ export async function cmdReviewLocal(options) {
|
|
|
248
325
|
throw new Error(`Ditto refused the local review request (HTTP ${error.status}${refusalDetail(error.body)}). No review was admitted and nothing was approved.`);
|
|
249
326
|
}
|
|
250
327
|
if (posted && !runId)
|
|
251
|
-
throw new Error(`${error instanceof Error ? error.message : String(error)}\nAdmission outcome is unknown; request ${requestId}.\nRecover the same admission: ${recovery()}\nChanged source, profile or context is refused; no automatic retry was made.`);
|
|
328
|
+
throw new Error(`${error instanceof Error ? error.message : String(error)}\nAdmission outcome is unknown; request ${requestId}.\nRecover the same admission: ${await recovery()}\nChanged source, profile or context is refused; no automatic retry was made.`);
|
|
252
329
|
throw error;
|
|
253
330
|
}
|
|
254
331
|
finally {
|
|
@@ -707,8 +784,10 @@ export function registerReviewCommands(program, addExamples, outputOption, orgOp
|
|
|
707
784
|
.option("--preview", "inspect selected paths and upload size locally; no auth, upload or inference")
|
|
708
785
|
.option("--timeout <seconds>", "cancel after this many seconds including capture and queue time (1-1800; default 1200, 1800 for deep)")
|
|
709
786
|
.option("--request-id <uuid>", "recover an uncertain admission with exactly the same source, profile and context")
|
|
710
|
-
.option("--gate", "exit 0 only on an approve recommendation, 2 on changes requested, 1 when there is no recommendation; a timeout (124) or interrupt (130/143) keeps its own code and is never approval")
|
|
711
|
-
.
|
|
787
|
+
.option("--gate", "exit 0 only on an approve recommendation, 2 on changes requested, 1 when there is no recommendation; a timeout (124) or interrupt (130/143) keeps its own code and is never approval")
|
|
788
|
+
.option("--trace <file>", "the coding agent's session (Claude Code or Codex JSONL): its prompt and steps become review input, and its linked issue's written requirements are checked; read only, never deleted")
|
|
789
|
+
.option("--issue <key>", "the Jira issue this change delivers (default: a [KEY-1] tag or tracker link in the trace prompt or context)"))
|
|
790
|
+
.action(cmdReviewLocal), ` heyditto review local --preview\n heyditto review local --staged --profile quick --org omniaura\n heyditto review local --base main --context-file .tmp/task.md --output json\n heyditto review local --base main --trace ~/.claude/projects/<project>/<session>.jsonl --issue DITTO-2434\n\nExit status: 0 approved or no blocking issue, 1 incomplete or failed, ${REQUEST_CHANGES_EXIT} changes requested.`);
|
|
712
791
|
scoped(review.command("local-status").description("inspect a pre-PR review run without starting or retrying it")
|
|
713
792
|
.argument("<run-id>", "local review run UUID")
|
|
714
793
|
.option("--gate", "exit 0 only on approve (never, since status cannot verify the working tree), 2 on changes, 1 otherwise")).action(cmdReviewLocalStatus);
|