@unbrained/pm-cli 2026.3.12 → 2026.5.1-2

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 (285) hide show
  1. package/.agents/pm/extensions/.managed-extensions.json +42 -0
  2. package/.agents/pm/extensions/beads/index.js +109 -0
  3. package/.agents/pm/extensions/beads/manifest.json +7 -0
  4. package/{dist/cli/commands/beads.js → .agents/pm/extensions/beads/runtime.js} +31 -21
  5. package/.agents/pm/extensions/beads/runtime.ts +702 -0
  6. package/.agents/pm/extensions/todos/index.js +126 -0
  7. package/.agents/pm/extensions/todos/manifest.json +7 -0
  8. package/{dist/extensions/builtins/todos/import-export.js → .agents/pm/extensions/todos/runtime.js} +39 -29
  9. package/.agents/pm/extensions/todos/runtime.ts +568 -0
  10. package/AGENTS.md +196 -92
  11. package/CHANGELOG.md +404 -0
  12. package/CODE_OF_CONDUCT.md +42 -0
  13. package/CONTRIBUTING.md +144 -0
  14. package/PRD.md +512 -164
  15. package/README.md +1053 -2
  16. package/SECURITY.md +51 -0
  17. package/dist/cli/commands/activity.d.ts +5 -0
  18. package/dist/cli/commands/activity.js +66 -3
  19. package/dist/cli/commands/activity.js.map +1 -1
  20. package/dist/cli/commands/aggregate.d.ts +54 -0
  21. package/dist/cli/commands/aggregate.js +181 -0
  22. package/dist/cli/commands/aggregate.js.map +1 -0
  23. package/dist/cli/commands/append.js +4 -1
  24. package/dist/cli/commands/append.js.map +1 -1
  25. package/dist/cli/commands/calendar.d.ts +109 -0
  26. package/dist/cli/commands/calendar.js +797 -0
  27. package/dist/cli/commands/calendar.js.map +1 -0
  28. package/dist/cli/commands/claim.d.ts +5 -1
  29. package/dist/cli/commands/claim.js +42 -21
  30. package/dist/cli/commands/claim.js.map +1 -1
  31. package/dist/cli/commands/close.d.ts +1 -0
  32. package/dist/cli/commands/close.js +54 -5
  33. package/dist/cli/commands/close.js.map +1 -1
  34. package/dist/cli/commands/comments-audit.d.ts +91 -0
  35. package/dist/cli/commands/comments-audit.js +195 -0
  36. package/dist/cli/commands/comments-audit.js.map +1 -0
  37. package/dist/cli/commands/comments.d.ts +1 -0
  38. package/dist/cli/commands/comments.js +70 -21
  39. package/dist/cli/commands/comments.js.map +1 -1
  40. package/dist/cli/commands/completion.d.ts +10 -4
  41. package/dist/cli/commands/completion.js +1184 -137
  42. package/dist/cli/commands/completion.js.map +1 -1
  43. package/dist/cli/commands/config.d.ts +35 -3
  44. package/dist/cli/commands/config.js +968 -13
  45. package/dist/cli/commands/config.js.map +1 -1
  46. package/dist/cli/commands/context.d.ts +86 -0
  47. package/dist/cli/commands/context.js +299 -0
  48. package/dist/cli/commands/context.js.map +1 -0
  49. package/dist/cli/commands/contracts.d.ts +78 -0
  50. package/dist/cli/commands/contracts.js +920 -0
  51. package/dist/cli/commands/contracts.js.map +1 -0
  52. package/dist/cli/commands/create.d.ts +48 -14
  53. package/dist/cli/commands/create.js +1331 -160
  54. package/dist/cli/commands/create.js.map +1 -1
  55. package/dist/cli/commands/dedupe-audit.d.ts +81 -0
  56. package/dist/cli/commands/dedupe-audit.js +330 -0
  57. package/dist/cli/commands/dedupe-audit.js.map +1 -0
  58. package/dist/cli/commands/deps.d.ts +52 -0
  59. package/dist/cli/commands/deps.js +204 -0
  60. package/dist/cli/commands/deps.js.map +1 -0
  61. package/dist/cli/commands/docs.d.ts +19 -0
  62. package/dist/cli/commands/docs.js +212 -13
  63. package/dist/cli/commands/docs.js.map +1 -1
  64. package/dist/cli/commands/extension.d.ts +122 -0
  65. package/dist/cli/commands/extension.js +1850 -0
  66. package/dist/cli/commands/extension.js.map +1 -0
  67. package/dist/cli/commands/files.d.ts +52 -1
  68. package/dist/cli/commands/files.js +455 -13
  69. package/dist/cli/commands/files.js.map +1 -1
  70. package/dist/cli/commands/gc.d.ts +11 -1
  71. package/dist/cli/commands/gc.js +89 -11
  72. package/dist/cli/commands/gc.js.map +1 -1
  73. package/dist/cli/commands/get.d.ts +13 -0
  74. package/dist/cli/commands/get.js +35 -3
  75. package/dist/cli/commands/get.js.map +1 -1
  76. package/dist/cli/commands/health.d.ts +10 -2
  77. package/dist/cli/commands/health.js +774 -23
  78. package/dist/cli/commands/health.js.map +1 -1
  79. package/dist/cli/commands/history.d.ts +20 -0
  80. package/dist/cli/commands/history.js +152 -6
  81. package/dist/cli/commands/history.js.map +1 -1
  82. package/dist/cli/commands/index.d.ts +16 -3
  83. package/dist/cli/commands/index.js +16 -3
  84. package/dist/cli/commands/index.js.map +1 -1
  85. package/dist/cli/commands/init.d.ts +7 -2
  86. package/dist/cli/commands/init.js +137 -5
  87. package/dist/cli/commands/init.js.map +1 -1
  88. package/dist/cli/commands/learnings.d.ts +17 -0
  89. package/dist/cli/commands/learnings.js +129 -0
  90. package/dist/cli/commands/learnings.js.map +1 -0
  91. package/dist/cli/commands/list.d.ts +29 -1
  92. package/dist/cli/commands/list.js +289 -53
  93. package/dist/cli/commands/list.js.map +1 -1
  94. package/dist/cli/commands/normalize.d.ts +51 -0
  95. package/dist/cli/commands/normalize.js +298 -0
  96. package/dist/cli/commands/normalize.js.map +1 -0
  97. package/dist/cli/commands/notes.d.ts +17 -0
  98. package/dist/cli/commands/notes.js +129 -0
  99. package/dist/cli/commands/notes.js.map +1 -0
  100. package/dist/cli/commands/reindex.d.ts +1 -0
  101. package/dist/cli/commands/reindex.js +208 -32
  102. package/dist/cli/commands/reindex.js.map +1 -1
  103. package/dist/cli/commands/restore.js +164 -30
  104. package/dist/cli/commands/restore.js.map +1 -1
  105. package/dist/cli/commands/search.d.ts +14 -1
  106. package/dist/cli/commands/search.js +475 -81
  107. package/dist/cli/commands/search.js.map +1 -1
  108. package/dist/cli/commands/stats.js +26 -10
  109. package/dist/cli/commands/stats.js.map +1 -1
  110. package/dist/cli/commands/templates.d.ts +26 -0
  111. package/dist/cli/commands/templates.js +179 -0
  112. package/dist/cli/commands/templates.js.map +1 -0
  113. package/dist/cli/commands/test-all.d.ts +19 -1
  114. package/dist/cli/commands/test-all.js +161 -13
  115. package/dist/cli/commands/test-all.js.map +1 -1
  116. package/dist/cli/commands/test-runs.d.ts +63 -0
  117. package/dist/cli/commands/test-runs.js +179 -0
  118. package/dist/cli/commands/test-runs.js.map +1 -0
  119. package/dist/cli/commands/test.d.ts +75 -1
  120. package/dist/cli/commands/test.js +1360 -41
  121. package/dist/cli/commands/test.js.map +1 -1
  122. package/dist/cli/commands/update-many.d.ts +57 -0
  123. package/dist/cli/commands/update-many.js +631 -0
  124. package/dist/cli/commands/update-many.js.map +1 -0
  125. package/dist/cli/commands/update.d.ts +30 -0
  126. package/dist/cli/commands/update.js +1393 -84
  127. package/dist/cli/commands/update.js.map +1 -1
  128. package/dist/cli/commands/validate.d.ts +30 -0
  129. package/dist/cli/commands/validate.js +1151 -0
  130. package/dist/cli/commands/validate.js.map +1 -0
  131. package/dist/cli/error-guidance.d.ts +33 -0
  132. package/dist/cli/error-guidance.js +337 -0
  133. package/dist/cli/error-guidance.js.map +1 -0
  134. package/dist/cli/extension-command-options.d.ts +1 -0
  135. package/dist/cli/extension-command-options.js +92 -0
  136. package/dist/cli/extension-command-options.js.map +1 -1
  137. package/dist/cli/help-content.d.ts +20 -0
  138. package/dist/cli/help-content.js +543 -0
  139. package/dist/cli/help-content.js.map +1 -0
  140. package/dist/cli/main.js +3625 -445
  141. package/dist/cli/main.js.map +1 -1
  142. package/dist/core/extensions/index.d.ts +13 -1
  143. package/dist/core/extensions/index.js +108 -1
  144. package/dist/core/extensions/index.js.map +1 -1
  145. package/dist/core/extensions/item-fields.d.ts +2 -0
  146. package/dist/core/extensions/item-fields.js +79 -0
  147. package/dist/core/extensions/item-fields.js.map +1 -0
  148. package/dist/core/extensions/loader.d.ts +322 -9
  149. package/dist/core/extensions/loader.js +911 -20
  150. package/dist/core/extensions/loader.js.map +1 -1
  151. package/dist/core/extensions/runtime-registrations.d.ts +5 -0
  152. package/dist/core/extensions/runtime-registrations.js +51 -0
  153. package/dist/core/extensions/runtime-registrations.js.map +1 -0
  154. package/dist/core/history/history-stream-policy.d.ts +20 -0
  155. package/dist/core/history/history-stream-policy.js +53 -0
  156. package/dist/core/history/history-stream-policy.js.map +1 -0
  157. package/dist/core/history/history.js +90 -1
  158. package/dist/core/history/history.js.map +1 -1
  159. package/dist/core/item/id.js +4 -1
  160. package/dist/core/item/id.js.map +1 -1
  161. package/dist/core/item/index.d.ts +1 -0
  162. package/dist/core/item/index.js +1 -0
  163. package/dist/core/item/index.js.map +1 -1
  164. package/dist/core/item/item-format.d.ts +11 -5
  165. package/dist/core/item/item-format.js +507 -24
  166. package/dist/core/item/item-format.js.map +1 -1
  167. package/dist/core/item/parent-reference-policy.d.ts +6 -0
  168. package/dist/core/item/parent-reference-policy.js +32 -0
  169. package/dist/core/item/parent-reference-policy.js.map +1 -0
  170. package/dist/core/item/parse.d.ts +5 -0
  171. package/dist/core/item/parse.js +216 -19
  172. package/dist/core/item/parse.js.map +1 -1
  173. package/dist/core/item/sprint-release-format.d.ts +6 -0
  174. package/dist/core/item/sprint-release-format.js +33 -0
  175. package/dist/core/item/sprint-release-format.js.map +1 -0
  176. package/dist/core/item/status.d.ts +3 -0
  177. package/dist/core/item/status.js +24 -0
  178. package/dist/core/item/status.js.map +1 -0
  179. package/dist/core/item/type-registry.d.ts +37 -0
  180. package/dist/core/item/type-registry.js +706 -0
  181. package/dist/core/item/type-registry.js.map +1 -0
  182. package/dist/core/lock/lock.d.ts +1 -1
  183. package/dist/core/lock/lock.js +101 -12
  184. package/dist/core/lock/lock.js.map +1 -1
  185. package/dist/core/output/command-aware.d.ts +1 -0
  186. package/dist/core/output/command-aware.js +394 -0
  187. package/dist/core/output/command-aware.js.map +1 -0
  188. package/dist/core/output/output.d.ts +3 -0
  189. package/dist/core/output/output.js +124 -6
  190. package/dist/core/output/output.js.map +1 -1
  191. package/dist/core/schema/runtime-field-filters.d.ts +3 -0
  192. package/dist/core/schema/runtime-field-filters.js +39 -0
  193. package/dist/core/schema/runtime-field-filters.js.map +1 -0
  194. package/dist/core/schema/runtime-field-values.d.ts +8 -0
  195. package/dist/core/schema/runtime-field-values.js +154 -0
  196. package/dist/core/schema/runtime-field-values.js.map +1 -0
  197. package/dist/core/schema/runtime-schema.d.ts +68 -0
  198. package/dist/core/schema/runtime-schema.js +554 -0
  199. package/dist/core/schema/runtime-schema.js.map +1 -0
  200. package/dist/core/search/cache.d.ts +13 -1
  201. package/dist/core/search/cache.js +123 -14
  202. package/dist/core/search/cache.js.map +1 -1
  203. package/dist/core/search/semantic-defaults.d.ts +6 -0
  204. package/dist/core/search/semantic-defaults.js +120 -0
  205. package/dist/core/search/semantic-defaults.js.map +1 -0
  206. package/dist/core/search/vector-stores.js +3 -1
  207. package/dist/core/search/vector-stores.js.map +1 -1
  208. package/dist/core/shared/command-types.d.ts +2 -0
  209. package/dist/core/shared/conflict-markers.d.ts +7 -0
  210. package/dist/core/shared/conflict-markers.js +27 -0
  211. package/dist/core/shared/conflict-markers.js.map +1 -0
  212. package/dist/core/shared/constants.d.ts +15 -4
  213. package/dist/core/shared/constants.js +141 -1
  214. package/dist/core/shared/constants.js.map +1 -1
  215. package/dist/core/shared/errors.d.ts +10 -1
  216. package/dist/core/shared/errors.js +3 -1
  217. package/dist/core/shared/errors.js.map +1 -1
  218. package/dist/core/shared/text-normalization.d.ts +4 -0
  219. package/dist/core/shared/text-normalization.js +33 -0
  220. package/dist/core/shared/text-normalization.js.map +1 -0
  221. package/dist/core/shared/time.d.ts +1 -2
  222. package/dist/core/shared/time.js +98 -11
  223. package/dist/core/shared/time.js.map +1 -1
  224. package/dist/core/store/index.d.ts +1 -0
  225. package/dist/core/store/index.js +1 -0
  226. package/dist/core/store/index.js.map +1 -1
  227. package/dist/core/store/item-format-migration.d.ts +9 -0
  228. package/dist/core/store/item-format-migration.js +87 -0
  229. package/dist/core/store/item-format-migration.js.map +1 -0
  230. package/dist/core/store/item-store.d.ts +13 -4
  231. package/dist/core/store/item-store.js +238 -51
  232. package/dist/core/store/item-store.js.map +1 -1
  233. package/dist/core/store/paths.d.ts +21 -3
  234. package/dist/core/store/paths.js +59 -4
  235. package/dist/core/store/paths.js.map +1 -1
  236. package/dist/core/store/settings.d.ts +14 -1
  237. package/dist/core/store/settings.js +463 -7
  238. package/dist/core/store/settings.js.map +1 -1
  239. package/dist/core/telemetry/consent.d.ts +2 -0
  240. package/dist/core/telemetry/consent.js +79 -0
  241. package/dist/core/telemetry/consent.js.map +1 -0
  242. package/dist/core/telemetry/runtime.d.ts +38 -0
  243. package/dist/core/telemetry/runtime.js +733 -0
  244. package/dist/core/telemetry/runtime.js.map +1 -0
  245. package/dist/core/test/background-runs.d.ts +117 -0
  246. package/dist/core/test/background-runs.js +760 -0
  247. package/dist/core/test/background-runs.js.map +1 -0
  248. package/dist/core/test/item-test-run-tracking.d.ts +9 -0
  249. package/dist/core/test/item-test-run-tracking.js +50 -0
  250. package/dist/core/test/item-test-run-tracking.js.map +1 -0
  251. package/dist/sdk/cli-contracts.d.ts +92 -0
  252. package/dist/sdk/cli-contracts.js +2357 -0
  253. package/dist/sdk/cli-contracts.js.map +1 -0
  254. package/dist/sdk/index.d.ts +34 -0
  255. package/dist/sdk/index.js +23 -0
  256. package/dist/sdk/index.js.map +1 -0
  257. package/dist/types.d.ts +197 -3
  258. package/dist/types.js +48 -1
  259. package/dist/types.js.map +1 -1
  260. package/docs/ARCHITECTURE.md +368 -39
  261. package/docs/EXTENSIONS.md +454 -49
  262. package/docs/RELEASING.md +70 -19
  263. package/docs/SDK.md +123 -0
  264. package/docs/examples/starter-extension/README.md +48 -0
  265. package/docs/examples/starter-extension/index.js +191 -0
  266. package/docs/examples/starter-extension/manifest.json +17 -0
  267. package/docs/examples/starter-extension/package.json +10 -0
  268. package/package.json +41 -14
  269. package/.pi/extensions/pm-cli/index.ts +0 -778
  270. package/dist/cli/commands/beads.d.ts +0 -16
  271. package/dist/cli/commands/beads.js.map +0 -1
  272. package/dist/cli/commands/install.d.ts +0 -18
  273. package/dist/cli/commands/install.js +0 -87
  274. package/dist/cli/commands/install.js.map +0 -1
  275. package/dist/core/extensions/builtins.d.ts +0 -3
  276. package/dist/core/extensions/builtins.js +0 -47
  277. package/dist/core/extensions/builtins.js.map +0 -1
  278. package/dist/extensions/builtins/beads/index.d.ts +0 -8
  279. package/dist/extensions/builtins/beads/index.js +0 -33
  280. package/dist/extensions/builtins/beads/index.js.map +0 -1
  281. package/dist/extensions/builtins/todos/import-export.d.ts +0 -26
  282. package/dist/extensions/builtins/todos/import-export.js.map +0 -1
  283. package/dist/extensions/builtins/todos/index.d.ts +0 -8
  284. package/dist/extensions/builtins/todos/index.js +0 -38
  285. package/dist/extensions/builtins/todos/index.js.map +0 -1
