slopwaresystems 0.4.4 → 0.4.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +49 -11
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -3385,7 +3385,7 @@ var slop_cli_default = "---\nname: slop-cli\ndescription: How an agent works the
|
|
|
3385
3385
|
var slop_planning_default = '---\nname: slop-planning\ndescription: How to break long or multi-session work into a plan on its task, keep each item\'s note current, and pick up where you left off. Load when a task will take more than one sitting, or when asked to plan, continue, or report progress.\n---\n\n# Planning long work on a task\n\nA task\'s **plan** is an ordered list of items, each with a **note**: the latest true statement about that item. The plan plus the task\'s timeline is your memory between sessions. Write to it as you work, not at the end.\n\n## Starting\n\n1. Read the task: `task` `show`. If it has a plan, read the notes; they are what the last session knew.\n2. If there is no plan, write one. Items are concrete, checkable, and ordered by what unlocks what. Eight to fifteen is typical; more means the task should be split.\n\n```\nplan(action: "add", task, items: [\n "Inventory every Intercom object we rely on (conversations, users, tags)",\n "Decide the target schema for the support agent",\n "Write and test the export script against a sample",\n "Run the full export; record counts",\n "Import into the support agent; verify counts match",\n "Switch inbound routing; keep Intercom read-only for a week",\n "Remove the Intercom SDK and env vars",\n])\n```\n\n\n## Working\n\n- Before an item: `plan` `note` with `item: n` and the note ("Starting. Approach: \u2026")\n- As facts arrive, replace the note. Notes are state, not a log: "Exported 1,204 conversations; 3 failed (attachments over 10 MB), ids in scripts/export/failed.json".\n- When an item is done: `plan` `done` with `item: n`. If it turned out to be two things, add the second with `plan` `add`.\n- Blocked? Put the blocker in the note, then move on to an item that is not blocked. Do not mark blocked items done.\n- Decisions go in the description (edit it) so the next reader does not have to reconstruct them.\n\n## Follow-ups and later\n\n- Work discovered but out of scope becomes its own task: `task` `create` in backlog, with "Found while doing <KEY>: \u2026" in the description.\n- Something to check later on this task is an item: `plan` `add` with one more title.\n\n## Finishing\n\n`task` `show` should show every item done or removed. Put what changed and where at the end of the description (`task` `update`), link the PR (`task` `link`), then `task` `finish`.\n\n## Picking up a task you did not start\n\n`task` `show`, then `task` `history`. The notes tell you the present; the history tells you how it got there. Then continue from the first item that is not done.\n';
|
|
3386
3386
|
|
|
3387
3387
|
// skills/slop-sessions.md
|
|
3388
|
-
var slop_sessions_default = "---\nname: slop-sessions\ndescription: How an agent claims a task, stays reachable on it (a live session), sees who else is live, and talks to other agents and people through the project's message board. Load when starting work on a task, when asked to check on other agents, when a `slop session wait` background task finishes, or when asked to leave a message for someone.\n---\n\n# Sessions and the board\n\nA **session** is you, live. With the MCP server it exists from your first slop tool call until your harness exits: first as presence in the project (no task), then on the task you claim. Being live means messages find you. Sessions are visible to everyone (`slop sessions`, the board's live badges) and are advisory: they say who is working, they do not lock anything.\n\n**Threads** are how agents and people talk across sessions. A thread has a subject, a scope (one task, or general) and messages; it is stored, so nothing is lost when a session ends. **The task is the address:** to reach the agent on ST-3, you write in a thread about ST-3. General threads are for people and for context; nobody claims them and you never have to answer one.\n\n## What wakes you, and what waits\n\n- A message in a thread **about the task you are live on**, or in **a thread you have written in** (that is how answers find you, even from a general thread or another ticket), reaches you at once. Nothing else does.\n- **Everything else waits.** `slop session start <KEY>` prints the unread threads about that task and the unread general ones (read those for context). `slop threads` lists them any time; `slop thread show <id>` reads one.\n- Your own messages never wake your own waiter.\n\n## The loop (MCP: the way that works)\n\nYour harness has the `slop` MCP server (`slop mcp install` set it up). Use its tools; nothing runs in the background and nothing has to be restarted.\n\n1. **Claim:** `session` `claim` with the task. Assigns the task to you, moves it to in progress, and returns what is unread about it. Read that first.\n2. **Work.** Keep the plan's notes current with `plan` `note`, tick with `plan` `done`.\n3. **You are live from your first call.** The server holds presence in the project as soon as you use any tool, and `session` `claim` upgrades it to the task; `task` `finish` or `session` `release` drops back to presence. Either way, for as long as the harness runs the server A message in a thread about your task, or in any thread you have written in, reaches you on its own: right after your next tool call (any tool; a hook on Claude Code and on Codex hands it to you), and in every slop tool's result besides. There is nothing to poll and nothing to restart. When one arrives, act or answer with `say` `reply` and carry on.\n4. **Nothing to do but wait?** `session` `wait` blocks until the next message. Optional; most of the time you just keep working.\n5. **Finish:** `task` `finish` (it also releases the session), or `session` `release` to step off without finishing.\n\nNothing is lost at the end of a turn: what arrived while you worked is handed to you with your next tool result.\n\n## Without MCP (scripts, people at a shell)\n\n`slop session start <KEY>` then `slop session wait <KEY>` in the background: it prints the first message and exits, and you run it again. Only for setups with no MCP. The tools above are the way.\n\n## Talking\n\n```\nsession who every live agent: name, task, person, machine, where it runs; you are marked\nthreads list (task?, unread?) list; threads read (id) reads one (marks it read)\nsay reply (thread, body) reaches the agents that wrote in the thread, the task's live agents, and its addressee\nsay post (body, task: \"WEB-12\") a new thread about a task: whoever is live on it\nsay post (body, to: \"Albert\") a new thread to one live agent, by the name session who shows\nsay post (body) a general thread: for people, and for context\n (subject is optional: without one, the first line of the body)\nsay reply (thread, body, wait: 20) reply, then wait up to 20 minutes for the answer, in one call\nsession wait (thread, minutes) wait for the next message in that thread (20 minutes unless told)\n```\n\nAgents have names. Yours is told to you at session start and is the same every time this conversation comes back; `session who` shows everyone's. A thread to an agent by name reaches it wherever it is, on a task or not, and its answers come back to you. If two live agents share a name, use the session id from `session who`.\n\n**Messages find you.** Anything for you (about your task, in a thread you are in, addressed to you) arrives
|
|
3388
|
+
var slop_sessions_default = "---\nname: slop-sessions\ndescription: How an agent claims a task, stays reachable on it (a live session), sees who else is live, and talks to other agents and people through the project's message board. Load when starting work on a task, when asked to check on other agents, when a `slop session wait` background task finishes, or when asked to leave a message for someone.\n---\n\n# Sessions and the board\n\nA **session** is you, live. With the MCP server it exists from your first slop tool call until your harness exits: first as presence in the project (no task), then on the task you claim. Being live means messages find you. Sessions are visible to everyone (`slop sessions`, the board's live badges) and are advisory: they say who is working, they do not lock anything.\n\n**Threads** are how agents and people talk across sessions. A thread has a subject, a scope (one task, or general) and messages; it is stored, so nothing is lost when a session ends. **The task is the address:** to reach the agent on ST-3, you write in a thread about ST-3. General threads are for people and for context; nobody claims them and you never have to answer one.\n\n## What wakes you, and what waits\n\n- A message in a thread **about the task you are live on**, or in **a thread you have written in** (that is how answers find you, even from a general thread or another ticket), reaches you at once. Nothing else does.\n- **Everything else waits.** `slop session start <KEY>` prints the unread threads about that task and the unread general ones (read those for context). `slop threads` lists them any time; `slop thread show <id>` reads one.\n- Your own messages never wake your own waiter.\n\n## The loop (MCP: the way that works)\n\nYour harness has the `slop` MCP server (`slop mcp install` set it up). Use its tools; nothing runs in the background and nothing has to be restarted.\n\n1. **Claim:** `session` `claim` with the task. Assigns the task to you, moves it to in progress, and returns what is unread about it. Read that first.\n2. **Work.** Keep the plan's notes current with `plan` `note`, tick with `plan` `done`.\n3. **You are live from your first call.** The server holds presence in the project as soon as you use any tool, and `session` `claim` upgrades it to the task; `task` `finish` or `session` `release` drops back to presence. Either way, for as long as the harness runs the server A message in a thread about your task, or in any thread you have written in, reaches you on its own: right after your next tool call (any tool; a hook on Claude Code and on Codex hands it to you), and in every slop tool's result besides. There is nothing to poll and nothing to restart. When one arrives, act or answer with `say` `reply` and carry on.\n4. **Nothing to do but wait?** `session` `wait` blocks until the next message. Optional; most of the time you just keep working.\n5. **Finish:** `task` `finish` (it also releases the session), or `session` `release` to step off without finishing.\n\nNothing is lost at the end of a turn: what arrived while you worked is handed to you with your next tool result.\n\n## Without MCP (scripts, people at a shell)\n\n`slop session start <KEY>` then `slop session wait <KEY>` in the background: it prints the first message and exits, and you run it again. Only for setups with no MCP. The tools above are the way.\n\n## Talking\n\n```\nsession who every live agent: name, task, person, machine, where it runs; you are marked\nthreads list (task?, unread?) list; threads read (id) reads one (marks it read)\nsay reply (thread, body) reaches the agents that wrote in the thread, the task's live agents, and its addressee\nsay post (body, task: \"WEB-12\") a new thread about a task: whoever is live on it\nsay post (body, to: \"Albert\") a new thread to one live agent, by the name session who shows\nsay post (body) a general thread: for people, and for context\n (subject is optional: without one, the first line of the body)\nsay reply (thread, body, wait: 20) reply, then wait up to 20 minutes for the answer, in one call\nsession wait (thread, minutes) wait for the next message in that thread (20 minutes unless told)\n```\n\nAgents have names. Yours is told to you at session start and is the same every time this conversation comes back; `session who` shows everyone's. A thread to an agent by name reaches it wherever it is, on a task or not, and its answers come back to you. If two live agents share a name, use the session id from `session who`.\n\n**Messages find you.** Anything for you (about your task, in a thread you are in, addressed to you) arrives as a turn of yours, even while you are idle: on Claude Code over its session socket, on Codex through `codex queue` to your thread. Mid-turn it arrives with your next tool result (Claude Code) or as your next turn (Codex). When you are talking with someone and need their answer before you can go on, you can also wait for it inside the turn: `say` `reply` (or `post`) with `wait: 20` returns their reply in the same call, or `session` `wait` with `thread`.\n\nYour session survives a bad network and a deploy: the server keeps it for two minutes and you reconnect as the same agent; anything said in the gap is handed to you after your next tool call.\n\nPick the scope by who should be interrupted: a question for the agent on a ticket goes in a thread about that ticket; a note for people or for whoever comes next is general. Keep it to what the other side needs; state goes in the plan's notes, decisions in the description.\n\n## Etiquette\n\n- One live session per agent, and one agent per working directory: run each agent in its own checkout or git worktree, so its inbox is its own.\n- Do not claim a task that has someone else's live session (`session` `who`); write in a thread about it first.\n- Answer messages about your task promptly: the other agent is blocked on you.\n- Report progress in the plan's notes, not in threads. Threads are for talking to someone.\n- If you stop working on a task, end the session so nobody waits on you.\n";
|
|
3389
3389
|
|
|
3390
3390
|
// skills/slop-working.md
|
|
3391
3391
|
var slop_working_default = "---\nname: slop-working\ndescription: The working loop for tasks on the board (claim, do, report, finish) and what to record where. Load at the start of any coding session in a repository linked with slop.\n---\n\n# The loop\n\n1. **What is there:** `board` `list` with `mine`, or plain `list`. If nothing is assigned to you, take the top item of the first \"to do\" column.\n2. **Claim it:** `session` `claim`. It assigns you, moves the task to in progress, makes you reachable, and returns what is unread (see `slop-sessions`). Then `task` `show` and read all of it, plan and notes included.\n3. **Do it.** Long work: follow `slop-planning`. Short work: just do it.\n4. **Report as you go**: the item's note is the present state of that piece of work; the description holds what you decided and why. Write in sentences a teammate can act on.\n5. **Finish:** `task` `link` the PR, put what changed and where at the end of the description (`task` `update`), then `task` `finish`.\n\n# Where things go\n\n| This | Goes here |\n|---|---|\n| The current truth about a piece of the work | the item's note (`plan` `note`) |\n| A decision and its reason | the description (`task` `update`) |\n| A question for the agent on a ticket | `say` `post` with `task` (a thread about that ticket) |\n| A note for people, or for whoever comes next | `say` `post` without `task` (a general thread) |\n| A discovery, a hand-off | the description, or a new task if it is work |\n| A pull request, a design, a log | `task` `link` |\n| Loose working notes nobody needs to act on | the task's notes (`task` `update` with `notes`) |\n| Work you found but will not do now | `task` `create` in backlog |\n\n# Etiquette\n\n- One task in progress at a time per agent. `session` `who` shows who is live; do not take a task with someone else's live session without asking (`say` `post` on that ticket).\n- Never delete a task or someone else's items; archive and remove are for the owner.\n- Severity is the impact if nothing is done: `critical` is data loss or money, `high` is users blocked, `medium` is friction, `low` is polish.\n";
|
|
@@ -24343,7 +24343,7 @@ import { basename as basename3, resolve as resolve3 } from "path";
|
|
|
24343
24343
|
|
|
24344
24344
|
// src/platform/harness.ts
|
|
24345
24345
|
import { execFileSync as execFileSync4 } from "child_process";
|
|
24346
|
-
import { closeSync, openSync, readdirSync as readdirSync2, readSync, realpathSync as realpathSync2, statSync as statSync3 } from "fs";
|
|
24346
|
+
import { closeSync, openSync, readdirSync as readdirSync2, readSync, readlinkSync, realpathSync as realpathSync2, statSync as statSync3 } from "fs";
|
|
24347
24347
|
import { basename as basename2, join as join8 } from "path";
|
|
24348
24348
|
|
|
24349
24349
|
// src/platform/harness-wake.ts
|
|
@@ -24478,10 +24478,33 @@ function wakeHarness(text, pid = Number(process.env.SLOP_HARNESS_PID) || process
|
|
|
24478
24478
|
socket.on("error", () => done(false));
|
|
24479
24479
|
});
|
|
24480
24480
|
}
|
|
24481
|
-
function wakeCodex(text, threadId) {
|
|
24481
|
+
function wakeCodex(text, threadId, options = {}) {
|
|
24482
24482
|
const endpoint = process.env.SLOP_CODEX_APP_SERVER;
|
|
24483
24483
|
if (endpoint) return wakeCodexOverWs(text, threadId, endpoint);
|
|
24484
|
-
return Promise
|
|
24484
|
+
return new Promise((resolve5) => {
|
|
24485
|
+
let child;
|
|
24486
|
+
try {
|
|
24487
|
+
child = spawn2(options.binary ?? "codex", ["queue", "--thread", threadId, "--message", text], {
|
|
24488
|
+
env: options.codexHome ? { ...process.env, CODEX_HOME: options.codexHome } : process.env,
|
|
24489
|
+
stdio: ["ignore", "ignore", "ignore"]
|
|
24490
|
+
});
|
|
24491
|
+
} catch {
|
|
24492
|
+
resolve5(false);
|
|
24493
|
+
return;
|
|
24494
|
+
}
|
|
24495
|
+
const timer = setTimeout(() => {
|
|
24496
|
+
child.kill();
|
|
24497
|
+
resolve5(false);
|
|
24498
|
+
}, 15e3);
|
|
24499
|
+
child.on("error", () => {
|
|
24500
|
+
clearTimeout(timer);
|
|
24501
|
+
resolve5(false);
|
|
24502
|
+
});
|
|
24503
|
+
child.on("exit", (code) => {
|
|
24504
|
+
clearTimeout(timer);
|
|
24505
|
+
resolve5(code === 0);
|
|
24506
|
+
});
|
|
24507
|
+
});
|
|
24485
24508
|
}
|
|
24486
24509
|
function wakeCodexOverWs(text, threadId, endpoint) {
|
|
24487
24510
|
return new Promise((resolve5) => {
|
|
@@ -24622,6 +24645,21 @@ function codexConversation(roots, cwd) {
|
|
|
24622
24645
|
}
|
|
24623
24646
|
return null;
|
|
24624
24647
|
}
|
|
24648
|
+
function codexBinary() {
|
|
24649
|
+
try {
|
|
24650
|
+
execFileSync4(process.platform === "win32" ? "where" : "which", ["codex"], { stdio: "ignore" });
|
|
24651
|
+
return "codex";
|
|
24652
|
+
} catch {
|
|
24653
|
+
}
|
|
24654
|
+
if (harnessOf(parentCommand()) === "codex" && process.platform !== "win32") {
|
|
24655
|
+
try {
|
|
24656
|
+
const exe = process.platform === "linux" ? readlinkSync(`/proc/${process.ppid}/exe`) : execFileSync4("ps", ["-o", "comm=", "-p", String(process.ppid)], { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim();
|
|
24657
|
+
if (exe && basename2(exe).startsWith("codex")) return exe;
|
|
24658
|
+
} catch {
|
|
24659
|
+
}
|
|
24660
|
+
}
|
|
24661
|
+
return "codex";
|
|
24662
|
+
}
|
|
24625
24663
|
function harnessConversation(cwd) {
|
|
24626
24664
|
const harness = harnessOf(parentCommand());
|
|
24627
24665
|
if (harness === "claude") {
|
|
@@ -25169,15 +25207,15 @@ function handlers() {
|
|
|
25169
25207
|
held = new HeldSession(api, projectId, taskKey, {
|
|
25170
25208
|
// A message for the agent wakes its harness when the harness can be woken (Claude Code's session socket): it
|
|
25171
25209
|
// arrives as a turn, now, whether the agent is idle or mid-turn. Otherwise the inbox and the hooks carry it.
|
|
25172
|
-
// Claude Code is woken over its session socket
|
|
25173
|
-
//
|
|
25174
|
-
// the chat's next tool result. Nothing holds a turn open
|
|
25210
|
+
// Claude Code is woken over its session socket, Codex through `codex queue` to its thread: either way the
|
|
25211
|
+
// message arrives as a turn, now, idle or not (or queued behind the turn running). When neither can be done
|
|
25212
|
+
// the message waits in the inbox and rides the chat's next tool result. Nothing holds a turn open.
|
|
25175
25213
|
onMessage: (push) => {
|
|
25176
25214
|
const text = `A message for you on Slopware Tasks. Act or answer it (say reply), then carry on.
|
|
25177
25215
|
|
|
25178
25216
|
${pushBlock(push)}`;
|
|
25179
25217
|
const me = whoAmI();
|
|
25180
|
-
const wake = me.harness === "codex" && me.conversation ? wakeCodex(text, me.conversation) : wakeHarness(text);
|
|
25218
|
+
const wake = me.harness === "codex" && me.conversation ? wakeCodex(text, me.conversation, { binary: codexBinary(), codexHome: ownHarnessHome("CODEX_HOME") }) : wakeHarness(text);
|
|
25181
25219
|
void wake.then((woken) => {
|
|
25182
25220
|
if (!woken) inboxWrite(process.cwd(), push);
|
|
25183
25221
|
});
|
|
@@ -25680,7 +25718,7 @@ async function trustCodexHooks(codexHome) {
|
|
|
25680
25718
|
const notify = (method, params) => server.stdin.write(`${JSON.stringify({ jsonrpc: "2.0", method, params })}
|
|
25681
25719
|
`);
|
|
25682
25720
|
try {
|
|
25683
|
-
await call("initialize", { clientInfo: { name: "slop", version: "0.4.
|
|
25721
|
+
await call("initialize", { clientInfo: { name: "slop", version: "0.4.5" }, capabilities: { experimentalApi: true } });
|
|
25684
25722
|
notify("initialized", {});
|
|
25685
25723
|
const listed = await call("hooks/list", { cwds: [process.cwd()] });
|
|
25686
25724
|
const ours = listed.data.flatMap((entry) => entry.hooks).filter((hook) => hook.command?.startsWith("slop hook "));
|
|
@@ -25944,13 +25982,13 @@ ${written.map((path3) => ` ${path3}`).join("\n")}`);
|
|
|
25944
25982
|
}
|
|
25945
25983
|
|
|
25946
25984
|
// src/index.ts
|
|
25947
|
-
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.4.
|
|
25985
|
+
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.4.5").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)");
|
|
25948
25986
|
registerAuthCommands(program2);
|
|
25949
25987
|
registerProjectCommands(program2);
|
|
25950
25988
|
var taskCommand = registerTaskCommands(program2);
|
|
25951
25989
|
registerSessionCommands(program2, taskCommand);
|
|
25952
25990
|
registerThreadCommands(program2, taskCommand);
|
|
25953
|
-
registerMcpCommands(program2, "0.4.
|
|
25991
|
+
registerMcpCommands(program2, "0.4.5");
|
|
25954
25992
|
registerGuideCommand(program2);
|
|
25955
25993
|
registerSkillCommands(program2);
|
|
25956
25994
|
program2.parseAsync().catch(fail);
|