smashspace 0.1.6 → 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.
Files changed (2) hide show
  1. package/dist/smash.mjs +58 -23
  2. 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.description !== void 0)
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> <title>").description("Add a checklist item to a card").action(async (cardId, title) => {
4202
- const item = await api(
4203
- `/api/cards/${cardId}/checklist-items`,
4204
- {
4205
- method: "POST",
4206
- body: JSON.stringify({ title })
4207
- }
4208
- );
4209
- show(item, [`\u2713 checklist item ${item.id} added`]);
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) => {
@@ -4804,24 +4845,18 @@ program2.command("init").description(
4804
4845
  show(void 0, [...actions, ...next]);
4805
4846
  }
4806
4847
  );
4807
- 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) => {
4808
4849
  const ctx = resolveContext(opts);
4809
4850
  const detail = await fetchBoard(ctx.boardId, ctx.baseUrl);
4810
4851
  const list = opts.list ? findListByName(detail, opts.list) : detail.lists[0];
4811
4852
  if (!list)
4812
4853
  throw new Error(`Board ${ctx.boardId} has no lists`);
4854
+ const description = opts.descFile ? await readTextArg(opts.descFile) : opts.desc;
4813
4855
  const card = await api(
4814
4856
  `/api/lists/${list.id}/cards`,
4815
- { method: "POST", body: JSON.stringify({ title }) },
4857
+ { method: "POST", body: JSON.stringify(description ? { title, description } : { title }) },
4816
4858
  ctx.baseUrl
4817
4859
  );
4818
- if (opts.desc) {
4819
- await api(
4820
- `/api/cards/${card.id}`,
4821
- { method: "PATCH", body: JSON.stringify({ description: opts.desc }) },
4822
- ctx.baseUrl
4823
- );
4824
- }
4825
4860
  show(card, [`\u2713 created ${card.id} "${title}" in ${list.title} (board ${ctx.entry.label})`]);
4826
4861
  });
4827
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) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "smashspace",
3
- "version": "0.1.6",
3
+ "version": "0.1.7",
4
4
  "description": "SmashSpace CLI + MCP — operate SmashSpace boards from your terminal, Claude Code, Cursor, and Codex.",
5
5
  "type": "module",
6
6
  "bin": {