slopwaresystems 0.1.1 → 0.1.2
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 +1 -1
- package/dist/index.js +32 -25
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@ The [Slopware Tasks](https://tasks.slopware.systems) board from the terminal. Bu
|
|
|
4
4
|
|
|
5
5
|
```bash
|
|
6
6
|
npm i -g slopwaresystems
|
|
7
|
-
slop login # approve the machine in your browser
|
|
7
|
+
slop login # approve the machine in your browser (on a VM or over SSH: open the printed link on any device)
|
|
8
8
|
cd your-repo && slop link WEB
|
|
9
9
|
slop task create "Retry storms on 429" -s high -d -
|
|
10
10
|
slop skills install # puts the agent skills into .claude/skills/
|
package/dist/index.js
CHANGED
|
@@ -3379,7 +3379,7 @@ import { homedir } from "os";
|
|
|
3379
3379
|
import { join } from "path";
|
|
3380
3380
|
|
|
3381
3381
|
// skills/slop-cli.md
|
|
3382
|
-
var slop_cli_default = '---\nname: slop-cli\ndescription: How to use the slop CLI to read and update the task board this repository is linked to. Load when asked to create, update, plan, comment on or finish tasks.\n---\n\n# slop: the board from the terminal\n\nThe repository you are in is linked to a project on Slopware Tasks. Everything a person can do on the board, you can do with `slop`. Add `--json` to any command for machine-readable output.\n\n## Orientation\n\n```bash\nslop status # which project this checkout is linked to\nslop statuses # the board\'s columns (key, label, category)\nslop members # who tasks can be assigned to\nslop task list [--all] [--status <column>] [--assignee me]\nslop task show <KEY> # title, status, severity, assignee, links, plan, description\nslop task history <KEY> # everything that happened to it, in order\n```\n\nTask keys look like `WEB-12`; the bare number works inside the linked project.\n\n## Creating and changing tasks\n\n```bash\nslop task create "Short imperative title" -d "What, where, why" -s <low|medium|high|critical> [--status <column>] [-a <email|me|none>]\nslop task update <KEY> [--title \u2026] [-d \u2026] [-s \u2026] [--status \u2026] [-a \u2026]\nslop task move <KEY> "<column label or key>"\nslop task start <KEY> # first "in progress" column\nslop task done <KEY> # first "done" column\nslop task assign <KEY> <email|me|none>\nslop task link <KEY> <url> # a PR, a design, a log; GitHub PRs are recognised\nslop task notes <KEY> [text|-] # the scratchpad under the links: read it, or replace it whole\n\n## Sessions (see the `slop-sessions` skill)\n\n```bash\nslop session start <KEY> # claim: assign me, move to in progress\nslop session wait <KEY> # be live until someone says something; run in the background\nslop session end <KEY>\nslop sessions # who is live on what\nslop task ask <KEY> "\u2026" # say something to whoever is live on it (also on the timeline)\n```\nslop task archive <KEY> # off the board, history kept\n```\n\nDescriptions are Markdown and are shown in full. Long text: `-d -` reads stdin.\n\n## Plans (see the `slop-planning` skill)\n\n```bash\nslop task items <KEY> # the plan, numbered, with notes\nslop task plan <KEY> - <<\'EOF\' # one item per line, appended in order\nInventory the Intercom data model\nWrite the export script\nEOF\nslop task item add <KEY> "title" [-n "note"]\nslop task item done <KEY> <n> # by number from `items`\nslop task item reopen <KEY> <n>\nslop task item note <KEY> <n> "what is true now"\nslop task item rename <KEY> <n> "new title"\nslop task item remove <KEY> <n>\n```\n\n## Projects\n\n```bash\nslop projects\nslop project create "Name" [--key KEY]\nslop project update [--name \u2026] [--description \u2026]
|
|
3382
|
+
var slop_cli_default = '---\nname: slop-cli\ndescription: How to use the slop CLI to read and update the task board this repository is linked to. Load when asked to create, update, plan, comment on or finish tasks.\n---\n\n# slop: the board from the terminal\n\nThe repository you are in is linked to a project on Slopware Tasks. Everything a person can do on the board, you can do with `slop`. Add `--json` to any command for machine-readable output.\n\n## Orientation\n\n```bash\nslop status # which project this checkout is linked to\nslop statuses # the board\'s columns (key, label, category)\nslop members # who tasks can be assigned to\nslop task list [--all] [--status <column>] [--assignee me]\nslop task show <KEY> # title, status, severity, assignee, links, plan, description\nslop task history <KEY> # everything that happened to it, in order\n```\n\nTask keys look like `WEB-12`; the bare number works inside the linked project.\n\nA link belongs to the clone: every git worktree of it (including agent sandboxes made with `git worktree add`) is linked without doing anything. On a machine with no browser, `slop login` prints a link and a code; open it on any device.\n\n## Creating and changing tasks\n\n```bash\nslop task create "Short imperative title" -d "What, where, why" -s <low|medium|high|critical> [--status <column>] [-a <email|me|none>]\nslop task update <KEY> [--title \u2026] [-d \u2026] [-s \u2026] [--status \u2026] [-a \u2026]\nslop task move <KEY> "<column label or key>"\nslop task start <KEY> # first "in progress" column\nslop task done <KEY> # first "done" column\nslop task assign <KEY> <email|me|none>\nslop task link <KEY> <url> # a PR, a design, a log; GitHub PRs are recognised\nslop task notes <KEY> [text|-] # the scratchpad under the links: read it, or replace it whole\n\n## Sessions (see the `slop-sessions` skill)\n\n```bash\nslop session start <KEY> # claim: assign me, move to in progress\nslop session wait <KEY> # be live until someone says something; run in the background\nslop session end <KEY>\nslop sessions # who is live on what\nslop task ask <KEY> "\u2026" # say something to whoever is live on it (also on the timeline)\n```\nslop task archive <KEY> # off the board, history kept\n```\n\nDescriptions are Markdown and are shown in full. Long text: `-d -` reads stdin.\n\n## Plans (see the `slop-planning` skill)\n\n```bash\nslop task items <KEY> # the plan, numbered, with notes\nslop task plan <KEY> - <<\'EOF\' # one item per line, appended in order\nInventory the Intercom data model\nWrite the export script\nEOF\nslop task item add <KEY> "title" [-n "note"]\nslop task item done <KEY> <n> # by number from `items`\nslop task item reopen <KEY> <n>\nslop task item note <KEY> <n> "what is true now"\nslop task item rename <KEY> <n> "new title"\nslop task item remove <KEY> <n>\n```\n\n## Projects\n\n```bash\nslop projects\nslop project create "Name" [--key KEY]\nslop project update [--name \u2026] [--description \u2026]\nslop project status add "In review" -c in_progress # columns: add | rename | remove | order\n```\n\n## Rules of the road\n\n- Do not delete tasks you did not create. Archive finished work instead.\n- Keep titles short and imperative; put the reasoning in the description or the plan\'s notes. There is no chat: the plan\'s notes are the running state, the description holds decisions.\n- When you open a pull request for a task, link it.\n';
|
|
3383
3383
|
|
|
3384
3384
|
// skills/slop-planning.md
|
|
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: `slop task show <KEY>`. 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```bash\nslop task plan <KEY> - <<\'EOF\'\nInventory every Intercom object we rely on (conversations, users, tags)\nDecide the target schema for the support agent\nWrite and test the export script against a sample\nRun the full export; record counts\nImport into the support agent; verify counts match\nSwitch inbound routing; keep Intercom read-only for a week\nRemove the Intercom SDK and env vars\nEOF\n```\n\n\n## Working\n\n- Before an item: `slop task item note <KEY> <n> "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: `slop task item done <KEY> <n>`. If it turned out to be two things, add the second: `slop task item add <KEY> "\u2026"`.\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: `slop task create "\u2026" --status backlog -d "Found while doing <KEY>: \u2026"`.\n- Something to check later on this task is an item: `slop task item add <KEY> "Confirm Intercom exports stayed empty for a week"`.\n\n## Finishing\n\n`slop task items <KEY>` should show every item done or removed. Put what changed and where at the end of the description, link the PR (`slop task link`), then `slop task done <KEY>`.\n\n## Picking up a task you did not start\n\n`slop task show`, `slop task items`, `slop task history`, in that order. 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';
|
|
@@ -23207,9 +23207,21 @@ var Invitation = external_exports.object({
|
|
|
23207
23207
|
role: OrgRole,
|
|
23208
23208
|
createdAt: Timestamp
|
|
23209
23209
|
});
|
|
23210
|
+
var InviteLink = external_exports.object({
|
|
23211
|
+
id: Id,
|
|
23212
|
+
role: OrgRole,
|
|
23213
|
+
createdAt: Timestamp,
|
|
23214
|
+
expiresAt: Timestamp.nullable(),
|
|
23215
|
+
uses: external_exports.number()
|
|
23216
|
+
});
|
|
23217
|
+
var CreateInviteLinkInput = external_exports.object({ role: OrgRole.default("member"), expiresInHours: external_exports.number().int().min(1).max(24 * 30).nullable().default(24) });
|
|
23218
|
+
var CreatedInviteLink = InviteLink.extend({ url: external_exports.string() });
|
|
23219
|
+
var JoinPreview = external_exports.object({ org: external_exports.object({ id: Id, name: external_exports.string() }), role: OrgRole, alreadyMember: external_exports.boolean() });
|
|
23210
23220
|
var MembersResult = external_exports.object({
|
|
23211
23221
|
members: external_exports.array(Member),
|
|
23212
|
-
invitations: external_exports.array(Invitation)
|
|
23222
|
+
invitations: external_exports.array(Invitation),
|
|
23223
|
+
/** Active links only. Admins see them; members get an empty list. */
|
|
23224
|
+
links: external_exports.array(InviteLink)
|
|
23213
23225
|
});
|
|
23214
23226
|
var InviteMemberInput = external_exports.object({ email: Email, role: OrgRole.default("member") });
|
|
23215
23227
|
var UpdateMemberInput = external_exports.object({ role: OrgRole });
|
|
@@ -23241,8 +23253,6 @@ function findStatus(statuses, ref) {
|
|
|
23241
23253
|
|
|
23242
23254
|
// ../contract/src/work/projects.ts
|
|
23243
23255
|
var ProjectKey = external_exports.string().trim().toUpperCase().pipe(external_exports.string().regex(/^[A-Z][A-Z0-9]{1,7}$/, "2\u20138 letters or digits, starting with a letter"));
|
|
23244
|
-
var PROJECT_THEMES = ["slate", "stone", "zinc", "mist", "sand", "rose", "violet", "emerald"];
|
|
23245
|
-
var ProjectTheme = external_exports.enum(PROJECT_THEMES);
|
|
23246
23256
|
var TaskCounts = external_exports.record(external_exports.string(), external_exports.number());
|
|
23247
23257
|
var Project = external_exports.object({
|
|
23248
23258
|
id: Id,
|
|
@@ -23250,8 +23260,6 @@ var Project = external_exports.object({
|
|
|
23250
23260
|
key: external_exports.string(),
|
|
23251
23261
|
name: external_exports.string(),
|
|
23252
23262
|
description: external_exports.string(),
|
|
23253
|
-
/** Null: no theme of its own; the viewer's personal theme applies. */
|
|
23254
|
-
theme: ProjectTheme.nullable(),
|
|
23255
23263
|
/** The board's columns, in order. */
|
|
23256
23264
|
statuses: external_exports.array(ProjectStatus),
|
|
23257
23265
|
createdAt: Timestamp,
|
|
@@ -23266,8 +23274,7 @@ var CreateProjectInput = external_exports.object({
|
|
|
23266
23274
|
});
|
|
23267
23275
|
var UpdateProjectInput = external_exports.object({
|
|
23268
23276
|
name: external_exports.string().trim().min(1).max(80),
|
|
23269
|
-
description: external_exports.string().trim().max(2e3)
|
|
23270
|
-
theme: ProjectTheme.nullable()
|
|
23277
|
+
description: external_exports.string().trim().max(2e3)
|
|
23271
23278
|
}).partial();
|
|
23272
23279
|
function suggestProjectKey(name) {
|
|
23273
23280
|
const words = name.toUpperCase().replace(/[^A-Z0-9 ]+/g, " ").split(/\s+/).filter(Boolean);
|
|
@@ -23640,19 +23647,21 @@ function openBrowser(url2) {
|
|
|
23640
23647
|
}
|
|
23641
23648
|
}
|
|
23642
23649
|
var sleep = (ms) => new Promise((done) => setTimeout(done, ms));
|
|
23650
|
+
var headless = () => Boolean(process.env.SSH_CONNECTION || process.env.SSH_TTY) || platform2() === "linux" && !process.env.DISPLAY && !process.env.WAYLAND_DISPLAY;
|
|
23643
23651
|
function registerAuthCommands(program3) {
|
|
23644
|
-
program3.command("login").description("Sign this machine in. Opens the web app, where you approve the login.").option("--url <url>", "the Slopware Tasks instance to sign in to").option("--no-browser", "print the link instead of opening it").action(async (options) => {
|
|
23652
|
+
program3.command("login").description("Sign this machine in. Opens the web app, where you approve the login. On a VM or over SSH, open the printed link on any device.").option("--url <url>", "the Slopware Tasks instance to sign in to").option("--no-browser", "print the link instead of opening it").action(async (options) => {
|
|
23645
23653
|
const baseUrl = (options.url || process.env.SLOP_URL || readConfig()?.baseUrl || DEFAULT_URL).replace(/\/+$/, "");
|
|
23646
23654
|
const api = createApi({ baseUrl, token: null });
|
|
23647
23655
|
const started = await api.post("/cli/auth/start", { machine: machineInfo() });
|
|
23656
|
+
const remote = headless();
|
|
23648
23657
|
process.stderr.write(`
|
|
23649
|
-
Approve this login in your
|
|
23658
|
+
Approve this login in a browser${remote ? " on any device (this machine has no browser; your laptop or phone is fine)" : ""}:
|
|
23650
23659
|
${started.verifyUrl}
|
|
23651
23660
|
|
|
23652
23661
|
Confirm the code matches: ${started.userCode}
|
|
23653
23662
|
|
|
23654
23663
|
`);
|
|
23655
|
-
if (options.browser) openBrowser(started.verifyUrl);
|
|
23664
|
+
if (options.browser && !remote) openBrowser(started.verifyUrl);
|
|
23656
23665
|
const deadline = Date.now() + started.expiresIn * 1e3;
|
|
23657
23666
|
while (Date.now() < deadline) {
|
|
23658
23667
|
await sleep(started.interval * 1e3);
|
|
@@ -23721,8 +23730,9 @@ function git(args, cwd = process.cwd()) {
|
|
|
23721
23730
|
function findRepo() {
|
|
23722
23731
|
const commonDir = git(["rev-parse", "--path-format=absolute", "--git-common-dir"]);
|
|
23723
23732
|
if (!commonDir) return null;
|
|
23724
|
-
const
|
|
23725
|
-
|
|
23733
|
+
const worktree = git(["rev-parse", "--show-toplevel"]);
|
|
23734
|
+
const root = basename(commonDir) === ".git" ? resolve(commonDir, "..") : worktree ?? commonDir;
|
|
23735
|
+
return { root, worktree: worktree ?? root, linkPath: join4(commonDir, "slop.json") };
|
|
23726
23736
|
}
|
|
23727
23737
|
function requireRepo() {
|
|
23728
23738
|
const repo = findRepo();
|
|
@@ -23845,17 +23855,13 @@ function registerProjectCommands(program3) {
|
|
|
23845
23855
|
const statuses = await api.put(`/projects/${target.id}/statuses/order`, { ids: refs.map((ref) => columnOf(target, ref).id) });
|
|
23846
23856
|
emit(statuses, () => list(statuses));
|
|
23847
23857
|
});
|
|
23848
|
-
project.command("update").description("Change the linked project's name
|
|
23858
|
+
project.command("update").description("Change the linked project's name or description.").option("-p, --project <key>", "project key (default: the linked project)").option("-n, --name <name>").option("-d, --description <text>").action(async (options) => {
|
|
23849
23859
|
const api = authedApi();
|
|
23850
23860
|
const target = await resolveProject(api, options.project);
|
|
23851
|
-
|
|
23852
|
-
|
|
23853
|
-
throw new CliError(`Unknown theme "${theme}"`, `use one of: ${PROJECT_THEMES.join(", ")}, none`);
|
|
23854
|
-
}
|
|
23855
|
-
if (options.name === void 0 && options.description === void 0 && theme === void 0) {
|
|
23856
|
-
throw new CliError("Nothing to change", "pass --name, --description or --theme");
|
|
23861
|
+
if (options.name === void 0 && options.description === void 0) {
|
|
23862
|
+
throw new CliError("Nothing to change", "pass --name or --description");
|
|
23857
23863
|
}
|
|
23858
|
-
const updated = await api.patch(`/projects/${target.id}`, { name: options.name, description: options.description
|
|
23864
|
+
const updated = await api.patch(`/projects/${target.id}`, { name: options.name, description: options.description });
|
|
23859
23865
|
emit(updated, () => `Updated ${updated.key} ${updated.name}`);
|
|
23860
23866
|
});
|
|
23861
23867
|
project.command("create <name>").description("Create a project.").option("-k, --key <key>", "task key prefix, e.g. WEB (default: derived from the name)").option("-d, --description <text>", "what the project is").option("--org <slug-or-id>", "which org, if you belong to several").action(async (name, options) => {
|
|
@@ -23891,10 +23897,11 @@ Try: slop task create "My first task"`);
|
|
|
23891
23897
|
program3.command("status").description("Show what this repository is linked to.").action(async () => {
|
|
23892
23898
|
const repo = findRepo();
|
|
23893
23899
|
const link = repo ? readLink(repo) : null;
|
|
23900
|
+
const where = !repo ? "" : repo.worktree === repo.root ? repo.root : `${repo.worktree} (worktree of ${repo.root})`;
|
|
23894
23901
|
emit(
|
|
23895
|
-
{ repo: repo?.root ?? null, link },
|
|
23896
|
-
() => !repo ? "Not inside a git repository." : !link ? `${
|
|
23897
|
-
not linked. Run: slop link <PROJECT_KEY>` : `${
|
|
23902
|
+
{ repo: repo?.root ?? null, worktree: repo?.worktree ?? null, link },
|
|
23903
|
+
() => !repo ? "Not inside a git repository." : !link ? `${where}
|
|
23904
|
+
not linked. Run: slop link <PROJECT_KEY>` : `${where}
|
|
23898
23905
|
linked to ${link.projectKey} ${link.projectName} (${link.baseUrl})`
|
|
23899
23906
|
);
|
|
23900
23907
|
});
|
|
@@ -24253,7 +24260,7 @@ ${written.map((path3) => ` ${path3}`).join("\n")}`);
|
|
|
24253
24260
|
}
|
|
24254
24261
|
|
|
24255
24262
|
// src/index.ts
|
|
24256
|
-
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.1.
|
|
24263
|
+
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.1.2").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)");
|
|
24257
24264
|
registerAuthCommands(program2);
|
|
24258
24265
|
registerProjectCommands(program2);
|
|
24259
24266
|
registerSessionCommands(program2, registerTaskCommands(program2));
|