pipe-kan 0.33.1 → 0.34.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +15 -4
- package/dist/pipe-kan.js +667 -75
- package/dist/ui/assets/index-Ci-6UWvl.js +56 -0
- package/dist/ui/assets/index-DD60NIO0.css +2 -0
- package/dist/ui/index.html +2 -2
- package/package.json +3 -2
- package/dist/ui/assets/index-BtuKBXwg.css +0 -2
- package/dist/ui/assets/index-DlebjkVt.js +0 -56
package/dist/pipe-kan.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/server.ts
|
|
4
|
-
import { readFileSync as
|
|
4
|
+
import { readFileSync as readFileSync8, writeSync } from "node:fs";
|
|
5
5
|
import { createServer } from "node:http";
|
|
6
6
|
import { tmpdir as tmpdir3 } from "node:os";
|
|
7
7
|
import { join as join11 } from "node:path";
|
|
@@ -156,6 +156,9 @@ function formatDueDate(value) {
|
|
|
156
156
|
function createdAt(fields) {
|
|
157
157
|
return typeof fields?.created === "string" && fields.created ? fields.created : undefined;
|
|
158
158
|
}
|
|
159
|
+
function updatedAt(fields) {
|
|
160
|
+
return typeof fields?.updated === "string" && fields.updated ? fields.updated : undefined;
|
|
161
|
+
}
|
|
159
162
|
function issueType(fields) {
|
|
160
163
|
const name = fields?.issuetype?.name ?? fields?.issueType?.name;
|
|
161
164
|
return typeof name === "string" ? name : undefined;
|
|
@@ -247,6 +250,7 @@ function toCard(issue, key, fieldId) {
|
|
|
247
250
|
const epic = epicKey(issue.fields);
|
|
248
251
|
const labels = issueLabels(issue.fields);
|
|
249
252
|
const created = createdAt(issue.fields);
|
|
253
|
+
const updated = updatedAt(issue.fields);
|
|
250
254
|
return {
|
|
251
255
|
key,
|
|
252
256
|
summary,
|
|
@@ -257,7 +261,8 @@ function toCard(issue, key, fieldId) {
|
|
|
257
261
|
...type ? { type } : {},
|
|
258
262
|
...epic ? { epic } : {},
|
|
259
263
|
...labels ? { labels } : {},
|
|
260
|
-
...created ? { created } : {}
|
|
264
|
+
...created ? { created } : {},
|
|
265
|
+
...updated ? { updated } : {}
|
|
261
266
|
};
|
|
262
267
|
}
|
|
263
268
|
function issuesToBoard(raw, opts = {}) {
|
|
@@ -360,6 +365,8 @@ function parseFlags(flags) {
|
|
|
360
365
|
let type = "";
|
|
361
366
|
let raw = "";
|
|
362
367
|
let projectsValue = "";
|
|
368
|
+
let workspacesValue = "";
|
|
369
|
+
let scopesValue = "";
|
|
363
370
|
let plane = false;
|
|
364
371
|
let workspace = "";
|
|
365
372
|
const statusEq = [];
|
|
@@ -372,6 +379,28 @@ function parseFlags(flags) {
|
|
|
372
379
|
plane = true;
|
|
373
380
|
continue;
|
|
374
381
|
}
|
|
382
|
+
if (token === "--workspaces" || token.startsWith("--workspaces=")) {
|
|
383
|
+
const parts = [];
|
|
384
|
+
if (token.startsWith("--workspaces="))
|
|
385
|
+
parts.push(token.slice("--workspaces=".length));
|
|
386
|
+
while (i + 1 < list.length && !list[i + 1].startsWith("-")) {
|
|
387
|
+
i++;
|
|
388
|
+
parts.push(list[i]);
|
|
389
|
+
}
|
|
390
|
+
workspacesValue = parts.join(",");
|
|
391
|
+
continue;
|
|
392
|
+
}
|
|
393
|
+
if (token === "--scopes" || token.startsWith("--scopes=")) {
|
|
394
|
+
const parts = [];
|
|
395
|
+
if (token.startsWith("--scopes="))
|
|
396
|
+
parts.push(token.slice("--scopes=".length));
|
|
397
|
+
while (i + 1 < list.length && !list[i + 1].startsWith("-")) {
|
|
398
|
+
i++;
|
|
399
|
+
parts.push(list[i]);
|
|
400
|
+
}
|
|
401
|
+
scopesValue = parts.join(",");
|
|
402
|
+
continue;
|
|
403
|
+
}
|
|
375
404
|
if (token === "--workspace" || token.startsWith("--workspace=")) {
|
|
376
405
|
const [value, next] = takeValue(list, i, "--workspace");
|
|
377
406
|
workspace = value.startsWith("=") ? value.slice(1) : value;
|
|
@@ -420,9 +449,11 @@ function parseFlags(flags) {
|
|
|
420
449
|
}
|
|
421
450
|
}
|
|
422
451
|
const projects = projectsValue.split(",").map((p) => p.trim().toUpperCase()).filter(Boolean);
|
|
452
|
+
const workspaces = uniqueSlugs(workspacesValue);
|
|
453
|
+
const scopes = parseScopes(scopesValue);
|
|
423
454
|
const workspaceSlug = workspace.trim() || undefined;
|
|
424
455
|
if (raw) {
|
|
425
|
-
return { jql: raw, projects, plane, workspace: workspaceSlug };
|
|
456
|
+
return { jql: raw, projects, plane, workspace: workspaceSlug, workspaces, scopes };
|
|
426
457
|
}
|
|
427
458
|
const projectClause = projects.length ? `project in (${projects.map((p) => `"${p}"`).join(", ")})` : "";
|
|
428
459
|
const clauses = [];
|
|
@@ -438,7 +469,47 @@ function parseFlags(flags) {
|
|
|
438
469
|
clauses.push(`status="${status}"`);
|
|
439
470
|
for (const status of statusNeq)
|
|
440
471
|
clauses.push(`status!="${status}"`);
|
|
441
|
-
return { jql: clauses.join(" AND "), projects, plane, workspace: workspaceSlug };
|
|
472
|
+
return { jql: clauses.join(" AND "), projects, plane, workspace: workspaceSlug, workspaces, scopes };
|
|
473
|
+
}
|
|
474
|
+
function uniqueSlugs(value) {
|
|
475
|
+
const seen = new Set;
|
|
476
|
+
const slugs = [];
|
|
477
|
+
for (const part of value.split(",")) {
|
|
478
|
+
const slug = part.trim().toLowerCase();
|
|
479
|
+
if (!slug || seen.has(slug))
|
|
480
|
+
continue;
|
|
481
|
+
seen.add(slug);
|
|
482
|
+
slugs.push(slug);
|
|
483
|
+
}
|
|
484
|
+
return slugs;
|
|
485
|
+
}
|
|
486
|
+
function parseScopes(value) {
|
|
487
|
+
const seen = new Set;
|
|
488
|
+
const scopes = [];
|
|
489
|
+
for (const part of value.split(",")) {
|
|
490
|
+
const trimmed = part.trim();
|
|
491
|
+
if (!trimmed)
|
|
492
|
+
continue;
|
|
493
|
+
const slash = trimmed.indexOf("/");
|
|
494
|
+
if (slash <= 0)
|
|
495
|
+
continue;
|
|
496
|
+
const workspace = trimmed.slice(0, slash).trim().toLowerCase();
|
|
497
|
+
const project = trimmed.slice(slash + 1).trim().toUpperCase();
|
|
498
|
+
if (!workspace || !project)
|
|
499
|
+
continue;
|
|
500
|
+
const key = `${workspace}/${project}`;
|
|
501
|
+
if (seen.has(key))
|
|
502
|
+
continue;
|
|
503
|
+
seen.add(key);
|
|
504
|
+
scopes.push({ workspace, project });
|
|
505
|
+
}
|
|
506
|
+
return scopes;
|
|
507
|
+
}
|
|
508
|
+
function setPlaneFlag(flags, on) {
|
|
509
|
+
const list = tokens(flags.trim()).filter((token) => token !== "--plane");
|
|
510
|
+
if (on)
|
|
511
|
+
list.unshift("--plane");
|
|
512
|
+
return list.join(" ");
|
|
442
513
|
}
|
|
443
514
|
function wantsPlane(flags) {
|
|
444
515
|
return parseFlags(flags).plane;
|
|
@@ -621,6 +692,11 @@ Available states for issue ${key}: ${available}`
|
|
|
621
692
|
issue.fields.description = input.description;
|
|
622
693
|
if (input.labels !== undefined)
|
|
623
694
|
issue.fields.labels = input.labels;
|
|
695
|
+
if (input.priority !== undefined)
|
|
696
|
+
issue.fields.priority = { name: input.priority };
|
|
697
|
+
if (input.assignee !== undefined) {
|
|
698
|
+
issue.fields.assignee = input.assignee ? { displayName: input.assignee } : undefined;
|
|
699
|
+
}
|
|
624
700
|
return { ok: true };
|
|
625
701
|
}
|
|
626
702
|
}
|
|
@@ -972,6 +1048,10 @@ ${result.stderr}`);
|
|
|
972
1048
|
for (const label of input.labels)
|
|
973
1049
|
args.push("-l", label);
|
|
974
1050
|
}
|
|
1051
|
+
if (input.assignee !== undefined)
|
|
1052
|
+
args.push("-a", input.assignee);
|
|
1053
|
+
if (input.priority !== undefined)
|
|
1054
|
+
args.push("--priority", input.priority);
|
|
975
1055
|
const result = await runRetry(args);
|
|
976
1056
|
if (result.code !== 0) {
|
|
977
1057
|
return { ok: false, error: (result.stderr || result.stdout).trim() };
|
|
@@ -1518,14 +1598,250 @@ function createApp(opts) {
|
|
|
1518
1598
|
},
|
|
1519
1599
|
async listProjectIdentifiers(workspace) {
|
|
1520
1600
|
return cli.listProjectIdentifiers?.(workspace) ?? [];
|
|
1601
|
+
},
|
|
1602
|
+
async listProjects(workspace) {
|
|
1603
|
+
return cli.listProjects?.(workspace) ?? [];
|
|
1604
|
+
},
|
|
1605
|
+
async parity() {
|
|
1606
|
+
return cli.parity?.() ?? { initiatives: [], projects: [], workspaces: [] };
|
|
1521
1607
|
}
|
|
1522
1608
|
};
|
|
1523
1609
|
return app;
|
|
1524
1610
|
}
|
|
1525
1611
|
|
|
1612
|
+
// src/plane-fixture.ts
|
|
1613
|
+
import { readFileSync as readFileSync3 } from "node:fs";
|
|
1614
|
+
var STATES = [
|
|
1615
|
+
{ id: "st-backlog", name: "Backlog", group: "backlog", sequence: 1 },
|
|
1616
|
+
{ id: "st-todo", name: "Todo", group: "unstarted", sequence: 2 },
|
|
1617
|
+
{ id: "st-progress", name: "In Progress", group: "started", sequence: 3 },
|
|
1618
|
+
{ id: "st-done", name: "Done", group: "completed", sequence: 4 },
|
|
1619
|
+
{ id: "st-cancelled", name: "Cancelled", group: "cancelled", sequence: 5 }
|
|
1620
|
+
];
|
|
1621
|
+
function mod(id, name, status, extra = {}) {
|
|
1622
|
+
return { id, name, status, created_at: "2026-01-04T00:00:00Z", updated_at: "2026-02-02T00:00:00Z", ...extra };
|
|
1623
|
+
}
|
|
1624
|
+
function issue(id, name, sequence, state, extra = {}) {
|
|
1625
|
+
return { id, name, sequence_id: sequence, state, created_at: "2026-01-06T00:00:00Z", updated_at: "2026-03-03T00:00:00Z", ...extra };
|
|
1626
|
+
}
|
|
1627
|
+
var SYNTHETIC_PLANE = {
|
|
1628
|
+
workspaces: [
|
|
1629
|
+
{ id: "ws-work", name: "Work", slug: "work" },
|
|
1630
|
+
{ id: "ws-personal", name: "Personal", slug: "personal" }
|
|
1631
|
+
],
|
|
1632
|
+
projects: {
|
|
1633
|
+
work: [
|
|
1634
|
+
{ id: "work-pulse", identifier: "PULSE", name: "Pulse" },
|
|
1635
|
+
{ id: "work-aph", identifier: "APH", name: "Aphrodite" }
|
|
1636
|
+
],
|
|
1637
|
+
personal: [
|
|
1638
|
+
{ id: "personal-pulse", identifier: "PULSE", name: "Pulse" },
|
|
1639
|
+
{ id: "personal-pkan", identifier: "PKAN", name: "Pipe kan" }
|
|
1640
|
+
]
|
|
1641
|
+
},
|
|
1642
|
+
states: STATES,
|
|
1643
|
+
labels: [
|
|
1644
|
+
{ id: "lbl-needs", name: "needs-input" },
|
|
1645
|
+
{ id: "lbl-roadmap", name: "roadmap" }
|
|
1646
|
+
],
|
|
1647
|
+
members: [
|
|
1648
|
+
{ id: "user-ada", display_name: "Ada" },
|
|
1649
|
+
{ id: "user-grace", display_name: "Grace" }
|
|
1650
|
+
],
|
|
1651
|
+
modules: {
|
|
1652
|
+
"work-pulse": [
|
|
1653
|
+
mod("mod-00000000000000000000000000000001", "Backlog objective", "backlog", { priority: "low" }),
|
|
1654
|
+
mod("mod-00000000000000000000000000000002", "Planned objective", "todo", { priority: "medium", start_date: "2026-04-01", target_date: "2026-12-01" }),
|
|
1655
|
+
mod("mod-00000000000000000000000000000003", "Slipped objective", "in_progress", { priority: "high", start_date: "2025-01-01", target_date: "2025-06-01" }),
|
|
1656
|
+
mod("mod-00000000000000000000000000000004", "On track objective", "in_progress", { priority: "urgent", start_date: "2026-03-01", target_date: "2026-11-15" }),
|
|
1657
|
+
mod("mod-00000000000000000000000000000005", "Shipped objective", "done", { priority: "medium", start_date: "2025-11-01", target_date: "2026-01-20" }),
|
|
1658
|
+
mod("mod-00000000000000000000000000000006", "Dropped objective", "cancelled", { priority: "low", target_date: "2025-03-01" }),
|
|
1659
|
+
mod("mod-00000000000000000000000000000007", "Undated objective", "in_progress", { priority: "low", start_date: "2026-05-01" })
|
|
1660
|
+
],
|
|
1661
|
+
"personal-pulse": [
|
|
1662
|
+
mod("mod-00000000000000000000000000000011", "Home objective", "in_progress", { priority: "medium", start_date: "2026-02-01", target_date: "2026-08-01" })
|
|
1663
|
+
],
|
|
1664
|
+
"work-aph": [
|
|
1665
|
+
mod("mod-00000000000000000000000000000021", "Aphrodite objective", "todo", { priority: "high", target_date: "2026-07-01" })
|
|
1666
|
+
],
|
|
1667
|
+
"personal-pkan": []
|
|
1668
|
+
},
|
|
1669
|
+
issues: {
|
|
1670
|
+
"work-pulse": [
|
|
1671
|
+
issue("wi-work-1", "Work pulse story", 1, "st-progress", { priority: "high", module_ids: ["mod-00000000000000000000000000000004"], assignee: "Ada", labels: ["lbl-roadmap"], target_date: "2026-11-01" }),
|
|
1672
|
+
issue("wi-work-2", "Work pulse done", 2, "st-done", { priority: "low", module_ids: ["mod-00000000000000000000000000000005"], assignee: "Grace" })
|
|
1673
|
+
],
|
|
1674
|
+
"personal-pulse": [
|
|
1675
|
+
issue("wi-home-1", "Personal pulse story", 1, "st-todo", { priority: "medium", module_ids: ["mod-00000000000000000000000000000011"], assignee: "Ada", labels: ["lbl-needs"], target_date: "2026-08-01" })
|
|
1676
|
+
],
|
|
1677
|
+
"work-aph": [issue("wi-aph-1", "Aphrodite story", 4, "st-backlog", { priority: "medium" })],
|
|
1678
|
+
"personal-pkan": [issue("wi-pkan-1", "Pipe kan story", 3, "st-todo", { priority: "low", assignee: "Grace" })]
|
|
1679
|
+
}
|
|
1680
|
+
};
|
|
1681
|
+
function page(results) {
|
|
1682
|
+
return { results, next_page_results: false, next_cursor: "" };
|
|
1683
|
+
}
|
|
1684
|
+
function planeFixtureFetch(fixture = SYNTHETIC_PLANE) {
|
|
1685
|
+
const data = JSON.parse(JSON.stringify(fixture));
|
|
1686
|
+
const stateById = new Map(data.states.map((state) => [state.id, state]));
|
|
1687
|
+
const labelById = new Map(data.labels.map((label) => [label.id, label]));
|
|
1688
|
+
return async (input, init) => {
|
|
1689
|
+
const url = new URL(String(input), "https://plane.test");
|
|
1690
|
+
const method = (init?.method ?? "GET").toUpperCase();
|
|
1691
|
+
const path = url.pathname.replace(/\/+$/, "") || "/";
|
|
1692
|
+
const parts = path.split("/").filter(Boolean);
|
|
1693
|
+
let body;
|
|
1694
|
+
if (init?.body)
|
|
1695
|
+
body = JSON.parse(String(init.body));
|
|
1696
|
+
if (method === "GET" && parts[2] === "users" && parts[4] === "workspaces") {
|
|
1697
|
+
return Response.json(data.workspaces);
|
|
1698
|
+
}
|
|
1699
|
+
if (method === "GET" && parts[2] === "workspaces" && parts.length === 3) {
|
|
1700
|
+
return Response.json(page(data.workspaces));
|
|
1701
|
+
}
|
|
1702
|
+
if (method === "GET" && /\/workspaces\/[^/]+\/projects$/.test(path)) {
|
|
1703
|
+
return Response.json(page(data.projects[parts[3] ?? ""] ?? []));
|
|
1704
|
+
}
|
|
1705
|
+
if (method === "GET" && parts[4] === "members" && parts.length === 5)
|
|
1706
|
+
return Response.json(page(data.members));
|
|
1707
|
+
const projectId = parts[5] ?? "";
|
|
1708
|
+
const collection = parts[6];
|
|
1709
|
+
const itemId = parts[7];
|
|
1710
|
+
const projectIssues = data.issues[projectId] ?? [];
|
|
1711
|
+
const projectModules = data.modules[projectId] ?? [];
|
|
1712
|
+
if (method === "GET" && collection === "states")
|
|
1713
|
+
return Response.json(page(data.states));
|
|
1714
|
+
if (method === "GET" && collection === "labels")
|
|
1715
|
+
return Response.json(page(data.labels));
|
|
1716
|
+
if (method === "GET" && collection === "modules" && !itemId)
|
|
1717
|
+
return Response.json(page(projectModules));
|
|
1718
|
+
if (method === "GET" && collection === "modules" && parts[8] === "module-issues") {
|
|
1719
|
+
const ids = projectIssues.filter((row) => row.module_ids?.includes(itemId ?? "")).map((row) => row.id);
|
|
1720
|
+
return Response.json(page(ids.map((id) => ({ issue: id, module: itemId }))));
|
|
1721
|
+
}
|
|
1722
|
+
if (method === "GET" && (collection === "work-items" || collection === "issues") && !itemId) {
|
|
1723
|
+
return Response.json(page(projectIssues.map((row) => toWorkItem(row, stateById, labelById))));
|
|
1724
|
+
}
|
|
1725
|
+
if (method === "PATCH" && collection === "modules" && itemId) {
|
|
1726
|
+
const module = projectModules.find((row) => row.id === itemId);
|
|
1727
|
+
if (module && typeof body?.status === "string")
|
|
1728
|
+
module.status = body.status;
|
|
1729
|
+
return Response.json({ id: itemId, ...body });
|
|
1730
|
+
}
|
|
1731
|
+
if (method === "PATCH" && (collection === "work-items" || collection === "issues") && itemId) {
|
|
1732
|
+
const issueRow = projectIssues.find((row) => row.id === itemId);
|
|
1733
|
+
if (issueRow && typeof body?.state === "string")
|
|
1734
|
+
issueRow.state = body.state;
|
|
1735
|
+
if (issueRow && typeof body?.priority === "string")
|
|
1736
|
+
issueRow.priority = body.priority;
|
|
1737
|
+
if (issueRow && typeof body?.name === "string")
|
|
1738
|
+
issueRow.name = body.name;
|
|
1739
|
+
return Response.json({ id: itemId, ...body });
|
|
1740
|
+
}
|
|
1741
|
+
if (method === "POST" && (collection === "work-items" || collection === "issues") && !itemId) {
|
|
1742
|
+
const sequence = 100 + projectIssues.length;
|
|
1743
|
+
projectIssues.push(issue(`wi-new-${sequence}`, String(body?.name ?? "New"), sequence, "st-todo"));
|
|
1744
|
+
return Response.json({ id: `wi-new-${sequence}`, sequence_id: sequence, name: body?.name }, { status: 201 });
|
|
1745
|
+
}
|
|
1746
|
+
return new Response(`unhandled ${method} ${path}`, { status: 404 });
|
|
1747
|
+
};
|
|
1748
|
+
}
|
|
1749
|
+
function toWorkItem(row, states, labels) {
|
|
1750
|
+
const state = states.get(row.state);
|
|
1751
|
+
return {
|
|
1752
|
+
id: row.id,
|
|
1753
|
+
name: row.name,
|
|
1754
|
+
sequence_id: row.sequence_id,
|
|
1755
|
+
priority: row.priority,
|
|
1756
|
+
state: state ? { id: state.id, name: state.name } : row.state,
|
|
1757
|
+
labels: row.labels ?? [],
|
|
1758
|
+
label_details: (row.labels ?? []).map((id) => labels.get(id)).filter(Boolean),
|
|
1759
|
+
module_ids: row.module_ids ?? [],
|
|
1760
|
+
assignees: row.assignee ? [{ display_name: row.assignee }] : [],
|
|
1761
|
+
created_at: row.created_at,
|
|
1762
|
+
updated_at: row.updated_at,
|
|
1763
|
+
target_date: row.target_date,
|
|
1764
|
+
start_date: row.start_date
|
|
1765
|
+
};
|
|
1766
|
+
}
|
|
1767
|
+
function resolvePlaneFetch(env = process.env) {
|
|
1768
|
+
const fixture = env.PLANE_FIXTURE?.trim();
|
|
1769
|
+
if (!fixture)
|
|
1770
|
+
return;
|
|
1771
|
+
if (fixture === "synthetic")
|
|
1772
|
+
return planeFixtureFetch(SYNTHETIC_PLANE);
|
|
1773
|
+
const parsed = JSON.parse(readFileSync3(fixture, "utf8"));
|
|
1774
|
+
return planeFixtureFetch(parsed);
|
|
1775
|
+
}
|
|
1776
|
+
|
|
1777
|
+
// src/parity.ts
|
|
1778
|
+
function normalizeStatus(status) {
|
|
1779
|
+
return status.trim().toLowerCase().replace(/[_-]+/g, " ").replace(/\s+/g, " ");
|
|
1780
|
+
}
|
|
1781
|
+
function parseDay(value) {
|
|
1782
|
+
if (!value)
|
|
1783
|
+
return;
|
|
1784
|
+
const day = /^(\d{4})-(\d{2})-(\d{2})/.exec(value.trim());
|
|
1785
|
+
if (day)
|
|
1786
|
+
return new Date(Number(day[1]), Number(day[2]) - 1, Number(day[3]));
|
|
1787
|
+
const parsed = new Date(value);
|
|
1788
|
+
if (Number.isNaN(parsed.getTime()))
|
|
1789
|
+
return;
|
|
1790
|
+
return new Date(parsed.getFullYear(), parsed.getMonth(), parsed.getDate());
|
|
1791
|
+
}
|
|
1792
|
+
function startOfToday(now) {
|
|
1793
|
+
return new Date(now.getFullYear(), now.getMonth(), now.getDate());
|
|
1794
|
+
}
|
|
1795
|
+
function computeHealth(status, targetDate, now = new Date) {
|
|
1796
|
+
const key = normalizeStatus(status);
|
|
1797
|
+
if (key === "done" || key === "completed" || key === "complete")
|
|
1798
|
+
return "green";
|
|
1799
|
+
if (key === "cancelled" || key === "canceled")
|
|
1800
|
+
return "red";
|
|
1801
|
+
if (key === "in progress" || key === "started" || key === "active" || key === "paused") {
|
|
1802
|
+
const target = parseDay(targetDate);
|
|
1803
|
+
if (key === "paused")
|
|
1804
|
+
return "yellow";
|
|
1805
|
+
if (target && target.getTime() < startOfToday(now).getTime())
|
|
1806
|
+
return "yellow";
|
|
1807
|
+
return "green";
|
|
1808
|
+
}
|
|
1809
|
+
if (key === "todo" || key === "to do" || key === "backlog" || key === "planned" || key === "unstarted" || key === "") {
|
|
1810
|
+
return "gray";
|
|
1811
|
+
}
|
|
1812
|
+
return "gray";
|
|
1813
|
+
}
|
|
1814
|
+
function isCompletedStatus(status) {
|
|
1815
|
+
const key = normalizeStatus(status);
|
|
1816
|
+
return key === "done" || key === "completed" || key === "complete" || key === "cancelled" || key === "canceled";
|
|
1817
|
+
}
|
|
1818
|
+
function sourceOptions(input) {
|
|
1819
|
+
return [
|
|
1820
|
+
{
|
|
1821
|
+
id: "plane",
|
|
1822
|
+
label: "Plane",
|
|
1823
|
+
enabled: input.planeKey,
|
|
1824
|
+
...input.planeKey ? {} : { reason: "Set PLANE_API_KEY to use Plane" }
|
|
1825
|
+
},
|
|
1826
|
+
{
|
|
1827
|
+
id: "jira",
|
|
1828
|
+
label: "Jira",
|
|
1829
|
+
enabled: true,
|
|
1830
|
+
...input.jiraBin ? {} : { reason: "jira-cli is not on PATH; Fake Jira is used" }
|
|
1831
|
+
},
|
|
1832
|
+
{
|
|
1833
|
+
id: "taskwarrior",
|
|
1834
|
+
label: "Taskwarrior",
|
|
1835
|
+
enabled: false,
|
|
1836
|
+
reason: "Taskwarrior is not connected"
|
|
1837
|
+
}
|
|
1838
|
+
];
|
|
1839
|
+
}
|
|
1840
|
+
|
|
1526
1841
|
// src/plane.ts
|
|
1527
1842
|
var DEFAULT_PLANE_HOST = "https://plane.tail48fe8.ts.net";
|
|
1528
1843
|
var DEFAULT_PLANE_WORKSPACE = "personal";
|
|
1844
|
+
var COMMON_WORKSPACE_SLUGS = ["personal", "work"];
|
|
1529
1845
|
var STATE_GROUPS = ["backlog", "unstarted", "started", "completed", "cancelled"];
|
|
1530
1846
|
function planeHost(env = process.env) {
|
|
1531
1847
|
return (env.PLANE_HOST ?? DEFAULT_PLANE_HOST).replace(/\/$/, "");
|
|
@@ -1692,12 +2008,14 @@ function workItemToIssue(item, project, opts) {
|
|
|
1692
2008
|
...opts.moduleKey ? { parent: { key: opts.moduleKey } } : {},
|
|
1693
2009
|
...labels.length ? { labels } : {},
|
|
1694
2010
|
...asString(item.created_at) ? { created: item.created_at } : {},
|
|
2011
|
+
...asString(item.updated_at) ? { updated: item.updated_at } : {},
|
|
1695
2012
|
...description ? { description } : {}
|
|
1696
2013
|
}
|
|
1697
2014
|
};
|
|
1698
2015
|
}
|
|
1699
2016
|
function moduleToIssue(module, project) {
|
|
1700
2017
|
const target = asString(module.target_date);
|
|
2018
|
+
const priority = titleCasePriority(module.priority);
|
|
1701
2019
|
return {
|
|
1702
2020
|
key: planeModuleKey(project.identifier, module.id),
|
|
1703
2021
|
fields: {
|
|
@@ -1705,8 +2023,10 @@ function moduleToIssue(module, project) {
|
|
|
1705
2023
|
status: { name: moduleStatusName(module.status) },
|
|
1706
2024
|
issuetype: { name: "Epic" },
|
|
1707
2025
|
issueType: { name: "Epic" },
|
|
2026
|
+
...priority ? { priority: { name: priority } } : {},
|
|
1708
2027
|
...target ? { duedate: target, "Target End Date": target } : {},
|
|
1709
|
-
...asString(module.created_at) ? { created: module.created_at } : {}
|
|
2028
|
+
...asString(module.created_at) ? { created: module.created_at } : {},
|
|
2029
|
+
...asString(module.updated_at) ? { updated: module.updated_at } : {}
|
|
1710
2030
|
}
|
|
1711
2031
|
};
|
|
1712
2032
|
}
|
|
@@ -1898,6 +2218,35 @@ function createPlaneCli(opts) {
|
|
|
1898
2218
|
return;
|
|
1899
2219
|
return { id: asString(row?.id) ?? slug, name: asString(row?.name) ?? slug, slug };
|
|
1900
2220
|
}
|
|
2221
|
+
const known = new Set;
|
|
2222
|
+
async function probeWorkspaces() {
|
|
2223
|
+
const env = opts.env ?? process.env;
|
|
2224
|
+
const fromFlags = parseFlags(`${defaultFlags} ${lastFlags}`);
|
|
2225
|
+
const candidates = new Set([
|
|
2226
|
+
...known,
|
|
2227
|
+
...fromFlags.workspaces,
|
|
2228
|
+
...fromFlags.workspace ? [fromFlags.workspace] : [],
|
|
2229
|
+
...fromFlags.scopes.map((scope) => scope.workspace),
|
|
2230
|
+
...env.PLANE_WORKSPACE ? [env.PLANE_WORKSPACE] : [],
|
|
2231
|
+
...(env.PLANE_WORKSPACES ?? "").split(","),
|
|
2232
|
+
...(env.PIPE_KAN_WORKSPACES ?? "").split(","),
|
|
2233
|
+
...COMMON_WORKSPACE_SLUGS
|
|
2234
|
+
]);
|
|
2235
|
+
const found = [];
|
|
2236
|
+
for (const raw of candidates) {
|
|
2237
|
+
const slug = raw.trim();
|
|
2238
|
+
if (!slug || !/^[A-Za-z0-9_-]+$/.test(slug))
|
|
2239
|
+
continue;
|
|
2240
|
+
try {
|
|
2241
|
+
await requestJson(queryPath(`/workspaces/${slug}/projects/`, { per_page: "1" }));
|
|
2242
|
+
known.add(slug);
|
|
2243
|
+
found.push({ id: slug, name: slug, slug });
|
|
2244
|
+
} catch {
|
|
2245
|
+
known.delete(slug);
|
|
2246
|
+
}
|
|
2247
|
+
}
|
|
2248
|
+
return found;
|
|
2249
|
+
}
|
|
1901
2250
|
function parseWorkspaceList(raw) {
|
|
1902
2251
|
const rows = Array.isArray(raw) ? raw : asRecord(raw)?.results;
|
|
1903
2252
|
if (!Array.isArray(rows))
|
|
@@ -1936,25 +2285,76 @@ function createPlaneCli(opts) {
|
|
|
1936
2285
|
status: asString(row?.status),
|
|
1937
2286
|
created_at: asString(row?.created_at),
|
|
1938
2287
|
target_date: asString(row?.target_date),
|
|
1939
|
-
start_date: asString(row?.start_date)
|
|
2288
|
+
start_date: asString(row?.start_date),
|
|
2289
|
+
priority: asString(row?.priority),
|
|
2290
|
+
updated_at: asString(row?.updated_at)
|
|
2291
|
+
};
|
|
2292
|
+
}
|
|
2293
|
+
function catalogPlan(flags) {
|
|
2294
|
+
const parsed = parseFlags(flags || defaultFlags);
|
|
2295
|
+
if (parsed.scopes.length) {
|
|
2296
|
+
return {
|
|
2297
|
+
slugs: [...new Set(parsed.scopes.map((scope) => scope.workspace))],
|
|
2298
|
+
pairs: parsed.scopes
|
|
2299
|
+
};
|
|
2300
|
+
}
|
|
2301
|
+
if (parsed.workspaces.length)
|
|
2302
|
+
return { slugs: parsed.workspaces, pairs: null };
|
|
2303
|
+
return { slugs: [workspaceOf(flags)], pairs: null };
|
|
2304
|
+
}
|
|
2305
|
+
function exposeKey(slug, key, multi) {
|
|
2306
|
+
return multi ? `${slug}/${key}` : key;
|
|
2307
|
+
}
|
|
2308
|
+
function prefixIssue(issue, slug, multi) {
|
|
2309
|
+
if (!multi || typeof issue.key !== "string")
|
|
2310
|
+
return issue;
|
|
2311
|
+
const parent = asRecord(issue.fields?.parent);
|
|
2312
|
+
const parentKey = asString(parent?.key);
|
|
2313
|
+
return {
|
|
2314
|
+
...issue,
|
|
2315
|
+
key: exposeKey(slug, issue.key, true),
|
|
2316
|
+
fields: {
|
|
2317
|
+
...issue.fields,
|
|
2318
|
+
...parentKey ? { parent: { ...parent, key: exposeKey(slug, parentKey, true) } } : {}
|
|
2319
|
+
}
|
|
1940
2320
|
};
|
|
1941
2321
|
}
|
|
2322
|
+
const projectCache = new Map;
|
|
2323
|
+
const cacheTtlMs = opts.cacheTtlMs ?? 30000;
|
|
2324
|
+
async function projectsFor(slug) {
|
|
2325
|
+
const hit = projectCache.get(slug);
|
|
2326
|
+
const now = nowFn();
|
|
2327
|
+
if (cacheTtlMs > 0 && hit && now - hit.at < cacheTtlMs)
|
|
2328
|
+
return hit.rows;
|
|
2329
|
+
const rows = (await paginate(`/workspaces/${slug}/projects/`)).map(parseProject).filter((row) => !!row).map((project) => ({ ...project, workspace: slug }));
|
|
2330
|
+
projectCache.set(slug, { at: now, rows });
|
|
2331
|
+
return rows;
|
|
2332
|
+
}
|
|
1942
2333
|
async function loadCatalog(flags) {
|
|
1943
2334
|
lastFlags = flags || defaultFlags;
|
|
1944
|
-
const
|
|
1945
|
-
|
|
2335
|
+
const plan = catalogPlan(lastFlags);
|
|
2336
|
+
const cacheKey = plan.slugs.join(",");
|
|
2337
|
+
if (catalog && catalog.flags === lastFlags && catalog.cacheKey === cacheKey)
|
|
1946
2338
|
return catalog;
|
|
1947
|
-
const
|
|
2339
|
+
const multi = plan.slugs.length > 1;
|
|
2340
|
+
const projects = [];
|
|
1948
2341
|
const states = new Map;
|
|
1949
2342
|
const labels = new Map;
|
|
1950
2343
|
const works = new Map;
|
|
1951
2344
|
const modules = new Map;
|
|
1952
2345
|
const columnStates = [];
|
|
2346
|
+
for (const slug of plan.slugs) {
|
|
2347
|
+
const listed = await projectsFor(slug);
|
|
2348
|
+
const chosen = plan.pairs ? listed.filter((project) => plan.pairs.some((pair) => pair.workspace === slug && pair.project === project.identifier.toUpperCase())) : scopedProjects(listed, lastFlags);
|
|
2349
|
+
for (const project of chosen)
|
|
2350
|
+
projects.push(project);
|
|
2351
|
+
}
|
|
1953
2352
|
for (const project of projects) {
|
|
1954
|
-
const
|
|
1955
|
-
const
|
|
1956
|
-
const
|
|
1957
|
-
const
|
|
2353
|
+
const slug = project.workspace || plan.slugs[0] || workspaceOf(lastFlags);
|
|
2354
|
+
const stateRows = await paginate(`/workspaces/${slug}/projects/${project.id}/states/`);
|
|
2355
|
+
const labelRows = await paginate(`/workspaces/${slug}/projects/${project.id}/labels/`);
|
|
2356
|
+
const moduleRows = await paginate(`/workspaces/${slug}/projects/${project.id}/modules/`);
|
|
2357
|
+
const workRows = await withResource((resource) => paginate(queryPath(`/workspaces/${slug}/projects/${project.id}/${resource}/`, {
|
|
1958
2358
|
expand: "assignees,labels,state,module"
|
|
1959
2359
|
})));
|
|
1960
2360
|
const stateById = new Map;
|
|
@@ -1976,7 +2376,7 @@ function createPlaneCli(opts) {
|
|
|
1976
2376
|
const module = parseModule(row);
|
|
1977
2377
|
if (!module)
|
|
1978
2378
|
continue;
|
|
1979
|
-
const issue = moduleToIssue(module, project);
|
|
2379
|
+
const issue = prefixIssue(moduleToIssue(module, project), slug, multi);
|
|
1980
2380
|
const cached = { issue, module, project };
|
|
1981
2381
|
moduleById.set(module.id, cached);
|
|
1982
2382
|
modules.set(String(issue.key), cached);
|
|
@@ -1988,7 +2388,7 @@ function createPlaneCli(opts) {
|
|
|
1988
2388
|
const issueByModule = new Map;
|
|
1989
2389
|
if (missingModules && moduleById.size) {
|
|
1990
2390
|
for (const moduleId of moduleById.keys()) {
|
|
1991
|
-
const rows = await paginate(`/workspaces/${
|
|
2391
|
+
const rows = await paginate(`/workspaces/${slug}/projects/${project.id}/modules/${moduleId}/module-issues/`);
|
|
1992
2392
|
const ids = rows.map((row) => asId(asRecord(row)?.issue) ?? asId(row)).filter((id) => !!id);
|
|
1993
2393
|
issueByModule.set(moduleId, ids);
|
|
1994
2394
|
}
|
|
@@ -1998,26 +2398,29 @@ function createPlaneCli(opts) {
|
|
|
1998
2398
|
const fromItem = moduleIdsOf(item)[0];
|
|
1999
2399
|
const fromJoin = [...issueByModule.entries()].find(([, ids]) => ids.includes(item.id))?.[0];
|
|
2000
2400
|
const moduleId = fromItem ?? fromJoin;
|
|
2001
|
-
const
|
|
2401
|
+
const bareModuleKey = moduleId ? planeModuleKey(project.identifier, moduleId) : undefined;
|
|
2002
2402
|
const issue = workItemToIssue(item, project, {
|
|
2003
2403
|
states: stateById,
|
|
2004
2404
|
labels: labelById,
|
|
2005
|
-
|
|
2405
|
+
...bareModuleKey ? { moduleKey: bareModuleKey } : {}
|
|
2006
2406
|
});
|
|
2007
|
-
const
|
|
2008
|
-
|
|
2407
|
+
const stamped = issue ? prefixIssue(issue, slug, multi) : undefined;
|
|
2408
|
+
const issueKey = typeof stamped?.key === "string" ? stamped.key : undefined;
|
|
2409
|
+
if (!stamped || !issueKey)
|
|
2009
2410
|
continue;
|
|
2411
|
+
const linked = bareModuleKey ? exposeKey(slug, bareModuleKey, multi) : undefined;
|
|
2010
2412
|
works.set(issueKey, {
|
|
2011
|
-
issue,
|
|
2413
|
+
issue: stamped,
|
|
2012
2414
|
item,
|
|
2013
2415
|
project,
|
|
2014
|
-
...
|
|
2416
|
+
...linked ? { moduleKey: linked } : {}
|
|
2015
2417
|
});
|
|
2016
2418
|
}
|
|
2017
2419
|
}
|
|
2018
2420
|
catalog = {
|
|
2019
2421
|
flags: lastFlags,
|
|
2020
|
-
workspace,
|
|
2422
|
+
workspace: plan.slugs[0] || workspaceOf(lastFlags),
|
|
2423
|
+
cacheKey,
|
|
2021
2424
|
projects,
|
|
2022
2425
|
states,
|
|
2023
2426
|
labels,
|
|
@@ -2027,6 +2430,73 @@ function createPlaneCli(opts) {
|
|
|
2027
2430
|
};
|
|
2028
2431
|
return catalog;
|
|
2029
2432
|
}
|
|
2433
|
+
function projectSlug(project) {
|
|
2434
|
+
return project.workspace || workspaceOf(lastFlags);
|
|
2435
|
+
}
|
|
2436
|
+
function buildParity(loaded) {
|
|
2437
|
+
const projects = loaded.projects.map((project) => {
|
|
2438
|
+
const issues = [...loaded.works.values()].filter((row) => row.project.id === project.id && row.project.workspace === project.workspace);
|
|
2439
|
+
const modules = [...loaded.modules.values()].filter((row) => row.project.id === project.id && row.project.workspace === project.workspace);
|
|
2440
|
+
const total = issues.length;
|
|
2441
|
+
const completed = issues.filter((row) => {
|
|
2442
|
+
const name = typeof row.issue.fields?.status?.name === "string" ? row.issue.fields.status.name : "";
|
|
2443
|
+
const key = name.toLowerCase();
|
|
2444
|
+
return key === "done" || key === "completed";
|
|
2445
|
+
}).length;
|
|
2446
|
+
const started = issues.some((row) => {
|
|
2447
|
+
const name = typeof row.issue.fields?.status?.name === "string" ? row.issue.fields.status.name : "";
|
|
2448
|
+
return name.toLowerCase() === "in progress";
|
|
2449
|
+
});
|
|
2450
|
+
const status = total > 0 && completed === total ? "done" : started ? "in_progress" : "backlog";
|
|
2451
|
+
const dates = [
|
|
2452
|
+
...modules.map((row) => row.module.target_date),
|
|
2453
|
+
...issues.map((row) => row.item.target_date)
|
|
2454
|
+
].filter((value) => Boolean(value));
|
|
2455
|
+
const starts = [
|
|
2456
|
+
...modules.map((row) => row.module.start_date),
|
|
2457
|
+
...issues.map((row) => row.item.start_date)
|
|
2458
|
+
].filter((value) => Boolean(value));
|
|
2459
|
+
const targetDate = dates.sort().at(-1);
|
|
2460
|
+
const startDate = starts.sort()[0];
|
|
2461
|
+
return {
|
|
2462
|
+
workspace: project.workspace || loaded.workspace,
|
|
2463
|
+
id: project.id,
|
|
2464
|
+
identifier: project.identifier,
|
|
2465
|
+
name: project.name,
|
|
2466
|
+
total,
|
|
2467
|
+
completed,
|
|
2468
|
+
status,
|
|
2469
|
+
...startDate ? { startDate } : {},
|
|
2470
|
+
...targetDate ? { targetDate } : {},
|
|
2471
|
+
health: computeHealth(status, targetDate)
|
|
2472
|
+
};
|
|
2473
|
+
});
|
|
2474
|
+
const active = new Map(projects.map((project) => [`${project.workspace}/${project.id}`, project.completed < project.total || project.total === 0]));
|
|
2475
|
+
const initiatives = [...loaded.modules.values()].map((row) => {
|
|
2476
|
+
const raw = row.module.status ?? "backlog";
|
|
2477
|
+
const priority = titleCasePriority(row.module.priority);
|
|
2478
|
+
const open = active.get(`${row.project.workspace}/${row.project.id}`) !== false;
|
|
2479
|
+
return {
|
|
2480
|
+
key: String(row.issue.key),
|
|
2481
|
+
name: row.module.name,
|
|
2482
|
+
workspace: row.project.workspace || loaded.workspace,
|
|
2483
|
+
project: row.project.identifier,
|
|
2484
|
+
status: moduleStatusName(raw),
|
|
2485
|
+
rawStatus: raw,
|
|
2486
|
+
...priority ? { priority } : {},
|
|
2487
|
+
...row.module.start_date ? { startDate: row.module.start_date } : {},
|
|
2488
|
+
...row.module.target_date ? { targetDate: row.module.target_date } : {},
|
|
2489
|
+
...row.module.updated_at ? { updated: row.module.updated_at } : {},
|
|
2490
|
+
health: computeHealth(raw, row.module.target_date),
|
|
2491
|
+
activeProjects: isCompletedStatus(raw) || !open ? 0 : 1
|
|
2492
|
+
};
|
|
2493
|
+
});
|
|
2494
|
+
return {
|
|
2495
|
+
initiatives,
|
|
2496
|
+
projects,
|
|
2497
|
+
workspaces: [...new Set(loaded.projects.map((project) => project.workspace || loaded.workspace))]
|
|
2498
|
+
};
|
|
2499
|
+
}
|
|
2030
2500
|
function filterWorks(flags, works) {
|
|
2031
2501
|
const parsed = parseFlags(flags || defaultFlags);
|
|
2032
2502
|
return works.filter(({ issue }) => {
|
|
@@ -2122,21 +2592,31 @@ function createPlaneCli(opts) {
|
|
|
2122
2592
|
async listWorkspaces() {
|
|
2123
2593
|
for (const path of ["/users/me/workspaces/", "/workspaces/"]) {
|
|
2124
2594
|
try {
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
continue;
|
|
2130
|
-
return [];
|
|
2131
|
-
}
|
|
2595
|
+
const listed = parseWorkspaceList(await requestJson(path));
|
|
2596
|
+
if (listed?.length)
|
|
2597
|
+
return listed;
|
|
2598
|
+
} catch {}
|
|
2132
2599
|
}
|
|
2133
|
-
return
|
|
2600
|
+
return probeWorkspaces();
|
|
2134
2601
|
},
|
|
2135
2602
|
async listProjectIdentifiers(workspace) {
|
|
2136
2603
|
const slug = workspace.trim();
|
|
2137
2604
|
if (!slug)
|
|
2138
2605
|
return [];
|
|
2139
|
-
return (await
|
|
2606
|
+
return (await projectsFor(slug)).map((row) => row.identifier);
|
|
2607
|
+
},
|
|
2608
|
+
async listProjects(workspace) {
|
|
2609
|
+
const slug = workspace.trim();
|
|
2610
|
+
if (!slug)
|
|
2611
|
+
return [];
|
|
2612
|
+
return (await projectsFor(slug)).map((row) => ({
|
|
2613
|
+
id: row.id,
|
|
2614
|
+
identifier: row.identifier,
|
|
2615
|
+
name: row.name
|
|
2616
|
+
}));
|
|
2617
|
+
},
|
|
2618
|
+
async parity(flags) {
|
|
2619
|
+
return buildParity(await loadCatalog(flags || lastFlags));
|
|
2140
2620
|
},
|
|
2141
2621
|
async move(key, status) {
|
|
2142
2622
|
try {
|
|
@@ -2155,7 +2635,7 @@ function createPlaneCli(opts) {
|
|
|
2155
2635
|
}).find(([name]) => name === status.toLowerCase())?.[1];
|
|
2156
2636
|
if (!mapped)
|
|
2157
2637
|
return { ok: false, error: `Unknown Plane module status "${status}"` };
|
|
2158
|
-
await request(`/workspaces/${
|
|
2638
|
+
await request(`/workspaces/${projectSlug(module.project)}/projects/${module.project.id}/modules/${module.module.id}/`, { method: "PATCH", body: JSON.stringify({ status: mapped }) });
|
|
2159
2639
|
catalog = undefined;
|
|
2160
2640
|
return { ok: true };
|
|
2161
2641
|
}
|
|
@@ -2165,7 +2645,7 @@ function createPlaneCli(opts) {
|
|
|
2165
2645
|
const state = await resolveStateId(work.project, status, lastFlags);
|
|
2166
2646
|
if (!state)
|
|
2167
2647
|
return { ok: false, error: `Unknown Plane state "${status}" for ${key}` };
|
|
2168
|
-
await withResource((resource) => request(`/workspaces/${
|
|
2648
|
+
await withResource((resource) => request(`/workspaces/${projectSlug(work.project)}/projects/${work.project.id}/${resource}/${work.item.id}/`, { method: "PATCH", body: JSON.stringify({ state }) }));
|
|
2169
2649
|
catalog = undefined;
|
|
2170
2650
|
return { ok: true };
|
|
2171
2651
|
} catch (err) {
|
|
@@ -2188,14 +2668,14 @@ function createPlaneCli(opts) {
|
|
|
2188
2668
|
body.state = state;
|
|
2189
2669
|
if (labels.length)
|
|
2190
2670
|
body.labels = labels;
|
|
2191
|
-
const created = asRecord(await withResource((resource) => requestJson(`/workspaces/${
|
|
2671
|
+
const created = asRecord(await withResource((resource) => requestJson(`/workspaces/${projectSlug(project)}/projects/${project.id}/${resource}/`, {
|
|
2192
2672
|
method: "POST",
|
|
2193
2673
|
body: JSON.stringify(body)
|
|
2194
2674
|
})));
|
|
2195
2675
|
const sequence = Number(created?.sequence_id);
|
|
2196
2676
|
const key = Number.isFinite(sequence) && sequence > 0 ? planeWorkItemKey(project.identifier, sequence) : undefined;
|
|
2197
2677
|
if (parent && created?.id) {
|
|
2198
|
-
await request(`/workspaces/${
|
|
2678
|
+
await request(`/workspaces/${projectSlug(project)}/projects/${project.id}/modules/${parent.module.id}/module-issues/`, { method: "POST", body: JSON.stringify({ issues: [created.id] }) });
|
|
2199
2679
|
}
|
|
2200
2680
|
catalog = undefined;
|
|
2201
2681
|
return key ? { ok: true, key } : { ok: true };
|
|
@@ -2205,6 +2685,24 @@ function createPlaneCli(opts) {
|
|
|
2205
2685
|
},
|
|
2206
2686
|
async edit(key, input) {
|
|
2207
2687
|
try {
|
|
2688
|
+
const loaded = await loadCatalog(lastFlags);
|
|
2689
|
+
const module = loaded.modules.get(key);
|
|
2690
|
+
if (module) {
|
|
2691
|
+
if (input.assignee !== undefined)
|
|
2692
|
+
return { ok: false, error: "Plane modules have no assignee field" };
|
|
2693
|
+
if (input.labels)
|
|
2694
|
+
return { ok: false, error: "Plane modules have no labels field" };
|
|
2695
|
+
const body = {};
|
|
2696
|
+
if (input.summary !== undefined)
|
|
2697
|
+
body.name = input.summary;
|
|
2698
|
+
if (input.priority !== undefined)
|
|
2699
|
+
body.priority = input.priority.toLowerCase();
|
|
2700
|
+
if (Object.keys(body).length) {
|
|
2701
|
+
await request(`/workspaces/${projectSlug(module.project)}/projects/${module.project.id}/modules/${module.module.id}/`, { method: "PATCH", body: JSON.stringify(body) });
|
|
2702
|
+
}
|
|
2703
|
+
catalog = undefined;
|
|
2704
|
+
return { ok: true };
|
|
2705
|
+
}
|
|
2208
2706
|
const work = await lookupByKey(key, lastFlags);
|
|
2209
2707
|
if (!work)
|
|
2210
2708
|
return { ok: false, error: `Work item ${key} not found` };
|
|
@@ -2215,7 +2713,19 @@ function createPlaneCli(opts) {
|
|
|
2215
2713
|
body.description_html = htmlParagraph(input.description);
|
|
2216
2714
|
if (input.labels)
|
|
2217
2715
|
body.labels = await resolveLabelIds(work.project, input.labels, lastFlags);
|
|
2218
|
-
|
|
2716
|
+
if (input.priority !== undefined)
|
|
2717
|
+
body.priority = input.priority.toLowerCase();
|
|
2718
|
+
if (input.assignee !== undefined) {
|
|
2719
|
+
const slug = projectSlug(work.project);
|
|
2720
|
+
const members = (await paginate(`/workspaces/${slug}/members/`)).map((row) => asRecord(row));
|
|
2721
|
+
const wanted = input.assignee.trim().toLowerCase();
|
|
2722
|
+
const member = members.find((row) => asString(row?.display_name)?.toLowerCase() === wanted);
|
|
2723
|
+
const memberId = asString(member?.id);
|
|
2724
|
+
if (wanted && !memberId)
|
|
2725
|
+
return { ok: false, error: `Plane member "${input.assignee}" was not found` };
|
|
2726
|
+
body.assignees = memberId ? [memberId] : [];
|
|
2727
|
+
}
|
|
2728
|
+
await withResource((resource) => request(`/workspaces/${projectSlug(work.project)}/projects/${work.project.id}/${resource}/${work.item.id}/`, { method: "PATCH", body: JSON.stringify(body) }));
|
|
2219
2729
|
catalog = undefined;
|
|
2220
2730
|
return { ok: true };
|
|
2221
2731
|
} catch (err) {
|
|
@@ -2223,7 +2733,12 @@ function createPlaneCli(opts) {
|
|
|
2223
2733
|
}
|
|
2224
2734
|
},
|
|
2225
2735
|
async open(key) {
|
|
2226
|
-
|
|
2736
|
+
const loaded = await loadCatalog(lastFlags);
|
|
2737
|
+
const module = loaded.modules.get(key);
|
|
2738
|
+
const work = loaded.works.get(key);
|
|
2739
|
+
const slug = module?.project.workspace || work?.project.workspace || workspaceOf(lastFlags);
|
|
2740
|
+
const bare = key.includes("/") ? key.slice(key.indexOf("/") + 1) : key;
|
|
2741
|
+
return `${host}/${slug}/browse/${bare}`;
|
|
2227
2742
|
},
|
|
2228
2743
|
async view(key) {
|
|
2229
2744
|
const loaded = await loadCatalog(lastFlags);
|
|
@@ -2245,13 +2760,16 @@ async function createBoardApp(opts) {
|
|
|
2245
2760
|
const store = IssueStore.fromRaw(opts.raw);
|
|
2246
2761
|
const plane = wantsPlane(flags);
|
|
2247
2762
|
const bin = resolveJiraBin(env.JIRA_BIN ?? "jira", env.PATH ?? "");
|
|
2763
|
+
const fixtureMode = Boolean(env.PLANE_FIXTURE?.trim());
|
|
2764
|
+
const useJira = Boolean(bin) && !fixtureMode;
|
|
2248
2765
|
const cli = plane ? createPlaneCli({
|
|
2249
2766
|
host: planeHost(env),
|
|
2250
2767
|
apiKey: env.PLANE_API_KEY ?? "",
|
|
2251
2768
|
workspace: planeWorkspace(flags, env),
|
|
2252
2769
|
flags,
|
|
2253
|
-
|
|
2254
|
-
|
|
2770
|
+
env,
|
|
2771
|
+
fetch: opts.fetch ?? resolvePlaneFetch(env)
|
|
2772
|
+
}) : useJira ? createJiraCli({
|
|
2255
2773
|
bin,
|
|
2256
2774
|
configPath: env.JIRA_CONFIG_FILE,
|
|
2257
2775
|
token: env.JIRA_API_TOKEN,
|
|
@@ -2270,7 +2788,7 @@ async function createBoardApp(opts) {
|
|
|
2270
2788
|
const local = createStoreCli(store, flags);
|
|
2271
2789
|
app.hydrate(JSON.parse(await local.list(app.flags)), { fromStore: true });
|
|
2272
2790
|
}
|
|
2273
|
-
return { app, store, kind: plane ? "plane" :
|
|
2791
|
+
return { app, store, kind: plane ? "plane" : useJira ? "jira" : "store" };
|
|
2274
2792
|
}
|
|
2275
2793
|
async function refreshFromJira(app, kind, opts = {}) {
|
|
2276
2794
|
if (opts.piped)
|
|
@@ -2281,7 +2799,7 @@ async function refreshFromJira(app, kind, opts = {}) {
|
|
|
2281
2799
|
}
|
|
2282
2800
|
|
|
2283
2801
|
// src/factory-ledger.ts
|
|
2284
|
-
import { existsSync as existsSync2, readFileSync as
|
|
2802
|
+
import { existsSync as existsSync2, readFileSync as readFileSync4, statSync } from "node:fs";
|
|
2285
2803
|
import { homedir as homedir3 } from "node:os";
|
|
2286
2804
|
import { join as join4 } from "node:path";
|
|
2287
2805
|
var DEFAULT_LEDGER_PATH = "~/code/software-factory/asf/runtime/factory-coordinator/jobs.jsonl";
|
|
@@ -2388,7 +2906,7 @@ function readLedger(env = process.env) {
|
|
|
2388
2906
|
if (_cache && _cache.path === path && _cache.mtime === mtime) {
|
|
2389
2907
|
return { jobs: _cache.jobs };
|
|
2390
2908
|
}
|
|
2391
|
-
const text =
|
|
2909
|
+
const text = readFileSync4(path, "utf8");
|
|
2392
2910
|
const jobs = classifyJobs(parseJobsJsonl(text));
|
|
2393
2911
|
_cache = { path, mtime, jobs };
|
|
2394
2912
|
return { jobs };
|
|
@@ -2456,7 +2974,9 @@ function handleAppApi(req, res, app, opts = {}) {
|
|
|
2456
2974
|
return;
|
|
2457
2975
|
}
|
|
2458
2976
|
try {
|
|
2459
|
-
|
|
2977
|
+
const projects = await app.listProjectIdentifiers?.(workspace) ?? [];
|
|
2978
|
+
const details = await app.listProjects?.(workspace) ?? [];
|
|
2979
|
+
json(res, 200, { projects, ...details.length ? { details } : {} });
|
|
2460
2980
|
} catch (err) {
|
|
2461
2981
|
const message = err instanceof Error ? err.message : "catalog failed";
|
|
2462
2982
|
json(res, 409, { error: message });
|
|
@@ -2464,6 +2984,38 @@ function handleAppApi(req, res, app, opts = {}) {
|
|
|
2464
2984
|
});
|
|
2465
2985
|
return true;
|
|
2466
2986
|
}
|
|
2987
|
+
if (url.pathname === "/api/parity" && method === "GET") {
|
|
2988
|
+
reply(req, res, async () => {
|
|
2989
|
+
json(res, 200, await app.parity?.() ?? { initiatives: [], projects: [], workspaces: [] });
|
|
2990
|
+
});
|
|
2991
|
+
return true;
|
|
2992
|
+
}
|
|
2993
|
+
if (url.pathname === "/api/sources" && method === "GET") {
|
|
2994
|
+
reply(req, res, async () => {
|
|
2995
|
+
const current = kind === "plane" ? "plane" : "jira";
|
|
2996
|
+
json(res, 200, {
|
|
2997
|
+
source: current,
|
|
2998
|
+
kind,
|
|
2999
|
+
sources: sourceOptions({
|
|
3000
|
+
planeKey: Boolean(env.PLANE_API_KEY?.trim() || env.PLANE_FIXTURE?.trim()),
|
|
3001
|
+
jiraBin: Boolean(resolveJiraBin(env.JIRA_BIN ?? "jira", env.PATH ?? ""))
|
|
3002
|
+
})
|
|
3003
|
+
});
|
|
3004
|
+
});
|
|
3005
|
+
return true;
|
|
3006
|
+
}
|
|
3007
|
+
if (url.pathname === "/api/source" && method === "POST") {
|
|
3008
|
+
reply(req, res, async (text) => {
|
|
3009
|
+
if (!opts.onSource) {
|
|
3010
|
+
json(res, 404, { error: "source switch unavailable" });
|
|
3011
|
+
return;
|
|
3012
|
+
}
|
|
3013
|
+
const body = text ? JSON.parse(text) : {};
|
|
3014
|
+
const result = await opts.onSource(String(body.source ?? ""));
|
|
3015
|
+
json(res, result.ok ? 200 : 409, result);
|
|
3016
|
+
});
|
|
3017
|
+
return true;
|
|
3018
|
+
}
|
|
2467
3019
|
if (url.pathname === "/api/refresh" && method === "POST") {
|
|
2468
3020
|
reply(req, res, async (text) => {
|
|
2469
3021
|
const body = text ? JSON.parse(text) : {};
|
|
@@ -2533,6 +3085,10 @@ function handleAppApi(req, res, app, opts = {}) {
|
|
|
2533
3085
|
input.summary = String(body.summary);
|
|
2534
3086
|
if (body.description !== undefined)
|
|
2535
3087
|
input.description = String(body.description);
|
|
3088
|
+
if (body.assignee !== undefined)
|
|
3089
|
+
input.assignee = String(body.assignee);
|
|
3090
|
+
if (body.priority !== undefined)
|
|
3091
|
+
input.priority = String(body.priority);
|
|
2536
3092
|
if (Array.isArray(body.labels)) {
|
|
2537
3093
|
input.labels = body.labels.filter((item) => typeof item === "string");
|
|
2538
3094
|
}
|
|
@@ -2650,7 +3206,7 @@ function formatCard(card) {
|
|
|
2650
3206
|
}
|
|
2651
3207
|
|
|
2652
3208
|
// src/server/agent/config.ts
|
|
2653
|
-
import { existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as
|
|
3209
|
+
import { existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync5, writeFileSync as writeFileSync2 } from "node:fs";
|
|
2654
3210
|
import { homedir as homedir4 } from "node:os";
|
|
2655
3211
|
import { delimiter as delimiter2, dirname as dirname2, join as join5 } from "node:path";
|
|
2656
3212
|
var DEFAULT_CONFIG_PATH = join5(homedir4(), ".config", "pipe-kan", "agent.json");
|
|
@@ -2709,7 +3265,7 @@ function loadAgentConfig(path = agentConfigPath()) {
|
|
|
2709
3265
|
if (!existsSync3(path))
|
|
2710
3266
|
return structuredClone(DEFAULT_CONFIG);
|
|
2711
3267
|
try {
|
|
2712
|
-
const raw = JSON.parse(
|
|
3268
|
+
const raw = JSON.parse(readFileSync5(path, "utf8"));
|
|
2713
3269
|
return mergeConfig(raw);
|
|
2714
3270
|
} catch {
|
|
2715
3271
|
return structuredClone(DEFAULT_CONFIG);
|
|
@@ -3236,7 +3792,7 @@ function parsedType(data) {
|
|
|
3236
3792
|
}
|
|
3237
3793
|
return t;
|
|
3238
3794
|
}
|
|
3239
|
-
function
|
|
3795
|
+
function issue2(...args) {
|
|
3240
3796
|
const [iss, input, inst] = args;
|
|
3241
3797
|
if (typeof iss === "string") {
|
|
3242
3798
|
return {
|
|
@@ -5663,7 +6219,7 @@ function handleRefineResult(result, payload, input, inst) {
|
|
|
5663
6219
|
};
|
|
5664
6220
|
if (inst._zod.def.params)
|
|
5665
6221
|
_iss.params = inst._zod.def.params;
|
|
5666
|
-
payload.issues.push(
|
|
6222
|
+
payload.issues.push(issue2(_iss));
|
|
5667
6223
|
}
|
|
5668
6224
|
}
|
|
5669
6225
|
// node_modules/zod/v4/core/memoizer.js
|
|
@@ -6489,11 +7045,11 @@ function _refine(Class, fn, _params) {
|
|
|
6489
7045
|
}
|
|
6490
7046
|
function _superRefine(fn, params) {
|
|
6491
7047
|
const ch = _check((payload) => {
|
|
6492
|
-
payload.addIssue = (
|
|
6493
|
-
if (typeof
|
|
6494
|
-
payload.issues.push(issue
|
|
7048
|
+
payload.addIssue = (issue) => {
|
|
7049
|
+
if (typeof issue === "string") {
|
|
7050
|
+
payload.issues.push(issue2(issue, payload.value, ch._zod.def));
|
|
6495
7051
|
} else {
|
|
6496
|
-
const _issue =
|
|
7052
|
+
const _issue = issue;
|
|
6497
7053
|
if (_issue.fatal)
|
|
6498
7054
|
_issue.continue = false;
|
|
6499
7055
|
_issue.code ?? (_issue.code = "custom");
|
|
@@ -6501,7 +7057,7 @@ function _superRefine(fn, params) {
|
|
|
6501
7057
|
_issue.input = payload.value;
|
|
6502
7058
|
_issue.inst ?? (_issue.inst = ch);
|
|
6503
7059
|
_issue.continue ?? (_issue.continue = !ch._zod.def.abort);
|
|
6504
|
-
payload.issues.push(
|
|
7060
|
+
payload.issues.push(issue2(_issue));
|
|
6505
7061
|
}
|
|
6506
7062
|
};
|
|
6507
7063
|
return fn(payload.value, payload);
|
|
@@ -8243,18 +8799,18 @@ var ZodTransform = /* @__PURE__ */ $constructor("ZodTransform", (inst, def) => {
|
|
|
8243
8799
|
if (_ctx.direction === "backward") {
|
|
8244
8800
|
throw new $ZodEncodeError(inst.constructor.name);
|
|
8245
8801
|
}
|
|
8246
|
-
payload.addIssue = (
|
|
8247
|
-
if (typeof
|
|
8248
|
-
payload.issues.push(issue
|
|
8802
|
+
payload.addIssue = (issue) => {
|
|
8803
|
+
if (typeof issue === "string") {
|
|
8804
|
+
payload.issues.push(issue2(issue, payload.value, def));
|
|
8249
8805
|
} else {
|
|
8250
|
-
const _issue =
|
|
8806
|
+
const _issue = issue;
|
|
8251
8807
|
if (_issue.fatal)
|
|
8252
8808
|
_issue.continue = false;
|
|
8253
8809
|
_issue.code ?? (_issue.code = "custom");
|
|
8254
8810
|
if (!("input" in _issue))
|
|
8255
8811
|
_issue.input = payload.value;
|
|
8256
8812
|
_issue.inst ?? (_issue.inst = inst);
|
|
8257
|
-
payload.issues.push(
|
|
8813
|
+
payload.issues.push(issue2(_issue));
|
|
8258
8814
|
}
|
|
8259
8815
|
};
|
|
8260
8816
|
const output = def.transform(payload.value, payload);
|
|
@@ -12791,7 +13347,7 @@ function permissionOptionForDecision(options, decision) {
|
|
|
12791
13347
|
}
|
|
12792
13348
|
|
|
12793
13349
|
// src/server/agent/skills.ts
|
|
12794
|
-
import { existsSync as existsSync4, readFileSync as
|
|
13350
|
+
import { existsSync as existsSync4, readFileSync as readFileSync6, readdirSync } from "node:fs";
|
|
12795
13351
|
import { homedir as homedir5 } from "node:os";
|
|
12796
13352
|
import { join as join7 } from "node:path";
|
|
12797
13353
|
import { fileURLToPath } from "node:url";
|
|
@@ -12840,7 +13396,7 @@ function skillPath(dir, id) {
|
|
|
12840
13396
|
}
|
|
12841
13397
|
function readSkill(path, id) {
|
|
12842
13398
|
try {
|
|
12843
|
-
const text =
|
|
13399
|
+
const text = readFileSync6(path, "utf8");
|
|
12844
13400
|
const front = parseFrontMatter(text);
|
|
12845
13401
|
return {
|
|
12846
13402
|
id,
|
|
@@ -12867,7 +13423,7 @@ function parseFrontMatter(text) {
|
|
|
12867
13423
|
}
|
|
12868
13424
|
|
|
12869
13425
|
// src/server/agent/tools.ts
|
|
12870
|
-
import { mkdirSync as mkdirSync3, readFileSync as
|
|
13426
|
+
import { mkdirSync as mkdirSync3, readFileSync as readFileSync7, writeFileSync as writeFileSync3 } from "node:fs";
|
|
12871
13427
|
import { join as join8 } from "node:path";
|
|
12872
13428
|
var TOOLS = [
|
|
12873
13429
|
{
|
|
@@ -13017,7 +13573,7 @@ var EXECUTORS = {
|
|
|
13017
13573
|
return { ok: false, error: "Path traversal not allowed" };
|
|
13018
13574
|
const repoRoot = process.cwd();
|
|
13019
13575
|
try {
|
|
13020
|
-
const text =
|
|
13576
|
+
const text = readFileSync7(join8(repoRoot, relPath), "utf8");
|
|
13021
13577
|
return { ok: true, value: { path: relPath, text } };
|
|
13022
13578
|
} catch (err) {
|
|
13023
13579
|
return { ok: false, error: String(err) };
|
|
@@ -13641,7 +14197,7 @@ function handleFakeJira(req, res, store) {
|
|
|
13641
14197
|
|
|
13642
14198
|
// src/http.ts
|
|
13643
14199
|
function handleRequest(req, res, ctx) {
|
|
13644
|
-
if (handleAppApi(req, res, ctx.app, { kind: ctx.kind }) || handleAgentApi(req, res, ctx.app)) {
|
|
14200
|
+
if (handleAppApi(req, res, ctx.app, { kind: ctx.kind, env: ctx.env, onSource: ctx.switchSource }) || handleAgentApi(req, res, ctx.app)) {
|
|
13645
14201
|
return true;
|
|
13646
14202
|
}
|
|
13647
14203
|
if (ctx.kind === "plane")
|
|
@@ -13757,15 +14313,51 @@ function announce(line) {
|
|
|
13757
14313
|
}
|
|
13758
14314
|
async function runServer(opts) {
|
|
13759
14315
|
const piped = await readPipe();
|
|
13760
|
-
const raw = piped ?? JSON.parse(
|
|
14316
|
+
const raw = piped ?? JSON.parse(readFileSync8(join11(opts.root, "fixtures/issues.json"), "utf8"));
|
|
13761
14317
|
const flags = argvToFlags(process.argv);
|
|
13762
|
-
const
|
|
14318
|
+
const booted = await createBoardApp({
|
|
13763
14319
|
raw,
|
|
13764
14320
|
piped: Boolean(piped),
|
|
13765
14321
|
flags
|
|
13766
14322
|
});
|
|
14323
|
+
const live = {
|
|
14324
|
+
app: booted.app,
|
|
14325
|
+
store: booted.store,
|
|
14326
|
+
kind: booted.kind,
|
|
14327
|
+
raw
|
|
14328
|
+
};
|
|
14329
|
+
async function switchSource(source) {
|
|
14330
|
+
if (source === "taskwarrior") {
|
|
14331
|
+
return { ok: false, kind: live.kind, source, reason: "Taskwarrior is not connected" };
|
|
14332
|
+
}
|
|
14333
|
+
if (source !== "plane" && source !== "jira") {
|
|
14334
|
+
return { ok: false, kind: live.kind, source, error: `Unknown source ${source}` };
|
|
14335
|
+
}
|
|
14336
|
+
if (source === "plane" && !process.env.PLANE_API_KEY?.trim()) {
|
|
14337
|
+
return { ok: false, kind: live.kind, source, reason: "Set PLANE_API_KEY to use Plane" };
|
|
14338
|
+
}
|
|
14339
|
+
const nextFlags = setPlaneFlag(live.app.flags, source === "plane");
|
|
14340
|
+
try {
|
|
14341
|
+
const next = await createBoardApp({ raw: live.raw, piped: false, flags: nextFlags });
|
|
14342
|
+
live.app = next.app;
|
|
14343
|
+
live.store = next.store;
|
|
14344
|
+
live.kind = next.kind;
|
|
14345
|
+
if (next.kind === "plane" || next.kind === "jira") {
|
|
14346
|
+
try {
|
|
14347
|
+
await refreshFromJira(live.app, next.kind);
|
|
14348
|
+
} catch (err) {
|
|
14349
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
14350
|
+
return { ok: true, kind: live.kind, source, error: message };
|
|
14351
|
+
}
|
|
14352
|
+
}
|
|
14353
|
+
return { ok: true, kind: live.kind, source };
|
|
14354
|
+
} catch (err) {
|
|
14355
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
14356
|
+
return { ok: false, kind: live.kind, source, error: message };
|
|
14357
|
+
}
|
|
14358
|
+
}
|
|
13767
14359
|
const server = createServer((req, res) => {
|
|
13768
|
-
if (handleRequest(req, res, { app, store, kind }))
|
|
14360
|
+
if (handleRequest(req, res, { app: live.app, store: live.store, kind: live.kind, switchSource }))
|
|
13769
14361
|
return;
|
|
13770
14362
|
if (opts.fallback) {
|
|
13771
14363
|
opts.fallback(req, res);
|
|
@@ -13776,34 +14368,34 @@ async function runServer(opts) {
|
|
|
13776
14368
|
res.end("pipe-kan UI is missing. Run bun run build.");
|
|
13777
14369
|
}
|
|
13778
14370
|
});
|
|
13779
|
-
if (kind === "plane") {
|
|
14371
|
+
if (live.kind === "plane") {
|
|
13780
14372
|
try {
|
|
13781
|
-
await refreshFromJira(app, kind, { piped: Boolean(piped) });
|
|
14373
|
+
await refreshFromJira(live.app, live.kind, { piped: Boolean(piped) });
|
|
13782
14374
|
} catch (err) {
|
|
13783
|
-
console.error(`${kind} Refresh failed`, err);
|
|
14375
|
+
console.error(`${live.kind} Refresh failed`, err);
|
|
13784
14376
|
}
|
|
13785
|
-
const error = app.board().error;
|
|
14377
|
+
const error = live.app.board().error;
|
|
13786
14378
|
if (error)
|
|
13787
|
-
console.error(`${kind} Refresh failed; keeping last live Board`, error);
|
|
14379
|
+
console.error(`${live.kind} Refresh failed; keeping last live Board`, error);
|
|
13788
14380
|
}
|
|
13789
14381
|
const { host, port } = resolveListen();
|
|
13790
14382
|
await bindListen(server, host, port);
|
|
13791
14383
|
const origin = `http://127.0.0.1:${port}`;
|
|
13792
14384
|
announce(`pipe-kan http://${host}:${port}`);
|
|
13793
|
-
if (kind === "plane") {
|
|
14385
|
+
if (live.kind === "plane") {
|
|
13794
14386
|
announce(`cli plane ${planeHost()} ${planeWorkspace(flags)}`);
|
|
13795
14387
|
announce(`Plane host default ${DEFAULT_PLANE_HOST}`);
|
|
13796
14388
|
} else {
|
|
13797
14389
|
const fakeConfig = writeJiraConfig(join11(tmpdir3(), "pipe-kan"), origin);
|
|
13798
|
-
announce(`cli ${kind === "jira" ? resolveJiraBin() : "store"}`);
|
|
14390
|
+
announce(`cli ${live.kind === "jira" ? resolveJiraBin() : "store"}`);
|
|
13799
14391
|
announce(`Fake Jira ${origin}/rest/api/2/search`);
|
|
13800
14392
|
announce(`Fake Jira config ${fakeConfig}`);
|
|
13801
14393
|
}
|
|
13802
|
-
if (kind === "jira") {
|
|
14394
|
+
if (live.kind === "jira") {
|
|
13803
14395
|
try {
|
|
13804
|
-
await refreshFromJira(app, kind, { piped: Boolean(piped) });
|
|
14396
|
+
await refreshFromJira(live.app, live.kind, { piped: Boolean(piped) });
|
|
13805
14397
|
} catch (err) {
|
|
13806
|
-
console.error(`${kind} Refresh failed; keeping Fixture Board`);
|
|
14398
|
+
console.error(`${live.kind} Refresh failed; keeping Fixture Board`);
|
|
13807
14399
|
console.error(err);
|
|
13808
14400
|
}
|
|
13809
14401
|
}
|