pipe-kan 0.14.0 → 0.15.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 (2) hide show
  1. package/dist/pipe-kan.js +98 -13
  2. package/package.json +1 -1
package/dist/pipe-kan.js CHANGED
@@ -1,10 +1,10 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/server.ts
4
- import { readFileSync as readFileSync2, writeSync } from "node:fs";
4
+ import { readFileSync as readFileSync3, writeSync } from "node:fs";
5
5
  import { createServer } from "node:http";
6
6
  import { tmpdir as tmpdir2 } from "node:os";
7
- import { join as join6 } from "node:path";
7
+ import { join as join7 } from "node:path";
8
8
 
9
9
  // src/board.ts
10
10
  function formatDueDate(value) {
@@ -10763,8 +10763,83 @@ function permissionOptionForDecision(options, decision) {
10763
10763
  return options.find((o) => o.kind === kind)?.optionId ?? options[0]?.optionId ?? null;
10764
10764
  }
10765
10765
 
10766
+ // src/server/agent/skills.ts
10767
+ import { existsSync as existsSync3, readFileSync as readFileSync2, readdirSync } from "node:fs";
10768
+ import { homedir as homedir2 } from "node:os";
10769
+ import { join as join4 } from "node:path";
10770
+ function createSkillRegistry(bundledDir = join4(import.meta.dirname, "..", "..", "..", ".agents", "skills")) {
10771
+ const userDir = join4(homedir2(), ".pi", "agent", "skills");
10772
+ return {
10773
+ list() {
10774
+ const bundled = listSkills(bundledDir);
10775
+ const user = existsSync3(userDir) ? listSkills(userDir) : [];
10776
+ const map = new Map;
10777
+ for (const skill of bundled)
10778
+ map.set(skill.id, skill);
10779
+ for (const skill of user)
10780
+ map.set(skill.id, skill);
10781
+ return [...map.values()].sort((a, b) => a.id.localeCompare(b.id));
10782
+ },
10783
+ load(id) {
10784
+ const userPath = skillPath(userDir, id);
10785
+ if (existsSync3(userPath))
10786
+ return readSkill(userPath, id);
10787
+ const bundledPath = skillPath(bundledDir, id);
10788
+ if (existsSync3(bundledPath))
10789
+ return readSkill(bundledPath, id);
10790
+ return;
10791
+ }
10792
+ };
10793
+ }
10794
+ function skillContextBlock(skill) {
10795
+ return {
10796
+ type: "resource",
10797
+ resource: {
10798
+ uri: `skill://${skill.id}`,
10799
+ mimeType: "text/markdown",
10800
+ text: skill.body
10801
+ }
10802
+ };
10803
+ }
10804
+ function listSkills(dir) {
10805
+ if (!existsSync3(dir))
10806
+ return [];
10807
+ return readdirSync(dir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => readSkill(skillPath(dir, entry.name), entry.name)).filter((skill) => skill !== undefined);
10808
+ }
10809
+ function skillPath(dir, id) {
10810
+ return join4(dir, id, "SKILL.md");
10811
+ }
10812
+ function readSkill(path, id) {
10813
+ try {
10814
+ const text = readFileSync2(path, "utf8");
10815
+ const front = parseFrontMatter(text);
10816
+ return {
10817
+ id,
10818
+ name: front.name ?? id,
10819
+ description: front.description ?? "",
10820
+ body: text
10821
+ };
10822
+ } catch {
10823
+ return;
10824
+ }
10825
+ }
10826
+ function parseFrontMatter(text) {
10827
+ const match = text.match(/^---\s*\n([\s\S]*?)\n---\s*\n/);
10828
+ if (!match)
10829
+ return {};
10830
+ const out = {};
10831
+ for (const line of match[1].split(`
10832
+ `)) {
10833
+ const idx = line.indexOf(":");
10834
+ if (idx > 0)
10835
+ out[line.slice(0, idx).trim()] = line.slice(idx + 1).trim();
10836
+ }
10837
+ return out;
10838
+ }
10839
+
10766
10840
  // src/server/agent/api.ts
10767
10841
  var sessions = new Map;
10842
+ var skills = createSkillRegistry();
10768
10843
  function json2(res, status, body) {
10769
10844
  res.statusCode = status;
10770
10845
  res.setHeader("content-type", "application/json");
@@ -10795,6 +10870,10 @@ function handleAgentApi(req, res) {
10795
10870
  });
10796
10871
  return true;
10797
10872
  }
