@arbidocs/cli 0.4.84 β†’ 0.4.85

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/CHANGELOG.md CHANGED
@@ -1,6 +1,42 @@
1
1
  # Changelog
2
2
 
3
- ## v0.4.84
3
+ ## v0.4.85
4
+
5
+ [compare changes](https://github.com/arbicity/ARBI-frontend/compare/v0.4.83...HEAD)
6
+
7
+ ### πŸš€ Enhancements
8
+
9
+ - **cli:** Tag kinds in arbi tags create, list, show and matrix ([6c7331ee9](https://github.com/arbicity/ARBI-frontend/commit/6c7331ee9))
10
+ - **blocks:** Mail kit (ConnectedMailbox, ConnectedMailComposer) shared by the app and hosts ([3c854536e](https://github.com/arbicity/ARBI-frontend/commit/3c854536e))
11
+ - **tags:** Flat issues with per-kind fields; the tag list is the table ([2194bb4d0](https://github.com/arbicity/ARBI-frontend/commit/2194bb4d0))
12
+
13
+ ### 🩹 Fixes
14
+
15
+ - **recording:** Overlay video captions in the lower half of the frame ([1ea3d58bc](https://github.com/arbicity/ARBI-frontend/commit/1ea3d58bc))
16
+ - **tags:** An issue in a list shows its evidence, not an empty rows table ([34e71de7b](https://github.com/arbicity/ARBI-frontend/commit/34e71de7b))
17
+ - **personas:** Overlay think-aloud captions in the lower half of the frame ([7b22e8609](https://github.com/arbicity/ARBI-frontend/commit/7b22e8609))
18
+ - **blocks:** Start AI-first workflow runs and never skip a failed step by default ([#1572](https://github.com/arbicity/ARBI-frontend/pull/1572))
19
+ - **client:** Type the password-change calls against the current schema ([498b0d1da](https://github.com/arbicity/ARBI-frontend/commit/498b0d1da))
20
+
21
+ ### πŸ’… Refactors
22
+
23
+ - **tags:** Stop sending kind_to_children ([c9bac43a9](https://github.com/arbicity/ARBI-frontend/commit/c9bac43a9))
24
+
25
+ ### 🏑 Chore
26
+
27
+ - Update OpenAPI schema ([f2437789f](https://github.com/arbicity/ARBI-frontend/commit/f2437789f))
28
+ - Measure only the markdown chunk in its size budget ([48a4aeda8](https://github.com/arbicity/ARBI-frontend/commit/48a4aeda8))
29
+
30
+ ### βœ… Tests
31
+
32
+ - **tags:** An issue in a list shows no rows table ([5e629e1a7](https://github.com/arbicity/ARBI-frontend/commit/5e629e1a7))
33
+
34
+ ### πŸ€– CI
35
+
36
+ - Run the full @arbidocs/blocks unit suite in the Test job ([c6fcbabd0](https://github.com/arbicity/ARBI-frontend/commit/c6fcbabd0))
37
+ - Move the blocks suite to its own path-filtered job ([a97af75bd](https://github.com/arbicity/ARBI-frontend/commit/a97af75bd))
38
+
39
+ ## v0.4.83
4
40
 
5
41
  [compare changes](https://github.com/arbicity/ARBI-frontend/compare/v0.4.81...HEAD)
6
42
 
@@ -10,10 +46,6 @@
10
46
  - **tags:** A Tags page for every tag kind and what carries it ([84f8b92ee](https://github.com/arbicity/ARBI-frontend/commit/84f8b92ee))
11
47
  - **tags:** Threads on the Tags page ([b45a97c30](https://github.com/arbicity/ARBI-frontend/commit/b45a97c30))
12
48
 
13
- ### 🩹 Fixes
14
-
15
- - Sign the viewer's PDF rendition of the exact version shown ([6846a54eb](https://github.com/arbicity/ARBI-frontend/commit/6846a54eb))
16
-
17
49
  ### 🏑 Chore
18
50
 
19
51
  - Update OpenAPI schema (tag kinds, entity extraction) ([557eae292](https://github.com/arbicity/ARBI-frontend/commit/557eae292))
package/SKILL.md CHANGED
@@ -61,7 +61,7 @@ Scripting notes:
61
61
 
62
62
  ## For Automations / AI Agents
63
63
 
64
- - **Structured output (`--json`)**: supported on `status`, `config show`, `health`, `models`, `workspaces`, `workspace users`, `workspace current`, `docs`, `convo` (or `convo list`), `tags` (or `tags list`), `contacts` (or `contacts list`), `find`, `settings get`, `task list` / `task status` / `task result`, `agent list`, `session list`, `dm list`, `notifications list`, `local ls` / `local find` / `local cat` / `local tree`, `update --check`, `register --non-interactive`, `ask -b`, `upload`, `watch`, `workflow list` / `create` / `run` / `update`, `run start` / `run steps` / `run step`, `tags create` / `tags fields` / `tags matrix`, `project set` / `show` / `events`, `usage conversation` / `usage project`. Use it whenever parsing output. `docs` also supports `--csv`, `--ids`, and `--count`. `arbi ask --json` without `-b` errors out β€” streaming mode is prose, not JSON; pass `-b` to get a background task ID, then `arbi task result --json`.
64
+ - **Structured output (`--json`)**: supported on `status`, `config show`, `health`, `models`, `workspaces`, `workspace users`, `workspace current`, `docs`, `convo` (or `convo list`), `tags` (or `tags list`), `tags show`, `contacts` (or `contacts list`), `find`, `settings get`, `task list` / `task status` / `task result`, `agent list`, `session list`, `dm list`, `notifications list`, `local ls` / `local find` / `local cat` / `local tree`, `update --check`, `register --non-interactive`, `ask -b`, `upload`, `watch`, `workflow list` / `create` / `run` / `update`, `run start` / `run steps` / `run step`, `tags create` / `tags fields` / `tags list --links`, `tag-kinds list` / `show` / `set-fields`, `project set` / `show` / `events`, `usage conversation` / `usage project`. Use it whenever parsing output. `docs` also supports `--csv`, `--ids`, and `--count`. `arbi ask --json` without `-b` errors out β€” streaming mode is prose, not JSON; pass `-b` to get a background task ID, then `arbi task result --json`.
65
65
  - **Human table output is lossy**: table rows may contain status glyphs (`●`, `🟩`, etc.) and emoji, and column widths truncate content. Never parse tables β€” always pass `--json`.
66
66
  - **Exit codes**:
67
67
  - `0` success
@@ -95,8 +95,10 @@ All document list/search/meta commands support machine-readable output. Prefer `
95
95
  - **Per-doc**: `arbi doc get <id...>` returns full JSON (no flag needed). `arbi doc delete <id...>` and `arbi doc update <id> '<json>'` take IDs directly.
96
96
  - **Upload by URL**: `arbi doc upload-url <url> --json`.
97
97
  - **Download**: `arbi download <id> -o <path>` writes the file; `-o -` streams raw bytes to stdout; `--json` emits a `{doc_id, path, bytes, filename}` metadata line after writing.
98
- - **Tags**: `arbi tags list --json`, `arbi tags create <name> -t <type> [--parent <tag>]`, `arbi tags delete <id>`.
99
- - **Tag fields and the matrix**: a tag can carry typed fields (`text`, `number`, `date`, `checkbox`, `select`, `person`); a child tag inherits its parent's, so a parent's fields are the columns of its children. `arbi tags fields <tag>` shows a tag's fields and values; `--define '<json>'` (or a `.json`/`.yaml` file of `[{name, type, options}]`) sets its own fields (a field defined again by name keeps its values; ones left out are dropped unless `--keep-others`); `--set name=value` (repeatable, `name=` clears) sets values, checked against the fields and a select's options. `arbi tags matrix --parent <tag>` (or `--tag <tag>` repeated) prints rows = tags, columns = fields, then DOCS and FACTS counts, gaps marked `β€”` and listed; `--json` adds each row's newest documents and facts (`--links <n>`). Tags are taken by id or unique name.
98
+ - **Tags**: `arbi tags list [--kind <kind>] [--parent <tag>] [--links [n]] --json`, `arbi tags show <tag> [--json]`, `arbi tags create <name> -t <type> [--kind <kind>] [--parent <tag>] [--set name=value]`, `arbi tags delete <id>`.
99
+ - **Tag kinds**: every tag has a `kind`: `label`, `annotation`, `search`, `issue`, `person` or `organisation` (shown in the KIND column of `tags list`, in `tags show`, and as `kind` in `--json`). `tags create --kind issue` makes an issue (without `--kind`: from the type, checkbox β†’ label; never from a parent); `tags list --kind <kind>` filters by it; an unknown kind is refused with the list of kinds. Issues, people and organisations are flat: no `--parent` (refused), and nothing nests under one. Parents are for label hierarchies (`tags list --parent <tag>` lists a label's children).
100
+ - **Kind fields (the columns)**: each kind has one field list per workspace (its issue fields, its person fields …; types `text`, `number`, `date`, `checkbox`, `select`, `person`); each tag holds its own values. `arbi tag-kinds [list] [--json]` shows every kind's fields, `arbi tag-kinds show <kind>` one kind's (people and organisations start with built-in Position, Organisation, Role, Comment / Role, Comment). `arbi tag-kinds set-fields <kind> '<json>'` (or a `.json`/`.yaml` file of `[{name, type, options}]`) sets them in full: a field named again (any case) keeps its id and values, a type cannot change, ones left out are removed unless `--keep-others`.
101
+ - **Values and the table**: `arbi tags fields <tag>` shows a tag's values for its kind's fields; `--set name=value` (repeatable, `name=` clears) sets them, checked against the kind's fields and a select's options (`tags create … --set` too). `arbi tags list --kind issue --links` prints the table: a row per tag, the kind's fields as columns, then DOCS, FACTS and THREADS counts, gaps marked `β€”` and listed; with `--json` each tag carries `field_values` and `links` (its newest documents with notes, facts and threads, up to `--links <n>`, default 50, plus the counts). Tags are taken by id or unique name.
100
102
  - **Doctags** (tag↔doc link): `arbi doctags create <tag-id> <doc-id...>` / `arbi doctags delete <tag-id> <doc-id...>`.
101
103
  - **Retrieve only**: `arbi find <query> --json` returns raw retrieval results without running the LLM.
102
104
  - **Exit codes**: 0 on success (including clean skips), 1 on error. Errors print to stderr.
package/dist/index.js CHANGED
@@ -3695,7 +3695,7 @@ function getLatestVersion(skipCache = false) {
3695
3695
  }
3696
3696
  }
3697
3697
  function getCurrentVersion() {
3698
- return "0.4.84";
3698
+ return "0.4.85";
3699
3699
  }
3700
3700
  function readChangelog(fromVersion, toVersion) {
3701
3701
  try {
@@ -3748,17 +3748,17 @@ function showChangelog(fromVersion, toVersion) {
3748
3748
  async function checkForUpdates(autoUpdate) {
3749
3749
  try {
3750
3750
  const latest = getLatestVersion();
3751
- if (!latest || latest === "0.4.84") return;
3751
+ if (!latest || latest === "0.4.85") return;
3752
3752
  if (autoUpdate) {
3753
3753
  warn(`
3754
- Your arbi version is out of date (${"0.4.84"} \u2192 ${latest}). Updating...`);
3754
+ Your arbi version is out of date (${"0.4.85"} \u2192 ${latest}). Updating...`);
3755
3755
  child_process.execSync("npm install -g @arbidocs/cli@latest", { stdio: "inherit" });
3756
- showChangelog("0.4.84", latest);
3756
+ showChangelog("0.4.85", latest);
3757
3757
  console.log(`Updated to ${latest}.`);
3758
3758
  } else {
3759
3759
  warn(
3760
3760
  `
3761
- Your arbi version is out of date (${"0.4.84"} \u2192 ${latest}).
3761
+ Your arbi version is out of date (${"0.4.85"} \u2192 ${latest}).
3762
3762
  Run "arbi update" to upgrade, or "arbi update auto" to always stay up to date.`
3763
3763
  );
3764
3764
  }
@@ -3790,10 +3790,10 @@ function markNagShown(latest) {
3790
3790
  function hintUpdateOnError() {
3791
3791
  try {
3792
3792
  const cached = readCache();
3793
- if (!cached || cached.latest === "0.4.84") return;
3793
+ if (!cached || cached.latest === "0.4.85") return;
3794
3794
  if (!shouldShowNag(cached.latest)) return;
3795
3795
  warn(
3796
- `Your arbi version is out of date (${"0.4.84"} \u2192 ${cached.latest}). Run "arbi update".`
3796
+ `Your arbi version is out of date (${"0.4.85"} \u2192 ${cached.latest}). Run "arbi update".`
3797
3797
  );
3798
3798
  markNagShown(cached.latest);
3799
3799
  } catch {
@@ -8519,16 +8519,16 @@ function parseFieldDefinitions(arg) {
8519
8519
  try {
8520
8520
  value = JSON.parse(trimmed);
8521
8521
  } catch (err) {
8522
- throw new Error(`--define is not valid JSON: ${err.message}`);
8522
+ throw new Error(`Fields are not valid JSON: ${err.message}`);
8523
8523
  }
8524
8524
  } else if (fs7.existsSync(arg)) value = readStructured(arg);
8525
8525
  else
8526
8526
  throw new Error(
8527
- `--define takes JSON (e.g. '[{"name":"budget","type":"number"}]') or a .json/.yaml file; no file "${arg}"`
8527
+ `Fields are JSON (e.g. '[{"name":"Status","type":"text"}]') or a .json/.yaml file; no file "${arg}"`
8528
8528
  );
8529
8529
  const list = isRecord(value) ? value.fields : value;
8530
8530
  if (!Array.isArray(list))
8531
- throw new Error('--define: expected a list of fields, or an object with a "fields" list');
8531
+ throw new Error('Fields: expected a list of fields, or an object with a "fields" list');
8532
8532
  const problems = [];
8533
8533
  const names = /* @__PURE__ */ new Set();
8534
8534
  const out = list.map((raw, i) => {
@@ -8580,7 +8580,7 @@ function parseFieldValues(pairs, fields) {
8580
8580
  const field = fields.find((f) => f.name.toLowerCase() === name.toLowerCase() || f.id === name);
8581
8581
  if (!field)
8582
8582
  throw new Error(
8583
- `No field "${name}" on this tag. Its fields: ${fields.map((f) => f.name).join(", ") || "(none: add some with --define)"}`
8583
+ `No field "${name}" for this kind. Its fields: ${fields.map((f) => f.name).join(", ") || "(none: add some with arbi tag-kinds set-fields)"}`
8584
8584
  );
8585
8585
  if (value.trim() === "") out[field.name] = null;
8586
8586
  else if (field.type === "select" && field.options?.length) {
@@ -8598,35 +8598,36 @@ function formatFieldValue(value) {
8598
8598
  if (typeof value === "object") return JSON.stringify(value);
8599
8599
  return String(value);
8600
8600
  }
8601
- function fieldRows(tag) {
8602
- return (tag.fields ?? []).map((f) => ({
8601
+ var fieldType = (f) => f.type === "select" && f.options?.length ? `select: ${f.options.join("|")}` : f.type;
8602
+ function fieldRows(tag, fields) {
8603
+ return fields.map((f) => ({
8603
8604
  id: f.id,
8604
8605
  name: f.name,
8605
- type: f.type === "select" && f.options?.length ? `select: ${f.options.join("|")}` : f.type,
8606
- value: formatFieldValue(tag.field_values?.[f.id]),
8607
- from: f.tag_ext_id === tag.external_id ? "own" : `inherited (${f.tag_ext_id})`
8606
+ type: fieldType(f),
8607
+ value: formatFieldValue(tag.field_values?.[f.id])
8608
8608
  }));
8609
8609
  }
8610
- function matrixTable(matrix) {
8611
- const columns = sdk.tags.tagMatrixColumns(matrix);
8612
- const headers = ["TAG", ...columns.map((c) => c.name.toUpperCase()), "DOCS", "FACTS"];
8610
+ function tagTable(tags, columns) {
8611
+ const headers = ["TAG", ...columns.map((c) => c.name.toUpperCase()), "DOCS", "FACTS", "THREADS"];
8613
8612
  const rows = [];
8614
8613
  const gaps = [];
8615
- for (const row of matrix.rows ?? []) {
8616
- const has = new Set((row.tag.fields ?? []).map((f) => f.id));
8617
- const cells = columns.map(
8618
- (c) => has.has(c.id) ? formatFieldValue(row.tag.field_values?.[c.id]) : "n/a"
8619
- );
8614
+ for (const tag of tags) {
8615
+ const links = tag.links;
8616
+ const docs = links?.doc_count ?? 0;
8617
+ const facts = links?.fact_count ?? 0;
8618
+ const threads = links?.thread_count ?? 0;
8619
+ const cells = columns.map((c) => formatFieldValue(tag.field_values?.[c.id]));
8620
8620
  rows.push([
8621
- row.tag.name,
8621
+ tag.name,
8622
8622
  ...cells,
8623
- row.doc_count ? String(row.doc_count) : GAP,
8624
- row.fact_count ? String(row.fact_count) : GAP
8623
+ docs ? String(docs) : GAP,
8624
+ facts ? String(facts) : GAP,
8625
+ threads ? String(threads) : GAP
8625
8626
  ]);
8626
- const missing = columns.filter((c, i) => has.has(c.id) && cells[i] === GAP).map((c) => c.name);
8627
- if (!row.doc_count) missing.push("documents");
8628
- if (!row.fact_count) missing.push("facts");
8629
- if (missing.length) gaps.push(`${row.tag.name}: no ${missing.join(", no ")}`);
8627
+ const missing = columns.filter((_, i) => cells[i] === GAP).map((c) => c.name);
8628
+ if (!docs) missing.push("documents");
8629
+ if (!facts) missing.push("facts");
8630
+ if (missing.length) gaps.push(`${tag.name}: no ${missing.join(", no ")}`);
8630
8631
  }
8631
8632
  return { headers, rows, gaps };
8632
8633
  }
@@ -8639,7 +8640,39 @@ function renderTable(headers, rows, max = 28) {
8639
8640
  return [line(headers), ...rows.map(line)];
8640
8641
  }
8641
8642
 
8643
+ // src/tag-kinds.ts
8644
+ var KINDS = {
8645
+ label: true,
8646
+ annotation: true,
8647
+ search: true,
8648
+ issue: true,
8649
+ person: true,
8650
+ organisation: true
8651
+ };
8652
+ var TAG_KINDS = Object.keys(KINDS);
8653
+ function parseTagKind(value, flag = "--kind") {
8654
+ const kind = value.trim().toLowerCase();
8655
+ if (!TAG_KINDS.includes(kind))
8656
+ throw new Error(`${flag} must be one of ${TAG_KINDS.join(", ")} (got "${value}")`);
8657
+ return kind;
8658
+ }
8659
+ function tagDetails(tag, parentName, fields = []) {
8660
+ const type = tag.tag_type?.type ?? "";
8661
+ const rows = [
8662
+ ["Kind:", tag.kind],
8663
+ ["Type:", type],
8664
+ ["Shared:", tag.shared ? "yes" : "no"],
8665
+ ["Documents:", String(tag.doctag_count ?? 0)]
8666
+ ];
8667
+ if (tag.parent_ext_id)
8668
+ rows.push(["Parent:", parentName ? `${parentName} (${tag.parent_ext_id})` : tag.parent_ext_id]);
8669
+ if (tag.instruction) rows.push(["Instruction:", tag.instruction]);
8670
+ for (const f of fields) rows.push([`${f.name}:`, formatFieldValue(tag.field_values?.[f.id])]);
8671
+ return rows;
8672
+ }
8673
+
8642
8674
  // src/commands/tags.ts
8675
+ var KIND_LIST = TAG_KINDS.join(", ");
8643
8676
  async function resolveTag(arbi, selector) {
8644
8677
  const res = resolveByIdOrName(await sdk.tags.listTags(arbi), selector);
8645
8678
  if (!res.ok) failResolveAndExit("Tag", selector, res);
@@ -8661,23 +8694,52 @@ async function fetchTagChoices(arbi) {
8661
8694
  };
8662
8695
  }
8663
8696
  function registerTagsCommand(program2) {
8664
- const tags = program2.command("tags").description("Tags: list, create, delete, update, share, unshare, rename, fields, matrix");
8665
- tags.command("list").description("List tags in the active workspace").option("-w, --workspace <id>", "Workspace ID (defaults to selected workspace)").option("--json", "Output as JSON").action(
8697
+ const tags = program2.command("tags").description("Tags: list, show, create, delete, update, share, unshare, rename, fields");
8698
+ tags.command("list").description(
8699
+ "List tags in the active workspace; with --links, as a table: the kind\u2019s fields as columns, then document, fact and thread counts, gaps marked \u2014"
8700
+ ).option("-w, --workspace <id>", "Workspace ID (defaults to selected workspace)").option("-k, --kind <kind>", `Only tags of this kind (${KIND_LIST})`).option("--parent <tag>", "Only this tag\u2019s children (id or name; a label hierarchy)").option(
8701
+ "--links [n]",
8702
+ "The table: each tag\u2019s values for its kind\u2019s fields and its documents, facts and threads (with --json: the newest n of each, default 50)"
8703
+ ).option("--json", "Output as JSON").action(
8666
8704
  (opts) => runAction(async () => {
8705
+ const kind = opts.kind === void 0 ? void 0 : parseTagKind(opts.kind);
8706
+ const linksLimit = typeof opts.links === "string" ? Number(opts.links) : void 0;
8707
+ if (linksLimit !== void 0 && (!Number.isInteger(linksLimit) || linksLimit < 0))
8708
+ throw new Error("--links takes a whole number");
8667
8709
  const { arbi } = await resolveWorkspace(opts.workspace);
8668
- const data = await sdk.tags.listTags(arbi);
8710
+ const parent = opts.parent ? await resolveTag(arbi, opts.parent) : void 0;
8711
+ const data = await sdk.tags.listTags(arbi, {
8712
+ ...kind ? { kind } : {},
8713
+ ...parent ? { parentId: parent.external_id } : {},
8714
+ ...opts.links ? { include: ["links"], linksLimit } : {}
8715
+ });
8669
8716
  if (opts.json) {
8670
8717
  printJson(data);
8671
8718
  return;
8672
8719
  }
8673
8720
  if (data.length === 0) {
8674
- process.stderr.write("No tags found.\n");
8721
+ process.stderr.write(
8722
+ kind ? `No ${kind} tags. Add one: arbi tags create <name> --kind ${kind}
8723
+ ` : "No tags found.\n"
8724
+ );
8725
+ return;
8726
+ }
8727
+ if (opts.links) {
8728
+ const columns = kind ? (await sdk.tags.getTagKind(arbi, kind)).fields : [];
8729
+ const table = tagTable(data, columns);
8730
+ for (const line of renderTable(table.headers, table.rows)) console.log(line);
8731
+ if (table.gaps.length) {
8732
+ console.log("");
8733
+ dim("Gaps:");
8734
+ for (const g of table.gaps) dim(` ${g}`);
8735
+ }
8675
8736
  return;
8676
8737
  }
8677
8738
  printTable(
8678
8739
  [
8679
8740
  { header: "ID", width: 24, value: (r) => r.external_id },
8680
8741
  { header: "NAME", width: 24, value: (r) => r.name },
8742
+ { header: "KIND", width: 13, value: (r) => r.kind ?? "" },
8681
8743
  {
8682
8744
  header: "TYPE",
8683
8745
  width: 12,
@@ -8691,12 +8753,43 @@ function registerTagsCommand(program2) {
8691
8753
  );
8692
8754
  })()
8693
8755
  );
8756
+ tags.command("show <tag>").description(
8757
+ "A tag's details: kind, type, sharing, documents, parent, instruction and its values for its kind's fields"
8758
+ ).option("-w, --workspace <id>", "Workspace ID (defaults to selected workspace)").option("--json", "Output as JSON").action(
8759
+ (selector, opts) => runAction(async () => {
8760
+ const { arbi } = await resolveWorkspace(opts.workspace);
8761
+ const all = await sdk.tags.listTags(arbi);
8762
+ const res = resolveByIdOrName(all, selector);
8763
+ if (!res.ok) failResolveAndExit("Tag", selector, res);
8764
+ const tag = res.item;
8765
+ if (opts.json) {
8766
+ printJson(tag);
8767
+ return;
8768
+ }
8769
+ const parent = all.find((t) => t.external_id === tag.parent_ext_id);
8770
+ const { fields } = await sdk.tags.getTagKind(arbi, tag.kind);
8771
+ bold(`${tag.name} (${tag.external_id})`);
8772
+ for (const [key, value] of tagDetails(tag, parent?.name, fields)) label(key, value);
8773
+ })()
8774
+ );
8694
8775
  tags.command("create [name]").description("Create a new tag (interactive form if no name given)").option("-w, --workspace <id>", "Workspace ID (defaults to selected workspace)").option(
8695
8776
  "-t, --type <type>",
8696
8777
  "Tag type (checkbox, text, number, select, folder, search, date, assistant)",
8697
8778
  "checkbox"
8698
- ).option("-i, --instruction <text>", "Tag instruction").option("--shared", "Make tag shared", false).option("--parent <tag>", "Make it a child of this tag (id or name): it inherits its fields").option("--json", "Output as JSON").action(
8779
+ ).option("-i, --instruction <text>", "Tag instruction").option("--shared", "Make tag shared", false).option(
8780
+ "--parent <tag>",
8781
+ "Make it a child of this tag (id or name; labels only: issues, people and organisations are flat)"
8782
+ ).option(
8783
+ "-k, --kind <kind>",
8784
+ `What it is (${KIND_LIST}). Default: from the type (checkbox: label)`
8785
+ ).option(
8786
+ "--set <name=value>",
8787
+ "A value for one of the kind's fields (repeatable), e.g. --set Status=Open",
8788
+ collect,
8789
+ []
8790
+ ).option("--json", "Output as JSON").action(
8699
8791
  (name, opts) => runAction(async () => {
8792
+ const kind = opts.kind === void 0 ? void 0 : parseTagKind(opts.kind);
8700
8793
  const { arbi } = await resolveWorkspace(opts.workspace);
8701
8794
  const parent = opts.parent ? await resolveTag(arbi, opts.parent) : void 0;
8702
8795
  const interactive = !name;
@@ -8715,6 +8808,13 @@ function registerTagsCommand(program2) {
8715
8808
  ]);
8716
8809
  const instruction = interactive ? opts.instruction ?? (await promptInput("Instruction (optional)", false) || null) : opts.instruction ?? null;
8717
8810
  const shared = interactive ? opts.shared || await promptConfirm("Shared?", false) : opts.shared ?? false;
8811
+ const fieldValues = opts.set.length ? parseFieldValues(
8812
+ opts.set,
8813
+ (await sdk.tags.getTagKind(
8814
+ arbi,
8815
+ kind ?? (tagType === "checkbox" ? "label" : "annotation")
8816
+ )).fields
8817
+ ) : null;
8718
8818
  const data = await sdk.tags.createTag(arbi, {
8719
8819
  name,
8720
8820
  tagType: {
@@ -8723,14 +8823,16 @@ function registerTagsCommand(program2) {
8723
8823
  },
8724
8824
  instruction,
8725
8825
  shared,
8726
- parentId: parent?.external_id ?? null
8826
+ parentId: parent?.external_id ?? null,
8827
+ kind: kind ?? null,
8828
+ fieldValues
8727
8829
  });
8728
8830
  if (opts.json) {
8729
8831
  printJson(data);
8730
8832
  return;
8731
8833
  }
8732
8834
  success(
8733
- `Created: ${data.name} (${ref(data.external_id)})${parent ? ` under ${parent.name}` : ""}`
8835
+ `Created: ${data.name} (${ref(data.external_id)}) [${data.kind}]${parent ? ` under ${parent.name}` : ""}`
8734
8836
  );
8735
8837
  })()
8736
8838
  );
@@ -8797,41 +8899,24 @@ function registerTagsCommand(program2) {
8797
8899
  })()
8798
8900
  );
8799
8901
  tags.command("fields <tag>").description(
8800
- "Show a tag's fields and values; --define its own fields (a child inherits its parent's), --set values"
8801
- ).option(
8802
- "--define <json|file>",
8803
- `Its own field definitions: JSON or a .json/.yaml file, e.g. '[{"name":"budget","type":"number"},{"name":"risk","type":"select","options":["low","high"]}]'. A field defined again by name keeps its values; ones left out are dropped unless --keep-others`
8804
- ).option("--keep-others", "With --define: keep its other own fields").option("--set <name=value>", "Set a field value (repeatable; name= clears it)", collect, []).option("--json", "Output as JSON").action(
8902
+ "Show a tag's values for its kind's fields; --set them. The fields themselves: arbi tag-kinds"
8903
+ ).option("--set <name=value>", "Set a field value (repeatable; name= clears it)", collect, []).option("--json", "Output as JSON").action(
8805
8904
  (selector, opts) => runAction(async () => {
8806
- const defs = opts.define ? parseFieldDefinitions(opts.define) : void 0;
8807
8905
  const { arbi } = await resolveWorkspace();
8808
8906
  let tag = await resolveTag(arbi, selector);
8809
- if (defs || opts.set.length) {
8810
- const fields = defs ? sdk.tags.mergeTagFieldDefinitions(tag, defs, { keepOthers: !!opts.keepOthers }) : void 0;
8811
- const known = fields ? [
8812
- ...(tag.fields ?? []).filter((f) => f.tag_ext_id !== tag.external_id),
8813
- ...fields.map((f) => ({
8814
- id: f.id ?? "",
8815
- name: f.name,
8816
- type: f.type,
8817
- options: f.options ?? null,
8818
- tag_ext_id: tag.external_id
8819
- }))
8820
- ] : tag.fields ?? [];
8821
- const values = opts.set.length ? parseFieldValues(opts.set, known) : void 0;
8822
- tag = await sdk.tags.updateTagFields(arbi, tag.external_id, { fields, values });
8823
- }
8907
+ const { fields } = await sdk.tags.getTagKind(arbi, tag.kind);
8908
+ if (opts.set.length)
8909
+ tag = await sdk.tags.setTagValues(arbi, tag.external_id, parseFieldValues(opts.set, fields));
8824
8910
  if (opts.json) {
8825
8911
  printJson(tag);
8826
8912
  return;
8827
8913
  }
8828
- if (defs || opts.set.length)
8829
- success(`Updated the fields of ${tag.name} (${ref(tag.external_id)})`);
8914
+ if (opts.set.length) success(`Updated the values of ${tag.name} (${ref(tag.external_id)})`);
8830
8915
  else bold(`${tag.name} (${tag.external_id})`);
8831
- const rows = fieldRows(tag);
8916
+ const rows = fieldRows(tag, fields);
8832
8917
  if (!rows.length) {
8833
8918
  dim(
8834
- `No fields. Define some: arbi tags fields ${tag.external_id} --define '[{"name":"budget","type":"number"}]'`
8919
+ `No ${tag.kind} fields. Add some: arbi tag-kinds set-fields ${tag.kind} '[{"name":"Status","type":"text"}]'`
8835
8920
  );
8836
8921
  return;
8837
8922
  }
@@ -8839,55 +8924,87 @@ function registerTagsCommand(program2) {
8839
8924
  [
8840
8925
  { header: "FIELD", width: 22, value: (r) => r.name },
8841
8926
  { header: "TYPE", width: 24, value: (r) => r.type },
8842
- { header: "VALUE", width: 28, value: (r) => r.value },
8843
- { header: "DEFINED", width: 30, value: (r) => r.from }
8927
+ { header: "VALUE", width: 40, value: (r) => r.value }
8844
8928
  ],
8845
8929
  rows
8846
8930
  );
8847
8931
  })()
8848
8932
  );
8849
- tags.command("matrix").description(
8850
- "Tags as a table: rows are a parent's children (--parent) or the given tags (--tag), columns their fields, then document and fact counts; gaps marked \u2014"
8851
- ).option("--parent <tag>", "Rows are this tag\u2019s children (id or name)").option("--tag <tag>", "A row (id or name; repeatable, in this order)", collect, []).option("--links <n>", "Most documents and facts listed per row (JSON)").option("--json", "Output as JSON").action(
8933
+ tags.allowUnknownOption(true).allowExcessArguments(true).action(async () => {
8934
+ const tail = tags.args ?? [];
8935
+ await tags.commands.find((c) => c.name() === "list").parseAsync(tail, { from: "user" });
8936
+ });
8937
+ }
8938
+ var KIND_LIST2 = TAG_KINDS.join(", ");
8939
+ function printSchema(schema) {
8940
+ bold(`${schema.kind}${schema.is_default ? " (built-in fields)" : ""}`);
8941
+ if (!schema.fields.length) {
8942
+ dim(
8943
+ ` No fields. Add some: arbi tag-kinds set-fields ${schema.kind} '[{"name":"Status","type":"select","options":["Open","Closed"]}]'`
8944
+ );
8945
+ return;
8946
+ }
8947
+ printTable(
8948
+ [
8949
+ { header: "ID", width: 12, value: (r) => r.id },
8950
+ { header: "FIELD", width: 24, value: (r) => r.name },
8951
+ { header: "TYPE", width: 32, value: (r) => r.type }
8952
+ ],
8953
+ schema.fields.map((f) => ({ id: f.id, name: f.name, type: fieldType(f) }))
8954
+ );
8955
+ }
8956
+ function registerTagKindsCommand(program2) {
8957
+ const kinds = program2.command("tag-kinds").description(
8958
+ "Tag kinds' fields in the workspace (the columns of a table of a kind's tags): list, show, set-fields"
8959
+ );
8960
+ kinds.command("list").description("Every kind's fields in the active workspace").option("--json", "Output as JSON").action(
8852
8961
  (opts) => runAction(async () => {
8853
- if (!opts.parent === !opts.tag.length)
8854
- throw new Error("Pass --parent <tag>, or one or more --tag <tag>");
8855
- const links = opts.links === void 0 ? void 0 : Number(opts.links);
8856
- if (links !== void 0 && (!Number.isInteger(links) || links < 0))
8857
- throw new Error("--links must be a whole number");
8858
8962
  const { arbi } = await resolveWorkspace();
8859
- const all = await sdk.tags.listTags(arbi);
8860
- const pick = (selector) => {
8861
- const res = resolveByIdOrName(all, selector);
8862
- if (!res.ok) failResolveAndExit("Tag", selector, res);
8863
- return res.item;
8864
- };
8865
- const selection = opts.parent ? { parentId: pick(opts.parent).external_id } : { tagIds: opts.tag.map((t) => pick(t).external_id) };
8866
- const matrix = await sdk.tags.getTagMatrix(arbi, selection, links);
8963
+ const all = await sdk.tags.listTagKinds(arbi);
8867
8964
  if (opts.json) {
8868
- printJson(matrix);
8869
- return;
8870
- }
8871
- const table = matrixTable(matrix);
8872
- if (matrix.parent) bold(`${matrix.parent.name} (${matrix.parent.external_id})`);
8873
- if (!table.rows.length) {
8874
- process.stderr.write(
8875
- opts.parent ? `No child tags. Add one: arbi tags create <name> --parent ${opts.parent}
8876
- ` : "No tags.\n"
8877
- );
8965
+ printJson(all);
8878
8966
  return;
8879
8967
  }
8880
- for (const line of renderTable(table.headers, table.rows)) console.log(line);
8881
- if (table.gaps.length) {
8968
+ for (const schema of all) {
8969
+ printSchema(schema);
8882
8970
  console.log("");
8883
- dim("Gaps:");
8884
- for (const g of table.gaps) dim(` ${g}`);
8885
8971
  }
8886
8972
  })()
8887
8973
  );
8888
- tags.allowUnknownOption(true).allowExcessArguments(true).action(async () => {
8889
- const tail = tags.args ?? [];
8890
- await tags.commands.find((c) => c.name() === "list").parseAsync(tail, { from: "user" });
8974
+ kinds.command("show <kind>").description(`A kind's fields (${KIND_LIST2})`).option("--json", "Output as JSON").action(
8975
+ (kindArg, opts) => runAction(async () => {
8976
+ const kind = parseTagKind(kindArg, "kind");
8977
+ const { arbi } = await resolveWorkspace();
8978
+ const schema = await sdk.tags.getTagKind(arbi, kind);
8979
+ if (opts.json) printJson(schema);
8980
+ else printSchema(schema);
8981
+ })()
8982
+ );
8983
+ kinds.command("set-fields <kind> <fields>").description(
8984
+ `Set a kind's fields: JSON or a .json/.yaml file of [{name, type, options}], e.g. '[{"name":"Status","type":"select","options":["Open","Closed"]},{"name":"Owner","type":"person"}]'. A field named again (any case) keeps its id and values; ones left out are removed unless --keep-others`
8985
+ ).option("--keep-others", "Keep the kind\u2019s other fields (after the given ones)").option("--json", "Output as JSON").action(
8986
+ (kindArg, fieldsArg, opts) => runAction(async () => {
8987
+ const kind = parseTagKind(kindArg, "kind");
8988
+ const wanted = parseFieldDefinitions(fieldsArg);
8989
+ const { arbi } = await resolveWorkspace();
8990
+ const current = await sdk.tags.getTagKind(arbi, kind);
8991
+ const schema = await sdk.tags.updateTagKind(
8992
+ arbi,
8993
+ kind,
8994
+ sdk.tags.mergeKindFieldDefinitions(current.fields, wanted, {
8995
+ keepOthers: !!opts.keepOthers
8996
+ })
8997
+ );
8998
+ if (opts.json) {
8999
+ printJson(schema);
9000
+ return;
9001
+ }
9002
+ success(`Set the ${kind} fields`);
9003
+ printSchema(schema);
9004
+ })()
9005
+ );
9006
+ kinds.action(async () => {
9007
+ await kinds.commands.find((c) => c.name() === "list").parseAsync([], { from: "user" });
8891
9008
  });
8892
9009
  }
8893
9010
  init_prompts();
@@ -11955,7 +12072,7 @@ console.info = (...args) => {
11955
12072
  _origInfo(...args);
11956
12073
  };
11957
12074
  var program = new commander.Command();
11958
- program.name("arbi").description("ARBI CLI \u2014 interact with ARBI from the terminal").version("0.4.84").showHelpAfterError(true).showSuggestionAfterError(true);
12075
+ program.name("arbi").description("ARBI CLI \u2014 interact with ARBI from the terminal").version("0.4.85").showHelpAfterError(true).showSuggestionAfterError(true);
11959
12076
  registerConfigCommand(program);
11960
12077
  registerLoginCommand(program);
11961
12078
  registerRegisterCommand(program);
@@ -11972,6 +12089,7 @@ registerWatchCommand(program);
11972
12089
  registerContactsCommand(program);
11973
12090
  registerDmCommand(program);
11974
12091
  registerTagsCommand(program);
12092
+ registerTagKindsCommand(program);
11975
12093
  registerTimelineCommand(program);
11976
12094
  registerDoctagsCommand(program);
11977
12095
  registerConversationsCommand(program);