slopwaresystems 0.2.3 → 0.2.4

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/index.js +82 -53
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -23518,6 +23518,29 @@ function resolveSession() {
23518
23518
  return { baseUrl, token };
23519
23519
  }
23520
23520
 
23521
+ // src/platform/session-file.ts
23522
+ import { createHash } from "crypto";
23523
+ import { existsSync as existsSync3, mkdirSync as mkdirSync3, readFileSync as readFileSync3, realpathSync, rmSync as rmSync2, writeFileSync as writeFileSync3 } from "fs";
23524
+ import { dirname as dirname2, join as join3 } from "path";
23525
+ var sessionsDir = () => join3(dirname2(configPath()), "sessions");
23526
+ var realCwd = (cwd) => {
23527
+ try {
23528
+ return realpathSync(cwd);
23529
+ } catch {
23530
+ return cwd;
23531
+ }
23532
+ };
23533
+ var sessionFile = (cwd) => join3(sessionsDir(), `${createHash("sha256").update(realCwd(cwd)).digest("hex").slice(0, 16)}.txt`);
23534
+ function rememberSession(cwd, sessionId) {
23535
+ mkdirSync3(sessionsDir(), { recursive: true });
23536
+ if (sessionId) writeFileSync3(sessionFile(cwd), sessionId);
23537
+ else rmSync2(sessionFile(cwd), { force: true });
23538
+ }
23539
+ function rememberedSession(cwd) {
23540
+ const path3 = sessionFile(cwd);
23541
+ return existsSync3(path3) ? readFileSync3(path3, "utf8").trim() || null : null;
23542
+ }
23543
+
23521
23544
  // src/platform/errors.ts
