panopticon-cli 0.4.32 → 0.5.0

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 (142) hide show
  1. package/README.md +96 -210
  2. package/dist/{agents-BDFHF4T3.js → agents-E43Y3HNU.js} +10 -7
  3. package/dist/chunk-7SN4L4PH.js +150 -0
  4. package/dist/chunk-7SN4L4PH.js.map +1 -0
  5. package/dist/{chunk-2NIAOCIC.js → chunk-AAFQANKW.js} +358 -97
  6. package/dist/chunk-AAFQANKW.js.map +1 -0
  7. package/dist/chunk-AQXETQHW.js +113 -0
  8. package/dist/chunk-AQXETQHW.js.map +1 -0
  9. package/dist/chunk-B3PF6JPQ.js +212 -0
  10. package/dist/chunk-B3PF6JPQ.js.map +1 -0
  11. package/dist/chunk-CFCUOV3Q.js +669 -0
  12. package/dist/chunk-CFCUOV3Q.js.map +1 -0
  13. package/dist/chunk-CWELWPWQ.js +32 -0
  14. package/dist/chunk-CWELWPWQ.js.map +1 -0
  15. package/dist/chunk-DI7ABPNQ.js +352 -0
  16. package/dist/chunk-DI7ABPNQ.js.map +1 -0
  17. package/dist/{chunk-VU4FLXV5.js → chunk-FQ66DECN.js} +31 -4
  18. package/dist/chunk-FQ66DECN.js.map +1 -0
  19. package/dist/{chunk-VIWUCJ4V.js → chunk-FTCPTHIJ.js} +57 -432
  20. package/dist/chunk-FTCPTHIJ.js.map +1 -0
  21. package/dist/{review-status-GWQYY77L.js → chunk-GFP3PIPB.js} +14 -7
  22. package/dist/chunk-GFP3PIPB.js.map +1 -0
  23. package/dist/chunk-GR6ZZMCX.js +816 -0
  24. package/dist/chunk-GR6ZZMCX.js.map +1 -0
  25. package/dist/chunk-HJSM6E6U.js +1038 -0
  26. package/dist/chunk-HJSM6E6U.js.map +1 -0
  27. package/dist/{chunk-XP2DXWYP.js → chunk-HZT2AOPN.js} +164 -39
  28. package/dist/chunk-HZT2AOPN.js.map +1 -0
  29. package/dist/chunk-JQBV3Q2W.js +29 -0
  30. package/dist/chunk-JQBV3Q2W.js.map +1 -0
  31. package/dist/{chunk-BWGFN44T.js → chunk-JT4O4YVM.js} +28 -16
  32. package/dist/chunk-JT4O4YVM.js.map +1 -0
  33. package/dist/chunk-NTO3EDB3.js +600 -0
  34. package/dist/chunk-NTO3EDB3.js.map +1 -0
  35. package/dist/{chunk-JY7R7V4G.js → chunk-OMNXYPXC.js} +2 -2
  36. package/dist/chunk-OMNXYPXC.js.map +1 -0
  37. package/dist/chunk-PELXV435.js +215 -0
  38. package/dist/chunk-PELXV435.js.map +1 -0
  39. package/dist/chunk-PPRFKTVC.js +154 -0
  40. package/dist/chunk-PPRFKTVC.js.map +1 -0
  41. package/dist/chunk-WQG2TYCB.js +677 -0
  42. package/dist/chunk-WQG2TYCB.js.map +1 -0
  43. package/dist/{chunk-HCTJFIJJ.js → chunk-YLPSQAM2.js} +2 -2
  44. package/dist/{chunk-HCTJFIJJ.js.map → chunk-YLPSQAM2.js.map} +1 -1
  45. package/dist/{chunk-6HXKTOD7.js → chunk-ZTFNYOC7.js} +53 -38
  46. package/dist/chunk-ZTFNYOC7.js.map +1 -0
  47. package/dist/cli/index.js +5103 -3165
  48. package/dist/cli/index.js.map +1 -1
  49. package/dist/{config-BOAMSKTF.js → config-4CJNUE3O.js} +7 -3
  50. package/dist/dashboard/prompts/merge-agent.md +217 -0
  51. package/dist/dashboard/prompts/review-agent.md +409 -0
  52. package/dist/dashboard/prompts/sync-main.md +84 -0
  53. package/dist/dashboard/prompts/test-agent.md +283 -0
  54. package/dist/dashboard/prompts/work-agent.md +249 -0
  55. package/dist/dashboard/public/assets/index-BxpjweAL.css +32 -0
  56. package/dist/dashboard/public/assets/index-DQHkwvvJ.js +743 -0
  57. package/dist/dashboard/public/index.html +2 -2
  58. package/dist/dashboard/server.js +17619 -4044
  59. package/dist/{dns-L3L2BB27.js → dns-7BDJSD3E.js} +4 -2
  60. package/dist/{feedback-writer-AAKF5BTK.js → feedback-writer-LVZ5TFYZ.js} +8 -4
  61. package/dist/feedback-writer-LVZ5TFYZ.js.map +1 -0
  62. package/dist/hume-WMAUBBV2.js +13 -0
  63. package/dist/index.d.ts +162 -40
  64. package/dist/index.js +67 -23
  65. package/dist/index.js.map +1 -1
  66. package/dist/{projects-VXRUCMLM.js → projects-JEIVIYC6.js} +3 -3
  67. package/dist/rally-RKFSWC7E.js +10 -0
  68. package/dist/{remote-agents-Z3R2A5BN.js → remote-agents-TFSMW7GN.js} +2 -2
  69. package/dist/{remote-workspace-2G6V2KNP.js → remote-workspace-AHVHQEES.js} +8 -8
  70. package/dist/review-status-EPFG4XM7.js +19 -0
  71. package/dist/shadow-state-5MDP6YXH.js +30 -0
  72. package/dist/shadow-state-5MDP6YXH.js.map +1 -0
  73. package/dist/{specialist-context-N32QBNNQ.js → specialist-context-ZC6A4M3I.js} +8 -7
  74. package/dist/{specialist-context-N32QBNNQ.js.map → specialist-context-ZC6A4M3I.js.map} +1 -1
  75. package/dist/{specialist-logs-GF3YV4KL.js → specialist-logs-KLGJCEUL.js} +7 -6
  76. package/dist/specialist-logs-KLGJCEUL.js.map +1 -0
  77. package/dist/{specialists-JBIW6MP4.js → specialists-O4HWDJL5.js} +7 -6
  78. package/dist/specialists-O4HWDJL5.js.map +1 -0
  79. package/dist/tldr-daemon-T3THOUGT.js +21 -0
  80. package/dist/tldr-daemon-T3THOUGT.js.map +1 -0
  81. package/dist/traefik-QN7R5I6V.js +19 -0
  82. package/dist/traefik-QN7R5I6V.js.map +1 -0
  83. package/dist/tunnel-W2GZBLEV.js +13 -0
  84. package/dist/tunnel-W2GZBLEV.js.map +1 -0
  85. package/dist/workspace-manager-IE4JL2JP.js +22 -0
  86. package/dist/workspace-manager-IE4JL2JP.js.map +1 -0
  87. package/package.json +2 -2
  88. package/scripts/heartbeat-hook +37 -10
  89. package/scripts/patches/llm-tldr-tsx-support.py +109 -0
  90. package/scripts/pre-tool-hook +26 -15
  91. package/scripts/record-cost-event.js +177 -43
  92. package/scripts/record-cost-event.ts +87 -3
  93. package/scripts/statusline.sh +169 -0
  94. package/scripts/stop-hook +21 -11
  95. package/scripts/tldr-post-edit +72 -0
  96. package/scripts/tldr-read-enforcer +275 -0
  97. package/scripts/work-agent-stop-hook +137 -0
  98. package/skills/check-merged/SKILL.md +143 -0
  99. package/skills/crash-investigation/SKILL.md +301 -0
  100. package/skills/github-cli/SKILL.md +185 -0
  101. package/skills/myn-standards/SKILL.md +351 -0
  102. package/skills/pan-reopen/SKILL.md +65 -0
  103. package/skills/pan-sync-main/SKILL.md +87 -0
  104. package/skills/pan-tldr/SKILL.md +149 -0
  105. package/skills/react-best-practices/SKILL.md +125 -0
  106. package/skills/spec-readiness/REPORT-TEMPLATE.md +158 -0
  107. package/skills/spec-readiness/SCORING-REFERENCE.md +369 -0
  108. package/skills/spec-readiness/SKILL.md +400 -0
  109. package/skills/spec-readiness-setup/SKILL.md +361 -0
  110. package/skills/workspace-status/SKILL.md +56 -0
  111. package/skills/write-spec/SKILL.md +138 -0
  112. package/templates/traefik/dynamic/panopticon.yml.template +0 -5
  113. package/templates/traefik/traefik.yml +0 -8
  114. package/dist/chunk-2NIAOCIC.js.map +0 -1
  115. package/dist/chunk-3XAB4IXF.js +0 -51
  116. package/dist/chunk-3XAB4IXF.js.map +0 -1
  117. package/dist/chunk-6HXKTOD7.js.map +0 -1
  118. package/dist/chunk-BBCUK6N2.js +0 -241
  119. package/dist/chunk-BBCUK6N2.js.map +0 -1
  120. package/dist/chunk-BWGFN44T.js.map +0 -1
  121. package/dist/chunk-ELK6Q7QI.js +0 -545
  122. package/dist/chunk-ELK6Q7QI.js.map +0 -1
  123. package/dist/chunk-JY7R7V4G.js.map +0 -1
  124. package/dist/chunk-LYSBSZYV.js +0 -1523
  125. package/dist/chunk-LYSBSZYV.js.map +0 -1
  126. package/dist/chunk-VIWUCJ4V.js.map +0 -1
  127. package/dist/chunk-VU4FLXV5.js.map +0 -1
  128. package/dist/chunk-XP2DXWYP.js.map +0 -1
  129. package/dist/dashboard/public/assets/index-C7X6LP5Z.css +0 -32
  130. package/dist/dashboard/public/assets/index-ClYqpcAJ.js +0 -645
  131. package/dist/feedback-writer-AAKF5BTK.js.map +0 -1
  132. package/dist/review-status-GWQYY77L.js.map +0 -1
  133. package/dist/traefik-CUJM6K5Z.js +0 -12
  134. /package/dist/{agents-BDFHF4T3.js.map → agents-E43Y3HNU.js.map} +0 -0
  135. /package/dist/{config-BOAMSKTF.js.map → config-4CJNUE3O.js.map} +0 -0
  136. /package/dist/{dns-L3L2BB27.js.map → dns-7BDJSD3E.js.map} +0 -0
  137. /package/dist/{projects-VXRUCMLM.js.map → hume-WMAUBBV2.js.map} +0 -0
  138. /package/dist/{remote-agents-Z3R2A5BN.js.map → projects-JEIVIYC6.js.map} +0 -0
  139. /package/dist/{specialist-logs-GF3YV4KL.js.map → rally-RKFSWC7E.js.map} +0 -0
  140. /package/dist/{specialists-JBIW6MP4.js.map → remote-agents-TFSMW7GN.js.map} +0 -0
  141. /package/dist/{remote-workspace-2G6V2KNP.js.map → remote-workspace-AHVHQEES.js.map} +0 -0
  142. /package/dist/{traefik-CUJM6K5Z.js.map → review-status-EPFG4XM7.js.map} +0 -0
