smashspace 0.2.1 → 0.3.0
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 +7 -1
- package/dist/smash.mjs +94 -6
- package/dist/smashspace-mcp.mjs +51 -11
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -95,7 +95,13 @@ smash --help # full command list (board/list/card/…)
|
|
|
95
95
|
```
|
|
96
96
|
|
|
97
97
|
Output is a concise line by default; add `--json` for machine-readable output on
|
|
98
|
-
any command.
|
|
98
|
+
any command.
|
|
99
|
+
|
|
100
|
+
**Field naming** (three concepts, three names — not a typo): a card's is
|
|
101
|
+
`title`, an artifact's is `name` (`--name`), a file/attachment's is `filename`
|
|
102
|
+
(with `sizeBytes`).
|
|
103
|
+
|
|
104
|
+
For agents:
|
|
99
105
|
|
|
100
106
|
```bash
|
|
101
107
|
smash scan --full # or --fields description,agentMeta
|
package/dist/smash.mjs
CHANGED
|
@@ -3654,6 +3654,10 @@ Use \`smash <group> --help\` for the full surface (\`board\`/\`list\`/\`card\`/
|
|
|
3654
3654
|
\`comment\`/\`checklist\`/\`label\`/\`artifact\`/\`attachment\`/\`agent-meta\`/
|
|
3655
3655
|
\`external-refs\`/\`assignees\`).
|
|
3656
3656
|
|
|
3657
|
+
**Field naming** (three concepts, three names \u2014 not a typo): a card's is
|
|
3658
|
+
\`title\`, an artifact's is \`name\` (\`--name\`), a file/attachment's is
|
|
3659
|
+
\`filename\` (with \`sizeBytes\`).
|
|
3660
|
+
|
|
3657
3661
|
## List matching
|
|
3658
3662
|
|
|
3659
3663
|
Use exact canonical names when present. For Todo-like selection, match
|
|
@@ -4406,18 +4410,56 @@ labelCmd.command("create <boardId> <name>").description("Create a board label").
|
|
|
4406
4410
|
show(label, [`\u2713 label ${label.id} created (${label.name}/${label.color})`]);
|
|
4407
4411
|
}
|
|
4408
4412
|
);
|
|
4409
|
-
labelCmd.command("
|
|
4413
|
+
labelCmd.command("list [boardId]").description(`List a board's labels (id, name, color, card count). Board from ${CONFIG_FILENAME} when omitted`).option("-b, --board <label>", "board label from config").option("--board-id <id>", "explicit board id (bypass config)").action(async (boardIdArg, opts) => {
|
|
4414
|
+
const ctx = resolveContext({ board: opts.board, boardId: boardIdArg ?? opts.boardId });
|
|
4415
|
+
const detail = await fetchBoard(ctx.boardId, ctx.baseUrl);
|
|
4416
|
+
const counts = /* @__PURE__ */ new Map();
|
|
4417
|
+
for (const l of detail.lists)
|
|
4418
|
+
for (const c of l.cards)
|
|
4419
|
+
for (const id of c.labelIds)
|
|
4420
|
+
counts.set(id, (counts.get(id) ?? 0) + 1);
|
|
4421
|
+
const rows = detail.labels.map((l) => ({ ...l, cardCount: counts.get(l.id) ?? 0 }));
|
|
4422
|
+
if (getOpts().json) {
|
|
4423
|
+
console.log(JSON.stringify(rows, null, 2));
|
|
4424
|
+
return;
|
|
4425
|
+
}
|
|
4426
|
+
if (rows.length === 0) {
|
|
4427
|
+
console.log("(no labels)");
|
|
4428
|
+
return;
|
|
4429
|
+
}
|
|
4430
|
+
for (const r of rows)
|
|
4431
|
+
console.log(`${r.id} ${r.name} (${r.color ?? "none"}) ${r.cardCount} cards`);
|
|
4432
|
+
});
|
|
4433
|
+
async function resolveCardLabel(cardId, idOrName) {
|
|
4434
|
+
const detail = await api(`/api/cards/${cardId}`);
|
|
4435
|
+
const labels = await api(`/api/boards/${detail.card.boardId}/labels`);
|
|
4436
|
+
const byId = labels.find((l) => l.id === idOrName);
|
|
4437
|
+
if (byId)
|
|
4438
|
+
return byId;
|
|
4439
|
+
const lc = idOrName.toLowerCase();
|
|
4440
|
+
const byName = labels.filter((l) => l.name.toLowerCase() === lc);
|
|
4441
|
+
if (byName.length === 1)
|
|
4442
|
+
return byName[0];
|
|
4443
|
+
if (byName.length > 1)
|
|
4444
|
+
throw new Error(`Label name "${idOrName}" matches ${byName.length} labels; pass the id (${byName.map((l) => l.id).join(", ")})`);
|
|
4445
|
+
throw new Error(
|
|
4446
|
+
`Label "${idOrName}" not found on this board. Labels: ${labels.map((l) => l.name).join(", ") || "(none)"}`
|
|
4447
|
+
);
|
|
4448
|
+
}
|
|
4449
|
+
labelCmd.command("attach <cardId> <label>").description("Attach a label to a card (label id or name)").action(async (cardId, idOrName) => {
|
|
4450
|
+
const label = await resolveCardLabel(cardId, idOrName);
|
|
4410
4451
|
await api(`/api/cards/${cardId}/labels`, {
|
|
4411
4452
|
method: "POST",
|
|
4412
|
-
body: JSON.stringify({ labelId })
|
|
4453
|
+
body: JSON.stringify({ labelId: label.id })
|
|
4413
4454
|
});
|
|
4414
|
-
console.log(`\u2713 label ${
|
|
4455
|
+
console.log(`\u2713 label ${label.name} (${label.id}) attached to ${cardId}`);
|
|
4415
4456
|
});
|
|
4416
|
-
labelCmd.command("detach <cardId> <
|
|
4417
|
-
await
|
|
4457
|
+
labelCmd.command("detach <cardId> <label>").description("Detach a label from a card (label id or name)").action(async (cardId, idOrName) => {
|
|
4458
|
+
const label = await resolveCardLabel(cardId, idOrName);
|
|
4459
|
+
await api(`/api/cards/${cardId}/labels/${label.id}`, {
|
|
4418
4460
|
method: "DELETE"
|
|
4419
4461
|
});
|
|
4420
|
-
console.log(`\u2713 label ${
|
|
4462
|
+
console.log(`\u2713 label ${label.name} (${label.id}) detached from ${cardId}`);
|
|
4421
4463
|
});
|
|
4422
4464
|
labelCmd.command("update <labelId>").description("Update a label's name and/or color").option("--name <name>", "New label name").option("--color <color>", "Color (green/lime/yellow/orange/red/pink/purple/blue/sky/gray), or empty string to clear").action(async (labelId, opts) => {
|
|
4423
4465
|
const patch = {};
|
|
@@ -4577,6 +4619,52 @@ assigneesCmd.command("add <cardId>").description("Append a single assignee (read
|
|
|
4577
4619
|
});
|
|
4578
4620
|
show(card, [`\u2713 card ${card.id} assignees now has ${next.length} entries`]);
|
|
4579
4621
|
});
|
|
4622
|
+
assigneesCmd.command("update <cardId> <assigneeId>").description("Change an assignee's icon (emoji) and/or display name").option("--emoji <emoji>", "New avatar_emoji").option("--no-emoji", "Remove the avatar_emoji (fall back to the default icon)").option("--name <name>", "New display_name").action(async (cardId, assigneeId, opts) => {
|
|
4623
|
+
if (opts.emoji === void 0 && opts.name === void 0) {
|
|
4624
|
+
console.error("Pass --emoji <e>, --no-emoji, or --name <name>");
|
|
4625
|
+
process.exit(1);
|
|
4626
|
+
}
|
|
4627
|
+
if (opts.name !== void 0 && !opts.name.trim()) {
|
|
4628
|
+
console.error("--name must not be empty");
|
|
4629
|
+
process.exit(1);
|
|
4630
|
+
}
|
|
4631
|
+
const detail = await api(`/api/cards/${cardId}`);
|
|
4632
|
+
const current = detail.card.assignees ?? [];
|
|
4633
|
+
if (!current.some((a) => a.id === assigneeId)) {
|
|
4634
|
+
console.error(`No assignee with id "${assigneeId}" on card ${cardId}`);
|
|
4635
|
+
process.exit(1);
|
|
4636
|
+
}
|
|
4637
|
+
const next = current.map((a) => {
|
|
4638
|
+
if (a.id !== assigneeId)
|
|
4639
|
+
return a;
|
|
4640
|
+
const { avatar_emoji, ...rest } = a;
|
|
4641
|
+
const emoji = opts.emoji === void 0 ? avatar_emoji : opts.emoji || void 0;
|
|
4642
|
+
return {
|
|
4643
|
+
...rest,
|
|
4644
|
+
...opts.name !== void 0 ? { display_name: opts.name } : {},
|
|
4645
|
+
...emoji ? { avatar_emoji: emoji } : {}
|
|
4646
|
+
};
|
|
4647
|
+
});
|
|
4648
|
+
const card = await api(`/api/cards/${cardId}`, {
|
|
4649
|
+
method: "PATCH",
|
|
4650
|
+
body: JSON.stringify({ assignees: next })
|
|
4651
|
+
});
|
|
4652
|
+
show(card, [`\u2713 card ${card.id} assignee ${assigneeId} updated`]);
|
|
4653
|
+
});
|
|
4654
|
+
assigneesCmd.command("remove <cardId> <assigneeId>").description("Remove a single assignee by id").action(async (cardId, assigneeId) => {
|
|
4655
|
+
const detail = await api(`/api/cards/${cardId}`);
|
|
4656
|
+
const current = detail.card.assignees ?? [];
|
|
4657
|
+
const next = current.filter((a) => a.id !== assigneeId);
|
|
4658
|
+
if (next.length === current.length) {
|
|
4659
|
+
console.error(`No assignee with id "${assigneeId}" on card ${cardId}`);
|
|
4660
|
+
process.exit(1);
|
|
4661
|
+
}
|
|
4662
|
+
const card = await api(`/api/cards/${cardId}`, {
|
|
4663
|
+
method: "PATCH",
|
|
4664
|
+
body: JSON.stringify({ assignees: next.length === 0 ? null : next })
|
|
4665
|
+
});
|
|
4666
|
+
show(card, [`\u2713 card ${card.id} assignees now has ${next.length} entries`]);
|
|
4667
|
+
});
|
|
4580
4668
|
assigneesCmd.command("clear <cardId>").description("Clear assignees on a card (set to null)").action(async (cardId) => {
|
|
4581
4669
|
const card = await api(`/api/cards/${cardId}`, {
|
|
4582
4670
|
method: "PATCH",
|
package/dist/smashspace-mcp.mjs
CHANGED
|
@@ -15469,6 +15469,24 @@ async function api(path, init = {}, baseUrlOverride) {
|
|
|
15469
15469
|
return void 0;
|
|
15470
15470
|
return await res.json();
|
|
15471
15471
|
}
|
|
15472
|
+
async function resolveCardLabel(cardId, idOrName) {
|
|
15473
|
+
if (typeof idOrName !== "string" || !idOrName)
|
|
15474
|
+
throw new Error("`label` (id or name) is required");
|
|
15475
|
+
const detail = await api(`/api/cards/${cardId}`);
|
|
15476
|
+
const labels = await api(`/api/boards/${detail.card.boardId}/labels`);
|
|
15477
|
+
const byId = labels.find((l) => l.id === idOrName);
|
|
15478
|
+
if (byId)
|
|
15479
|
+
return byId;
|
|
15480
|
+
const lc = idOrName.toLowerCase();
|
|
15481
|
+
const byName = labels.filter((l) => l.name.toLowerCase() === lc);
|
|
15482
|
+
if (byName.length === 1)
|
|
15483
|
+
return byName[0];
|
|
15484
|
+
if (byName.length > 1)
|
|
15485
|
+
throw new Error(`Label name "${idOrName}" matches ${byName.length} labels; pass the id (${byName.map((l) => l.id).join(", ")})`);
|
|
15486
|
+
throw new Error(
|
|
15487
|
+
`Label "${idOrName}" not found on this board. Labels: ${labels.map((l) => l.name).join(", ") || "(none)"}`
|
|
15488
|
+
);
|
|
15489
|
+
}
|
|
15472
15490
|
var tools = [
|
|
15473
15491
|
{
|
|
15474
15492
|
name: "smash_create_board",
|
|
@@ -15635,7 +15653,7 @@ var tools = [
|
|
|
15635
15653
|
},
|
|
15636
15654
|
{
|
|
15637
15655
|
name: "smash_set_agent_meta",
|
|
15638
|
-
description: "Set or clear a card's agent_meta (structured field for external agents like Claude Code). Pass agentMeta=null to clear. Common shape: {primary_agent, status: 'idle'|'working'|'waiting_review'|'blocked'|'done', turn: 'human'|'agent'|'blocked', started_at, updated_at, progress (0..1), current_step}. Extra keys are accepted for forward-compat.",
|
|
15656
|
+
description: "Set or clear a card's agent_meta (structured field for external agents like Claude Code). Pass agentMeta=null to clear. Common shape: {primary_agent, status: 'idle'|'working'|'waiting_review'|'blocked'|'done', turn: 'human'|'agent'|'blocked', started_at, updated_at, progress (0..1), current_step}. avatar_emoji is shown on the board's cards, so give each agent a distinct emoji (and reuse the same id) to make ownership visible at a glance. Extra keys are accepted for forward-compat.",
|
|
15639
15657
|
inputSchema: {
|
|
15640
15658
|
type: "object",
|
|
15641
15659
|
properties: {
|
|
@@ -15839,42 +15857,64 @@ var tools = [
|
|
|
15839
15857
|
body: JSON.stringify({ name: args.name, color: args.color })
|
|
15840
15858
|
})
|
|
15841
15859
|
},
|
|
15860
|
+
{
|
|
15861
|
+
name: "smash_list_labels",
|
|
15862
|
+
description: "List a board's labels with id, name, color, and how many cards carry each. Use this to find a label before attaching it.",
|
|
15863
|
+
inputSchema: {
|
|
15864
|
+
type: "object",
|
|
15865
|
+
properties: { boardId: { type: "string" } },
|
|
15866
|
+
required: ["boardId"]
|
|
15867
|
+
},
|
|
15868
|
+
handler: async (args) => {
|
|
15869
|
+
const detail = await api(`/api/boards/${args.boardId}`);
|
|
15870
|
+
const counts = /* @__PURE__ */ new Map();
|
|
15871
|
+
for (const l of detail.lists)
|
|
15872
|
+
for (const c of l.cards)
|
|
15873
|
+
for (const id of c.labelIds)
|
|
15874
|
+
counts.set(id, (counts.get(id) ?? 0) + 1);
|
|
15875
|
+
return detail.labels.map((l) => ({ ...l, cardCount: counts.get(l.id) ?? 0 }));
|
|
15876
|
+
}
|
|
15877
|
+
},
|
|
15842
15878
|
{
|
|
15843
15879
|
name: "smash_attach_label",
|
|
15844
|
-
description: "Attach a board label to a card.",
|
|
15880
|
+
description: "Attach a board label to a card. `label` may be the label id or its name (case-insensitive) on the card's board.",
|
|
15845
15881
|
inputSchema: {
|
|
15846
15882
|
type: "object",
|
|
15847
15883
|
properties: {
|
|
15848
15884
|
cardId: { type: "string" },
|
|
15849
|
-
|
|
15885
|
+
label: { type: "string", description: "Label id or name" },
|
|
15886
|
+
labelId: { type: "string", description: "Deprecated alias of `label`" }
|
|
15850
15887
|
},
|
|
15851
|
-
required: ["cardId"
|
|
15888
|
+
required: ["cardId"]
|
|
15852
15889
|
},
|
|
15853
15890
|
handler: async (args) => {
|
|
15891
|
+
const label = await resolveCardLabel(String(args.cardId), args.label ?? args.labelId);
|
|
15854
15892
|
await api(`/api/cards/${args.cardId}/labels`, {
|
|
15855
15893
|
method: "POST",
|
|
15856
|
-
body: JSON.stringify({ labelId:
|
|
15894
|
+
body: JSON.stringify({ labelId: label.id })
|
|
15857
15895
|
});
|
|
15858
|
-
return { ok: true };
|
|
15896
|
+
return { ok: true, label };
|
|
15859
15897
|
}
|
|
15860
15898
|
},
|
|
15861
15899
|
{
|
|
15862
15900
|
name: "smash_detach_label",
|
|
15863
|
-
description: "Detach a label from a card.",
|
|
15901
|
+
description: "Detach a label from a card. `label` may be the label id or its name (case-insensitive).",
|
|
15864
15902
|
inputSchema: {
|
|
15865
15903
|
type: "object",
|
|
15866
15904
|
properties: {
|
|
15867
15905
|
cardId: { type: "string" },
|
|
15868
|
-
|
|
15906
|
+
label: { type: "string", description: "Label id or name" },
|
|
15907
|
+
labelId: { type: "string", description: "Deprecated alias of `label`" }
|
|
15869
15908
|
},
|
|
15870
|
-
required: ["cardId"
|
|
15909
|
+
required: ["cardId"]
|
|
15871
15910
|
},
|
|
15872
15911
|
handler: async (args) => {
|
|
15912
|
+
const label = await resolveCardLabel(String(args.cardId), args.label ?? args.labelId);
|
|
15873
15913
|
await api(
|
|
15874
|
-
`/api/cards/${args.cardId}/labels/${
|
|
15914
|
+
`/api/cards/${args.cardId}/labels/${label.id}`,
|
|
15875
15915
|
{ method: "DELETE" }
|
|
15876
15916
|
);
|
|
15877
|
-
return { ok: true };
|
|
15917
|
+
return { ok: true, label };
|
|
15878
15918
|
}
|
|
15879
15919
|
},
|
|
15880
15920
|
{
|