smashspace 0.1.1 → 0.1.3

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 (3) hide show
  1. package/README.md +46 -1
  2. package/dist/smash.mjs +377 -1
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -28,9 +28,31 @@ The token is delivered to a local loopback port and saved to
28
28
  `~/.config/smash/credentials.json`. The same credential is used by the MCP
29
29
  server below. (Headless? use `smash login --token <PAT>`.)
30
30
 
31
+ ## Onboard a project for agents: `smash init`
32
+
33
+ Run once per repo to scaffold everything agents (Claude Code / Codex) need to
34
+ operate SmashSpace as the task backbone — no manual copying:
35
+
36
+ ```bash
37
+ smash init --board-id b_xxxxxxxx
38
+ ```
39
+
40
+ It writes (and is safe to re-run — existing config is kept unless `--force`):
41
+
42
+ - `.claude/skills/smash/SKILL.md` + `.codex/skills/smash/SKILL.md` — the
43
+ SmashSpace Operator playbook (CLI/MCP usage + Inbox→Todo→Doing→Review→Done
44
+ workflow + safety rules). Always refreshed to the installed CLI version.
45
+ - `.smashspace.json` — pins the instance + board.
46
+ - `.mcp.json` — wires the `smashspace` MCP server (merged, other servers kept).
47
+
48
+ Flags: `--skills-only` (just the operator skill), `--no-mcp`, `--force`,
49
+ `--board-id <id>`. Then `smash login` once on the machine and the agents are
50
+ ready.
51
+
31
52
  ## Project config: `.smashspace.json`
32
53
 
33
- Drop this at your repo root so `smash` knows which board to operate on:
54
+ `smash init` writes this for you; drop it at your repo root manually if you
55
+ prefer, so `smash` knows which board to operate on:
34
56
 
35
57
  ```json
36
58
  {
@@ -45,6 +67,7 @@ Drop this at your repo root so `smash` knows which board to operate on:
45
67
  ## Everyday commands
46
68
 
47
69
  ```bash
70
+ smash status # what context is this directory in? (see below)
48
71
  smash read # show the board (lists + cards)
49
72
  smash scan --list "やること" # actionable cards as JSON (for agents)
50
73
  smash create "Fix signup bug" # add a card (to the first list, or --list)
@@ -56,6 +79,28 @@ smash --help # full command list (board/list/card/…)
56
79
  Pick a board with `-b <label>` (defaults to `main`), or bypass config with
57
80
  `--board-id <id>`.
58
81
 
82
+ ## Multiple repos on one machine
83
+
84
+ `smash` is **directory-aware**: it walks up from your current directory to the
85
+ nearest `.smashspace.json`, so each repo operates on its own instance and board
86
+ automatically — no global switch to flip.
87
+
88
+ ```bash
89
+ smash status # config file, instance, board, and signed-in account for CWD
90
+ ```
91
+
92
+ ```text
93
+ config .smashspace.json
94
+ base_url https://smashspace.app (.smashspace.json)
95
+ board main → b_xxxxxxxx
96
+ account <userId> ✓ token ok (23 boards) [file]
97
+ ```
98
+
99
+ Run it whenever you're unsure which repo/board a command will hit. Add
100
+ `--no-check` to skip the network round-trip, or `--json` for scripts. The login
101
+ token itself is stored once per instance in `~/.config/smash/credentials.json`
102
+ and shared across repos that point at the same `base_url`.
103
+
59
104
  > `smash space …` is an alias of `smash board …`, and the HTTP API mirrors
60
105
  > `/api/boards` at `/api/spaces`. In `.smashspace.json`, `space_id` / `spaces`
61
106
  > also work as aliases for `board_id` / `boards`.