@@ -0,0 +1,797 @@
1
+ import { pathExists } from "../../core/fs/fs-utils.js";
2
+ import { getActiveExtensionRegistrations } from "../../core/extensions/index.js";
3
+ import { resolveItemTypeRegistry, resolveTypeName } from "../../core/item/type-registry.js";
4
+ import { normalizeStatusInput } from "../../core/item/status.js";
5
+ import { collectRuntimeFilterValues, matchesRuntimeFilters } from "../../core/schema/runtime-field-filters.js";
6
+ import { resolveRuntimeFieldRegistry, resolveRuntimeStatusRegistry, } from "../../core/schema/runtime-schema.js";
7
+ import { EXIT_CODE } from "../../core/shared/constants.js";
8
+ import { PmCliError } from "../../core/shared/errors.js";
9
+ import { compareTimestampStrings, nowIso, resolveIsoOrRelative } from "../../core/shared/time.js";
10
+ import { listAllFrontMatter } from "../../core/store/item-store.js";
11
+ import { getSettingsPath, resolvePmRoot } from "../../core/store/paths.js";
12
+ import { readSettings } from "../../core/store/settings.js";
13
+ import { RECURRENCE_WEEKDAY_VALUES } from "../../types/index.js";
14
+ export const CALENDAR_VIEW_VALUES = ["agenda", "day", "week", "month"];
15
+ export const CALENDAR_OUTPUT_VALUES = ["markdown", "toon", "json"];
16
+ const UTC_DAY_TO_WEEKDAY = ["sun", "mon", "tue", "wed", "thu", "fri", "sat"];
17
+ const DEFAULT_RECURRENCE_LOOKAHEAD_DAYS = 365;
18
+ const DEFAULT_RECURRENCE_LOOKBACK_DAYS = 365;
19
+ const MAX_RECURRENCE_OCCURRENCES = 1000;
20
+ function weekdayOrderIndex(value) {
21
+ return RECURRENCE_WEEKDAY_VALUES.indexOf(value);
22
+ }
23
+ function parseLimit(raw) {
24
+ if (raw === undefined)
25
+ return undefined;
26
+ const parsed = Number(raw);
27
+ if (!Number.isInteger(parsed) || parsed < 0) {
28
+ throw new PmCliError("Calendar limit must be a non-negative integer", EXIT_CODE.USAGE);
29
+ }
30
+ return parsed;
31
+ }
32
+ function parseNonNegativeInteger(raw, label) {
33
+ if (raw === undefined)
34
+ return undefined;
35
+ const parsed = Number(raw);
36
+ if (!Number.isInteger(parsed) || parsed < 0) {
37
+ throw new PmCliError(`${label} must be a non-negative integer`, EXIT_CODE.USAGE);
38
+ }
39
+ return parsed;
40
+ }
41
+ function parseIncludeSources(raw) {
42
+ if (!raw) {
43
+ return new Set(["deadlines", "reminders", "events"]);
44
+ }
45
+ const normalized = raw.trim().toLowerCase();
46
+ if (normalized === "all") {
47
+ return new Set(["deadlines", "reminders", "events"]);
48
+ }
49
+ const values = normalized
50
+ .split(/[\|,]/)
51
+ .map((value) => value.trim())
52
+ .filter((value) => value.length > 0);
53
+ if (values.length === 0) {
54
+ throw new PmCliError("Calendar include filter must not be empty", EXIT_CODE.USAGE);
55
+ }
56
+ const include = new Set();
57
+ for (const value of values) {
58
+ if (value === "deadlines" || value === "deadline") {
59
+ include.add("deadlines");
60
+ continue;
61
+ }
62
+ if (value === "reminders" || value === "reminder") {
63
+ include.add("reminders");
64
+ continue;
65
+ }
66
+ if (value === "events" || value === "event") {
67
+ include.add("events");
68
+ continue;
69
+ }
70
+ throw new PmCliError("Calendar include filter must be deadlines|reminders|events|all", EXIT_CODE.USAGE);
71
+ }
72
+ return include;
73
+ }
74
+ function parseView(raw) {
75
+ if (!raw)
76
+ return "agenda";
77
+ const normalized = raw.trim().toLowerCase();
78
+ if (!CALENDAR_VIEW_VALUES.includes(normalized)) {
79
+ throw new PmCliError("Calendar view must be one of agenda|day|week|month", EXIT_CODE.USAGE);
80
+ }
81
+ return normalized;
82
+ }
83
+ function parseOutputFormat(raw) {
84
+ if (!raw)
85
+ return undefined;
86
+ const normalized = raw.trim().toLowerCase();
87
+ if (!CALENDAR_OUTPUT_VALUES.includes(normalized)) {
88
+ throw new PmCliError("Calendar format must be one of markdown|toon|json", EXIT_CODE.USAGE);
89
+ }
90
+ return normalized;
91
+ }
92
+ export function resolveCalendarOutputFormat(options, global) {
93
+ const commandFormat = parseOutputFormat(options.format);
94
+ if (global.json && commandFormat && commandFormat !== "json") {
95
+ throw new PmCliError("Cannot combine --json with --format markdown|toon", EXIT_CODE.USAGE);
96
+ }
97
+ if (global.json) {
98
+ return "json";
99
+ }
100
+ return commandFormat ?? "markdown";
101
+ }
102
+ function parsePriority(raw) {
103
+ if (raw === undefined)
104
+ return undefined;
105
+ const parsed = Number(raw);
106
+ if (!Number.isInteger(parsed) || parsed < 0 || parsed > 4) {
107
+ throw new PmCliError("Calendar priority filter must be 0..4", EXIT_CODE.USAGE);
108
+ }
109
+ return parsed;
110
+ }
111
+ function parseType(raw, typeRegistry) {
112
+ if (raw === undefined)
113
+ return undefined;
114
+ const parsed = resolveTypeName(raw, typeRegistry);
115
+ if (!parsed) {
116
+ throw new PmCliError(`Calendar type filter must be one of ${typeRegistry.types.join("|")}`, EXIT_CODE.USAGE);
117
+ }
118
+ return parsed;
119
+ }
120
+ function parseStatus(raw, statusRegistry) {
121
+ if (raw === undefined)
122
+ return undefined;
123
+ const normalized = normalizeStatusInput(raw, statusRegistry);
124
+ if (!normalized) {
125
+ const allowedStatuses = statusRegistry.definitions.map((definition) => definition.id);
126
+ throw new PmCliError(`Calendar status filter must be one of ${allowedStatuses.join("|")}`, EXIT_CODE.USAGE);
127
+ }
128
+ return normalized;
129
+ }
130
+ function parseAssigneeFilter(raw) {
131
+ if (raw === undefined) {
132
+ return undefined;
133
+ }
134
+ const normalized = raw.trim().toLowerCase();
135
+ if (!normalized) {
136
+ throw new PmCliError("Calendar assignee filter must be one of assigned|unassigned", EXIT_CODE.USAGE);
137
+ }
138
+ if (normalized !== "assigned" && normalized !== "unassigned") {
139
+ throw new PmCliError(`Invalid calendar assignee filter "${raw}". Allowed: assigned|unassigned`, EXIT_CODE.USAGE);
140
+ }
141
+ return normalized;
142
+ }
143
+ function toUtcDayKey(timestamp) {
144
+ return new Date(timestamp).toISOString().slice(0, 10);
145
+ }
146
+ function startOfUtcDay(timestamp) {
147
+ const date = new Date(timestamp);
148
+ return new Date(Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate(), 0, 0, 0, 0)).toISOString();
149
+ }
150
+ function addUtcDays(timestamp, days) {
151
+ const date = new Date(timestamp);
152
+ return new Date(date.getTime() + days * 24 * 60 * 60 * 1000).toISOString();
153
+ }
154
+ function startOfUtcWeekMonday(timestamp) {
155
+ const base = new Date(startOfUtcDay(timestamp));
156
+ const day = base.getUTCDay();
157
+ const delta = day === 0 ? -6 : 1 - day;
158
+ return addUtcDays(base.toISOString(), delta);
159
+ }
160
+ function startOfUtcMonth(timestamp) {
161
+ const date = new Date(timestamp);
162
+ return new Date(Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), 1, 0, 0, 0, 0)).toISOString();
163
+ }
164
+ function startOfNextUtcMonth(timestamp) {
165
+ const date = new Date(timestamp);
166
+ return new Date(Date.UTC(date.getUTCFullYear(), date.getUTCMonth() + 1, 1, 0, 0, 0, 0)).toISOString();
167
+ }
168
+ function maxTimestamp(left, right) {
169
+ return compareTimestampStrings(left, right) >= 0 ? left : right;
170
+ }
171
+ function toSortedCountRecord(values) {
172
+ return Object.fromEntries([...values.entries()].sort(([left], [right]) => left.localeCompare(right)));
173
+ }
174
+ function incrementCount(values, key) {
175
+ values.set(key, (values.get(key) ?? 0) + 1);
176
+ }
177
+ function formatRecurrenceRuleForSummary(rule) {
178
+ if (!rule) {
179
+ return null;
180
+ }
181
+ const parts = [`freq=${rule.freq}`];
182
+ if (rule.interval !== undefined) {
183
+ parts.push(`interval=${rule.interval}`);
184
+ }
185
+ if (rule.count !== undefined) {
186
+ parts.push(`count=${rule.count}`);
187
+ }
188
+ if (rule.until) {
189
+ parts.push(`until=${rule.until}`);
190
+ }
191
+ if (rule.by_weekday && rule.by_weekday.length > 0) {
192
+ const weekdays = [...rule.by_weekday].sort((left, right) => weekdayOrderIndex(left) - weekdayOrderIndex(right));
193
+ parts.push(`by_weekday=${weekdays.join("|")}`);
194
+ }
195
+ if (rule.by_month_day && rule.by_month_day.length > 0) {
196
+ const monthDays = [...rule.by_month_day].sort((left, right) => left - right);
197
+ parts.push(`by_month_day=${monthDays.join("|")}`);
198
+ }
199
+ if (rule.exdates && rule.exdates.length > 0) {
200
+ const exdates = [...rule.exdates].sort((left, right) => left.localeCompare(right));
201
+ parts.push(`exdates=${exdates.join("|")}`);
202
+ }
203
+ return parts.join(",");
204
+ }
205
+ function buildUtcTimestamp(year, month, day, timeSource) {
206
+ const candidate = new Date(Date.UTC(year, month, day, timeSource.getUTCHours(), timeSource.getUTCMinutes(), timeSource.getUTCSeconds(), timeSource.getUTCMilliseconds()));
207
+ if (candidate.getUTCFullYear() !== year || candidate.getUTCMonth() !== month || candidate.getUTCDate() !== day) {
208
+ return undefined;
209
+ }
210
+ return candidate.toISOString();
211
+ }
212
+ function weekdayToken(timestamp) {
213
+ return UTC_DAY_TO_WEEKDAY[new Date(timestamp).getUTCDay()];
214
+ }
215
+ function buildRecurringEventWindow(start, end, nowValue, lookbackDays, lookaheadDays) {
216
+ const windowStart = start ?? addUtcDays(nowValue, -(lookbackDays ?? DEFAULT_RECURRENCE_LOOKBACK_DAYS));
217
+ const windowEnd = end ?? addUtcDays(start ?? nowValue, lookaheadDays ?? DEFAULT_RECURRENCE_LOOKAHEAD_DAYS);
218
+ return {
219
+ start: windowStart,
220
+ end: compareTimestampStrings(windowEnd, windowStart) > 0 ? windowEnd : addUtcDays(windowStart, 1),
221
+ };
222
+ }
223
+ function expandRecurringOccurrences(startAt, recurrence, window, occurrenceLimit) {
224
+ const maxOccurrences = occurrenceLimit ?? MAX_RECURRENCE_OCCURRENCES;
225
+ const interval = recurrence.interval ?? 1;
226
+ const countLimit = recurrence.count ?? Number.POSITIVE_INFINITY;
227
+ const until = recurrence.until;
228
+ const excluded = new Set(recurrence.exdates ?? []);
229
+ const recurrenceWeekdays = recurrence.by_weekday && recurrence.by_weekday.length > 0 ? [...recurrence.by_weekday] : [weekdayToken(startAt)];
230
+ const recurrenceMonthDays = recurrence.by_month_day && recurrence.by_month_day.length > 0 ? [...recurrence.by_month_day] : [new Date(startAt).getUTCDate()];
231
+ const weekdayFilter = recurrence.by_weekday ? new Set(recurrence.by_weekday) : undefined;
232
+ const monthDayFilter = recurrence.by_month_day ? new Set(recurrence.by_month_day) : undefined;
233
+ const sortedWeekdays = [...new Set(recurrenceWeekdays)].sort((left, right) => weekdayOrderIndex(left) -
234
+ weekdayOrderIndex(right));
235
+ const sortedMonthDays = [...new Set(recurrenceMonthDays)].sort((left, right) => left - right);
236
+ const occurrences = [];
237
+ let produced = 0;
238
+ const consumeCandidate = (candidateAt) => {
239
+ if (compareTimestampStrings(candidateAt, startAt) < 0) {
240
+ return "continue";
241
+ }
242
+ if (until && compareTimestampStrings(candidateAt, until) > 0) {
243
+ return "stop";
244
+ }
245
+ if (compareTimestampStrings(candidateAt, window.end) >= 0) {
246
+ return "stop";
247
+ }
248
+ if (excluded.has(candidateAt)) {
249
+ return "continue";
250
+ }
251
+ produced += 1;
252
+ if (compareTimestampStrings(candidateAt, window.start) >= 0) {
253
+ occurrences.push(candidateAt);
254
+ }
255
+ if (produced >= countLimit) {
256
+ return "stop";
257
+ }
258
+ return "continue";
259
+ };
260
+ const startDate = new Date(startAt);
261
+ if (recurrence.freq === "daily") {
262
+ let candidateAt = startAt;
263
+ for (let iteration = 0; iteration < maxOccurrences; iteration += 1) {
264
+ const candidateWeekday = weekdayToken(candidateAt);
265
+ const candidateMonthDay = new Date(candidateAt).getUTCDate();
266
+ const weekdayMatches = !weekdayFilter || weekdayFilter.has(candidateWeekday);
267
+ const monthDayMatches = !monthDayFilter || monthDayFilter.has(candidateMonthDay);
268
+ if (weekdayMatches && monthDayMatches && consumeCandidate(candidateAt) === "stop") {
269
+ break;
270
+ }
271
+ candidateAt = addUtcDays(candidateAt, interval);
272
+ }
273
+ return occurrences;
274
+ }
275
+ if (recurrence.freq === "weekly") {
276
+ const weekStart = startOfUtcWeekMonday(startAt);
277
+ for (let step = 0; step < maxOccurrences; step += 1) {
278
+ const candidateWeekStart = addUtcDays(weekStart, step * interval * 7);
279
+ for (const weekday of sortedWeekdays) {
280
+ const dayOffset = weekdayOrderIndex(weekday);
281
+ const candidateDay = addUtcDays(candidateWeekStart, dayOffset);
282
+ const dayDate = new Date(candidateDay);
283
+ const candidateAt = new Date(Date.UTC(dayDate.getUTCFullYear(), dayDate.getUTCMonth(), dayDate.getUTCDate(), startDate.getUTCHours(), startDate.getUTCMinutes(), startDate.getUTCSeconds(), startDate.getUTCMilliseconds())).toISOString();
284
+ const candidateMonthDay = dayDate.getUTCDate();
285
+ if (monthDayFilter && !monthDayFilter.has(candidateMonthDay)) {
286
+ continue;
287
+ }
288
+ const consumed = consumeCandidate(candidateAt);
289
+ if (consumed === "stop") {
290
+ return occurrences;
291
+ }
292
+ }
293
+ }
294
+ return occurrences;
295
+ }
296
+ if (recurrence.freq === "monthly") {
297
+ const startYear = startDate.getUTCFullYear();
298
+ const startMonth = startDate.getUTCMonth();
299
+ for (let step = 0; step < maxOccurrences; step += 1) {
300
+ const monthAnchor = new Date(Date.UTC(startYear, startMonth + step * interval, 1, 0, 0, 0, 0));
301
+ const year = monthAnchor.getUTCFullYear();
302
+ const month = monthAnchor.getUTCMonth();
303
+ for (const monthDay of sortedMonthDays) {
304
+ const candidateAt = buildUtcTimestamp(year, month, monthDay, startDate);
305
+ if (!candidateAt) {
306
+ continue;
307
+ }
308
+ const candidateWeekday = weekdayToken(candidateAt);
309
+ if (weekdayFilter && !weekdayFilter.has(candidateWeekday)) {
310
+ continue;
311
+ }
312
+ const consumed = consumeCandidate(candidateAt);
313
+ if (consumed === "stop") {
314
+ return occurrences;
315
+ }
316
+ }
317
+ }
318
+ return occurrences;
319
+ }
320
+ const year = startDate.getUTCFullYear();
321
+ const month = startDate.getUTCMonth();
322
+ for (let step = 0; step < maxOccurrences; step += 1) {
323
+ const candidateYear = year + step * interval;
324
+ for (const monthDay of sortedMonthDays) {
325
+ const candidateAt = buildUtcTimestamp(candidateYear, month, monthDay, startDate);
326
+ if (!candidateAt) {
327
+ continue;
328
+ }
329
+ const candidateWeekday = weekdayToken(candidateAt);
330
+ if (weekdayFilter && !weekdayFilter.has(candidateWeekday)) {
331
+ continue;
332
+ }
333
+ const consumed = consumeCandidate(candidateAt);
334
+ if (consumed === "stop") {
335
+ return occurrences;
336
+ }
337
+ }
338
+ }
339
+ return occurrences;
340
+ }
341
+ function sortEvents(values) {
342
+ return [...values].sort((a, b) => {
343
+ const byAt = compareTimestampStrings(a.at, b.at);
344
+ if (byAt !== 0)
345
+ return byAt;
346
+ const byPriority = a.item_priority - b.item_priority;
347
+ if (byPriority !== 0)
348
+ return byPriority;
349
+ const byId = a.item_id.localeCompare(b.item_id);
350
+ if (byId !== 0)
351
+ return byId;
352
+ const byKind = a.kind.localeCompare(b.kind);
353
+ if (byKind !== 0)
354
+ return byKind;
355
+ const byEventTitle = String(a.event_title).localeCompare(String(b.event_title));
356
+ if (byEventTitle !== 0)
357
+ return byEventTitle;
358
+ return String(a.reminder_text).localeCompare(String(b.reminder_text));
359
+ });
360
+ }
361
+ function buildEventSeed(items, recurringWindow, includeSources, occurrenceLimit) {
362
+ const events = [];
363
+ for (const item of items) {
364
+ if (includeSources.has("deadlines") && item.deadline) {
365
+ events.push({
366
+ at: item.deadline,
367
+ date: toUtcDayKey(item.deadline),
368
+ kind: "deadline",
369
+ reminder_text: null,
370
+ event_title: null,
371
+ event_end: null,
372
+ event_location: null,
373
+ event_all_day: null,
374
+ event_timezone: null,
375
+ event_recurring: null,
376
+ event_recurrence_rule: null,
377
+ item_id: item.id,
378
+ item_title: item.title,
379
+ item_type: item.type,
380
+ item_status: item.status,
381
+ item_priority: item.priority,
382
+ item_assignee: item.assignee ?? null,
383
+ item_deadline: item.deadline,
384
+ item_tags: item.tags,
385
+ });
386
+ }
387
+ for (const reminder of includeSources.has("reminders") ? (item.reminders ?? []) : []) {
388
+ events.push({
389
+ at: reminder.at,
390
+ date: toUtcDayKey(reminder.at),
391
+ kind: "reminder",
392
+ reminder_text: reminder.text,
393
+ event_title: null,
394
+ event_end: null,
395
+ event_location: null,
396
+ event_all_day: null,
397
+ event_timezone: null,
398
+ event_recurring: null,
399
+ event_recurrence_rule: null,
400
+ item_id: item.id,
401
+ item_title: item.title,
402
+ item_type: item.type,
403
+ item_status: item.status,
404
+ item_priority: item.priority,
405
+ item_assignee: item.assignee ?? null,
406
+ item_deadline: item.deadline ?? null,
407
+ item_tags: item.tags,
408
+ });
409
+ }
410
+ for (const event of includeSources.has("events") ? (item.events ?? []) : []) {
411
+ const recurrenceRuleSummary = formatRecurrenceRuleForSummary(event.recurrence);
412
+ const recurringDurationMs = (() => {
413
+ if (!event.end_at) {
414
+ return null;
415
+ }
416
+ const duration = Date.parse(event.end_at) - Date.parse(event.start_at);
417
+ return Number.isFinite(duration) && duration > 0 ? duration : null;
418
+ })();
419
+ const occurrences = event.recurrence
420
+ ? expandRecurringOccurrences(event.start_at, event.recurrence, recurringWindow, occurrenceLimit)
421
+ : [event.start_at];
422
+ for (const occurrenceAt of occurrences) {
423
+ const occurrenceEnd = event.recurrence && recurringDurationMs !== null
424
+ ? new Date(new Date(occurrenceAt).getTime() + recurringDurationMs).toISOString()
425
+ : (event.end_at ?? null);
426
+ events.push({
427
+ at: occurrenceAt,
428
+ date: toUtcDayKey(occurrenceAt),
429
+ kind: "event",
430
+ reminder_text: event.description ?? null,
431
+ event_title: event.title ?? item.title,
432
+ event_end: occurrenceEnd,
433
+ event_location: event.location ?? null,
434
+ event_all_day: event.all_day ?? null,
435
+ event_timezone: event.timezone ?? null,
436
+ event_recurring: event.recurrence ? true : false,
437
+ event_recurrence_rule: recurrenceRuleSummary,
438
+ item_id: item.id,
439
+ item_title: item.title,
440
+ item_type: item.type,
441
+ item_status: item.status,
442
+ item_priority: item.priority,
443
+ item_assignee: item.assignee ?? null,
444
+ item_deadline: item.deadline ?? null,
445
+ item_tags: item.tags,
446
+ });
447
+ }
448
+ }
449
+ }
450
+ return sortEvents(events);
451
+ }
452
+ function filterItems(items, options, typeRegistry, statusRegistry, runtimeFieldFilters) {
453
+ const typeFilter = parseType(options.type, typeRegistry);
454
+ const tagFilter = options.tag?.trim().toLowerCase();
455
+ const priorityFilter = parsePriority(options.priority);
456
+ const statusFilter = parseStatus(options.status, statusRegistry);
457
+ const assigneeFilter = options.assignee?.trim();
458
+ const assigneeModeFilter = parseAssigneeFilter(options.assigneeFilter);
459
+ const sprintFilter = options.sprint?.trim();
460
+ const releaseFilter = options.release?.trim();
461
+ if (assigneeFilter && (assigneeFilter.toLowerCase() === "none" || assigneeFilter.toLowerCase() === "null")) {
462
+ throw new PmCliError('--assignee no longer accepts "none" or "null". Use --assignee-filter unassigned.', EXIT_CODE.USAGE);
463
+ }
464
+ if (assigneeFilter !== undefined && assigneeModeFilter === "unassigned") {
465
+ throw new PmCliError("Cannot combine --assignee with --assignee-filter unassigned", EXIT_CODE.USAGE);
466
+ }
467
+ return items.filter((item) => {
468
+ if (typeFilter && item.type !== typeFilter)
469
+ return false;
470
+ if (tagFilter && !item.tags.some((tag) => tag.trim().toLowerCase() === tagFilter))
471
+ return false;
472
+ if (priorityFilter !== undefined && item.priority !== priorityFilter)
473
+ return false;
474
+ if (statusFilter && item.status !== statusFilter)
475
+ return false;
476
+ if (assigneeModeFilter === "assigned" && !item.assignee)
477
+ return false;
478
+ if (assigneeModeFilter === "unassigned" && item.assignee)
479
+ return false;
480
+ if (assigneeFilter !== undefined && item.assignee !== assigneeFilter) {
481
+ return false;
482
+ }
483
+ if (sprintFilter !== undefined && item.sprint !== sprintFilter)
484
+ return false;
485
+ if (releaseFilter !== undefined && item.release !== releaseFilter)
486
+ return false;
487
+ if (!matchesRuntimeFilters(item, runtimeFieldFilters))
488
+ return false;
489
+ return true;
490
+ });
491
+ }
492
+ function includeEventInWindow(event, start, end) {
493
+ if (start && compareTimestampStrings(event.at, start) < 0) {
494
+ return false;
495
+ }
496
+ if (end && compareTimestampStrings(event.at, end) >= 0) {
497
+ return false;
498
+ }
499
+ return true;
500
+ }
501
+ function resolveCalendarBoundaryInput(raw, nowValue, fieldLabel) {
502
+ if (raw.trim().toLowerCase() === "today") {
503
+ return startOfUtcDay(nowValue);
504
+ }
505
+ return resolveIsoOrRelative(raw, new Date(nowValue), fieldLabel);
506
+ }
507
+ function buildRange(view, options, nowValue) {
508
+ const anchor = options.date ? resolveCalendarBoundaryInput(options.date, nowValue, "--date") : nowValue;
509
+ const includePast = options.past === true;
510
+ const fullPeriodRequested = options.fullPeriod === true;
511
+ const from = options.from ? resolveCalendarBoundaryInput(options.from, nowValue, "--from") : undefined;
512
+ const to = options.to ? resolveCalendarBoundaryInput(options.to, nowValue, "--to") : undefined;
513
+ if (from && to && compareTimestampStrings(from, to) >= 0) {
514
+ throw new PmCliError("Calendar --from must be before --to", EXIT_CODE.USAGE);
515
+ }
516
+ if (view !== "agenda" && (options.from !== undefined || options.to !== undefined)) {
517
+ throw new PmCliError("--from and --to are only supported for --view agenda", EXIT_CODE.USAGE);
518
+ }
519
+ if (view === "agenda") {
520
+ if (fullPeriodRequested) {
521
+ throw new PmCliError("--full-period is only supported for --view day|week|month. For agenda windows, use --from and --to.", EXIT_CODE.USAGE);
522
+ }
523
+ const start = from ?? (options.date ? anchor : includePast ? undefined : nowValue);
524
+ return {
525
+ anchor,
526
+ start,
527
+ end: to,
528
+ fullPeriod: false,
529
+ };
530
+ }
531
+ if (view === "day") {
532
+ const dayStart = startOfUtcDay(anchor);
533
+ const fullPeriod = includePast || fullPeriodRequested;
534
+ const start = fullPeriod ? dayStart : maxTimestamp(dayStart, nowValue);
535
+ return {
536
+ anchor,
537
+ start,
538
+ end: addUtcDays(dayStart, 1),
539
+ periodStart: dayStart,
540
+ periodEnd: addUtcDays(dayStart, 1),
541
+ fullPeriod,
542
+ };
543
+ }
544
+ if (view === "week") {
545
+ const weekStart = startOfUtcWeekMonday(anchor);
546
+ const fullPeriod = includePast || fullPeriodRequested;
547
+ const start = fullPeriod ? weekStart : maxTimestamp(weekStart, nowValue);
548
+ return {
549
+ anchor,
550
+ start,
551
+ end: addUtcDays(weekStart, 7),
552
+ periodStart: weekStart,
553
+ periodEnd: addUtcDays(weekStart, 7),
554
+ fullPeriod,
555
+ };
556
+ }
557
+ const monthStart = startOfUtcMonth(anchor);
558
+ const monthEnd = startOfNextUtcMonth(anchor);
559
+ const fullPeriod = includePast || fullPeriodRequested;
560
+ const start = fullPeriod ? monthStart : maxTimestamp(monthStart, nowValue);
561
+ return {
562
+ anchor,
563
+ start,
564
+ end: monthEnd,
565
+ periodStart: monthStart,
566
+ periodEnd: monthEnd,
567
+ fullPeriod,
568
+ };
569
+ }
570
+ function bucketEventsByDay(events) {
571
+ const dayMap = new Map();
572
+ for (const event of events) {
573
+ const list = dayMap.get(event.date) ?? [];
574
+ list.push(event);
575
+ dayMap.set(event.date, list);
576
+ }
577
+ return [...dayMap.entries()]
578
+ .sort(([left], [right]) => left.localeCompare(right))
579
+ .map(([date, dayEvents]) => ({
580
+ date,
581
+ count: dayEvents.length,
582
+ events: sortEvents(dayEvents),
583
+ }));
584
+ }
585
+ function summarize(events) {
586
+ const itemIds = new Set();
587
+ const byType = new Map();
588
+ const byStatus = new Map();
589
+ let deadlines = 0;
590
+ let reminders = 0;
591
+ let scheduled = 0;
592
+ let recurringEvents = 0;
593
+ for (const event of events) {
594
+ itemIds.add(event.item_id);
595
+ incrementCount(byType, event.item_type);
596
+ incrementCount(byStatus, event.item_status);
597
+ if (event.kind === "deadline") {
598
+ deadlines += 1;
599
+ }
600
+ else if (event.kind === "reminder") {
601
+ reminders += 1;
602
+ }
603
+ else {
604
+ scheduled += 1;
605
+ if (event.event_recurring === true) {
606
+ recurringEvents += 1;
607
+ }
608
+ }
609
+ }
610
+ return {
611
+ events: events.length,
612
+ items: itemIds.size,
613
+ deadlines,
614
+ reminders,
615
+ scheduled,
616
+ by_kind: {
617
+ deadline: deadlines,
618
+ reminder: reminders,
619
+ event: scheduled,
620
+ },
621
+ by_type: toSortedCountRecord(byType),
622
+ by_status: toSortedCountRecord(byStatus),
623
+ recurring_events: recurringEvents,
624
+ };
625
+ }
626
+ function formatWindow(range) {
627
+ const start = range.start ?? "unbounded";
628
+ const end = range.end ?? "unbounded";
629
+ return `${start} -> ${end}`;
630
+ }
631
+ function formatClock(timestamp) {
632
+ return `${new Date(timestamp).toISOString().slice(11, 16)}Z`;
633
+ }
634
+ function formatSummaryCountRecord(values) {
635
+ const entries = Object.entries(values);
636
+ if (entries.length === 0) {
637
+ return "empty";
638
+ }
639
+ return entries.map(([key, value]) => `${key}=${value}`).join(", ");
640
+ }
641
+ function formatEventEnd(event) {
642
+ if (!event.event_end) {
643
+ return null;
644
+ }
645
+ if (event.event_all_day === true) {
646
+ return event.event_end.slice(0, 10);
647
+ }
648
+ if (event.date === toUtcDayKey(event.event_end)) {
649
+ return formatClock(event.event_end);
650
+ }
651
+ return event.event_end;
652
+ }
653
+ function formatEventLine(event) {
654
+ const core = `${formatClock(event.at)} [${event.kind}] ${event.item_id} type=${event.item_type} p${event.item_priority} ${event.item_status} ${event.item_title}`;
655
+ if (event.kind === "reminder") {
656
+ const reminderText = event.reminder_text ?? "";
657
+ return `${core} — ${reminderText} reminder=${JSON.stringify(reminderText)}`;
658
+ }
659
+ if (event.kind === "event") {
660
+ const details = [];
661
+ const title = event.event_title ?? event.item_title;
662
+ details.push(title);
663
+ details.push(`title=${JSON.stringify(title)}`);
664
+ if (event.event_recurring) {
665
+ details.push("(recurring)");
666
+ details.push("recurring=true");
667
+ }
668
+ const end = formatEventEnd(event);
669
+ if (end) {
670
+ details.push(`end=${end}`);
671
+ }
672
+ if (event.event_all_day !== null) {
673
+ details.push(`all_day=${event.event_all_day ? "true" : "false"}`);
674
+ }
675
+ if (event.event_timezone) {
676
+ details.push(`timezone=${event.event_timezone}`);
677
+ }
678
+ if (event.event_location) {
679
+ details.push(`@ ${event.event_location}`);
680
+ details.push(`location=${JSON.stringify(event.event_location)}`);
681
+ }
682
+ if (event.event_recurrence_rule) {
683
+ details.push(`recurrence=${event.event_recurrence_rule}`);
684
+ }
685
+ if (event.reminder_text) {
686
+ details.push(`description=${JSON.stringify(event.reminder_text)}`);
687
+ }
688
+ return `${core} — ${details.join(" ")}`;
689
+ }
690
+ return core;
691
+ }
692
+ export function renderCalendarMarkdown(result) {
693
+ const lines = [];
694
+ lines.push(`# pm calendar (${result.view})`);
695
+ lines.push("");
696
+ lines.push(`- now: ${result.now}`);
697
+ lines.push(`- window: ${formatWindow(result.range)}`);
698
+ if (result.range.period_start && result.range.period_end) {
699
+ lines.push(`- period: ${result.range.period_start} -> ${result.range.period_end}`);
700
+ lines.push(`- period-mode: ${result.range.full_period ? "full-period" : "active-window"}`);
701
+ }
702
+ lines.push(`- events: ${result.summary.events} (deadlines: ${result.summary.deadlines}, reminders: ${result.summary.reminders}, scheduled: ${result.summary.scheduled})`);
703
+ lines.push(`- items: ${result.summary.items}`);
704
+ lines.push(`- by-kind: ${formatSummaryCountRecord(result.summary.by_kind)}`);
705
+ lines.push(`- by-type: ${formatSummaryCountRecord(result.summary.by_type)}`);
706
+ lines.push(`- by-status: ${formatSummaryCountRecord(result.summary.by_status)}`);
707
+ lines.push(`- recurring-events: ${result.summary.recurring_events}`);
708
+ lines.push("");
709
+ if (result.events.length === 0) {
710
+ lines.push("No calendar events matched the selected filters.");
711
+ return lines.join("\n");
712
+ }
713
+ for (const day of result.days) {
714
+ lines.push(`## ${day.date} (${day.count})`);
715
+ for (const event of day.events) {
716
+ lines.push(`- ${formatEventLine(event)}`);
717
+ }
718
+ lines.push("");
719
+ }
720
+ if (result.warnings && result.warnings.length > 0) {
721
+ lines.push("### warnings");
722
+ for (const warning of result.warnings) {
723
+ lines.push(`- ${warning}`);
724
+ }
725
+ lines.push("");
726
+ }
727
+ return lines.join("\n").replace(/\n+$/, "");
728
+ }
729
+ export async function runCalendar(options, global) {
730
+ const pmRoot = resolvePmRoot(process.cwd(), global.path);
731
+ if (!(await pathExists(getSettingsPath(pmRoot)))) {
732
+ throw new PmCliError(`Tracker is not initialized at ${pmRoot}. Run pm init first.`, EXIT_CODE.NOT_FOUND);
733
+ }
734
+ const nowValue = nowIso();
735
+ const view = parseView(options.view);
736
+ const rangeBounds = buildRange(view, options, nowValue);
737
+ const limit = parseLimit(options.limit);
738
+ const includeSources = parseIncludeSources(options.include);
739
+ const recurrenceLookaheadDays = parseNonNegativeInteger(options.recurrenceLookaheadDays, "Calendar recurrence lookahead days");
740
+ const recurrenceLookbackDays = parseNonNegativeInteger(options.recurrenceLookbackDays, "Calendar recurrence lookback days");
741
+ const occurrenceLimit = parseNonNegativeInteger(options.occurrenceLimit, "Calendar occurrence limit");
742
+ if (occurrenceLimit !== undefined && occurrenceLimit < 1) {
743
+ throw new PmCliError("Calendar occurrence limit must be >= 1", EXIT_CODE.USAGE);
744
+ }
745
+ const settings = await readSettings(pmRoot);
746
+ const statusRegistry = resolveRuntimeStatusRegistry(settings.schema);
747
+ const runtimeFieldRegistry = resolveRuntimeFieldRegistry(settings.schema);
748
+ const runtimeFieldFilters = collectRuntimeFilterValues(options, runtimeFieldRegistry, "calendar");
749
+ const typeRegistry = resolveItemTypeRegistry(settings, getActiveExtensionRegistrations());
750
+ const listWarnings = [];
751
+ const items = await listAllFrontMatter(pmRoot, settings.item_format, typeRegistry.type_to_folder, listWarnings, settings.schema);
752
+ const filteredItems = filterItems(items, options, typeRegistry, statusRegistry, runtimeFieldFilters);
753
+ const recurringWindow = buildRecurringEventWindow(rangeBounds.start, rangeBounds.end, nowValue, recurrenceLookbackDays, recurrenceLookaheadDays);
754
+ const seededEvents = buildEventSeed(filteredItems, recurringWindow, includeSources, occurrenceLimit);
755
+ const rangedEvents = seededEvents.filter((event) => includeEventInWindow(event, rangeBounds.start, rangeBounds.end));
756
+ const limitedEvents = limit === undefined ? rangedEvents : rangedEvents.slice(0, limit);
757
+ const days = bucketEventsByDay(limitedEvents);
758
+ const warnings = [...new Set(listWarnings)].sort((left, right) => left.localeCompare(right));
759
+ return {
760
+ view,
761
+ output_default: "markdown",
762
+ now: nowValue,
763
+ anchor: rangeBounds.anchor,
764
+ range: {
765
+ start: rangeBounds.start ?? null,
766
+ end: rangeBounds.end ?? null,
767
+ period_start: rangeBounds.periodStart ?? null,
768
+ period_end: rangeBounds.periodEnd ?? null,
769
+ full_period: rangeBounds.fullPeriod,
770
+ past: options.past === true,
771
+ from: options.from ?? null,
772
+ to: options.to ?? null,
773
+ },
774
+ filters: {
775
+ type: options.type ?? null,
776
+ tag: options.tag ?? null,
777
+ priority: options.priority ?? null,
778
+ status: options.status ?? null,
779
+ assignee: options.assignee ?? null,
780
+ assignee_filter: options.assigneeFilter ?? null,
781
+ sprint: options.sprint ?? null,
782
+ release: options.release ?? null,
783
+ runtime_filters: runtimeFieldFilters,
784
+ limit: options.limit ?? null,
785
+ include: options.include ?? null,
786
+ full_period: options.fullPeriod === true ? "true" : null,
787
+ recurrence_lookahead_days: options.recurrenceLookaheadDays ?? null,
788
+ recurrence_lookback_days: options.recurrenceLookbackDays ?? null,
789
+ occurrence_limit: options.occurrenceLimit ?? null,
790
+ },
791
+ summary: summarize(limitedEvents),
792
+ events: limitedEvents,
793
+ days,
794
+ ...(warnings.length > 0 ? { warnings } : {}),
795
+ };
796
+ }
797
+ //# sourceMappingURL=calendar.js.map