@atollhq/cli 0.1.4 → 0.1.6
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 +26 -3
- package/dist/index.js +383 -36
- package/dist/index.js.map +1 -1
- package/package.json +2 -1
package/dist/index.js
CHANGED
|
@@ -13,6 +13,7 @@ var __export = (target, all) => {
|
|
|
13
13
|
// src/lib/config.ts
|
|
14
14
|
var config_exports = {};
|
|
15
15
|
__export(config_exports, {
|
|
16
|
+
CONFIG_PATH: () => CONFIG_PATH,
|
|
16
17
|
deleteConfig: () => deleteConfig,
|
|
17
18
|
ensureProfile: () => ensureProfile,
|
|
18
19
|
getActiveProfile: () => getActiveProfile,
|
|
@@ -20,6 +21,7 @@ __export(config_exports, {
|
|
|
20
21
|
getProfile: () => getProfile,
|
|
21
22
|
readConfig: () => readConfig,
|
|
22
23
|
resolveConfig: () => resolveConfig,
|
|
24
|
+
resolveDefaultProject: () => resolveDefaultProject,
|
|
23
25
|
writeConfig: () => writeConfig
|
|
24
26
|
});
|
|
25
27
|
function readConfig() {
|
|
@@ -62,9 +64,13 @@ function resolveConfig(opts) {
|
|
|
62
64
|
apiKey: process.env.ATOLL_API_KEY || profile?.apiKey || (profileName ? void 0 : config.apiKey),
|
|
63
65
|
orgSlug: process.env.ATOLL_ORG || profile?.orgSlug || (profileName ? void 0 : config.orgSlug),
|
|
64
66
|
defaultTeam: process.env.ATOLL_TEAM || profile?.defaultTeam || (profileName ? void 0 : config.defaultTeam),
|
|
67
|
+
defaultProject: process.env.ATOLL_PROJECT || profile?.defaultProject || (profileName ? void 0 : config.defaultProject),
|
|
65
68
|
baseUrl: process.env.ATOLL_BASE_URL || profile?.baseUrl || (profileName ? void 0 : config.baseUrl)
|
|
66
69
|
};
|
|
67
70
|
}
|
|
71
|
+
function resolveDefaultProject(explicitProject) {
|
|
72
|
+
return explicitProject || resolveConfig().defaultProject;
|
|
73
|
+
}
|
|
68
74
|
function getApiKey(opts) {
|
|
69
75
|
return resolveConfig(opts).apiKey;
|
|
70
76
|
}
|
|
@@ -87,6 +93,8 @@ var import_node_path4 = require("path");
|
|
|
87
93
|
|
|
88
94
|
// src/commands/auth.ts
|
|
89
95
|
var import_commander = require("commander");
|
|
96
|
+
var import_node_process = require("process");
|
|
97
|
+
var import_promises = require("readline/promises");
|
|
90
98
|
init_config();
|
|
91
99
|
|
|
92
100
|
// src/lib/client.ts
|
|
@@ -190,24 +198,174 @@ var AtollClient = class {
|
|
|
190
198
|
};
|
|
191
199
|
|
|
192
200
|
// src/commands/auth.ts
|
|
201
|
+
var DEFAULT_BASE_URL2 = "https://atollhq.com";
|
|
202
|
+
function redactKey(key) {
|
|
203
|
+
if (key.length <= 8) return "********";
|
|
204
|
+
return `${key.slice(0, 8)}...${key.slice(-4)}`;
|
|
205
|
+
}
|
|
206
|
+
function profileSummaries(config = readConfig()) {
|
|
207
|
+
return Object.entries(config.profiles ?? {}).sort(([a], [b]) => a.localeCompare(b)).map(([name, profile]) => ({
|
|
208
|
+
name,
|
|
209
|
+
active: name === config.activeProfile,
|
|
210
|
+
apiKeySet: !!profile.apiKey,
|
|
211
|
+
orgSlug: profile.orgSlug ?? null,
|
|
212
|
+
defaultTeam: profile.defaultTeam ?? null,
|
|
213
|
+
defaultProject: profile.defaultProject ?? null,
|
|
214
|
+
baseUrl: profile.baseUrl ?? null
|
|
215
|
+
}));
|
|
216
|
+
}
|
|
217
|
+
function formatProfileLine(profile) {
|
|
218
|
+
const marker = profile.active ? "*" : " ";
|
|
219
|
+
const parts = [
|
|
220
|
+
`${marker} ${profile.name}`,
|
|
221
|
+
profile.apiKeySet ? "key=set" : "key=not set",
|
|
222
|
+
profile.orgSlug ? `org=${profile.orgSlug}` : null,
|
|
223
|
+
profile.defaultTeam ? `team=${profile.defaultTeam}` : null,
|
|
224
|
+
profile.defaultProject ? `project=${profile.defaultProject}` : null,
|
|
225
|
+
profile.baseUrl ? `baseUrl=${profile.baseUrl}` : null
|
|
226
|
+
].filter(Boolean);
|
|
227
|
+
return parts.join(" ");
|
|
228
|
+
}
|
|
229
|
+
async function ask(rl, question, defaultValue, opts) {
|
|
230
|
+
const suffix = defaultValue ? ` (${opts?.defaultLabel ?? defaultValue})` : "";
|
|
231
|
+
const answer = (await rl.question(`${question}${suffix}: `)).trim();
|
|
232
|
+
return answer || defaultValue || "";
|
|
233
|
+
}
|
|
234
|
+
async function askRequired(rl, question, defaultValue, opts) {
|
|
235
|
+
while (true) {
|
|
236
|
+
const answer = await ask(rl, question, defaultValue, opts);
|
|
237
|
+
if (answer) return answer;
|
|
238
|
+
console.log("This value is required.");
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
async function selectOne(rl, label, items, format, opts) {
|
|
242
|
+
if (items.length === 0) return null;
|
|
243
|
+
if (items.length === 1 && !opts?.allowSkip) return items[0];
|
|
244
|
+
console.log(label);
|
|
245
|
+
items.forEach((item, index) => {
|
|
246
|
+
console.log(` ${index + 1}. ${format(item)}`);
|
|
247
|
+
});
|
|
248
|
+
if (opts?.allowSkip) console.log(" 0. Skip");
|
|
249
|
+
while (true) {
|
|
250
|
+
const defaultChoice = opts?.defaultIndex !== void 0 ? String(opts.defaultIndex + 1) : opts?.allowSkip ? "0" : "1";
|
|
251
|
+
const answer = await ask(rl, "Choose a number", defaultChoice);
|
|
252
|
+
const choice = Number.parseInt(answer, 10);
|
|
253
|
+
if (opts?.allowSkip && choice === 0) return null;
|
|
254
|
+
if (Number.isInteger(choice) && choice >= 1 && choice <= items.length) {
|
|
255
|
+
return items[choice - 1];
|
|
256
|
+
}
|
|
257
|
+
console.log("Enter a valid number from the list.");
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
function deleteProfile(profileName) {
|
|
261
|
+
const config = readConfig();
|
|
262
|
+
if (!getProfile(config, profileName)) {
|
|
263
|
+
outputError(`Profile "${profileName}" not found.`);
|
|
264
|
+
process.exit(1);
|
|
265
|
+
}
|
|
266
|
+
delete config.profiles?.[profileName];
|
|
267
|
+
if (config.activeProfile === profileName) {
|
|
268
|
+
const nextProfile = Object.keys(config.profiles ?? {}).sort()[0];
|
|
269
|
+
if (nextProfile) config.activeProfile = nextProfile;
|
|
270
|
+
else delete config.activeProfile;
|
|
271
|
+
}
|
|
272
|
+
writeConfig(config);
|
|
273
|
+
return { activeProfile: config.activeProfile ?? null };
|
|
274
|
+
}
|
|
275
|
+
async function runProfileSetup(rl) {
|
|
276
|
+
const config = readConfig();
|
|
277
|
+
const profileName = await askRequired(rl, "Profile name", config.activeProfile || "default");
|
|
278
|
+
const existing = getProfile(config, profileName);
|
|
279
|
+
const apiKey = await askRequired(
|
|
280
|
+
rl,
|
|
281
|
+
existing?.apiKey ? `API key (${redactKey(existing.apiKey)})` : "API key",
|
|
282
|
+
existing?.apiKey,
|
|
283
|
+
existing?.apiKey ? { defaultLabel: "keep existing" } : void 0
|
|
284
|
+
);
|
|
285
|
+
const baseUrl = await ask(rl, "Base URL", existing?.baseUrl || DEFAULT_BASE_URL2);
|
|
286
|
+
console.log("Validating API key...");
|
|
287
|
+
const client = new AtollClient({ apiKey, baseUrl });
|
|
288
|
+
await client.get("/api/auth/me");
|
|
289
|
+
const { orgs } = await client.get("/api/orgs");
|
|
290
|
+
if (!orgs || orgs.length === 0) {
|
|
291
|
+
outputError("No organizations found for this API key.");
|
|
292
|
+
process.exit(1);
|
|
293
|
+
}
|
|
294
|
+
const defaultOrgIndex = existing?.orgSlug ? orgs.findIndex((org2) => org2.slug === existing.orgSlug) : -1;
|
|
295
|
+
const org = await selectOne(
|
|
296
|
+
rl,
|
|
297
|
+
"Organizations",
|
|
298
|
+
orgs,
|
|
299
|
+
(item) => `${item.name} (${item.slug})`,
|
|
300
|
+
{ defaultIndex: defaultOrgIndex >= 0 ? defaultOrgIndex : void 0 }
|
|
301
|
+
);
|
|
302
|
+
if (!org) {
|
|
303
|
+
outputError("No organization selected.");
|
|
304
|
+
process.exit(1);
|
|
305
|
+
}
|
|
306
|
+
const { projects } = await client.get(`/api/orgs/${org.id}/projects`);
|
|
307
|
+
const defaultProjectIndex = existing?.defaultProject ? (projects ?? []).findIndex((project2) => project2.id === existing.defaultProject) : -1;
|
|
308
|
+
const project = await selectOne(
|
|
309
|
+
rl,
|
|
310
|
+
"Default project",
|
|
311
|
+
projects ?? [],
|
|
312
|
+
(item) => `${item.name} (${item.id})`,
|
|
313
|
+
{ allowSkip: true, defaultIndex: defaultProjectIndex >= 0 ? defaultProjectIndex : void 0 }
|
|
314
|
+
);
|
|
315
|
+
const defaultTeam = await ask(rl, "Default team slug or ID (optional)", existing?.defaultTeam);
|
|
316
|
+
const updatedConfig = readConfig();
|
|
317
|
+
const profile = ensureProfile(updatedConfig, profileName);
|
|
318
|
+
profile.apiKey = apiKey;
|
|
319
|
+
profile.orgSlug = org.slug;
|
|
320
|
+
if (baseUrl && baseUrl !== DEFAULT_BASE_URL2) profile.baseUrl = baseUrl;
|
|
321
|
+
else delete profile.baseUrl;
|
|
322
|
+
if (defaultTeam) profile.defaultTeam = defaultTeam;
|
|
323
|
+
else delete profile.defaultTeam;
|
|
324
|
+
if (project) profile.defaultProject = project.id;
|
|
325
|
+
else delete profile.defaultProject;
|
|
326
|
+
updatedConfig.activeProfile = profileName;
|
|
327
|
+
writeConfig(updatedConfig);
|
|
328
|
+
output(
|
|
329
|
+
{
|
|
330
|
+
status: "ok",
|
|
331
|
+
profile: profileName,
|
|
332
|
+
orgSlug: profile.orgSlug,
|
|
333
|
+
defaultTeam: profile.defaultTeam ?? null,
|
|
334
|
+
defaultProject: profile.defaultProject ?? null,
|
|
335
|
+
baseUrl: profile.baseUrl ?? null,
|
|
336
|
+
apiKey: redactKey(apiKey)
|
|
337
|
+
},
|
|
338
|
+
[
|
|
339
|
+
`\u2713 Profile "${profileName}" saved and set active`,
|
|
340
|
+
` Org: ${profile.orgSlug}`,
|
|
341
|
+
profile.defaultProject ? ` Project: ${profile.defaultProject}` : " Project: (not set)",
|
|
342
|
+
profile.defaultTeam ? ` Team: ${profile.defaultTeam}` : null,
|
|
343
|
+
profile.baseUrl ? ` Base URL: ${profile.baseUrl}` : null,
|
|
344
|
+
` API key: ${redactKey(apiKey)}`
|
|
345
|
+
].filter(Boolean).join("\n")
|
|
346
|
+
);
|
|
347
|
+
}
|
|
193
348
|
var authCommand = new import_commander.Command("auth").description("Manage authentication");
|
|
194
|
-
authCommand.command("login").description("Save an API key to ~/.atoll/config.json").requiredOption("--key <API_KEY>", "API key to store").option("--profile <name>", "Profile name to store this API key under").option("--org <slug>", "Default org slug for this profile").option("--team <team>", "Default team slug or ID for this profile").option("--base-url <url>", "Base URL for this profile").action((opts) => {
|
|
349
|
+
authCommand.command("login").description("Save an API key to ~/.atoll/config.json").requiredOption("--key <API_KEY>", "API key to store").option("--profile <name>", "Profile name to store this API key under").option("--org <slug>", "Default org slug for this profile").option("--team <team>", "Default team slug or ID for this profile").option("--project <id>", "Default project ID for this profile").option("--base-url <url>", "Base URL for this profile").action((opts) => {
|
|
195
350
|
const config = readConfig();
|
|
196
351
|
const profileName = opts.profile || process.env.ATOLL_PROFILE || config.activeProfile;
|
|
197
352
|
const orgSlug = opts.org ?? process.env.ATOLL_CLI_ORG;
|
|
198
353
|
const defaultTeam = opts.team ?? process.env.ATOLL_CLI_TEAM;
|
|
354
|
+
const defaultProject = opts.project;
|
|
199
355
|
const baseUrl = opts.baseUrl;
|
|
200
356
|
if (profileName) {
|
|
201
357
|
const profile = ensureProfile(config, profileName);
|
|
202
358
|
profile.apiKey = opts.key;
|
|
203
359
|
if (orgSlug !== void 0) profile.orgSlug = orgSlug;
|
|
204
360
|
if (defaultTeam !== void 0) profile.defaultTeam = defaultTeam;
|
|
361
|
+
if (defaultProject !== void 0) profile.defaultProject = defaultProject;
|
|
205
362
|
if (baseUrl !== void 0) profile.baseUrl = baseUrl;
|
|
206
363
|
config.activeProfile = profileName;
|
|
207
364
|
} else {
|
|
208
365
|
config.apiKey = opts.key;
|
|
209
366
|
if (orgSlug !== void 0) config.orgSlug = orgSlug;
|
|
210
367
|
if (defaultTeam !== void 0) config.defaultTeam = defaultTeam;
|
|
368
|
+
if (defaultProject !== void 0) config.defaultProject = defaultProject;
|
|
211
369
|
if (baseUrl !== void 0) config.baseUrl = baseUrl;
|
|
212
370
|
}
|
|
213
371
|
writeConfig(config);
|
|
@@ -216,6 +374,93 @@ authCommand.command("login").description("Save an API key to ~/.atoll/config.jso
|
|
|
216
374
|
profileName ? `\u2713 API key saved to profile "${profileName}" in ~/.atoll/config.json` : "\u2713 API key saved to ~/.atoll/config.json"
|
|
217
375
|
);
|
|
218
376
|
});
|
|
377
|
+
authCommand.command("setup").description("Interactively create or update an auth profile").action(async () => {
|
|
378
|
+
if (!import_node_process.stdin.isTTY || !import_node_process.stdout.isTTY) {
|
|
379
|
+
outputError("Interactive setup requires a TTY. Use `atoll auth login --profile <name> --key <API_KEY>` for non-interactive setup.");
|
|
380
|
+
process.exit(2);
|
|
381
|
+
}
|
|
382
|
+
const rl = (0, import_promises.createInterface)({ input: import_node_process.stdin, output: import_node_process.stdout });
|
|
383
|
+
try {
|
|
384
|
+
await runProfileSetup(rl);
|
|
385
|
+
} catch (err) {
|
|
386
|
+
const msg = err.message || String(err);
|
|
387
|
+
if (/API (401|403):/.test(msg)) {
|
|
388
|
+
outputError("API key is invalid or expired");
|
|
389
|
+
process.exit(1);
|
|
390
|
+
}
|
|
391
|
+
outputError(`Setup failed: ${msg}`);
|
|
392
|
+
process.exit(1);
|
|
393
|
+
} finally {
|
|
394
|
+
rl.close();
|
|
395
|
+
}
|
|
396
|
+
});
|
|
397
|
+
authCommand.command("manage").description("Interactively view, add, switch, or delete auth profiles").action(async () => {
|
|
398
|
+
if (!import_node_process.stdin.isTTY || !import_node_process.stdout.isTTY) {
|
|
399
|
+
outputError("Interactive profile management requires a TTY. Use `atoll auth profiles`, `atoll auth setup`, `atoll auth use`, or `atoll auth delete-profile` instead.");
|
|
400
|
+
process.exit(2);
|
|
401
|
+
}
|
|
402
|
+
const rl = (0, import_promises.createInterface)({ input: import_node_process.stdin, output: import_node_process.stdout });
|
|
403
|
+
try {
|
|
404
|
+
while (true) {
|
|
405
|
+
const config = readConfig();
|
|
406
|
+
const profiles = profileSummaries(config);
|
|
407
|
+
console.log("\nProfiles");
|
|
408
|
+
if (profiles.length === 0) {
|
|
409
|
+
console.log(" No profiles configured.");
|
|
410
|
+
} else {
|
|
411
|
+
for (const profile of profiles) console.log(` ${formatProfileLine(profile)}`);
|
|
412
|
+
}
|
|
413
|
+
const action = await selectOne(
|
|
414
|
+
rl,
|
|
415
|
+
"Actions",
|
|
416
|
+
["Add or update profile", "Switch active profile", "Delete profile", "Quit"],
|
|
417
|
+
(item) => item
|
|
418
|
+
);
|
|
419
|
+
if (action === "Add or update profile") {
|
|
420
|
+
await runProfileSetup(rl);
|
|
421
|
+
} else if (action === "Switch active profile") {
|
|
422
|
+
if (profiles.length === 0) {
|
|
423
|
+
console.log("No profiles to switch.");
|
|
424
|
+
continue;
|
|
425
|
+
}
|
|
426
|
+
const selected = await selectOne(rl, "Profiles", profiles, (profile) => formatProfileLine(profile));
|
|
427
|
+
if (selected) {
|
|
428
|
+
const nextConfig = readConfig();
|
|
429
|
+
nextConfig.activeProfile = selected.name;
|
|
430
|
+
writeConfig(nextConfig);
|
|
431
|
+
console.log(`\u2713 Active profile set to "${selected.name}"`);
|
|
432
|
+
}
|
|
433
|
+
} else if (action === "Delete profile") {
|
|
434
|
+
if (profiles.length === 0) {
|
|
435
|
+
console.log("No profiles to delete.");
|
|
436
|
+
continue;
|
|
437
|
+
}
|
|
438
|
+
const selected = await selectOne(rl, "Profiles", profiles, (profile) => formatProfileLine(profile));
|
|
439
|
+
if (!selected) continue;
|
|
440
|
+
const confirm = await ask(rl, `Delete profile "${selected.name}"? Type the profile name to confirm`);
|
|
441
|
+
if (confirm !== selected.name) {
|
|
442
|
+
console.log("Delete cancelled.");
|
|
443
|
+
continue;
|
|
444
|
+
}
|
|
445
|
+
const result = deleteProfile(selected.name);
|
|
446
|
+
console.log(`\u2713 Deleted profile "${selected.name}"`);
|
|
447
|
+
if (result.activeProfile) console.log(` Active profile is now "${result.activeProfile}"`);
|
|
448
|
+
} else {
|
|
449
|
+
break;
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
} catch (err) {
|
|
453
|
+
const msg = err.message || String(err);
|
|
454
|
+
if (/API (401|403):/.test(msg)) {
|
|
455
|
+
outputError("API key is invalid or expired");
|
|
456
|
+
process.exit(1);
|
|
457
|
+
}
|
|
458
|
+
outputError(`Profile management failed: ${msg}`);
|
|
459
|
+
process.exit(1);
|
|
460
|
+
} finally {
|
|
461
|
+
rl.close();
|
|
462
|
+
}
|
|
463
|
+
});
|
|
219
464
|
authCommand.command("status").description("Show current auth status and user info").option("--profile <name>", "Profile name to check").action(async (opts) => {
|
|
220
465
|
const resolved = resolveConfig({ profile: opts.profile });
|
|
221
466
|
const apiKey = resolved.apiKey;
|
|
@@ -250,14 +495,7 @@ authCommand.command("status").description("Show current auth status and user inf
|
|
|
250
495
|
authCommand.command("profiles").description("List saved auth profiles").action(() => {
|
|
251
496
|
const config = readConfig();
|
|
252
497
|
const activeProfile = config.activeProfile;
|
|
253
|
-
const profiles =
|
|
254
|
-
name,
|
|
255
|
-
active: name === activeProfile,
|
|
256
|
-
apiKeySet: !!profile.apiKey,
|
|
257
|
-
orgSlug: profile.orgSlug ?? null,
|
|
258
|
-
defaultTeam: profile.defaultTeam ?? null,
|
|
259
|
-
baseUrl: profile.baseUrl ?? null
|
|
260
|
-
}));
|
|
498
|
+
const profiles = profileSummaries(config);
|
|
261
499
|
if (!process.stdout.isTTY || process.env.OUTPUT_FORMAT === "json") {
|
|
262
500
|
output({ activeProfile: activeProfile ?? null, profiles }, "");
|
|
263
501
|
return;
|
|
@@ -267,17 +505,20 @@ authCommand.command("profiles").description("List saved auth profiles").action((
|
|
|
267
505
|
return;
|
|
268
506
|
}
|
|
269
507
|
for (const profile of profiles) {
|
|
270
|
-
|
|
271
|
-
const parts = [
|
|
272
|
-
`${marker} ${profile.name}`,
|
|
273
|
-
profile.apiKeySet ? "key=set" : "key=not set",
|
|
274
|
-
profile.orgSlug ? `org=${profile.orgSlug}` : null,
|
|
275
|
-
profile.defaultTeam ? `team=${profile.defaultTeam}` : null,
|
|
276
|
-
profile.baseUrl ? `baseUrl=${profile.baseUrl}` : null
|
|
277
|
-
].filter(Boolean);
|
|
278
|
-
console.log(parts.join(" "));
|
|
508
|
+
console.log(formatProfileLine(profile));
|
|
279
509
|
}
|
|
280
510
|
});
|
|
511
|
+
authCommand.command("delete-profile <profile>").description("Delete a saved auth profile").option("--force", "Delete without confirmation").action((profileName, opts) => {
|
|
512
|
+
if (!opts.force) {
|
|
513
|
+
outputError("Profile deletion requires --force. Use `atoll auth manage` for interactive deletion.");
|
|
514
|
+
process.exit(2);
|
|
515
|
+
}
|
|
516
|
+
const result = deleteProfile(profileName);
|
|
517
|
+
output(
|
|
518
|
+
{ deleted: profileName, activeProfile: result.activeProfile },
|
|
519
|
+
result.activeProfile ? `\u2713 Deleted profile "${profileName}". Active profile is now "${result.activeProfile}".` : `\u2713 Deleted profile "${profileName}". No active profile is set.`
|
|
520
|
+
);
|
|
521
|
+
});
|
|
281
522
|
authCommand.command("use <profile>").description("Set the active auth profile").action((profileName) => {
|
|
282
523
|
const config = readConfig();
|
|
283
524
|
if (!getProfile(config, profileName)) {
|
|
@@ -313,6 +554,7 @@ authCommand.command("logout").description("Remove stored API key").option("--pro
|
|
|
313
554
|
|
|
314
555
|
// src/commands/issue.ts
|
|
315
556
|
var import_commander2 = require("commander");
|
|
557
|
+
init_config();
|
|
316
558
|
|
|
317
559
|
// src/lib/colors.ts
|
|
318
560
|
function isTTY() {
|
|
@@ -486,12 +728,12 @@ function padEnd(str, len) {
|
|
|
486
728
|
var issueCommand = new import_commander2.Command("issue").description("Manage issues").addHelpText("after", `
|
|
487
729
|
Examples:
|
|
488
730
|
$ atoll issue list
|
|
489
|
-
$ atoll issue list --status in_progress --priority 1
|
|
731
|
+
$ atoll issue list --project <project-id> --status in_progress --priority 1
|
|
490
732
|
$ atoll issue view ATOLL-42
|
|
491
|
-
$ atoll issue create --title "Fix login" --priority 1 --status todo
|
|
733
|
+
$ atoll issue create --title "Fix login" --project <project-id> --priority 1 --status todo
|
|
492
734
|
$ atoll issue update ATOLL-42 --status done
|
|
493
735
|
$ atoll issue assign ATOLL-42 --to <user-id>`);
|
|
494
|
-
issueCommand.command("list").description("List issues").option("--status <status>", `Filter by status (${VALID_STATUSES.join(", ")})`).option("--assignee <user>", "Filter by assignee ID").option("--priority <n>", "Filter by priority (0=urgent, 1=high, 2=medium, 3=low)", parseInt).option("--limit <n>", "Max results (1-100)", parseInt).option("--offset <n>", "Results offset for pagination", parseInt).action(async (opts) => {
|
|
736
|
+
issueCommand.command("list").description("List issues").option("--status <status>", `Filter by status (${VALID_STATUSES.join(", ")})`).option("--assignee <user>", "Filter by assignee ID").option("--priority <n>", "Filter by priority (0=urgent, 1=high, 2=medium, 3=low)", parseInt).option("--project <id>", "Filter by project ID").option("--limit <n>", "Max results (1-100)", parseInt).option("--offset <n>", "Results offset for pagination", parseInt).action(async (opts) => {
|
|
495
737
|
try {
|
|
496
738
|
if (opts.status && !VALID_STATUSES.includes(opts.status)) {
|
|
497
739
|
outputError(`Invalid status "${opts.status}". Must be one of: ${VALID_STATUSES.join(", ")}`);
|
|
@@ -505,10 +747,12 @@ issueCommand.command("list").description("List issues").option("--status <status
|
|
|
505
747
|
const org = await resolveOrg(client);
|
|
506
748
|
const limit = normalizeLimit(opts.limit);
|
|
507
749
|
const offset = normalizeOffset(opts.offset);
|
|
750
|
+
const projectId = resolveDefaultProject(opts.project);
|
|
508
751
|
const params = new URLSearchParams();
|
|
509
752
|
if (opts.status) params.set("status", opts.status);
|
|
510
753
|
if (opts.assignee) params.set("assigneeId", opts.assignee);
|
|
511
754
|
if (opts.priority !== void 0) params.set("priority", String(opts.priority));
|
|
755
|
+
if (projectId) params.set("projectId", projectId);
|
|
512
756
|
params.set("limit", String(limit));
|
|
513
757
|
if (offset > 0) params.set("offset", String(offset));
|
|
514
758
|
const qs = params.toString();
|
|
@@ -595,7 +839,7 @@ Subtasks: ${enriched.sub_tasks.length}`);
|
|
|
595
839
|
}
|
|
596
840
|
issueCommand.command("view <identifier>").description("View issue details (UUID or PREFIX-NUMBER e.g. ATOLL-42)").action(viewIssue);
|
|
597
841
|
issueCommand.command("get <identifier>").description("Get issue details (UUID or PREFIX-NUMBER e.g. ATOLL-42)").action(viewIssue);
|
|
598
|
-
issueCommand.command("create").description("Create a new issue").requiredOption("--title <title>", "Issue title").option("--description <text>", "Issue description").option("--status <status>", `Status (${VALID_STATUSES.join(", ")})`).option("--priority <n>", "Priority (0=urgent, 1=high, 2=medium, 3=low)", parseInt).action(async (opts) => {
|
|
842
|
+
issueCommand.command("create").description("Create a new issue").requiredOption("--title <title>", "Issue title").option("--description <text>", "Issue description").option("--status <status>", `Status (${VALID_STATUSES.join(", ")})`).option("--priority <n>", "Priority (0=urgent, 1=high, 2=medium, 3=low)", parseInt).option("--project <id>", "Project ID").action(async (opts) => {
|
|
599
843
|
try {
|
|
600
844
|
if (opts.status && !VALID_STATUSES.includes(opts.status)) {
|
|
601
845
|
outputError(`Invalid status "${opts.status}". Must be one of: ${VALID_STATUSES.join(", ")}`);
|
|
@@ -608,9 +852,11 @@ issueCommand.command("create").description("Create a new issue").requiredOption(
|
|
|
608
852
|
const client = new AtollClient();
|
|
609
853
|
const org = await resolveOrg(client);
|
|
610
854
|
const body = { title: opts.title };
|
|
855
|
+
const projectId = resolveDefaultProject(opts.project);
|
|
611
856
|
if (opts.description !== void 0) body.description = opts.description;
|
|
612
857
|
if (opts.status) body.status = opts.status;
|
|
613
858
|
if (opts.priority !== void 0) body.priority = opts.priority;
|
|
859
|
+
if (projectId) body.projectId = projectId;
|
|
614
860
|
const { issue } = await client.post(`/api/orgs/${org.id}/issues`, body);
|
|
615
861
|
const projects = await fetchProjectMap(client, org.id);
|
|
616
862
|
const enriched = attachIssueUrl(issue, client.baseUrl, org.slug, projects);
|
|
@@ -1012,22 +1258,32 @@ projectCommand.command("get <projectId>").description("Get project details and i
|
|
|
1012
1258
|
|
|
1013
1259
|
// src/commands/milestone.ts
|
|
1014
1260
|
var import_commander5 = require("commander");
|
|
1261
|
+
init_config();
|
|
1015
1262
|
function progressBar2(progress, width = 20) {
|
|
1016
1263
|
const filled = Math.round(progress / 100 * width);
|
|
1017
1264
|
const empty = width - filled;
|
|
1018
1265
|
return `[${"\u2588".repeat(filled)}${"\u2591".repeat(empty)}] ${progress}%`;
|
|
1019
1266
|
}
|
|
1267
|
+
function resolveProjectOrExit(explicitProject) {
|
|
1268
|
+
const projectId = resolveDefaultProject(explicitProject);
|
|
1269
|
+
if (!projectId) {
|
|
1270
|
+
outputError("No project selected. Pass --project <id> or run `atoll config set-project <project-id>`.");
|
|
1271
|
+
process.exit(2);
|
|
1272
|
+
}
|
|
1273
|
+
return projectId;
|
|
1274
|
+
}
|
|
1020
1275
|
var milestoneCommand = new import_commander5.Command("milestone").description("Manage milestones").addHelpText("after", `
|
|
1021
1276
|
Examples:
|
|
1022
1277
|
$ atoll milestone list --project <project-id>
|
|
1023
1278
|
$ atoll milestone create --project <project-id> --name "v1.0" --date 2026-06-01`);
|
|
1024
|
-
milestoneCommand.command("list").description("List milestones for a project").
|
|
1279
|
+
milestoneCommand.command("list").description("List milestones for a project").option("--project <id>", "Project ID").option("--limit <n>", "Max results (1-100)", parseInt).action(async (opts) => {
|
|
1280
|
+
const projectId = resolveProjectOrExit(opts.project);
|
|
1025
1281
|
const client = new AtollClient();
|
|
1026
1282
|
try {
|
|
1027
1283
|
const limit = normalizeLimit(opts.limit);
|
|
1028
1284
|
const orgId = await resolveOrgId(client);
|
|
1029
1285
|
const data = await client.get(
|
|
1030
|
-
`/api/orgs/${orgId}/projects/${
|
|
1286
|
+
`/api/orgs/${orgId}/projects/${projectId}/milestones`
|
|
1031
1287
|
);
|
|
1032
1288
|
const allMilestones = data.milestones ?? [];
|
|
1033
1289
|
const milestones = allMilestones.slice(0, limit);
|
|
@@ -1065,12 +1321,13 @@ milestoneCommand.command("list").description("List milestones for a project").re
|
|
|
1065
1321
|
handleApiError(err);
|
|
1066
1322
|
}
|
|
1067
1323
|
});
|
|
1068
|
-
milestoneCommand.command("create").description("Create a new milestone").
|
|
1324
|
+
milestoneCommand.command("create").description("Create a new milestone").option("--project <id>", "Project ID").requiredOption("--name <name>", "Milestone name").option("--date <YYYY-MM-DD>", "Due date").option("--description <desc>", "Description").action(async (opts) => {
|
|
1325
|
+
const projectId = resolveProjectOrExit(opts.project);
|
|
1069
1326
|
const client = new AtollClient();
|
|
1070
1327
|
try {
|
|
1071
1328
|
const orgId = await resolveOrgId(client);
|
|
1072
1329
|
const data = await client.post(
|
|
1073
|
-
`/api/orgs/${orgId}/projects/${
|
|
1330
|
+
`/api/orgs/${orgId}/projects/${projectId}/milestones`,
|
|
1074
1331
|
{
|
|
1075
1332
|
name: opts.name,
|
|
1076
1333
|
description: opts.description ?? null,
|
|
@@ -1092,17 +1349,37 @@ milestoneCommand.command("create").description("Create a new milestone").require
|
|
|
1092
1349
|
// src/commands/config.ts
|
|
1093
1350
|
var import_commander6 = require("commander");
|
|
1094
1351
|
init_config();
|
|
1095
|
-
|
|
1352
|
+
function configLocation(profileName) {
|
|
1353
|
+
return profileName ? `${CONFIG_PATH} profile "${profileName}"` : CONFIG_PATH;
|
|
1354
|
+
}
|
|
1355
|
+
function sourceForField(cfg, profileName, field, envVar) {
|
|
1356
|
+
if (process.env[envVar]) return `$${envVar}`;
|
|
1357
|
+
if (profileName && cfg.profiles?.[profileName]?.[field]) return configLocation(profileName);
|
|
1358
|
+
if (!profileName && cfg[field]) return configLocation();
|
|
1359
|
+
return null;
|
|
1360
|
+
}
|
|
1361
|
+
function withSource(value, source) {
|
|
1362
|
+
return source ? `${bold(value)} ${dim(`from ${source}`)}` : bold(value);
|
|
1363
|
+
}
|
|
1364
|
+
var configCommand = new import_commander6.Command("config").description("Manage CLI configuration (org, team, project, API key)").addHelpText("after", `
|
|
1096
1365
|
Examples:
|
|
1097
1366
|
$ atoll config show
|
|
1098
1367
|
$ atoll config set-org my-org
|
|
1099
1368
|
$ atoll config set-team team-abc123
|
|
1369
|
+
$ atoll config set-project project-abc123
|
|
1100
1370
|
$ atoll config set-base-url https://atollhq.com`);
|
|
1101
1371
|
configCommand.command("show").description("Display current configuration").option("--profile <name>", "Profile name to show").action((opts) => {
|
|
1102
1372
|
const cfg = readConfig();
|
|
1103
1373
|
const activeProfile = cfg.activeProfile;
|
|
1104
1374
|
const resolved = resolveConfig({ profile: opts.profile });
|
|
1105
1375
|
const apiKeySet = !!resolved.apiKey;
|
|
1376
|
+
const sources = {
|
|
1377
|
+
apiKey: sourceForField(cfg, resolved.profile, "apiKey", "ATOLL_API_KEY"),
|
|
1378
|
+
orgSlug: sourceForField(cfg, resolved.profile, "orgSlug", "ATOLL_ORG"),
|
|
1379
|
+
defaultTeam: sourceForField(cfg, resolved.profile, "defaultTeam", "ATOLL_TEAM"),
|
|
1380
|
+
defaultProject: sourceForField(cfg, resolved.profile, "defaultProject", "ATOLL_PROJECT"),
|
|
1381
|
+
baseUrl: sourceForField(cfg, resolved.profile, "baseUrl", "ATOLL_BASE_URL")
|
|
1382
|
+
};
|
|
1106
1383
|
if (!process.stdout.isTTY || process.env.OUTPUT_FORMAT === "json") {
|
|
1107
1384
|
output(
|
|
1108
1385
|
{
|
|
@@ -1110,8 +1387,10 @@ configCommand.command("show").description("Display current configuration").optio
|
|
|
1110
1387
|
selectedProfile: resolved.profile ?? null,
|
|
1111
1388
|
orgSlug: resolved.orgSlug ?? null,
|
|
1112
1389
|
defaultTeam: resolved.defaultTeam ?? null,
|
|
1390
|
+
defaultProject: resolved.defaultProject ?? null,
|
|
1113
1391
|
baseUrl: resolved.baseUrl ?? null,
|
|
1114
1392
|
apiKeySet,
|
|
1393
|
+
sources,
|
|
1115
1394
|
profiles: Object.keys(cfg.profiles ?? {}).sort()
|
|
1116
1395
|
},
|
|
1117
1396
|
""
|
|
@@ -1121,10 +1400,11 @@ configCommand.command("show").description("Display current configuration").optio
|
|
|
1121
1400
|
console.log(`${bold("Atoll CLI Configuration")}`);
|
|
1122
1401
|
console.log(` ${dim("Active profile:")} ${activeProfile ? bold(activeProfile) : gray("(not set)")}`);
|
|
1123
1402
|
console.log(` ${dim("Selected profile:")} ${resolved.profile ? bold(resolved.profile) : gray("(legacy config)")}`);
|
|
1124
|
-
console.log(` ${dim("Org slug:")} ${resolved.orgSlug ?
|
|
1125
|
-
console.log(` ${dim("Default team:")} ${resolved.defaultTeam ?
|
|
1126
|
-
console.log(` ${dim("
|
|
1127
|
-
console.log(` ${dim("
|
|
1403
|
+
console.log(` ${dim("Org slug:")} ${resolved.orgSlug ? withSource(resolved.orgSlug, sources.orgSlug) : gray("(not set)")}`);
|
|
1404
|
+
console.log(` ${dim("Default team:")} ${resolved.defaultTeam ? withSource(resolved.defaultTeam, sources.defaultTeam) : gray("(not set)")}`);
|
|
1405
|
+
console.log(` ${dim("Default proj:")} ${resolved.defaultProject ? withSource(resolved.defaultProject, sources.defaultProject) : gray("(not set)")}`);
|
|
1406
|
+
console.log(` ${dim("Base URL:")} ${resolved.baseUrl ? withSource(resolved.baseUrl, sources.baseUrl) : gray("(default)")}`);
|
|
1407
|
+
console.log(` ${dim("API key:")} ${apiKeySet ? withSource("set", sources.apiKey) : gray("not set \u2014 run `atoll auth login`")}`);
|
|
1128
1408
|
});
|
|
1129
1409
|
configCommand.command("set-org <slug>").description("Set the default organisation slug").option("--profile <name>", "Profile name to update").action((slug, opts) => {
|
|
1130
1410
|
const cfg = readConfig();
|
|
@@ -1154,6 +1434,34 @@ configCommand.command("set-team <team>").description("Set the default team slug
|
|
|
1154
1434
|
profileName ? success(`Default team for profile "${profileName}" set to "${team}"`) : success(`Default team set to "${team}"`)
|
|
1155
1435
|
);
|
|
1156
1436
|
});
|
|
1437
|
+
configCommand.command("set-project <project>").description("Set the default project ID").option("--profile <name>", "Profile name to update").action((project, opts) => {
|
|
1438
|
+
const cfg = readConfig();
|
|
1439
|
+
const profileName = opts.profile || process.env.ATOLL_PROFILE || cfg.activeProfile;
|
|
1440
|
+
if (profileName) {
|
|
1441
|
+
ensureProfile(cfg, profileName).defaultProject = project;
|
|
1442
|
+
} else {
|
|
1443
|
+
cfg.defaultProject = project;
|
|
1444
|
+
}
|
|
1445
|
+
writeConfig(cfg);
|
|
1446
|
+
output(
|
|
1447
|
+
{ defaultProject: project, profile: profileName ?? null },
|
|
1448
|
+
profileName ? success(`Default project for profile "${profileName}" set to "${project}"`) : success(`Default project set to "${project}"`)
|
|
1449
|
+
);
|
|
1450
|
+
});
|
|
1451
|
+
configCommand.command("clear-project").description("Clear the default project ID").option("--profile <name>", "Profile name to update").action((opts) => {
|
|
1452
|
+
const cfg = readConfig();
|
|
1453
|
+
const profileName = opts.profile || process.env.ATOLL_PROFILE || cfg.activeProfile;
|
|
1454
|
+
if (profileName) {
|
|
1455
|
+
delete ensureProfile(cfg, profileName).defaultProject;
|
|
1456
|
+
} else {
|
|
1457
|
+
delete cfg.defaultProject;
|
|
1458
|
+
}
|
|
1459
|
+
writeConfig(cfg);
|
|
1460
|
+
output(
|
|
1461
|
+
{ defaultProject: null, profile: profileName ?? null },
|
|
1462
|
+
profileName ? success(`Default project for profile "${profileName}" cleared`) : success("Default project cleared")
|
|
1463
|
+
);
|
|
1464
|
+
});
|
|
1157
1465
|
configCommand.command("set-base-url <url>").description("Set the default API base URL").option("--profile <name>", "Profile name to update").action((url, opts) => {
|
|
1158
1466
|
const cfg = readConfig();
|
|
1159
1467
|
const profileName = opts.profile || process.env.ATOLL_PROFILE || cfg.activeProfile;
|
|
@@ -1424,6 +1732,7 @@ var agentContextCommand = new import_commander9.Command("agent-context").descrip
|
|
|
1424
1732
|
apiKeySet: Boolean(profile.apiKey),
|
|
1425
1733
|
orgSlug: profile.orgSlug ?? null,
|
|
1426
1734
|
defaultTeam: profile.defaultTeam ?? null,
|
|
1735
|
+
defaultProject: profile.defaultProject ?? null,
|
|
1427
1736
|
baseUrl: profile.baseUrl ?? null
|
|
1428
1737
|
}));
|
|
1429
1738
|
outputJson({
|
|
@@ -1442,6 +1751,7 @@ var agentContextCommand = new import_commander9.Command("agent-context").descrip
|
|
|
1442
1751
|
selectedProfile: resolved.profile ?? null,
|
|
1443
1752
|
orgSlug: resolved.orgSlug ?? null,
|
|
1444
1753
|
defaultTeam: resolved.defaultTeam ?? null,
|
|
1754
|
+
defaultProject: resolved.defaultProject ?? null,
|
|
1445
1755
|
baseUrl: resolved.baseUrl ?? null,
|
|
1446
1756
|
apiKeySet: Boolean(resolved.apiKey)
|
|
1447
1757
|
},
|
|
@@ -1465,6 +1775,15 @@ function buildCommandContext() {
|
|
|
1465
1775
|
},
|
|
1466
1776
|
signal_types: SIGNAL_TYPES
|
|
1467
1777
|
},
|
|
1778
|
+
auth: {
|
|
1779
|
+
subcommands: {
|
|
1780
|
+
setup: { description: "Interactive profile setup", flags: {} },
|
|
1781
|
+
manage: { description: "Interactive profile manager for listing, adding/updating, switching, and deleting profiles", flags: {} },
|
|
1782
|
+
profiles: { description: "List saved profiles", flags: { "--json": { type: "boolean" } } },
|
|
1783
|
+
use: { args: ["profile"], flags: { "--json": { type: "boolean" } } },
|
|
1784
|
+
"delete-profile": { args: ["profile"], flags: { "--force": { type: "boolean" }, "--json": { type: "boolean" } } }
|
|
1785
|
+
}
|
|
1786
|
+
},
|
|
1468
1787
|
issue: {
|
|
1469
1788
|
subcommands: {
|
|
1470
1789
|
list: {
|
|
@@ -1472,6 +1791,7 @@ function buildCommandContext() {
|
|
|
1472
1791
|
"--status": { type: "enum", values: ISSUE_STATUSES },
|
|
1473
1792
|
"--assignee": { type: "string" },
|
|
1474
1793
|
"--priority": { type: "enum", values: PRIORITIES },
|
|
1794
|
+
"--project": { type: "string", default_from: "ATOLL_PROJECT or profile.defaultProject" },
|
|
1475
1795
|
"--limit": { type: "integer", min: 1, max: 100, default: 25 },
|
|
1476
1796
|
"--offset": { type: "integer", min: 0, default: 0 },
|
|
1477
1797
|
"--json": { type: "boolean", default: false }
|
|
@@ -1485,6 +1805,7 @@ function buildCommandContext() {
|
|
|
1485
1805
|
"--description": { type: "string" },
|
|
1486
1806
|
"--status": { type: "enum", values: ISSUE_STATUSES },
|
|
1487
1807
|
"--priority": { type: "enum", values: PRIORITIES },
|
|
1808
|
+
"--project": { type: "string", default_from: "ATOLL_PROJECT or profile.defaultProject" },
|
|
1488
1809
|
"--json": { type: "boolean", default: false }
|
|
1489
1810
|
}
|
|
1490
1811
|
},
|
|
@@ -1511,6 +1832,26 @@ function buildCommandContext() {
|
|
|
1511
1832
|
unassign: { args: ["identifier"], flags: { "--json": { type: "boolean" } } }
|
|
1512
1833
|
}
|
|
1513
1834
|
},
|
|
1835
|
+
milestone: {
|
|
1836
|
+
subcommands: {
|
|
1837
|
+
list: {
|
|
1838
|
+
flags: {
|
|
1839
|
+
"--project": { type: "string", default_from: "ATOLL_PROJECT or profile.defaultProject", required_without_default: true },
|
|
1840
|
+
"--limit": { type: "integer", min: 1, max: 100, default: 25 },
|
|
1841
|
+
"--json": { type: "boolean" }
|
|
1842
|
+
}
|
|
1843
|
+
},
|
|
1844
|
+
create: {
|
|
1845
|
+
flags: {
|
|
1846
|
+
"--project": { type: "string", default_from: "ATOLL_PROJECT or profile.defaultProject", required_without_default: true },
|
|
1847
|
+
"--name": { type: "string", required: true },
|
|
1848
|
+
"--date": { type: "string" },
|
|
1849
|
+
"--description": { type: "string" },
|
|
1850
|
+
"--json": { type: "boolean" }
|
|
1851
|
+
}
|
|
1852
|
+
}
|
|
1853
|
+
}
|
|
1854
|
+
},
|
|
1514
1855
|
project: {
|
|
1515
1856
|
subcommands: {
|
|
1516
1857
|
list: { flags: { "--limit": { type: "integer", min: 1, max: 100, default: 25 }, "--json": { type: "boolean" } } },
|
|
@@ -1583,7 +1924,7 @@ init_config();
|
|
|
1583
1924
|
var CONFIG_DIR2 = (0, import_node_path3.join)((0, import_node_os2.homedir)(), ".atoll");
|
|
1584
1925
|
var FEEDBACK_PATH = (0, import_node_path3.join)(CONFIG_DIR2, "feedback.jsonl");
|
|
1585
1926
|
var VALID_TYPES = ["bug", "feature"];
|
|
1586
|
-
var
|
|
1927
|
+
var DEFAULT_BASE_URL3 = "https://atollhq.com";
|
|
1587
1928
|
var feedbackCommand = new import_commander10.Command("feedback").description("Record CLI or platform feedback").argument("[text...]", "Feedback text").option("--type <type>", `Feedback type (${VALID_TYPES.join(", ")})`, "bug").option("--url <url>", "Related page or endpoint URL").option("--send", "Also submit to the configured Atoll feedback endpoint").action(async (text, opts) => {
|
|
1588
1929
|
if (text.length === 0) {
|
|
1589
1930
|
outputError('Feedback text is required. Usage: atoll feedback "what went wrong"');
|
|
@@ -1636,7 +1977,7 @@ async function recordFeedback(description, opts) {
|
|
|
1636
1977
|
upstream_status: null,
|
|
1637
1978
|
upstream_error: null
|
|
1638
1979
|
};
|
|
1639
|
-
const endpoint = process.env.ATOLL_FEEDBACK_ENDPOINT || (opts.send ? `${resolveConfig().baseUrl ||
|
|
1980
|
+
const endpoint = process.env.ATOLL_FEEDBACK_ENDPOINT || (opts.send ? `${resolveConfig().baseUrl || DEFAULT_BASE_URL3}/api/feedback` : null);
|
|
1640
1981
|
if (endpoint) {
|
|
1641
1982
|
try {
|
|
1642
1983
|
const response = await fetch(endpoint, {
|
|
@@ -1695,6 +2036,7 @@ Examples:
|
|
|
1695
2036
|
$ atoll heartbeat
|
|
1696
2037
|
$ atoll project list
|
|
1697
2038
|
$ atoll milestone list --project <id>
|
|
2039
|
+
$ atoll auth setup
|
|
1698
2040
|
$ atoll config show`).option("--profile <name>", "Use a saved auth profile (env: ATOLL_PROFILE)").option("--org <slug>", "Override default org slug (env: ATOLL_ORG)").option("--team <id>", "Override default team slug/ID (env: ATOLL_TEAM)").option("--json", "Emit machine-readable JSON").hook("preAction", (_thisCommand, actionCommand) => {
|
|
1699
2041
|
const opts = actionCommand.optsWithGlobals();
|
|
1700
2042
|
if (opts.json) {
|
|
@@ -1748,8 +2090,8 @@ _atoll_completions() {
|
|
|
1748
2090
|
local issue_cmds="list view get create update archive unarchive delete assign unassign"
|
|
1749
2091
|
local project_cmds="list create view get"
|
|
1750
2092
|
local milestone_cmds="list create"
|
|
1751
|
-
local config_cmds="show set-org set-team set-base-url"
|
|
1752
|
-
local auth_cmds="login logout status profiles use"
|
|
2093
|
+
local config_cmds="show set-org set-team set-project clear-project set-base-url"
|
|
2094
|
+
local auth_cmds="login setup manage logout status profiles use delete-profile"
|
|
1753
2095
|
local webhook_cmds="list create delete"
|
|
1754
2096
|
local feedback_cmds="add list"
|
|
1755
2097
|
local completion_cmds="bash zsh"
|
|
@@ -1866,15 +2208,20 @@ _atoll() {
|
|
|
1866
2208
|
'show[Show configuration]' \\
|
|
1867
2209
|
'set-org[Set default org slug]' \\
|
|
1868
2210
|
'set-team[Set default team]' \\
|
|
2211
|
+
'set-project[Set default project]' \\
|
|
2212
|
+
'clear-project[Clear default project]' \\
|
|
1869
2213
|
'set-base-url[Set default API base URL]'
|
|
1870
2214
|
;;
|
|
1871
2215
|
auth)
|
|
1872
2216
|
_values 'subcommand' \\
|
|
1873
2217
|
'login[Log in]' \\
|
|
2218
|
+
'setup[Interactive profile setup]' \\
|
|
2219
|
+
'manage[Interactive profile manager]' \\
|
|
1874
2220
|
'logout[Log out]' \\
|
|
1875
2221
|
'status[Show auth status]' \\
|
|
1876
2222
|
'profiles[List auth profiles]' \\
|
|
1877
|
-
'use[Set active auth profile]'
|
|
2223
|
+
'use[Set active auth profile]' \\
|
|
2224
|
+
'delete-profile[Delete auth profile]'
|
|
1878
2225
|
;;
|
|
1879
2226
|
webhook)
|
|
1880
2227
|
_values 'subcommand' \\
|