@robbeverhelst/do 1.48.0 → 1.50.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.
Files changed (2) hide show
  1. package/dist/do.js +248 -28
  2. package/package.json +1 -1
package/dist/do.js CHANGED
@@ -21567,32 +21567,37 @@ function parseCapture(input) {
21567
21567
  return { title, tags, ...priority ? { priority } : {}, ...area ? { area } : {} };
21568
21568
  }
21569
21569
  // ../../packages/schema/src/presets/collection-types.ts
21570
+ var coverUrl = exports_external.string().url().max(2048).nullish();
21570
21571
  var bookFacetSchema = exports_external.object({
21571
21572
  author: exports_external.string().min(1).max(200).nullish(),
21572
- coverUrl: exports_external.string().url().max(2048).nullish(),
21573
+ coverUrl,
21573
21574
  pageCount: exports_external.number().int().min(1).max(1e5).nullish()
21574
21575
  }).strict();
21575
- var stubFacetSchema = exports_external.object({}).strict();
21576
- var ALL_STATUSES = collectionStatusValues;
21577
- var DEFAULT_STATUS_LABELS = {
21578
- want: "Want",
21579
- have: "Have",
21580
- doing: "Doing",
21581
- done: "Done",
21582
- dropped: "Dropped"
21583
- };
21584
- function stubType(key, label, itemNoun) {
21585
- return {
21586
- key,
21587
- label,
21588
- itemNoun,
21589
- statuses: ALL_STATUSES,
21590
- statusLabels: DEFAULT_STATUS_LABELS,
21591
- facetSchema: stubFacetSchema,
21592
- view: "grid",
21593
- stub: true
21594
- };
21595
- }
21576
+ var filmFacetSchema = exports_external.object({
21577
+ kind: exports_external.enum(["movie", "show"]).nullish(),
21578
+ year: exports_external.number().int().min(1888).max(2100).nullish(),
21579
+ director: exports_external.string().min(1).max(200).nullish(),
21580
+ coverUrl
21581
+ }).strict();
21582
+ var gameFacetSchema2 = exports_external.object({
21583
+ platform: exports_external.string().min(1).max(120).nullish(),
21584
+ coverUrl
21585
+ }).strict();
21586
+ var musicFacetSchema = exports_external.object({
21587
+ artist: exports_external.string().min(1).max(200).nullish(),
21588
+ year: exports_external.number().int().min(1860).max(2100).nullish(),
21589
+ coverUrl
21590
+ }).strict();
21591
+ var placeFacetSchema2 = exports_external.object({
21592
+ address: exports_external.string().min(1).max(400).nullish(),
21593
+ lat: exports_external.number().min(-90).max(90).nullish(),
21594
+ lng: exports_external.number().min(-180).max(180).nullish()
21595
+ }).strict();
21596
+ var wineFacetSchema = exports_external.object({
21597
+ type: exports_external.enum(["wine", "whisky"]).nullish(),
21598
+ region: exports_external.string().min(1).max(200).nullish(),
21599
+ vintage: exports_external.number().int().min(1800).max(2100).nullish()
21600
+ }).strict();
21596
21601
  var COLLECTION_TYPES = {
21597
21602
  book: {
21598
21603
  key: "book",
@@ -21608,15 +21613,90 @@ var COLLECTION_TYPES = {
21608
21613
  },
21609
21614
  facetSchema: bookFacetSchema,
21610
21615
  view: "shelf",
21616
+ subtitleKey: "author",
21611
21617
  lookupId: "open-library"
21612
21618
  },
21613
- film: stubType("film", "Films & TV", "title"),
21614
- game: stubType("game", "Games", "game"),
21615
- music: stubType("music", "Music", "album"),
21616
- place: stubType("place", "Places", "place"),
21617
- wine: stubType("wine", "Wines", "bottle")
21619
+ film: {
21620
+ key: "film",
21621
+ label: "Films & TV",
21622
+ itemNoun: "title",
21623
+ statuses: ["want", "doing", "done", "dropped"],
21624
+ statusLabels: { want: "Want", doing: "Watching", done: "Watched", dropped: "Dropped" },
21625
+ facetSchema: filmFacetSchema,
21626
+ view: "grid",
21627
+ subtitleKey: "director",
21628
+ lookupId: "tmdb"
21629
+ },
21630
+ game: {
21631
+ key: "game",
21632
+ label: "Games",
21633
+ itemNoun: "game",
21634
+ statuses: ["want", "have", "doing", "done", "dropped"],
21635
+ statusLabels: {
21636
+ want: "Want",
21637
+ have: "Have",
21638
+ doing: "Playing",
21639
+ done: "Beaten",
21640
+ dropped: "Dropped"
21641
+ },
21642
+ facetSchema: gameFacetSchema2,
21643
+ view: "grid",
21644
+ subtitleKey: "platform",
21645
+ lookupId: "igdb"
21646
+ },
21647
+ music: {
21648
+ key: "music",
21649
+ label: "Music",
21650
+ itemNoun: "album",
21651
+ statuses: ["want", "done"],
21652
+ statusLabels: { want: "Want", done: "Listened" },
21653
+ facetSchema: musicFacetSchema,
21654
+ view: "shelf",
21655
+ subtitleKey: "artist"
21656
+ },
21657
+ place: {
21658
+ key: "place",
21659
+ label: "Places",
21660
+ itemNoun: "place",
21661
+ statuses: ["want", "done"],
21662
+ statusLabels: { want: "Want", done: "Been" },
21663
+ facetSchema: placeFacetSchema2,
21664
+ view: "map",
21665
+ subtitleKey: "address"
21666
+ },
21667
+ wine: {
21668
+ key: "wine",
21669
+ label: "Wines",
21670
+ itemNoun: "bottle",
21671
+ statuses: ["want", "have"],
21672
+ statusLabels: { want: "Want", have: "Have" },
21673
+ facetSchema: wineFacetSchema,
21674
+ view: "grid",
21675
+ subtitleKey: "region"
21676
+ }
21618
21677
  };
