panopticon-cli 0.4.12 → 0.4.14
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/dist/{chunk-WLL3UEYI.js → chunk-YCX7RVYW.js} +94 -53
- package/dist/chunk-YCX7RVYW.js.map +1 -0
- package/dist/cli/index.js +1 -1
- package/dist/dashboard/public/assets/{index-Bwu9d0w1.js → index-CE4Bu8aP.js} +42 -42
- package/dist/dashboard/public/index.html +1 -1
- package/dist/dashboard/server.js +168 -68
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-WLL3UEYI.js.map +0 -1
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
}
|
|
18
18
|
})();
|
|
19
19
|
</script>
|
|
20
|
-
<script type="module" crossorigin src="/assets/index-
|
|
20
|
+
<script type="module" crossorigin src="/assets/index-CE4Bu8aP.js"></script>
|
|
21
21
|
<link rel="stylesheet" crossorigin href="/assets/index-Bp7DyPLO.css">
|
|
22
22
|
</head>
|
|
23
23
|
<body class="bg-surface text-content transition-colors duration-150">
|
package/dist/dashboard/server.js
CHANGED
|
@@ -51275,7 +51275,7 @@ var rally_exports = {};
|
|
|
51275
51275
|
__export(rally_exports, {
|
|
51276
51276
|
RallyTracker: () => RallyTracker
|
|
51277
51277
|
});
|
|
51278
|
-
var STATE_MAP, PRIORITY_MAP, REVERSE_PRIORITY_MAP, RallyTracker;
|
|
51278
|
+
var STATE_MAP, QUERYABLE_TYPES, FETCH_FIELDS, PRIORITY_MAP, REVERSE_PRIORITY_MAP, RallyTracker;
|
|
51279
51279
|
var init_rally = __esm({
|
|
51280
51280
|
"../../lib/tracker/rally.ts"() {
|
|
51281
51281
|
"use strict";
|
|
@@ -51287,6 +51287,27 @@ var init_rally = __esm({
|
|
|
51287
51287
|
Completed: "closed",
|
|
51288
51288
|
Accepted: "closed"
|
|
51289
51289
|
};
|
|
51290
|
+
QUERYABLE_TYPES = [
|
|
51291
|
+
{ type: "hierarchicalrequirement", stateField: "ScheduleState", closedStates: ["Completed", "Accepted"] },
|
|
51292
|
+
{ type: "defect", stateField: "State", closedStates: ["Closed"] },
|
|
51293
|
+
{ type: "task", stateField: "State", closedStates: ["Completed"] }
|
|
51294
|
+
];
|
|
51295
|
+
FETCH_FIELDS = [
|
|
51296
|
+
"ObjectID",
|
|
51297
|
+
"FormattedID",
|
|
51298
|
+
"Name",
|
|
51299
|
+
"Description",
|
|
51300
|
+
"ScheduleState",
|
|
51301
|
+
"State",
|
|
51302
|
+
"Tags",
|
|
51303
|
+
"Owner",
|
|
51304
|
+
"Priority",
|
|
51305
|
+
"DueDate",
|
|
51306
|
+
"CreationDate",
|
|
51307
|
+
"LastUpdateDate",
|
|
51308
|
+
"Parent",
|
|
51309
|
+
"_type"
|
|
51310
|
+
];
|
|
51290
51311
|
PRIORITY_MAP = {
|
|
51291
51312
|
"Resolve Immediately": 0,
|
|
51292
51313
|
High: 1,
|
|
@@ -51324,50 +51345,55 @@ var init_rally = __esm({
|
|
|
51324
51345
|
this.workspace = config2.workspace;
|
|
51325
51346
|
this.project = config2.project;
|
|
51326
51347
|
}
|
|
51348
|
+
/**
|
|
51349
|
+
* List issues by querying each artifact type separately and merging results.
|
|
51350
|
+
*
|
|
51351
|
+
* Rally WSAPI cannot apply ScheduleState filters across the generic Artifact
|
|
51352
|
+
* endpoint because not all subtypes have that field. We query each type with
|
|
51353
|
+
* its own state field, then merge and sort. (PAN-168)
|
|
51354
|
+
*/
|
|
51327
51355
|
async listIssues(filters) {
|
|
51328
|
-
const queryString = this.buildQueryString(filters);
|
|
51329
51356
|
if (process.env.DEBUG?.includes("rally")) {
|
|
51330
51357
|
console.debug("[Rally] Query filters:", JSON.stringify(filters));
|
|
51331
|
-
console.debug("[Rally] Generated query:", queryString);
|
|
51332
|
-
}
|
|
51333
|
-
const query = {
|
|
51334
|
-
type: "artifact",
|
|
51335
|
-
// Query all artifact types
|
|
51336
|
-
fetch: [
|
|
51337
|
-
"FormattedID",
|
|
51338
|
-
"Name",
|
|
51339
|
-
"Description",
|
|
51340
|
-
"ScheduleState",
|
|
51341
|
-
"State",
|
|
51342
|
-
// For Defects
|
|
51343
|
-
"Tags",
|
|
51344
|
-
"Owner",
|
|
51345
|
-
"Priority",
|
|
51346
|
-
"DueDate",
|
|
51347
|
-
"CreationDate",
|
|
51348
|
-
"LastUpdateDate",
|
|
51349
|
-
"Parent",
|
|
51350
|
-
"_type"
|
|
51351
|
-
],
|
|
51352
|
-
limit: filters?.limit ?? 50,
|
|
51353
|
-
query: queryString
|
|
51354
|
-
};
|
|
51355
|
-
if (this.workspace) {
|
|
51356
|
-
query.workspace = this.workspace;
|
|
51357
|
-
}
|
|
51358
|
-
if (this.project) {
|
|
51359
|
-
query.project = this.project;
|
|
51360
|
-
query.projectScopeDown = true;
|
|
51361
51358
|
}
|
|
51362
|
-
|
|
51363
|
-
|
|
51364
|
-
|
|
51365
|
-
|
|
51366
|
-
|
|
51367
|
-
throw new TrackerAuthError("rally", "Invalid API key or insufficient permissions");
|
|
51359
|
+
const limit = filters?.limit ?? 50;
|
|
51360
|
+
const queries = QUERYABLE_TYPES.map(async (artifactType) => {
|
|
51361
|
+
const queryString = this.buildQueryStringForType(filters, artifactType);
|
|
51362
|
+
if (process.env.DEBUG?.includes("rally")) {
|
|
51363
|
+
console.debug(`[Rally] ${artifactType.type} query:`, queryString);
|
|
51368
51364
|
}
|
|
51369
|
-
|
|
51370
|
-
|
|
51365
|
+
const query = {
|
|
51366
|
+
type: artifactType.type,
|
|
51367
|
+
fetch: FETCH_FIELDS,
|
|
51368
|
+
limit,
|
|
51369
|
+
query: queryString
|
|
51370
|
+
};
|
|
51371
|
+
if (this.workspace) {
|
|
51372
|
+
query.workspace = this.workspace;
|
|
51373
|
+
}
|
|
51374
|
+
if (this.project) {
|
|
51375
|
+
query.project = this.project;
|
|
51376
|
+
query.projectScopeDown = true;
|
|
51377
|
+
}
|
|
51378
|
+
try {
|
|
51379
|
+
const result = await this.queryRally(query);
|
|
51380
|
+
return result.Results.map((artifact) => this.normalizeIssue(artifact));
|
|
51381
|
+
} catch (error) {
|
|
51382
|
+
if (error.message?.includes("Unauthorized") || error.message?.includes("401")) {
|
|
51383
|
+
throw new TrackerAuthError("rally", "Invalid API key or insufficient permissions");
|
|
51384
|
+
}
|
|
51385
|
+
if (process.env.DEBUG?.includes("rally")) {
|
|
51386
|
+
console.debug(`[Rally] Failed to query ${artifactType.type}:`, error.message);
|
|
51387
|
+
}
|
|
51388
|
+
return [];
|
|
51389
|
+
}
|
|
51390
|
+
});
|
|
51391
|
+
const results = await Promise.all(queries);
|
|
51392
|
+
const allIssues = results.flat();
|
|
51393
|
+
allIssues.sort(
|
|
51394
|
+
(a, b) => new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime()
|
|
51395
|
+
);
|
|
51396
|
+
return allIssues.slice(0, limit);
|
|
51371
51397
|
}
|
|
51372
51398
|
async getIssue(id) {
|
|
51373
51399
|
try {
|
|
@@ -51552,24 +51578,31 @@ var init_rally = __esm({
|
|
|
51552
51578
|
}
|
|
51553
51579
|
// Private helper methods
|
|
51554
51580
|
/**
|
|
51555
|
-
* Build a Rally WSAPI query string
|
|
51581
|
+
* Build a Rally WSAPI query string for a specific artifact type.
|
|
51556
51582
|
*
|
|
51557
|
-
*
|
|
51558
|
-
*
|
|
51559
|
-
*
|
|
51560
|
-
*
|
|
51583
|
+
* Each artifact type has its own state field:
|
|
51584
|
+
* - HierarchicalRequirement: ScheduleState (Defined, In-Progress, Completed, Accepted)
|
|
51585
|
+
* - Defect: State (Submitted, Open, Fixed, Closed)
|
|
51586
|
+
* - Task: State (Defined, In-Progress, Completed)
|
|
51561
51587
|
*
|
|
51562
|
-
*
|
|
51563
|
-
*
|
|
51588
|
+
* Rally WSAPI v2.0 requires binary-nested AND/OR with outer parentheses.
|
|
51589
|
+
* (PAN-166, PAN-168)
|
|
51564
51590
|
*/
|
|
51565
|
-
|
|
51591
|
+
buildQueryStringForType(filters, artifactType) {
|
|
51566
51592
|
const conditions = [];
|
|
51567
51593
|
if (filters?.state && !filters.includeClosed) {
|
|
51568
51594
|
const rallyState = this.reverseMapState(filters.state);
|
|
51569
|
-
conditions.push(`(
|
|
51595
|
+
conditions.push(`(${artifactType.stateField} = "${rallyState}")`);
|
|
51570
51596
|
}
|
|
51571
51597
|
if (!filters?.includeClosed) {
|
|
51572
|
-
|
|
51598
|
+
const closedConditions = artifactType.closedStates.map(
|
|
51599
|
+
(state) => `(${artifactType.stateField} != "${state}")`
|
|
51600
|
+
);
|
|
51601
|
+
const closedExpr = closedConditions.reduce(
|
|
51602
|
+
(acc, cond) => acc ? `(${acc} AND ${cond})` : cond,
|
|
51603
|
+
""
|
|
51604
|
+
);
|
|
51605
|
+
conditions.push(closedExpr);
|
|
51573
51606
|
}
|
|
51574
51607
|
if (filters?.assignee) {
|
|
51575
51608
|
conditions.push(`(Owner.Name contains "${filters.assignee}")`);
|
|
@@ -51591,13 +51624,21 @@ var init_rally = __esm({
|
|
|
51591
51624
|
const state = this.mapState(stateValue);
|
|
51592
51625
|
const labels = [];
|
|
51593
51626
|
if (rallyArtifact.Tags && rallyArtifact.Tags._tagsNameArray) {
|
|
51594
|
-
|
|
51627
|
+
for (const tag of rallyArtifact.Tags._tagsNameArray) {
|
|
51628
|
+
if (typeof tag === "string") {
|
|
51629
|
+
labels.push(tag);
|
|
51630
|
+
} else if (tag?.Name) {
|
|
51631
|
+
labels.push(tag.Name);
|
|
51632
|
+
}
|
|
51633
|
+
}
|
|
51595
51634
|
}
|
|
51596
51635
|
const priority = rallyArtifact.Priority ? PRIORITY_MAP[rallyArtifact.Priority] ?? 2 : void 0;
|
|
51636
|
+
const objectId = rallyArtifact.ObjectID || rallyArtifact.FormattedID;
|
|
51637
|
+
const artifactType = rallyArtifact._type || "artifact";
|
|
51597
51638
|
const baseUrl = this.restApi.server.replace("/slm/webservice/", "");
|
|
51598
|
-
const url = `${baseUrl}/#/detail/${
|
|
51639
|
+
const url = `${baseUrl}/#/detail/${artifactType.toLowerCase()}/${objectId}`;
|
|
51599
51640
|
return {
|
|
51600
|
-
id:
|
|
51641
|
+
id: String(objectId),
|
|
51601
51642
|
ref: rallyArtifact.FormattedID,
|
|
51602
51643
|
title: rallyArtifact.Name || "",
|
|
51603
51644
|
description: rallyArtifact.Description || "",
|
|
@@ -67224,16 +67265,38 @@ async function postMergeCleanup(issueId, projectPath) {
|
|
|
67224
67265
|
}
|
|
67225
67266
|
const isGitHub = issueId.toUpperCase().startsWith("PAN-");
|
|
67226
67267
|
if (isGitHub) {
|
|
67268
|
+
const issueNum = issueId.replace(/^PAN-/i, "");
|
|
67269
|
+
try {
|
|
67270
|
+
const branchName = `feature/${issueId.toLowerCase()}`;
|
|
67271
|
+
const { stdout: prListRaw } = await execAsync10(
|
|
67272
|
+
`gh pr list --repo eltmon/panopticon-cli --head "${branchName}" --state open --json number --jq '.[0].number'`,
|
|
67273
|
+
{ cwd: projectPath, encoding: "utf-8" }
|
|
67274
|
+
);
|
|
67275
|
+
const prNumber = prListRaw.trim();
|
|
67276
|
+
if (prNumber) {
|
|
67277
|
+
await execAsync10(
|
|
67278
|
+
`gh pr close ${prNumber} --repo eltmon/panopticon-cli --comment "Merged to main via Panopticon merge-agent"`,
|
|
67279
|
+
{ cwd: projectPath, encoding: "utf-8" }
|
|
67280
|
+
);
|
|
67281
|
+
console.log(`[merge-agent] \u2713 Closed PR #${prNumber} for ${issueId}`);
|
|
67282
|
+
logActivity("pr_closed", `Closed PR #${prNumber} for ${issueId}`);
|
|
67283
|
+
}
|
|
67284
|
+
} catch (err) {
|
|
67285
|
+
console.warn(`[merge-agent] Could not close PR: ${err}`);
|
|
67286
|
+
}
|
|
67227
67287
|
try {
|
|
67228
|
-
const issueNum = issueId.replace(/^PAN-/i, "");
|
|
67229
67288
|
await execAsync10(`gh issue edit ${issueNum} --remove-label "in-progress" --add-label "done" 2>/dev/null || true`, {
|
|
67230
67289
|
cwd: projectPath,
|
|
67231
67290
|
encoding: "utf-8"
|
|
67232
67291
|
});
|
|
67233
|
-
|
|
67234
|
-
|
|
67292
|
+
await execAsync10(`gh issue close ${issueNum} --repo eltmon/panopticon-cli --comment "Merged to main" 2>/dev/null || true`, {
|
|
67293
|
+
cwd: projectPath,
|
|
67294
|
+
encoding: "utf-8"
|
|
67295
|
+
});
|
|
67296
|
+
console.log(`[merge-agent] \u2713 Updated and closed GitHub issue #${issueNum}`);
|
|
67297
|
+
logActivity("issue_closed", `Closed GitHub issue #${issueNum} after merge`);
|
|
67235
67298
|
} catch (err) {
|
|
67236
|
-
console.warn(`[merge-agent] Could not
|
|
67299
|
+
console.warn(`[merge-agent] Could not close GitHub issue: ${err}`);
|
|
67237
67300
|
}
|
|
67238
67301
|
} else {
|
|
67239
67302
|
try {
|
|
@@ -67246,6 +67309,18 @@ async function postMergeCleanup(issueId, projectPath) {
|
|
|
67246
67309
|
console.warn(`[merge-agent] Could not update Linear issue: ${err}`);
|
|
67247
67310
|
}
|
|
67248
67311
|
}
|
|
67312
|
+
try {
|
|
67313
|
+
const apiPort = process.env.API_PORT || process.env.PORT || "3011";
|
|
67314
|
+
const apiUrl = process.env.DASHBOARD_URL || `http://localhost:${apiPort}`;
|
|
67315
|
+
await fetch(`${apiUrl}/api/specialists/done`, {
|
|
67316
|
+
method: "POST",
|
|
67317
|
+
headers: { "Content-Type": "application/json" },
|
|
67318
|
+
body: JSON.stringify({ specialist: "merge", issueId, status: "passed", notes: "Merge and validation completed" })
|
|
67319
|
+
});
|
|
67320
|
+
console.log(`[merge-agent] \u2713 Reported merge success to dashboard API`);
|
|
67321
|
+
} catch (err) {
|
|
67322
|
+
console.warn(`[merge-agent] Could not report to dashboard API: ${err}`);
|
|
67323
|
+
}
|
|
67249
67324
|
await conditionalBeadsCompaction(projectPath);
|
|
67250
67325
|
console.log(`[merge-agent] Post-merge cleanup completed for ${issueId}`);
|
|
67251
67326
|
}
|
|
@@ -67703,12 +67778,7 @@ Report any issues or conflicts you encountered.`;
|
|
|
67703
67778
|
});
|
|
67704
67779
|
const currentHead = currentHeadRaw.trim();
|
|
67705
67780
|
if (currentHead !== headBefore) {
|
|
67706
|
-
|
|
67707
|
-
cwd: projectPath,
|
|
67708
|
-
encoding: "utf-8"
|
|
67709
|
-
});
|
|
67710
|
-
const commitMessage = commitMessageRaw.trim().toLowerCase();
|
|
67711
|
-
if (commitMessage.includes("merge") || commitMessage.includes(sourceBranch.toLowerCase())) {
|
|
67781
|
+
{
|
|
67712
67782
|
try {
|
|
67713
67783
|
const { stdout: remoteHeadRaw } = await execAsync10(`git rev-parse origin/${targetBranch}`, {
|
|
67714
67784
|
cwd: projectPath,
|
|
@@ -85012,6 +85082,8 @@ function mapGitHubStateToCanonical(state, labels) {
|
|
|
85012
85082
|
return "todo";
|
|
85013
85083
|
}
|
|
85014
85084
|
function mapRallyStateToCanonical(scheduleState) {
|
|
85085
|
+
if (!scheduleState)
|
|
85086
|
+
return "todo";
|
|
85015
85087
|
const stateLower = scheduleState.toLowerCase();
|
|
85016
85088
|
if (stateLower === "defined")
|
|
85017
85089
|
return "todo";
|
|
@@ -85588,10 +85660,11 @@ var IssueDataService = class {
|
|
|
85588
85660
|
});
|
|
85589
85661
|
const formatted = issues.map((issue) => {
|
|
85590
85662
|
const canonicalStatus = mapRallyStateToCanonical(issue.state);
|
|
85663
|
+
const identifier = issue.ref || issue.id || "unknown";
|
|
85591
85664
|
return {
|
|
85592
|
-
id: `rally-${issue.id}`,
|
|
85593
|
-
identifier
|
|
85594
|
-
title: issue.title,
|
|
85665
|
+
id: `rally-${issue.id || identifier}`,
|
|
85666
|
+
identifier,
|
|
85667
|
+
title: issue.title || "",
|
|
85595
85668
|
description: issue.description || "",
|
|
85596
85669
|
status: canonicalStatus === "todo" ? "Todo" : canonicalStatus === "in_progress" ? "In Progress" : canonicalStatus === "done" ? "Done" : "Todo",
|
|
85597
85670
|
priority: issue.priority ?? 3,
|
|
@@ -85599,8 +85672,8 @@ var IssueDataService = class {
|
|
|
85599
85672
|
name: issue.assignee,
|
|
85600
85673
|
email: `${issue.assignee.replace(/\s+/g, ".").toLowerCase()}@rally`
|
|
85601
85674
|
} : void 0,
|
|
85602
|
-
labels: issue.labels
|
|
85603
|
-
url: issue.url,
|
|
85675
|
+
labels: Array.isArray(issue.labels) ? issue.labels.filter((l) => typeof l === "string") : [],
|
|
85676
|
+
url: issue.url || "",
|
|
85604
85677
|
createdAt: issue.createdAt,
|
|
85605
85678
|
updatedAt: issue.updatedAt,
|
|
85606
85679
|
project: {
|
|
@@ -94469,6 +94542,14 @@ app.post("/api/specialists/done", async (req, res) => {
|
|
|
94469
94542
|
console.log(`[specialists/done] Cleared ${normalizedIssueId} from ${specialist}-agent queue`);
|
|
94470
94543
|
}
|
|
94471
94544
|
}
|
|
94545
|
+
if (specialist === "merge" && status === "passed") {
|
|
94546
|
+
try {
|
|
94547
|
+
await closeIssueAfterMerge(normalizedIssueId);
|
|
94548
|
+
console.log(`[specialists/done] Closed issue/PR for ${normalizedIssueId} after merge`);
|
|
94549
|
+
} catch (err) {
|
|
94550
|
+
console.warn(`[specialists/done] Failed to close issue after merge: ${err}`);
|
|
94551
|
+
}
|
|
94552
|
+
}
|
|
94472
94553
|
res.json({
|
|
94473
94554
|
success: true,
|
|
94474
94555
|
specialist,
|
|
@@ -94878,6 +94959,25 @@ app.post("/api/workspaces/:issueId/merge", async (req, res) => {
|
|
|
94878
94959
|
});
|
|
94879
94960
|
app.post("/api/workspaces/:issueId/approve", async (req, res) => {
|
|
94880
94961
|
const { issueId } = req.params;
|
|
94962
|
+
const existingStatus = getReviewStatus(issueId);
|
|
94963
|
+
if (existingStatus?.readyForMerge && existingStatus.reviewStatus === "passed" && existingStatus.testStatus === "passed") {
|
|
94964
|
+
console.log(`[approve] Review+test already passed for ${issueId}, forwarding to merge endpoint...`);
|
|
94965
|
+
try {
|
|
94966
|
+
const apiPort = process.env.API_PORT || process.env.PORT || "3011";
|
|
94967
|
+
const mergeRes = await fetch(`http://localhost:${apiPort}/api/workspaces/${issueId}/merge`, {
|
|
94968
|
+
method: "POST",
|
|
94969
|
+
headers: { "Content-Type": "application/json" }
|
|
94970
|
+
});
|
|
94971
|
+
const mergeData = await mergeRes.json();
|
|
94972
|
+
if (mergeRes.ok) {
|
|
94973
|
+
return res.json(mergeData);
|
|
94974
|
+
} else {
|
|
94975
|
+
return res.status(mergeRes.status).json(mergeData);
|
|
94976
|
+
}
|
|
94977
|
+
} catch (err) {
|
|
94978
|
+
return res.status(500).json({ error: `Failed to forward to merge: ${err.message}` });
|
|
94979
|
+
}
|
|
94980
|
+
}
|
|
94881
94981
|
const issuePrefix = issueId.split("-")[0];
|
|
94882
94982
|
const projectPath = getProjectPath(void 0, issuePrefix);
|
|
94883
94983
|
const issueLower = issueId.toLowerCase();
|
package/dist/index.js
CHANGED