@trycadence/cli 0.1.6 → 0.1.8

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.
Files changed (2) hide show
  1. package/dist/cadence +243 -31
  2. package/package.json +1 -1
package/dist/cadence CHANGED
@@ -1466,6 +1466,9 @@ function createCadenceClient(options = {}) {
1466
1466
  },
1467
1467
  tickets: {
1468
1468
  get: (input) => rpc.tickets.get.query(input),
1469
+ detail: (input) => rpc.tickets.detail.query(input),
1470
+ activity: (input) => rpc.tickets.activity.query(input),
1471
+ workLog: (input) => rpc.tickets.workLog.query(input),
1469
1472
  list: (input) => rpc.tickets.list.query(input),
1470
1473
  create: (input) => rpc.tickets.create.mutate(input),
1471
1474
  attach: (input) => rpc.tickets.attach.mutate(input),
@@ -1575,6 +1578,7 @@ var knownCommandPaths = [
1575
1578
  ["events", "list"],
1576
1579
  ["work", "overview"],
1577
1580
  ["projects", "list"],
1581
+ ["projects", "use"],
1578
1582
  ["init"],
1579
1583
  ["status"],
1580
1584
  ["help"]
@@ -1823,6 +1827,39 @@ async function findRepoCadenceDirectory(cwd) {
1823
1827
  current = parent;
1824
1828
  }
1825
1829
  }
1830
+ async function findRepoConfigDirectory(cwd) {
1831
+ let current = cwd;
1832
+ while (true) {
1833
+ const repoConfig = join(current, ".cadence", "config.json");
1834
+ if (await Bun.file(repoConfig).exists()) {
1835
+ return join(current, ".cadence");
1836
+ }
1837
+ const parent = dirname(current);
1838
+ if (parent === current) {
1839
+ return null;
1840
+ }
1841
+ current = parent;
1842
+ }
1843
+ }
1844
+ function resolveGitRootFromCommand(cwd) {
1845
+ const result = spawnSync("git", ["rev-parse", "--show-toplevel"], {
1846
+ cwd,
1847
+ encoding: "utf8"
1848
+ });
1849
+ if (result.status !== 0) {
1850
+ return null;
1851
+ }
1852
+ return result.stdout.trim() || null;
1853
+ }
1854
+ async function repoConfigPathForWrite(options) {
1855
+ const cwd = options.cwd ?? process.cwd();
1856
+ const existingConfigDirectory = await findRepoConfigDirectory(cwd);
1857
+ if (existingConfigDirectory) {
1858
+ return join(existingConfigDirectory, "config.json");
1859
+ }
1860
+ const gitRoot = options.resolveGitRoot ? await options.resolveGitRoot() : resolveGitRootFromCommand(cwd);
1861
+ return join(gitRoot ?? cwd, ".cadence", "config.json");
1862
+ }
1826
1863
  function getConfigHome(env) {
1827
1864
  return env.CADENCE_CONFIG_HOME ?? (env.HOME ? join(env.HOME, ".config", "cadence") : join(parse(process.cwd()).root, ".config", "cadence"));
1828
1865
  }
@@ -1840,16 +1877,13 @@ async function resolveCliConfig(flags, options = {}) {
1840
1877
  repoConfigPromise,
1841
1878
  localConfigPromise
1842
1879
  ]);
1843
- const profiles = {
1844
- ...globalConfig.profiles ?? {},
1845
- ...repoConfig.profiles ?? {},
1846
- ...localConfig.profiles ?? {}
1847
- };
1848
1880
  const envProfile = env.CADENCE_PROFILE;
1849
1881
  const profile = envProfile ?? localConfig.profile ?? repoConfig.profile ?? globalConfig.profile ?? null;