21619
21678
  var COLLECTION_TYPE_LIST = collectionTypeValues.map((k) => COLLECTION_TYPES[k]);
21679
+ var collectionTypeSummarySchema = exports_external.object({
21680
+ key: collectionTypeSchema,
21681
+ label: exports_external.string(),
21682
+ itemNoun: exports_external.string(),
21683
+ statuses: exports_external.array(collectionStatusSchema),
21684
+ statusLabels: exports_external.record(exports_external.string(), exports_external.string()),
21685
+ view: collectionViewSchema,
21686
+ lookupId: exports_external.string().nullable()
21687
+ });
21688
+ function toCollectionTypeSummary(config2) {
21689
+ return {
21690
+ key: config2.key,
21691
+ label: config2.label,
21692
+ itemNoun: config2.itemNoun,
21693
+ statuses: [...config2.statuses],
21694
+ statusLabels: { ...config2.statusLabels },
21695
+ view: config2.view,
21696
+ lookupId: config2.lookupId ?? null
21697
+ };
21698
+ }
21699
+ var COLLECTION_TYPE_SUMMARIES = COLLECTION_TYPE_LIST.map(toCollectionTypeSummary);
21620
21700
  // ../../packages/schema/src/presets/recipe.ts
21621
21701
  var presetEntitySchema = exports_external.enum([
21622
21702
  "habit",
@@ -23635,7 +23715,7 @@ function parseCapture2(input) {
23635
23715
 
23636
23716
  // src/meta.ts
23637
23717
  var CLI_NAME = "do";
23638
- var VERSION = "1.48.0";
23718
+ var VERSION = "1.50.0";
23639
23719
  var API_PREFIX = "/api/v1";
23640
23720
  var DEFAULT_API_URL = "http://localhost:3001";
23641
23721
 
@@ -24262,6 +24342,81 @@ var COMMAND_SPECS = [
24262
24342
  returns: "PresetInstallResult",
24263
24343
  notes: "Authors content as the caller, so it needs a key minted while logged in."
24264
24344
  },
24345
+ {
24346
+ path: ["collection", "types"],
24347
+ summary: "List the catalog Types (key \xB7 label \xB7 status subset).",
24348
+ args: [],
24349
+ flags: [],
24350
+ scopes: ["collections:read"],
24351
+ returns: "CollectionTypeSummary",
24352
+ notes: "The Type registry is code-defined (ADR 0017); served from GET /api/v1/collection/types."
24353
+ },
24354
+ {
24355
+ path: ["collection", "list"],
24356
+ summary: "List Collection Items, optionally sliced by Type / Status / tag.",
24357
+ args: [],
24358
+ flags: [
24359
+ { name: "type", type: "string", description: "Filter to one Type (book|film|game|\u2026)." },
24360
+ {
24361
+ name: "status",
24362
+ type: "string",
24363
+ description: "Filter to one Status (want|have|doing|done|dropped)."
24364
+ },
24365
+ { name: "tag", type: "string", description: "Filter to Items carrying this tag." }
24366
+ ],
24367
+ scopes: ["collections:read"],
24368
+ returns: "CollectionItemView"
24369
+ },
24370
+ {
24371
+ path: ["collection", "add"],
24372
+ summary: "Add a Collection Item (the title may embed #tag).",
24373
+ args: [{ name: "title", required: true, description: "Item title; may embed #tag." }],
24374
+ flags: [
24375
+ { name: "type", type: "string", description: "Type (book|film|game|music|place|wine)." },
24376
+ {
24377
+ name: "status",
24378
+ type: "string",
24379
+ description: "Initial status; defaults to the Type's first."
24380
+ }
24381
+ ],
24382
+ scopes: ["collections:write"],
24383
+ returns: "CollectionItemView",
24384
+ grammar: {
24385
+ "#tag": "Adds a tag. Repeatable: '#scifi #2024'."
24386
+ },
24387
+ notes: "--type is required; the per-Type facet rides --data (validated server-side against the Type)."
24388
+ },
24389
+ {
24390
+ path: ["collection", "set-status"],
24391
+ summary: "Set a Collection Item's status (browse/filter spine).",
24392
+ args: [
24393
+ { name: "id", required: true, description: "Item ULID." },
24394
+ {
24395
+ name: "status",
24396
+ required: true,
24397
+ description: "want|have|doing|done|dropped (per-Type subset)."
24398
+ }
24399
+ ],
24400
+ flags: [],
24401
+ scopes: ["collections:write"],
24402
+ returns: "CollectionItemView"
24403
+ },
24404
+ {
24405
+ path: ["collection", "add-entry"],
24406
+ summary: "Log a dated Entry against an Item (recomputes its headline rating).",
24407
+ args: [{ name: "id", required: true, description: "Item ULID." }],
24408
+ flags: [
24409
+ { name: "rating", type: "string", description: "Rating 1\u20135 for this Entry." },
24410
+ { name: "note", type: "string", description: "Free-text note for this Entry." },
24411
+ {
24412
+ name: "on",
24413
+ type: "string",
24414
+ description: "Day it occurred (YYYY-MM-DD); defaults to today."
24415
+ }
24416
+ ],
24417
+ scopes: ["collections:write"],
24418
+ returns: "CollectionItemDetail"
24419
+ },
24265
24420
  {
24266
24421
  path: ["invite", "create"],
24267
24422
  summary: "Create a Space invite (email/link/guest).",
@@ -25018,6 +25173,55 @@ var HANDLERS = {
25018
25173
  const result = presetInstallResultSchema.parse(await requireClient().post(`/presets/${encodeURIComponent(key)}/install`, undefined));
25019
25174
  return { data: result, render: renderPresetInstall };
25020
25175
  },
25176
+ "collection types": async ({ requireClient }) => {
25177
+ const types2 = collectionTypeSummarySchema.array().parse(await requireClient().get("/collection/types"));
25178
+ return { data: types2, render: renderList };
25179
+ },
25180
+ "collection list": async ({ flags, requireClient }) => {
25181
+ const query = {
25182
+ type: flagString(flags, "type"),
25183
+ status: flagString(flags, "status"),
25184
+ tag: flagString(flags, "tag")
25185
+ };
25186
+ return { data: await requireClient().get("/collection", { query }), render: renderList };
25187
+ },
25188
+ "collection add": async ({ args, flags, requireClient }) => {
25189
+ const { title, tags } = parseTitleTags(args);
25190
+ if (title.length === 0)
25191
+ throw usageError('collection add needs a title, e.g. `do collection add "Dune" --type book`');
25192
+ const type = requireFlag(flags, "type", "collection add needs --type <book|film|game|music|place|wine>");
25193
+ const body = parseInput(createCollectionItemInputSchema, {
25194
+ id: ulid3(),
25195
+ type,
25196
+ title,
25197
+ ...tags.length > 0 ? { tags } : {},
25198
+ ...flagString(flags, "status") ? { status: flagString(flags, "status") } : {}
25199
+ });
25200
+ return { data: await requireClient().post("/collection", body), render: renderRecord };
25201
+ },
25202
+ "collection set-status": async ({ args, requireClient }) => {
25203
+ const id = requireArg(args, 0, "item id");
25204
+ const status = requireArg(args, 1, "status");
25205
+ const body = parseInput(updateCollectionItemInputSchema, { status });
25206
+ return { data: await requireClient().patch(`/collection/${id}`, body), render: renderRecord };
25207
+ },
25208
+ "collection add-entry": async ({ args, flags, io, requireClient }) => {
25209
+ const id = requireArg(args, 0, "item id");
25210
+ const rating = flagString(flags, "rating");
25211
+ if (rating !== undefined && Number.isNaN(Number(rating))) {
25212
+ throw usageError("--rating must be a number (1\u20135)");
25213
+ }
25214
+ const body = parseInput(createCollectionEntryInputSchema, {
25215
+ id: ulid3(),
25216
+ occurredOn: flagString(flags, "on") ?? isoDay(io.now()),
25217
+ ...rating !== undefined ? { rating: Number(rating) } : {},
25218
+ ...flagString(flags, "note") ? { note: flagString(flags, "note") } : {}
25219
+ });
25220
+ return {
25221
+ data: await requireClient().post(`/collection/${id}/entries`, body),
25222
+ render: renderRecord
25223
+ };
25224
+ },
25021
25225
  "invite create": async ({ flags, requireClient }) => {
25022
25226
  const body = parseInput(createInviteInputSchema, { id: ulid3(), ...readDataFlag(flags) });
25023
25227
  return { data: await requireClient().post("/invites", body), render: renderRecord };
@@ -25203,6 +25407,22 @@ async function setModuleVisibility(args, requireClient, on) {
25203
25407
  const body = parseInput(updateUserSettingsInputSchema, { enabledModules });
25204
25408
  return { data: await client.patch("/settings", body), render: renderRecord };
25205
25409
  }
25410
+ function parseTitleTags(args) {
25411
+ const titleWords = [];
25412
+ const tags = [];
25413
+ for (const token of args.join(" ").split(/\s+/).filter(Boolean)) {
25414
+ if (token.length > 1 && token.startsWith("#"))
25415
+ tags.push(token.slice(1));
25416
+ else
25417
+ titleWords.push(token);
25418
+ }
25419
+ return { title: titleWords.join(" ").trim(), tags };
25420
+ }
25421
+ function isoDay(date6) {
25422
+ const month = String(date6.getMonth() + 1).padStart(2, "0");
25423
+ const day = String(date6.getDate()).padStart(2, "0");
25424
+ return `${date6.getFullYear()}-${month}-${day}`;
25425
+ }
25206
25426
  function requireArg(args, index2, name) {
25207
25427
  const value = args[index2];
25208
25428
  if (value === undefined || value.length === 0) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@robbeverhelst/do",
3
- "version": "1.48.0",
3
+ "version": "1.50.0",
4
4
  "private": false,
5
5
  "description": "do — the agent-drivable CLI over the public /api/v1 surface",
6
6
  "type": "module",