@@ -0,0 +1,669 @@
1
+ import {
2
+ __esm,
3
+ init_esm_shims
4
+ } from "./chunk-ZHC57RCV.js";
5
+
6
+ // src/lib/tracker/rally-api.ts
7
+ var RallyRestApi;
8
+ var init_rally_api = __esm({
9
+ "src/lib/tracker/rally-api.ts"() {
10
+ "use strict";
11
+ init_esm_shims();
12
+ RallyRestApi = class {
13
+ apiKey;
14
+ server;
15
+ customHeaders;
16
+ constructor(config) {
17
+ this.apiKey = config.apiKey;
18
+ this.server = config.server || "https://rally1.rallydev.com";
19
+ this.customHeaders = config.requestOptions?.headers || {};
20
+ }
21
+ /**
22
+ * Query Rally artifacts
23
+ */
24
+ async query(config) {
25
+ const params = new URLSearchParams();
26
+ if (config.query) {
27
+ params.set("query", config.query);
28
+ }
29
+ if (config.fetch && config.fetch.length > 0) {
30
+ params.set("fetch", config.fetch.join(","));
31
+ }
32
+ if (config.limit !== void 0) {
33
+ params.set("pagesize", String(config.limit));
34
+ }
35
+ if (config.workspace) {
36
+ params.set("workspace", config.workspace);
37
+ }
38
+ if (config.project) {
39
+ params.set("project", config.project);
40
+ if (config.projectScopeDown) {
41
+ params.set("projectScopeDown", "true");
42
+ }
43
+ }
44
+ if (config.order) {
45
+ params.set("order", config.order);
46
+ }
47
+ const url = `${this.server}/slm/webservice/v2.0/${config.type}?${params.toString()}`;
48
+ const response = await fetch(url, {
49
+ method: "GET",
50
+ headers: {
51
+ "ZSESSIONID": this.apiKey,
52
+ "Content-Type": "application/json",
53
+ ...this.customHeaders
54
+ }
55
+ });
56
+ if (!response.ok) {
57
+ if (response.status === 401) {
58
+ throw new Error("Unauthorized: Invalid API key or insufficient permissions");
59
+ }
60
+ throw new Error(`Rally API query failed: ${response.status} ${response.statusText}`);
61
+ }
62
+ const result = await response.json();
63
+ if (result.QueryResult.Errors && result.QueryResult.Errors.length > 0) {
64
+ const errorDetail = result.QueryResult.Errors.join(", ");
65
+ const queryDetail = config.query ? ` (Query: ${config.query})` : "";
66
+ if (process.env.DEBUG?.includes("rally")) {
67
+ console.error("[Rally WSAPI] Query failed:", { query: config.query, errors: result.QueryResult.Errors });
68
+ }
69
+ throw new Error(`Rally API query failed: ${errorDetail}${queryDetail}`);
70
+ }
71
+ return result;
72
+ }
73
+ /**
74
+ * Create a Rally object
75
+ */
76
+ async create(config) {
77
+ const url = `${this.server}/slm/webservice/v2.0/${config.type}/create`;
78
+ const body = {
79
+ [config.type]: config.data
80
+ };
81
+ const params = new URLSearchParams();
82
+ if (config.fetch && config.fetch.length > 0) {
83
+ params.set("fetch", config.fetch.join(","));
84
+ }
85
+ const finalUrl = params.toString() ? `${url}?${params.toString()}` : url;
86
+ const response = await fetch(finalUrl, {
87
+ method: "POST",
88
+ headers: {
89
+ "ZSESSIONID": this.apiKey,
90
+ "Content-Type": "application/json",
91
+ ...this.customHeaders
92
+ },
93
+ body: JSON.stringify(body)
94
+ });
95
+ if (!response.ok) {
96
+ throw new Error(`Rally API create failed: ${response.status} ${response.statusText}`);
97
+ }
98
+ const result = await response.json();
99
+ if (result.CreateResult.Errors && result.CreateResult.Errors.length > 0) {
100
+ throw new Error(`Rally API create failed: ${result.CreateResult.Errors.join(", ")}`);
101
+ }
102
+ return result;
103
+ }
104
+ /**
105
+ * Update a Rally object
106
+ */
107
+ async update(config) {
108
+ const objectId = config.ref.split("/").pop();
109
+ const url = `${this.server}/slm/webservice/v2.0/${config.type}/${objectId}`;
110
+ const body = {
111
+ [config.type]: config.data
112
+ };
113
+ const params = new URLSearchParams();
114
+ if (config.fetch && config.fetch.length > 0) {
115
+ params.set("fetch", config.fetch.join(","));
116
+ }
117
+ const finalUrl = params.toString() ? `${url}?${params.toString()}` : url;
118
+ const response = await fetch(finalUrl, {
119
+ method: "POST",
120
+ headers: {
121
+ "ZSESSIONID": this.apiKey,
122
+ "Content-Type": "application/json",
123
+ ...this.customHeaders
124
+ },
125
+ body: JSON.stringify(body)
126
+ });
127
+ if (!response.ok) {
128
+ throw new Error(`Rally API update failed: ${response.status} ${response.statusText}`);
129
+ }
130
+ const result = await response.json();
131
+ if (result.OperationResult.Errors && result.OperationResult.Errors.length > 0) {
132
+ throw new Error(`Rally API update failed: ${result.OperationResult.Errors.join(", ")}`);
133
+ }
134
+ return result;
135
+ }
136
+ };
137
+ }
138
+ });
139
+
140
+ // src/lib/tracker/interface.ts
141
+ var NotImplementedError, IssueNotFoundError, TrackerAuthError;
142
+ var init_interface = __esm({
143
+ "src/lib/tracker/interface.ts"() {
144
+ "use strict";
145
+ init_esm_shims();
146
+ NotImplementedError = class extends Error {
147
+ constructor(feature) {
148
+ super(`Not implemented: ${feature}`);
149
+ this.name = "NotImplementedError";
150
+ }
151
+ };
152
+ IssueNotFoundError = class extends Error {
153
+ constructor(id, tracker) {
154
+ super(`Issue not found: ${id} (tracker: ${tracker})`);
155
+ this.name = "IssueNotFoundError";
156
+ }
157
+ };
158
+ TrackerAuthError = class extends Error {
159
+ constructor(tracker, message) {
160
+ super(`Authentication failed for ${tracker}: ${message}`);
161
+ this.name = "TrackerAuthError";
162
+ }
163
+ };
164
+ }
165
+ });
166
+
167
+ // src/lib/tracker/rally.ts
168
+ var STATE_MAP, QUERYABLE_TYPES, FETCH_FIELDS, PRIORITY_MAP, REVERSE_PRIORITY_MAP, RallyTracker;
169
+ var init_rally = __esm({
170
+ "src/lib/tracker/rally.ts"() {
171
+ init_esm_shims();
172
+ init_rally_api();
173
+ init_interface();
174
+ STATE_MAP = {
175
+ // User Stories (ScheduleState)
176
+ "New": "open",
177
+ "Idea": "open",
178
+ "Defined": "open",
179
+ "In-Progress": "in_progress",
180
+ "Completed": "closed",
181
+ "Accepted": "closed",
182
+ // Defects (State)
183
+ "Submitted": "open",
184
+ "Open": "in_progress",
185
+ // "Open" defects are actively being worked
186
+ "Fixed": "closed",
187
+ "Closed": "closed",
188
+ // Features / PortfolioItems (State)
189
+ "Discovering": "open",
190
+ "Developing": "in_progress",
191
+ "Done": "closed"
192
+ };
193
+ QUERYABLE_TYPES = [
194
+ { type: "hierarchicalrequirement", stateField: "ScheduleState", closedStates: ["Completed", "Accepted"] },
195
+ { type: "defect", stateField: "State", closedStates: ["Closed"] },
196
+ { type: "task", stateField: "State", closedStates: ["Completed"] },
197
+ { type: "portfolioitem/feature", stateField: "State", closedStates: ["Done"] }
198
+ ];
199
+ FETCH_FIELDS = [
200
+ "ObjectID",
201
+ "FormattedID",
202
+ "Name",
203
+ "Description",
204
+ "ScheduleState",
205
+ "State",
206
+ "Tags",
207
+ "Owner",
208
+ "Priority",
209
+ "DueDate",
210
+ "CreationDate",
211
+ "LastUpdateDate",
212
+ "Parent",
213
+ "PortfolioItem",
214
+ "_type"
215
+ ];
216
+ PRIORITY_MAP = {
217
+ "Resolve Immediately": 0,
218
+ High: 1,
219
+ Normal: 2,
220
+ Low: 3
221
+ };
222
+ REVERSE_PRIORITY_MAP = {
223
+ 0: "Resolve Immediately",
224
+ 1: "High",
225
+ 2: "Normal",
226
+ 3: "Low",
227
+ 4: "Low"
228
+ };
229
+ RallyTracker = class {
230
+ name = "rally";
231
+ restApi;
232
+ workspace;
233
+ project;
234
+ constructor(config) {
235
+ if (!config.apiKey) {
236
+ throw new TrackerAuthError("rally", "API key is required");
237
+ }
238
+ this.restApi = new RallyRestApi({
239
+ apiKey: config.apiKey,
240
+ server: config.server || "https://rally1.rallydev.com",
241
+ requestOptions: {
242
+ headers: {
243
+ "X-RallyIntegrationType": "Panopticon",
244
+ "X-RallyIntegrationName": "Panopticon CLI",
245
+ "X-RallyIntegrationVendor": "Mind Your Now",
246
+ "X-RallyIntegrationVersion": "0.2.0"
247
+ }
248
+ }
249
+ });
250
+ this.workspace = config.workspace;
251
+ this.project = config.project;
252
+ }
253
+ /**
254
+ * List issues by querying each artifact type separately and merging results.
255
+ *
256
+ * Rally WSAPI cannot apply ScheduleState filters across the generic Artifact
257
+ * endpoint because not all subtypes have that field. We query each type with
258
+ * its own state field, then merge and sort. (PAN-168)
259
+ */
260
+ async listIssues(filters) {
261
+ if (process.env.DEBUG?.includes("rally")) {
262
+ console.debug("[Rally] Query filters:", JSON.stringify(filters));
263
+ }
264
+ const limit = filters?.limit ?? 50;
265
+ let projectObjectId;
266
+ if (this.project) {
267
+ const match = this.project.match(/\/project\/(\d+)/);
268
+ if (match) projectObjectId = match[1];
269
+ }
270
+ const queries = QUERYABLE_TYPES.map(async (artifactType) => {
271
+ const queryString = this.buildQueryStringForType(filters, artifactType, projectObjectId);
272
+ if (process.env.DEBUG?.includes("rally")) {
273
+ console.debug(`[Rally] ${artifactType.type} query:`, queryString);
274
+ }
275
+ const query = {
276
+ type: artifactType.type,
277
+ fetch: FETCH_FIELDS,
278
+ limit,
279
+ query: queryString
280
+ };
281
+ if (this.workspace) {
282
+ query.workspace = this.workspace;
283
+ }
284
+ if (this.project) {
285
+ query.project = this.project;
286
+ query.projectScopeDown = true;
287
+ }
288
+ try {
289
+ const result = await this.queryRally(query);
290
+ return result.Results.map((artifact) => this.normalizeIssue(artifact));
291
+ } catch (error) {
292
+ if (error.message?.includes("Unauthorized") || error.message?.includes("401")) {
293
+ throw new TrackerAuthError("rally", "Invalid API key or insufficient permissions");
294
+ }
295
+ if (process.env.DEBUG?.includes("rally")) {
296
+ console.debug(`[Rally] Failed to query ${artifactType.type}:`, error.message);
297
+ }
298
+ return [];
299
+ }
300
+ });
301
+ const results = await Promise.all(queries);
302
+ const allIssues = results.flat();
303
+ allIssues.sort(
304
+ (a, b) => new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime()
305
+ );
306
+ return allIssues.slice(0, limit);
307
+ }
308
+ async getIssue(id) {
309
+ try {
310
+ const query = {
311
+ type: "artifact",
312
+ fetch: [
313
+ "FormattedID",
314
+ "Name",
315
+ "Description",
316
+ "ScheduleState",
317
+ "State",
318
+ "Tags",
319
+ "Owner",
320
+ "Priority",
321
+ "DueDate",
322
+ "CreationDate",
323
+ "LastUpdateDate",
324
+ "Parent",
325
+ "_type"
326
+ ],
327
+ query: `(FormattedID = "${id}")`
328
+ };
329
+ if (this.workspace) {
330
+ query.workspace = this.workspace;
331
+ }
332
+ const result = await this.queryRally(query);
333
+ if (!result.Results || result.Results.length === 0) {
334
+ throw new IssueNotFoundError(id, "rally");
335
+ }
336
+ return this.normalizeIssue(result.Results[0]);
337
+ } catch (error) {
338
+ if (error instanceof IssueNotFoundError) throw error;
339
+ throw new IssueNotFoundError(id, "rally");
340
+ }
341
+ }
342
+ async updateIssue(id, update) {
343
+ const issue = await this.getIssue(id);
344
+ const query = {
345
+ type: "artifact",
346
+ fetch: ["ObjectID", "_ref", "_type"],
347
+ query: `(FormattedID = "${id}")`
348
+ };
349
+ if (this.workspace) {
350
+ query.workspace = this.workspace;
351
+ }
352
+ const result = await this.queryRally(query);
353
+ if (!result.Results || result.Results.length === 0) {
354
+ throw new IssueNotFoundError(id, "rally");
355
+ }
356
+ const artifact = result.Results[0];
357
+ const updatePayload = {};
358
+ if (update.title !== void 0) {
359
+ updatePayload.Name = update.title;
360
+ }
361
+ if (update.description !== void 0) {
362
+ updatePayload.Description = update.description;
363
+ }
364
+ if (update.state !== void 0) {
365
+ const artifactType = (artifact._type || "").toLowerCase();
366
+ const kind = artifactType.startsWith("portfolioitem") ? "feature" : artifactType === "defect" ? "defect" : "story";
367
+ const rallyState = this.reverseMapState(update.state, kind);
368
+ if (kind === "story") {
369
+ updatePayload.ScheduleState = rallyState;
370
+ } else {
371
+ updatePayload.State = rallyState;
372
+ }
373
+ }
374
+ if (update.priority !== void 0) {
375
+ updatePayload.Priority = REVERSE_PRIORITY_MAP[update.priority] || "Normal";
376
+ }
377
+ if (update.dueDate !== void 0) {
378
+ updatePayload.DueDate = update.dueDate;
379
+ }
380
+ if (Object.keys(updatePayload).length > 0) {
381
+ await this.updateRally(artifact._type.toLowerCase(), artifact._ref, updatePayload);
382
+ }
383
+ return this.getIssue(id);
384
+ }
385
+ async createIssue(newIssue) {
386
+ if (!this.project && !newIssue.team) {
387
+ throw new Error("Project is required to create an issue. Set it in config or provide team field.");
388
+ }
389
+ const project = newIssue.team || this.project;
390
+ const createPayload = {
391
+ Name: newIssue.title,
392
+ Description: newIssue.description || "",
393
+ Project: project
394
+ };
395
+ if (newIssue.priority !== void 0) {
396
+ createPayload.Priority = REVERSE_PRIORITY_MAP[newIssue.priority] || "Normal";
397
+ }
398
+ if (newIssue.dueDate) {
399
+ createPayload.DueDate = newIssue.dueDate;
400
+ }
401
+ if (this.workspace) {
402
+ createPayload.Workspace = this.workspace;
403
+ }
404
+ const result = await this.createRally("hierarchicalrequirement", createPayload);
405
+ return this.getIssue(result.Object.FormattedID);
406
+ }
407
+ async getComments(issueId) {
408
+ const issue = await this.getIssue(issueId);
409
+ const query = {
410
+ type: "artifact",
411
+ fetch: ["ObjectID", "_ref", "Discussion"],
412
+ query: `(FormattedID = "${issueId}")`
413
+ };
414
+ if (this.workspace) {
415
+ query.workspace = this.workspace;
416
+ }
417
+ const result = await this.queryRally(query);
418
+ if (!result.Results || result.Results.length === 0) {
419
+ return [];
420
+ }
421
+ const artifact = result.Results[0];
422
+ if (!artifact.Discussion) {
423
+ return [];
424
+ }
425
+ const postsQuery = {
426
+ type: "conversationpost",
427
+ fetch: ["ObjectID", "Text", "User", "CreationDate", "PostNumber"],
428
+ query: `(Discussion = "${artifact.Discussion._ref}")`,
429
+ order: "PostNumber"
430
+ };
431
+ const postsResult = await this.queryRally(postsQuery);
432
+ return (postsResult.Results || []).map((post) => ({
433
+ id: post.ObjectID,
434
+ issueId,
435
+ body: post.Text || "",
436
+ author: post.User?._refObjectName || "Unknown",
437
+ createdAt: post.CreationDate,
438
+ updatedAt: post.CreationDate
439
+ // Rally doesn't track comment updates separately
440
+ }));
441
+ }
442
+ async addComment(issueId, body) {
443
+ const query = {
444
+ type: "artifact",
445
+ fetch: ["ObjectID", "_ref", "Discussion"],
446
+ query: `(FormattedID = "${issueId}")`
447
+ };
448
+ if (this.workspace) {
449
+ query.workspace = this.workspace;
450
+ }
451
+ const result = await this.queryRally(query);
452
+ if (!result.Results || result.Results.length === 0) {
453
+ throw new IssueNotFoundError(issueId, "rally");
454
+ }
455
+ const artifact = result.Results[0];
456
+ let discussionRef = artifact.Discussion?._ref;
457
+ if (!discussionRef) {
458
+ const discussionResult = await this.createRally("conversationpost", {
459
+ Artifact: artifact._ref,
460
+ Text: body
461
+ });
462
+ return {
463
+ id: discussionResult.Object.ObjectID,
464
+ issueId,
465
+ body,
466
+ author: "Panopticon",
467
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
468
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
469
+ };
470
+ }
471
+ const postResult = await this.createRally("conversationpost", {
472
+ Artifact: artifact._ref,
473
+ Text: body
474
+ });
475
+ return {
476
+ id: postResult.Object.ObjectID,
477
+ issueId,
478
+ body,
479
+ author: "Panopticon",
480
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
481
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
482
+ };
483
+ }
484
+ async transitionIssue(id, state) {
485
+ await this.updateIssue(id, { state });
486
+ }
487
+ async linkPR(issueId, prUrl) {
488
+ await this.addComment(issueId, `Linked Pull Request: ${prUrl}`);
489
+ }
490
+ // Private helper methods
491
+ /**
492
+ * Build a Rally WSAPI query string for a specific artifact type.
493
+ *
494
+ * Each artifact type has its own state field:
495
+ * - HierarchicalRequirement: ScheduleState (Defined, In-Progress, Completed, Accepted)
496
+ * - Defect: State (Submitted, Open, Fixed, Closed)
497
+ * - Task: State (Defined, In-Progress, Completed)
498
+ *
499
+ * Rally WSAPI v2.0 requires binary-nested AND/OR with outer parentheses.
500
+ * (PAN-166, PAN-168)
501
+ */
502
+ buildQueryStringForType(filters, artifactType, projectObjectId) {
503
+ const conditions = [];
504
+ if (projectObjectId) {
505
+ conditions.push(`(Project.ObjectID = "${projectObjectId}")`);
506
+ }
507
+ if (filters?.state && !filters.includeClosed) {
508
+ const kind = artifactType.type.startsWith("portfolioitem") ? "feature" : artifactType.type === "defect" ? "defect" : "story";
509
+ const rallyState = this.reverseMapState(filters.state, kind);
510
+ conditions.push(`(${artifactType.stateField} = "${rallyState}")`);
511
+ }
512
+ if (!filters?.includeClosed) {
513
+ const closedConditions = artifactType.closedStates.map(
514
+ (state) => `(${artifactType.stateField} != "${state}")`
515
+ );
516
+ const closedExpr = closedConditions.reduce(
517
+ (acc, cond) => acc ? `(${acc} AND ${cond})` : cond,
518
+ ""
519
+ );
520
+ conditions.push(closedExpr);
521
+ }
522
+ if (filters?.assignee) {
523
+ conditions.push(`(Owner.Name contains "${filters.assignee}")`);
524
+ }
525
+ if (filters?.labels && filters.labels.length > 0) {
526
+ const labelConditions = filters.labels.map(
527
+ (label) => `(Tags.Name contains "${label}")`
528
+ );
529
+ const labelExpr = labelConditions.reduce((acc, cond) => acc ? `(${acc} AND ${cond})` : cond, "");
530
+ conditions.push(labelExpr);
531
+ }
532
+ if (filters?.query) {
533
+ conditions.push(`((Name contains "${filters.query}") OR (Description contains "${filters.query}"))`);
534
+ }
535
+ return conditions.reduce((acc, cond) => acc ? `(${acc} AND ${cond})` : cond, "");
536
+ }
537
+ normalizeIssue(rallyArtifact) {
538
+ const rawStateValue = rallyArtifact.ScheduleState || rallyArtifact.State || "Defined";
539
+ const stateValue = typeof rawStateValue === "object" && rawStateValue !== null ? rawStateValue.Name || rawStateValue._refObjectName || "Defined" : rawStateValue;
540
+ const state = this.mapState(stateValue);
541
+ const labels = [];
542
+ if (rallyArtifact.Tags && rallyArtifact.Tags._tagsNameArray) {
543
+ for (const tag of rallyArtifact.Tags._tagsNameArray) {
544
+ if (typeof tag === "string") {
545
+ labels.push(tag);
546
+ } else if (tag?.Name) {
547
+ labels.push(tag.Name);
548
+ }
549
+ }
550
+ }
551
+ const priority = rallyArtifact.Priority ? PRIORITY_MAP[rallyArtifact.Priority] ?? 2 : void 0;
552
+ const objectId = rallyArtifact.ObjectID || rallyArtifact.FormattedID;
553
+ const artifactType = rallyArtifact._type || "artifact";
554
+ const baseUrl = this.restApi.server.replace("/slm/webservice/", "");
555
+ const url = `${baseUrl}/#/detail/${artifactType.toLowerCase()}/${objectId}`;
556
+ let parentRef;
557
+ if (rallyArtifact.PortfolioItem) {
558
+ if (rallyArtifact.PortfolioItem.FormattedID) {
559
+ parentRef = rallyArtifact.PortfolioItem.FormattedID;
560
+ } else if (rallyArtifact.PortfolioItem._refObjectName) {
561
+ parentRef = rallyArtifact.PortfolioItem._refObjectName;
562
+ }
563
+ } else if (rallyArtifact.Parent) {
564
+ if (rallyArtifact.Parent.FormattedID) {
565
+ parentRef = rallyArtifact.Parent.FormattedID;
566
+ } else if (rallyArtifact.Parent._refObjectName) {
567
+ parentRef = rallyArtifact.Parent._refObjectName;
568
+ }
569
+ }
570
+ return {
571
+ id: String(objectId),
572
+ ref: rallyArtifact.FormattedID,
573
+ title: rallyArtifact.Name || "",
574
+ description: rallyArtifact.Description || "",
575
+ state,
576
+ labels,
577
+ assignee: rallyArtifact.Owner?._refObjectName,
578
+ url,
579
+ tracker: "rally",
580
+ priority,
581
+ dueDate: rallyArtifact.DueDate,
582
+ createdAt: rallyArtifact.CreationDate,
583
+ updatedAt: rallyArtifact.LastUpdateDate,
584
+ parentRef,
585
+ artifactType,
586
+ rawState: stateValue
587
+ };
588
+ }
589
+ mapState(rallyState) {
590
+ return STATE_MAP[rallyState] ?? "open";
591
+ }
592
+ reverseMapState(state, kind = "story") {
593
+ if (kind === "feature") {
594
+ switch (state) {
595
+ case "open":
596
+ return "Discovering";
597
+ case "in_progress":
598
+ return "Developing";
599
+ case "closed":
600
+ return "Done";
601
+ default:
602
+ return "Discovering";
603
+ }
604
+ }
605
+ if (kind === "defect") {
606
+ switch (state) {
607
+ case "open":
608
+ return "Submitted";
609
+ case "in_progress":
610
+ return "Open";
611
+ case "closed":
612
+ return "Closed";
613
+ default:
614
+ return "Submitted";
615
+ }
616
+ }
617
+ switch (state) {
618
+ case "open":
619
+ return "Defined";
620
+ case "in_progress":
621
+ return "In-Progress";
622
+ case "closed":
623
+ return "Completed";
624
+ default:
625
+ return "Defined";
626
+ }
627
+ }
628
+ // Rally API wrapper methods
629
+ async queryRally(queryConfig) {
630
+ const result = await this.restApi.query(queryConfig);
631
+ return {
632
+ Results: result.QueryResult.Results,
633
+ TotalResultCount: result.QueryResult.TotalResultCount
634
+ };
635
+ }
636
+ async createRally(type, data) {
637
+ const result = await this.restApi.create({
638
+ type,
639
+ data,
640
+ fetch: ["FormattedID", "ObjectID", "_ref"]
641
+ });
642
+ return {
643
+ Object: result.CreateResult.Object
644
+ };
645
+ }
646
+ async updateRally(type, ref, data) {
647
+ const result = await this.restApi.update({
648
+ type,
649
+ ref,
650
+ data,
651
+ fetch: ["FormattedID", "ObjectID"]
652
+ });
653
+ return {
654
+ Object: result.OperationResult.Object
655
+ };
656
+ }
657
+ };
658
+ }
659
+ });
660
+
661
+ export {
662
+ NotImplementedError,
663
+ IssueNotFoundError,
664
+ TrackerAuthError,
665
+ init_interface,
666
+ RallyTracker,
667
+ init_rally
668
+ };
669
+ //# sourceMappingURL=chunk-CFCUOV3Q.js.map