package/dist/smash.mjs CHANGED
@@ -3392,9 +3392,10 @@ var {
3392
3392
  import { spawn } from "node:child_process";
3393
3393
  import { createServer } from "node:http";
3394
3394
  import { randomBytes } from "node:crypto";
3395
- import { readFile as readFile2, writeFile as writeFile2 } from "node:fs/promises";
3395
+ import { mkdir as mkdir2, readFile as readFile2, writeFile as writeFile2 } from "node:fs/promises";
3396
3396
  import { platform as platform2 } from "node:os";
3397
3397
  import { createInterface } from "node:readline/promises";
3398
+ import { relative, join as join3, dirname as dirname3 } from "node:path";
3398
3399
  import { stdin, stdout } from "node:process";
3399
3400
 
3400
3401
  // ../../shared/board-registry.ts
@@ -3571,6 +3572,219 @@ function resolveBoardEntry(config, label) {
3571
3572
  return entry;
3572
3573
  }
3573
3574
 
3575
+ // ../../cli/templates.ts
3576
+ var SMASH_SKILL_MD = `---
3577
+ name: smash
3578
+ description: Operate SmashSpace task boards from the terminal and MCP. Use when the user asks for /smash list, /smash inbox, /smash todo-dig, /smash todo-dev, /smash review, task-board triage, card development, card review, or moving SmashSpace cards between Inbox/Todo/Doing/Review/Done.
3579
+ ---
3580
+
3581
+ # SmashSpace Operator
3582
+
3583
+ Use this skill to run repeatable workflows against a SmashSpace task board.
3584
+
3585
+ SmashSpace is a Cloudflare-native Kanban where people work in the UI and AI
3586
+ agents work through the CLI, MCP, and HTTP API on the **same** data.
3587
+ Product: <https://smashspace.app>.
3588
+
3589
+ ## How to reach the board
3590
+
3591
+ Operate through the **\`smashspace-mcp\` MCP tools** when available; otherwise fall
3592
+ back to the **\`smash\` CLI**. Do **not** hit the HTTP API with \`curl\` or a raw
3593
+ browser \u2014 the CLI and MCP are the supported surfaces and they resolve auth and
3594
+ the target board for you.
3595
+
3596
+ - CLI install: \`npm i -g smashspace\` (then \`smash --help\`)
3597
+ - MCP server: \`smashspace-mcp\` (exposes \`smash_*\` tools, same token as the CLI)
3598
+ - Sign in once per machine: \`smash login\` (browser approval; token saved to
3599
+ \`~/.config/smash/credentials.json\` and reused by MCP)
3600
+
3601
+ ### Which board am I on?
3602
+
3603
+ \`smash\` is **directory-aware**: it walks up from the current directory to the
3604
+ nearest \`.smashspace.json\`. **Always confirm context before acting:**
3605
+
3606
+ \`\`\`bash
3607
+ smash status # config file, instance, board, signed-in account
3608
+ \`\`\`
3609
+
3610
+ Pick a board with \`-b <label>\` (defaults to \`main\`), or bypass config with
3611
+ \`--board-id <id>\`. Canonical lists on a task board: \`Inbox\`, \`Todo\`, \`Doing\`,
3612
+ \`Review\`, \`Done\`.
3613
+
3614
+ ## Safety Rules
3615
+
3616
+ - Never move a card to \`Done\` without verification.
3617
+ - This workflow uses \`Done\` \u2014 there is no separate "Complete" state.
3618
+ - Before development, inspect the **full** card: title, description, checklist,
3619
+ labels, comments, images/attachments, artifacts, external refs, assignees,
3620
+ custom data, and \`agent_meta\`.
3621
+ - Before changing a card, leave a comment that explains the plan.
3622
+ - Preserve user changes in the repo. Do not revert unrelated dirty files.
3623
+ - Scope remote writes to the resolved board (\`smash status\` to confirm).
3624
+ - If an operation can affect production data beyond the task board, ask first.
3625
+
3626
+ ## Command vocabulary
3627
+
3628
+ MCP-first; the CLI equivalents are shown so the workflow is reproducible.
3629
+
3630
+ \`\`\`bash
3631
+ smash status # resolved config / instance / board / account
3632
+ smash read # show the board (lists + cards)
3633
+ smash scan --list "Todo" # actionable cards as JSON (best for agents)
3634
+ smash create "Fix signup bug" # add a card to the resolved board (--list to target)
3635
+ smash move <cardId> "Review" # move a card to a list by name
3636
+ smash archive <cardId> # archive (soft-delete) a card
3637
+
3638
+ smash card show <cardId> # full card detail before acting
3639
+ smash card update <cardId> --title/--desc ... # edit fields
3640
+ smash comment add <cardId> "plan / progress" # leave a comment
3641
+ smash checklist add <cardId> "step" # checklist items
3642
+ smash agent-meta set <cardId> --data '{...}' # structured agent state (see below)
3643
+ smash artifact create <cardId> --kind markdown --name "Report" --content-md-file ./r.md
3644
+ \`\`\`
3645
+
3646
+ Use \`smash <group> --help\` for the full surface (\`board\`/\`list\`/\`card\`/
3647
+ \`comment\`/\`checklist\`/\`label\`/\`artifact\`/\`attachment\`/\`agent-meta\`/
3648
+ \`external-refs\`/\`assignees\`).
3649
+
3650
+ ## List matching
3651
+
3652
+ Use exact canonical names when present. For Todo-like selection, match
3653
+ case-insensitively and semantically: \`Todo\`, \`To Do\`, \`\u3084\u308B\u3053\u3068\`, \`\u3084\u308B\`,
3654
+ \`\u30BF\u30B9\u30AF\`, \`Backlog\`, \`Next\`, \`Ready\`, \`Planned\`. Exclude \`Doing\`, \`Review\`,
3655
+ \`Done\`, and archive-like lists unless the user explicitly asks.
3656
+
3657
+ ## \`agent_meta\` convention
3658
+
3659
+ Keep the card's agent state current while you work:
3660
+
3661
+ \`\`\`json
3662
+ {
3663
+ "primary_agent": "claude-code", // or "codex"
3664
+ "status": "working", // working | waiting_review | done
3665
+ "turn": "agent", // agent | human
3666
+ "current_step": "concise phase",
3667
+ "progress": 0.4 // realistic partial value
3668
+ }
3669
+ \`\`\`
3670
+
3671
+ Set \`status: "done"\` only after verification passes.
3672
+
3673
+ ## Command: /smash list
3674
+
3675
+ Show the current board state before choosing work. Do **not** mutate.
3676
+
3677
+ 1. Read the resolved board (\`smash read\`, or \`smash scan\` for JSON).
3678
+ 2. Report board title, board ID, each list (name, ID, card count), and active
3679
+ card titles, kept compact.
3680
+
3681
+ Infer options from wording: \`brief\` (names + counts), \`full\` (all cards incl.
3682
+ Done), \`todo\` / \`review\` / \`inbox\` (that list only), \`ids\` (include IDs).
3683
+
3684
+ ## Command: /smash inbox
3685
+
3686
+ Research the board and create actionable improvement cards in \`Inbox\`.
3687
+
3688
+ 1. Read the current board to avoid duplicates.
3689
+ 2. Generate concrete ideas from multiple perspectives (entrepreneur, UI/UX,
3690
+ engineer, AI specialist).
3691
+ 3. For each, create one \`Inbox\` card with a specific title, a detailed
3692
+ description (user value, expected behavior, acceptance criteria), and an
3693
+ optional checklist. Add a short comment naming the perspective and rationale.
3694
+
3695
+ Quality bar: titles must be concrete and implementable; no vague cards like
3696
+ "Improve UI"; avoid duplicating existing cards.
3697
+
3698
+ ## Command: /smash todo-dig
3699
+
3700
+ Deeply analyze Todo-like cards and prepare them for implementation.
3701
+
3702
+ 1. Find Todo-like lists (see list matching).
3703
+ 2. Inspect all card data (\`smash card show <cardId>\`).
3704
+ 3. Comment the implementation approach, likely files/modules, risks, unknowns,
3705
+ and validation plan.
3706
+ 4. Update \`agent_meta\` (\`status: working\`, \`turn: agent\`, \`progress: ~0.2\`).
3707
+ 5. Create/update a checklist with implementation and verification steps.
3708
+ 6. Do not move cards unless asked.
3709
+
3710
+ Run cards in parallel only when implementation areas do not overlap.
3711
+
3712
+ ## Command: /smash todo-dev
3713
+
3714
+ Implement Todo-like cards end to end.
3715
+
3716
+ 1. Find Todo-like lists; inspect the full card.
3717
+ 2. Comment the implementation approach **before editing code**.
3718
+ 3. Move the card to \`Doing\` (\`smash move <cardId> "Doing"\`).
3719
+ 4. Update \`agent_meta\` (\`status: working\`, \`progress\` starting near \`0.1\`).
3720
+ 5. Implement the change in the repo.
3721
+ 6. Run appropriate checks: \`npm run build\`; unit/API/contract tests when
3722
+ relevant; a dev-browser/UI check for UI changes.
3723
+ 7. Update checklist items as completed.
3724
+ 8. Add a completion comment with changed files and verification results.
3725
+ 9. Move to \`Review\` \u2014 **not** \`Done\` \u2014 unless /smash review has already verified.
3726
+
3727
+ Use subagents only for independent work with disjoint file ownership.
3728
+
3729
+ ## Command: /smash review
3730
+
3731
+ Verify \`Review\` cards and move verified work to \`Done\`.
3732
+
3733
+ 1. Inspect the card; understand what must be verified.
3734
+ 2. Choose verification: UI change \u2192 run the app/dev-browser; logic/API change \u2192
3735
+ unit/integration/API tests; broad change \u2192 \`npm run build\` + targeted tests.
3736
+ 3. Inspect screenshots or test output.
3737
+ 4. On pass: comment the verification command/result, move \`Review\` \u2192 \`Done\`, set
3738
+ \`agent_meta.status: done\`.
3739
+ 5. On fail: keep in \`Review\` or move back to \`Doing\`, comment the failure and the
3740
+ next action.
3741
+
3742
+ ## Fallbacks
3743
+
3744
+ - Prefer \`smashspace-mcp\` MCP tools. If unavailable, use the \`smash\` CLI.
3745
+ - If the CLI is not installed: \`npm i -g smashspace\`, then \`smash login\`.
3746
+ - If a board returns "Sign-in required": run \`smash login\`, or set \`SMASH_TOKEN\`
3747
+ for a headless/CI agent.
3748
+
3749
+ ## Required Final Report
3750
+
3751
+ At the end of any /smash workflow, report:
3752
+
3753
+ - board and list(s) touched
3754
+ - cards created, moved, or updated
3755
+ - comments / checklists / custom data / \`agent_meta\` changes
3756
+ - verification commands and results
3757
+ - any cards left blocked or unverified
3758
+ `;
3759
+ var OPENAI_AGENT_YAML = `interface:
3760
+ display_name: "SmashSpace Operator"
3761
+ short_description: "Run SmashSpace task-board workflows."
3762
+ default_prompt: "Use $smash to run /smash review on the resolved board."
3763
+ `;
3764
+ function smashspaceJson(baseUrl, boardId) {
3765
+ return `${JSON.stringify(
3766
+ {
3767
+ base_url: baseUrl,
3768
+ default: "main",
3769
+ spaces: {
3770
+ main: { space_id: boardId, comment: "Team task board" }
3771
+ }
3772
+ },
3773
+ null,
3774
+ 2
3775
+ )}
3776
+ `;
3777
+ }
3778
+ function mcpServerEntry(baseUrl, boardId) {
3779
+ return {
3780
+ command: "smashspace-mcp",
3781
+ env: {
3782
+ SMASH_BASE_URL: baseUrl,
3783
+ SMASH_SPACES: `${baseUrl.replace(/\/+$/, "")}/space/${boardId}`
3784
+ }
3785
+ };
3786
+ }
3787
+
3574
3788
  // ../../cli/index.ts
3575
3789
  var DEFAULT_BASE_URL = process.env.SMASH_BASE_URL ?? "https://smashspace.app";
3576
3790
  var program2 = new Command();
@@ -4389,6 +4603,168 @@ program2.command("whoami").description("Show the user identified by the saved/en
4389
4603
  [`\u2713 ${me.userId} (${me.boardCount} boards) on ${baseUrl}`]
4390
4604
  );
4391
4605
  });
