@andypai/agent-kanban 0.6.0 → 0.6.1

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 (48) hide show
  1. package/README.md +42 -19
  2. package/package.json +3 -2
  3. package/src/__tests__/activity.test.ts +12 -0
  4. package/src/__tests__/api.test.ts +4 -2
  5. package/src/__tests__/board-utils.test.ts +16 -3
  6. package/src/__tests__/column-roles.test.ts +34 -0
  7. package/src/__tests__/commands/board.test.ts +7 -0
  8. package/src/__tests__/conflict.test.ts +11 -1
  9. package/src/__tests__/db.test.ts +70 -0
  10. package/src/__tests__/index.test.ts +55 -0
  11. package/src/__tests__/jira-cache.test.ts +56 -0
  12. package/src/__tests__/jira-jql.test.ts +51 -0
  13. package/src/__tests__/jira-provider-read.test.ts +50 -0
  14. package/src/__tests__/mcp-server.test.ts +2 -2
  15. package/src/__tests__/metrics.test.ts +37 -1
  16. package/src/__tests__/postgres-local-provider.test.ts +90 -1
  17. package/src/__tests__/server.test.ts +126 -0
  18. package/src/__tests__/use-cases.test.ts +77 -0
  19. package/src/__tests__/webhooks.test.ts +75 -22
  20. package/src/api.ts +58 -36
  21. package/src/column-roles.ts +52 -0
  22. package/src/commands/board.ts +4 -4
  23. package/src/db.ts +145 -114
  24. package/src/errors.ts +1 -0
  25. package/src/index.ts +84 -33
  26. package/src/mcp/core.ts +8 -7
  27. package/src/metrics.ts +48 -23
  28. package/src/provider-runtime.ts +9 -2
  29. package/src/providers/index.ts +4 -1
  30. package/src/providers/jira-cache.ts +23 -6
  31. package/src/providers/jira-core.ts +921 -0
  32. package/src/providers/jira-jql.ts +48 -0
  33. package/src/providers/jira.ts +111 -759
  34. package/src/providers/linear-cache.ts +5 -3
  35. package/src/providers/linear-core.ts +688 -0
  36. package/src/providers/linear.ts +70 -554
  37. package/src/providers/postgres-jira-cache.ts +597 -0
  38. package/src/providers/postgres-jira.ts +15 -1312
  39. package/src/providers/postgres-linear-cache.ts +500 -0
  40. package/src/providers/postgres-linear.ts +22 -1088
  41. package/src/providers/postgres-local.ts +181 -74
  42. package/src/providers/types.ts +71 -2
  43. package/src/server.ts +118 -32
  44. package/src/use-cases.ts +139 -0
  45. package/src/webhooks.ts +26 -0
  46. package/ui/dist/assets/index-65LcV0R7.js +40 -0
  47. package/ui/dist/index.html +1 -1
  48. package/ui/dist/assets/index-CFhtfqCn.js +0 -40
@@ -0,0 +1,48 @@
1
+ import { ErrorCode, KanbanError } from '../errors'
2
+
3
+ // Jira project keys are alphanumeric with underscores (the API-canonical key,
4
+ // e.g. `ENG`). Anything else would be malformed and, interpolated into JQL,
5
+ // could break out of the query.
6
+ const PROJECT_KEY_RE = /^[A-Za-z0-9_]+$/
7
+
8
+ // JQL datetime literals contain only digits and date/time separators. This
9
+ // covers both the space form (`1970-01-01 00:00`) and the ISO forms Jira
10
+ // returns in `issue.fields.updated` (`2026-01-05T00:00:00Z`, `...+0000`). A
11
+ // double quote, backslash, or any JQL operator/keyword cannot match, so a
12
+ // tampered `updated` value (e.g. delivered via webhook) cannot escape the
13
+ // quoted literal.
14
+ const JQL_TIMESTAMP_RE = /^[0-9 :.\-TZ+]+$/
15
+
16
+ const DEFAULT_SINCE = '1970-01-01 00:00'
17
+
18
+ export function assertSafeProjectKey(key: string): string {
19
+ if (!PROJECT_KEY_RE.test(key)) {
20
+ throw new KanbanError(
21
+ ErrorCode.PROVIDER_NOT_CONFIGURED,
22
+ `Invalid Jira project key ${JSON.stringify(key)}: expected alphanumeric characters only`,
23
+ )
24
+ }
25
+ return key
26
+ }
27
+
28
+ /**
29
+ * Return the cursor only if it's a safe JQL datetime literal, else null. Callers
30
+ * use this both to build the JQL and to seed `newestUpdatedAt`, so a rejected
31
+ * cursor is never carried forward and re-persisted (which would otherwise trap
32
+ * every future sync into a full scan).
33
+ */
34
+ export function safeDeltaSince(since: string | null): string | null {
35
+ if (since === null) return null
36
+ return JQL_TIMESTAMP_RE.test(since) ? since : null
37
+ }
38
+
39
+ /**
40
+ * Build the delta-sync JQL with both interpolated values validated. An invalid
41
+ * `since` (only possible if a persisted/upstream timestamp was tampered with)
42
+ * falls back to a full scan rather than risking injection.
43
+ */
44
+ export function buildDeltaJql(projectKey: string, since: string | null): string {
45
+ assertSafeProjectKey(projectKey)
46
+ const sinceClause = safeDeltaSince(since) ?? DEFAULT_SINCE
47
+ return `project = ${projectKey} AND updated >= "${sinceClause}" ORDER BY updated ASC`
48
+ }