@mediamonks/canopy-cli 0.7.26 → 0.7.27

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.
@@ -24,12 +24,15 @@ canopy feedback attach <id> ./after-fix.png --comment "fixed — see the shot"
24
24
 
25
25
  ## Subcommands
26
26
 
27
- Inbox commands resolve the project the usual way — `canopy.json` in the target
28
- directory, or `--project <slug>` — and require project membership. The
29
- item-scoped ones (`show`, `comment`, `status`, `attach`) also work with just
30
- the id from anywhere: report ids are unique across projects, so with no
31
- project context the item is looked up by id alone. `list` and `show` take
32
- `--json` for machine-readable output.
27
+ Every report has a key made of its project and a number, like
28
+ `my-project-17`. Inbox commands resolve the project the usual way —
29
+ `canopy.json` in the target directory, or `--project <slug>` — and require
30
+ project membership; inside the project you can refer to a report by its
31
+ number alone (`canopy feedback show 17`). The item-scoped commands (`show`,
32
+ `comment`, `status`, `attach`) also take the full key from anywhere, with no
33
+ project context: the key names its project. Older ids from before keys
34
+ existed still work wherever a key does. `list` and `show` take `--json` for
35
+ machine-readable output.
33
36
 
34
37
  - `list [--status <s>] [--kind <k>]` — the project's inbox. Statuses:
35
38
  `investigate`, `investigated`, `accepted`, `in_progress`, `resolved`,
@@ -1,4 +1,4 @@
1
- import { t as version } from "./package-CBso2611.mjs";
1
+ import { t as version } from "./package-CsmBH_fN.mjs";
2
2
  import { n as siteConfig } from "./site-DM2J10CB.mjs";
3
3
  import { i as latestKnownVersion, r as compareSemver } from "./version-check-CcsA1QjI.mjs";
4
4
  import { r as reportErrors } from "./ui-DR-siu2S.mjs";
@@ -1,4 +1,4 @@
1
- import { t as version } from "./package-CBso2611.mjs";
1
+ import { t as version } from "./package-CsmBH_fN.mjs";
2
2
  import { a as projectDirArgs, c as resolveProject, d as tryResolveProject, n as siteConfig, u as tryReadCanopyJson } from "./site-DM2J10CB.mjs";
3
3
  import { c as apiDownload, l as apiRequest, r as reportErrors, t as formatDateTime, u as apiUpload } from "./ui-DR-siu2S.mjs";
4
4
  import { defineCommand } from "citty";
@@ -72,19 +72,38 @@ const jsonArg = { json: {
72
72
  type: "boolean",
73
73
  description: "Machine-readable JSON output"
74
74
  } };
75
+ const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
76
+ const KEY_RE = /^([a-z][a-z0-9-]*)-(\d+)$/;
75
77
  /**
76
- * The project an item-scoped command targets: `--project` or canopy.json when
77
- * present, otherwise the API's ID-only lookup — item ids are globally unique,
78
- * so the item names its own project. That's the usual agent situation: holding
79
- * an id from a notification or a Slack thread, in a directory that isn't the
80
- * project.
78
+ * The project slug a full key names (`kitchen-sink-17` → `kitchen-sink`), or
79
+ * null for a UUID or a bare number. UUIDs are checked first: one whose last
80
+ * group is all digits would otherwise look like a key.
81
81
  */