1850
- const profileConfig = profile ? profiles[profile] : undefined;
1851
- const server = flags.server ?? env.CADENCE_SERVER ?? localConfig.server ?? profileConfig?.server ?? repoConfig.server ?? globalConfig.server ?? defaultCliApiBaseUrl;
1852
- const webBaseUrl = env.CADENCE_WEB_BASE_URL ?? localConfig.webBaseUrl ?? profileConfig?.webBaseUrl ?? repoConfig.webBaseUrl ?? globalConfig.webBaseUrl ?? deriveWebBaseUrl(server);
1882
+ const globalProfileConfig = profile ? globalConfig.profiles?.[profile] : undefined;
1883
+ const repoProfileConfig = profile ? repoConfig.profiles?.[profile] : undefined;
1884
+ const localProfileConfig = profile ? localConfig.profiles?.[profile] : undefined;
1885
+ const server = flags.server ?? env.CADENCE_SERVER ?? localConfig.server ?? localProfileConfig?.server ?? repoConfig.server ?? repoProfileConfig?.server ?? globalProfileConfig?.server ?? globalConfig.server ?? defaultCliApiBaseUrl;
1886
+ const webBaseUrl = env.CADENCE_WEB_BASE_URL ?? localConfig.webBaseUrl ?? localProfileConfig?.webBaseUrl ?? repoConfig.webBaseUrl ?? repoProfileConfig?.webBaseUrl ?? globalProfileConfig?.webBaseUrl ?? globalConfig.webBaseUrl ?? deriveWebBaseUrl(server);
1853
1887
  const projectId = flags.project ?? localConfig.projectId ?? repoConfig.projectId ?? globalConfig.projectId ?? null;
