tines 0.0.123 → 0.0.124

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/index.js +57 -14
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -3838,7 +3838,11 @@ function createApiClient(options) {
3838
3838
  listLabels: () => get("/api/v1/labels"),
3839
3839
  createLabel: (body) => request("POST", "/api/v1/labels", body),
3840
3840
  updateLabel: (labelRef, body) => request("PATCH", `/api/v1/labels/${encodeURIComponent(labelRef)}`, body),
3841
- deleteLabel: (labelRef) => request("DELETE", `/api/v1/labels/${encodeURIComponent(labelRef)}`),
3841
+ deleteLabel: (labelRef, body = {}) => request(
3842
+ "DELETE",
3843
+ `/api/v1/labels/${encodeURIComponent(labelRef)}`,
3844
+ body
3845
+ ),
3842
3846
  addIssueLabels: (issueId, labels) => request("POST", `/api/v1/issues/${issueId}/labels`, { labels }),
3843
3847
  removeIssueLabel: (issueId, labelRef) => request("DELETE", `/api/v1/issues/${issueId}/labels/${encodeURIComponent(labelRef)}`),
3844
3848
  listProjectIssues: (projectId, filters = {}) => get(`/api/v1/projects/${projectId}/issues${query(filters)}`),
@@ -4467,6 +4471,13 @@ async function resolveIssue(api, ref) {
4467
4471
  const proj = await resolveProject(api, project);
4468
4472
  return api.getIssueByNumber(proj.id, number);
4469
4473
  }
4474
+ async function resolveLabelFlag(api, ref) {
4475
+ const match = (await api.listLabels()).items.find(
4476
+ (l) => l.id === ref || l.name.toLowerCase() === ref.toLowerCase()
4477
+ );
4478
+ if (!match) die(`no such label: ${ref}`);
4479
+ return match.id;
4480
+ }
4470
4481
  async function resolveStateFlag(api, ref) {
4471
4482
  const sep2 = ref.indexOf("/");
4472
4483
  if (sep2 < 1 || sep2 === ref.length - 1) {
@@ -4499,6 +4510,7 @@ async function resolveScopeFlags(api, opts) {
4499
4510
  if (opts.state !== void 0)
4500
4511
  scope.workflow_state_id = (await resolveStateFlag(api, opts.state)).state.id;
4501
4512
  if (opts.issue !== void 0) scope.issue_id = (await resolveIssue(api, opts.issue)).id;
4513
+ if (opts.label !== void 0) scope.label_id = await resolveLabelFlag(api, opts.label);
4502
4514
  return scope;
4503
4515
  }
4504
4516
  function printContextItem(item) {
@@ -4537,9 +4549,10 @@ Scope flags (combinable \u2014 an item applies where ALL of its set dimensions m
4537
4549
  --project <name> only for issues in this project
4538
4550
  --state <workflow>/<state> only for issues currently in this state
4539
4551
  --issue <project>/<number> only for this issue
4552
+ --label <name> only for issues carrying this label
4540
4553
  `;
4541
4554
  function withScopeFlags(cmd) {
4542
- return cmd.option("-p, --project <name>", "scope: project name or id").option("-s, --state <workflow/state>", "scope: workflow-qualified state").option("-i, --issue <ref>", "scope: issue (<project>/<number>)").addHelpText("after", SCOPE_FLAGS_HELP);
4555
+ return cmd.option("-p, --project <name>", "scope: project name or id").option("-s, --state <workflow/state>", "scope: workflow-qualified state").option("-i, --issue <ref>", "scope: issue (<project>/<number>)").option("-l, --label <name>", "scope: issue label name or id").addHelpText("after", SCOPE_FLAGS_HELP);
4543
4556
  }
4544
4557
  function register(program3) {
4545
4558
  const context = program3.command("context").description(
@@ -4562,6 +4575,7 @@ function register(program3) {
4562
4575
  project: scope.project_id ?? void 0,
4563
4576
  state: scope.workflow_state_id ?? void 0,
4564
4577
  issue: scope.issue_id ?? void 0,
4578
+ label: scope.label_id ?? void 0,
4565
4579
  q: opts.search,
4566
4580
  exact: opts.exact ? true : void 0,
4567
4581
  ...page
@@ -4630,7 +4644,7 @@ function register(program3) {
4630
4644
  withScopeFlags(
4631
4645
  context.command("edit <id>").description("Edit a context item: payload, name, description, or scope").option("-n, --name <name>", "rename the item").option("-d, --description <text>", "set the description").option("--body <md>", "prompt body: inline Markdown or @file (escape a literal @ as @@)").option("--file <path>=@<local>", "add or replace a skill file (repeatable)", collect, []).option("--remove-file <path>", "remove a skill file (repeatable)", collect, []).option("--repo-url <url>", "repo: clone URL").option("--branch <branch>", "repo: branch (empty string clears it)").option("--dir <dir>", "repo: checkout directory (empty string restores the URL default)").option(
4632
4646
  "--unset <dimension>",
4633
- "drop a scope dimension: project, state, or issue (repeatable)",
4647
+ "drop a scope dimension: project, state, issue, or label (repeatable)",
4634
4648
  collect,
4635
4649
  []
4636
4650
  ).option(
@@ -4652,7 +4666,8 @@ function register(program3) {
4652
4666
  if (dim === "project") body.project_id = null;
4653
4667
  else if (dim === "state") body.workflow_state_id = null;
4654
4668
  else if (dim === "issue") body.issue_id = null;
4655
- else die(`--unset takes project, state, or issue, got "${dim}"`);
4669
+ else if (dim === "label") body.label_id = null;
4670
+ else die(`--unset takes project, state, issue, or label, got "${dim}"`);
4656
4671
  }
4657
4672
  if (opts.body !== void 0) body.body = readBodyValue(opts.body);
4658
4673
  if (opts.file.length > 0 || opts.removeFile.length > 0) {
@@ -5574,6 +5589,9 @@ function stateScope(issue, state, workflow) {
5574
5589
  workflow_name: workflow.name,
5575
5590
  issue_id: null,
5576
5591
  issue_ref: null,
5592
+ label_id: null,
5593
+ label_name: null,
5594
+ label_color: null,
5577
5595
  label: `project ${issue.project_name} \xB7 state ${state.name}`
5578
5596
  };
5579
5597
  }
@@ -5692,8 +5710,15 @@ function register4(program3) {
5692
5710
  if (opts.json) return printJson(res);
5693
5711
  if (res.items.length === 0) return console.log("no labels");
5694
5712
  table([
5695
- ["NAME", "COLOR", "ISSUES", "DESCRIPTION"],
5696
- ...res.items.map((l) => [l.name, l.color, String(l.issue_count), l.description])
5713
+ ["NAME", "COLOR", "ISSUES", "ITEMS", "RULES", "DESCRIPTION"],
5714
+ ...res.items.map((l) => [
5715
+ l.name,
5716
+ l.color,
5717
+ String(l.issue_count),
5718
+ String(l.context_item_count),
5719
+ String(l.routing_rule_count),
5720
+ l.description
5721
+ ])
5697
5722
  ]);
5698
5723
  });
5699
5724
  withCommon(
@@ -5724,7 +5749,7 @@ function register4(program3) {
5724
5749
  }
5725
5750
  );
5726
5751
  withCommon(
5727
- labels.command("delete <name>").description("Delete a label and detach it from every issue").option("-y, --yes", "skip the confirmation")
5752
+ labels.command("delete <name>").description("Delete a label and detach it from every issue").option("-y, --yes", "skip the confirmation").option("-f, --force", "also delete the context items and routing rules scoped to this label")
5728
5753
  ).action(async (name2, opts) => {
5729
5754
  const api = client(opts);
5730
5755
  if (!opts.yes) {
@@ -5732,18 +5757,33 @@ function register4(program3) {
5732
5757
  (l) => l.name.toLowerCase() === name2.toLowerCase() || l.id === name2
5733
5758
  );
5734
5759
  if (!existing) die(`no such label: ${name2}`);
5760
+ const scoped = [
5761
+ `${existing.issue_count} issue${existing.issue_count === 1 ? "" : "s"}`,
5762
+ ...existing.context_item_count > 0 ? [
5763
+ `${existing.context_item_count} context item${existing.context_item_count === 1 ? "" : "s"}`
5764
+ ] : [],
5765
+ ...existing.routing_rule_count > 0 ? [
5766
+ `${existing.routing_rule_count} routing rule${existing.routing_rule_count === 1 ? "" : "s"}`
5767
+ ] : []
5768
+ ].join(", ");
5735
5769
  const rl = createInterface({ input: process.stdin, output: process.stdout });
5736
5770
  const answer = await rl.question(
5737
- `Delete label "${existing.name}"? It is on ${existing.issue_count} issue${existing.issue_count === 1 ? "" : "s"}. [y/N] `
5771
+ `Delete label "${existing.name}"${opts.force ? " and everything scoped to it" : ""}? It is on ${scoped}. [y/N] `
5738
5772
  );
5739
5773
  rl.close();
5740
5774
  if (!/^y(es)?$/i.test(answer.trim())) die("aborted");
5741
5775
  }
5742
- const res = await api.deleteLabel(name2);
5776
+ const res = await api.deleteLabel(name2, opts.force ? { force: true } : {});
5743
5777
  if (opts.json) return printJson(res);
5744
5778
  console.log(
5745
5779
  `deleted label "${name2}" (was on ${res.issue_count} issue${res.issue_count === 1 ? "" : "s"})`
5746
5780
  );
5781
+ for (const item of res.context_items_deleted) {
5782
+ console.log(` deleted ${item.kind} "${item.name}" (${item.scope_label})`);
5783
+ }
5784
+ for (const rule of res.routing_rules_deleted) {
5785
+ console.log(` deleted routing rule (${rule.scope_label})`);
5786
+ }
5747
5787
  });
5748
5788
  }
5749
5789
 
@@ -5938,10 +5978,12 @@ default workflow: ${defaultWorkflow}`);
5938
5978
  async function resolveRoutingScope(api, opts) {
5939
5979
  const projectId = opts.project !== void 0 ? (await resolveProject(api, opts.project)).id : null;
5940
5980
  const stateId = opts.state !== void 0 ? (await resolveStateFlag(api, opts.state)).state.id : null;
5981
+ const labelId = opts.label !== void 0 ? await resolveLabelFlag(api, opts.label) : null;
5941
5982
  const parts = [];
5942
5983
  if (opts.project) parts.push(`project ${opts.project}`);
5943
5984
  if (opts.state) parts.push(`state ${opts.state}`);
5944
- return { projectId, stateId, label: parts.length > 0 ? parts.join(" \xB7 ") : "global" };
5985
+ if (opts.label) parts.push(`label ${opts.label}`);
5986
+ return { projectId, stateId, labelId, label: parts.length > 0 ? parts.join(" \xB7 ") : "global" };
5945
5987
  }
5946
5988
  function register7(program3) {
5947
5989
  const routing = program3.command("routing").description(
@@ -5961,7 +6003,7 @@ function register7(program3) {
5961
6003
  withCommon(
5962
6004
  routing.command("set <target...>").description(
5963
6005
  "Create or replace the rule at a scope: an ordered list of <runner>[:tier] targets"
5964
- ).option("-p, --project <name>", "scope: project name or id").option("-s, --state <workflow/state>", "scope: workflow-qualified state")
6006
+ ).option("-p, --project <name>", "scope: project name or id").option("-s, --state <workflow/state>", "scope: workflow-qualified state").option("-l, --label <name>", "scope: issue label name or id")
5965
6007
  ).action(async (targetSpecs, opts) => {
5966
6008
  const api = client(opts);
5967
6009
  const scope = await resolveRoutingScope(api, opts);
@@ -5973,11 +6015,12 @@ function register7(program3) {
5973
6015
  }
5974
6016
  const { items } = await api.listRoutingRules();
5975
6017
  const existing = items.find(
5976
- (r) => r.scope.project_id === scope.projectId && r.scope.workflow_state_id === scope.stateId
6018
+ (r) => r.scope.project_id === scope.projectId && r.scope.workflow_state_id === scope.stateId && r.scope.label_id === scope.labelId
5977
6019
  );
5978
6020
  const rule = existing ? await api.updateRoutingRule(existing.id, { targets }) : await api.createRoutingRule({
5979
6021
  project_id: scope.projectId,
5980
6022
  workflow_state_id: scope.stateId,
6023
+ label_id: scope.labelId,
5981
6024
  targets
5982
6025
  });
5983
6026
  if (opts.json) return printJson(rule);
@@ -5987,13 +6030,13 @@ function register7(program3) {
5987
6030
  for (const warning of rule.warnings) console.log(`warning: ${warning.message}`);
5988
6031
  });
5989
6032
  withCommon(
5990
- routing.command("clear").description("Delete the rule at a scope (issues it matched stop dispatching)").option("-p, --project <name>", "scope: project name or id").option("-s, --state <workflow/state>", "scope: workflow-qualified state")
6033
+ routing.command("clear").description("Delete the rule at a scope (issues it matched stop dispatching)").option("-p, --project <name>", "scope: project name or id").option("-s, --state <workflow/state>", "scope: workflow-qualified state").option("-l, --label <name>", "scope: issue label name or id")
5991
6034
  ).action(async (opts) => {
5992
6035
  const api = client(opts);
5993
6036
  const scope = await resolveRoutingScope(api, opts);
5994
6037
  const { items } = await api.listRoutingRules();
5995
6038
  const existing = items.find(
5996
- (r) => r.scope.project_id === scope.projectId && r.scope.workflow_state_id === scope.stateId
6039
+ (r) => r.scope.project_id === scope.projectId && r.scope.workflow_state_id === scope.stateId && r.scope.label_id === scope.labelId
5997
6040
  );
5998
6041
  if (!existing) die(`no routing rule at scope ${scope.label}`);
5999
6042
  await api.deleteRoutingRule(existing.id);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tines",
3
- "version": "0.0.123",
3
+ "version": "0.0.124",
4
4
  "description": "CLI for Tines, an orchestration layer for AI agents",
5
5
  "repository": {
6
6
  "type": "git",