tines 0.0.136 → 0.0.138

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 +134 -11
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -3828,6 +3828,7 @@ function createApiClient(options) {
3828
3828
  // Projects
3829
3829
  listProjects: (params = {}) => get(`/api/v1/projects${query(params)}`),
3830
3830
  createProject: (body) => request("POST", "/api/v1/projects", body),
3831
+ listStarters: () => get("/api/v1/projects/starters"),
3831
3832
  getProject: (id) => get(`/api/v1/projects/${id}`),
3832
3833
  updateProject: (id, body) => request("PATCH", `/api/v1/projects/${id}`, body),
3833
3834
  deleteProject: (id, body) => request("DELETE", `/api/v1/projects/${id}`, body),
@@ -4465,7 +4466,9 @@ async function resolveProject(api, ref) {
4465
4466
  die(`no project named "${ref}" (have: ${have || "none"})`);
4466
4467
  }
4467
4468
  async function resolveWorkflow(api, ref) {
4468
- const { items } = await api.listWorkflows();
4469
+ return pickWorkflow((await api.listWorkflows()).items, ref);
4470
+ }
4471
+ function pickWorkflow(items, ref) {
4469
4472
  const found = items.find((w) => w.id === ref) ?? (items.filter((w) => w.name === ref).length === 1 ? items.find((w) => w.name === ref) : void 0);
4470
4473
  if (found) return found;
4471
4474
  if (items.filter((w) => w.name === ref).length > 1) {
@@ -7970,6 +7973,20 @@ existing sets wholesale when present.
7970
7973
  Each NEW state should carry a "prompt" \u2014 its initial stage instructions,
7971
7974
  created as a state-scoped context item \u2014 or pass --no-prompts to skip.
7972
7975
 
7976
+ A state may inherit context from another state \u2014 its base \u2014 with
7977
+ "inherits_from". Give the base as "<workflow>/<state>" (the qualified form
7978
+ \`tines workflows show\` prints, so it can be pasted straight back in), as a
7979
+ bare state name to mean one of this same request's states, or as a state id:
7980
+
7981
+ { "id": "wfs_abc", "name": "Merging", "category": "active",
7982
+ "inherits_from": "Engineering/Review" }
7983
+
7984
+ Items scoped to the base are part of an issue's context in the child state,
7985
+ stitched before the child's own layer. Chains are at most 3 states long and
7986
+ may not cycle. On an EXISTING state (one with an "id") the field is
7987
+ merge-patch: leaving it out keeps the current base, and "inherits_from": null
7988
+ clears it. See \`tines workflows bases\` for the pointers already in place.
7989
+
7973
7990
  A transition may declare artifact requirements ("requires"): it can then only
7974
7991
  be taken once a FRESH artifact with that name \u2014 attached (or reaffirmed)
7975
7992
  since the issue entered its current state \u2014 exists on the issue:
@@ -7983,18 +8000,92 @@ since the issue entered its current state \u2014 exists on the issue:
7983
8000
  "image/"; file/text only) are optional narrowing; "artifact" is the slot
7984
8001
  name issues must carry (see: tines issues artifacts --help).
7985
8002
  `;
7986
- function printWorkflowDetail(wf) {
8003
+ async function loadLibrary(api) {
8004
+ const workflows = await listAll((page) => api.listWorkflows(page));
8005
+ const states = /* @__PURE__ */ new Map();
8006
+ for (const workflow of workflows) {
8007
+ for (const state of workflow.states) states.set(state.id, { workflow, state });
8008
+ }
8009
+ const children = /* @__PURE__ */ new Map();
8010
+ for (const entry of states.values()) {
8011
+ const base = entry.state.inherits_from;
8012
+ if (base === null) continue;
8013
+ const siblings = children.get(base);
8014
+ if (siblings) siblings.push(entry);
8015
+ else children.set(base, [entry]);
8016
+ }
8017
+ return { workflows, states, children };
8018
+ }
8019
+ var qualify = (entry) => `${entry.workflow.name} / ${entry.state.name}`;
8020
+ function stateLabel(lib, id) {
8021
+ const entry = lib.states.get(id);
8022
+ return entry ? qualify(entry) : id;
8023
+ }
8024
+ function inheritanceLines(lib, state) {
8025
+ const lines = [];
8026
+ if (state.inherits_from !== null) {
8027
+ lines.push(` inherits from: ${stateLabel(lib, state.inherits_from)}`);
8028
+ }
8029
+ const kids = lib.children.get(state.id) ?? [];
8030
+ if (kids.length > 0) lines.push(` inherited by: ${kids.map(qualify).join(", ")}`);
8031
+ return lines;
8032
+ }
8033
+ async function resolveStateBases(states, library) {
8034
+ if (!Array.isArray(states)) return;
8035
+ let lib;
8036
+ for (const entry of states) {
8037
+ if (typeof entry !== "object" || entry === null) continue;
8038
+ const state = entry;
8039
+ if (typeof state.inherits_from !== "string" || !state.inherits_from.includes("/")) continue;
8040
+ lib ??= await library();
8041
+ state.inherits_from = resolveBasePair(
8042
+ lib,
8043
+ state.inherits_from,
8044
+ typeof state.name === "string" ? state.name : "(unnamed)"
8045
+ );
8046
+ }
8047
+ }
8048
+ function resolveBasePair(lib, pair, stateName) {
8049
+ const sep2 = pair.indexOf("/");
8050
+ const workflowRef = pair.slice(0, sep2).trim();
8051
+ const stateRef = pair.slice(sep2 + 1).trim();
8052
+ const where = `state "${stateName}" inherits from "${pair}"`;
8053
+ if (!workflowRef || !stateRef) {
8054
+ die(`${where}, which is not a <workflow>/<state> pair`);
8055
+ }
8056
+ const byName = lib.workflows.filter((w) => w.name === workflowRef);
8057
+ if (byName.length > 1)
8058
+ die(`${where}, but workflow name "${workflowRef}" is ambiguous; use an id`);
8059
+ const workflow = lib.workflows.find((w) => w.id === workflowRef) ?? byName[0];
8060
+ if (!workflow) {
8061
+ die(
8062
+ `${where}, but there is no workflow "${workflowRef}" (have: ${lib.workflows.map((w) => w.name).join(", ")})`
8063
+ );
8064
+ }
8065
+ const state = workflow.states.find((s) => s.name === stateRef) ?? workflow.states.find((s) => s.id === stateRef);
8066
+ if (!state) {
8067
+ die(
8068
+ `${where}, but workflow "${workflow.name}" has no state "${stateRef}" (have: ${workflow.states.map((s) => s.name).join(", ")})`
8069
+ );
8070
+ }
8071
+ return state.id;
8072
+ }
8073
+ function printWorkflowDetail(wf, lib) {
7987
8074
  console.log(`${wf.name}${wf.is_system ? " (standard, read-only)" : ""} [${wf.id}]`);
7988
8075
  if (wf.description) console.log(wf.description);
7989
8076
  console.log("\nstates:");
7990
8077
  const byId = new Map(wf.states.map((s) => [s.id, s]));
7991
- table(
8078
+ const rows = wf.states.length === 0 ? [] : formatTable(
7992
8079
  wf.states.map((s) => [
7993
8080
  ` ${s.name}`,
7994
8081
  s.category,
7995
8082
  s.id === wf.initial_state_id ? "(initial)" : ""
7996
8083
  ])
7997
- );
8084
+ ).split("\n");
8085
+ for (const [i, row] of rows.entries()) {
8086
+ console.log(row);
8087
+ for (const line of inheritanceLines(lib, wf.states[i])) console.log(line);
8088
+ }
7998
8089
  console.log("\ntransitions:");
7999
8090
  for (const t of wf.transitions) {
8000
8091
  console.log(
@@ -8034,10 +8125,38 @@ function register11(program3) {
8034
8125
  withCommon(
8035
8126
  workflows.command("show <id-or-name>").description("Show a workflow with states and transitions")
8036
8127
  ).action(async (ref, opts) => {
8037
- const api = client(opts);
8038
- const wf = await resolveWorkflow(api, ref);
8128
+ const lib = await loadLibrary(client(opts));
8129
+ const wf = pickWorkflow(lib.workflows, ref);
8039
8130
  if (opts.json) return printJson(wf);
8040
- printWorkflowDetail(wf);
8131
+ printWorkflowDetail(wf, lib);
8132
+ });
8133
+ withCommon(
8134
+ workflows.command("bases").description("List the states other states inherit context from, with their children")
8135
+ ).action(async (opts) => {
8136
+ const lib = await loadLibrary(client(opts));
8137
+ const bases = [...lib.states.values()].filter((e) => lib.children.has(e.state.id));
8138
+ if (opts.json) {
8139
+ const ref = (e) => ({
8140
+ workflow: { id: e.workflow.id, name: e.workflow.name },
8141
+ state: { id: e.state.id, name: e.state.name }
8142
+ });
8143
+ return printJson(
8144
+ bases.map((b) => ({
8145
+ ...ref(b),
8146
+ inherited_by: lib.children.get(b.state.id).map(ref)
8147
+ }))
8148
+ );
8149
+ }
8150
+ if (bases.length === 0) return console.log("no base states");
8151
+ let group;
8152
+ for (const base of bases) {
8153
+ if (base.workflow.id !== group) {
8154
+ console.log(`${group === void 0 ? "" : "\n"}${base.workflow.name}`);
8155
+ group = base.workflow.id;
8156
+ }
8157
+ console.log(` ${base.state.name}`);
8158
+ console.log(` inherited by: ${lib.children.get(base.state.id).map(qualify).join(", ")}`);
8159
+ }
8041
8160
  });
8042
8161
  withCommon(
8043
8162
  workflows.command("create [json]").description(
@@ -8053,11 +8172,13 @@ see \`tines workflows create --help\` for the expected shape`
8053
8172
  );
8054
8173
  }
8055
8174
  assertNewStatesHavePrompts(body.states, opts.prompts);
8056
- const wf = await client(opts).createWorkflow(body);
8175
+ const api = client(opts);
8176
+ await resolveStateBases(body.states, () => loadLibrary(api));
8177
+ const wf = await api.createWorkflow(body);
8057
8178
  if (opts.json) return printJson(wf);
8058
8179
  console.log(`created workflow "${wf.name}" (${wf.id})
8059
8180
  `);
8060
- printWorkflowDetail(wf);
8181
+ printWorkflowDetail(wf, await loadLibrary(api));
8061
8182
  }
8062
8183
  );
8063
8184
  withCommon(
@@ -8065,9 +8186,11 @@ see \`tines workflows create --help\` for the expected shape`
8065
8186
  ).action(
8066
8187
  async (ref, inline, opts) => {
8067
8188
  const api = client(opts);
8068
- const wf = await resolveWorkflow(api, ref);
8189
+ const lib = await loadLibrary(api);
8190
+ const wf = pickWorkflow(lib.workflows, ref);
8069
8191
  const body = readJsonBody(inline, opts.file) ?? {};
8070
8192
  assertNewStatesHavePrompts(body.states, opts.prompts);
8193
+ await resolveStateBases(body.states, async () => lib);
8071
8194
  if (opts.name !== void 0) body.name = opts.name;
8072
8195
  if (opts.description !== void 0) body.description = opts.description;
8073
8196
  if (opts.initialState !== void 0) body.initial_state = opts.initialState;
@@ -8078,7 +8201,7 @@ see \`tines workflows create --help\` for the expected shape`
8078
8201
  if (opts.json) return printJson(updated);
8079
8202
  console.log(`updated workflow "${updated.name}" (${updated.id})
8080
8203
  `);
8081
- printWorkflowDetail(updated);
8204
+ printWorkflowDetail(updated, await loadLibrary(api));
8082
8205
  }
8083
8206
  );
8084
8207
  withCommon(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tines",
3
- "version": "0.0.136",
3
+ "version": "0.0.138",
4
4
  "description": "CLI for Tines, an orchestration layer for AI agents",
5
5
  "repository": {
6
6
  "type": "git",