10873
+ if (url.pathname === "/api/agent/skills" && method === "GET") {
10874
+ json2(res, 200, skills.list().map((s) => ({ id: s.id, name: s.name, description: s.description })));
10875
+ return true;
10876
+ }
10798
10877
  if (url.pathname === "/api/agent/session" && method === "POST") {
10799
10878
  const cfg = loadAgentConfig();
10800
10879
  const backendConfig = cfg.agents[cfg.defaultAgent];
@@ -10817,7 +10896,13 @@ function handleAgentApi(req, res) {
10817
10896
  json2(res, 404, { error: "Session not found" });
10818
10897
  return;
10819
10898
  }
10820
- return session.prompt(String(body.prompt ?? ""), body.context ?? []).then(() => {
10899
+ const context = [...body.context ?? []];
10900
+ if (body.skillId) {
10901
+ const skill = skills.load(body.skillId);
10902
+ if (skill)
10903
+ context.push(skillContextBlock(skill));
10904
+ }
10905
+ return session.prompt(String(body.prompt ?? ""), context).then(() => {
10821
10906
  json2(res, 200, { ok: true });
10822
10907
  });
10823
10908
  }).catch((err) => json2(res, 500, { error: String(err) }));
@@ -10999,10 +11084,10 @@ function handleRequest(req, res, ctx) {
10999
11084
 
11000
11085
  // src/jira-config.ts
11001
11086
  import { mkdirSync, writeFileSync } from "node:fs";
11002
- import { join as join4 } from "node:path";
11087
+ import { join as join5 } from "node:path";
11003
11088
  function writeJiraConfig(dir, server) {
11004
11089
  mkdirSync(dir, { recursive: true });
11005
- const path = join4(dir, "jira.config.yml");
11090
+ const path = join5(dir, "jira.config.yml");
11006
11091
  writeFileSync(path, [
11007
11092
  "installation: Cloud",
11008
11093
  `server: ${server}`,
@@ -11057,8 +11142,8 @@ function stdinStat() {
11057
11142
  }
11058
11143
 
11059
11144
  // src/ui.ts
11060
- import { createReadStream, existsSync as existsSync3, statSync } from "node:fs";
11061
- import { dirname, extname, join as join5, resolve as resolve2, sep } from "node:path";
11145
+ import { createReadStream, existsSync as existsSync4, statSync } from "node:fs";
11146
+ import { dirname, extname, join as join6, resolve as resolve2, sep } from "node:path";
11062
11147
  import { fileURLToPath } from "node:url";
11063
11148
  var types = {
11064
11149
  ".css": "text/css; charset=utf-8",
@@ -11075,7 +11160,7 @@ function packageRoot(from = import.meta.url) {
11075
11160
  return resolve2(dirname(fileURLToPath(from)), "..");
11076
11161
  }
11077
11162
  function uiDir(root) {
11078
- return join5(root, "dist", "ui");
11163
+ return join6(root, "dist", "ui");
11079
11164
  }
11080
11165
  function inside(root, file) {
11081
11166
  const base = resolve2(root);
@@ -11084,12 +11169,12 @@ function inside(root, file) {
11084
11169
  }
11085
11170
  function sendUi(root, req, res) {
11086
11171
  const ui = uiDir(root);
11087
- const index = join5(ui, "index.html");
11088
- if (!existsSync3(index))
11172
+ const index = join6(ui, "index.html");
11173
+ if (!existsSync4(index))
11089
11174
  return false;
11090
11175
  const path = new URL(req.url ?? "/", "http://127.0.0.1").pathname;
11091
11176
  const wanted = resolve2(ui, `.${decodeURIComponent(path)}`);
11092
- const file = inside(ui, wanted) && existsSync3(wanted) && statSync(wanted).isFile() ? wanted : index;
11177
+ const file = inside(ui, wanted) && existsSync4(wanted) && statSync(wanted).isFile() ? wanted : index;
11093
11178
  res.setHeader("content-type", types[extname(file)] ?? "application/octet-stream");
11094
11179
  createReadStream(file).pipe(res);
11095
11180
  return true;
@@ -11105,7 +11190,7 @@ function announce(line) {
11105
11190
  }
11106
11191
  async function runServer(opts) {
11107
11192
  const piped = await readPipe();
11108
- const raw = piped ?? JSON.parse(readFileSync2(join6(opts.root, "fixtures/issues.json"), "utf8"));
11193
+ const raw = piped ?? JSON.parse(readFileSync3(join7(opts.root, "fixtures/issues.json"), "utf8"));
11109
11194
  const { app, store, kind } = await createBoardApp({
11110
11195
  raw,
11111
11196
  piped: Boolean(piped)
@@ -11125,7 +11210,7 @@ async function runServer(opts) {
11125
11210
  const { host, port } = resolveListen();
11126
11211
  await bindListen(server, host, port);
11127
11212
  const origin = `http://127.0.0.1:${port}`;
11128
- const fakeConfig = writeJiraConfig(join6(tmpdir2(), "pipe-kan"), origin);
11213
+ const fakeConfig = writeJiraConfig(join7(tmpdir2(), "pipe-kan"), origin);
11129
11214
  announce(`pipe-kan http://${host}:${port}`);
11130
11215
  announce(`cli ${kind === "jira" ? resolveJiraBin() : "store"}`);
11131
11216
  announce(`Fake Jira ${origin}/rest/api/2/search`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pipe-kan",
3
- "version": "0.14.0",
3
+ "version": "0.15.0",
4
4
  "description": "Local Kanban for jira-cli",
5
5
  "license": "MIT",
6
6
  "type": "module",