exponential-cli 1.5.0 → 1.7.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/dist/index.js +627 -38
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/index.ts
|
|
4
|
-
import { Command as
|
|
4
|
+
import { Command as Command18 } from "commander";
|
|
5
5
|
|
|
6
6
|
// src/commands/auth.ts
|
|
7
7
|
import { Command } from "commander";
|
|
@@ -1176,6 +1176,127 @@ Pipeline Stages (${stages.length})`));
|
|
|
1176
1176
|
}
|
|
1177
1177
|
console.log();
|
|
1178
1178
|
}
|
|
1179
|
+
function outputPageJson(page) {
|
|
1180
|
+
console.log(JSON.stringify(page, null, 2));
|
|
1181
|
+
}
|
|
1182
|
+
function outputPagePretty(page) {
|
|
1183
|
+
console.log(chalk3.gray("\u2500".repeat(50)));
|
|
1184
|
+
console.log(`
|
|
1185
|
+
${chalk3.bold(page.title)}`);
|
|
1186
|
+
console.log(chalk3.gray(` ID: ${page.id}`));
|
|
1187
|
+
if (page.project) {
|
|
1188
|
+
console.log(` ${chalk3.gray("Project:")} ${page.project.name}`);
|
|
1189
|
+
}
|
|
1190
|
+
if (page.body) {
|
|
1191
|
+
console.log(`
|
|
1192
|
+
${page.body}
|
|
1193
|
+
`);
|
|
1194
|
+
}
|
|
1195
|
+
}
|
|
1196
|
+
function outputPagesJson(pages) {
|
|
1197
|
+
console.log(JSON.stringify({ pages, total: pages.length }, null, 2));
|
|
1198
|
+
}
|
|
1199
|
+
function outputPagesPretty(pages) {
|
|
1200
|
+
if (pages.length === 0) {
|
|
1201
|
+
console.log(chalk3.gray("No pages found."));
|
|
1202
|
+
return;
|
|
1203
|
+
}
|
|
1204
|
+
console.log(chalk3.bold(`
|
|
1205
|
+
Pages (${pages.length} total)`));
|
|
1206
|
+
console.log(chalk3.gray("\u2500".repeat(50)));
|
|
1207
|
+
for (const p of pages) {
|
|
1208
|
+
console.log(` ${chalk3.bold(p.title)}`);
|
|
1209
|
+
console.log(chalk3.gray(` ID: ${p.id}`));
|
|
1210
|
+
}
|
|
1211
|
+
console.log();
|
|
1212
|
+
}
|
|
1213
|
+
function outputRequirementJson(requirement) {
|
|
1214
|
+
console.log(JSON.stringify(requirement, null, 2));
|
|
1215
|
+
}
|
|
1216
|
+
function outputRequirementPretty(requirement) {
|
|
1217
|
+
const met = requirement.checkedAt ? chalk3.green("[met]") : chalk3.gray("[unmet]");
|
|
1218
|
+
const kind = requirement.kind ? chalk3.gray(` (${requirement.kind})`) : "";
|
|
1219
|
+
console.log(` ${met} ${requirement.statement}${kind}`);
|
|
1220
|
+
console.log(chalk3.gray(` ID: ${requirement.id}`));
|
|
1221
|
+
}
|
|
1222
|
+
function outputRequirementsJson(requirements) {
|
|
1223
|
+
console.log(JSON.stringify({ requirements, total: requirements.length }, null, 2));
|
|
1224
|
+
}
|
|
1225
|
+
function outputRequirementsPretty(requirements) {
|
|
1226
|
+
if (requirements.length === 0) {
|
|
1227
|
+
console.log(chalk3.gray("No requirements found."));
|
|
1228
|
+
return;
|
|
1229
|
+
}
|
|
1230
|
+
const metCount = requirements.filter((r) => r.checkedAt).length;
|
|
1231
|
+
console.log(chalk3.bold(`
|
|
1232
|
+
Requirements (${metCount}/${requirements.length} met)`));
|
|
1233
|
+
console.log(chalk3.gray("\u2500".repeat(50)));
|
|
1234
|
+
for (const r of requirements) {
|
|
1235
|
+
outputRequirementPretty(r);
|
|
1236
|
+
}
|
|
1237
|
+
console.log();
|
|
1238
|
+
}
|
|
1239
|
+
function outputRequirementBatchJson(results) {
|
|
1240
|
+
console.log(JSON.stringify({
|
|
1241
|
+
results,
|
|
1242
|
+
total: results.length,
|
|
1243
|
+
succeeded: results.filter((r) => r.success).length,
|
|
1244
|
+
failed: results.filter((r) => !r.success).length
|
|
1245
|
+
}, null, 2));
|
|
1246
|
+
}
|
|
1247
|
+
function outputRequirementBatchPretty(results) {
|
|
1248
|
+
for (const r of results) {
|
|
1249
|
+
if (r.success) {
|
|
1250
|
+
console.log(` ${chalk3.green("\u2713")} #${r.index}: ${r.requirement?.statement ?? ""}`);
|
|
1251
|
+
} else {
|
|
1252
|
+
console.log(` ${chalk3.red("\u2717")} #${r.index}: ${r.error ?? "failed"}`);
|
|
1253
|
+
}
|
|
1254
|
+
}
|
|
1255
|
+
const ok = results.filter((r) => r.success).length;
|
|
1256
|
+
console.log(chalk3.bold(`
|
|
1257
|
+
${ok}/${results.length} requirements added`));
|
|
1258
|
+
}
|
|
1259
|
+
function getScopeStatusColor(status) {
|
|
1260
|
+
switch (status) {
|
|
1261
|
+
case "PLANNED":
|
|
1262
|
+
return "blue";
|
|
1263
|
+
case "IN_PROGRESS":
|
|
1264
|
+
return "yellow";
|
|
1265
|
+
case "SHIPPED":
|
|
1266
|
+
return "green";
|
|
1267
|
+
case "DEPRECATED":
|
|
1268
|
+
return "red";
|
|
1269
|
+
default:
|
|
1270
|
+
return "gray";
|
|
1271
|
+
}
|
|
1272
|
+
}
|
|
1273
|
+
function outputScopeJson(scope) {
|
|
1274
|
+
console.log(JSON.stringify(scope, null, 2));
|
|
1275
|
+
}
|
|
1276
|
+
function outputScopePretty(scope) {
|
|
1277
|
+
const badge = chalk3[getScopeStatusColor(scope.status)](`[${scope.status}]`);
|
|
1278
|
+
console.log(` ${badge} ${chalk3.bold(scope.version)}`);
|
|
1279
|
+
console.log(chalk3.gray(` ID: ${scope.id}`));
|
|
1280
|
+
if (scope.description) {
|
|
1281
|
+
console.log(` ${scope.description.substring(0, 120)}${scope.description.length > 120 ? "..." : ""}`);
|
|
1282
|
+
}
|
|
1283
|
+
}
|
|
1284
|
+
function outputScopesJson(scopes) {
|
|
1285
|
+
console.log(JSON.stringify({ scopes, total: scopes.length }, null, 2));
|
|
1286
|
+
}
|
|
1287
|
+
function outputScopesPretty(scopes) {
|
|
1288
|
+
if (scopes.length === 0) {
|
|
1289
|
+
console.log(chalk3.gray("No scopes found."));
|
|
1290
|
+
return;
|
|
1291
|
+
}
|
|
1292
|
+
console.log(chalk3.bold(`
|
|
1293
|
+
Scopes (${scopes.length} total)`));
|
|
1294
|
+
console.log(chalk3.gray("\u2500".repeat(50)));
|
|
1295
|
+
for (const s of scopes) {
|
|
1296
|
+
outputScopePretty(s);
|
|
1297
|
+
}
|
|
1298
|
+
console.log();
|
|
1299
|
+
}
|
|
1179
1300
|
|
|
1180
1301
|
// src/commands/actions.ts
|
|
1181
1302
|
var VALID_PRIORITIES = [
|
|
@@ -1582,6 +1703,28 @@ function createContactsCommand() {
|
|
|
1582
1703
|
handleError(error, useJson);
|
|
1583
1704
|
}
|
|
1584
1705
|
});
|
|
1706
|
+
contacts.command("enrich").description("Queue a web-search enrichment job for an existing contact (runs regardless of the workspace auto-enrich setting)").argument("<id>", "Contact ID").action(async (id, _options, cmd) => {
|
|
1707
|
+
const globalOpts = cmd.optsWithGlobals();
|
|
1708
|
+
const useJson = shouldUseJson(globalOpts.json, globalOpts.pretty);
|
|
1709
|
+
try {
|
|
1710
|
+
const client = getClient();
|
|
1711
|
+
const result = await client.contacts.enrich(id);
|
|
1712
|
+
if (useJson) {
|
|
1713
|
+
console.log(JSON.stringify(result, null, 2));
|
|
1714
|
+
} else if (result.enqueued) {
|
|
1715
|
+
console.log(`
|
|
1716
|
+
\u2713 Enrichment queued for contact ${id} (job ${result.enrichmentId})`);
|
|
1717
|
+
} else if (result.reason === "already-in-flight") {
|
|
1718
|
+
console.log(`
|
|
1719
|
+
\u2022 Enrichment already in progress for contact ${id} (job ${result.enrichmentId})`);
|
|
1720
|
+
} else {
|
|
1721
|
+
console.log(`
|
|
1722
|
+
\u2022 Not queued for contact ${id}${result.reason ? ` (${result.reason})` : ""}`);
|
|
1723
|
+
}
|
|
1724
|
+
} catch (error) {
|
|
1725
|
+
handleError(error, useJson);
|
|
1726
|
+
}
|
|
1727
|
+
});
|
|
1585
1728
|
return contacts;
|
|
1586
1729
|
}
|
|
1587
1730
|
|
|
@@ -1927,7 +2070,7 @@ function createEpicsCommand() {
|
|
|
1927
2070
|
}
|
|
1928
2071
|
|
|
1929
2072
|
// src/commands/features.ts
|
|
1930
|
-
import { Command as
|
|
2073
|
+
import { Command as Command10 } from "commander";
|
|
1931
2074
|
|
|
1932
2075
|
// src/commands/stories.ts
|
|
1933
2076
|
import { Command as Command6 } from "commander";
|
|
@@ -2036,12 +2179,325 @@ function createStoriesCommand() {
|
|
|
2036
2179
|
return stories;
|
|
2037
2180
|
}
|
|
2038
2181
|
|
|
2182
|
+
// src/commands/scopes.ts
|
|
2183
|
+
import { Command as Command7 } from "commander";
|
|
2184
|
+
var SCOPE_STATUSES = [
|
|
2185
|
+
"PLANNED",
|
|
2186
|
+
"IN_PROGRESS",
|
|
2187
|
+
"SHIPPED",
|
|
2188
|
+
"DEPRECATED"
|
|
2189
|
+
];
|
|
2190
|
+
function validateScopeStatus(value) {
|
|
2191
|
+
if (!value) return void 0;
|
|
2192
|
+
if (!SCOPE_STATUSES.includes(value)) {
|
|
2193
|
+
throw new Error(
|
|
2194
|
+
`Invalid status "${value}". Valid: ${SCOPE_STATUSES.join(", ")}`
|
|
2195
|
+
);
|
|
2196
|
+
}
|
|
2197
|
+
return value;
|
|
2198
|
+
}
|
|
2199
|
+
function createScopesCommand() {
|
|
2200
|
+
const scopes = new Command7("scopes").description(
|
|
2201
|
+
`Manage a feature's scopes: shippable increments ("V1", "V2"), each with its own lifecycle.`
|
|
2202
|
+
);
|
|
2203
|
+
scopes.command("list").description("List a feature's scopes").requiredOption("--feature <id>", "Feature CUID").action(
|
|
2204
|
+
async (options, cmd) => {
|
|
2205
|
+
const globalOpts = cmd.optsWithGlobals();
|
|
2206
|
+
const useJson = shouldUseJson(globalOpts.json, globalOpts.pretty);
|
|
2207
|
+
try {
|
|
2208
|
+
const client = getClient();
|
|
2209
|
+
const list = await client.scopes.list({ featureId: options.feature });
|
|
2210
|
+
if (useJson) outputScopesJson(list);
|
|
2211
|
+
else outputScopesPretty(list);
|
|
2212
|
+
} catch (error) {
|
|
2213
|
+
handleError(error, useJson);
|
|
2214
|
+
}
|
|
2215
|
+
}
|
|
2216
|
+
);
|
|
2217
|
+
scopes.command("add").description("Add a scope to a feature").requiredOption("--feature <id>", "Feature CUID").requiredOption("--version <label>", 'Short label for the increment, e.g. "V1"').requiredOption("-d, --description <text>", "What this scope delivers (markdown supported)").option("--status <status>", `Status: ${SCOPE_STATUSES.join(", ")} (default PLANNED)`).action(
|
|
2218
|
+
async (options, cmd) => {
|
|
2219
|
+
const globalOpts = cmd.optsWithGlobals();
|
|
2220
|
+
const useJson = shouldUseJson(globalOpts.json, globalOpts.pretty);
|
|
2221
|
+
try {
|
|
2222
|
+
const status = validateScopeStatus(options.status);
|
|
2223
|
+
const client = getClient();
|
|
2224
|
+
const scope = await client.scopes.create({
|
|
2225
|
+
featureId: options.feature,
|
|
2226
|
+
version: options.version,
|
|
2227
|
+
description: options.description,
|
|
2228
|
+
status
|
|
2229
|
+
});
|
|
2230
|
+
if (useJson) outputScopeJson(scope);
|
|
2231
|
+
else {
|
|
2232
|
+
console.log("\n\u2713 Scope added");
|
|
2233
|
+
outputScopePretty(scope);
|
|
2234
|
+
}
|
|
2235
|
+
} catch (error) {
|
|
2236
|
+
handleError(error, useJson);
|
|
2237
|
+
}
|
|
2238
|
+
}
|
|
2239
|
+
);
|
|
2240
|
+
scopes.command("update").description("Update a scope (setting SHIPPED stamps shippedAt and rolls the feature status up)").requiredOption("--id <id>", "Scope CUID").option("--version <label>", "New version label").option("-d, --description <text>", "New description").option("--status <status>", `Status: ${SCOPE_STATUSES.join(", ")}`).action(
|
|
2241
|
+
async (options, cmd) => {
|
|
2242
|
+
const globalOpts = cmd.optsWithGlobals();
|
|
2243
|
+
const useJson = shouldUseJson(globalOpts.json, globalOpts.pretty);
|
|
2244
|
+
try {
|
|
2245
|
+
const status = validateScopeStatus(options.status);
|
|
2246
|
+
const client = getClient();
|
|
2247
|
+
const scope = await client.scopes.update({
|
|
2248
|
+
id: options.id,
|
|
2249
|
+
version: options.version,
|
|
2250
|
+
description: options.description,
|
|
2251
|
+
status
|
|
2252
|
+
});
|
|
2253
|
+
if (useJson) outputScopeJson(scope);
|
|
2254
|
+
else {
|
|
2255
|
+
console.log("\n\u2713 Scope updated");
|
|
2256
|
+
outputScopePretty(scope);
|
|
2257
|
+
}
|
|
2258
|
+
} catch (error) {
|
|
2259
|
+
handleError(error, useJson);
|
|
2260
|
+
}
|
|
2261
|
+
}
|
|
2262
|
+
);
|
|
2263
|
+
scopes.command("rm").description("Delete a scope").requiredOption("--id <id>", "Scope CUID").action(async (options, cmd) => {
|
|
2264
|
+
const globalOpts = cmd.optsWithGlobals();
|
|
2265
|
+
const useJson = shouldUseJson(globalOpts.json, globalOpts.pretty);
|
|
2266
|
+
try {
|
|
2267
|
+
const client = getClient();
|
|
2268
|
+
const result = await client.scopes.delete(options.id);
|
|
2269
|
+
if (useJson) console.log(JSON.stringify(result, null, 2));
|
|
2270
|
+
else console.log("\n\u2713 Scope deleted");
|
|
2271
|
+
} catch (error) {
|
|
2272
|
+
handleError(error, useJson);
|
|
2273
|
+
}
|
|
2274
|
+
});
|
|
2275
|
+
return scopes;
|
|
2276
|
+
}
|
|
2277
|
+
|
|
2278
|
+
// src/commands/requirements.ts
|
|
2279
|
+
import { Command as Command8 } from "commander";
|
|
2280
|
+
var REQUIREMENT_KINDS = [
|
|
2281
|
+
"FUNCTIONAL",
|
|
2282
|
+
"NON_FUNCTIONAL",
|
|
2283
|
+
"CONSTRAINT"
|
|
2284
|
+
];
|
|
2285
|
+
function validateKind(value) {
|
|
2286
|
+
if (!value) return void 0;
|
|
2287
|
+
if (!REQUIREMENT_KINDS.includes(value)) {
|
|
2288
|
+
throw new Error(
|
|
2289
|
+
`Invalid kind "${value}". Valid: ${REQUIREMENT_KINDS.join(", ")}`
|
|
2290
|
+
);
|
|
2291
|
+
}
|
|
2292
|
+
return value;
|
|
2293
|
+
}
|
|
2294
|
+
async function readStdin2() {
|
|
2295
|
+
const chunks = [];
|
|
2296
|
+
for await (const chunk of process.stdin) {
|
|
2297
|
+
chunks.push(chunk);
|
|
2298
|
+
}
|
|
2299
|
+
return Buffer.concat(chunks).toString("utf-8");
|
|
2300
|
+
}
|
|
2301
|
+
function createRequirementsCommand() {
|
|
2302
|
+
const requirements = new Command8("requirements").description(
|
|
2303
|
+
`Manage a feature's requirements: atomic, testable EARS-style "shall" statements, checkable met/unmet.`
|
|
2304
|
+
);
|
|
2305
|
+
requirements.command("add").description(
|
|
2306
|
+
[
|
|
2307
|
+
"Add requirements to a feature.",
|
|
2308
|
+
"",
|
|
2309
|
+
"Flag mode: pass -s/--statement (plus optional --kind/--scope) to add one requirement.",
|
|
2310
|
+
"Batch mode: pipe a JSON array of { statement, kind?, scopeId? } on stdin",
|
|
2311
|
+
" to add many at once (per-item success/failure is reported)."
|
|
2312
|
+
].join("\n")
|
|
2313
|
+
).requiredOption("--feature <id>", "Feature CUID").option("-s, --statement <text>", 'One EARS-style "shall" statement').option("--kind <kind>", `Kind: ${REQUIREMENT_KINDS.join(", ")}`).option("--scope <id>", "Feature scope CUID to pin the requirement to").action(
|
|
2314
|
+
async (options, cmd) => {
|
|
2315
|
+
const globalOpts = cmd.optsWithGlobals();
|
|
2316
|
+
const useJson = shouldUseJson(globalOpts.json, globalOpts.pretty);
|
|
2317
|
+
try {
|
|
2318
|
+
const client = getClient();
|
|
2319
|
+
if (options.statement) {
|
|
2320
|
+
const requirement = await client.requirements.create({
|
|
2321
|
+
featureId: options.feature,
|
|
2322
|
+
statement: options.statement,
|
|
2323
|
+
kind: validateKind(options.kind),
|
|
2324
|
+
scopeId: options.scope
|
|
2325
|
+
});
|
|
2326
|
+
if (useJson) outputRequirementJson(requirement);
|
|
2327
|
+
else {
|
|
2328
|
+
console.log("\n\u2713 Requirement added");
|
|
2329
|
+
outputRequirementPretty(requirement);
|
|
2330
|
+
}
|
|
2331
|
+
return;
|
|
2332
|
+
}
|
|
2333
|
+
if (process.stdin.isTTY) {
|
|
2334
|
+
throw new Error(
|
|
2335
|
+
"Pass -s/--statement, or pipe a JSON array of requirements on stdin."
|
|
2336
|
+
);
|
|
2337
|
+
}
|
|
2338
|
+
const raw = await readStdin2();
|
|
2339
|
+
let batch;
|
|
2340
|
+
try {
|
|
2341
|
+
const parsed = JSON.parse(raw);
|
|
2342
|
+
if (!Array.isArray(parsed)) throw new Error("not an array");
|
|
2343
|
+
batch = parsed;
|
|
2344
|
+
} catch {
|
|
2345
|
+
throw new Error(
|
|
2346
|
+
"stdin must be a JSON array of { statement, kind?, scopeId? } objects."
|
|
2347
|
+
);
|
|
2348
|
+
}
|
|
2349
|
+
const results = [];
|
|
2350
|
+
for (const [index, item] of batch.entries()) {
|
|
2351
|
+
try {
|
|
2352
|
+
if (!item.statement || typeof item.statement !== "string") {
|
|
2353
|
+
throw new Error('missing "statement"');
|
|
2354
|
+
}
|
|
2355
|
+
const input = {
|
|
2356
|
+
featureId: options.feature,
|
|
2357
|
+
statement: item.statement,
|
|
2358
|
+
kind: validateKind(item.kind),
|
|
2359
|
+
scopeId: item.scopeId
|
|
2360
|
+
};
|
|
2361
|
+
const requirement = await client.requirements.create(input);
|
|
2362
|
+
results.push({ index, success: true, requirement });
|
|
2363
|
+
} catch (error) {
|
|
2364
|
+
results.push({
|
|
2365
|
+
index,
|
|
2366
|
+
success: false,
|
|
2367
|
+
error: error instanceof Error ? error.message : String(error)
|
|
2368
|
+
});
|
|
2369
|
+
}
|
|
2370
|
+
}
|
|
2371
|
+
if (useJson) outputRequirementBatchJson(results);
|
|
2372
|
+
else outputRequirementBatchPretty(results);
|
|
2373
|
+
if (results.some((r) => !r.success)) process.exitCode = 1;
|
|
2374
|
+
} catch (error) {
|
|
2375
|
+
handleError(error, useJson);
|
|
2376
|
+
}
|
|
2377
|
+
}
|
|
2378
|
+
);
|
|
2379
|
+
requirements.command("list").description("List a feature's requirements").requiredOption("--feature <id>", "Feature CUID").option("--scope <id>", "Only requirements pinned to this scope").action(
|
|
2380
|
+
async (options, cmd) => {
|
|
2381
|
+
const globalOpts = cmd.optsWithGlobals();
|
|
2382
|
+
const useJson = shouldUseJson(globalOpts.json, globalOpts.pretty);
|
|
2383
|
+
try {
|
|
2384
|
+
const client = getClient();
|
|
2385
|
+
const list = await client.requirements.list({
|
|
2386
|
+
featureId: options.feature,
|
|
2387
|
+
scopeId: options.scope
|
|
2388
|
+
});
|
|
2389
|
+
if (useJson) outputRequirementsJson(list);
|
|
2390
|
+
else outputRequirementsPretty(list);
|
|
2391
|
+
} catch (error) {
|
|
2392
|
+
handleError(error, useJson);
|
|
2393
|
+
}
|
|
2394
|
+
}
|
|
2395
|
+
);
|
|
2396
|
+
requirements.command("check").description("Mark a requirement met (or unmet with --unmet)").requiredOption("--id <id>", "Requirement CUID").option("--unmet", "Mark unmet instead of met").action(
|
|
2397
|
+
async (options, cmd) => {
|
|
2398
|
+
const globalOpts = cmd.optsWithGlobals();
|
|
2399
|
+
const useJson = shouldUseJson(globalOpts.json, globalOpts.pretty);
|
|
2400
|
+
try {
|
|
2401
|
+
const client = getClient();
|
|
2402
|
+
const requirement = await client.requirements.setChecked(
|
|
2403
|
+
options.id,
|
|
2404
|
+
!options.unmet
|
|
2405
|
+
);
|
|
2406
|
+
if (useJson) outputRequirementJson(requirement);
|
|
2407
|
+
else {
|
|
2408
|
+
console.log(`
|
|
2409
|
+
\u2713 Requirement marked ${options.unmet ? "unmet" : "met"}`);
|
|
2410
|
+
outputRequirementPretty(requirement);
|
|
2411
|
+
}
|
|
2412
|
+
} catch (error) {
|
|
2413
|
+
handleError(error, useJson);
|
|
2414
|
+
}
|
|
2415
|
+
}
|
|
2416
|
+
);
|
|
2417
|
+
requirements.command("rm").description("Delete a requirement").requiredOption("--id <id>", "Requirement CUID").action(async (options, cmd) => {
|
|
2418
|
+
const globalOpts = cmd.optsWithGlobals();
|
|
2419
|
+
const useJson = shouldUseJson(globalOpts.json, globalOpts.pretty);
|
|
2420
|
+
try {
|
|
2421
|
+
const client = getClient();
|
|
2422
|
+
const result = await client.requirements.delete(options.id);
|
|
2423
|
+
if (useJson) console.log(JSON.stringify(result, null, 2));
|
|
2424
|
+
else console.log("\n\u2713 Requirement deleted");
|
|
2425
|
+
} catch (error) {
|
|
2426
|
+
handleError(error, useJson);
|
|
2427
|
+
}
|
|
2428
|
+
});
|
|
2429
|
+
return requirements;
|
|
2430
|
+
}
|
|
2431
|
+
|
|
2432
|
+
// src/commands/areas.ts
|
|
2433
|
+
import { Command as Command9 } from "commander";
|
|
2434
|
+
import chalk4 from "chalk";
|
|
2435
|
+
function outputAreasPretty(areas) {
|
|
2436
|
+
if (areas.length === 0) {
|
|
2437
|
+
console.log(chalk4.gray("No areas found."));
|
|
2438
|
+
return;
|
|
2439
|
+
}
|
|
2440
|
+
console.log(chalk4.bold(`
|
|
2441
|
+
Areas (${areas.length} total)`));
|
|
2442
|
+
console.log(chalk4.gray("\u2500".repeat(50)));
|
|
2443
|
+
for (const a of areas) {
|
|
2444
|
+
const count = a._count?.features != null ? chalk4.gray(` (${a._count.features} features)`) : "";
|
|
2445
|
+
console.log(` ${chalk4.bold(a.name)}${count}`);
|
|
2446
|
+
console.log(chalk4.gray(` ID: ${a.id}`));
|
|
2447
|
+
}
|
|
2448
|
+
console.log();
|
|
2449
|
+
}
|
|
2450
|
+
function createAreasCommand() {
|
|
2451
|
+
const areas = new Command9("areas").description(
|
|
2452
|
+
"Manage a product's areas: the buckets features are filed under (a feature has exactly one or none)."
|
|
2453
|
+
);
|
|
2454
|
+
areas.command("list").description("List a product's areas").requiredOption("--product <slug|id>", "Product slug or CUID").option("--workspace <slug|id>", "Workspace (required when --product is a slug)").action(
|
|
2455
|
+
async (options, cmd) => {
|
|
2456
|
+
const globalOpts = cmd.optsWithGlobals();
|
|
2457
|
+
const useJson = shouldUseJson(globalOpts.json, globalOpts.pretty);
|
|
2458
|
+
try {
|
|
2459
|
+
const client = getClient();
|
|
2460
|
+
const workspaceId = await resolveWorkspaceId(client, options.workspace);
|
|
2461
|
+
const productId = await resolveProductId(client, workspaceId, options.product);
|
|
2462
|
+
const list = await client.areas.list({ productId });
|
|
2463
|
+
if (useJson) console.log(JSON.stringify({ areas: list, total: list.length }, null, 2));
|
|
2464
|
+
else outputAreasPretty(list);
|
|
2465
|
+
} catch (error) {
|
|
2466
|
+
handleError(error, useJson);
|
|
2467
|
+
}
|
|
2468
|
+
}
|
|
2469
|
+
);
|
|
2470
|
+
areas.command("create").description("Create an area in a product").requiredOption("--product <slug|id>", "Product slug or CUID").requiredOption("-n, --name <name>", "Area name (unique per product)").option("-d, --description <text>", "What belongs in this area").option("--workspace <slug|id>", "Workspace (required when --product is a slug)").action(
|
|
2471
|
+
async (options, cmd) => {
|
|
2472
|
+
const globalOpts = cmd.optsWithGlobals();
|
|
2473
|
+
const useJson = shouldUseJson(globalOpts.json, globalOpts.pretty);
|
|
2474
|
+
try {
|
|
2475
|
+
const client = getClient();
|
|
2476
|
+
const workspaceId = await resolveWorkspaceId(client, options.workspace);
|
|
2477
|
+
const productId = await resolveProductId(client, workspaceId, options.product);
|
|
2478
|
+
const area = await client.areas.create({
|
|
2479
|
+
productId,
|
|
2480
|
+
name: options.name,
|
|
2481
|
+
description: options.description
|
|
2482
|
+
});
|
|
2483
|
+
if (useJson) console.log(JSON.stringify(area, null, 2));
|
|
2484
|
+
else console.log(`
|
|
2485
|
+
\u2713 Area created: ${area.name} (${area.id})`);
|
|
2486
|
+
} catch (error) {
|
|
2487
|
+
handleError(error, useJson);
|
|
2488
|
+
}
|
|
2489
|
+
}
|
|
2490
|
+
);
|
|
2491
|
+
return areas;
|
|
2492
|
+
}
|
|
2493
|
+
|
|
2039
2494
|
// src/commands/features.ts
|
|
2040
2495
|
var FEATURE_STATUSES = [
|
|
2041
2496
|
"IDEA",
|
|
2042
2497
|
"DEFINED",
|
|
2043
2498
|
"IN_PROGRESS",
|
|
2044
2499
|
"SHIPPED",
|
|
2500
|
+
"DEPRECATED",
|
|
2045
2501
|
"ARCHIVED"
|
|
2046
2502
|
];
|
|
2047
2503
|
function validateFeatureStatus(value) {
|
|
@@ -2054,7 +2510,7 @@ function validateFeatureStatus(value) {
|
|
|
2054
2510
|
return value;
|
|
2055
2511
|
}
|
|
2056
2512
|
function createFeaturesCommand() {
|
|
2057
|
-
const features = new
|
|
2513
|
+
const features = new Command10("features").description(
|
|
2058
2514
|
"Manage features. A feature lives under a product and groups user stories, scopes, and tickets."
|
|
2059
2515
|
);
|
|
2060
2516
|
features.command("list").description("List features in a product").requiredOption("--product <slug|id>", "Product slug or CUID").option("--workspace <slug|id>", "Workspace (required when --product is a slug)").option("--status <status>", `Filter by status: ${FEATURE_STATUSES.join(", ")}`).action(
|
|
@@ -2090,7 +2546,7 @@ function createFeaturesCommand() {
|
|
|
2090
2546
|
handleError(error, useJson);
|
|
2091
2547
|
}
|
|
2092
2548
|
});
|
|
2093
|
-
features.command("create").description("Create a new feature in a product").requiredOption("--product <slug|id>", "Product slug or CUID").requiredOption("-n, --name <name>", "Feature name").option("-d, --description <text>", "Description (markdown supported)").option("--vision <text>", "Vision / target outcome").option("--status <status>", `Status: ${FEATURE_STATUSES.join(", ")}`).option("--priority <n>", "Priority 0-4 (lower = higher)", parseInt).option("--effort <n>", "Effort estimate", parseFloat).option("--workspace <slug|id>", "Workspace (required when --product is a slug)").action(
|
|
2549
|
+
features.command("create").description("Create a new feature in a product").requiredOption("--product <slug|id>", "Product slug or CUID").requiredOption("-n, --name <name>", "Feature name").option("-d, --description <text>", "Description (markdown supported)").option("--vision <text>", "Vision / target outcome").option("--status <status>", `Status: ${FEATURE_STATUSES.join(", ")}`).option("--priority <n>", "Priority 0-4 (lower = higher)", parseInt).option("--effort <n>", "Effort estimate", parseFloat).option("--area <id>", "Area CUID (per-product bucket) to file the feature under").option("--workspace <slug|id>", "Workspace (required when --product is a slug)").action(
|
|
2094
2550
|
async (options, cmd) => {
|
|
2095
2551
|
const globalOpts = cmd.optsWithGlobals();
|
|
2096
2552
|
const useJson = shouldUseJson(globalOpts.json, globalOpts.pretty);
|
|
@@ -2110,7 +2566,8 @@ function createFeaturesCommand() {
|
|
|
2110
2566
|
vision: options.vision,
|
|
2111
2567
|
status,
|
|
2112
2568
|
priority: options.priority,
|
|
2113
|
-
effort: options.effort
|
|
2569
|
+
effort: options.effort,
|
|
2570
|
+
areaId: options.area
|
|
2114
2571
|
});
|
|
2115
2572
|
if (useJson) outputFeatureJson(feature);
|
|
2116
2573
|
else {
|
|
@@ -2122,7 +2579,7 @@ function createFeaturesCommand() {
|
|
|
2122
2579
|
}
|
|
2123
2580
|
}
|
|
2124
2581
|
);
|
|
2125
|
-
features.command("update").description("Update an existing feature").requiredOption("--id <id>", "Feature CUID").option("-n, --name <name>", "New name").option("-d, --description <text>", "New description").option("--vision <text>", "New vision text").option("--status <status>", `Status: ${FEATURE_STATUSES.join(", ")}`).option("--priority <n>", "Priority 0-4", parseInt).option("--effort <n>", "Effort estimate", parseFloat).action(
|
|
2582
|
+
features.command("update").description("Update an existing feature").requiredOption("--id <id>", "Feature CUID").option("-n, --name <name>", "New name").option("-d, --description <text>", "New description").option("--vision <text>", "New vision text").option("--status <status>", `Status: ${FEATURE_STATUSES.join(", ")}`).option("--priority <n>", "Priority 0-4", parseInt).option("--effort <n>", "Effort estimate", parseFloat).option("--area <id>", 'Area CUID ("none" to clear)').action(
|
|
2126
2583
|
async (options, cmd) => {
|
|
2127
2584
|
const globalOpts = cmd.optsWithGlobals();
|
|
2128
2585
|
const useJson = shouldUseJson(globalOpts.json, globalOpts.pretty);
|
|
@@ -2136,7 +2593,8 @@ function createFeaturesCommand() {
|
|
|
2136
2593
|
vision: options.vision,
|
|
2137
2594
|
status,
|
|
2138
2595
|
priority: options.priority,
|
|
2139
|
-
effort: options.effort
|
|
2596
|
+
effort: options.effort,
|
|
2597
|
+
areaId: options.area === "none" ? null : options.area
|
|
2140
2598
|
});
|
|
2141
2599
|
if (useJson) outputFeatureJson(feature);
|
|
2142
2600
|
else {
|
|
@@ -2148,13 +2606,51 @@ function createFeaturesCommand() {
|
|
|
2148
2606
|
}
|
|
2149
2607
|
}
|
|
2150
2608
|
);
|
|
2609
|
+
features.command("link-page").description("Link a Knowledge page (PRD, spec, research) to a feature").requiredOption("--feature <id>", "Feature CUID").requiredOption("--page <id>", "Page CUID").option("--scope <id>", "Feature scope CUID to pin the page to").action(
|
|
2610
|
+
async (options, cmd) => {
|
|
2611
|
+
const globalOpts = cmd.optsWithGlobals();
|
|
2612
|
+
const useJson = shouldUseJson(globalOpts.json, globalOpts.pretty);
|
|
2613
|
+
try {
|
|
2614
|
+
const client = getClient();
|
|
2615
|
+
const link = await client.features.linkPage({
|
|
2616
|
+
featureId: options.feature,
|
|
2617
|
+
pageId: options.page,
|
|
2618
|
+
scopeId: options.scope
|
|
2619
|
+
});
|
|
2620
|
+
if (useJson) console.log(JSON.stringify(link, null, 2));
|
|
2621
|
+
else console.log("\n\u2713 Page linked");
|
|
2622
|
+
} catch (error) {
|
|
2623
|
+
handleError(error, useJson);
|
|
2624
|
+
}
|
|
2625
|
+
}
|
|
2626
|
+
);
|
|
2627
|
+
features.command("unlink-page").description("Unlink a Knowledge page from a feature").requiredOption("--feature <id>", "Feature CUID").requiredOption("--page <id>", "Page CUID").action(
|
|
2628
|
+
async (options, cmd) => {
|
|
2629
|
+
const globalOpts = cmd.optsWithGlobals();
|
|
2630
|
+
const useJson = shouldUseJson(globalOpts.json, globalOpts.pretty);
|
|
2631
|
+
try {
|
|
2632
|
+
const client = getClient();
|
|
2633
|
+
const result = await client.features.unlinkPage(
|
|
2634
|
+
options.feature,
|
|
2635
|
+
options.page
|
|
2636
|
+
);
|
|
2637
|
+
if (useJson) console.log(JSON.stringify(result, null, 2));
|
|
2638
|
+
else console.log("\n\u2713 Page unlinked");
|
|
2639
|
+
} catch (error) {
|
|
2640
|
+
handleError(error, useJson);
|
|
2641
|
+
}
|
|
2642
|
+
}
|
|
2643
|
+
);
|
|
2151
2644
|
features.addCommand(createStoriesCommand());
|
|
2645
|
+
features.addCommand(createScopesCommand());
|
|
2646
|
+
features.addCommand(createRequirementsCommand());
|
|
2647
|
+
features.addCommand(createAreasCommand());
|
|
2152
2648
|
return features;
|
|
2153
2649
|
}
|
|
2154
2650
|
|
|
2155
2651
|
// src/commands/labels.ts
|
|
2156
|
-
import { Command as
|
|
2157
|
-
import
|
|
2652
|
+
import { Command as Command11 } from "commander";
|
|
2653
|
+
import chalk5 from "chalk";
|
|
2158
2654
|
var TRIAGE_LABELS = [
|
|
2159
2655
|
{ slug: "needs-triage", name: "needs-triage", color: "avatar-orange" },
|
|
2160
2656
|
{ slug: "needs-info", name: "needs-info", color: "avatar-yellow" },
|
|
@@ -2168,22 +2664,22 @@ function looksLikeCuid(value) {
|
|
|
2168
2664
|
}
|
|
2169
2665
|
function outputTagsPretty(tags) {
|
|
2170
2666
|
if (tags.length === 0) {
|
|
2171
|
-
console.log(
|
|
2667
|
+
console.log(chalk5.dim("No labels found."));
|
|
2172
2668
|
return;
|
|
2173
2669
|
}
|
|
2174
2670
|
console.log();
|
|
2175
2671
|
for (const tag of tags) {
|
|
2176
|
-
const scope = tag.workspaceId === null ?
|
|
2177
|
-
const category = tag.category === null ?
|
|
2672
|
+
const scope = tag.workspaceId === null ? chalk5.cyan("global") : chalk5.magenta("workspace");
|
|
2673
|
+
const category = tag.category === null ? chalk5.dim("(label)") : chalk5.dim(`(${tag.category})`);
|
|
2178
2674
|
console.log(
|
|
2179
|
-
` ${
|
|
2675
|
+
` ${chalk5.bold(tag.slug)} ${tag.name} ${chalk5.gray(tag.color)} ${scope} ${category}`
|
|
2180
2676
|
);
|
|
2181
2677
|
}
|
|
2182
2678
|
console.log();
|
|
2183
2679
|
}
|
|
2184
2680
|
function outputTagPretty(tag, prefix) {
|
|
2185
2681
|
if (prefix) console.log(prefix);
|
|
2186
|
-
console.log(` ${
|
|
2682
|
+
console.log(` ${chalk5.bold(tag.slug)} ${tag.name} ${chalk5.gray(tag.color)}`);
|
|
2187
2683
|
}
|
|
2188
2684
|
async function resolveLabelIds(client, workspaceId, values) {
|
|
2189
2685
|
if (values.length === 0) return [];
|
|
@@ -2207,7 +2703,7 @@ async function resolveLabelIds(client, workspaceId, values) {
|
|
|
2207
2703
|
return [...byId, ...resolvedFromSlugs];
|
|
2208
2704
|
}
|
|
2209
2705
|
function createLabelsCommand() {
|
|
2210
|
-
const labels = new
|
|
2706
|
+
const labels = new Command11("labels").description(
|
|
2211
2707
|
"Manage labels (orthogonal tags applied to actions, tickets, features, and epics)."
|
|
2212
2708
|
);
|
|
2213
2709
|
labels.command("list").description("List labels visible in a workspace").option("--workspace <slug|id>", "Workspace slug or CUID").option(
|
|
@@ -2255,7 +2751,7 @@ function createLabelsCommand() {
|
|
|
2255
2751
|
if (useJson) {
|
|
2256
2752
|
console.log(JSON.stringify(tag, null, 2));
|
|
2257
2753
|
} else {
|
|
2258
|
-
outputTagPretty(tag,
|
|
2754
|
+
outputTagPretty(tag, chalk5.green("\n\u2713 Label created"));
|
|
2259
2755
|
}
|
|
2260
2756
|
} catch (error) {
|
|
2261
2757
|
handleError(error, useJson);
|
|
@@ -2293,10 +2789,10 @@ function createLabelsCommand() {
|
|
|
2293
2789
|
console.log(JSON.stringify(results, null, 2));
|
|
2294
2790
|
} else {
|
|
2295
2791
|
console.log();
|
|
2296
|
-
console.log(
|
|
2792
|
+
console.log(chalk5.bold("Triage labels:"));
|
|
2297
2793
|
for (const r of results) {
|
|
2298
|
-
const tag =
|
|
2299
|
-
const status = r.status === "created" ?
|
|
2794
|
+
const tag = chalk5.bold(r.slug);
|
|
2795
|
+
const status = r.status === "created" ? chalk5.green("created") : chalk5.dim("existed");
|
|
2300
2796
|
console.log(` ${tag} ${status}`);
|
|
2301
2797
|
}
|
|
2302
2798
|
console.log();
|
|
@@ -2310,7 +2806,7 @@ function createLabelsCommand() {
|
|
|
2310
2806
|
}
|
|
2311
2807
|
|
|
2312
2808
|
// src/commands/organizations.ts
|
|
2313
|
-
import { Command as
|
|
2809
|
+
import { Command as Command12 } from "commander";
|
|
2314
2810
|
var VALID_SIZES = [
|
|
2315
2811
|
"1-10",
|
|
2316
2812
|
"11-50",
|
|
@@ -2320,7 +2816,7 @@ var VALID_SIZES = [
|
|
|
2320
2816
|
"1000+"
|
|
2321
2817
|
];
|
|
2322
2818
|
function createOrganizationsCommand() {
|
|
2323
|
-
const organizations = new
|
|
2819
|
+
const organizations = new Command12("organizations").alias("orgs").description("Manage CRM organizations");
|
|
2324
2820
|
organizations.command("list").description("List organizations").requiredOption("--workspace <id>", "Workspace ID").option("--search <query>", "Search by name or description").option("--industry <industry>", "Filter by industry").option("--limit <n>", "Max results to return", parseInt).option("--cursor <cursor>", "Pagination cursor").action(async (options, cmd) => {
|
|
2325
2821
|
const globalOpts = cmd.optsWithGlobals();
|
|
2326
2822
|
const useJson = shouldUseJson(globalOpts.json, globalOpts.pretty);
|
|
@@ -2387,10 +2883,101 @@ function createOrganizationsCommand() {
|
|
|
2387
2883
|
return organizations;
|
|
2388
2884
|
}
|
|
2389
2885
|
|
|
2886
|
+
// src/commands/pages.ts
|
|
2887
|
+
import { readFileSync } from "fs";
|
|
2888
|
+
import { Command as Command13 } from "commander";
|
|
2889
|
+
function readBody(body, bodyFile) {
|
|
2890
|
+
if (body !== void 0) return body;
|
|
2891
|
+
if (bodyFile === void 0) return void 0;
|
|
2892
|
+
if (bodyFile === "-") return readFileSync(0, "utf-8");
|
|
2893
|
+
return readFileSync(bodyFile, "utf-8");
|
|
2894
|
+
}
|
|
2895
|
+
function createPagesCommand() {
|
|
2896
|
+
const pages = new Command13("pages").description(
|
|
2897
|
+
'Manage Knowledge pages (PRDs, research, technical specs). Link one to a feature with "features link-page".'
|
|
2898
|
+
);
|
|
2899
|
+
pages.command("list").description("List pages in a workspace").option("--workspace <slug|id>", "Workspace (defaults to the configured default)").option("--search <text>", "Filter by title/content").action(
|
|
2900
|
+
async (options, cmd) => {
|
|
2901
|
+
const globalOpts = cmd.optsWithGlobals();
|
|
2902
|
+
const useJson = shouldUseJson(globalOpts.json, globalOpts.pretty);
|
|
2903
|
+
try {
|
|
2904
|
+
const client = getClient();
|
|
2905
|
+
const workspaceId = await resolveWorkspaceId(client, options.workspace);
|
|
2906
|
+
const list = await client.pages.list({
|
|
2907
|
+
workspaceId,
|
|
2908
|
+
search: options.search
|
|
2909
|
+
});
|
|
2910
|
+
if (useJson) outputPagesJson(list);
|
|
2911
|
+
else outputPagesPretty(list);
|
|
2912
|
+
} catch (error) {
|
|
2913
|
+
handleError(error, useJson);
|
|
2914
|
+
}
|
|
2915
|
+
}
|
|
2916
|
+
);
|
|
2917
|
+
pages.command("get <id>").description("Get a page by CUID (body is Markdown)").action(async (id, _options, cmd) => {
|
|
2918
|
+
const globalOpts = cmd.optsWithGlobals();
|
|
2919
|
+
const useJson = shouldUseJson(globalOpts.json, globalOpts.pretty);
|
|
2920
|
+
try {
|
|
2921
|
+
const client = getClient();
|
|
2922
|
+
const page = await client.pages.get(id);
|
|
2923
|
+
if (useJson) outputPageJson(page);
|
|
2924
|
+
else outputPagePretty(page);
|
|
2925
|
+
} catch (error) {
|
|
2926
|
+
handleError(error, useJson);
|
|
2927
|
+
}
|
|
2928
|
+
});
|
|
2929
|
+
pages.command("create").description("Create a page. Body: -b text, --body-file <path>, or --body-file - for stdin.").option("--workspace <slug|id>", "Workspace (defaults to the configured default)").requiredOption("-t, --title <title>", "Page title").option("-b, --body <markdown>", "Markdown body").option("--body-file <path>", 'Read the Markdown body from a file ("-" = stdin)').action(
|
|
2930
|
+
async (options, cmd) => {
|
|
2931
|
+
const globalOpts = cmd.optsWithGlobals();
|
|
2932
|
+
const useJson = shouldUseJson(globalOpts.json, globalOpts.pretty);
|
|
2933
|
+
try {
|
|
2934
|
+
const client = getClient();
|
|
2935
|
+
const workspaceId = await resolveWorkspaceId(client, options.workspace);
|
|
2936
|
+
const page = await client.pages.create({
|
|
2937
|
+
workspaceId,
|
|
2938
|
+
title: options.title,
|
|
2939
|
+
body: readBody(options.body, options.bodyFile)
|
|
2940
|
+
});
|
|
2941
|
+
if (useJson) outputPageJson(page);
|
|
2942
|
+
else {
|
|
2943
|
+
console.log("\n\u2713 Page created");
|
|
2944
|
+
outputPagePretty(page);
|
|
2945
|
+
}
|
|
2946
|
+
} catch (error) {
|
|
2947
|
+
handleError(error, useJson);
|
|
2948
|
+
}
|
|
2949
|
+
}
|
|
2950
|
+
);
|
|
2951
|
+
pages.command("update").description(
|
|
2952
|
+
"Update a page. Sending a body is a Markdown-source write: the Markdown becomes canonical."
|
|
2953
|
+
).requiredOption("--id <id>", "Page CUID").option("-t, --title <title>", "New title").option("-b, --body <markdown>", "New Markdown body (replaces the whole body)").option("--body-file <path>", 'Read the new Markdown body from a file ("-" = stdin)').action(
|
|
2954
|
+
async (options, cmd) => {
|
|
2955
|
+
const globalOpts = cmd.optsWithGlobals();
|
|
2956
|
+
const useJson = shouldUseJson(globalOpts.json, globalOpts.pretty);
|
|
2957
|
+
try {
|
|
2958
|
+
const client = getClient();
|
|
2959
|
+
const page = await client.pages.update({
|
|
2960
|
+
id: options.id,
|
|
2961
|
+
title: options.title,
|
|
2962
|
+
body: readBody(options.body, options.bodyFile)
|
|
2963
|
+
});
|
|
2964
|
+
if (useJson) outputPageJson(page);
|
|
2965
|
+
else {
|
|
2966
|
+
console.log("\n\u2713 Page updated");
|
|
2967
|
+
outputPagePretty(page);
|
|
2968
|
+
}
|
|
2969
|
+
} catch (error) {
|
|
2970
|
+
handleError(error, useJson);
|
|
2971
|
+
}
|
|
2972
|
+
}
|
|
2973
|
+
);
|
|
2974
|
+
return pages;
|
|
2975
|
+
}
|
|
2976
|
+
|
|
2390
2977
|
// src/commands/products.ts
|
|
2391
|
-
import { Command as
|
|
2978
|
+
import { Command as Command14 } from "commander";
|
|
2392
2979
|
function createProductsCommand() {
|
|
2393
|
-
const products = new
|
|
2980
|
+
const products = new Command14("products").description(
|
|
2394
2981
|
"Manage products. A workspace contains products; a product holds features and tickets."
|
|
2395
2982
|
);
|
|
2396
2983
|
products.command("list").description("List products in a workspace").option("--workspace <slug|id>", "Workspace slug or CUID (defaults to configured workspace)").action(async (options, cmd) => {
|
|
@@ -2453,9 +3040,9 @@ function createProductsCommand() {
|
|
|
2453
3040
|
}
|
|
2454
3041
|
|
|
2455
3042
|
// src/commands/projects.ts
|
|
2456
|
-
import { Command as
|
|
3043
|
+
import { Command as Command15 } from "commander";
|
|
2457
3044
|
function createProjectsCommand() {
|
|
2458
|
-
const projects = new
|
|
3045
|
+
const projects = new Command15("projects").description("Manage projects");
|
|
2459
3046
|
projects.command("list").description("List all projects").option("--workspace <id>", "Filter by workspace ID").option("--include-actions", "Include actions in output").action(async (options, cmd) => {
|
|
2460
3047
|
const globalOpts = cmd.optsWithGlobals();
|
|
2461
3048
|
const useJson = shouldUseJson(globalOpts.json, globalOpts.pretty);
|
|
@@ -2478,7 +3065,7 @@ function createProjectsCommand() {
|
|
|
2478
3065
|
}
|
|
2479
3066
|
|
|
2480
3067
|
// src/commands/tickets.ts
|
|
2481
|
-
import { Command as
|
|
3068
|
+
import { Command as Command16 } from "commander";
|
|
2482
3069
|
function collectRepeatable(value, previous) {
|
|
2483
3070
|
return [...previous, value];
|
|
2484
3071
|
}
|
|
@@ -2521,7 +3108,7 @@ function validateTicketType(value) {
|
|
|
2521
3108
|
return value;
|
|
2522
3109
|
}
|
|
2523
3110
|
function createTicketsCommand() {
|
|
2524
|
-
const tickets = new
|
|
3111
|
+
const tickets = new Command16("tickets").description(
|
|
2525
3112
|
"Manage product backlog tickets. Tickets live under a product, link to features/epics, depend on each other, and can be implemented via actions."
|
|
2526
3113
|
);
|
|
2527
3114
|
tickets.command("list").description(
|
|
@@ -2624,7 +3211,7 @@ function createTicketsCommand() {
|
|
|
2624
3211
|
handleError(error, useJson);
|
|
2625
3212
|
}
|
|
2626
3213
|
});
|
|
2627
|
-
tickets.command("create").description("Create a new ticket in a product").requiredOption("--product <slug|id>", "Product slug or CUID").requiredOption("-t, --title <title>", "Ticket title").option("-b, --body <text>", "Body (markdown supported)").option("--type <type>", `Ticket type: ${TICKET_TYPES.join(", ")}`).option("--status <status>", `Initial status: ${TICKET_STATUSES.join(", ")}`).option("--priority <n>", "Priority 0-4 (lower = higher)", parseInt).option("--points <n>", "Story points", parseFloat).option("--feature <id>", "Feature CUID to attach to").option("--epic <id>", "Epic CUID to attach to").option("--assignee <id>", "Assignee user ID").option("--branch <name>", "Branch name").option("--pr <url>", "Pull request URL").option("--workspace <slug|id>", "Workspace (required when --product is a slug)").option(
|
|
3214
|
+
tickets.command("create").description("Create a new ticket in a product").requiredOption("--product <slug|id>", "Product slug or CUID").requiredOption("-t, --title <title>", "Ticket title").option("-b, --body <text>", "Body (markdown supported)").option("--type <type>", `Ticket type: ${TICKET_TYPES.join(", ")}`).option("--status <status>", `Initial status: ${TICKET_STATUSES.join(", ")}`).option("--priority <n>", "Priority 0-4 (lower = higher)", parseInt).option("--points <n>", "Story points", parseFloat).option("--feature <id>", "Feature CUID to attach to").option("--scope <id>", "Feature scope CUID to attach to (requires --feature)").option("--epic <id>", "Epic CUID to attach to").option("--assignee <id>", "Assignee user ID").option("--branch <name>", "Branch name").option("--pr <url>", "Pull request URL").option("--workspace <slug|id>", "Workspace (required when --product is a slug)").option(
|
|
2628
3215
|
"--label <slug-or-id>",
|
|
2629
3216
|
"Label to attach (repeatable)",
|
|
2630
3217
|
collectRepeatable,
|
|
@@ -2652,6 +3239,7 @@ function createTicketsCommand() {
|
|
|
2652
3239
|
priority: options.priority,
|
|
2653
3240
|
points: options.points,
|
|
2654
3241
|
featureId: options.feature,
|
|
3242
|
+
scopeId: options.scope,
|
|
2655
3243
|
epicId: options.epic,
|
|
2656
3244
|
assigneeId: options.assignee,
|
|
2657
3245
|
branchName: options.branch,
|
|
@@ -2834,7 +3422,7 @@ function createTicketsCommand() {
|
|
|
2834
3422
|
handleError(error, useJson);
|
|
2835
3423
|
}
|
|
2836
3424
|
});
|
|
2837
|
-
const comment = new
|
|
3425
|
+
const comment = new Command16("comment").description("Manage comments on a ticket");
|
|
2838
3426
|
comment.command("add").description("Add a comment to a ticket").requiredOption("--id <id>", "Ticket CUID").requiredOption("-m, --message <text>", "Comment content (markdown supported)").action(async (options, cmd) => {
|
|
2839
3427
|
const globalOpts = cmd.optsWithGlobals();
|
|
2840
3428
|
const useJson = shouldUseJson(globalOpts.json, globalOpts.pretty);
|
|
@@ -2873,10 +3461,10 @@ function createTicketsCommand() {
|
|
|
2873
3461
|
}
|
|
2874
3462
|
|
|
2875
3463
|
// src/commands/workspaces.ts
|
|
2876
|
-
import { Command as
|
|
2877
|
-
import
|
|
3464
|
+
import { Command as Command17 } from "commander";
|
|
3465
|
+
import chalk6 from "chalk";
|
|
2878
3466
|
function createWorkspacesCommand() {
|
|
2879
|
-
const workspaces = new
|
|
3467
|
+
const workspaces = new Command17("workspaces").description("Manage workspaces");
|
|
2880
3468
|
workspaces.command("list").description("List all workspaces").action(async (_options, cmd) => {
|
|
2881
3469
|
const globalOpts = cmd.optsWithGlobals();
|
|
2882
3470
|
const useJson = shouldUseJson(globalOpts.json, globalOpts.pretty);
|
|
@@ -2889,7 +3477,7 @@ function createWorkspacesCommand() {
|
|
|
2889
3477
|
const config = getConfig();
|
|
2890
3478
|
outputWorkspacesPretty(workspaces2);
|
|
2891
3479
|
if (config.defaultWorkspaceSlug) {
|
|
2892
|
-
console.log(
|
|
3480
|
+
console.log(chalk6.gray(`Default workspace: ${config.defaultWorkspaceSlug}`));
|
|
2893
3481
|
}
|
|
2894
3482
|
}
|
|
2895
3483
|
} catch (error) {
|
|
@@ -2902,10 +3490,10 @@ function createWorkspacesCommand() {
|
|
|
2902
3490
|
const workspaces2 = await client.workspaces.list();
|
|
2903
3491
|
const workspace = workspaces2.find((w) => w.slug === slug);
|
|
2904
3492
|
if (!workspace) {
|
|
2905
|
-
console.error(
|
|
2906
|
-
console.log(
|
|
3493
|
+
console.error(chalk6.red(`Workspace with slug "${slug}" not found.`));
|
|
3494
|
+
console.log(chalk6.gray("Available workspaces:"));
|
|
2907
3495
|
workspaces2.forEach((w) => {
|
|
2908
|
-
console.log(
|
|
3496
|
+
console.log(chalk6.gray(` - ${w.slug} (${w.name})`));
|
|
2909
3497
|
});
|
|
2910
3498
|
process.exit(1);
|
|
2911
3499
|
}
|
|
@@ -2913,7 +3501,7 @@ function createWorkspacesCommand() {
|
|
|
2913
3501
|
defaultWorkspaceId: workspace.id,
|
|
2914
3502
|
defaultWorkspaceSlug: workspace.slug
|
|
2915
3503
|
});
|
|
2916
|
-
console.log(
|
|
3504
|
+
console.log(chalk6.green(`Default workspace set to: ${workspace.name} (${workspace.slug})`));
|
|
2917
3505
|
} catch (error) {
|
|
2918
3506
|
handleError(error);
|
|
2919
3507
|
}
|
|
@@ -2922,7 +3510,7 @@ function createWorkspacesCommand() {
|
|
|
2922
3510
|
}
|
|
2923
3511
|
|
|
2924
3512
|
// src/index.ts
|
|
2925
|
-
var program = new
|
|
3513
|
+
var program = new Command18();
|
|
2926
3514
|
program.name("exponential").description(
|
|
2927
3515
|
[
|
|
2928
3516
|
"CLI to interact with Exponential productivity app.",
|
|
@@ -2940,6 +3528,7 @@ program.addCommand(createEpicsCommand());
|
|
|
2940
3528
|
program.addCommand(createFeaturesCommand());
|
|
2941
3529
|
program.addCommand(createLabelsCommand());
|
|
2942
3530
|
program.addCommand(createOrganizationsCommand());
|
|
3531
|
+
program.addCommand(createPagesCommand());
|
|
2943
3532
|
program.addCommand(createProductsCommand());
|
|
2944
3533
|
program.addCommand(createProjectsCommand());
|
|
2945
3534
|
program.addCommand(createTicketsCommand());
|