pipe-kan 0.30.1 → 0.31.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.
package/README.md CHANGED
@@ -57,7 +57,7 @@ bunx pipe-kan --plane --projects APH,PULSE,PKAN,PUI,DEC
57
57
  | `PLANE_API_KEY` | (required in Plane mode) | personal access token (`X-API-Key`) |
58
58
  | `PLANE_HOST` | `https://plane.tail48fe8.ts.net` | Plane origin; API is `{host}/api/v1` |
59
59
 
60
- Same `--projects` style as Jira: comma-separated, trimmed, uppercased. The header **Scope flags** field accepts the same string, then **Refresh**.
60
+ Same `--projects` style as Jira: comma-separated, trimmed, uppercased. The header **Scope flags** field accepts the same string, then **Refresh**. In Plane mode a header **Workspace** pill (beside the project-identifier chip) types a slug, writes `--workspace` into Scope flags, and Refresh-confirms all Epics. Jira omits the pill. Next launch still uses argv `--workspace`, else `PLANE_WORKSPACE`, else `personal`.
61
61
 
62
62
  On Refresh:
63
63
 
package/dist/pipe-kan.js CHANGED
@@ -1504,6 +1504,12 @@ function createApp(opts) {
1504
1504
  error: err instanceof Error ? err.message : "jira issue view failed"
1505
1505
  };
1506
1506
  }
1507
+ },
1508
+ async listWorkspaces() {
1509
+ return cli.listWorkspaces?.() ?? [];
1510
+ },
1511
+ async listProjectIdentifiers(workspace) {
1512
+ return cli.listProjectIdentifiers?.(workspace) ?? [];
1507
1513
  }
1508
1514
  };
1509
1515
  return app;
@@ -1877,6 +1883,19 @@ function createPlaneCli(opts) {
1877
1883
  return;
1878
1884
  return { id, identifier, name: asString(row?.name) ?? identifier };
1879
1885
  }
1886
+ function parseWorkspace(raw) {
1887
+ const row = asRecord(raw);
1888
+ const slug = asString(row?.slug);
1889
+ if (!slug)
1890
+ return;
1891
+ return { id: asString(row?.id) ?? slug, name: asString(row?.name) ?? slug, slug };
1892
+ }
1893
+ function parseWorkspaceList(raw) {
1894
+ const rows = Array.isArray(raw) ? raw : asRecord(raw)?.results;
1895
+ if (!Array.isArray(rows))
1896
+ return;
1897
+ return rows.map(parseWorkspace).filter((row) => !!row);
1898
+ }
1880
1899
  function parseState(raw) {
1881
1900
  const row = asRecord(raw);
1882
1901
  const id = asString(row?.id);
@@ -2092,6 +2111,25 @@ function createPlaneCli(opts) {
2092
2111
  const loaded = await loadCatalog(flags || defaultFlags);
2093
2112
  return loaded.columns;
2094
2113
  },
2114
+ async listWorkspaces() {
2115
+ for (const path of ["/users/me/workspaces/", "/workspaces/"]) {
2116
+ try {
2117
+ return parseWorkspaceList(await requestJson(path)) ?? [];
2118
+ } catch (err) {
2119
+ const status = err.status;
2120
+ if (status === 404 && path === "/users/me/workspaces/")
2121
+ continue;
2122
+ return [];
2123
+ }
2124
+ }
2125
+ return [];
2126
+ },
2127
+ async listProjectIdentifiers(workspace) {
2128
+ const slug = workspace.trim();
2129
+ if (!slug)
2130
+ return [];
2131
+ return (await paginate(`/workspaces/${slug}/projects/`)).map(parseProject).filter((row) => !!row).map((row) => row.identifier);
2132
+ },
2095
2133
  async move(key, status) {
2096
2134
  try {
2097
2135
  const module = (await loadCatalog(lastFlags)).modules.get(key);
@@ -2259,11 +2297,46 @@ function reply(req, res, work) {
2259
2297
  json(res, 500, { error: message });
2260
2298
  });
2261
2299
  }
2262
- function handleAppApi(req, res, app) {
2300
+ async function boardEnvelope(body, kind, flags, env, app) {
2301
+ if (kind !== "plane")
2302
+ return { ...body, kind };
2303
+ const envelope = { ...body, kind, workspace: planeWorkspace(flags, env) };
2304
+ try {
2305
+ const workspaces = await app.listWorkspaces?.();
2306
+ if (workspaces && workspaces.length)
2307
+ return { ...envelope, workspaces };
2308
+ } catch {}
2309
+ return envelope;
2310
+ }
2311
+ function handleAppApi(req, res, app, opts = {}) {
2312
+ const kind = opts.kind ?? "store";
2313
+ const env = opts.env ?? process.env;
2263
2314
  const url = pathOf(req);
2264
2315
  const method = (req.method ?? "GET").toUpperCase();
2265
2316
  if (url.pathname === "/api/board" && method === "GET") {
2266
- json(res, 200, { ...app.board(), flags: app.flags });
2317
+ reply(req, res, async () => {
2318
+ json(res, 200, await boardEnvelope({ ...app.board(), flags: app.flags }, kind, app.flags, env, app));
2319
+ });
2320
+ return true;
2321
+ }
2322
+ if (url.pathname === "/api/projects" && method === "GET") {
2323
+ reply(req, res, async () => {
2324
+ if (kind !== "plane") {
2325
+ json(res, 404, { error: "not plane" });
2326
+ return;
2327
+ }
2328
+ const workspace = (url.searchParams.get("workspace") ?? "").trim();
2329
+ if (!workspace) {
2330
+ json(res, 400, { error: "workspace required" });
2331
+ return;
2332
+ }
2333
+ try {
2334
+ json(res, 200, { projects: await app.listProjectIdentifiers?.(workspace) ?? [] });
2335
+ } catch (err) {
2336
+ const message = err instanceof Error ? err.message : "catalog failed";
2337
+ json(res, 409, { error: message });
2338
+ }
2339
+ });
2267
2340
  return true;
2268
2341
  }
2269
2342
  if (url.pathname === "/api/refresh" && method === "POST") {
@@ -2275,10 +2348,10 @@ function handleAppApi(req, res, app) {
2275
2348
  json(res, 400, { error: "selected Refresh requires epicKeys" });
2276
2349
  return;
2277
2350
  }
2278
- json(res, 200, await app.refresh(undefined, { scope: "selected", epicKeys }));
2351
+ json(res, 200, await boardEnvelope(await app.refresh(undefined, { scope: "selected", epicKeys }), kind, app.flags, env, app));
2279
2352
  return;
2280
2353
  }
2281
- json(res, 200, await app.refresh(body.flags));
2354
+ json(res, 200, await boardEnvelope(await app.refresh(body.flags), kind, app.flags, env, app));
2282
2355
  });
2283
2356
  return true;
2284
2357
  }
@@ -13307,8 +13380,9 @@ function handleFakeJira(req, res, store) {
13307
13380
 
13308
13381
  // src/http.ts
13309
13382
  function handleRequest(req, res, ctx) {
13310
- if (handleAppApi(req, res, ctx.app) || handleAgentApi(req, res, ctx.app))
13383
+ if (handleAppApi(req, res, ctx.app, { kind: ctx.kind }) || handleAgentApi(req, res, ctx.app)) {
13311
13384
  return true;
13385
+ }
13312
13386
  if (ctx.kind === "plane")
13313
13387
  return false;
13314
13388
  return handleFakeJira(req, res, ctx.store);