smashspace 0.1.5 → 0.1.7
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/smash.mjs +80 -23
- package/package.json +1 -1
package/dist/smash.mjs
CHANGED
|
@@ -3599,7 +3599,11 @@ the target board for you.
|
|
|
3599
3599
|
shim that fails with "Volta error: Could not execute command".
|
|
3600
3600
|
- MCP server: \`smashspace-mcp\` (exposes \`smash_*\` tools, same token as the CLI)
|
|
3601
3601
|
- Sign in once per machine: \`smash login\` (browser approval; token saved to
|
|
3602
|
-
\`~/.config/smash/credentials.json\` and reused by MCP)
|
|
3602
|
+
\`~/.config/smash/credentials.json\` and reused by MCP). It is idempotent \u2014 if a
|
|
3603
|
+
valid token is already saved it does nothing, so don't re-run it in a loop.
|
|
3604
|
+
- If \`smash\` is on PATH, **call it directly** \u2014 do not prefix
|
|
3605
|
+
\`PATH=\u2026/node/\u2026/bin\`. A "Volta error: Could not execute command" means a stale
|
|
3606
|
+
install; fix it with \`volta install smashspace\` rather than PATH-hacking.
|
|
3603
3607
|
|
|
3604
3608
|
### Which board am I on?
|
|
3605
3609
|
|
|
@@ -3671,7 +3675,20 @@ Keep the card's agent state current while you work:
|
|
|
3671
3675
|
}
|
|
3672
3676
|
\`\`\`
|
|
3673
3677
|
|
|
3674
|
-
Set \`status: "done"\` only after verification passes.
|
|
3678
|
+
Set \`status: "done"\` only after verification passes. Keep it current on
|
|
3679
|
+
meaningful moves/edits too (not just during todo-dev) so a human can see whose
|
|
3680
|
+
turn it is at a glance.
|
|
3681
|
+
|
|
3682
|
+
## Batching (stay inside the CLI)
|
|
3683
|
+
|
|
3684
|
+
Prefer the CLI's own batch flags over dropping to a shell/node loop:
|
|
3685
|
+
|
|
3686
|
+
\`\`\`bash
|
|
3687
|
+
smash create "Title" --desc-file spec.md # multi-line description, no quoting pain
|
|
3688
|
+
smash card update <cardId> --desc-file - # description from stdin
|
|
3689
|
+
smash checklist add <cardId> --file steps.txt # one item per line (or many args)
|
|
3690
|
+
smash checklist add <cardId> "Step 1" "Step 2" "Step 3"
|
|
3691
|
+
\`\`\`
|
|
3675
3692
|
|
|
3676
3693
|
## Command: /smash list
|
|
3677
3694
|
|
|
@@ -3829,6 +3846,15 @@ function show(data, headerLines = []) {
|
|
|
3829
3846
|
if (data !== void 0)
|
|
3830
3847
|
console.log(JSON.stringify(data, null, 2));
|
|
3831
3848
|
}
|
|
3849
|
+
async function readTextArg(pathOrDash) {
|
|
3850
|
+
if (pathOrDash === "-") {
|
|
3851
|
+
const chunks = [];
|
|
3852
|
+
for await (const chunk of stdin)
|
|
3853
|
+
chunks.push(chunk);
|
|
3854
|
+
return Buffer.concat(chunks).toString("utf8");
|
|
3855
|
+
}
|
|
3856
|
+
return readFile2(pathOrDash, "utf8");
|
|
3857
|
+
}
|
|
3832
3858
|
function resolveBaseUrl(loaded = loadProjectConfig()) {
|
|
3833
3859
|
if (program2.getOptionValueSource("baseUrl") === "cli")
|
|
3834
3860
|
return getOpts().baseUrl;
|
|
@@ -4058,12 +4084,14 @@ ${detail.card.description}`);
|
|
|
4058
4084
|
}
|
|
4059
4085
|
}
|
|
4060
4086
|
});
|
|
4061
|
-
cardCmd.command("update <cardId>").description("Update card fields (title, description, due date, cover, checklist title)").option("--title <title>", "New title").option("--description <description>", "New description (markdown)").option("--due <iso>", "Due date as ISO string, or empty string to clear").option("--cover <attachmentId>", "Cover attachment ID, or empty string to clear").option("--checklist-title <title>", "Custom checklist section title, or empty string to clear").action(
|
|
4087
|
+
cardCmd.command("update <cardId>").description("Update card fields (title, description, due date, cover, checklist title)").option("--title <title>", "New title").option("--description <description>", "New description (markdown)").option("--desc-file <path>", "read description from a file (or - for stdin)").option("--due <iso>", "Due date as ISO string, or empty string to clear").option("--cover <attachmentId>", "Cover attachment ID, or empty string to clear").option("--checklist-title <title>", "Custom checklist section title, or empty string to clear").action(
|
|
4062
4088
|
async (cardId, opts) => {
|
|
4063
4089
|
const patch = {};
|
|
4064
4090
|
if (opts.title !== void 0)
|
|
4065
4091
|
patch.title = opts.title;
|
|
4066
|
-
if (opts.
|
|
4092
|
+
if (opts.descFile !== void 0)
|
|
4093
|
+
patch.description = await readTextArg(opts.descFile);
|
|
4094
|
+
else if (opts.description !== void 0)
|
|
4067
4095
|
patch.description = opts.description;
|
|
4068
4096
|
if (opts.due !== void 0)
|
|
4069
4097
|
patch.dueAt = opts.due === "" ? null : opts.due;
|
|
@@ -4072,7 +4100,7 @@ cardCmd.command("update <cardId>").description("Update card fields (title, descr
|
|
|
4072
4100
|
if (opts.checklistTitle !== void 0)
|
|
4073
4101
|
patch.checklistTitle = opts.checklistTitle === "" ? null : opts.checklistTitle;
|
|
4074
4102
|
if (Object.keys(patch).length === 0) {
|
|
4075
|
-
console.error("Provide at least one of: --title, --description, --due, --cover, --checklist-title");
|
|
4103
|
+
console.error("Provide at least one of: --title, --description, --desc-file, --due, --cover, --checklist-title");
|
|
4076
4104
|
process.exit(1);
|
|
4077
4105
|
}
|
|
4078
4106
|
const card = await api(`/api/cards/${cardId}`, {
|
|
@@ -4198,15 +4226,28 @@ archiveCmd.command("purge <cardId>").description("Permanently delete an archived
|
|
|
4198
4226
|
console.log(`\u2713 card ${cardId} permanently deleted`);
|
|
4199
4227
|
});
|
|
4200
4228
|
var checklistCmd = program2.command("checklist").description("Card checklist items");
|
|
4201
|
-
checklistCmd.command("add <cardId>
|
|
4202
|
-
|
|
4203
|
-
|
|
4204
|
-
|
|
4205
|
-
|
|
4206
|
-
|
|
4207
|
-
|
|
4208
|
-
|
|
4209
|
-
|
|
4229
|
+
checklistCmd.command("add <cardId> [titles...]").description("Add one or more checklist items (repeat titles, or --file with one per line)").option("--file <path>", "read items from a file, one per line (or - for stdin)").action(async (cardId, titles, opts) => {
|
|
4230
|
+
let items = titles ?? [];
|
|
4231
|
+
if (opts.file) {
|
|
4232
|
+
const text = await readTextArg(opts.file);
|
|
4233
|
+
items = items.concat(
|
|
4234
|
+
text.split(/\r?\n/).map((s) => s.trim()).filter(Boolean)
|
|
4235
|
+
);
|
|
4236
|
+
}
|
|
4237
|
+
if (items.length === 0) {
|
|
4238
|
+
console.error("Provide at least one item title, or --file <path>");
|
|
4239
|
+
process.exit(1);
|
|
4240
|
+
}
|
|
4241
|
+
const created = [];
|
|
4242
|
+
for (const title of items) {
|
|
4243
|
+
created.push(
|
|
4244
|
+
await api(`/api/cards/${cardId}/checklist-items`, {
|
|
4245
|
+
method: "POST",
|
|
4246
|
+
body: JSON.stringify({ title })
|
|
4247
|
+
})
|
|
4248
|
+
);
|
|
4249
|
+
}
|
|
4250
|
+
show(created, [`\u2713 ${created.length} checklist item(s) added`]);
|
|
4210
4251
|
});
|
|
4211
4252
|
checklistCmd.command("toggle <itemId>").description("Toggle a checklist item to checked/unchecked").option("--check", "Mark as checked", false).option("--uncheck", "Mark as unchecked", false).action(
|
|
4212
4253
|
async (itemId, opts) => {
|
|
@@ -4531,8 +4572,30 @@ program2.command("login").description(
|
|
|
4531
4572
|
).option(
|
|
4532
4573
|
"--token <token>",
|
|
4533
4574
|
"Skip the browser flow and save the provided token directly"
|
|
4575
|
+
).option(
|
|
4576
|
+
"-f, --force",
|
|
4577
|
+
"Re-issue a token even if a valid one is already saved"
|
|
4534
4578
|
).action(async (opts) => {
|
|
4535
4579
|
const baseUrl = resolveBaseUrl();
|
|
4580
|
+
if (!opts.token && !opts.force) {
|
|
4581
|
+
const existing = await resolveToken(baseUrl);
|
|
4582
|
+
if (existing) {
|
|
4583
|
+
const check = await fetch(new URL("/api/usage/me", baseUrl), {
|
|
4584
|
+
headers: { Authorization: `Bearer ${existing}` }
|
|
4585
|
+
}).catch(() => null);
|
|
4586
|
+
if (check?.ok) {
|
|
4587
|
+
const me2 = await check.json();
|
|
4588
|
+
show(
|
|
4589
|
+
{ baseUrl, userId: me2.userId, alreadySignedIn: true },
|
|
4590
|
+
[
|
|
4591
|
+
`\u2713 already signed in as ${me2.userId} on ${baseUrl} \u2014 saved token is valid, nothing to do.`,
|
|
4592
|
+
` (use 'smash login --force' to re-issue)`
|
|
4593
|
+
]
|
|
4594
|
+
);
|
|
4595
|
+
return;
|
|
4596
|
+
}
|
|
4597
|
+
}
|
|
4598
|
+
}
|
|
4536
4599
|
let token = opts.token?.trim();
|
|
4537
4600
|
if (!token) {
|
|
4538
4601
|
try {
|
|
@@ -4782,24 +4845,18 @@ program2.command("init").description(
|
|
|
4782
4845
|
show(void 0, [...actions, ...next]);
|
|
4783
4846
|
}
|
|
4784
4847
|
);
|
|
4785
|
-
program2.command("create <title>").description(`Create a card on the resolved board (${CONFIG_FILENAME}, default "main")`).option("-b, --board <label>", "board label from config").option("--board-id <id>", "explicit board id (bypass config)").option("-l, --list <name>", "target list name (default: first list)").option("-d, --desc <text>", "card description (Markdown)").action(async (title, opts) => {
|
|
4848
|
+
program2.command("create <title>").description(`Create a card on the resolved board (${CONFIG_FILENAME}, default "main")`).option("-b, --board <label>", "board label from config").option("--board-id <id>", "explicit board id (bypass config)").option("-l, --list <name>", "target list name (default: first list)").option("-d, --desc <text>", "card description (Markdown)").option("--desc-file <path>", "read description from a file (or - for stdin)").action(async (title, opts) => {
|
|
4786
4849
|
const ctx = resolveContext(opts);
|
|
4787
4850
|
const detail = await fetchBoard(ctx.boardId, ctx.baseUrl);
|
|
4788
4851
|
const list = opts.list ? findListByName(detail, opts.list) : detail.lists[0];
|
|
4789
4852
|
if (!list)
|
|
4790
4853
|
throw new Error(`Board ${ctx.boardId} has no lists`);
|
|
4854
|
+
const description = opts.descFile ? await readTextArg(opts.descFile) : opts.desc;
|
|
4791
4855
|
const card = await api(
|
|
4792
4856
|
`/api/lists/${list.id}/cards`,
|
|
4793
|
-
{ method: "POST", body: JSON.stringify({ title }) },
|
|
4857
|
+
{ method: "POST", body: JSON.stringify(description ? { title, description } : { title }) },
|
|
4794
4858
|
ctx.baseUrl
|
|
4795
4859
|
);
|
|
4796
|
-
if (opts.desc) {
|
|
4797
|
-
await api(
|
|
4798
|
-
`/api/cards/${card.id}`,
|
|
4799
|
-
{ method: "PATCH", body: JSON.stringify({ description: opts.desc }) },
|
|
4800
|
-
ctx.baseUrl
|
|
4801
|
-
);
|
|
4802
|
-
}
|
|
4803
4860
|
show(card, [`\u2713 created ${card.id} "${title}" in ${list.title} (board ${ctx.entry.label})`]);
|
|
4804
4861
|
});
|
|
4805
4862
|
program2.command("read").description(`Show the resolved board \u2014 lists + cards (${CONFIG_FILENAME}, default "main")`).option("-b, --board <label>", "board label from config").option("--board-id <id>", "explicit board id (bypass config)").action(async (opts) => {
|