4606
+ program2.command("status").aliases(["context", "where"]).description(
4607
+ "Show the context resolved from the current directory: config file, instance, board, and signed-in account"
4608
+ ).option("--no-check", "Skip the live token check (offline)").action(async (opts) => {
4609
+ const loaded = loadProjectConfig();
4610
+ const baseUrl = resolveBaseUrl(loaded);
4611
+ const baseSource = program2.getOptionValueSource("baseUrl") === "cli" ? "--base-url" : loaded?.config.base_url ? CONFIG_FILENAME : process.env.SMASH_BASE_URL ? "env:SMASH_BASE_URL" : "default";
4612
+ let board = null;
4613
+ let boardError = null;
4614
+ if (loaded) {
4615
+ try {
4616
+ const entry = resolveBoardEntry(loaded.config, void 0);
4617
+ board = { label: entry.label, boardId: entry.board_id };
4618
+ } catch (e) {
4619
+ boardError = e instanceof Error ? e.message : String(e);
4620
+ }
4621
+ }
4622
+ const cred = await findCredential(baseUrl);
4623
+ const token = await resolveToken(baseUrl);
4624
+ const tokenSource = process.env.SMASH_TOKEN ? "env:SMASH_TOKEN" : cred ? "file" : null;
4625
+ let live = null;
4626
+ if (token && opts.check) {
4627
+ try {
4628
+ const ctrl = new AbortController();
4629
+ const timer = setTimeout(() => ctrl.abort(), 5e3);
4630
+ const res = await fetch(new URL("/api/usage/me", baseUrl), {
4631
+ headers: { Authorization: `Bearer ${token}` },
4632
+ signal: ctrl.signal
4633
+ }).finally(() => clearTimeout(timer));
4634
+ if (res.ok) {
4635
+ const me = await res.json();
4636
+ live = { ok: true, userId: me.userId, boardCount: me.boardCount };
4637
+ } else {
4638
+ live = { ok: false, error: `${res.status} ${res.statusText}` };
4639
+ }
4640
+ } catch (e) {
4641
+ live = { ok: false, error: e instanceof Error ? e.message : String(e) };
4642
+ }
4643
+ }
4644
+ const account = (live?.ok ? live.userId : void 0) ?? cred?.userId ?? null;
4645
+ if (getOpts().json) {
4646
+ show({
4647
+ cwd: process.cwd(),
4648
+ config: loaded?.path ?? null,
4649
+ baseUrl,
4650
+ baseUrlSource: baseSource,
4651
+ board,
4652
+ boardError,
4653
+ account,
4654
+ tokenPresent: !!token,
4655
+ tokenSource,
4656
+ live,
4657
+ boards: loaded?.config.boards.map((b) => ({
4658
+ label: b.label,
4659
+ boardId: b.board_id
4660
+ })) ?? []
4661
+ });
4662
+ return;
4663
+ }
4664
+ const cfgDisplay = loaded ? relative(process.cwd(), loaded.path) || loaded.path : `(none \u2014 searched up from ${process.cwd()})`;
4665
+ const lines = [
4666
+ `config ${cfgDisplay}`,
4667
+ `base_url ${baseUrl} (${baseSource})`
4668
+ ];
4669
+ if (board)
4670
+ lines.push(`board ${board.label} \u2192 ${board.boardId}`);
4671
+ else if (boardError)
4672
+ lines.push(`board (unresolved: ${boardError})`);
4673
+ else
4674
+ lines.push(`board (no ${CONFIG_FILENAME}; pass --board-id)`);
4675
+ if (!token) {
4676
+ lines.push(`account \u2717 not signed in \u2014 run 'smash login'`);
4677
+ } else if (live) {
4678
+ lines.push(
4679
+ live.ok ? `account ${live.userId} \u2713 token ok (${live.boardCount} boards) [${tokenSource}]` : `account ${account ?? "?"} \u2717 token check failed: ${live.error} [${tokenSource}]`
4680
+ );
4681
+ } else {
4682
+ lines.push(
4683
+ `account ${account ?? "(token set)"} [${tokenSource}] (not checked)`
4684
+ );
4685
+ }
4686
+ if (loaded && loaded.config.boards.length > 1) {
4687
+ const active = board?.label ?? loaded.config.default ?? "main";
4688
+ lines.push("", "boards:");
4689
+ for (const b of loaded.config.boards) {
4690
+ lines.push(` ${b.label === active ? "*" : " "} ${b.label} \u2192 ${b.board_id}`);
4691
+ }
4692
+ }
4693
+ show(void 0, lines);
4694
+ });
4695
+ program2.command("init").description(
4696
+ "Scaffold this project for agents: operator skill + .smashspace.json + .mcp.json"
4697
+ ).option("--board-id <id>", "Board/space id to pin in .smashspace.json / .mcp.json").option("--skills-only", "Only (re)install the operator skill files").option("--no-mcp", "Skip writing .mcp.json").option("-f, --force", "Overwrite .smashspace.json / .mcp.json if they exist").action(
4698
+ async (opts) => {
4699
+ const cwd = process.cwd();
4700
+ const actions = [];
4701
+ const abs = (rel) => join3(cwd, rel);
4702
+ const put = async (rel, content) => {
4703
+ const p = abs(rel);
4704
+ await mkdir2(dirname3(p), { recursive: true });
4705
+ await writeFile2(p, content, "utf8");
4706
+ };
4707
+ const exists = async (rel) => {
4708
+ try {
4709
+ await readFile2(abs(rel));
4710
+ return true;
4711
+ } catch {
4712
+ return false;
4713
+ }
4714
+ };
4715
+ await put(".claude/skills/smash/SKILL.md", SMASH_SKILL_MD);
4716
+ await put(".codex/skills/smash/SKILL.md", SMASH_SKILL_MD);
4717
+ await put(".codex/skills/smash/agents/openai.yaml", OPENAI_AGENT_YAML);
4718
+ actions.push(
4719
+ "\u2713 operator skill \u2192 .claude/skills/smash/ + .codex/skills/smash/"
4720
+ );
4721
+ const baseUrl = resolveBaseUrl();
4722
+ const boardId = opts.boardId ?? "b_xxxxxxxx";
4723
+ const placeholder = boardId === "b_xxxxxxxx";
4724
+ if (!opts.skillsOnly) {
4725
+ if (await exists(CONFIG_FILENAME) && !opts.force) {
4726
+ actions.push(`\u2022 ${CONFIG_FILENAME} exists \u2014 kept (use --force)`);
4727
+ } else {
4728
+ await put(CONFIG_FILENAME, smashspaceJson(baseUrl, boardId));
4729
+ actions.push(
4730
+ `\u2713 ${CONFIG_FILENAME}${placeholder ? " (placeholder space_id \u2014 edit it)" : ` (${boardId})`}`
4731
+ );
4732
+ }
4733
+ if (opts.mcp !== false) {
4734
+ let mcp = { mcpServers: {} };
4735
+ if (await exists(".mcp.json")) {
4736
+ try {
4737
+ mcp = JSON.parse(await readFile2(abs(".mcp.json"), "utf8"));
4738
+ } catch {
4739
+ }
4740
+ if (!mcp.mcpServers)
4741
+ mcp.mcpServers = {};
4742
+ }
4743
+ const servers = mcp.mcpServers;
4744
+ if (servers.smashspace && !opts.force) {
4745
+ actions.push("\u2022 .mcp.json already wires 'smashspace' \u2014 kept (use --force)");
4746
+ } else {
4747
+ servers.smashspace = mcpServerEntry(baseUrl, boardId);
4748
+ await put(".mcp.json", `${JSON.stringify(mcp, null, 2)}
4749
+ `);
4750
+ actions.push("\u2713 .mcp.json \u2014 smashspace MCP server");
4751
+ }
4752
+ }
4753
+ }
4754
+ const next = ["", "Next:", " 1. smash login # sign in (browser approval)"];
4755
+ if (!opts.skillsOnly && placeholder) {
4756
+ next.push(" 2. edit .smashspace.json # set your real space_id (`smash board list` to find it)");
4757
+ }
4758
+ next.push(
4759
+ " \u2192 Claude Code / Codex pick up .claude|.codex/skills/smash automatically"
4760
+ );
4761
+ if (getOpts().json) {
4762
+ show({ cwd, actions, baseUrl, boardId: placeholder ? null : boardId });
4763
+ return;
4764
+ }
4765
+ show(void 0, [...actions, ...next]);
4766
+ }
4767
+ );
4392
4768
  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) => {
4393
4769
  const ctx = resolveContext(opts);
4394
4770
  const detail = await fetchBoard(ctx.boardId, ctx.baseUrl);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "smashspace",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "description": "SmashSpace CLI + MCP — operate SmashSpace boards from your terminal, Claude Code, Cursor, and Codex.",
5
5
  "type": "module",
6
6
  "bin": {