82
- async function resolveItemSlug(args, itemId) {
82
+ function slugOfKey(ref) {
83
+ if (UUID_RE.test(ref)) return null;
84
+ return KEY_RE.exec(ref.toLowerCase())?.[1] ?? null;
85
+ }
86
+ /**
87
+ * The project an item-scoped command targets. A full key names its own
88
+ * project and wins over the directory — `canopy feedback show other-3` from
89
+ * inside `sink` means other's report, not a miss on sink's. Otherwise
90
+ * `--project` or canopy.json; and failing those, the API's project-less
91
+ * lookup for a UUID from an old link. A bare number is only meaningful with a
92
+ * project in hand.
93
+ */
94
+ async function resolveItemSlug(args, ref) {
95
+ const fromKey = slugOfKey(ref);
96
+ if (fromKey) return fromKey;
83
97
  const { slug } = tryResolveProject(args);
84
98
  if (slug) return slug;
85
- const { project } = await apiRequest("GET", `/feedback/${encodeURIComponent(itemId)}`);
99
+ if (/^\d+$/.test(ref)) throw new Error(`"${ref}" is a bare report number — run this inside the project, pass --project, or use the full key (e.g. my-project-${ref}).`);
100
+ const { project } = await apiRequest("GET", `/feedback/${encodeURIComponent(ref)}`);
86
101
  return project.slug;
87
102
  }
103
+ /** What a report is called in output: its key, or the UUID on pre-key rows. */
104
+ function refOf(item) {
105
+ return item.key ?? item.id;
106
+ }
88
107
  function firstLine(body) {
89
108
  const line = body.split("\n")[0] ?? "";
90
109
  return line.length > 72 ? `${line.slice(0, 71)}…` : line;
@@ -101,7 +120,7 @@ function printItems(items) {
101
120
  for (const item of items) {
102
121
  const comments = item.comment_count ? ` 💬${item.comment_count}` : "";
103
122
  const shots = item.attachment_count ? ` 📎${item.attachment_count}` : "";
104
- console.log(`${item.id} ${item.status} ${item.kind} ${itemLabel(item)}${comments}${shots}`);
123
+ console.log(`${refOf(item)} ${item.status} ${item.kind} ${itemLabel(item)}${comments}${shots}`);
105
124
  }
106
125
  }
107
126
  const listCommand = defineCommand({
@@ -139,7 +158,7 @@ const showCommand = defineCommand({
139
158
  args: {
140
159
  id: {
141
160
  type: "positional",
142
- description: "Item id (from `canopy feedback list`)",
161
+ description: "Report key like my-project-17 (from `canopy feedback list`), or its number inside the project",
143
162
  required: true
144
163
  },
145
164
  ...projectDirArgs,
@@ -150,7 +169,8 @@ const showCommand = defineCommand({
150
169
  }
151
170
  },
152
171
  run: reportErrors("feedback show", async ({ args }) => {
153
- const { slug } = tryResolveProject(args);
172
+ const slug = slugOfKey(args.id) ?? tryResolveProject(args).slug;
173
+ if (!slug && /^\d+$/.test(args.id)) throw new Error(`"${args.id}" is a bare report number — run this inside the project, pass --project, or use the full key (e.g. my-project-${args.id}).`);
154
174
  const detail = await apiRequest("GET", slug ? `/projects/${slug}/feedback/${encodeURIComponent(args.id)}` : `/feedback/${encodeURIComponent(args.id)}`);
155
175
  if (args.json) {
156
176
  console.log(JSON.stringify(detail, null, 2));
@@ -158,7 +178,7 @@ const showCommand = defineCommand({
158
178
  }
159
179
  const { item, comments } = detail;
160
180
  if (item.title) console.log(item.title);
161
- console.log(`${item.kind} · ${item.status} · via ${item.source} · ${formatDateTime(item.created_at)}`);
181
+ console.log(`${refOf(item)} · ${item.kind} · ${item.status} · via ${item.source} · ${formatDateTime(item.created_at)}`);
162
182
  console.log(`reported by ${item.reporter_name || item.reporter_email || "unknown"}` + (item.deployment_id ? ` on ${item.deployment_id}` : "") + (item.channel === "staging" ? " (staging)" : ""));
163
183
  console.log(`\n${item.body}\n`);
164
184
  if (item.context) {
@@ -207,7 +227,7 @@ const commentCommand = defineCommand({
207
227
  args: {
208
228
  id: {
209
229
  type: "positional",
210
- description: "Item id",
230
+ description: "Report key (my-project-17), or its number inside the project",
211
231
  required: true
212
232
  },
213
233
  text: {
@@ -237,7 +257,7 @@ const statusCommand = defineCommand({
237
257
  args: {
238
258
  id: {
239
259
  type: "positional",
240
- description: "Item id",
260
+ description: "Report key (my-project-17), or its number inside the project",
241
261
  required: true
242
262
  },
243
263
  to: {
@@ -281,7 +301,7 @@ const mineCommand = defineCommand({
281
301
  console.log("You haven't filed any reports.");
282
302
  return;
283
303
  }
284
- for (const item of items) console.log(`${item.id} ${item.status} ${item.kind} [${item.project_slug}] ${itemLabel(item)}`);
304
+ for (const item of items) console.log(`${refOf(item)} ${item.status} ${item.kind} [${item.project_slug}] ${itemLabel(item)}`);
285
305
  })
286
306
  });
287
307
  const reportCommand = defineCommand({
@@ -348,8 +368,8 @@ const reportCommand = defineCommand({
348
368
  ...args.status ? { status: args.status } : {},
349
369
  context
350
370
  });
351
- console.log(`Filed ${kind} ${item.id} on ${item.project} as ${item.status}.`);
352
- if (paths.length > 0) await attachFiles(item.project, item.id, paths, args.caption);
371
+ console.log(`Filed ${kind} ${item.key} on ${item.project} as ${item.status}.`);
372
+ if (paths.length > 0) await attachFiles(item.project, item.key, paths, args.caption);
353
373
  console.log(`Follow it with \`canopy feedback mine\` or on the dashboard's Reports page.`);
354
374
  })
355
375
  });
@@ -370,7 +390,7 @@ const feedbackCommand = defineCommand({
370
390
  args: {
371
391
  id: {
372
392
  type: "positional",
373
- description: "Item id",
393
+ description: "Report key (my-project-17), or its number inside the project",
374
394
  required: true
375
395
  },
376
396
  file: {
package/dist/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { t as version } from "./package-CBso2611.mjs";
2
+ import { t as version } from "./package-CsmBH_fN.mjs";
3
3
  import { r as siteHeaderLine, t as setInvocationProfile } from "./site-DM2J10CB.mjs";
4
4
  import { a as notifyOfUpdate, o as runRefresh } from "./version-check-CcsA1QjI.mjs";
5
5
  import { defineCommand, runMain } from "citty";
@@ -77,13 +77,13 @@ const main = defineCommand({
77
77
  egress: () => import("./egress-Cn-P46O3.mjs").then((m) => m.egressCommand),
78
78
  secrets: () => import("./secrets-BD3DaTih.mjs").then((m) => m.secretsCommand),
79
79
  schedule: () => import("./schedule-Buu71RWQ.mjs").then((m) => m.scheduleCommand),
80
- feedback: () => import("./feedback-C9m3dNkJ.mjs").then((m) => m.feedbackCommand),
80
+ feedback: () => import("./feedback-DormOsQa.mjs").then((m) => m.feedbackCommand),
81
81
  db: () => import("./db-DVMwCLlU.mjs").then((m) => m.dbCommand),
82
82
  usage: () => import("./usage-81JSiWIx.mjs").then((m) => m.usageCommand),
83
83
  open: () => import("./open-rS1ARzOy.mjs").then((m) => m.openCommand),
84
- docs: () => import("./docs-x3hMtUzT.mjs").then((m) => m.docsCommand),
85
- skill: () => import("./skill-DmVlZXgv.mjs").then((m) => m.skillCommand),
86
- upgrade: () => import("./upgrade-C7u8pxeV.mjs").then((m) => m.upgradeCommand),
84
+ docs: () => import("./docs-CuS9nIXm.mjs").then((m) => m.docsCommand),
85
+ skill: () => import("./skill-DX18WOap.mjs").then((m) => m.skillCommand),
86
+ upgrade: () => import("./upgrade-PfdH0NPe.mjs").then((m) => m.upgradeCommand),
87
87
  logs: () => import("./logs-CfDINw7U.mjs").then((m) => m.logsCommand)
88
88
  }
89
89
  });
@@ -1,4 +1,4 @@
1
1
  //#region package.json
2
- var version = "0.7.26";
2
+ var version = "0.7.27";
3
3
  //#endregion
4
4
  export { version as t };
@@ -1,4 +1,4 @@
1
- import { t as version } from "./package-CBso2611.mjs";
1
+ import { t as version } from "./package-CsmBH_fN.mjs";
2
2
  import { r as reportErrors } from "./ui-DR-siu2S.mjs";
3
3
  import { defineCommand } from "citty";
4
4
  import { cpSync, existsSync, mkdirSync, rmSync, symlinkSync, writeFileSync } from "node:fs";
@@ -112,7 +112,7 @@ const upgradeCommand = defineCommand({
112
112
  const spec = `${PACKAGE_NAME}@${args.version ?? "latest"}`;
113
113
  const command = pmCommand(pm);
114
114
  const cmd = installArgs(pm, spec);
115
- const { skillInstalled } = await import("./skill-DmVlZXgv.mjs");
115
+ const { skillInstalled } = await import("./skill-DX18WOap.mjs");
116
116
  const { homedir } = await import("node:os");
117
117
  const refreshGlobal = skillInstalled(homedir());
118
118
  const refreshRepo = process.cwd() !== homedir() && skillInstalled(process.cwd());
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mediamonks/canopy-cli",
3
- "version": "0.7.26",
3
+ "version": "0.7.27",
4
4
  "description": "Canopy CLI — create, develop and deploy Canopy projects.",
5
5
  "bin": {
6
6
  "canopy": "./dist/index.mjs"