1854
1888
  return {
1855
1889
  server,
@@ -2117,6 +2151,118 @@ function formatJson(envelope) {
2117
2151
  return `${JSON.stringify(envelope, null, 2)}
2118
2152
  `;
2119
2153
  }
2154
+ function isRecord(value) {
2155
+ return Boolean(value && typeof value === "object" && !Array.isArray(value));
2156
+ }
2157
+ function stringField(value, field) {
2158
+ if (!isRecord(value)) {
2159
+ return null;
2160
+ }
2161
+ const fieldValue = value[field];
2162
+ return typeof fieldValue === "string" ? fieldValue : null;
2163
+ }
2164
+ function countLabel(value, singular, plural = `${singular}s`) {
2165
+ const count = Array.isArray(value) ? value.length : 0;
2166
+ return `${count} ${count === 1 ? singular : plural}`;
2167
+ }
2168
+ function humanCommandOutput(commandName, data) {
2169
+ switch (commandName) {
2170
+ case "auth.status":
2171
+ return [
2172
+ `Server: ${stringField(data, "server") ?? "unknown"}`,
2173
+ `Project: ${stringField(data, "projectId") ?? "not configured"}`,
2174
+ `Credential: ${isRecord(data) && data.credentialConfigured ? "configured" : "not configured"}`
2175
+ ].join(`
2176
+ `) + `
2177
+ `;
2178
+ case "auth.logout":
2179
+ return `Cadence CLI credential removed.
2180
+ `;
2181
+ case "events.list":
2182
+ return `Found ${countLabel(data, "event")}.
2183
+ `;
2184
+ case "work.overview":
2185
+ return `Loaded work overview.
2186
+ `;
2187
+ case "tickets.get":
2188
+ return `Loaded ticket ${stringField(data, "id") ?? ""}`.trimEnd() + `.
2189
+ `;
2190
+ case "tickets.list":
2191
+ return `Found ${countLabel(data, "ticket")}.
2192
+ `;
2193
+ case "sessions.current":
2194
+ return `Loaded current session context.
2195
+ `;
2196
+ case "changesets.get":
2197
+ case "changesets.context":
2198
+ case "changesets.current":
2199
+ return `Loaded ChangeSet context.
2200
+ `;
2201
+ case "changesets.list":
2202
+ return `Found ${countLabel(data, "ChangeSet")}.
2203
+ `;
2204
+ case "changesets.notes.get":
2205
+ return `Loaded ChangeSet PR notes.
2206
+ `;
2207
+ case "projects.list":
2208
+ return `Found ${countLabel(data, "project")}.
2209
+ `;
2210
+ case "actors.ensure-workspace-agent": {
2211
+ const displayName = stringField(data, "displayName");
2212
+ const actorId = stringField(data, "actorId");
2213
+ return `Workspace agent ready${displayName ? `: ${displayName}` : ""}${actorId ? ` (${actorId})` : ""}.
2214
+ `;
2215
+ }
2216
+ case "intake":
2217
+ return `Created intake ${stringField(data, "id") ?? ""}`.trimEnd() + `.
2218
+ `;
2219
+ case "intake.dismiss":
2220
+ return `Dismissed intake.
2221
+ `;
2222
+ case "tickets.attach":
2223
+ return `Attached intake to ticket.
2224
+ `;
2225
+ case "tickets.create":
2226
+ return `Created ticket ${stringField(data, "id") ?? ""}`.trimEnd() + `.
2227
+ `;
2228
+ case "tickets.update":
2229
+ return `Updated ticket ${stringField(data, "id") ?? ""}`.trimEnd() + `.
2230
+ `;
2231
+ case "tickets.claim":
2232
+ return `Claimed ticket lease ${stringField(data, "id") ?? ""}`.trimEnd() + `.
2233
+ `;
2234
+ case "tickets.release":
2235
+ return `Released ticket lease ${stringField(data, "id") ?? ""}`.trimEnd() + `.
2236
+ `;
2237
+ case "tickets.log":
2238
+ return `Added ticket work-log entry.
2239
+ `;
2240
+ case "tickets.complete":
2241
+ return `Completed ticket ${stringField(data, "id") ?? ""}`.trimEnd() + `.
2242
+ `;
2243
+ case "sessions.start":
2244
+ return `Started session ${stringField(data, "id") ?? ""}`.trimEnd() + `.
2245
+ `;
2246
+ case "sessions.end":
2247
+ return `Ended session ${stringField(data, "id") ?? ""}`.trimEnd() + `.
2248
+ `;
2249
+ case "sessions.files":
2250
+ return `Recorded session files.
2251
+ `;
2252
+ case "changesets.create":
2253
+ return `Created ChangeSet ${stringField(data, "id") ?? ""}`.trimEnd() + `.
2254
+ `;
2255
+ case "changesets.notes.put":
2256
+ return `Saved ChangeSet PR notes.
2257
+ `;
2258
+ case "changesets.notes.apply":
2259
+ return `Marked ChangeSet PR notes applied.
2260
+ `;
2261
+ default:
2262
+ return `Done.
2263
+ `;
2264
+ }
2265
+ }
2120
2266
  function helpText() {
2121
2267
  return [
2122
2268
  "Cadence CLI",
@@ -2129,6 +2275,7 @@ function helpText() {
2129
2275
  " cadence actors ensure-workspace-agent --agent-kind <kind> [--workspace-name <name>] [--workspace-ref <ref>] [--display-name <name>] [--project <project-id>] [--json]",
2130
2276
  " cadence status [--project <project-id>] [--json]",
2131
2277
  " cadence projects list [--json]",
2278
+ " cadence projects use <project-id|org/project> [--server <url>] [--json]",
2132
2279
  " cadence work overview [--project <project-id>] [--json]",
2133
2280
  " cadence tickets get <ticket-id> [--project <project-id>] [--json]",
2134
2281
  " cadence tickets list [--project <project-id>] [--status <status>] [--json]",
@@ -2156,7 +2303,7 @@ function helpText() {
2156
2303
  " cadence events list [--project <project-id>] [--ticket <ticket-id>] [--changeset <changeset-id>] [--session <session-id>] [--json]",
2157
2304
  "",
2158
2305
  "Global flags:",
2159
- " --project <id> Cadence project ID or org/project slug",
2306
+ " --project <id|org/project> Cadence project ID or org/project slug",
2160
2307
  " --server <url> Cadence API server override",
2161
2308
  " --json Print stable JSON envelope",
2162
2309
  "",
@@ -2442,8 +2589,21 @@ async function runStatus(parsed, options) {
2442
2589
  exitCode: 0
2443
2590
  };
2444
2591
  }
2592
+ const projectLabel = config.projectId ?? "not configured";
2593
+ const profileLabel = config.profile ?? "default";
2594
+ const credentialLabel = credential ? "configured" : "not configured";
2445
2595
  return {
2446
- stdout: `Cadence API ${health.ok ? "ok" : "unavailable"} at ${config.server}
2596
+ stdout: [
2597
+ `Cadence API: ${health.ok ? "ok" : "unavailable"} at ${config.server}`,
2598
+ `Cadence web: ${webBaseUrl}`,
2599
+ `Profile: ${profileLabel}`,
2600
+ `Project: ${projectLabel}`,
2601
+ `Credential: ${credentialLabel}`,
2602
+ `Repo config: ${config.repoConfigPath ?? "not found"}`,
2603
+ `Local config: ${config.localConfigPath ?? "not found"}`,
2604
+ `Global config: ${config.globalConfigPath}`
2605
+ ].join(`
2606
+ `) + `
2447
2607
  `,
2448
2608
  stderr: "",
2449
2609
  exitCode: 0
@@ -2451,7 +2611,7 @@ async function runStatus(parsed, options) {
2451
2611
  }
2452
2612
  async function runInit(parsed, options) {
2453
2613
  const cwd = options.cwd ?? process.cwd();
2454
- const repoConfigPath = join(cwd, ".cadence", "config.json");
2614
+ const repoConfigPath = await repoConfigPathForWrite(options);
2455
2615
  const config = await resolveCliConfig(parsed.flags, {
2456
2616
  ...options,
2457
2617
  cwd
@@ -2509,6 +2669,15 @@ async function resolveProjectReference(projectReference, client) {
2509
2669
  projectSlug
2510
2670
  });
2511
2671
  }
2672
+ async function resolveRequiredProject(config, client) {
2673
+ return await resolveProjectReference(requireProjectId(config), client);
2674
+ }
2675
+ function configWithProject(config, project) {
2676
+ return {
2677
+ ...config,
2678
+ projectId: project.id
2679
+ };
2680
+ }
2512
2681
  function formatProjectChoice(project, index) {
2513
2682
  const slug = project.orgSlug && project.projectSlug ? `${project.orgSlug}/${project.projectSlug}` : project.id;
2514
2683
  const name = project.name ? ` ${project.name}` : "";
@@ -2596,11 +2765,17 @@ async function runAuthCommand(parsed, options) {
2596
2765
  await writeStoredCredential(store, config.server, poll.credential);
2597
2766
  await mergeConfigFile(config.globalConfigPath, { server: config.server });
2598
2767
  await writeInteractiveStatus("Cadence CLI login approved. Credential stored.", options);
2768
+ const projectConfigured = Boolean(config.projectId);
2769
+ if (!projectConfigured) {
2770
+ await writeInteractiveStatus("No Cadence project is configured for this repo. Run cadence init to choose one.", options);
2771
+ }
2599
2772
  data = {
2600
2773
  server: config.server,
2601
2774
  credentialStored: true,
2775
+ projectConfigured,
2602
2776
  globalConfigPath: config.globalConfigPath,
2603
- loginUrl: challenge.loginUrl
2777
+ loginUrl: challenge.loginUrl,
2778
+ ...!projectConfigured ? { nextCommand: "cadence init" } : {}
2604
2779
  };
2605
2780
  }
2606
2781
  break;
@@ -2638,18 +2813,26 @@ async function runAuthCommand(parsed, options) {
2638
2813
  exitCode: 0
2639
2814
  };
2640
2815
  }
2816
+ if (parsed.command.name === "auth.login") {
2817
+ return {
2818
+ stdout: "",
2819
+ stderr: "",
2820
+ exitCode: 0
2821
+ };
2822
+ }
2641
2823
  return {
2642
- stdout: `${JSON.stringify(data, null, 2)}
2643
- `,
2824
+ stdout: humanCommandOutput(parsed.command.name, data),
2644
2825
  stderr: "",
2645
2826
  exitCode: 0
2646
2827
  };
2647
2828
  }
2648
2829
  async function runReadCommand(parsed, options) {
2649
2830
  const config = await resolveCliConfig(parsed.flags, options);
2650
- const projectId = requireProjectId(config);
2651
2831
  const client = await createClient(config, options);
2652
- const meta = commandMeta(parsed, config);
2832
+ const project = await resolveRequiredProject(config, client);
2833
+ const resolvedConfig = configWithProject(config, project);
2834
+ const projectId = project.id;
2835
+ const meta = commandMeta(parsed, resolvedConfig);
2653
2836
  let data;
2654
2837
  switch (parsed.command.name) {
2655
2838
  case "events.list":
@@ -2742,8 +2925,7 @@ async function runReadCommand(parsed, options) {
2742
2925
  };
2743
2926
  }
2744
2927
  return {
2745
- stdout: `${JSON.stringify(data, null, 2)}
2746
- `,
2928
+ stdout: humanCommandOutput(parsed.command.name, data),
2747
2929
  stderr: "",
2748
2930
  exitCode: 0
2749
2931
  };
@@ -2751,25 +2933,53 @@ async function runReadCommand(parsed, options) {
2751
2933
  async function runProjectCommand(parsed, options) {
2752
2934
  const config = await resolveCliConfig(parsed.flags, options);
2753
2935
  const client = await createClient(config, options);
2754
- const meta = commandMeta(parsed, config);
2755
2936
  let data;
2937
+ let outputConfig = config;
2756
2938
  switch (parsed.command.name) {
2757
2939
  case "projects.list":
2758
2940
  data = await client.projects.list();
2759
2941
  break;
2942
+ case "projects.use":
2943
+ {
2944
+ const project = await resolveProjectReference(requireArg(parsed, 0, "<project-id|org/project>"), client);
2945
+ const repoConfigPath = await repoConfigPathForWrite(options);
2946
+ const updates = {
2947
+ projectId: project.id,
2948
+ ...parsed.flags.server ? { server: parsed.flags.server } : {}
2949
+ };
2950
+ await mergeConfigFile(repoConfigPath, updates);
2951
+ outputConfig = configWithProject(config, project);
2952
+ data = {
2953
+ repoConfigPath,
2954
+ projectId: project.id,
2955
+ project,
2956
+ ...parsed.flags.server ? { server: parsed.flags.server } : {}
2957
+ };
2958
+ }
2959
+ break;
2760
2960
  default:
2761
2961
  throw new CliError("CLI_USAGE", `Unknown command: ${parsed.command.path.join(" ")}`);
2762
2962
  }
2763
2963
  if (parsed.flags.json) {
2764
2964
  return {
2765
- stdout: formatJson(successEnvelope(data, meta)),
2965
+ stdout: formatJson(successEnvelope(data, commandMeta(parsed, outputConfig))),
2766
2966
  stderr: "",
2767
2967
  exitCode: 0
2768
2968
  };
2769
2969
  }
2770
- return {
2771
- stdout: `${JSON.stringify(data, null, 2)}
2970
+ if (parsed.command.name === "projects.use") {
2971
+ const project = data;
2972
+ const slug = project.project?.orgSlug && project.project.projectSlug ? `${project.project.orgSlug}/${project.project.projectSlug}` : project.projectId;
2973
+ const name = project.project?.name ? ` (${project.project.name})` : "";
2974
+ return {
2975
+ stdout: `Using Cadence project ${slug}${name}.
2772
2976
  `,
2977
+ stderr: "",
2978
+ exitCode: 0
2979
+ };
2980
+ }
2981
+ return {
2982
+ stdout: humanCommandOutput(parsed.command.name, data),
2773
2983
  stderr: "",
2774
2984
  exitCode: 0
2775
2985
  };
@@ -2777,11 +2987,13 @@ async function runProjectCommand(parsed, options) {
2777
2987
  async function runActorCommand(parsed, options) {
2778
2988
  const config = await resolveCliConfig(parsed.flags, options);
2779
2989
  const client = await createClient(config, options);
2780
- const meta = commandMeta(parsed, config);
2990
+ const project = await resolveRequiredProject(config, client);
2991
+ const resolvedConfig = configWithProject(config, project);
2992
+ const meta = commandMeta(parsed, resolvedConfig);
2781
2993
  let data;
2782
2994
  switch (parsed.command.name) {
2783
2995
  case "actors.ensure-workspace-agent":
2784
- data = await ensureWorkspaceAgentIdentity(parsed, config, client, options);
2996
+ data = await ensureWorkspaceAgentIdentity(parsed, resolvedConfig, client, options);
2785
2997
  break;
2786
2998
  default:
2787
2999
  throw new CliError("CLI_USAGE", `Unknown command: ${parsed.command.path.join(" ")}`);
@@ -2794,17 +3006,18 @@ async function runActorCommand(parsed, options) {
2794
3006
  };
2795
3007
  }
2796
3008
  return {
2797
- stdout: `${JSON.stringify(data, null, 2)}
2798
- `,
3009
+ stdout: humanCommandOutput(parsed.command.name, data),
2799
3010
  stderr: "",
2800
3011
  exitCode: 0
2801
3012
  };
2802
3013
  }
2803
3014
  async function runIntakeCommand(parsed, options) {
2804
3015
  const config = await resolveCliConfig(parsed.flags, options);
2805
- const projectId = requireProjectId(config);
2806
3016
  const client = await createClient(config, options);
2807
- const meta = commandMeta(parsed, config);
3017
+ const project = await resolveRequiredProject(config, client);
3018
+ const resolvedConfig = configWithProject(config, project);
3019
+ const projectId = project.id;
3020
+ const meta = commandMeta(parsed, resolvedConfig);
2808
3021
  let data;
2809
3022
  const ifVersion = () => parseRequiredPositiveInteger(requireOption(parsed, "if-version"), "--if-version");
2810
3023
  switch (parsed.command.name) {
@@ -3036,8 +3249,7 @@ async function runIntakeCommand(parsed, options) {
3036
3249
  };
3037
3250
  }
3038
3251
  return {
3039
- stdout: `${JSON.stringify(data, null, 2)}
3040
- `,
3252
+ stdout: humanCommandOutput(parsed.command.name, data),
3041
3253
  stderr: "",
3042
3254
  exitCode: 0
3043
3255
  };
@@ -3101,7 +3313,7 @@ async function runCli(argv, options = {}) {
3101
3313
  if (parsed.command.name === "events.list" || parsed.command.name === "work.overview" || parsed.command.name === "tickets.get" || parsed.command.name === "tickets.list" || parsed.command.name === "sessions.current" || parsed.command.name === "changesets.get" || parsed.command.name === "changesets.context" || parsed.command.name === "changesets.current" || parsed.command.name === "changesets.list" || parsed.command.name === "changesets.notes.get") {
3102
3314
  return await runReadCommand(parsed, options);
3103
3315
  }
3104
- if (parsed.command.name === "projects.list") {
3316
+ if (parsed.command.name === "projects.list" || parsed.command.name === "projects.use") {
3105
3317
  return await runProjectCommand(parsed, options);
3106
3318
  }
3107
3319
  if (parsed.command.name === "intake" || parsed.command.name === "intake.dismiss" || parsed.command.name === "tickets.attach" || parsed.command.name === "tickets.create" || parsed.command.name === "tickets.update" || parsed.command.name === "tickets.claim" || parsed.command.name === "tickets.release" || parsed.command.name === "tickets.log" || parsed.command.name === "tickets.complete" || parsed.command.name === "sessions.start" || parsed.command.name === "sessions.end" || parsed.command.name === "sessions.files" || parsed.command.name === "changesets.create" || parsed.command.name === "changesets.notes.put" || parsed.command.name === "changesets.notes.apply") {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@trycadence/cli",
3
- "version": "0.1.6",
3
+ "version": "0.1.8",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "bin": {