23522
23545
  var CliError = class extends Error {
23523
23546
  constructor(message, hint, code = "error", exitCode = 1) {
@@ -23577,15 +23600,17 @@ function createApi(options) {
23577
23600
  function authedApi() {
23578
23601
  const session = resolveSession();
23579
23602
  if (!session.token) throw new CliError(`Not signed in to ${session.baseUrl}`, "slop login", "unauthenticated");
23580
- return createApi(session);
23603
+ const client = createApi(session);
23604
+ client.sessionId = rememberedSession(process.cwd());
23605
+ return client;
23581
23606
  }
23582
23607
 
23583
23608
  // src/platform/machine.ts
23584
23609
  import { execFileSync } from "child_process";
23585
- import { createHash, randomUUID } from "crypto";
23586
- import { existsSync as existsSync3, mkdirSync as mkdirSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "fs";
23610
+ import { createHash as createHash2, randomUUID } from "crypto";
23611
+ import { existsSync as existsSync4, mkdirSync as mkdirSync4, readFileSync as readFileSync4, writeFileSync as writeFileSync4 } from "fs";
23587
23612
  import { hostname as hostname3, platform, userInfo } from "os";
23588
- import { dirname as dirname2, join as join3 } from "path";
23613
+ import { dirname as dirname3, join as join4 } from "path";
23589
23614
  function run(command, args) {
23590
23615
  try {
23591
23616
  return execFileSync(command, args, { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"], timeout: 5e3 });
@@ -23599,7 +23624,7 @@ function osMachineId() {
23599
23624
  return /"IOPlatformUUID" = "([^"]+)"/.exec(run("ioreg", ["-rd1", "-c", "IOPlatformExpertDevice"]) ?? "")?.[1] ?? null;
23600
23625
  case "linux":
23601
23626
  for (const path3 of ["/etc/machine-id", "/var/lib/dbus/machine-id"]) {
23602
- if (existsSync3(path3)) return readFileSync3(path3, "utf8").trim() || null;
23627
+ if (existsSync4(path3)) return readFileSync4(path3, "utf8").trim() || null;
23603
23628
  }
23604
23629
  return null;
23605
23630
  case "win32":
@@ -23611,18 +23636,18 @@ function osMachineId() {
23611
23636
  }
23612
23637
  }
23613
23638
  function persistedMachineId() {
23614
- const path3 = join3(dirname2(configPath()), "machine-id");
23615
- if (existsSync3(path3)) return readFileSync3(path3, "utf8").trim();
23639
+ const path3 = join4(dirname3(configPath()), "machine-id");
23640
+ if (existsSync4(path3)) return readFileSync4(path3, "utf8").trim();
23616
23641
  const id = randomUUID();
23617
- mkdirSync3(dirname2(path3), { recursive: true, mode: 448 });
23618
- writeFileSync3(path3, `${id}
23642
+ mkdirSync4(dirname3(path3), { recursive: true, mode: 448 });
23643
+ writeFileSync4(path3, `${id}
23619
23644
  `, { mode: 384 });
23620
23645
  return id;
23621
23646
  }
23622
23647
  function machineInfo() {
23623
23648
  const raw = `${osMachineId() ?? persistedMachineId()}:${userInfo().username}`;
23624
23649
  return {
23625
- fingerprint: createHash("sha256").update(raw).digest("hex"),
23650
+ fingerprint: createHash2("sha256").update(raw).digest("hex"),
23626
23651
  name: hostname3().replace(/\.local$/, ""),
23627
23652
  platform: platform()
23628
23653
  };
@@ -23763,9 +23788,9 @@ function registerGuideCommand(program3) {
23763
23788
 
23764
23789
  // src/platform/git.ts
23765
23790
  import { execFileSync as execFileSync2 } from "child_process";
23766
- import { createHash as createHash2, randomUUID as randomUUID2 } from "crypto";
23767
- import { existsSync as existsSync4, readFileSync as readFileSync4, rmSync as rmSync2, writeFileSync as writeFileSync4 } from "fs";
23768
- import { basename, join as join4, resolve } from "path";
23791
+ import { createHash as createHash3, randomUUID as randomUUID2 } from "crypto";
23792
+ import { existsSync as existsSync5, readFileSync as readFileSync5, rmSync as rmSync3, writeFileSync as writeFileSync5 } from "fs";
23793
+ import { basename, join as join5, resolve } from "path";
23769
23794
  function git(args, cwd = process.cwd()) {
23770
23795
  try {
23771
23796
  return execFileSync2("git", args, { cwd, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim();
@@ -23778,7 +23803,7 @@ function findRepo() {
23778
23803
  if (!commonDir) return null;
23779
23804
  const worktree = git(["rev-parse", "--show-toplevel"]);
23780
23805
  const root = basename(commonDir) === ".git" ? resolve(commonDir, "..") : worktree ?? commonDir;
23781
- return { root, worktree: worktree ?? root, linkPath: join4(commonDir, "slop.json") };
23806
+ return { root, worktree: worktree ?? root, linkPath: join5(commonDir, "slop.json") };
23782
23807
  }
23783
23808
  function requireRepo() {
23784
23809
  const repo = findRepo();
@@ -23787,17 +23812,17 @@ function requireRepo() {
23787
23812
  }
23788
23813
  function readLink(repo) {
23789
23814
  try {
23790
- return JSON.parse(readFileSync4(repo.linkPath, "utf8"));
23815
+ return JSON.parse(readFileSync5(repo.linkPath, "utf8"));
23791
23816
  } catch {
23792
23817
  return null;
23793
23818
  }
23794
23819
  }
23795
23820
  function writeLink(repo, link) {
23796
- writeFileSync4(repo.linkPath, `${JSON.stringify(link, null, 2)}
23821
+ writeFileSync5(repo.linkPath, `${JSON.stringify(link, null, 2)}
23797
23822
  `);
23798
23823
  }
23799
23824
  function removeLink(repo) {
23800
- if (existsSync4(repo.linkPath)) rmSync2(repo.linkPath);
23825
+ if (existsSync5(repo.linkPath)) rmSync3(repo.linkPath);
23801
23826
  }
23802
23827
  function normalizeRemote(url2) {
23803
23828
  const scp = /^[^@/]+@([^:/]+):(.+)$/.exec(url2);
@@ -23811,7 +23836,7 @@ function describeRepo(repo, existing) {
23811
23836
  let fingerprint;
23812
23837
  let localRepoId;
23813
23838
  if (roots) {
23814
- fingerprint = createHash2("sha256").update(roots.split("\n").sort().join("\n")).digest("hex");
23839
+ fingerprint = createHash3("sha256").update(roots.split("\n").sort().join("\n")).digest("hex");
23815
23840
  } else {
23816
23841
  localRepoId = existing?.localRepoId ?? randomUUID2();
23817
23842
  fingerprint = `local:${localRepoId}`;
@@ -23955,11 +23980,11 @@ linked to ${link.projectKey} ${link.projectName} (${link.baseUrl})`
23955
23980
 
23956
23981
  // src/commands/mcp.ts
23957
23982
  import { execFileSync as execFileSync3 } from "child_process";
23958
- import { createHash as createHash3 } from "crypto";
23959
- import { appendFileSync, existsSync as existsSync5, mkdirSync as mkdirSync4, readFileSync as readFileSync8, readdirSync, realpathSync, rmSync as rmSync3, writeFileSync as writeFileSync5 } from "fs";
23960
- import { dirname as dirname3 } from "path";
23983
+ import { createHash as createHash4 } from "crypto";
23984
+ import { appendFileSync, existsSync as existsSync6, mkdirSync as mkdirSync5, readFileSync as readFileSync9, readdirSync, realpathSync as realpathSync2, rmSync as rmSync4, writeFileSync as writeFileSync6 } from "fs";
23985
+ import { dirname as dirname4 } from "path";
23961
23986
  import { homedir as homedir3 } from "os";
23962
- import { join as join5 } from "path";
23987
+ import { join as join6 } from "path";
23963
23988
 
23964
23989
  // src/platform/live.ts
23965
23990
  async function waitForMessage(api, projectId, taskKey, options = {}) {
@@ -23975,6 +24000,7 @@ async function waitForMessage(api, projectId, taskKey, options = {}) {
23975
24000
  const push = JSON.parse(String(event.data));
23976
24001
  if (push.type === "hello") {
23977
24002
  api.sessionId = push.session.id;
24003
+ rememberSession(process.cwd(), push.session.id);
23978
24004
  options.onHello?.(push.session);
23979
24005
  return;
23980
24006
  }
@@ -23989,6 +24015,7 @@ async function waitForMessage(api, projectId, taskKey, options = {}) {
23989
24015
  socket.addEventListener("close", (event) => done({ kind: "dropped", reason: event.reason || `code ${event.code}` }));
23990
24016
  socket.addEventListener("error", () => done({ kind: "dropped", reason: "socket error" }));
23991
24017
  });
24018
+ if (outcome.kind !== "message") rememberSession(process.cwd(), null);
23992
24019
  if (outcome.kind === "message" || outcome.kind === "timeout") {
23993
24020
  socket.close(1e3, outcome.kind === "message" ? "handled" : "timeout");
23994
24021
  await new Promise((resolve2) => {
@@ -24030,6 +24057,7 @@ var HeldSession = class {
24030
24057
  const push = JSON.parse(String(event.data));
24031
24058
  if (push.type === "hello") {
24032
24059
  this.api.sessionId = push.session.id;
24060
+ rememberSession(process.cwd(), push.session.id);
24033
24061
  return;
24034
24062
  }
24035
24063
  if (push.type === "end") {
@@ -24058,6 +24086,7 @@ var HeldSession = class {
24058
24086
  stop() {
24059
24087
  this.stopped = true;
24060
24088
  this.api.sessionId = null;
24089
+ rememberSession(process.cwd(), null);
24061
24090
  if (this.timer) clearTimeout(this.timer);
24062
24091
  this.socket?.close(1e3, "released");
24063
24092
  }
@@ -24120,11 +24149,11 @@ function serveMcp(tools2, info) {
24120
24149
  }
24121
24150
 
24122
24151
  // src/commands/tasks.ts
24123
- import { readFileSync as readFileSync6 } from "fs";
24152
+ import { readFileSync as readFileSync7 } from "fs";
24124
24153
 
24125
24154
  // src/commands/items.ts
24126
- import { readFileSync as readFileSync5 } from "fs";
24127
- var readText = (value) => value === "-" ? readFileSync5(0, "utf8").trim() : value;
24155
+ import { readFileSync as readFileSync6 } from "fs";
24156
+ var readText = (value) => value === "-" ? readFileSync6(0, "utf8").trim() : value;
24128
24157
  var path2 = (projectId, key) => `/projects/${projectId}/tasks/${encodeURIComponent(key)}/items`;
24129
24158
  function renderItems(items) {
24130
24159
  if (items.length === 0) return "No plan yet. Write one: slop task plan <KEY> -";
@@ -24151,7 +24180,7 @@ function registerItemCommands(task, withProject) {
24151
24180
  withProject(task.command("plan <key> [lines...]")).description('Append items to the plan, one per argument; "-" reads one per line from stdin (list markers are fine).').action(async (key, lines, options) => {
24152
24181
  const api = authedApi();
24153
24182
  const project = await resolveProject(api, options.project);
24154
- const source = lines.length === 1 && lines[0] === "-" ? readFileSync5(0, "utf8") : lines.join("\n");
24183
+ const source = lines.length === 1 && lines[0] === "-" ? readFileSync6(0, "utf8") : lines.join("\n");
24155
24184
  const titles = source.split("\n").map((line) => line.replace(/^\s*(?:[-*+]|\d+[.)])\s*(?:\[[ xX]\]\s*)?/, "").trim()).filter(Boolean);
24156
24185
  if (titles.length === 0) throw new CliError("Nothing to add", 'give items as arguments, or "-" and one per line on stdin');
24157
24186
  const items = await api.post(path2(project.id, key), { items: titles.map((title3) => ({ title: title3 })) });
@@ -24208,7 +24237,7 @@ function oneOf(value, allowed, what) {
24208
24237
  if (allowed.includes(normalized)) return normalized;
24209
24238
  throw new CliError(`Unknown ${what} "${value}"`, `use one of: ${allowed.join(", ")}`);
24210
24239
  }
24211
- var readDescription = (value) => value === "-" ? readFileSync6(0, "utf8").trim() : value;
24240
+ var readDescription = (value) => value === "-" ? readFileSync7(0, "utf8").trim() : value;
24212
24241
  var taskUrl = (baseUrl, projectId, task) => `${baseUrl}/projects/${projectId}?task=${task.key}`;
24213
24242
  var loadProject = (api, id) => api.get(`/projects/${id}`);
24214
24243
  function resolveStatus(project, ref) {
@@ -24359,8 +24388,8 @@ ${taskUrl(api.baseUrl, project.id, created)}`);
24359
24388
  }
24360
24389
 
24361
24390
  // src/commands/threads.ts
24362
- import { readFileSync as readFileSync7 } from "fs";
24363
- var readText2 = (value) => value === "-" ? readFileSync7(0, "utf8").trim() : value;
24391
+ import { readFileSync as readFileSync8 } from "fs";
24392
+ var readText2 = (value) => value === "-" ? readFileSync8(0, "utf8").trim() : value;
24364
24393
  var when = (iso) => iso.slice(0, 16).replace("T", " ");
24365
24394
  var who = (message) => `${message.author.name}${message.via === "agent" ? ` (agent${message.machine ? ` \xB7 ${message.machine}` : ""})` : ""}`;
24366
24395
  function threadLine(thread) {
@@ -24441,26 +24470,26 @@ var int2 = (description2) => ({ type: "integer", description: description2 });
24441
24470
  var schema = (properties, required2 = []) => ({ type: "object", properties, required: required2, additionalProperties: false });
24442
24471
  var PROJECT = str("Project key. Defaults to the project this repository is linked to.");
24443
24472
  var taskPath = (projectId, key) => `/projects/${projectId}/tasks/${encodeURIComponent(key)}`;
24444
- var inboxDir = () => join5(dirname3(configPath()), "inbox");
24445
- var realCwd = (cwd) => {
24473
+ var inboxDir = () => join6(dirname4(configPath()), "inbox");
24474
+ var realCwd2 = (cwd) => {
24446
24475
  try {
24447
- return realpathSync(cwd);
24476
+ return realpathSync2(cwd);
24448
24477
  } catch {
24449
24478
  return cwd;
24450
24479
  }
24451
24480
  };
24452
- var inboxKey = (cwd) => createHash3("sha256").update(realCwd(cwd)).digest("hex").slice(0, 16);
24453
- var inboxFile = (cwd) => join5(inboxDir(), `${inboxKey(cwd)}.jsonl`);
24481
+ var inboxKey = (cwd) => createHash4("sha256").update(realCwd2(cwd)).digest("hex").slice(0, 16);
24482
+ var inboxFile = (cwd) => join6(inboxDir(), `${inboxKey(cwd)}.jsonl`);
24454
24483
  function inboxWrite(cwd, push) {
24455
- mkdirSync4(inboxDir(), { recursive: true });
24484
+ mkdirSync5(inboxDir(), { recursive: true });
24456
24485
  appendFileSync(inboxFile(cwd), `${JSON.stringify(push)}
24457
24486
  `);
24458
24487
  }
24459
24488
  function inboxDrain(cwd) {
24460
24489
  const path3 = inboxFile(cwd);
24461
- if (!existsSync5(path3)) return [];
24462
- const lines = readFileSync8(path3, "utf8").split("\n").filter(Boolean);
24463
- rmSync3(path3, { force: true });
24490
+ if (!existsSync6(path3)) return [];
24491
+ const lines = readFileSync9(path3, "utf8").split("\n").filter(Boolean);
24492
+ rmSync4(path3, { force: true });
24464
24493
  return lines.map((line) => JSON.parse(line));
24465
24494
  }
24466
24495
  async function digest(api, projectId, taskKey) {
@@ -24658,7 +24687,7 @@ ${task.notes}` : ""].filter(Boolean).join("\n");
24658
24687
  }
24659
24688
  function readJson(path3) {
24660
24689
  try {
24661
- return JSON.parse(readFileSync8(path3, "utf8"));
24690
+ return JSON.parse(readFileSync9(path3, "utf8"));
24662
24691
  } catch {
24663
24692
  return {};
24664
24693
  }
@@ -24671,13 +24700,13 @@ function installClaude() {
24671
24700
  } catch {
24672
24701
  done.push("Claude Code: run `claude mcp add --scope user slop -- slop mcp` (the claude command was not found here)");
24673
24702
  }
24674
- const dir = join5(homedir3(), ".claude");
24675
- const path3 = join5(dir, "settings.json");
24676
- mkdirSync4(dir, { recursive: true });
24703
+ const dir = join6(homedir3(), ".claude");
24704
+ const path3 = join6(dir, "settings.json");
24705
+ mkdirSync5(dir, { recursive: true });
24677
24706
  const settings = readJson(path3);
24678
24707
  settings.hooks = mergeHooks(settings.hooks ?? {});
24679
24708
  settings.env = { ...settings.env ?? {}, MCP_TOOL_TIMEOUT: "1800000" };
24680
- writeFileSync5(path3, `${JSON.stringify(settings, null, 2)}
24709
+ writeFileSync6(path3, `${JSON.stringify(settings, null, 2)}
24681
24710
  `);
24682
24711
  done.push(`Claude Code: PostToolUse and Stop hooks and MCP_TOOL_TIMEOUT written to ${path3}`);
24683
24712
  return done;
@@ -24695,23 +24724,23 @@ function mergeHooks(hooks) {
24695
24724
  }
24696
24725
  function installCodex() {
24697
24726
  const done = [];
24698
- const dir = join5(homedir3(), ".codex");
24699
- mkdirSync4(dir, { recursive: true });
24700
- const configPath2 = join5(dir, "config.toml");
24701
- const current = existsSync5(configPath2) ? readFileSync8(configPath2, "utf8") : "";
24727
+ const dir = join6(homedir3(), ".codex");
24728
+ mkdirSync5(dir, { recursive: true });
24729
+ const configPath2 = join6(dir, "config.toml");
24730
+ const current = existsSync6(configPath2) ? readFileSync9(configPath2, "utf8") : "";
24702
24731
  if (/^\[mcp_servers\.slop\]/m.test(current)) done.push(`Codex: MCP server already in ${configPath2}`);
24703
24732
  else {
24704
- writeFileSync5(configPath2, `${current.trimEnd()}${current.trim() ? "\n\n" : ""}[mcp_servers.slop]
24733
+ writeFileSync6(configPath2, `${current.trimEnd()}${current.trim() ? "\n\n" : ""}[mcp_servers.slop]
24705
24734
  command = "slop"
24706
24735
  args = ["mcp"]
24707
24736
  tool_timeout_sec = 1800
24708
24737
  `);
24709
24738
  done.push(`Codex: MCP server "slop" added to ${configPath2}`);
24710
24739
  }
24711
- const hooksPath = join5(dir, "hooks.json");
24740
+ const hooksPath = join6(dir, "hooks.json");
24712
24741
  const file2 = readJson(hooksPath);
24713
24742
  file2.hooks = mergeHooks(file2.hooks ?? {});
24714
- writeFileSync5(hooksPath, `${JSON.stringify(file2, null, 2)}
24743
+ writeFileSync6(hooksPath, `${JSON.stringify(file2, null, 2)}
24715
24744
  `);
24716
24745
  done.push(`Codex: PostToolUse and Stop hooks written to ${hooksPath}. Codex asks you to trust new hooks once (/hooks in a session).`);
24717
24746
  return done;
@@ -24797,7 +24826,7 @@ function registerMcpCommands(program3, version2) {
24797
24826
  }
24798
24827
 
24799
24828
  // src/commands/sessions.ts
24800
- import { readFileSync as readFileSync9 } from "fs";
24829
+ import { readFileSync as readFileSync10 } from "fs";
24801
24830
  var taskPath2 = (projectId, key) => `/projects/${projectId}/tasks/${encodeURIComponent(key)}`;
24802
24831
  var sessionLine = (session) => [session.taskKey ?? "(no task)", session.user.name, session.machine.name, session.startedAt];
24803
24832
  function registerSessionCommands(program3, task) {
@@ -24881,13 +24910,13 @@ ${written.map((path3) => ` ${path3}`).join("\n")}`);
24881
24910
  }
24882
24911
 
24883
24912
  // src/index.ts
24884
- var program2 = new Command().name("slop").description("Slopware Tasks from the terminal. Everything a person can do on the board, an agent can do here.").version("0.2.3").option("--json", "machine-readable output: JSON on stdout, errors as JSON on stderr").hook("preAction", (_, action) => setJsonMode(Boolean(action.optsWithGlobals().json))).showHelpAfterError("(run with --help for usage)");
24913
+ var program2 = new Command().name("slop").description("Slopware Tasks from the terminal. Everything a person can do on the board, an agent can do here.").version("0.2.4").option("--json", "machine-readable output: JSON on stdout, errors as JSON on stderr").hook("preAction", (_, action) => setJsonMode(Boolean(action.optsWithGlobals().json))).showHelpAfterError("(run with --help for usage)");
24885
24914
  registerAuthCommands(program2);
24886
24915
  registerProjectCommands(program2);
24887
24916
  var taskCommand = registerTaskCommands(program2);
24888
24917
  registerSessionCommands(program2, taskCommand);
24889
24918
  registerThreadCommands(program2, taskCommand);
24890
- registerMcpCommands(program2, "0.2.3");
24919
+ registerMcpCommands(program2, "0.2.4");
24891
24920
  registerGuideCommand(program2);
24892
24921
  registerSkillCommands(program2);
24893
24922
  program2.parseAsync().catch(fail);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "slopwaresystems",
3
- "version": "0.2.3",
3
+ "version": "0.2.4",
4
4
  "private": false,
5
5
  "description": "slop: the Slopware Tasks board from the terminal, for people and coding agents.",
6
6
  "type": "module",