tines 0.0.136 → 0.0.137

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