@danypops/tickets 0.4.6 → 0.4.7

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@danypops/tickets",
3
- "version": "0.4.6",
3
+ "version": "0.4.7",
4
4
  "description": "Unified CLI, daemon, and TypeScript library for issue tracking across GitHub, GitLab, and Jira.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -32,11 +32,38 @@ interface OperationSpec {
32
32
  readonly effect: VehicleEffect;
33
33
  readonly properties: Record<string, LooseObjectProperty>;
34
34
  readonly required: readonly string[];
35
+ /**
36
+ * Reshapes the flat tool-facing input into whatever TICKET_OP_HANDLERS
37
+ * expects, for the one operation where those differ: issue.list flattens
38
+ * project/status/assignee/labels/limit as top-level tool properties
39
+ * (matching issue.search's own flat convention, and pi-stef/atlassian's
40
+ * jira_search_issues/jira_get_project_issues -- every filter param is its
41
+ * own top-level property there too, never an opaque nested bag), while
42
+ * the RPC/CLI-level handler contract keeps its existing nested
43
+ * `filter: ListFilter` shape. Identity by default.
44
+ */
45
+ readonly mapInput?: (input: Record<string, unknown>) => Record<string, unknown>;
46
+ }
47
+
48
+ const stringArrayProp: LooseObjectProperty = { type: "array" };
49
+
50
+ function definedEntriesOnly(input: Record<string, unknown>): Record<string, unknown> {
51
+ return Object.fromEntries(Object.entries(input).filter(([, value]) => value !== undefined));
35
52
  }
36
53
 
37
54
  const OPERATIONS: readonly OperationSpec[] = [
38
55
  { action: "backends.list", description: "Lists every configured backend name (github, gitlab, jira, ...).", effect: "read", properties: {}, required: [] },
39
- { action: "issue.list", description: "Lists issues from one backend, optionally filtered.", effect: "read", properties: { backend: stringProp, filter: { type: "object" } }, required: ["backend"] },
56
+ {
57
+ action: "issue.list",
58
+ description: "Lists issues from one backend, optionally filtered.",
59
+ effect: "read",
60
+ properties: { backend: stringProp, project: stringProp, status: stringProp, assignee: stringProp, labels: stringArrayProp, limit: numberProp },
61
+ required: ["backend"],
62
+ mapInput: ({ backend, project, status, assignee, labels, limit }) => ({
63
+ backend,
64
+ filter: definedEntriesOnly({ project, status, assignee, labels, limit }),
65
+ }),
66
+ },
40
67
  { action: "issue.get", description: "Gets one issue by its ref (e.g. \"github:#42\").", effect: "read", properties: { ref: stringProp }, required: ["ref"] },
41
68
  {
42
69
  action: "issue.create",
@@ -103,7 +130,11 @@ export function createTicketsVehicleRegistry(deps: Omit<TicketsAppDeps, "vehicle
103
130
  limits: LIMITS,
104
131
  });
105
132
  const handler = TICKET_OP_HANDLERS[spec.action];
106
- registry.register(OWNER, bindVehicleOperation(operation, () => async (context) => handler(deps, context.input as never)));
133
+ const mapInput = spec.mapInput ?? ((input: Record<string, unknown>) => input);
134
+ registry.register(
135
+ OWNER,
136
+ bindVehicleOperation(operation, () => async (context) => handler(deps, mapInput(context.input as Record<string, unknown>) as never)),
137
+ );
107
138
  }
108
139
 
109
140
  return registry;